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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-old/] [binutils-2.18.50/] [gas/] [config/] [tc-crx.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-crx.c -- Assembler code for the CRX CPU core.
/* tc-crx.c -- Assembler code for the CRX CPU core.
   Copyright 2004, 2007 Free Software Foundation, Inc.
   Copyright 2004, 2007 Free Software Foundation, Inc.
 
 
   Contributed by Tomer Levi, NSC, Israel.
   Contributed by Tomer Levi, NSC, Israel.
   Originally written for GAS 2.12 by Tomer Levi, NSC, Israel.
   Originally written for GAS 2.12 by Tomer Levi, NSC, Israel.
   Updates, BFDizing, GNUifying and ELF support by Tomer Levi.
   Updates, BFDizing, GNUifying and ELF support by Tomer Levi.
 
 
   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 the
   along with GAS; see the file COPYING.  If not, write to the
   Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
   Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
   MA 02110-1301, USA.  */
   MA 02110-1301, USA.  */
 
 
#include "as.h"
#include "as.h"
#include "safe-ctype.h"
#include "safe-ctype.h"
#include "dwarf2dbg.h"
#include "dwarf2dbg.h"
#include "opcode/crx.h"
#include "opcode/crx.h"
#include "elf/crx.h"
#include "elf/crx.h"
 
 
/* Word is considered here as a 16-bit unsigned short int.  */
/* Word is considered here as a 16-bit unsigned short int.  */
#define WORD_SHIFT  16
#define WORD_SHIFT  16
 
 
/* Register is 4-bit size.  */
/* Register is 4-bit size.  */
#define REG_SIZE   4
#define REG_SIZE   4
 
 
/* Maximum size of a single instruction (in words).  */
/* Maximum size of a single instruction (in words).  */
#define INSN_MAX_SIZE   3
#define INSN_MAX_SIZE   3
 
 
/* Maximum bits which may be set in a `mask16' operand.  */
/* Maximum bits which may be set in a `mask16' operand.  */
#define MAX_REGS_IN_MASK16  8
#define MAX_REGS_IN_MASK16  8
 
 
/* Utility macros for string comparison.  */
/* Utility macros for string comparison.  */
#define streq(a, b)           (strcmp (a, b) == 0)
#define streq(a, b)           (strcmp (a, b) == 0)
#define strneq(a, b, c)       (strncmp (a, b, c) == 0)
#define strneq(a, b, c)       (strncmp (a, b, c) == 0)
 
 
/* Assign a number NUM, shifted by SHIFT bytes, into a location
/* Assign a number NUM, shifted by SHIFT bytes, into a location
   pointed by index BYTE of array 'output_opcode'.  */
   pointed by index BYTE of array 'output_opcode'.  */
#define CRX_PRINT(BYTE, NUM, SHIFT)   output_opcode[BYTE] |= (NUM << SHIFT)
#define CRX_PRINT(BYTE, NUM, SHIFT)   output_opcode[BYTE] |= (NUM << SHIFT)
 
 
/* Operand errors.  */
/* Operand errors.  */
typedef enum
typedef enum
  {
  {
    OP_LEGAL = 0,        /* Legal operand.  */
    OP_LEGAL = 0,        /* Legal operand.  */
    OP_OUT_OF_RANGE,    /* Operand not within permitted range.  */
    OP_OUT_OF_RANGE,    /* Operand not within permitted range.  */
    OP_NOT_EVEN,        /* Operand is Odd number, should be even.  */
    OP_NOT_EVEN,        /* Operand is Odd number, should be even.  */
    OP_ILLEGAL_DISPU4,  /* Operand is not within DISPU4 range.  */
    OP_ILLEGAL_DISPU4,  /* Operand is not within DISPU4 range.  */
    OP_ILLEGAL_CST4,    /* Operand is not within CST4 range.  */
    OP_ILLEGAL_CST4,    /* Operand is not within CST4 range.  */
    OP_NOT_UPPER_64KB   /* Operand is not within the upper 64KB
    OP_NOT_UPPER_64KB   /* Operand is not within the upper 64KB
                           (0xFFFF0000-0xFFFFFFFF).  */
                           (0xFFFF0000-0xFFFFFFFF).  */
  }
  }
op_err;
op_err;
 
 
/* Opcode mnemonics hash table.  */
/* Opcode mnemonics hash table.  */
static struct hash_control *crx_inst_hash;
static struct hash_control *crx_inst_hash;
/* CRX registers hash table.  */
/* CRX registers hash table.  */
static struct hash_control *reg_hash;
static struct hash_control *reg_hash;
/* CRX coprocessor registers hash table.  */
/* CRX coprocessor registers hash table.  */
static struct hash_control *copreg_hash;
static struct hash_control *copreg_hash;
/* Current instruction we're assembling.  */
/* Current instruction we're assembling.  */
const inst *instruction;
const inst *instruction;
 
 
/* Global variables.  */
/* Global variables.  */
 
 
/* Array to hold an instruction encoding.  */
/* Array to hold an instruction encoding.  */
long output_opcode[2];
long output_opcode[2];
 
 
/* Nonzero means a relocatable symbol.  */
/* Nonzero means a relocatable symbol.  */
int relocatable;
int relocatable;
 
 
/* A copy of the original instruction (used in error messages).  */
/* A copy of the original instruction (used in error messages).  */
char ins_parse[MAX_INST_LEN];
char ins_parse[MAX_INST_LEN];
 
 
/* The current processed argument number.  */
/* The current processed argument number.  */
int cur_arg_num;
int cur_arg_num;
 
 
/* Generic assembler global variables which must be defined by all targets.  */
/* Generic assembler global variables which must be defined by all targets.  */
 
 
/* Characters which always start a comment.  */
/* Characters which always start a comment.  */
const char comment_chars[] = "#";
const char comment_chars[] = "#";
 
 
/* Characters which start a comment at the beginning of a line.  */
/* Characters which start a comment at the beginning of a line.  */
const char line_comment_chars[] = "#";
const char line_comment_chars[] = "#";
 
 
/* This array holds machine specific line separator characters.  */
/* This array holds machine specific line separator characters.  */
const char line_separator_chars[] = ";";
const char line_separator_chars[] = ";";
 
 
/* Chars that can be used to separate mant from exp in floating point nums.  */
/* Chars that can be used to separate mant from exp in floating point nums.  */
const char EXP_CHARS[] = "eE";
const char EXP_CHARS[] = "eE";
 
 
/* Chars that mean this number is a floating point constant as in 0f12.456  */
/* Chars that mean this number is a floating point constant as in 0f12.456  */
const char FLT_CHARS[] = "f'";
const char FLT_CHARS[] = "f'";
 
 
/* Target-specific multicharacter options, not const-declared at usage.  */
/* Target-specific multicharacter options, not const-declared at usage.  */
const char *md_shortopts = "";
const char *md_shortopts = "";
struct option md_longopts[] =
struct option md_longopts[] =
{
{
  {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);
 
 
/* This table describes all the machine specific pseudo-ops
/* This table describes all the machine specific pseudo-ops
   the assembler has to support.  The fields are:
   the assembler has to support.  The fields are:
   *** Pseudo-op name without dot.
   *** Pseudo-op name without dot.
   *** Function to call to execute this pseudo-op.
   *** Function to call to execute this pseudo-op.
   *** Integer arg to pass to the function.  */
   *** Integer arg to pass to the function.  */
 
 
const pseudo_typeS md_pseudo_table[] =
const pseudo_typeS md_pseudo_table[] =
{
{
  /* In CRX machine, align is in bytes (not a ptwo boundary).  */
  /* In CRX machine, align is in bytes (not a ptwo boundary).  */
  {"align", s_align_bytes, 0},
  {"align", s_align_bytes, 0},
  {0, 0, 0}
  {0, 0, 0}
};
};
 
 
/* CRX relaxation table.  */
/* CRX relaxation table.  */
const relax_typeS md_relax_table[] =
const relax_typeS md_relax_table[] =
{
{
  /* bCC  */
  /* bCC  */
  {0xfa, -0x100, 2, 1},                 /*  8 */
  {0xfa, -0x100, 2, 1},                 /*  8 */
  {0xfffe, -0x10000, 4, 2},             /* 16 */
  {0xfffe, -0x10000, 4, 2},             /* 16 */
  {0xfffffffe, -0xfffffffe, 6, 0},       /* 32 */
  {0xfffffffe, -0xfffffffe, 6, 0},       /* 32 */
 
 
  /* bal  */
  /* bal  */
  {0xfffe, -0x10000, 4, 4},             /* 16 */
  {0xfffe, -0x10000, 4, 4},             /* 16 */
  {0xfffffffe, -0xfffffffe, 6, 0},       /* 32 */
  {0xfffffffe, -0xfffffffe, 6, 0},       /* 32 */
 
 
  /* cmpbr/bcop  */
  /* cmpbr/bcop  */
  {0xfe, -0x100, 4, 6},                 /*  8 */
  {0xfe, -0x100, 4, 6},                 /*  8 */
  {0xfffffe, -0x1000000, 6, 0}           /* 24 */
  {0xfffffe, -0x1000000, 6, 0}           /* 24 */
};
};
 
 
static void    reset_vars               (char *);
static void    reset_vars               (char *);
static reg     get_register             (char *);
static reg     get_register             (char *);
static copreg  get_copregister          (char *);
static copreg  get_copregister          (char *);
static argtype get_optype               (operand_type);
static argtype get_optype               (operand_type);
static int     get_opbits               (operand_type);
static int     get_opbits               (operand_type);
static int     get_opflags              (operand_type);
static int     get_opflags              (operand_type);
static int     get_number_of_operands   (void);
static int     get_number_of_operands   (void);
static void    parse_operand            (char *, ins *);
static void    parse_operand            (char *, ins *);
static int     gettrap                  (char *);
static int     gettrap                  (char *);
static void    handle_LoadStor          (char *);
static void    handle_LoadStor          (char *);
static int     get_cinv_parameters      (char *);
static int     get_cinv_parameters      (char *);
static long    getconstant              (long, int);
static long    getconstant              (long, int);
static op_err  check_range              (long *, int, unsigned int, int);
static op_err  check_range              (long *, int, unsigned int, int);
static int     getreg_image             (reg);
static int     getreg_image             (reg);
static void    parse_operands           (ins *, char *);
static void    parse_operands           (ins *, char *);
static void    parse_insn               (ins *, char *);
static void    parse_insn               (ins *, char *);
static void    print_operand            (int, int, argument *);
static void    print_operand            (int, int, argument *);
static void    print_constant           (int, int, argument *);
static void    print_constant           (int, int, argument *);
static int     exponent2scale           (int);
static int     exponent2scale           (int);
static void    mask_reg                 (int, unsigned short *);
static void    mask_reg                 (int, unsigned short *);
static void    process_label_constant   (char *, ins *);
static void    process_label_constant   (char *, ins *);
static void    set_operand              (char *, ins *);
static void    set_operand              (char *, ins *);
static char *  preprocess_reglist       (char *, int *);
static char *  preprocess_reglist       (char *, int *);
static int     assemble_insn            (char *, ins *);
static int     assemble_insn            (char *, ins *);
static void    print_insn               (ins *);
static void    print_insn               (ins *);
static void    warn_if_needed           (ins *);
static void    warn_if_needed           (ins *);
static int     adjust_if_needed         (ins *);
static int     adjust_if_needed         (ins *);
 
 
/* Return the bit size for a given operand.  */
/* Return the bit size for a given operand.  */
 
 
static int
static int
get_opbits (operand_type op)
get_opbits (operand_type op)
{
{
  if (op < MAX_OPRD)
  if (op < MAX_OPRD)
    return crx_optab[op].bit_size;
    return crx_optab[op].bit_size;
  else
  else
    return 0;
    return 0;
}
}
 
 
/* Return the argument type of a given operand.  */
/* Return the argument type of a given operand.  */
 
 
static argtype
static argtype
get_optype (operand_type op)
get_optype (operand_type op)
{
{
  if (op < MAX_OPRD)
  if (op < MAX_OPRD)
    return crx_optab[op].arg_type;
    return crx_optab[op].arg_type;
  else
  else
    return nullargs;
    return nullargs;
}
}
 
 
/* Return the flags of a given operand.  */
/* Return the flags of a given operand.  */
 
 
static int
static int
get_opflags (operand_type op)
get_opflags (operand_type op)
{
{
  if (op < MAX_OPRD)
  if (op < MAX_OPRD)
    return crx_optab[op].flags;
    return crx_optab[op].flags;
  else
  else
    return 0;
    return 0;
}
}
 
 
/* Get the core processor register 'reg_name'.  */
/* Get the core processor register 'reg_name'.  */
 
 
static reg
static reg
get_register (char *reg_name)
get_register (char *reg_name)
{
{
  const reg_entry *reg;
  const reg_entry *reg;
 
 
  reg = (const reg_entry *) hash_find (reg_hash, reg_name);
  reg = (const reg_entry *) hash_find (reg_hash, reg_name);
 
 
  if (reg != NULL)
  if (reg != NULL)
    return reg->value.reg_val;
    return reg->value.reg_val;
  else
  else
    return nullregister;
    return nullregister;
}
}
 
 
/* Get the coprocessor register 'copreg_name'.  */
/* Get the coprocessor register 'copreg_name'.  */
 
 
static copreg
static copreg
get_copregister (char *copreg_name)
get_copregister (char *copreg_name)
{
{
  const reg_entry *copreg;
  const reg_entry *copreg;
 
 
  copreg = (const reg_entry *) hash_find (copreg_hash, copreg_name);
  copreg = (const reg_entry *) hash_find (copreg_hash, copreg_name);
 
 
  if (copreg != NULL)
  if (copreg != NULL)
    return copreg->value.copreg_val;
    return copreg->value.copreg_val;
  else
  else
    return nullcopregister;
    return nullcopregister;
}
}
 
 
/* Round up a section size to the appropriate boundary.  */
/* Round up a section size to the appropriate boundary.  */
 
 
valueT
valueT
md_section_align (segT seg, valueT val)
md_section_align (segT seg, valueT val)
{
{
  /* Round .text section to a multiple of 2.  */
  /* Round .text section to a multiple of 2.  */
  if (seg == text_section)
  if (seg == text_section)
    return (val + 1) & ~1;
    return (val + 1) & ~1;
  return val;
  return val;
}
}
 
 
/* Parse an operand that is machine-specific (remove '*').  */
/* Parse an operand that is machine-specific (remove '*').  */
 
 
void
void
md_operand (expressionS * exp)
md_operand (expressionS * exp)
{
{
  char c = *input_line_pointer;
  char c = *input_line_pointer;
 
 
  switch (c)
  switch (c)
    {
    {
    case '*':
    case '*':
      input_line_pointer++;
      input_line_pointer++;
      expression (exp);
      expression (exp);
      break;
      break;
    default:
    default:
      break;
      break;
    }
    }
}
}
 
 
/* Reset global variables before parsing a new instruction.  */
/* Reset global variables before parsing a new instruction.  */
 
 
static void
static void
reset_vars (char *op)
reset_vars (char *op)
{
{
  cur_arg_num = relocatable = 0;
  cur_arg_num = relocatable = 0;
  memset (& output_opcode, '\0', sizeof (output_opcode));
  memset (& output_opcode, '\0', sizeof (output_opcode));
 
 
  /* Save a copy of the original OP (used in error messages).  */
  /* Save a copy of the original OP (used in error messages).  */
  strncpy (ins_parse, op, sizeof ins_parse - 1);
  strncpy (ins_parse, op, sizeof ins_parse - 1);
  ins_parse [sizeof ins_parse - 1] = 0;
  ins_parse [sizeof ins_parse - 1] = 0;
}
}
 
 
/* This macro decides whether a particular reloc is an entry in a
/* This macro decides whether a particular reloc is an entry in a
   switch table.  It is used when relaxing, because the linker needs
   switch table.  It is used when relaxing, because the linker needs
   to know about all such entries so that it can adjust them if
   to know about all such entries so that it can adjust them if
   necessary.  */
   necessary.  */
 
 
#define SWITCH_TABLE(fix)                                 \
#define SWITCH_TABLE(fix)                                 \
  (   (fix)->fx_addsy != NULL                             \
  (   (fix)->fx_addsy != NULL                             \
   && (fix)->fx_subsy != NULL                             \
   && (fix)->fx_subsy != NULL                             \
   && S_GET_SEGMENT ((fix)->fx_addsy) ==                  \
   && S_GET_SEGMENT ((fix)->fx_addsy) ==                  \
      S_GET_SEGMENT ((fix)->fx_subsy)                     \
      S_GET_SEGMENT ((fix)->fx_subsy)                     \
   && S_GET_SEGMENT (fix->fx_addsy) != undefined_section  \
   && S_GET_SEGMENT (fix->fx_addsy) != undefined_section  \
   && (   (fix)->fx_r_type == BFD_RELOC_CRX_NUM8          \
   && (   (fix)->fx_r_type == BFD_RELOC_CRX_NUM8          \
       || (fix)->fx_r_type == BFD_RELOC_CRX_NUM16         \
       || (fix)->fx_r_type == BFD_RELOC_CRX_NUM16         \
       || (fix)->fx_r_type == BFD_RELOC_CRX_NUM32))
       || (fix)->fx_r_type == BFD_RELOC_CRX_NUM32))
 
 
/* See whether we need to force a relocation into the output file.
/* See whether we need to force a relocation into the output file.
   This is used to force out switch and PC relative relocations when
   This is used to force out switch and PC relative relocations when
   relaxing.  */
   relaxing.  */
 
 
int
int
crx_force_relocation (fixS *fix)
crx_force_relocation (fixS *fix)
{
{
  if (generic_force_reloc (fix) || SWITCH_TABLE (fix))
  if (generic_force_reloc (fix) || SWITCH_TABLE (fix))
    return 1;
    return 1;
 
 
  return 0;
  return 0;
}
}
 
 
/* Generate a relocation entry for a fixup.  */
/* Generate a relocation entry for a fixup.  */
 
 
arelent *
arelent *
tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS * fixP)
tc_gen_reloc (asection *section 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->addend = fixP->fx_offset;
  reloc->addend = fixP->fx_offset;
 
 
  if (fixP->fx_subsy != NULL)
  if (fixP->fx_subsy != NULL)
    {
    {
      if (SWITCH_TABLE (fixP))
      if (SWITCH_TABLE (fixP))
        {
        {
          /* Keep the current difference in the addend.  */
          /* Keep the current difference in the addend.  */
          reloc->addend = (S_GET_VALUE (fixP->fx_addsy)
          reloc->addend = (S_GET_VALUE (fixP->fx_addsy)
                           - S_GET_VALUE (fixP->fx_subsy) + fixP->fx_offset);
                           - S_GET_VALUE (fixP->fx_subsy) + fixP->fx_offset);
 
 
          switch (fixP->fx_r_type)
          switch (fixP->fx_r_type)
            {
            {
            case BFD_RELOC_CRX_NUM8:
            case BFD_RELOC_CRX_NUM8:
              fixP->fx_r_type = BFD_RELOC_CRX_SWITCH8;
              fixP->fx_r_type = BFD_RELOC_CRX_SWITCH8;
              break;
              break;
            case BFD_RELOC_CRX_NUM16:
            case BFD_RELOC_CRX_NUM16:
              fixP->fx_r_type = BFD_RELOC_CRX_SWITCH16;
              fixP->fx_r_type = BFD_RELOC_CRX_SWITCH16;
              break;
              break;
            case BFD_RELOC_CRX_NUM32:
            case BFD_RELOC_CRX_NUM32:
              fixP->fx_r_type = BFD_RELOC_CRX_SWITCH32;
              fixP->fx_r_type = BFD_RELOC_CRX_SWITCH32;
              break;
              break;
            default:
            default:
              abort ();
              abort ();
              break;
              break;
            }
            }
        }
        }
      else
      else
        {
        {
          /* We only resolve difference expressions in the same section.  */
          /* We only resolve difference expressions in the same section.  */
          as_bad_where (fixP->fx_file, fixP->fx_line,
          as_bad_where (fixP->fx_file, fixP->fx_line,
                        _("can't resolve `%s' {%s section} - `%s' {%s section}"),
                        _("can't resolve `%s' {%s section} - `%s' {%s section}"),
                        fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : "0",
                        fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : "0",
                        segment_name (fixP->fx_addsy
                        segment_name (fixP->fx_addsy
                                      ? S_GET_SEGMENT (fixP->fx_addsy)
                                      ? S_GET_SEGMENT (fixP->fx_addsy)
                                      : absolute_section),
                                      : absolute_section),
                        S_GET_NAME (fixP->fx_subsy),
                        S_GET_NAME (fixP->fx_subsy),
                        segment_name (S_GET_SEGMENT (fixP->fx_addsy)));
                        segment_name (S_GET_SEGMENT (fixP->fx_addsy)));
        }
        }
    }
    }
 
 
  assert ((int) fixP->fx_r_type > 0);
  assert ((int) fixP->fx_r_type > 0);
  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,
                    _("internal error: reloc %d (`%s') not supported by object file format"),
                    _("internal error: reloc %d (`%s') not supported by object file format"),
                    fixP->fx_r_type,
                    fixP->fx_r_type,
                    bfd_get_reloc_code_name (fixP->fx_r_type));
                    bfd_get_reloc_code_name (fixP->fx_r_type));
      return NULL;
      return NULL;
    }
    }
  assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
  assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
 
 
  return reloc;
  return reloc;
}
}
 
 
/* Prepare machine-dependent frags for relaxation.  */
/* Prepare machine-dependent frags for relaxation.  */
 
 
int
int
md_estimate_size_before_relax (fragS *fragp, asection *seg)
md_estimate_size_before_relax (fragS *fragp, asection *seg)
{
{
  /* If symbol is undefined or located in a different section,
  /* If symbol is undefined or located in a different section,
     select the largest supported relocation.  */
     select the largest supported relocation.  */
  relax_substateT subtype;
  relax_substateT subtype;
  relax_substateT rlx_state[] = {0, 2,
  relax_substateT rlx_state[] = {0, 2,
                                 3, 4,
                                 3, 4,
                                 5, 6};
                                 5, 6};
 
 
  for (subtype = 0; subtype < ARRAY_SIZE (rlx_state); subtype += 2)
  for (subtype = 0; subtype < ARRAY_SIZE (rlx_state); subtype += 2)
    {
    {
      if (fragp->fr_subtype == rlx_state[subtype]
      if (fragp->fr_subtype == rlx_state[subtype]
          && (!S_IS_DEFINED (fragp->fr_symbol)
          && (!S_IS_DEFINED (fragp->fr_symbol)
              || seg != S_GET_SEGMENT (fragp->fr_symbol)))
              || seg != S_GET_SEGMENT (fragp->fr_symbol)))
        {
        {
          fragp->fr_subtype = rlx_state[subtype + 1];
          fragp->fr_subtype = rlx_state[subtype + 1];
          break;
          break;
        }
        }
    }
    }
 
 
  if (fragp->fr_subtype >= ARRAY_SIZE (md_relax_table))
  if (fragp->fr_subtype >= ARRAY_SIZE (md_relax_table))
    abort ();
    abort ();
 
 
  return md_relax_table[fragp->fr_subtype].rlx_length;
  return md_relax_table[fragp->fr_subtype].rlx_length;
}
}
 
 
void
void
md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, fragS *fragP)
md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, fragS *fragP)
{
{
  /* 'opcode' points to the start of the instruction, whether
  /* 'opcode' points to the start of the instruction, whether
     we need to change the instruction's fixed encoding.  */
     we need to change the instruction's fixed encoding.  */
  char *opcode = fragP->fr_literal + fragP->fr_fix;
  char *opcode = fragP->fr_literal + fragP->fr_fix;
  bfd_reloc_code_real_type reloc;
  bfd_reloc_code_real_type reloc;
 
 
  subseg_change (sec, 0);
  subseg_change (sec, 0);
 
 
  switch (fragP->fr_subtype)
  switch (fragP->fr_subtype)
    {
    {
    case 0:
    case 0:
      reloc = BFD_RELOC_CRX_REL8;
      reloc = BFD_RELOC_CRX_REL8;
      break;
      break;
    case 1:
    case 1:
      *opcode = 0x7e;
      *opcode = 0x7e;
      reloc = BFD_RELOC_CRX_REL16;
      reloc = BFD_RELOC_CRX_REL16;
      break;
      break;
    case 2:
    case 2:
      *opcode = 0x7f;
      *opcode = 0x7f;
      reloc = BFD_RELOC_CRX_REL32;
      reloc = BFD_RELOC_CRX_REL32;
      break;
      break;
    case 3:
    case 3:
      reloc = BFD_RELOC_CRX_REL16;
      reloc = BFD_RELOC_CRX_REL16;
      break;
      break;
    case 4:
    case 4:
      *++opcode = 0x31;
      *++opcode = 0x31;
      reloc = BFD_RELOC_CRX_REL32;
      reloc = BFD_RELOC_CRX_REL32;
      break;
      break;
    case 5:
    case 5:
      reloc = BFD_RELOC_CRX_REL8_CMP;
      reloc = BFD_RELOC_CRX_REL8_CMP;
      break;
      break;
    case 6:
    case 6:
      *++opcode = 0x31;
      *++opcode = 0x31;
      reloc = BFD_RELOC_CRX_REL24;
      reloc = BFD_RELOC_CRX_REL24;
      break;
      break;
    default:
    default:
      abort ();
      abort ();
      break;
      break;
    }
    }
 
 
    fix_new (fragP, fragP->fr_fix,
    fix_new (fragP, fragP->fr_fix,
             bfd_get_reloc_size (bfd_reloc_type_lookup (stdoutput, reloc)),
             bfd_get_reloc_size (bfd_reloc_type_lookup (stdoutput, reloc)),
             fragP->fr_symbol, fragP->fr_offset, 1, reloc);
             fragP->fr_symbol, fragP->fr_offset, 1, reloc);
    fragP->fr_var = 0;
    fragP->fr_var = 0;
    fragP->fr_fix += md_relax_table[fragP->fr_subtype].rlx_length;
    fragP->fr_fix += md_relax_table[fragP->fr_subtype].rlx_length;
}
}
 
 
/* Process machine-dependent command line options.  Called once for
/* Process machine-dependent command line options.  Called once for
   each option on the command line that the machine-independent part of
   each option on the command line that the machine-independent part of
   GAS does not understand.  */
   GAS does not understand.  */
 
 
int
int
md_parse_option (int c ATTRIBUTE_UNUSED, char *arg ATTRIBUTE_UNUSED)
md_parse_option (int c ATTRIBUTE_UNUSED, char *arg ATTRIBUTE_UNUSED)
{
{
  return 0;
  return 0;
}
}
 
 
/* Machine-dependent usage-output.  */
/* Machine-dependent usage-output.  */
 
 
void
void
md_show_usage (FILE *stream ATTRIBUTE_UNUSED)
md_show_usage (FILE *stream ATTRIBUTE_UNUSED)
{
{
  return;
  return;
}
}
 
 
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, target_big_endian);
  return ieee_md_atof (type, litP, sizeP, target_big_endian);
}
}
 
 
/* Apply a fixS (fixup of an instruction or data that we didn't have
/* Apply a fixS (fixup of an instruction or data that we didn't have
   enough info to complete immediately) to the data in a frag.
   enough info to complete immediately) to the data in a frag.
   Since linkrelax is nonzero and TC_LINKRELAX_FIXUP is defined to disable
   Since linkrelax is nonzero and TC_LINKRELAX_FIXUP is defined to disable
   relaxation of debug sections, this function is called only when
   relaxation of debug sections, this function is called only when
   fixuping relocations of debug sections.  */
   fixuping relocations of debug sections.  */
 
 
void
void
md_apply_fix (fixS *fixP, valueT *valP, segT seg)
md_apply_fix (fixS *fixP, valueT *valP, segT seg)
{
{
  valueT val = * valP;
  valueT val = * valP;
  char *buf = fixP->fx_frag->fr_literal + fixP->fx_where;
  char *buf = fixP->fx_frag->fr_literal + fixP->fx_where;
  fixP->fx_offset = 0;
  fixP->fx_offset = 0;
 
 
  switch (fixP->fx_r_type)
  switch (fixP->fx_r_type)
    {
    {
    case BFD_RELOC_CRX_NUM8:
    case BFD_RELOC_CRX_NUM8:
      bfd_put_8 (stdoutput, (unsigned char) val, buf);
      bfd_put_8 (stdoutput, (unsigned char) val, buf);
      break;
      break;
    case BFD_RELOC_CRX_NUM16:
    case BFD_RELOC_CRX_NUM16:
      bfd_put_16 (stdoutput, val, buf);
      bfd_put_16 (stdoutput, val, buf);
      break;
      break;
    case BFD_RELOC_CRX_NUM32:
    case BFD_RELOC_CRX_NUM32:
      bfd_put_32 (stdoutput, val, buf);
      bfd_put_32 (stdoutput, val, buf);
      break;
      break;
    default:
    default:
      /* We shouldn't ever get here because linkrelax is nonzero.  */
      /* We shouldn't ever get here because linkrelax is nonzero.  */
      abort ();
      abort ();
      break;
      break;
    }
    }
 
 
  fixP->fx_done = 0;
  fixP->fx_done = 0;
 
 
  if (fixP->fx_addsy == NULL
  if (fixP->fx_addsy == NULL
      && fixP->fx_pcrel == 0)
      && fixP->fx_pcrel == 0)
    fixP->fx_done = 1;
    fixP->fx_done = 1;
 
 
  if (fixP->fx_pcrel == 1
  if (fixP->fx_pcrel == 1
      && fixP->fx_addsy != NULL
      && fixP->fx_addsy != NULL
      && S_GET_SEGMENT (fixP->fx_addsy) == seg)
      && S_GET_SEGMENT (fixP->fx_addsy) == seg)
    fixP->fx_done = 1;
    fixP->fx_done = 1;
}
}
 
 
/* The location from which a PC relative jump should be calculated,
/* The location from which a PC relative jump should be calculated,
   given a PC relative reloc.  */
   given a PC relative reloc.  */
 
 
long
long
md_pcrel_from (fixS *fixp)
md_pcrel_from (fixS *fixp)
{
{
  return fixp->fx_frag->fr_address + fixp->fx_where;
  return fixp->fx_frag->fr_address + fixp->fx_where;
}
}
 
 
/* This function is called once, at assembler startup time.  This should
/* This function is called once, at assembler startup time.  This should
   set up all the tables, etc that the MD part of the assembler needs.  */
   set up all the tables, etc that the MD part of the assembler needs.  */
 
 
void
void
md_begin (void)
md_begin (void)
{
{
  const char *hashret = NULL;
  const char *hashret = NULL;
  int i = 0;
  int i = 0;
 
 
  /* Set up a hash table for the instructions.  */
  /* Set up a hash table for the instructions.  */
  if ((crx_inst_hash = hash_new ()) == NULL)
  if ((crx_inst_hash = hash_new ()) == NULL)
    as_fatal (_("Virtual memory exhausted"));
    as_fatal (_("Virtual memory exhausted"));
 
 
  while (crx_instruction[i].mnemonic != NULL)
  while (crx_instruction[i].mnemonic != NULL)
    {
    {
      const char *mnemonic = crx_instruction[i].mnemonic;
      const char *mnemonic = crx_instruction[i].mnemonic;
 
 
      hashret = hash_insert (crx_inst_hash, mnemonic,
      hashret = hash_insert (crx_inst_hash, mnemonic,
        (PTR) &crx_instruction[i]);
        (PTR) &crx_instruction[i]);
 
 
      if (hashret != NULL && *hashret != '\0')
      if (hashret != NULL && *hashret != '\0')
        as_fatal (_("Can't hash `%s': %s\n"), crx_instruction[i].mnemonic,
        as_fatal (_("Can't hash `%s': %s\n"), crx_instruction[i].mnemonic,
                  *hashret == 0 ? _("(unknown reason)") : hashret);
                  *hashret == 0 ? _("(unknown reason)") : hashret);
 
 
      /* Insert unique names into hash table.  The CRX instruction set
      /* Insert unique names into hash table.  The CRX 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.  */
      do
      do
        {
        {
          ++i;
          ++i;
        }
        }
      while (crx_instruction[i].mnemonic != NULL
      while (crx_instruction[i].mnemonic != NULL
             && streq (crx_instruction[i].mnemonic, mnemonic));
             && streq (crx_instruction[i].mnemonic, mnemonic));
    }
    }
 
 
  /* Initialize reg_hash hash table.  */
  /* Initialize reg_hash hash table.  */
  if ((reg_hash = hash_new ()) == NULL)
  if ((reg_hash = hash_new ()) == NULL)
    as_fatal (_("Virtual memory exhausted"));
    as_fatal (_("Virtual memory exhausted"));
 
 
  {
  {
    const reg_entry *regtab;
    const reg_entry *regtab;
 
 
    for (regtab = crx_regtab;
    for (regtab = crx_regtab;
         regtab < (crx_regtab + NUMREGS); regtab++)
         regtab < (crx_regtab + NUMREGS); regtab++)
      {
      {
        hashret = hash_insert (reg_hash, regtab->name, (PTR) regtab);
        hashret = hash_insert (reg_hash, regtab->name, (PTR) regtab);
        if (hashret)
        if (hashret)
          as_fatal (_("Internal Error:  Can't hash %s: %s"),
          as_fatal (_("Internal Error:  Can't hash %s: %s"),
                    regtab->name,
                    regtab->name,
                    hashret);
                    hashret);
      }
      }
  }
  }
 
 
  /* Initialize copreg_hash hash table.  */
  /* Initialize copreg_hash hash table.  */
  if ((copreg_hash = hash_new ()) == NULL)
  if ((copreg_hash = hash_new ()) == NULL)
    as_fatal (_("Virtual memory exhausted"));
    as_fatal (_("Virtual memory exhausted"));
 
 
  {
  {
    const reg_entry *copregtab;
    const reg_entry *copregtab;
 
 
    for (copregtab = crx_copregtab; copregtab < (crx_copregtab + NUMCOPREGS);
    for (copregtab = crx_copregtab; copregtab < (crx_copregtab + NUMCOPREGS);
         copregtab++)
         copregtab++)
      {
      {
        hashret = hash_insert (copreg_hash, copregtab->name, (PTR) copregtab);
        hashret = hash_insert (copreg_hash, copregtab->name, (PTR) copregtab);
        if (hashret)
        if (hashret)
          as_fatal (_("Internal Error:  Can't hash %s: %s"),
          as_fatal (_("Internal Error:  Can't hash %s: %s"),
                    copregtab->name,
                    copregtab->name,
                    hashret);
                    hashret);
      }
      }
  }
  }
  /*  Set linkrelax here to avoid fixups in most sections.  */
  /*  Set linkrelax here to avoid fixups in most sections.  */
  linkrelax = 1;
  linkrelax = 1;
}
}
 
 
/* Process constants (immediate/absolute)
/* Process constants (immediate/absolute)
   and labels (jump targets/Memory locations).  */
   and labels (jump targets/Memory locations).  */
 
 
static void
static void
process_label_constant (char *str, ins * crx_ins)
process_label_constant (char *str, ins * crx_ins)
{
{
  char *saved_input_line_pointer;
  char *saved_input_line_pointer;
  argument *cur_arg = &crx_ins->arg[cur_arg_num];  /* Current argument.  */
  argument *cur_arg = &crx_ins->arg[cur_arg_num];  /* Current argument.  */
 
 
  saved_input_line_pointer = input_line_pointer;
  saved_input_line_pointer = input_line_pointer;
  input_line_pointer = str;
  input_line_pointer = str;
 
 
  expression (&crx_ins->exp);
  expression (&crx_ins->exp);
 
 
  switch (crx_ins->exp.X_op)
  switch (crx_ins->exp.X_op)
    {
    {
    case O_big:
    case O_big:
    case O_absent:
    case O_absent:
      /* Missing or bad expr becomes absolute 0.  */
      /* Missing or bad expr becomes absolute 0.  */
      as_bad (_("missing or invalid displacement expression `%s' taken as 0"),
      as_bad (_("missing or invalid displacement expression `%s' taken as 0"),
              str);
              str);
      crx_ins->exp.X_op = O_constant;
      crx_ins->exp.X_op = O_constant;
      crx_ins->exp.X_add_number = 0;
      crx_ins->exp.X_add_number = 0;
      crx_ins->exp.X_add_symbol = (symbolS *) 0;
      crx_ins->exp.X_add_symbol = (symbolS *) 0;
      crx_ins->exp.X_op_symbol = (symbolS *) 0;
      crx_ins->exp.X_op_symbol = (symbolS *) 0;
      /* Fall through.  */
      /* Fall through.  */
 
 
    case O_constant:
    case O_constant:
      cur_arg->X_op = O_constant;
      cur_arg->X_op = O_constant;
      cur_arg->constant = crx_ins->exp.X_add_number;
      cur_arg->constant = crx_ins->exp.X_add_number;
      break;
      break;
 
 
    case O_symbol:
    case O_symbol:
    case O_subtract:
    case O_subtract:
    case O_add:
    case O_add:
      cur_arg->X_op = O_symbol;
      cur_arg->X_op = O_symbol;
      crx_ins->rtype = BFD_RELOC_NONE;
      crx_ins->rtype = BFD_RELOC_NONE;
      relocatable = 1;
      relocatable = 1;
 
 
      switch (cur_arg->type)
      switch (cur_arg->type)
        {
        {
        case arg_cr:
        case arg_cr:
          if (IS_INSN_TYPE (LD_STOR_INS_INC))
          if (IS_INSN_TYPE (LD_STOR_INS_INC))
            crx_ins->rtype = BFD_RELOC_CRX_REGREL12;
            crx_ins->rtype = BFD_RELOC_CRX_REGREL12;
          else if (IS_INSN_TYPE (CSTBIT_INS)
          else if (IS_INSN_TYPE (CSTBIT_INS)
                   || IS_INSN_TYPE (STOR_IMM_INS))
                   || IS_INSN_TYPE (STOR_IMM_INS))
            crx_ins->rtype = BFD_RELOC_CRX_REGREL28;
            crx_ins->rtype = BFD_RELOC_CRX_REGREL28;
          else
          else
            crx_ins->rtype = BFD_RELOC_CRX_REGREL32;
            crx_ins->rtype = BFD_RELOC_CRX_REGREL32;
          break;
          break;
 
 
        case arg_idxr:
        case arg_idxr:
            crx_ins->rtype = BFD_RELOC_CRX_REGREL22;
            crx_ins->rtype = BFD_RELOC_CRX_REGREL22;
          break;
          break;
 
 
        case arg_c:
        case arg_c:
          if (IS_INSN_MNEMONIC ("bal") || IS_INSN_TYPE (DCR_BRANCH_INS))
          if (IS_INSN_MNEMONIC ("bal") || IS_INSN_TYPE (DCR_BRANCH_INS))
            crx_ins->rtype = BFD_RELOC_CRX_REL16;
            crx_ins->rtype = BFD_RELOC_CRX_REL16;
          else if (IS_INSN_TYPE (BRANCH_INS))
          else if (IS_INSN_TYPE (BRANCH_INS))
            crx_ins->rtype = BFD_RELOC_CRX_REL8;
            crx_ins->rtype = BFD_RELOC_CRX_REL8;
          else if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (STOR_IMM_INS)
          else if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (STOR_IMM_INS)
                   || IS_INSN_TYPE (CSTBIT_INS))
                   || IS_INSN_TYPE (CSTBIT_INS))
            crx_ins->rtype = BFD_RELOC_CRX_ABS32;
            crx_ins->rtype = BFD_RELOC_CRX_ABS32;
          else if (IS_INSN_TYPE (BRANCH_NEQ_INS))
          else if (IS_INSN_TYPE (BRANCH_NEQ_INS))
            crx_ins->rtype = BFD_RELOC_CRX_REL4;
            crx_ins->rtype = BFD_RELOC_CRX_REL4;
          else if (IS_INSN_TYPE (CMPBR_INS) || IS_INSN_TYPE (COP_BRANCH_INS))
          else if (IS_INSN_TYPE (CMPBR_INS) || IS_INSN_TYPE (COP_BRANCH_INS))
            crx_ins->rtype = BFD_RELOC_CRX_REL8_CMP;
            crx_ins->rtype = BFD_RELOC_CRX_REL8_CMP;
          break;
          break;
 
 
        case arg_ic:
        case arg_ic:
          if (IS_INSN_TYPE (ARITH_INS))
          if (IS_INSN_TYPE (ARITH_INS))
            crx_ins->rtype = BFD_RELOC_CRX_IMM32;
            crx_ins->rtype = BFD_RELOC_CRX_IMM32;
          else if (IS_INSN_TYPE (ARITH_BYTE_INS))
          else if (IS_INSN_TYPE (ARITH_BYTE_INS))
            crx_ins->rtype = BFD_RELOC_CRX_IMM16;
            crx_ins->rtype = BFD_RELOC_CRX_IMM16;
          break;
          break;
        default:
        default:
          break;
          break;
      }
      }
      break;
      break;
 
 
    default:
    default:
      cur_arg->X_op = crx_ins->exp.X_op;
      cur_arg->X_op = crx_ins->exp.X_op;
      break;
      break;
    }
    }
 
 
  input_line_pointer = saved_input_line_pointer;
  input_line_pointer = saved_input_line_pointer;
  return;
  return;
}
}
 
 
/* Get the values of the scale to be encoded -
/* Get the values of the scale to be encoded -
   used for the scaled index mode of addressing.  */
   used for the scaled index mode of addressing.  */
 
 
static int
static int
exponent2scale (int val)
exponent2scale (int val)
{
{
  int exponent;
  int exponent;
 
 
  /* If 'val' is 0, the following 'for' will be an endless loop.  */
  /* If 'val' is 0, the following 'for' will be an endless loop.  */
  if (val == 0)
  if (val == 0)
    return 0;
    return 0;
 
 
  for (exponent = 0; (val != 1); val >>= 1, exponent++)
  for (exponent = 0; (val != 1); val >>= 1, exponent++)
    ;
    ;
 
 
  return exponent;
  return exponent;
}
}
 
 
/* Parsing different types of operands
/* Parsing different types of operands
   -> constants             Immediate/Absolute/Relative numbers
   -> constants             Immediate/Absolute/Relative numbers
   -> Labels                Relocatable symbols
   -> Labels                Relocatable symbols
   -> (rbase)               Register base
   -> (rbase)               Register base
   -> disp(rbase)           Register relative
   -> disp(rbase)           Register relative
   -> disp(rbase)+          Post-increment mode
   -> disp(rbase)+          Post-increment mode
   -> disp(rbase,ridx,scl)  Register index mode  */
   -> disp(rbase,ridx,scl)  Register index mode  */
 
 
static void
static void
set_operand (char *operand, ins * crx_ins)
set_operand (char *operand, ins * crx_ins)
{
{
  char *operandS; /* Pointer to start of sub-opearand.  */
  char *operandS; /* Pointer to start of sub-opearand.  */
  char *operandE; /* Pointer to end of sub-opearand.  */
  char *operandE; /* Pointer to end of sub-opearand.  */
  expressionS scale;
  expressionS scale;
  int scale_val;
  int scale_val;
  char *input_save, c;
  char *input_save, c;
  argument *cur_arg = &crx_ins->arg[cur_arg_num]; /* Current argument.  */
  argument *cur_arg = &crx_ins->arg[cur_arg_num]; /* Current argument.  */
 
 
  /* Initialize pointers.  */
  /* Initialize pointers.  */
  operandS = operandE = operand;
  operandS = operandE = operand;
 
 
  switch (cur_arg->type)
  switch (cur_arg->type)
    {
    {
    case arg_sc:    /* Case *+0x18.  */
    case arg_sc:    /* Case *+0x18.  */
    case arg_ic:    /* Case $0x18.  */
    case arg_ic:    /* Case $0x18.  */
      operandS++;
      operandS++;
    case arg_c:     /* Case 0x18.  */
    case arg_c:     /* Case 0x18.  */
      /* Set constant.  */
      /* Set constant.  */
      process_label_constant (operandS, crx_ins);
      process_label_constant (operandS, crx_ins);
 
 
      if (cur_arg->type != arg_ic)
      if (cur_arg->type != arg_ic)
        cur_arg->type = arg_c;
        cur_arg->type = arg_c;
      break;
      break;
 
 
    case arg_icr:   /* Case $0x18(r1).  */
    case arg_icr:   /* Case $0x18(r1).  */
      operandS++;
      operandS++;
    case arg_cr:    /* Case 0x18(r1).   */
    case arg_cr:    /* Case 0x18(r1).   */
      /* Set displacement constant.  */
      /* Set displacement constant.  */
      while (*operandE != '(')
      while (*operandE != '(')
        operandE++;
        operandE++;
      *operandE = '\0';
      *operandE = '\0';
      process_label_constant (operandS, crx_ins);
      process_label_constant (operandS, crx_ins);
      operandS = operandE;
      operandS = operandE;
    case arg_rbase: /* Case (r1).  */
    case arg_rbase: /* Case (r1).  */
      operandS++;
      operandS++;
      /* Set register base.  */
      /* Set register base.  */
      while (*operandE != ')')
      while (*operandE != ')')
        operandE++;
        operandE++;
      *operandE = '\0';
      *operandE = '\0';
      if ((cur_arg->r = get_register (operandS)) == nullregister)
      if ((cur_arg->r = get_register (operandS)) == nullregister)
        as_bad (_("Illegal register `%s' in Instruction `%s'"),
        as_bad (_("Illegal register `%s' in Instruction `%s'"),
                operandS, ins_parse);
                operandS, ins_parse);
 
 
      if (cur_arg->type != arg_rbase)
      if (cur_arg->type != arg_rbase)
        cur_arg->type = arg_cr;
        cur_arg->type = arg_cr;
      break;
      break;
 
 
    case arg_idxr:
    case arg_idxr:
      /* Set displacement constant.  */
      /* Set displacement constant.  */
      while (*operandE != '(')
      while (*operandE != '(')
        operandE++;
        operandE++;
      *operandE = '\0';
      *operandE = '\0';
      process_label_constant (operandS, crx_ins);
      process_label_constant (operandS, crx_ins);
      operandS = ++operandE;
      operandS = ++operandE;
 
 
      /* Set register base.  */
      /* Set register base.  */
      while ((*operandE != ',') && (! ISSPACE (*operandE)))
      while ((*operandE != ',') && (! ISSPACE (*operandE)))
        operandE++;
        operandE++;
      *operandE++ = '\0';
      *operandE++ = '\0';
      if ((cur_arg->r = get_register (operandS)) == nullregister)
      if ((cur_arg->r = get_register (operandS)) == nullregister)
        as_bad (_("Illegal register `%s' in Instruction `%s'"),
        as_bad (_("Illegal register `%s' in Instruction `%s'"),
                operandS, ins_parse);
                operandS, ins_parse);
 
 
      /* Skip leading white space.  */
      /* Skip leading white space.  */
      while (ISSPACE (*operandE))
      while (ISSPACE (*operandE))
        operandE++;
        operandE++;
      operandS = operandE;
      operandS = operandE;
 
 
      /* Set register index.  */
      /* Set register index.  */
      while ((*operandE != ')') && (*operandE != ','))
      while ((*operandE != ')') && (*operandE != ','))
        operandE++;
        operandE++;
      c = *operandE;
      c = *operandE;
      *operandE++ = '\0';
      *operandE++ = '\0';
 
 
      if ((cur_arg->i_r = get_register (operandS)) == nullregister)
      if ((cur_arg->i_r = get_register (operandS)) == nullregister)
        as_bad (_("Illegal register `%s' in Instruction `%s'"),
        as_bad (_("Illegal register `%s' in Instruction `%s'"),
                operandS, ins_parse);
                operandS, ins_parse);
 
 
      /* Skip leading white space.  */
      /* Skip leading white space.  */
      while (ISSPACE (*operandE))
      while (ISSPACE (*operandE))
        operandE++;
        operandE++;
      operandS = operandE;
      operandS = operandE;
 
 
      /* Set the scale.  */
      /* Set the scale.  */
      if (c == ')')
      if (c == ')')
        cur_arg->scale = 0;
        cur_arg->scale = 0;
      else
      else
        {
        {
          while (*operandE != ')')
          while (*operandE != ')')
            operandE++;
            operandE++;
          *operandE = '\0';
          *operandE = '\0';
 
 
          /* Preprocess the scale string.  */
          /* Preprocess the scale string.  */
          input_save = input_line_pointer;
          input_save = input_line_pointer;
          input_line_pointer = operandS;
          input_line_pointer = operandS;
          expression (&scale);
          expression (&scale);
          input_line_pointer = input_save;
          input_line_pointer = input_save;
 
 
          scale_val = scale.X_add_number;
          scale_val = scale.X_add_number;
 
 
          /* Check if the scale value is legal.  */
          /* Check if the scale value is legal.  */
          if (scale_val != 1 && scale_val != 2
          if (scale_val != 1 && scale_val != 2
              && scale_val != 4 && scale_val != 8)
              && scale_val != 4 && scale_val != 8)
            as_bad (_("Illegal Scale - `%d'"), scale_val);
            as_bad (_("Illegal Scale - `%d'"), scale_val);
 
 
          cur_arg->scale = exponent2scale (scale_val);
          cur_arg->scale = exponent2scale (scale_val);
        }
        }
      break;
      break;
 
 
    default:
    default:
      break;
      break;
    }
    }
}
}
 
 
/* Parse a single operand.
/* Parse a single operand.
   operand - Current operand to parse.
   operand - Current operand to parse.
   crx_ins - Current assembled instruction.  */
   crx_ins - Current assembled instruction.  */
 
 
static void
static void
parse_operand (char *operand, ins * crx_ins)
parse_operand (char *operand, ins * crx_ins)
{
{
  int ret_val;
  int ret_val;
  argument *cur_arg = &crx_ins->arg[cur_arg_num]; /* Current argument.  */
  argument *cur_arg = &crx_ins->arg[cur_arg_num]; /* Current argument.  */
 
 
  /* Initialize the type to NULL before parsing.  */
  /* Initialize the type to NULL before parsing.  */
  cur_arg->type = nullargs;
  cur_arg->type = nullargs;
 
 
  /* Check whether this is a general processor register.  */
  /* Check whether this is a general processor register.  */
  if ((ret_val = get_register (operand)) != nullregister)
  if ((ret_val = get_register (operand)) != nullregister)
    {
    {
      cur_arg->type = arg_r;
      cur_arg->type = arg_r;
      cur_arg->r = ret_val;
      cur_arg->r = ret_val;
      cur_arg->X_op = O_register;
      cur_arg->X_op = O_register;
      return;
      return;
    }
    }
 
 
  /* Check whether this is a core [special] coprocessor register.  */
  /* Check whether this is a core [special] coprocessor register.  */
  if ((ret_val = get_copregister (operand)) != nullcopregister)
  if ((ret_val = get_copregister (operand)) != nullcopregister)
    {
    {
      cur_arg->type = arg_copr;
      cur_arg->type = arg_copr;
      if (ret_val >= cs0)
      if (ret_val >= cs0)
        cur_arg->type = arg_copsr;
        cur_arg->type = arg_copsr;
      cur_arg->cr = ret_val;
      cur_arg->cr = ret_val;
      cur_arg->X_op = O_register;
      cur_arg->X_op = O_register;
      return;
      return;
    }
    }
 
 
  /* Deal with special characters.  */
  /* Deal with special characters.  */
  switch (operand[0])
  switch (operand[0])
    {
    {
    case '$':
    case '$':
      if (strchr (operand, '(') != NULL)
      if (strchr (operand, '(') != NULL)
        cur_arg->type = arg_icr;
        cur_arg->type = arg_icr;
      else
      else
        cur_arg->type = arg_ic;
        cur_arg->type = arg_ic;
      goto set_params;
      goto set_params;
      break;
      break;
 
 
    case '*':
    case '*':
      cur_arg->type = arg_sc;
      cur_arg->type = arg_sc;
      goto set_params;
      goto set_params;
      break;
      break;
 
 
    case '(':
    case '(':
      cur_arg->type = arg_rbase;
      cur_arg->type = arg_rbase;
      goto set_params;
      goto set_params;
      break;
      break;
 
 
    default:
    default:
        break;
        break;
    }
    }
 
 
  if (strchr (operand, '(') != NULL)
  if (strchr (operand, '(') != NULL)
    {
    {
      if (strchr (operand, ',') != NULL
      if (strchr (operand, ',') != NULL
          && (strchr (operand, ',') > strchr (operand, '(')))
          && (strchr (operand, ',') > strchr (operand, '(')))
            cur_arg->type = arg_idxr;
            cur_arg->type = arg_idxr;
      else
      else
        cur_arg->type = arg_cr;
        cur_arg->type = arg_cr;
    }
    }
  else
  else
    cur_arg->type = arg_c;
    cur_arg->type = arg_c;
  goto set_params;
  goto set_params;
 
 
/* Parse an operand according to its type.  */
/* Parse an operand according to its type.  */
set_params:
set_params:
  cur_arg->constant = 0;
  cur_arg->constant = 0;
  set_operand (operand, crx_ins);
  set_operand (operand, crx_ins);
}
}
 
 
/* Parse the various operands. Each operand is then analyzed to fillup
/* Parse the various operands. Each operand is then analyzed to fillup
   the fields in the crx_ins data structure.  */
   the fields in the crx_ins data structure.  */
 
 
static void
static void
parse_operands (ins * crx_ins, char *operands)
parse_operands (ins * crx_ins, char *operands)
{
{
  char *operandS;              /* Operands string.  */
  char *operandS;              /* Operands string.  */
  char *operandH, *operandT;   /* Single operand head/tail pointers.  */
  char *operandH, *operandT;   /* Single operand head/tail pointers.  */
  int allocated = 0;            /* Indicates a new operands string was allocated.  */
  int allocated = 0;            /* Indicates a new operands string was allocated.  */
  char *operand[MAX_OPERANDS]; /* Separating the operands.  */
  char *operand[MAX_OPERANDS]; /* Separating the operands.  */
  int op_num = 0;               /* Current operand number we are parsing.  */
  int op_num = 0;               /* Current operand number we are parsing.  */
  int bracket_flag = 0;         /* Indicates a bracket '(' was found.  */
  int bracket_flag = 0;         /* Indicates a bracket '(' was found.  */
  int sq_bracket_flag = 0;     /* Indicates a square bracket '[' was found.  */
  int sq_bracket_flag = 0;     /* Indicates a square bracket '[' was found.  */
 
 
  /* Preprocess the list of registers, if necessary.  */
  /* Preprocess the list of registers, if necessary.  */
  operandS = operandH = operandT = (INST_HAS_REG_LIST) ?
  operandS = operandH = operandT = (INST_HAS_REG_LIST) ?
    preprocess_reglist (operands, &allocated) : operands;
    preprocess_reglist (operands, &allocated) : operands;
 
 
  while (*operandT != '\0')
  while (*operandT != '\0')
    {
    {
      if (*operandT == ',' && bracket_flag != 1 && sq_bracket_flag != 1)
      if (*operandT == ',' && bracket_flag != 1 && sq_bracket_flag != 1)
        {
        {
          *operandT++ = '\0';
          *operandT++ = '\0';
          operand[op_num++] = strdup (operandH);
          operand[op_num++] = strdup (operandH);
          operandH = operandT;
          operandH = operandT;
          continue;
          continue;
        }
        }
 
 
      if (*operandT == ' ')
      if (*operandT == ' ')
        as_bad (_("Illegal operands (whitespace): `%s'"), ins_parse);
        as_bad (_("Illegal operands (whitespace): `%s'"), ins_parse);
 
 
      if (*operandT == '(')
      if (*operandT == '(')
        bracket_flag = 1;
        bracket_flag = 1;
      else if (*operandT == '[')
      else if (*operandT == '[')
        sq_bracket_flag = 1;
        sq_bracket_flag = 1;
 
 
      if (*operandT == ')')
      if (*operandT == ')')
        {
        {
          if (bracket_flag)
          if (bracket_flag)
            bracket_flag = 0;
            bracket_flag = 0;
          else
          else
            as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
            as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
        }
        }
      else if (*operandT == ']')
      else if (*operandT == ']')
        {
        {
          if (sq_bracket_flag)
          if (sq_bracket_flag)
            sq_bracket_flag = 0;
            sq_bracket_flag = 0;
          else
          else
            as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
            as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
        }
        }
 
 
      if (bracket_flag == 1 && *operandT == ')')
      if (bracket_flag == 1 && *operandT == ')')
        bracket_flag = 0;
        bracket_flag = 0;
      else if (sq_bracket_flag == 1 && *operandT == ']')
      else if (sq_bracket_flag == 1 && *operandT == ']')
        sq_bracket_flag = 0;
        sq_bracket_flag = 0;
 
 
      operandT++;
      operandT++;
    }
    }
 
 
  /* Adding the last operand.  */
  /* Adding the last operand.  */
  operand[op_num++] = strdup (operandH);
  operand[op_num++] = strdup (operandH);
  crx_ins->nargs = op_num;
  crx_ins->nargs = op_num;
 
 
  /* Verifying correct syntax of operands (all brackets should be closed).  */
  /* Verifying correct syntax of operands (all brackets should be closed).  */
  if (bracket_flag || sq_bracket_flag)
  if (bracket_flag || sq_bracket_flag)
    as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
    as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
 
 
  /* Now we parse each operand separately.  */
  /* Now we parse each operand separately.  */
  for (op_num = 0; op_num < crx_ins->nargs; op_num++)
  for (op_num = 0; op_num < crx_ins->nargs; op_num++)
    {
    {
      cur_arg_num = op_num;
      cur_arg_num = op_num;
      parse_operand (operand[op_num], crx_ins);
      parse_operand (operand[op_num], crx_ins);
      free (operand[op_num]);
      free (operand[op_num]);
    }
    }
 
 
  if (allocated)
  if (allocated)
    free (operandS);
    free (operandS);
}
}
 
 
/* Get the trap index in dispatch table, given its name.
/* Get the trap index in dispatch table, given its name.
   This routine is used by assembling the 'excp' instruction.  */
   This routine is used by assembling the 'excp' instruction.  */
 
 
static int
static int
gettrap (char *s)
gettrap (char *s)
{
{
  const trap_entry *trap;
  const trap_entry *trap;
 
 
  for (trap = crx_traps; trap < (crx_traps + NUMTRAPS); trap++)
  for (trap = crx_traps; trap < (crx_traps + NUMTRAPS); trap++)
    if (strcasecmp (trap->name, s) == 0)
    if (strcasecmp (trap->name, s) == 0)
      return trap->entry;
      return trap->entry;
 
 
  as_bad (_("Unknown exception: `%s'"), s);
  as_bad (_("Unknown exception: `%s'"), s);
  return 0;
  return 0;
}
}
 
 
/* Post-Increment instructions, as well as Store-Immediate instructions, are a
/* Post-Increment instructions, as well as Store-Immediate instructions, are a
   sub-group within load/stor instruction groups.
   sub-group within load/stor instruction groups.
   Therefore, when parsing a Post-Increment/Store-Immediate insn, we have to
   Therefore, when parsing a Post-Increment/Store-Immediate insn, we have to
   advance the instruction pointer to the start of that sub-group (that is, up
   advance the instruction pointer to the start of that sub-group (that is, up
   to the first instruction of that type).
   to the first instruction of that type).
   Otherwise, the insn will be mistakenly identified as of type LD_STOR_INS.  */
   Otherwise, the insn will be mistakenly identified as of type LD_STOR_INS.  */
 
 
static void
static void
handle_LoadStor (char *operands)
handle_LoadStor (char *operands)
{
{
  /* Post-Increment instructions precede Store-Immediate instructions in
  /* Post-Increment instructions precede Store-Immediate instructions in
     CRX instruction table, hence they are handled before.
     CRX instruction table, hence they are handled before.
     This synchronization should be kept.  */
     This synchronization should be kept.  */
 
 
  /* Assuming Post-Increment insn has the following format :
  /* Assuming Post-Increment insn has the following format :
     'MNEMONIC DISP(REG)+, REG' (e.g. 'loadw 12(r5)+, r6').
     'MNEMONIC DISP(REG)+, REG' (e.g. 'loadw 12(r5)+, r6').
     LD_STOR_INS_INC are the only store insns containing a plus sign (+).  */
     LD_STOR_INS_INC are the only store insns containing a plus sign (+).  */
  if (strstr (operands, ")+") != NULL)
  if (strstr (operands, ")+") != NULL)
    {
    {
      while (! IS_INSN_TYPE (LD_STOR_INS_INC))
      while (! IS_INSN_TYPE (LD_STOR_INS_INC))
        instruction++;
        instruction++;
      return;
      return;
    }
    }
 
 
  /* Assuming Store-Immediate insn has the following format :
  /* Assuming Store-Immediate insn has the following format :
     'MNEMONIC $DISP, ...' (e.g. 'storb $1, 12(r5)').
     'MNEMONIC $DISP, ...' (e.g. 'storb $1, 12(r5)').
     STOR_IMM_INS are the only store insns containing a dollar sign ($).  */
     STOR_IMM_INS are the only store insns containing a dollar sign ($).  */
  if (strstr (operands, "$") != NULL)
  if (strstr (operands, "$") != NULL)
    while (! IS_INSN_TYPE (STOR_IMM_INS))
    while (! IS_INSN_TYPE (STOR_IMM_INS))
      instruction++;
      instruction++;
}
}
 
 
/* Top level module where instruction parsing starts.
/* Top level module where instruction parsing starts.
   crx_ins - data structure holds some information.
   crx_ins - data structure holds some information.
   operands - holds the operands part of the whole instruction.  */
   operands - holds the operands part of the whole instruction.  */
 
 
static void
static void
parse_insn (ins *insn, char *operands)
parse_insn (ins *insn, char *operands)
{
{
  int i;
  int i;
 
 
  /* Handle instructions with no operands.  */
  /* Handle instructions with no operands.  */
  for (i = 0; no_op_insn[i] != NULL; i++)
  for (i = 0; no_op_insn[i] != NULL; i++)
  {
  {
    if (streq (no_op_insn[i], instruction->mnemonic))
    if (streq (no_op_insn[i], instruction->mnemonic))
    {
    {
      insn->nargs = 0;
      insn->nargs = 0;
      return;
      return;
    }
    }
  }
  }
 
 
  /* Handle 'excp'/'cinv' instructions.  */
  /* Handle 'excp'/'cinv' instructions.  */
  if (IS_INSN_MNEMONIC ("excp") || IS_INSN_MNEMONIC ("cinv"))
  if (IS_INSN_MNEMONIC ("excp") || IS_INSN_MNEMONIC ("cinv"))
    {
    {
      insn->nargs = 1;
      insn->nargs = 1;
      insn->arg[0].type = arg_ic;
      insn->arg[0].type = arg_ic;
      insn->arg[0].constant = IS_INSN_MNEMONIC ("excp") ?
      insn->arg[0].constant = IS_INSN_MNEMONIC ("excp") ?
        gettrap (operands) : get_cinv_parameters (operands);
        gettrap (operands) : get_cinv_parameters (operands);
      insn->arg[0].X_op = O_constant;
      insn->arg[0].X_op = O_constant;
      return;
      return;
    }
    }
 
 
  /* Handle load/stor unique instructions before parsing.  */
  /* Handle load/stor unique instructions before parsing.  */
  if (IS_INSN_TYPE (LD_STOR_INS))
  if (IS_INSN_TYPE (LD_STOR_INS))
    handle_LoadStor (operands);
    handle_LoadStor (operands);
 
 
  if (operands != NULL)
  if (operands != NULL)
    parse_operands (insn, operands);
    parse_operands (insn, operands);
}
}
 
 
/* Cinv instruction requires special handling.  */
/* Cinv instruction requires special handling.  */
 
 
static int
static int
get_cinv_parameters (char * operand)
get_cinv_parameters (char * operand)
{
{
  char *p = operand;
  char *p = operand;
  int d_used = 0, i_used = 0, u_used = 0, b_used = 0;
  int d_used = 0, i_used = 0, u_used = 0, b_used = 0;
 
 
  while (*++p != ']')
  while (*++p != ']')
    {
    {
      if (*p == ',' || *p == ' ')
      if (*p == ',' || *p == ' ')
        continue;
        continue;
 
 
      if (*p == 'd')
      if (*p == 'd')
        d_used = 1;
        d_used = 1;
      else if (*p == 'i')
      else if (*p == 'i')
        i_used = 1;
        i_used = 1;
      else if (*p == 'u')
      else if (*p == 'u')
        u_used = 1;
        u_used = 1;
      else if (*p == 'b')
      else if (*p == 'b')
        b_used = 1;
        b_used = 1;
      else
      else
        as_bad (_("Illegal `cinv' parameter: `%c'"), *p);
        as_bad (_("Illegal `cinv' parameter: `%c'"), *p);
    }
    }
 
 
  return ((b_used ? 8 : 0)
  return ((b_used ? 8 : 0)
        + (d_used ? 4 : 0)
        + (d_used ? 4 : 0)
        + (i_used ? 2 : 0)
        + (i_used ? 2 : 0)
        + (u_used ? 1 : 0));
        + (u_used ? 1 : 0));
}
}
 
 
/* Retrieve the opcode image of a given register.
/* Retrieve the opcode image of a given register.
   If the register is illegal for the current instruction,
   If the register is illegal for the current instruction,
   issue an error.  */
   issue an error.  */
 
 
static int
static int
getreg_image (reg r)
getreg_image (reg r)
{
{
  const reg_entry *reg;
  const reg_entry *reg;
  char *reg_name;
  char *reg_name;
  int is_procreg = 0; /* Nonzero means argument should be processor reg.  */
  int is_procreg = 0; /* Nonzero means argument should be processor reg.  */
 
 
  if (((IS_INSN_MNEMONIC ("mtpr")) && (cur_arg_num == 1))
  if (((IS_INSN_MNEMONIC ("mtpr")) && (cur_arg_num == 1))
      || ((IS_INSN_MNEMONIC ("mfpr")) && (cur_arg_num == 0)) )
      || ((IS_INSN_MNEMONIC ("mfpr")) && (cur_arg_num == 0)) )
    is_procreg = 1;
    is_procreg = 1;
 
 
  /* Check whether the register is in registers table.  */
  /* Check whether the register is in registers table.  */
  if (r < MAX_REG)
  if (r < MAX_REG)
    reg = &crx_regtab[r];
    reg = &crx_regtab[r];
  /* Check whether the register is in coprocessor registers table.  */
  /* Check whether the register is in coprocessor registers table.  */
  else if (r < MAX_COPREG)
  else if (r < MAX_COPREG)
    reg = &crx_copregtab[r-MAX_REG];
    reg = &crx_copregtab[r-MAX_REG];
  /* Register not found.  */
  /* Register not found.  */
  else
  else
    {
    {
      as_bad (_("Unknown register: `%d'"), r);
      as_bad (_("Unknown register: `%d'"), r);
      return 0;
      return 0;
    }
    }
 
 
  reg_name = reg->name;
  reg_name = reg->name;
 
 
/* Issue a error message when register is illegal.  */
/* Issue a error message when register is illegal.  */
#define IMAGE_ERR \
#define IMAGE_ERR \
  as_bad (_("Illegal register (`%s') in Instruction: `%s'"), \
  as_bad (_("Illegal register (`%s') in Instruction: `%s'"), \
            reg_name, ins_parse);                            \
            reg_name, ins_parse);                            \
  break;
  break;
 
 
  switch (reg->type)
  switch (reg->type)
  {
  {
    case CRX_U_REGTYPE:
    case CRX_U_REGTYPE:
      if (is_procreg || (instruction->flags & USER_REG))
      if (is_procreg || (instruction->flags & USER_REG))
        return reg->image;
        return reg->image;
      else
      else
        IMAGE_ERR;
        IMAGE_ERR;
 
 
    case CRX_CFG_REGTYPE:
    case CRX_CFG_REGTYPE:
      if (is_procreg)
      if (is_procreg)
        return reg->image;
        return reg->image;
      else
      else
        IMAGE_ERR;
        IMAGE_ERR;
 
 
    case CRX_R_REGTYPE:
    case CRX_R_REGTYPE:
      if (! is_procreg)
      if (! is_procreg)
        return reg->image;
        return reg->image;
      else
      else
        IMAGE_ERR;
        IMAGE_ERR;
 
 
    case CRX_C_REGTYPE:
    case CRX_C_REGTYPE:
    case CRX_CS_REGTYPE:
    case CRX_CS_REGTYPE:
      return reg->image;
      return reg->image;
      break;
      break;
 
 
    default:
    default:
      IMAGE_ERR;
      IMAGE_ERR;
  }
  }
 
 
  return 0;
  return 0;
}
}
 
 
/* Routine used to represent integer X using NBITS bits.  */
/* Routine used to represent integer X using NBITS bits.  */
 
 
static long
static long
getconstant (long x, int nbits)
getconstant (long x, int nbits)
{
{
  /* The following expression avoids overflow if
  /* The following expression avoids overflow if
     'nbits' is the number of bits in 'bfd_vma'.  */
     'nbits' is the number of bits in 'bfd_vma'.  */
  return (x & ((((1 << (nbits - 1)) - 1) << 1) | 1));
  return (x & ((((1 << (nbits - 1)) - 1) << 1) | 1));
}
}
 
 
/* Print a constant value to 'output_opcode':
/* Print a constant value to 'output_opcode':
   ARG holds the operand's type and value.
   ARG holds the operand's type and value.
   SHIFT represents the location of the operand to be print into.
   SHIFT represents the location of the operand to be print into.
   NBITS determines the size (in bits) of the constant.  */
   NBITS determines the size (in bits) of the constant.  */
 
 
static void
static void
print_constant (int nbits, int shift, argument *arg)
print_constant (int nbits, int shift, argument *arg)
{
{
  unsigned long mask = 0;
  unsigned long mask = 0;
 
 
  long constant = getconstant (arg->constant, nbits);
  long constant = getconstant (arg->constant, nbits);
 
 
  switch (nbits)
  switch (nbits)
  {
  {
    case 32:
    case 32:
    case 28:
    case 28:
    case 24:
    case 24:
    case 22:
    case 22:
      /* mask the upper part of the constant, that is, the bits
      /* mask the upper part of the constant, that is, the bits
         going to the lowest byte of output_opcode[0].
         going to the lowest byte of output_opcode[0].
         The upper part of output_opcode[1] is always filled,
         The upper part of output_opcode[1] is always filled,
         therefore it is always masked with 0xFFFF.  */
         therefore it is always masked with 0xFFFF.  */
      mask = (1 << (nbits - 16)) - 1;
      mask = (1 << (nbits - 16)) - 1;
      /* Divide the constant between two consecutive words :
      /* Divide the constant between two consecutive words :
                 0         1         2         3
                 0         1         2         3
            +---------+---------+---------+---------+
            +---------+---------+---------+---------+
            |         | X X X X | X X X X |         |
            |         | X X X X | X X X X |         |
            +---------+---------+---------+---------+
            +---------+---------+---------+---------+
              output_opcode[0]    output_opcode[1]     */
              output_opcode[0]    output_opcode[1]     */
 
 
      CRX_PRINT (0, (constant >> WORD_SHIFT) & mask, 0);
      CRX_PRINT (0, (constant >> WORD_SHIFT) & mask, 0);
      CRX_PRINT (1, (constant & 0xFFFF), WORD_SHIFT);
      CRX_PRINT (1, (constant & 0xFFFF), WORD_SHIFT);
      break;
      break;
 
 
    case 16:
    case 16:
    case 12:
    case 12:
      /* Special case - in arg_cr, the SHIFT represents the location
      /* Special case - in arg_cr, the SHIFT represents the location
         of the REGISTER, not the constant, which is itself not shifted.  */
         of the REGISTER, not the constant, which is itself not shifted.  */
      if (arg->type == arg_cr)
      if (arg->type == arg_cr)
        {
        {
          CRX_PRINT (0, constant,  0);
          CRX_PRINT (0, constant,  0);
          break;
          break;
        }
        }
 
 
      /* When instruction size is 3 and 'shift' is 16, a 16-bit constant is
      /* When instruction size is 3 and 'shift' is 16, a 16-bit constant is
         always filling the upper part of output_opcode[1]. If we mistakenly
         always filling the upper part of output_opcode[1]. If we mistakenly
         write it to output_opcode[0], the constant prefix (that is, 'match')
         write it to output_opcode[0], the constant prefix (that is, 'match')
         will be overridden.
         will be overridden.
                 0         1         2         3
                 0         1         2         3
            +---------+---------+---------+---------+
            +---------+---------+---------+---------+
            | 'match' |         | X X X X |         |
            | 'match' |         | X X X X |         |
            +---------+---------+---------+---------+
            +---------+---------+---------+---------+
              output_opcode[0]    output_opcode[1]     */
              output_opcode[0]    output_opcode[1]     */
 
 
      if ((instruction->size > 2) && (shift == WORD_SHIFT))
      if ((instruction->size > 2) && (shift == WORD_SHIFT))
        CRX_PRINT (1, constant, WORD_SHIFT);
        CRX_PRINT (1, constant, WORD_SHIFT);
      else
      else
        CRX_PRINT (0, constant, shift);
        CRX_PRINT (0, constant, shift);
      break;
      break;
 
 
    default:
    default:
      CRX_PRINT (0, constant,  shift);
      CRX_PRINT (0, constant,  shift);
      break;
      break;
  }
  }
}
}
 
 
/* Print an operand to 'output_opcode', which later on will be
/* Print an operand to 'output_opcode', which later on will be
   printed to the object file:
   printed to the object file:
   ARG holds the operand's type, size and value.
   ARG holds the operand's type, size and value.
   SHIFT represents the printing location of operand.
   SHIFT represents the printing location of operand.
   NBITS determines the size (in bits) of a constant operand.  */
   NBITS determines the size (in bits) of a constant operand.  */
 
 
static void
static void
print_operand (int nbits, int shift, argument *arg)
print_operand (int nbits, int shift, argument *arg)
{
{
  switch (arg->type)
  switch (arg->type)
    {
    {
    case arg_r:
    case arg_r:
      CRX_PRINT (0, getreg_image (arg->r), shift);
      CRX_PRINT (0, getreg_image (arg->r), shift);
      break;
      break;
 
 
    case arg_copr:
    case arg_copr:
      if (arg->cr < c0 || arg->cr > c15)
      if (arg->cr < c0 || arg->cr > c15)
        as_bad (_("Illegal Co-processor register in Instruction `%s' "),
        as_bad (_("Illegal Co-processor register in Instruction `%s' "),
                ins_parse);
                ins_parse);
      CRX_PRINT (0, getreg_image (arg->cr), shift);
      CRX_PRINT (0, getreg_image (arg->cr), shift);
      break;
      break;
 
 
    case arg_copsr:
    case arg_copsr:
      if (arg->cr < cs0 || arg->cr > cs15)
      if (arg->cr < cs0 || arg->cr > cs15)
        as_bad (_("Illegal Co-processor special register in Instruction `%s' "),
        as_bad (_("Illegal Co-processor special register in Instruction `%s' "),
                ins_parse);
                ins_parse);
      CRX_PRINT (0, getreg_image (arg->cr), shift);
      CRX_PRINT (0, getreg_image (arg->cr), shift);
      break;
      break;
 
 
    case arg_idxr:
    case arg_idxr:
      /*    16      12        8    6         0
      /*    16      12        8    6         0
            +--------------------------------+
            +--------------------------------+
            | r_base | r_idx  | scl|  disp   |
            | r_base | r_idx  | scl|  disp   |
            +--------------------------------+    */
            +--------------------------------+    */
      CRX_PRINT (0, getreg_image (arg->r), 12);
      CRX_PRINT (0, getreg_image (arg->r), 12);
      CRX_PRINT (0, getreg_image (arg->i_r), 8);
      CRX_PRINT (0, getreg_image (arg->i_r), 8);
      CRX_PRINT (0, arg->scale, 6);
      CRX_PRINT (0, arg->scale, 6);
    case arg_ic:
    case arg_ic:
    case arg_c:
    case arg_c:
      print_constant (nbits, shift, arg);
      print_constant (nbits, shift, arg);
      break;
      break;
 
 
    case arg_rbase:
    case arg_rbase:
      CRX_PRINT (0, getreg_image (arg->r), shift);
      CRX_PRINT (0, getreg_image (arg->r), shift);
      break;
      break;
 
 
    case arg_cr:
    case arg_cr:
      /* case base_cst4.  */
      /* case base_cst4.  */
      if (instruction->flags & DISPU4MAP)
      if (instruction->flags & DISPU4MAP)
        print_constant (nbits, shift + REG_SIZE, arg);
        print_constant (nbits, shift + REG_SIZE, arg);
      else
      else
        /* rbase_disps<NN> and other such cases.  */
        /* rbase_disps<NN> and other such cases.  */
        print_constant (nbits, shift, arg);
        print_constant (nbits, shift, arg);
      /* Add the register argument to the output_opcode.  */
      /* Add the register argument to the output_opcode.  */
      CRX_PRINT (0, getreg_image (arg->r), shift);
      CRX_PRINT (0, getreg_image (arg->r), shift);
      break;
      break;
 
 
    default:
    default:
      break;
      break;
    }
    }
}
}
 
 
/* Retrieve the number of operands for the current assembled instruction.  */
/* Retrieve the number of operands for the current assembled instruction.  */
 
 
static int
static int
get_number_of_operands (void)
get_number_of_operands (void)
{
{
  int i;
  int i;
 
 
  for (i = 0; instruction->operands[i].op_type && i < MAX_OPERANDS; i++)
  for (i = 0; instruction->operands[i].op_type && i < MAX_OPERANDS; i++)
    ;
    ;
  return i;
  return i;
}
}
 
 
/* Verify that the number NUM can be represented in BITS bits (that is,
/* Verify that the number NUM can be represented in BITS bits (that is,
   within its permitted range), based on the instruction's FLAGS.
   within its permitted range), based on the instruction's FLAGS.
   If UPDATE is nonzero, update the value of NUM if necessary.
   If UPDATE is nonzero, update the value of NUM if necessary.
   Return OP_LEGAL upon success, actual error type upon failure.  */
   Return OP_LEGAL upon success, actual error type upon failure.  */
 
 
static op_err
static op_err
check_range (long *num, int bits, int unsigned flags, int update)
check_range (long *num, int bits, int unsigned flags, int update)
{
{
  long min, max;
  long min, max;
  int retval = OP_LEGAL;
  int retval = OP_LEGAL;
  int bin;
  int bin;
  long upper_64kb = 0xFFFF0000;
  long upper_64kb = 0xFFFF0000;
  long value = *num;
  long value = *num;
 
 
  /* For hosts witah longs bigger than 32-bits make sure that the top
  /* For hosts witah longs bigger than 32-bits make sure that the top
     bits of a 32-bit negative value read in by the parser are set,
     bits of a 32-bit negative value read in by the parser are set,
     so that the correct comparisons are made.  */
     so that the correct comparisons are made.  */
  if (value & 0x80000000)
  if (value & 0x80000000)
    value |= (-1L << 31);
    value |= (-1L << 31);
 
 
  /* Verify operand value is even.  */
  /* Verify operand value is even.  */
  if (flags & OP_EVEN)
  if (flags & OP_EVEN)
    {
    {
      if (value % 2)
      if (value % 2)
        return OP_NOT_EVEN;
        return OP_NOT_EVEN;
    }
    }
 
 
  if (flags & OP_UPPER_64KB)
  if (flags & OP_UPPER_64KB)
    {
    {
      /* Check if value is to be mapped to upper 64 KB memory area.  */
      /* Check if value is to be mapped to upper 64 KB memory area.  */
      if ((value & upper_64kb) == upper_64kb)
      if ((value & upper_64kb) == upper_64kb)
        {
        {
          value -= upper_64kb;
          value -= upper_64kb;
          if (update)
          if (update)
            *num = value;
            *num = value;
        }
        }
      else
      else
        return OP_NOT_UPPER_64KB;
        return OP_NOT_UPPER_64KB;
    }
    }
 
 
  if (flags & OP_SHIFT)
  if (flags & OP_SHIFT)
    {
    {
      value >>= 1;
      value >>= 1;
      if (update)
      if (update)
        *num = value;
        *num = value;
    }
    }
  else if (flags & OP_SHIFT_DEC)
  else if (flags & OP_SHIFT_DEC)
    {
    {
      value = (value >> 1) - 1;
      value = (value >> 1) - 1;
      if (update)
      if (update)
        *num = value;
        *num = value;
    }
    }
 
 
  if (flags & OP_ESC)
  if (flags & OP_ESC)
    {
    {
      /* 0x7e and 0x7f are reserved escape sequences of dispe9.  */
      /* 0x7e and 0x7f are reserved escape sequences of dispe9.  */
      if (value == 0x7e || value == 0x7f)
      if (value == 0x7e || value == 0x7f)
        return OP_OUT_OF_RANGE;
        return OP_OUT_OF_RANGE;
    }
    }
 
 
  if (flags & OP_DISPU4)
  if (flags & OP_DISPU4)
    {
    {
      int is_dispu4 = 0;
      int is_dispu4 = 0;
 
 
      int mul = (instruction->flags & DISPUB4) ? 1
      int mul = (instruction->flags & DISPUB4) ? 1
                : (instruction->flags & DISPUW4) ? 2
                : (instruction->flags & DISPUW4) ? 2
                : (instruction->flags & DISPUD4) ? 4 : 0;
                : (instruction->flags & DISPUD4) ? 4 : 0;
 
 
      for (bin = 0; bin < cst4_maps; bin++)
      for (bin = 0; bin < cst4_maps; bin++)
        {
        {
          if (value == (mul * bin))
          if (value == (mul * bin))
            {
            {
              is_dispu4 = 1;
              is_dispu4 = 1;
              if (update)
              if (update)
                *num = bin;
                *num = bin;
              break;
              break;
            }
            }
        }
        }
      if (!is_dispu4)
      if (!is_dispu4)
        retval = OP_ILLEGAL_DISPU4;
        retval = OP_ILLEGAL_DISPU4;
    }
    }
  else if (flags & OP_CST4)
  else if (flags & OP_CST4)
    {
    {
      int is_cst4 = 0;
      int is_cst4 = 0;
 
 
      for (bin = 0; bin < cst4_maps; bin++)
      for (bin = 0; bin < cst4_maps; bin++)
        {
        {
          if (value == cst4_map[bin])
          if (value == cst4_map[bin])
            {
            {
              is_cst4 = 1;
              is_cst4 = 1;
              if (update)
              if (update)
                *num = bin;
                *num = bin;
              break;
              break;
            }
            }
        }
        }
      if (!is_cst4)
      if (!is_cst4)
        retval = OP_ILLEGAL_CST4;
        retval = OP_ILLEGAL_CST4;
    }
    }
  else if (flags & OP_SIGNED)
  else if (flags & OP_SIGNED)
    {
    {
      max = (1 << (bits - 1)) - 1;
      max = (1 << (bits - 1)) - 1;
      min = - (1 << (bits - 1));
      min = - (1 << (bits - 1));
      if ((value > max) || (value < min))
      if ((value > max) || (value < min))
        retval = OP_OUT_OF_RANGE;
        retval = OP_OUT_OF_RANGE;
    }
    }
  else if (flags & OP_UNSIGNED)
  else if (flags & OP_UNSIGNED)
    {
    {
      max = ((((1 << (bits - 1)) - 1) << 1) | 1);
      max = ((((1 << (bits - 1)) - 1) << 1) | 1);
      min = 0;
      min = 0;
      if (((unsigned long) value > (unsigned long) max)
      if (((unsigned long) value > (unsigned long) max)
            || ((unsigned long) value < (unsigned long) min))
            || ((unsigned long) value < (unsigned long) min))
        retval = OP_OUT_OF_RANGE;
        retval = OP_OUT_OF_RANGE;
    }
    }
  return retval;
  return retval;
}
}
 
 
/* Assemble a single instruction:
/* Assemble a single instruction:
   INSN is already parsed (that is, all operand values and types are set).
   INSN is already parsed (that is, all operand values and types are set).
   For instruction to be assembled, we need to find an appropriate template in
   For instruction to be assembled, we need to find an appropriate template in
   the instruction table, meeting the following conditions:
   the instruction table, meeting the following conditions:
    1: Has the same number of operands.
    1: Has the same number of operands.
    2: Has the same operand types.
    2: Has the same operand types.
    3: Each operand size is sufficient to represent the instruction's values.
    3: Each operand size is sufficient to represent the instruction's values.
   Returns 1 upon success, 0 upon failure.  */
   Returns 1 upon success, 0 upon failure.  */
 
 
static int
static int
assemble_insn (char *mnemonic, ins *insn)
assemble_insn (char *mnemonic, ins *insn)
{
{
  /* Type of each operand in the current template.  */
  /* Type of each operand in the current template.  */
  argtype cur_type[MAX_OPERANDS];
  argtype cur_type[MAX_OPERANDS];
  /* Size (in bits) of each operand in the current template.  */
  /* Size (in bits) of each operand in the current template.  */
  unsigned int cur_size[MAX_OPERANDS];
  unsigned int cur_size[MAX_OPERANDS];
  /* Flags of each operand in the current template.  */
  /* Flags of each operand in the current template.  */
  unsigned int cur_flags[MAX_OPERANDS];
  unsigned int cur_flags[MAX_OPERANDS];
  /* Instruction type to match.  */
  /* Instruction type to match.  */
  unsigned int ins_type;
  unsigned int ins_type;
  /* Boolean flag to mark whether a match was found.  */
  /* Boolean flag to mark whether a match was found.  */
  int match = 0;
  int match = 0;
  int i;
  int i;
  /* Nonzero if an instruction with same number of operands was found.  */
  /* Nonzero if an instruction with same number of operands was found.  */
  int found_same_number_of_operands = 0;
  int found_same_number_of_operands = 0;
  /* Nonzero if an instruction with same argument types was found.  */
  /* Nonzero if an instruction with same argument types was found.  */
  int found_same_argument_types = 0;
  int found_same_argument_types = 0;
  /* Nonzero if a constant was found within the required range.  */
  /* Nonzero if a constant was found within the required range.  */
  int found_const_within_range  = 0;
  int found_const_within_range  = 0;
  /* Argument number of an operand with invalid type.  */
  /* Argument number of an operand with invalid type.  */
  int invalid_optype = -1;
  int invalid_optype = -1;
  /* Argument number of an operand with invalid constant value.  */
  /* Argument number of an operand with invalid constant value.  */
  int invalid_const  = -1;
  int invalid_const  = -1;
  /* Operand error (used for issuing various constant error messages).  */
  /* Operand error (used for issuing various constant error messages).  */
  op_err op_error, const_err = OP_LEGAL;
  op_err op_error, const_err = OP_LEGAL;
 
 
/* Retrieve data (based on FUNC) for each operand of a given instruction.  */
/* Retrieve data (based on FUNC) for each operand of a given instruction.  */
#define GET_CURRENT_DATA(FUNC, ARRAY)                             \
#define GET_CURRENT_DATA(FUNC, ARRAY)                             \
  for (i = 0; i < insn->nargs; i++)                                \
  for (i = 0; i < insn->nargs; i++)                                \
    ARRAY[i] = FUNC (instruction->operands[i].op_type)
    ARRAY[i] = FUNC (instruction->operands[i].op_type)
 
 
#define GET_CURRENT_TYPE    GET_CURRENT_DATA(get_optype, cur_type)
#define GET_CURRENT_TYPE    GET_CURRENT_DATA(get_optype, cur_type)
#define GET_CURRENT_SIZE    GET_CURRENT_DATA(get_opbits, cur_size)
#define GET_CURRENT_SIZE    GET_CURRENT_DATA(get_opbits, cur_size)
#define GET_CURRENT_FLAGS   GET_CURRENT_DATA(get_opflags, cur_flags)
#define GET_CURRENT_FLAGS   GET_CURRENT_DATA(get_opflags, cur_flags)
 
 
  /* Instruction has no operands -> only copy the constant opcode.   */
  /* Instruction has no operands -> only copy the constant opcode.   */
  if (insn->nargs == 0)
  if (insn->nargs == 0)
    {
    {
      output_opcode[0] = BIN (instruction->match, instruction->match_bits);
      output_opcode[0] = BIN (instruction->match, instruction->match_bits);
      return 1;
      return 1;
    }
    }
 
 
  /* In some case, same mnemonic can appear with different instruction types.
  /* In some case, same mnemonic can appear with different instruction types.
     For example, 'storb' is supported with 3 different types :
     For example, 'storb' is supported with 3 different types :
     LD_STOR_INS, LD_STOR_INS_INC, STOR_IMM_INS.
     LD_STOR_INS, LD_STOR_INS_INC, STOR_IMM_INS.
     We assume that when reaching this point, the instruction type was
     We assume that when reaching this point, the instruction type was
     pre-determined. We need to make sure that the type stays the same
     pre-determined. We need to make sure that the type stays the same
     during a search for matching instruction.  */
     during a search for matching instruction.  */
  ins_type = CRX_INS_TYPE(instruction->flags);
  ins_type = CRX_INS_TYPE(instruction->flags);
 
 
  while (/* Check that match is still not found.  */
  while (/* Check that match is still not found.  */
         match != 1
         match != 1
         /* Check we didn't get to end of table.  */
         /* Check we didn't get to end of table.  */
         && instruction->mnemonic != NULL
         && instruction->mnemonic != NULL
         /* Check that the actual mnemonic is still available.  */
         /* Check that the actual mnemonic is still available.  */
         && IS_INSN_MNEMONIC (mnemonic)
         && IS_INSN_MNEMONIC (mnemonic)
         /* Check that the instruction type wasn't changed.  */
         /* Check that the instruction type wasn't changed.  */
         && IS_INSN_TYPE(ins_type))
         && IS_INSN_TYPE(ins_type))
    {
    {
      /* Check whether number of arguments is legal.  */
      /* Check whether number of arguments is legal.  */
      if (get_number_of_operands () != insn->nargs)
      if (get_number_of_operands () != insn->nargs)
        goto next_insn;
        goto next_insn;
      found_same_number_of_operands = 1;
      found_same_number_of_operands = 1;
 
 
      /* Initialize arrays with data of each operand in current template.  */
      /* Initialize arrays with data of each operand in current template.  */
      GET_CURRENT_TYPE;
      GET_CURRENT_TYPE;
      GET_CURRENT_SIZE;
      GET_CURRENT_SIZE;
      GET_CURRENT_FLAGS;
      GET_CURRENT_FLAGS;
 
 
      /* Check for type compatibility.  */
      /* Check for type compatibility.  */
      for (i = 0; i < insn->nargs; i++)
      for (i = 0; i < insn->nargs; i++)
        {
        {
          if (cur_type[i] != insn->arg[i].type)
          if (cur_type[i] != insn->arg[i].type)
            {
            {
              if (invalid_optype == -1)
              if (invalid_optype == -1)
                invalid_optype = i + 1;
                invalid_optype = i + 1;
              goto next_insn;
              goto next_insn;
            }
            }
        }
        }
      found_same_argument_types = 1;
      found_same_argument_types = 1;
 
 
      for (i = 0; i < insn->nargs; i++)
      for (i = 0; i < insn->nargs; i++)
        {
        {
          /* Reverse the operand indices for certain opcodes:
          /* Reverse the operand indices for certain opcodes:
             Index 0      -->> 1
             Index 0      -->> 1
             Index 1      -->> 0
             Index 1      -->> 0
             Other index  -->> stays the same.  */
             Other index  -->> stays the same.  */
          int j = instruction->flags & REVERSE_MATCH ?
          int j = instruction->flags & REVERSE_MATCH ?
                  i == 0 ? 1 :
                  i == 0 ? 1 :
                  i == 1 ? 0 : i :
                  i == 1 ? 0 : i :
                  i;
                  i;
 
 
          /* Only check range - don't update the constant's value, since the
          /* Only check range - don't update the constant's value, since the
             current instruction may not be the last we try to match.
             current instruction may not be the last we try to match.
             The constant's value will be updated later, right before printing
             The constant's value will be updated later, right before printing
             it to the object file.  */
             it to the object file.  */
          if ((insn->arg[j].X_op == O_constant)
          if ((insn->arg[j].X_op == O_constant)
               && (op_error = check_range (&insn->arg[j].constant, cur_size[j],
               && (op_error = check_range (&insn->arg[j].constant, cur_size[j],
                                           cur_flags[j], 0)))
                                           cur_flags[j], 0)))
            {
            {
              if (invalid_const == -1)
              if (invalid_const == -1)
              {
              {
                invalid_const = j + 1;
                invalid_const = j + 1;
                const_err = op_error;
                const_err = op_error;
              }
              }
              goto next_insn;
              goto next_insn;
            }
            }
          /* For symbols, we make sure the relocation size (which was already
          /* For symbols, we make sure the relocation size (which was already
             determined) is sufficient.  */
             determined) is sufficient.  */
          else if ((insn->arg[j].X_op == O_symbol)
          else if ((insn->arg[j].X_op == O_symbol)
                    && ((bfd_reloc_type_lookup (stdoutput, insn->rtype))->bitsize
                    && ((bfd_reloc_type_lookup (stdoutput, insn->rtype))->bitsize
                         > cur_size[j]))
                         > cur_size[j]))
                  goto next_insn;
                  goto next_insn;
        }
        }
      found_const_within_range = 1;
      found_const_within_range = 1;
 
 
      /* If we got till here -> Full match is found.  */
      /* If we got till here -> Full match is found.  */
      match = 1;
      match = 1;
      break;
      break;
 
 
/* Try again with next instruction.  */
/* Try again with next instruction.  */
next_insn:
next_insn:
      instruction++;
      instruction++;
    }
    }
 
 
  if (!match)
  if (!match)
    {
    {
      /* We haven't found a match - instruction can't be assembled.  */
      /* We haven't found a match - instruction can't be assembled.  */
      if (!found_same_number_of_operands)
      if (!found_same_number_of_operands)
        as_bad (_("Incorrect number of operands"));
        as_bad (_("Incorrect number of operands"));
      else if (!found_same_argument_types)
      else if (!found_same_argument_types)
        as_bad (_("Illegal type of operand (arg %d)"), invalid_optype);
        as_bad (_("Illegal type of operand (arg %d)"), invalid_optype);
      else if (!found_const_within_range)
      else if (!found_const_within_range)
      {
      {
        switch (const_err)
        switch (const_err)
        {
        {
        case OP_OUT_OF_RANGE:
        case OP_OUT_OF_RANGE:
          as_bad (_("Operand out of range (arg %d)"), invalid_const);
          as_bad (_("Operand out of range (arg %d)"), invalid_const);
          break;
          break;
        case OP_NOT_EVEN:
        case OP_NOT_EVEN:
          as_bad (_("Operand has odd displacement (arg %d)"), invalid_const);
          as_bad (_("Operand has odd displacement (arg %d)"), invalid_const);
          break;
          break;
        case OP_ILLEGAL_DISPU4:
        case OP_ILLEGAL_DISPU4:
          as_bad (_("Invalid DISPU4 operand value (arg %d)"), invalid_const);
          as_bad (_("Invalid DISPU4 operand value (arg %d)"), invalid_const);
          break;
          break;
        case OP_ILLEGAL_CST4:
        case OP_ILLEGAL_CST4:
          as_bad (_("Invalid CST4 operand value (arg %d)"), invalid_const);
          as_bad (_("Invalid CST4 operand value (arg %d)"), invalid_const);
          break;
          break;
        case OP_NOT_UPPER_64KB:
        case OP_NOT_UPPER_64KB:
          as_bad (_("Operand value is not within upper 64 KB (arg %d)"),
          as_bad (_("Operand value is not within upper 64 KB (arg %d)"),
                    invalid_const);
                    invalid_const);
          break;
          break;
        default:
        default:
          as_bad (_("Illegal operand (arg %d)"), invalid_const);
          as_bad (_("Illegal operand (arg %d)"), invalid_const);
          break;
          break;
        }
        }
      }
      }
 
 
      return 0;
      return 0;
    }
    }
  else
  else
    /* Full match - print the encoding to output file.  */
    /* Full match - print the encoding to output file.  */
    {
    {
      /* Make further checkings (such that couldn't be made earlier).
      /* Make further checkings (such that couldn't be made earlier).
         Warn the user if necessary.  */
         Warn the user if necessary.  */
      warn_if_needed (insn);
      warn_if_needed (insn);
 
 
      /* Check whether we need to adjust the instruction pointer.  */
      /* Check whether we need to adjust the instruction pointer.  */
      if (adjust_if_needed (insn))
      if (adjust_if_needed (insn))
        /* If instruction pointer was adjusted, we need to update
        /* If instruction pointer was adjusted, we need to update
           the size of the current template operands.  */
           the size of the current template operands.  */
        GET_CURRENT_SIZE;
        GET_CURRENT_SIZE;
 
 
      for (i = 0; i < insn->nargs; i++)
      for (i = 0; i < insn->nargs; i++)
        {
        {
          int j = instruction->flags & REVERSE_MATCH ?
          int j = instruction->flags & REVERSE_MATCH ?
                  i == 0 ? 1 :
                  i == 0 ? 1 :
                  i == 1 ? 0 : i :
                  i == 1 ? 0 : i :
                  i;
                  i;
 
 
          /* This time, update constant value before printing it.  */
          /* This time, update constant value before printing it.  */
          if ((insn->arg[j].X_op == O_constant)
          if ((insn->arg[j].X_op == O_constant)
               && (check_range (&insn->arg[j].constant, cur_size[j],
               && (check_range (&insn->arg[j].constant, cur_size[j],
                                cur_flags[j], 1) != OP_LEGAL))
                                cur_flags[j], 1) != OP_LEGAL))
              as_fatal (_("Illegal operand (arg %d)"), j+1);
              as_fatal (_("Illegal operand (arg %d)"), j+1);
        }
        }
 
 
      /* First, copy the instruction's opcode.  */
      /* First, copy the instruction's opcode.  */
      output_opcode[0] = BIN (instruction->match, instruction->match_bits);
      output_opcode[0] = BIN (instruction->match, instruction->match_bits);
 
 
      for (i = 0; i < insn->nargs; i++)
      for (i = 0; i < insn->nargs; i++)
        {
        {
          cur_arg_num = i;
          cur_arg_num = i;
          print_operand (cur_size[i], instruction->operands[i].shift,
          print_operand (cur_size[i], instruction->operands[i].shift,
                         &insn->arg[i]);
                         &insn->arg[i]);
        }
        }
    }
    }
 
 
  return 1;
  return 1;
}
}
 
 
/* Bunch of error checkings.
/* Bunch of error checkings.
   The checks are made after a matching instruction was found.  */
   The checks are made after a matching instruction was found.  */
 
 
void
void
warn_if_needed (ins *insn)
warn_if_needed (ins *insn)
{
{
  /* If the post-increment address mode is used and the load/store
  /* If the post-increment address mode is used and the load/store
     source register is the same as rbase, the result of the
     source register is the same as rbase, the result of the
     instruction is undefined.  */
     instruction is undefined.  */
  if (IS_INSN_TYPE (LD_STOR_INS_INC))
  if (IS_INSN_TYPE (LD_STOR_INS_INC))
    {
    {
      /* Enough to verify that one of the arguments is a simple reg.  */
      /* Enough to verify that one of the arguments is a simple reg.  */
      if ((insn->arg[0].type == arg_r) || (insn->arg[1].type == arg_r))
      if ((insn->arg[0].type == arg_r) || (insn->arg[1].type == arg_r))
        if (insn->arg[0].r == insn->arg[1].r)
        if (insn->arg[0].r == insn->arg[1].r)
          as_bad (_("Same src/dest register is used (`r%d'), result is undefined"),
          as_bad (_("Same src/dest register is used (`r%d'), result is undefined"),
                   insn->arg[0].r);
                   insn->arg[0].r);
    }
    }
 
 
  /* Some instruction assume the stack pointer as rptr operand.
  /* Some instruction assume the stack pointer as rptr operand.
     Issue an error when the register to be loaded is also SP.  */
     Issue an error when the register to be loaded is also SP.  */
  if (instruction->flags & NO_SP)
  if (instruction->flags & NO_SP)
    {
    {
      if (getreg_image (insn->arg[0].r) == getreg_image (sp))
      if (getreg_image (insn->arg[0].r) == getreg_image (sp))
        as_bad (_("`%s' has undefined result"), ins_parse);
        as_bad (_("`%s' has undefined result"), ins_parse);
    }
    }
 
 
  /* If the rptr register is specified as one of the registers to be loaded,
  /* If the rptr register is specified as one of the registers to be loaded,
     the final contents of rptr are undefined. Thus, we issue an error.  */
     the final contents of rptr are undefined. Thus, we issue an error.  */
  if (instruction->flags & NO_RPTR)
  if (instruction->flags & NO_RPTR)
    {
    {
      if ((1 << getreg_image (insn->arg[0].r)) & insn->arg[1].constant)
      if ((1 << getreg_image (insn->arg[0].r)) & insn->arg[1].constant)
        as_bad (_("Same src/dest register is used (`r%d'), result is undefined"),
        as_bad (_("Same src/dest register is used (`r%d'), result is undefined"),
         getreg_image (insn->arg[0].r));
         getreg_image (insn->arg[0].r));
    }
    }
}
}
 
 
/* In some cases, we need to adjust the instruction pointer although a
/* In some cases, we need to adjust the instruction pointer although a
   match was already found. Here, we gather all these cases.
   match was already found. Here, we gather all these cases.
   Returns 1 if instruction pointer was adjusted, otherwise 0.  */
   Returns 1 if instruction pointer was adjusted, otherwise 0.  */
 
 
int
int
adjust_if_needed (ins *insn)
adjust_if_needed (ins *insn)
{
{
  int ret_value = 0;
  int ret_value = 0;
 
 
  /* Special check for 'addub $0, r0' instruction -
  /* Special check for 'addub $0, r0' instruction -
     The opcode '0000 0000 0000 0000' is not allowed.  */
     The opcode '0000 0000 0000 0000' is not allowed.  */
  if (IS_INSN_MNEMONIC ("addub"))
  if (IS_INSN_MNEMONIC ("addub"))
    {
    {
      if ((instruction->operands[0].op_type == cst4)
      if ((instruction->operands[0].op_type == cst4)
          && instruction->operands[1].op_type == regr)
          && instruction->operands[1].op_type == regr)
        {
        {
          if (insn->arg[0].constant == 0 && insn->arg[1].r == r0)
          if (insn->arg[0].constant == 0 && insn->arg[1].r == r0)
            {
            {
              instruction++;
              instruction++;
              ret_value = 1;
              ret_value = 1;
            }
            }
        }
        }
    }
    }
 
 
  /* Optimization: Omit a zero displacement in bit operations,
  /* Optimization: Omit a zero displacement in bit operations,
     saving 2-byte encoding space (e.g., 'cbitw $8, 0(r1)').  */
     saving 2-byte encoding space (e.g., 'cbitw $8, 0(r1)').  */
  if (IS_INSN_TYPE (CSTBIT_INS))
  if (IS_INSN_TYPE (CSTBIT_INS))
    {
    {
      if ((instruction->operands[1].op_type == rbase_disps12)
      if ((instruction->operands[1].op_type == rbase_disps12)
           && (insn->arg[1].X_op == O_constant)
           && (insn->arg[1].X_op == O_constant)
           && (insn->arg[1].constant == 0))
           && (insn->arg[1].constant == 0))
            {
            {
              instruction--;
              instruction--;
              ret_value = 1;
              ret_value = 1;
            }
            }
    }
    }
 
 
  return ret_value;
  return ret_value;
}
}
 
 
/* Set the appropriate bit for register 'r' in 'mask'.
/* Set the appropriate bit for register 'r' in 'mask'.
   This indicates that this register is loaded or stored by
   This indicates that this register is loaded or stored by
   the instruction.  */
   the instruction.  */
 
 
static void
static void
mask_reg (int r, unsigned short int *mask)
mask_reg (int r, unsigned short int *mask)
{
{
  if ((reg)r > (reg)sp)
  if ((reg)r > (reg)sp)
    {
    {
      as_bad (_("Invalid Register in Register List"));
      as_bad (_("Invalid Register in Register List"));
      return;
      return;
    }
    }
 
 
  *mask |= (1 << r);
  *mask |= (1 << r);
}
}
 
 
/* Preprocess register list - create a 16-bit mask with one bit for each
/* Preprocess register list - create a 16-bit mask with one bit for each
   of the 16 general purpose registers. If a bit is set, it indicates
   of the 16 general purpose registers. If a bit is set, it indicates
   that this register is loaded or stored by the instruction.  */
   that this register is loaded or stored by the instruction.  */
 
 
static char *
static char *
preprocess_reglist (char *param, int *allocated)
preprocess_reglist (char *param, int *allocated)
{
{
  char reg_name[MAX_REGNAME_LEN]; /* Current parsed register name.  */
  char reg_name[MAX_REGNAME_LEN]; /* Current parsed register name.  */
  char *regP;                     /* Pointer to 'reg_name' string.  */
  char *regP;                     /* Pointer to 'reg_name' string.  */
  int reg_counter = 0;             /* Count number of parsed registers.  */
  int reg_counter = 0;             /* Count number of parsed registers.  */
  unsigned short int mask = 0;     /* Mask for 16 general purpose registers.  */
  unsigned short int mask = 0;     /* Mask for 16 general purpose registers.  */
  char *new_param;                /* New created operands string.  */
  char *new_param;                /* New created operands string.  */
  char *paramP = param;           /* Pointer to original opearands string.  */
  char *paramP = param;           /* Pointer to original opearands string.  */
  char maskstring[10];            /* Array to print the mask as a string.  */
  char maskstring[10];            /* Array to print the mask as a string.  */
  int hi_found = 0, lo_found = 0; /* Boolean flags for hi/lo registers.  */
  int hi_found = 0, lo_found = 0; /* Boolean flags for hi/lo registers.  */
  reg r;
  reg r;
  copreg cr;
  copreg cr;
 
 
  /* If 'param' is already in form of a number, no need to preprocess.  */
  /* If 'param' is already in form of a number, no need to preprocess.  */
  if (strchr (paramP, '{') == NULL)
  if (strchr (paramP, '{') == NULL)
    return param;
    return param;
 
 
  /* Verifying correct syntax of operand.  */
  /* Verifying correct syntax of operand.  */
  if (strchr (paramP, '}') == NULL)
  if (strchr (paramP, '}') == NULL)
    as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
    as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
 
 
  while (*paramP++ != '{');
  while (*paramP++ != '{');
 
 
  new_param = (char *)xcalloc (MAX_INST_LEN, sizeof (char));
  new_param = (char *)xcalloc (MAX_INST_LEN, sizeof (char));
  *allocated = 1;
  *allocated = 1;
  strncpy (new_param, param, paramP - param - 1);
  strncpy (new_param, param, paramP - param - 1);
 
 
  while (*paramP != '}')
  while (*paramP != '}')
    {
    {
      regP = paramP;
      regP = paramP;
      memset (&reg_name, '\0', sizeof (reg_name));
      memset (&reg_name, '\0', sizeof (reg_name));
 
 
      while (ISALNUM (*paramP))
      while (ISALNUM (*paramP))
        paramP++;
        paramP++;
 
 
      strncpy (reg_name, regP, paramP - regP);
      strncpy (reg_name, regP, paramP - regP);
 
 
      /* Coprocessor register c<N>.  */
      /* Coprocessor register c<N>.  */
      if (IS_INSN_TYPE (COP_REG_INS))
      if (IS_INSN_TYPE (COP_REG_INS))
        {
        {
          if (((cr = get_copregister (reg_name)) == nullcopregister)
          if (((cr = get_copregister (reg_name)) == nullcopregister)
              || (crx_copregtab[cr-MAX_REG].type != CRX_C_REGTYPE))
              || (crx_copregtab[cr-MAX_REG].type != CRX_C_REGTYPE))
            as_fatal (_("Illegal register `%s' in cop-register list"), reg_name);
            as_fatal (_("Illegal register `%s' in cop-register list"), reg_name);
          mask_reg (getreg_image (cr - c0), &mask);
          mask_reg (getreg_image (cr - c0), &mask);
        }
        }
      /* Coprocessor Special register cs<N>.  */
      /* Coprocessor Special register cs<N>.  */
      else if (IS_INSN_TYPE (COPS_REG_INS))
      else if (IS_INSN_TYPE (COPS_REG_INS))
        {
        {
          if (((cr = get_copregister (reg_name)) == nullcopregister)
          if (((cr = get_copregister (reg_name)) == nullcopregister)
              || (crx_copregtab[cr-MAX_REG].type != CRX_CS_REGTYPE))
              || (crx_copregtab[cr-MAX_REG].type != CRX_CS_REGTYPE))
            as_fatal (_("Illegal register `%s' in cop-special-register list"),
            as_fatal (_("Illegal register `%s' in cop-special-register list"),
                      reg_name);
                      reg_name);
          mask_reg (getreg_image (cr - cs0), &mask);
          mask_reg (getreg_image (cr - cs0), &mask);
        }
        }
      /* User register u<N>.  */
      /* User register u<N>.  */
      else if (instruction->flags & USER_REG)
      else if (instruction->flags & USER_REG)
        {
        {
          if (streq(reg_name, "uhi"))
          if (streq(reg_name, "uhi"))
            {
            {
              hi_found = 1;
              hi_found = 1;
              goto next_inst;
              goto next_inst;
            }
            }
          else if (streq(reg_name, "ulo"))
          else if (streq(reg_name, "ulo"))
            {
            {
              lo_found = 1;
              lo_found = 1;
              goto next_inst;
              goto next_inst;
            }
            }
          else if (((r = get_register (reg_name)) == nullregister)
          else if (((r = get_register (reg_name)) == nullregister)
              || (crx_regtab[r].type != CRX_U_REGTYPE))
              || (crx_regtab[r].type != CRX_U_REGTYPE))
            as_fatal (_("Illegal register `%s' in user register list"), reg_name);
            as_fatal (_("Illegal register `%s' in user register list"), reg_name);
 
 
          mask_reg (getreg_image (r - u0), &mask);
          mask_reg (getreg_image (r - u0), &mask);
        }
        }
      /* General purpose register r<N>.  */
      /* General purpose register r<N>.  */
      else
      else
        {
        {
          if (streq(reg_name, "hi"))
          if (streq(reg_name, "hi"))
            {
            {
              hi_found = 1;
              hi_found = 1;
              goto next_inst;
              goto next_inst;
            }
            }
          else if (streq(reg_name, "lo"))
          else if (streq(reg_name, "lo"))
            {
            {
              lo_found = 1;
              lo_found = 1;
              goto next_inst;
              goto next_inst;
            }
            }
          else if (((r = get_register (reg_name)) == nullregister)
          else if (((r = get_register (reg_name)) == nullregister)
              || (crx_regtab[r].type != CRX_R_REGTYPE))
              || (crx_regtab[r].type != CRX_R_REGTYPE))
            as_fatal (_("Illegal register `%s' in register list"), reg_name);
            as_fatal (_("Illegal register `%s' in register list"), reg_name);
 
 
          mask_reg (getreg_image (r - r0), &mask);
          mask_reg (getreg_image (r - r0), &mask);
        }
        }
 
 
      if (++reg_counter > MAX_REGS_IN_MASK16)
      if (++reg_counter > MAX_REGS_IN_MASK16)
        as_bad (_("Maximum %d bits may be set in `mask16' operand"),
        as_bad (_("Maximum %d bits may be set in `mask16' operand"),
                MAX_REGS_IN_MASK16);
                MAX_REGS_IN_MASK16);
 
 
next_inst:
next_inst:
      while (!ISALNUM (*paramP) && *paramP != '}')
      while (!ISALNUM (*paramP) && *paramP != '}')
          paramP++;
          paramP++;
    }
    }
 
 
  if (*++paramP != '\0')
  if (*++paramP != '\0')
    as_warn (_("rest of line ignored; first ignored character is `%c'"),
    as_warn (_("rest of line ignored; first ignored character is `%c'"),
             *paramP);
             *paramP);
 
 
  switch (hi_found + lo_found)
  switch (hi_found + lo_found)
    {
    {
    case 0:
    case 0:
      /* At least one register should be specified.  */
      /* At least one register should be specified.  */
      if (mask == 0)
      if (mask == 0)
        as_bad (_("Illegal `mask16' operand, operation is undefined - `%s'"),
        as_bad (_("Illegal `mask16' operand, operation is undefined - `%s'"),
                ins_parse);
                ins_parse);
      break;
      break;
 
 
    case 1:
    case 1:
      /* HI can't be specified without LO (and vise-versa).  */
      /* HI can't be specified without LO (and vise-versa).  */
      as_bad (_("HI/LO registers should be specified together"));
      as_bad (_("HI/LO registers should be specified together"));
      break;
      break;
 
 
    case 2:
    case 2:
      /* HI/LO registers mustn't be masked with additional registers.  */
      /* HI/LO registers mustn't be masked with additional registers.  */
      if (mask != 0)
      if (mask != 0)
        as_bad (_("HI/LO registers should be specified without additional registers"));
        as_bad (_("HI/LO registers should be specified without additional registers"));
 
 
    default:
    default:
      break;
      break;
    }
    }
 
 
  sprintf (maskstring, "$0x%x", mask);
  sprintf (maskstring, "$0x%x", mask);
  strcat (new_param, maskstring);
  strcat (new_param, maskstring);
  return new_param;
  return new_param;
}
}
 
 
/* Print the instruction.
/* Print the instruction.
   Handle also cases where the instruction is relaxable/relocatable.  */
   Handle also cases where the instruction is relaxable/relocatable.  */
 
 
void
void
print_insn (ins *insn)
print_insn (ins *insn)
{
{
  unsigned int i, j, insn_size;
  unsigned int i, j, insn_size;
  char *this_frag;
  char *this_frag;
  unsigned short words[4];
  unsigned short words[4];
  int addr_mod;
  int addr_mod;
 
 
  /* Arrange the insn encodings in a WORD size array.  */
  /* Arrange the insn encodings in a WORD size array.  */
  for (i = 0, j = 0; i < 2; i++)
  for (i = 0, j = 0; i < 2; i++)
    {
    {
      words[j++] = (output_opcode[i] >> 16) & 0xFFFF;
      words[j++] = (output_opcode[i] >> 16) & 0xFFFF;
      words[j++] = output_opcode[i] & 0xFFFF;
      words[j++] = output_opcode[i] & 0xFFFF;
    }
    }
 
 
  /* Handle relaxtion.  */
  /* Handle relaxtion.  */
  if ((instruction->flags & RELAXABLE) && relocatable)
  if ((instruction->flags & RELAXABLE) && relocatable)
    {
    {
      int relax_subtype;
      int relax_subtype;
 
 
      /* Write the maximal instruction size supported.  */
      /* Write the maximal instruction size supported.  */
      insn_size = INSN_MAX_SIZE;
      insn_size = INSN_MAX_SIZE;
 
 
      /* bCC  */
      /* bCC  */
      if (IS_INSN_TYPE (BRANCH_INS))
      if (IS_INSN_TYPE (BRANCH_INS))
        relax_subtype = 0;
        relax_subtype = 0;
      /* bal  */
      /* bal  */
      else if (IS_INSN_TYPE (DCR_BRANCH_INS) || IS_INSN_MNEMONIC ("bal"))
      else if (IS_INSN_TYPE (DCR_BRANCH_INS) || IS_INSN_MNEMONIC ("bal"))
        relax_subtype = 3;
        relax_subtype = 3;
      /* cmpbr/bcop  */
      /* cmpbr/bcop  */
      else if (IS_INSN_TYPE (CMPBR_INS) || IS_INSN_TYPE (COP_BRANCH_INS))
      else if (IS_INSN_TYPE (CMPBR_INS) || IS_INSN_TYPE (COP_BRANCH_INS))
        relax_subtype = 5;
        relax_subtype = 5;
      else
      else
        abort ();
        abort ();
 
 
      this_frag = frag_var (rs_machine_dependent, insn_size * 2,
      this_frag = frag_var (rs_machine_dependent, insn_size * 2,
                            4, relax_subtype,
                            4, relax_subtype,
                            insn->exp.X_add_symbol,
                            insn->exp.X_add_symbol,
                            insn->exp.X_add_number,
                            insn->exp.X_add_number,
                            0);
                            0);
    }
    }
  else
  else
    {
    {
      insn_size = instruction->size;
      insn_size = instruction->size;
      this_frag = frag_more (insn_size * 2);
      this_frag = frag_more (insn_size * 2);
 
 
      /* Handle relocation.  */
      /* Handle relocation.  */
      if ((relocatable) && (insn->rtype != BFD_RELOC_NONE))
      if ((relocatable) && (insn->rtype != BFD_RELOC_NONE))
        {
        {
          reloc_howto_type *reloc_howto;
          reloc_howto_type *reloc_howto;
          int size;
          int size;
 
 
          reloc_howto = bfd_reloc_type_lookup (stdoutput, insn->rtype);
          reloc_howto = bfd_reloc_type_lookup (stdoutput, insn->rtype);
 
 
          if (!reloc_howto)
          if (!reloc_howto)
            abort ();
            abort ();
 
 
          size = bfd_get_reloc_size (reloc_howto);
          size = bfd_get_reloc_size (reloc_howto);
 
 
          if (size < 1 || size > 4)
          if (size < 1 || size > 4)
            abort ();
            abort ();
 
 
          fix_new_exp (frag_now, this_frag - frag_now->fr_literal,
          fix_new_exp (frag_now, this_frag - frag_now->fr_literal,
                       size, &insn->exp, reloc_howto->pc_relative,
                       size, &insn->exp, reloc_howto->pc_relative,
                       insn->rtype);
                       insn->rtype);
        }
        }
    }
    }
 
 
  /* Verify a 2-byte code alignment.  */
  /* Verify a 2-byte code alignment.  */
  addr_mod = frag_now_fix () & 1;
  addr_mod = frag_now_fix () & 1;
  if (frag_now->has_code && frag_now->insn_addr != addr_mod)
  if (frag_now->has_code && frag_now->insn_addr != addr_mod)
    as_bad (_("instruction address is not a multiple of 2"));
    as_bad (_("instruction address is not a multiple of 2"));
  frag_now->insn_addr = addr_mod;
  frag_now->insn_addr = addr_mod;
  frag_now->has_code = 1;
  frag_now->has_code = 1;
 
 
  /* Write the instruction encoding to frag.  */
  /* Write the instruction encoding to frag.  */
  for (i = 0; i < insn_size; i++)
  for (i = 0; i < insn_size; i++)
    {
    {
      md_number_to_chars (this_frag, (valueT) words[i], 2);
      md_number_to_chars (this_frag, (valueT) words[i], 2);
      this_frag += 2;
      this_frag += 2;
    }
    }
}
}
 
 
/* This is the guts of the machine-dependent assembler.  OP points to a
/* This is the guts of the machine-dependent assembler.  OP points to a
   machine dependent instruction.  This function is supposed to emit
   machine dependent instruction.  This function is supposed to emit
   the frags/bytes it assembles to.  */
   the frags/bytes it assembles to.  */
 
 
void
void
md_assemble (char *op)
md_assemble (char *op)
{
{
  ins crx_ins;
  ins crx_ins;
  char *param;
  char *param;
  char c;
  char c;
 
 
  /* Reset global variables for a new instruction.  */
  /* Reset global variables for a new instruction.  */
  reset_vars (op);
  reset_vars (op);
 
 
  /* Strip the mnemonic.  */
  /* Strip the mnemonic.  */
  for (param = op; *param != 0 && !ISSPACE (*param); param++)
  for (param = op; *param != 0 && !ISSPACE (*param); param++)
    ;
    ;
  c = *param;
  c = *param;
  *param++ = '\0';
  *param++ = '\0';
 
 
  /* Find the instruction.  */
  /* Find the instruction.  */
  instruction = (const inst *) hash_find (crx_inst_hash, op);
  instruction = (const inst *) hash_find (crx_inst_hash, op);
  if (instruction == NULL)
  if (instruction == NULL)
    {
    {
      as_bad (_("Unknown opcode: `%s'"), op);
      as_bad (_("Unknown opcode: `%s'"), op);
      return;
      return;
    }
    }
 
 
  /* Tie dwarf2 debug info to the address at the start of the insn.  */
  /* Tie dwarf2 debug info to the address at the start of the insn.  */
  dwarf2_emit_insn (0);
  dwarf2_emit_insn (0);
 
 
  /* Parse the instruction's operands.  */
  /* Parse the instruction's operands.  */
  parse_insn (&crx_ins, param);
  parse_insn (&crx_ins, param);
 
 
  /* Assemble the instruction - return upon failure.  */
  /* Assemble the instruction - return upon failure.  */
  if (assemble_insn (op, &crx_ins) == 0)
  if (assemble_insn (op, &crx_ins) == 0)
    return;
    return;
 
 
  /* Print the instruction.  */
  /* Print the instruction.  */
  print_insn (&crx_ins);
  print_insn (&crx_ins);
}
}
 
 

powered by: WebSVN 2.1.0

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