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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-old/] [binutils-2.18.50/] [gas/] [config/] [tc-tic54x.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-tic54x.c -- Assembly code for the Texas Instruments TMS320C54X
/* tc-tic54x.c -- Assembly code for the Texas Instruments TMS320C54X
   Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
   Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
   Free Software Foundation, Inc.
   Free Software Foundation, Inc.
   Contributed by Timothy Wall (twall@cygnus.com)
   Contributed by Timothy Wall (twall@cygnus.com)
 
 
   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 Free
   along with GAS; see the file COPYING.  If not, write to the Free
   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
   02110-1301, USA.  */
   02110-1301, USA.  */
 
 
/* Texas Instruments TMS320C54X machine specific gas.
/* Texas Instruments TMS320C54X machine specific gas.
   Written by Timothy Wall (twall@alum.mit.edu).
   Written by Timothy Wall (twall@alum.mit.edu).
 
 
   Valuable things to do:
   Valuable things to do:
   Pipeline conflict warnings
   Pipeline conflict warnings
   We encode/decode "ld #_label, dp" differently in relocatable files
   We encode/decode "ld #_label, dp" differently in relocatable files
     This means we're not compatible with TI output containing those
     This means we're not compatible with TI output containing those
     expressions.  We store the upper nine bits; TI stores the lower nine
     expressions.  We store the upper nine bits; TI stores the lower nine
     bits.  How they recover the original upper nine bits is beyond me.
     bits.  How they recover the original upper nine bits is beyond me.
 
 
   Tests to add to expect testsuite:
   Tests to add to expect testsuite:
     '=' and '==' with .if, .elseif, and .break
     '=' and '==' with .if, .elseif, and .break
 
 
   Incompatibilities (mostly trivial):
   Incompatibilities (mostly trivial):
   We don't allow '''
   We don't allow '''
   We fill text section with zeroes instead of "nop"s
   We fill text section with zeroes instead of "nop"s
   We don't convert '' or "" to a single instance
   We don't convert '' or "" to a single instance
   We don't convert '' to '\0'
   We don't convert '' to '\0'
   We don't allow strings with .byte/.half/.short/.long
   We don't allow strings with .byte/.half/.short/.long
   Probably details of the subsym stuff are different
   Probably details of the subsym stuff are different
   TI sets labels to be data type 4 (T_INT); GAS uses T_NULL.
   TI sets labels to be data type 4 (T_INT); GAS uses T_NULL.
 
 
   COFF1 limits section names to 8 characters.
   COFF1 limits section names to 8 characters.
   Some of the default behavior changed from COFF1 to COFF2.  */
   Some of the default behavior changed from COFF1 to COFF2.  */
 
 
#include <limits.h>
#include <limits.h>
#include "as.h"
#include "as.h"
#include "safe-ctype.h"
#include "safe-ctype.h"
#include "sb.h"
#include "sb.h"
#include "macro.h"
#include "macro.h"
#include "subsegs.h"
#include "subsegs.h"
#include "struc-symbol.h"
#include "struc-symbol.h"
#include "opcode/tic54x.h"
#include "opcode/tic54x.h"
#include "obj-coff.h"
#include "obj-coff.h"
#include <math.h>
#include <math.h>
 
 
 
 
static struct stag
static struct stag
{
{
  symbolS *sym;                 /* Symbol for this stag; value is offset.  */
  symbolS *sym;                 /* Symbol for this stag; value is offset.  */
  const char *name;             /* Shortcut to symbol name.  */
  const char *name;             /* Shortcut to symbol name.  */
  bfd_vma size;                 /* Size of struct/union.  */
  bfd_vma size;                 /* Size of struct/union.  */
  int current_bitfield_offset;  /* Temporary for tracking fields.  */
  int current_bitfield_offset;  /* Temporary for tracking fields.  */
  int is_union;
  int is_union;
  struct stag_field             /* List of fields.  */
  struct stag_field             /* List of fields.  */
  {
  {
    const char *name;
    const char *name;
    bfd_vma offset;             /* Of start of this field.  */
    bfd_vma offset;             /* Of start of this field.  */
    int bitfield_offset;        /* Of start of this field.  */
    int bitfield_offset;        /* Of start of this field.  */
    struct stag *stag;          /* If field is struct/union.  */
    struct stag *stag;          /* If field is struct/union.  */
    struct stag_field *next;
    struct stag_field *next;
  } *field;
  } *field;
  /* For nesting; used only in stag construction.  */
  /* For nesting; used only in stag construction.  */
  struct stag *inner;           /* Enclosed .struct.  */
  struct stag *inner;           /* Enclosed .struct.  */
  struct stag *outer;           /* Enclosing .struct.  */
  struct stag *outer;           /* Enclosing .struct.  */
} *current_stag = NULL;
} *current_stag = NULL;
 
 
#define MAX_LINE 256 /* Lines longer than this are truncated by TI's asm.  */
#define MAX_LINE 256 /* Lines longer than this are truncated by TI's asm.  */
 
 
typedef struct _tic54x_insn
typedef struct _tic54x_insn
{
{
  const template *tm;           /* Opcode template.  */
  const template *tm;           /* Opcode template.  */
 
 
  char mnemonic[MAX_LINE];      /* Opcode name/mnemonic.  */
  char mnemonic[MAX_LINE];      /* Opcode name/mnemonic.  */
  char parmnemonic[MAX_LINE];   /* 2nd mnemonic of parallel insn.  */
  char parmnemonic[MAX_LINE];   /* 2nd mnemonic of parallel insn.  */
 
 
  int opcount;
  int opcount;
  struct opstruct
  struct opstruct
  {
  {
    char buf[MAX_LINE];
    char buf[MAX_LINE];
    enum optype type;
    enum optype type;
    expressionS exp;
    expressionS exp;
  } operands[MAX_OPERANDS];
  } operands[MAX_OPERANDS];
 
 
  int paropcount;
  int paropcount;
  struct opstruct paroperands[MAX_OPERANDS];
  struct opstruct paroperands[MAX_OPERANDS];
 
 
  int is_lkaddr;
  int is_lkaddr;
  int lkoperand;
  int lkoperand;
  int words;                    /* Size of insn in 16-bit words.  */
  int words;                    /* Size of insn in 16-bit words.  */
  int using_default_dst;        /* Do we need to explicitly set an
  int using_default_dst;        /* Do we need to explicitly set an
                                   omitted OP_DST operand?  */
                                   omitted OP_DST operand?  */
  struct
  struct
  {
  {
    unsigned short word;             /* Final encoded opcode data.  */
    unsigned short word;             /* Final encoded opcode data.  */
    int unresolved;
    int unresolved;
    int r_nchars;                    /* Relocation size.  */
    int r_nchars;                    /* Relocation size.  */
    bfd_reloc_code_real_type r_type; /* Relocation type.  */
    bfd_reloc_code_real_type r_type; /* Relocation type.  */
    expressionS addr_expr;           /* Storage for unresolved expressions.  */
    expressionS addr_expr;           /* Storage for unresolved expressions.  */
  } opcode[3];
  } opcode[3];
} tic54x_insn;
} tic54x_insn;
 
 
enum cpu_version
enum cpu_version
{
{
  VNONE = 0, V541 = 1, V542 = 2, V543 = 3, V545 = 5, V548 = 8, V549 = 9,
  VNONE = 0, V541 = 1, V542 = 2, V543 = 3, V545 = 5, V548 = 8, V549 = 9,
  V545LP = 15, V546LP = 16
  V545LP = 15, V546LP = 16
};
};
 
 
enum address_mode
enum address_mode
{
{
  c_mode,   /* 16-bit addresses.  */
  c_mode,   /* 16-bit addresses.  */
  far_mode  /* >16-bit addresses.  */
  far_mode  /* >16-bit addresses.  */
};
};
 
 
static segT stag_saved_seg;
static segT stag_saved_seg;
static subsegT stag_saved_subseg;
static subsegT stag_saved_subseg;
 
 
const char comment_chars[] = ";";
const char comment_chars[] = ";";
const char line_comment_chars[] = ";*#"; /* At column zero only.  */
const char line_comment_chars[] = ";*#"; /* At column zero only.  */
const char line_separator_chars[] = ""; /* Not permitted.  */
const char line_separator_chars[] = ""; /* Not permitted.  */
 
 
int emitting_long = 0;
int emitting_long = 0;
 
 
/* Characters which indicate that this is a floating point constant.  */
/* Characters which indicate that this is a floating point constant.  */
const char FLT_CHARS[] = "fF";
const char FLT_CHARS[] = "fF";
 
 
/* Characters that can be used to separate mantissa from exp in FP
/* Characters that can be used to separate mantissa from exp in FP
   nums.  */
   nums.  */
const char EXP_CHARS[] = "eE";
const char EXP_CHARS[] = "eE";
 
 
const char *md_shortopts = "";
const char *md_shortopts = "";
 
 
#define OPTION_ADDRESS_MODE     (OPTION_MD_BASE)
#define OPTION_ADDRESS_MODE     (OPTION_MD_BASE)
#define OPTION_CPU_VERSION      (OPTION_ADDRESS_MODE + 1)
#define OPTION_CPU_VERSION      (OPTION_ADDRESS_MODE + 1)
#define OPTION_COFF_VERSION     (OPTION_CPU_VERSION + 1)
#define OPTION_COFF_VERSION     (OPTION_CPU_VERSION + 1)
#define OPTION_STDERR_TO_FILE   (OPTION_COFF_VERSION + 1)
#define OPTION_STDERR_TO_FILE   (OPTION_COFF_VERSION + 1)
 
 
struct option md_longopts[] =
struct option md_longopts[] =
{
{
  { "mfar-mode",       no_argument,         NULL, OPTION_ADDRESS_MODE },
  { "mfar-mode",       no_argument,         NULL, OPTION_ADDRESS_MODE },
  { "mf",              no_argument,         NULL, OPTION_ADDRESS_MODE },
  { "mf",              no_argument,         NULL, OPTION_ADDRESS_MODE },
  { "mcpu",            required_argument,   NULL, OPTION_CPU_VERSION },
  { "mcpu",            required_argument,   NULL, OPTION_CPU_VERSION },
  { "merrors-to-file", required_argument,   NULL, OPTION_STDERR_TO_FILE },
  { "merrors-to-file", required_argument,   NULL, OPTION_STDERR_TO_FILE },
  { "me",              required_argument,   NULL, OPTION_STDERR_TO_FILE },
  { "me",              required_argument,   NULL, OPTION_STDERR_TO_FILE },
  { 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);
 
 
static int assembly_begun = 0;
static int assembly_begun = 0;
/* Addressing mode is not entirely implemented; the latest rev of the Other
/* Addressing mode is not entirely implemented; the latest rev of the Other
   assembler doesn't seem to make any distinction whatsoever; all relocations
   assembler doesn't seem to make any distinction whatsoever; all relocations
   are stored as extended relocatiosn.  Older versions used REL16 vs RELEXT16,
   are stored as extended relocatiosn.  Older versions used REL16 vs RELEXT16,
   but now it seems all relocations are RELEXT16.  We use all RELEXT16.
   but now it seems all relocations are RELEXT16.  We use all RELEXT16.
 
 
   The cpu version is kind of a waste of time as well.  There is one
   The cpu version is kind of a waste of time as well.  There is one
   instruction (RND) for LP devices only, and several for devices with
   instruction (RND) for LP devices only, and several for devices with
   extended addressing only.  We include it for compatibility.  */
   extended addressing only.  We include it for compatibility.  */
static enum address_mode amode = c_mode;
static enum address_mode amode = c_mode;
static enum cpu_version cpu = VNONE;
static enum cpu_version cpu = VNONE;
 
 
/* Include string substitutions in listing?  */
/* Include string substitutions in listing?  */
static int listing_sslist = 0;
static int listing_sslist = 0;
 
 
/* Did we do subsym substitutions on the line?  */
/* Did we do subsym substitutions on the line?  */
static int substitution_line = 0;
static int substitution_line = 0;
 
 
/* Last label seen.  */
/* Last label seen.  */
static symbolS *last_label_seen = NULL;
static symbolS *last_label_seen = NULL;
 
 
/* This ensures that all new labels are unique.  */
/* This ensures that all new labels are unique.  */
static int local_label_id;
static int local_label_id;
 
 
static struct hash_control *subsym_recurse_hash; /* Prevent infinite recurse.  */
static struct hash_control *subsym_recurse_hash; /* Prevent infinite recurse.  */
static struct hash_control *math_hash; /* Built-in math functions.  */
static struct hash_control *math_hash; /* Built-in math functions.  */
/* Allow maximum levels of macro nesting; level 0 is the main substitution
/* Allow maximum levels of macro nesting; level 0 is the main substitution
   symbol table.  The other assembler only does 32 levels, so there!  */
   symbol table.  The other assembler only does 32 levels, so there!  */
static struct hash_control *subsym_hash[100];
static struct hash_control *subsym_hash[100];
 
 
/* Keep track of local labels so we can substitute them before GAS sees them
/* Keep track of local labels so we can substitute them before GAS sees them
   since macros use their own 'namespace' for local labels, use a separate hash
   since macros use their own 'namespace' for local labels, use a separate hash
 
 
   We do our own local label handling 'cuz it's subtly different from the
   We do our own local label handling 'cuz it's subtly different from the
   stock GAS handling.
   stock GAS handling.
 
 
   We use our own macro nesting counter, since GAS overloads it when expanding
   We use our own macro nesting counter, since GAS overloads it when expanding
   other things (like conditionals and repeat loops).  */
   other things (like conditionals and repeat loops).  */
static int macro_level = 0;
static int macro_level = 0;
static struct hash_control *local_label_hash[100];
static struct hash_control *local_label_hash[100];
/* Keep track of struct/union tags.  */
/* Keep track of struct/union tags.  */
static struct hash_control *stag_hash;
static struct hash_control *stag_hash;
static struct hash_control *op_hash;
static struct hash_control *op_hash;
static struct hash_control *parop_hash;
static struct hash_control *parop_hash;
static struct hash_control *reg_hash;
static struct hash_control *reg_hash;
static struct hash_control *mmreg_hash;
static struct hash_control *mmreg_hash;
static struct hash_control *cc_hash;
static struct hash_control *cc_hash;
static struct hash_control *cc2_hash;
static struct hash_control *cc2_hash;
static struct hash_control *cc3_hash;
static struct hash_control *cc3_hash;
static struct hash_control *sbit_hash;
static struct hash_control *sbit_hash;
static struct hash_control *misc_symbol_hash;
static struct hash_control *misc_symbol_hash;
 
 
/* Only word (et al.), align, or conditionals are allowed within
/* Only word (et al.), align, or conditionals are allowed within
   .struct/.union.  */
   .struct/.union.  */
#define ILLEGAL_WITHIN_STRUCT()                                 \
#define ILLEGAL_WITHIN_STRUCT()                                 \
  do                                                            \
  do                                                            \
    if (current_stag != NULL)                                   \
    if (current_stag != NULL)                                   \
      {                                                         \
      {                                                         \
        as_bad (_("pseudo-op illegal within .struct/.union"));  \
        as_bad (_("pseudo-op illegal within .struct/.union"));  \
        return;                                                 \
        return;                                                 \
      }                                                         \
      }                                                         \
  while (0)
  while (0)
 
 
static void     tic54x_emit_char        PARAMS ((char));
static void     tic54x_emit_char        PARAMS ((char));
static fragS *  frag_prev               PARAMS ((fragS *, segT));
static fragS *  frag_prev               PARAMS ((fragS *, segT));
static fragS *  bit_offset_frag         PARAMS ((fragS *, segT));
static fragS *  bit_offset_frag         PARAMS ((fragS *, segT));
static int      frag_bit_offset         PARAMS ((fragS *, segT));
static int      frag_bit_offset         PARAMS ((fragS *, segT));
static char *   parse_expression        PARAMS ((char *, expressionS *));
static char *   parse_expression        PARAMS ((char *, expressionS *));
static void     tic54x_asg              PARAMS ((int));
static void     tic54x_asg              PARAMS ((int));
static void     tic54x_eval             PARAMS ((int));
static void     tic54x_eval             PARAMS ((int));
static void     tic54x_bss              PARAMS ((int));
static void     tic54x_bss              PARAMS ((int));
static void     stag_add_field_symbols  PARAMS ((struct stag *, const char *, bfd_vma, symbolS *, const char *));
static void     stag_add_field_symbols  PARAMS ((struct stag *, const char *, bfd_vma, symbolS *, const char *));
static void     stag_add_field          PARAMS ((struct stag *, const char *, bfd_vma, struct stag *));
static void     stag_add_field          PARAMS ((struct stag *, const char *, bfd_vma, struct stag *));
static void     tic54x_struct           PARAMS ((int));
static void     tic54x_struct           PARAMS ((int));
static void     tic54x_endstruct        PARAMS ((int));
static void     tic54x_endstruct        PARAMS ((int));
static void     tic54x_tag              PARAMS ((int));
static void     tic54x_tag              PARAMS ((int));
static void     tic54x_struct_field     PARAMS ((int));
static void     tic54x_struct_field     PARAMS ((int));
static void     tic54x_cons             PARAMS ((int));
static void     tic54x_cons             PARAMS ((int));
static void     tic54x_remove_local_label PARAMS ((const char *, PTR));
static void     tic54x_remove_local_label PARAMS ((const char *, PTR));
static void     tic54x_clear_local_labels PARAMS ((int));
static void     tic54x_clear_local_labels PARAMS ((int));
static void     tic54x_sect             PARAMS ((int));
static void     tic54x_sect             PARAMS ((int));
static void     tic54x_space            PARAMS ((int));
static void     tic54x_space            PARAMS ((int));
static void     tic54x_usect            PARAMS ((int));
static void     tic54x_usect            PARAMS ((int));
static enum cpu_version lookup_version  PARAMS ((const char *));
static enum cpu_version lookup_version  PARAMS ((const char *));
static void     set_cpu                 PARAMS ((enum cpu_version));
static void     set_cpu                 PARAMS ((enum cpu_version));
static void     tic54x_version          PARAMS ((int));
static void     tic54x_version          PARAMS ((int));
static void     tic54x_float_cons       PARAMS ((int));
static void     tic54x_float_cons       PARAMS ((int));
static void     tic54x_stringer         PARAMS ((int));
static void     tic54x_stringer         PARAMS ((int));
static void     tic54x_p2align          PARAMS ((int));
static void     tic54x_p2align          PARAMS ((int));
static void     tic54x_align_words      PARAMS ((int));
static void     tic54x_align_words      PARAMS ((int));
static void     tic54x_field            PARAMS ((int));
static void     tic54x_field            PARAMS ((int));
static int      tic54x_initialized_section PARAMS ((segT));
static int      tic54x_initialized_section PARAMS ((segT));
static void     tic54x_clink            PARAMS ((int));
static void     tic54x_clink            PARAMS ((int));
static void     tic54x_set_default_include PARAMS ((int));
static void     tic54x_set_default_include PARAMS ((int));
static void     tic54x_include          PARAMS ((int));
static void     tic54x_include          PARAMS ((int));
static void     tic54x_message          PARAMS ((int));
static void     tic54x_message          PARAMS ((int));
static void     tic54x_label            PARAMS ((int));
static void     tic54x_label            PARAMS ((int));
static void     tic54x_mmregs           PARAMS ((int));
static void     tic54x_mmregs           PARAMS ((int));
static void     tic54x_loop             PARAMS ((int));
static void     tic54x_loop             PARAMS ((int));
static void     tic54x_endloop          PARAMS ((int));
static void     tic54x_endloop          PARAMS ((int));
static void     tic54x_break            PARAMS ((int));
static void     tic54x_break            PARAMS ((int));
static void     set_address_mode        PARAMS ((int));
static void     set_address_mode        PARAMS ((int));
static void     tic54x_address_mode     PARAMS ((int));
static void     tic54x_address_mode     PARAMS ((int));
static void     tic54x_sblock           PARAMS ((int));
static void     tic54x_sblock           PARAMS ((int));
static void     tic54x_set              PARAMS ((int));
static void     tic54x_set              PARAMS ((int));
static void     tic54x_fclist           PARAMS ((int));
static void     tic54x_fclist           PARAMS ((int));
static void     tic54x_sslist           PARAMS ((int));
static void     tic54x_sslist           PARAMS ((int));
static void     tic54x_var              PARAMS ((int));
static void     tic54x_var              PARAMS ((int));
static void     tic54x_mlib             PARAMS ((int));
static void     tic54x_mlib             PARAMS ((int));
static int      subsym_symlen           PARAMS ((char *, char *));
static int      subsym_symlen           PARAMS ((char *, char *));
static int      subsym_symcmp           PARAMS ((char *, char *));
static int      subsym_symcmp           PARAMS ((char *, char *));
static int      subsym_firstch          PARAMS ((char *, char *));
static int      subsym_firstch          PARAMS ((char *, char *));
static int      subsym_lastch           PARAMS ((char *, char *));
static int      subsym_lastch           PARAMS ((char *, char *));
static int      subsym_isdefed          PARAMS ((char *, char *));
static int      subsym_isdefed          PARAMS ((char *, char *));
static int      subsym_ismember         PARAMS ((char *, char *));
static int      subsym_ismember         PARAMS ((char *, char *));
static int      subsym_iscons           PARAMS ((char *, char *));
static int      subsym_iscons           PARAMS ((char *, char *));
static int      subsym_isname           PARAMS ((char *, char *));
static int      subsym_isname           PARAMS ((char *, char *));
static int      subsym_isreg            PARAMS ((char *, char *));
static int      subsym_isreg            PARAMS ((char *, char *));
static int      subsym_structsz         PARAMS ((char *, char *));
static int      subsym_structsz         PARAMS ((char *, char *));
static int      subsym_structacc        PARAMS ((char *, char *));
static int      subsym_structacc        PARAMS ((char *, char *));
static float    math_ceil               PARAMS ((float, float));
static float    math_ceil               PARAMS ((float, float));
static float    math_cvi                PARAMS ((float, float));
static float    math_cvi                PARAMS ((float, float));
static float    math_floor              PARAMS ((float, float));
static float    math_floor              PARAMS ((float, float));
static float    math_fmod               PARAMS ((float, float));
static float    math_fmod               PARAMS ((float, float));
static float    math_int                PARAMS ((float, float));
static float    math_int                PARAMS ((float, float));
static float    math_round              PARAMS ((float, float));
static float    math_round              PARAMS ((float, float));
static float    math_sgn                PARAMS ((float, float));
static float    math_sgn                PARAMS ((float, float));
static float    math_trunc              PARAMS ((float, float));
static float    math_trunc              PARAMS ((float, float));
static float    math_acos               PARAMS ((float, float));
static float    math_acos               PARAMS ((float, float));
static float    math_asin               PARAMS ((float, float));
static float    math_asin               PARAMS ((float, float));
static float    math_atan               PARAMS ((float, float));
static float    math_atan               PARAMS ((float, float));
static float    math_atan2              PARAMS ((float, float));
static float    math_atan2              PARAMS ((float, float));
static float    math_cosh               PARAMS ((float, float));
static float    math_cosh               PARAMS ((float, float));
static float    math_cos                PARAMS ((float, float));
static float    math_cos                PARAMS ((float, float));
static float    math_cvf                PARAMS ((float, float));
static float    math_cvf                PARAMS ((float, float));
static float    math_exp                PARAMS ((float, float));
static float    math_exp                PARAMS ((float, float));
static float    math_fabs               PARAMS ((float, float));
static float    math_fabs               PARAMS ((float, float));
static float    math_ldexp              PARAMS ((float, float));
static float    math_ldexp              PARAMS ((float, float));
static float    math_log10              PARAMS ((float, float));
static float    math_log10              PARAMS ((float, float));
static float    math_log                PARAMS ((float, float));
static float    math_log                PARAMS ((float, float));
static float    math_max                PARAMS ((float, float));
static float    math_max                PARAMS ((float, float));
static float    math_min                PARAMS ((float, float));
static float    math_min                PARAMS ((float, float));
static float    math_pow                PARAMS ((float, float));
static float    math_pow                PARAMS ((float, float));
static float    math_sin                PARAMS ((float, float));
static float    math_sin                PARAMS ((float, float));
static float    math_sinh               PARAMS ((float, float));
static float    math_sinh               PARAMS ((float, float));
static float    math_sqrt               PARAMS ((float, float));
static float    math_sqrt               PARAMS ((float, float));
static float    math_tan                PARAMS ((float, float));
static float    math_tan                PARAMS ((float, float));
static float    math_tanh               PARAMS ((float, float));
static float    math_tanh               PARAMS ((float, float));
static int      is_accumulator          PARAMS ((struct opstruct *));
static int      is_accumulator          PARAMS ((struct opstruct *));
static int      get_operands            PARAMS ((struct opstruct operands[], char *));
static int      get_operands            PARAMS ((struct opstruct operands[], char *));
static int      is_immediate            PARAMS ((struct opstruct *));
static int      is_immediate            PARAMS ((struct opstruct *));
static int      is_absolute             PARAMS ((struct opstruct *));
static int      is_absolute             PARAMS ((struct opstruct *));
static int      is_indirect             PARAMS ((struct opstruct *));
static int      is_indirect             PARAMS ((struct opstruct *));
static int      is_dual                 PARAMS ((struct opstruct *));
static int      is_dual                 PARAMS ((struct opstruct *));
static int      is_mmreg                PARAMS ((struct opstruct *));
static int      is_mmreg                PARAMS ((struct opstruct *));
static int      is_type                 PARAMS ((struct opstruct *, enum optype));
static int      is_type                 PARAMS ((struct opstruct *, enum optype));
static int      operands_match          PARAMS ((tic54x_insn *, struct opstruct *, int, const enum optype *, int, int));
static int      operands_match          PARAMS ((tic54x_insn *, struct opstruct *, int, const enum optype *, int, int));
static int      encode_dmad             PARAMS ((tic54x_insn *, struct opstruct *, int));
static int      encode_dmad             PARAMS ((tic54x_insn *, struct opstruct *, int));
static int      encode_address          PARAMS ((tic54x_insn *, struct opstruct *));
static int      encode_address          PARAMS ((tic54x_insn *, struct opstruct *));
static int      encode_indirect         PARAMS ((tic54x_insn *, struct opstruct *));
static int      encode_indirect         PARAMS ((tic54x_insn *, struct opstruct *));
static int      encode_integer          PARAMS ((tic54x_insn *, struct opstruct *, int, int, int, unsigned short));
static int      encode_integer          PARAMS ((tic54x_insn *, struct opstruct *, int, int, int, unsigned short));
static int      encode_condition        PARAMS ((tic54x_insn *, struct opstruct *));
static int      encode_condition        PARAMS ((tic54x_insn *, struct opstruct *));
static int      encode_cc3              PARAMS ((tic54x_insn *, struct opstruct *));
static int      encode_cc3              PARAMS ((tic54x_insn *, struct opstruct *));
static int      encode_arx              PARAMS ((tic54x_insn *, struct opstruct *));
static int      encode_arx              PARAMS ((tic54x_insn *, struct opstruct *));
static int      encode_cc2              PARAMS ((tic54x_insn *, struct opstruct *));
static int      encode_cc2              PARAMS ((tic54x_insn *, struct opstruct *));
static int      encode_operand          PARAMS ((tic54x_insn *, enum optype, struct opstruct *));
static int      encode_operand          PARAMS ((tic54x_insn *, enum optype, struct opstruct *));
static void     emit_insn               PARAMS ((tic54x_insn *));
static void     emit_insn               PARAMS ((tic54x_insn *));
static int      build_insn              PARAMS ((tic54x_insn *));
static int      build_insn              PARAMS ((tic54x_insn *));
static int      optimize_insn           PARAMS ((tic54x_insn *));
static int      optimize_insn           PARAMS ((tic54x_insn *));
static int      tic54x_parse_insn       PARAMS ((tic54x_insn *, char *));
static int      tic54x_parse_insn       PARAMS ((tic54x_insn *, char *));
static int      next_line_shows_parallel PARAMS ((char *));
static int      next_line_shows_parallel PARAMS ((char *));
static int      tic54x_parse_parallel_insn_firstline PARAMS ((tic54x_insn *, char *));
static int      tic54x_parse_parallel_insn_firstline PARAMS ((tic54x_insn *, char *));
static int      tic54x_parse_parallel_insn_lastline PARAMS ((tic54x_insn *, char *));
static int      tic54x_parse_parallel_insn_lastline PARAMS ((tic54x_insn *, char *));
static char *   subsym_get_arg          PARAMS ((char *, char *, char **, int));
static char *   subsym_get_arg          PARAMS ((char *, char *, char **, int));
static void     subsym_create_or_replace PARAMS ((char *, char *));
static void     subsym_create_or_replace PARAMS ((char *, char *));
static char *   subsym_lookup           PARAMS ((char *, int));
static char *   subsym_lookup           PARAMS ((char *, int));
static char *   subsym_substitute       PARAMS ((char *, int));
static char *   subsym_substitute       PARAMS ((char *, int));
 
 
 
 
void
void
md_show_usage (stream)
md_show_usage (stream)
     FILE *stream;
     FILE *stream;
{
{
  fprintf (stream, _("C54x-specific command line  options:\n"));
  fprintf (stream, _("C54x-specific command line  options:\n"));
  fprintf (stream, _("-mfar-mode | -mf          Use extended addressing\n"));
  fprintf (stream, _("-mfar-mode | -mf          Use extended addressing\n"));
  fprintf (stream, _("-mcpu=<CPU version>       Specify the CPU version\n"));
  fprintf (stream, _("-mcpu=<CPU version>       Specify the CPU version\n"));
  fprintf (stream, _("-merrors-to-file <filename>\n"));
  fprintf (stream, _("-merrors-to-file <filename>\n"));
  fprintf (stream, _("-me <filename>            Redirect errors to a file\n"));
  fprintf (stream, _("-me <filename>            Redirect errors to a file\n"));
}
}
 
 
/* Output a single character (upper octect is zero).  */
/* Output a single character (upper octect is zero).  */
 
 
static void
static void
tic54x_emit_char (c)
tic54x_emit_char (c)
     char c;
     char c;
{
{
  expressionS exp;
  expressionS exp;
 
 
  exp.X_op = O_constant;
  exp.X_op = O_constant;
  exp.X_add_number = c;
  exp.X_add_number = c;
  emit_expr (&exp, 2);
  emit_expr (&exp, 2);
}
}
 
 
/* Walk backwards in the frag chain.  */
/* Walk backwards in the frag chain.  */
 
 
static fragS *
static fragS *
frag_prev (frag, seg)
frag_prev (frag, seg)
     fragS *frag;
     fragS *frag;
     segT seg;
     segT seg;
{
{
  segment_info_type *seginfo = seg_info (seg);
  segment_info_type *seginfo = seg_info (seg);
  fragS *fragp;
  fragS *fragp;
 
 
  for (fragp = seginfo->frchainP->frch_root; fragp; fragp = fragp->fr_next)
  for (fragp = seginfo->frchainP->frch_root; fragp; fragp = fragp->fr_next)
    if (fragp->fr_next == frag)
    if (fragp->fr_next == frag)
      return fragp;
      return fragp;
 
 
  return NULL;
  return NULL;
}
}
 
 
static fragS *
static fragS *
bit_offset_frag (frag, seg)
bit_offset_frag (frag, seg)
     fragS *frag;
     fragS *frag;
     segT seg;
     segT seg;
{
{
  while (frag != NULL)
  while (frag != NULL)
    {
    {
      if (frag->fr_fix == 0
      if (frag->fr_fix == 0
          && frag->fr_opcode == NULL
          && frag->fr_opcode == NULL
          && frag->tc_frag_data == 0)
          && frag->tc_frag_data == 0)
        frag = frag_prev (frag, seg);
        frag = frag_prev (frag, seg);
      else
      else
        return frag;
        return frag;
    }
    }
  return NULL;
  return NULL;
}
}
 
 
/* Return the number of bits allocated in the most recent word, or zero if
/* Return the number of bits allocated in the most recent word, or zero if
   none. .field/.space/.bes may leave words partially allocated.  */
   none. .field/.space/.bes may leave words partially allocated.  */
 
 
static int
static int
frag_bit_offset (frag, seg)
frag_bit_offset (frag, seg)
     fragS *frag;
     fragS *frag;
     segT seg;
     segT seg;
{
{
  frag = bit_offset_frag (frag, seg);
  frag = bit_offset_frag (frag, seg);
 
 
  if (frag)
  if (frag)
    return frag->fr_opcode != NULL ? -1 : frag->tc_frag_data;
    return frag->fr_opcode != NULL ? -1 : frag->tc_frag_data;
 
 
  return 0;
  return 0;
}
}
 
 
/* Read an expression from a C string; returns a pointer past the end of the
/* Read an expression from a C string; returns a pointer past the end of the
   expression.  */
   expression.  */
 
 
static char *
static char *
parse_expression (str, exp)
parse_expression (str, exp)
     char *str;
     char *str;
     expressionS * exp;
     expressionS * exp;
{
{
  char *s;
  char *s;
  char *tmp;
  char *tmp;
 
 
  tmp = input_line_pointer;     /* Save line pointer.  */
  tmp = input_line_pointer;     /* Save line pointer.  */
  input_line_pointer = str;
  input_line_pointer = str;
  expression (exp);
  expression (exp);
  s = input_line_pointer;
  s = input_line_pointer;
  input_line_pointer = tmp;     /* Restore line pointer.  */
  input_line_pointer = tmp;     /* Restore line pointer.  */
  return s;                     /* Return pointer to where parsing stopped.  */
  return s;                     /* Return pointer to where parsing stopped.  */
}
}
 
 
/* .asg "character-string"|character-string, symbol
/* .asg "character-string"|character-string, symbol
 
 
   .eval is the only pseudo-op allowed to perform arithmetic on substitution
   .eval is the only pseudo-op allowed to perform arithmetic on substitution
   symbols.  all other use of symbols defined with .asg are currently
   symbols.  all other use of symbols defined with .asg are currently
   unsupported.  */
   unsupported.  */
 
 
static void
static void
tic54x_asg (x)
tic54x_asg (x)
     int x ATTRIBUTE_UNUSED;
     int x ATTRIBUTE_UNUSED;
{
{
  int c;
  int c;
  char *name;
  char *name;
  char *str;
  char *str;
  char *tmp;
  char *tmp;
  int quoted = *input_line_pointer == '"';
  int quoted = *input_line_pointer == '"';
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  if (quoted)
  if (quoted)
    {
    {
      int len;
      int len;
      str = demand_copy_C_string (&len);
      str = demand_copy_C_string (&len);
      c = *input_line_pointer;
      c = *input_line_pointer;
    }
    }
  else
  else
    {
    {
      str = input_line_pointer;
      str = input_line_pointer;
      while ((c = *input_line_pointer) != ',')
      while ((c = *input_line_pointer) != ',')
        {
        {
          if (is_end_of_line[(int) *input_line_pointer])
          if (is_end_of_line[(int) *input_line_pointer])
            break;
            break;
          ++input_line_pointer;
          ++input_line_pointer;
        }
        }
      *input_line_pointer = 0;
      *input_line_pointer = 0;
    }
    }
  if (c != ',')
  if (c != ',')
    {
    {
      as_bad (_("Comma and symbol expected for '.asg STRING, SYMBOL'"));
      as_bad (_("Comma and symbol expected for '.asg STRING, SYMBOL'"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  name = ++input_line_pointer;
  name = ++input_line_pointer;
  c = get_symbol_end ();        /* Get terminator.  */
  c = get_symbol_end ();        /* Get terminator.  */
  if (!ISALPHA (*name))
  if (!ISALPHA (*name))
    {
    {
      as_bad ("symbols assigned with .asg must begin with a letter");
      as_bad ("symbols assigned with .asg must begin with a letter");
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  tmp = xmalloc (strlen (str) + 1);
  tmp = xmalloc (strlen (str) + 1);
  strcpy (tmp, str);
  strcpy (tmp, str);
  str = tmp;
  str = tmp;
  tmp = xmalloc (strlen (name) + 1);
  tmp = xmalloc (strlen (name) + 1);
  strcpy (tmp, name);
  strcpy (tmp, name);
  name = tmp;
  name = tmp;
  subsym_create_or_replace (name, str);
  subsym_create_or_replace (name, str);
  *input_line_pointer = c;
  *input_line_pointer = c;
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
/* .eval expression, symbol
/* .eval expression, symbol
   There's something screwy about this.  The other assembler sometimes does and
   There's something screwy about this.  The other assembler sometimes does and
   sometimes doesn't substitute symbols defined with .eval.
   sometimes doesn't substitute symbols defined with .eval.
   We'll put the symbols into the subsym table as well as the normal symbol
   We'll put the symbols into the subsym table as well as the normal symbol
   table, since that's what works best.  */
   table, since that's what works best.  */
 
 
static void
static void
tic54x_eval (x)
tic54x_eval (x)
     int x ATTRIBUTE_UNUSED;
     int x ATTRIBUTE_UNUSED;
{
{
  char c;
  char c;
  int value;
  int value;
  char *name;
  char *name;
  symbolS *symbolP;
  symbolS *symbolP;
  char valuestr[32], *tmp;
  char valuestr[32], *tmp;
  int quoted;
  int quoted;
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  SKIP_WHITESPACE ();
  SKIP_WHITESPACE ();
 
 
  quoted = *input_line_pointer == '"';
  quoted = *input_line_pointer == '"';
  if (quoted)
  if (quoted)
    ++input_line_pointer;
    ++input_line_pointer;
  value = get_absolute_expression ();
  value = get_absolute_expression ();
  if (quoted)
  if (quoted)
    {
    {
      if (*input_line_pointer != '"')
      if (*input_line_pointer != '"')
        {
        {
          as_bad (_("Unterminated string after absolute expression"));
          as_bad (_("Unterminated string after absolute expression"));
          ignore_rest_of_line ();
          ignore_rest_of_line ();
          return;
          return;
        }
        }
      ++input_line_pointer;
      ++input_line_pointer;
    }
    }
  if (*input_line_pointer++ != ',')
  if (*input_line_pointer++ != ',')
    {
    {
      as_bad (_("Comma and symbol expected for '.eval EXPR, SYMBOL'"));
      as_bad (_("Comma and symbol expected for '.eval EXPR, SYMBOL'"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
  name = input_line_pointer;
  name = input_line_pointer;
  c = get_symbol_end ();        /* Get terminator.  */
  c = get_symbol_end ();        /* Get terminator.  */
  tmp = xmalloc (strlen (name) + 1);
  tmp = xmalloc (strlen (name) + 1);
  name = strcpy (tmp, name);
  name = strcpy (tmp, name);
  *input_line_pointer = c;
  *input_line_pointer = c;
 
 
  if (!ISALPHA (*name))
  if (!ISALPHA (*name))
    {
    {
      as_bad (_("symbols assigned with .eval must begin with a letter"));
      as_bad (_("symbols assigned with .eval must begin with a letter"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
  symbolP = symbol_new (name, absolute_section,
  symbolP = symbol_new (name, absolute_section,
                        (valueT) value, &zero_address_frag);
                        (valueT) value, &zero_address_frag);
  SF_SET_LOCAL (symbolP);
  SF_SET_LOCAL (symbolP);
  symbol_table_insert (symbolP);
  symbol_table_insert (symbolP);
 
 
  /* The "other" assembler sometimes doesn't put .eval's in the subsym table
  /* The "other" assembler sometimes doesn't put .eval's in the subsym table
     But since there's not written rule as to when, don't even bother trying
     But since there's not written rule as to when, don't even bother trying
     to match their behavior.  */
     to match their behavior.  */
  sprintf (valuestr, "%d", value);
  sprintf (valuestr, "%d", value);
  tmp = xmalloc (strlen (valuestr) + 1);
  tmp = xmalloc (strlen (valuestr) + 1);
  strcpy (tmp, valuestr);
  strcpy (tmp, valuestr);
  subsym_create_or_replace (name, tmp);
  subsym_create_or_replace (name, tmp);
 
 
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
/* .bss symbol, size [, [blocking flag] [, alignment flag]
/* .bss symbol, size [, [blocking flag] [, alignment flag]
 
 
   alignment is to a longword boundary; blocking is to 128-word boundary.
   alignment is to a longword boundary; blocking is to 128-word boundary.
 
 
   1) if there is a hole in memory, this directive should attempt to fill it
   1) if there is a hole in memory, this directive should attempt to fill it
      (not yet implemented).
      (not yet implemented).
 
 
   2) if the blocking flag is not set, allocate at the current SPC
   2) if the blocking flag is not set, allocate at the current SPC
      otherwise, check to see if the current SPC plus the space to be
      otherwise, check to see if the current SPC plus the space to be
      allocated crosses the page boundary (128 words).
      allocated crosses the page boundary (128 words).
      if there's not enough space, create a hole and align with the next page
      if there's not enough space, create a hole and align with the next page
      boundary.
      boundary.
      (not yet implemented).  */
      (not yet implemented).  */
 
 
static void
static void
tic54x_bss (x)
tic54x_bss (x)
     int x ATTRIBUTE_UNUSED;
     int x ATTRIBUTE_UNUSED;
{
{
  char c;
  char c;
  char *name;
  char *name;
  char *p;
  char *p;
  int words;
  int words;
  segT current_seg;
  segT current_seg;
  subsegT current_subseg;
  subsegT current_subseg;
  symbolS *symbolP;
  symbolS *symbolP;
  int block = 0;
  int block = 0;
  int align = 0;
  int align = 0;
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  current_seg = now_seg;        /* Save current seg.  */
  current_seg = now_seg;        /* Save current seg.  */
  current_subseg = now_subseg;  /* Save current subseg.  */
  current_subseg = now_subseg;  /* Save current subseg.  */
 
 
  name = input_line_pointer;
  name = input_line_pointer;
  c = get_symbol_end ();        /* Get terminator.  */
  c = get_symbol_end ();        /* Get terminator.  */
  if (c != ',')
  if (c != ',')
    {
    {
      as_bad (".bss size argument missing\n");
      as_bad (".bss size argument missing\n");
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  ++input_line_pointer;
  ++input_line_pointer;
  words = get_absolute_expression ();
  words = get_absolute_expression ();
  if (words < 0)
  if (words < 0)
    {
    {
      as_bad (".bss size %d < 0!", words);
      as_bad (".bss size %d < 0!", words);
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  if (*input_line_pointer == ',')
  if (*input_line_pointer == ',')
    {
    {
      /* The blocking flag may be missing.  */
      /* The blocking flag may be missing.  */
      ++input_line_pointer;
      ++input_line_pointer;
      if (*input_line_pointer != ',')
      if (*input_line_pointer != ',')
        block = get_absolute_expression ();
        block = get_absolute_expression ();
      else
      else
        block = 0;
        block = 0;
 
 
      if (*input_line_pointer == ',')
      if (*input_line_pointer == ',')
        {
        {
          ++input_line_pointer;
          ++input_line_pointer;
          align = get_absolute_expression ();
          align = get_absolute_expression ();
        }
        }
      else
      else
        align = 0;
        align = 0;
    }
    }
  else
  else
    block = align = 0;
    block = align = 0;
 
 
  subseg_set (bss_section, 0);
  subseg_set (bss_section, 0);
  symbolP = symbol_find_or_make (name);
  symbolP = symbol_find_or_make (name);
 
 
  if (S_GET_SEGMENT (symbolP) == bss_section)
  if (S_GET_SEGMENT (symbolP) == bss_section)
    symbolP->sy_frag->fr_symbol = (symbolS *) NULL;
    symbolP->sy_frag->fr_symbol = (symbolS *) NULL;
 
 
  symbol_set_frag (symbolP, frag_now);
  symbol_set_frag (symbolP, frag_now);
  p = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
  p = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
                (offsetT) (words * OCTETS_PER_BYTE), (char *) 0);
                (offsetT) (words * OCTETS_PER_BYTE), (char *) 0);
  *p = 0;                        /* Fill char.  */
  *p = 0;                        /* Fill char.  */
 
 
  S_SET_SEGMENT (symbolP, bss_section);
  S_SET_SEGMENT (symbolP, bss_section);
 
 
  /* The symbol may already have been created with a preceding
  /* The symbol may already have been created with a preceding
     ".globl" directive -- be careful not to step on storage class
     ".globl" directive -- be careful not to step on storage class
     in that case.  Otherwise, set it to static.  */
     in that case.  Otherwise, set it to static.  */
  if (S_GET_STORAGE_CLASS (symbolP) != C_EXT)
  if (S_GET_STORAGE_CLASS (symbolP) != C_EXT)
    S_SET_STORAGE_CLASS (symbolP, C_STAT);
    S_SET_STORAGE_CLASS (symbolP, C_STAT);
 
 
  if (align)
  if (align)
    {
    {
      /* s_align eats end of line; restore it */
      /* s_align eats end of line; restore it */
      s_align_bytes (4);
      s_align_bytes (4);
      --input_line_pointer;
      --input_line_pointer;
    }
    }
 
 
  if (block)
  if (block)
    bss_section->flags |= SEC_TIC54X_BLOCK;
    bss_section->flags |= SEC_TIC54X_BLOCK;
 
 
  subseg_set (current_seg, current_subseg);     /* Restore current seg.  */
  subseg_set (current_seg, current_subseg);     /* Restore current seg.  */
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
static void
static void
stag_add_field_symbols (stag, path, base_offset, rootsym, root_stag_name)
stag_add_field_symbols (stag, path, base_offset, rootsym, root_stag_name)
     struct stag *stag;
     struct stag *stag;
     const char *path;
     const char *path;
     bfd_vma base_offset;
     bfd_vma base_offset;
     symbolS *rootsym;
     symbolS *rootsym;
     const char *root_stag_name;
     const char *root_stag_name;
{
{
  char prefix[strlen (path) + 2];
  char prefix[strlen (path) + 2];
  struct stag_field *field = stag->field;
  struct stag_field *field = stag->field;
 
 
  /* Construct a symbol for every field contained within this structure
  /* Construct a symbol for every field contained within this structure
     including fields within structure fields.  */
     including fields within structure fields.  */
  strcpy (prefix, path);
  strcpy (prefix, path);
  if (*path)
  if (*path)
    strcat (prefix, ".");
    strcat (prefix, ".");
 
 
  while (field != NULL)
  while (field != NULL)
    {
    {
      int len = strlen (prefix) + strlen (field->name) + 2;
      int len = strlen (prefix) + strlen (field->name) + 2;
      char *name = xmalloc (len);
      char *name = xmalloc (len);
      strcpy (name, prefix);
      strcpy (name, prefix);
      strcat (name, field->name);
      strcat (name, field->name);
 
 
      if (rootsym == NULL)
      if (rootsym == NULL)
        {
        {
          symbolS *sym;
          symbolS *sym;
          sym = symbol_new (name, absolute_section,
          sym = symbol_new (name, absolute_section,
                            (field->stag ? field->offset :
                            (field->stag ? field->offset :
                             (valueT) (base_offset + field->offset)),
                             (valueT) (base_offset + field->offset)),
                            &zero_address_frag);
                            &zero_address_frag);
          SF_SET_LOCAL (sym);
          SF_SET_LOCAL (sym);
          symbol_table_insert (sym);
          symbol_table_insert (sym);
        }
        }
      else
      else
        {
        {
          char *replacement = xmalloc (strlen (name)
          char *replacement = xmalloc (strlen (name)
                                       + strlen (stag->name) + 2);
                                       + strlen (stag->name) + 2);
          strcpy (replacement, S_GET_NAME (rootsym));
          strcpy (replacement, S_GET_NAME (rootsym));
          strcat (replacement, "+");
          strcat (replacement, "+");
          strcat (replacement, root_stag_name);
          strcat (replacement, root_stag_name);
          strcat (replacement, name + strlen (S_GET_NAME (rootsym)));
          strcat (replacement, name + strlen (S_GET_NAME (rootsym)));
          hash_insert (subsym_hash[0], name, replacement);
          hash_insert (subsym_hash[0], name, replacement);
        }
        }
 
 
      /* Recurse if the field is a structure.
      /* Recurse if the field is a structure.
         Note the field offset is relative to the outermost struct.  */
         Note the field offset is relative to the outermost struct.  */
      if (field->stag != NULL)
      if (field->stag != NULL)
        stag_add_field_symbols (field->stag, name,
        stag_add_field_symbols (field->stag, name,
                                field->offset,
                                field->offset,
                                rootsym, root_stag_name);
                                rootsym, root_stag_name);
      field = field->next;
      field = field->next;
    }
    }
}
}
 
 
/* Keep track of stag fields so that when structures are nested we can add the
/* Keep track of stag fields so that when structures are nested we can add the
   complete dereferencing symbols to the symbol table.  */
   complete dereferencing symbols to the symbol table.  */
 
 
static void
static void
stag_add_field (parent, name, offset, stag)
stag_add_field (parent, name, offset, stag)
     struct stag *parent;
     struct stag *parent;
     const char *name;
     const char *name;
     bfd_vma offset;
     bfd_vma offset;
     struct stag *stag;
     struct stag *stag;
{
{
  struct stag_field *sfield = xmalloc (sizeof (struct stag_field));
  struct stag_field *sfield = xmalloc (sizeof (struct stag_field));
 
 
  memset (sfield, 0, sizeof (*sfield));
  memset (sfield, 0, sizeof (*sfield));
  sfield->name = strcpy (xmalloc (strlen (name) + 1), name);
  sfield->name = strcpy (xmalloc (strlen (name) + 1), name);
  sfield->offset = offset;
  sfield->offset = offset;
  sfield->bitfield_offset = parent->current_bitfield_offset;
  sfield->bitfield_offset = parent->current_bitfield_offset;
  sfield->stag = stag;
  sfield->stag = stag;
  if (parent->field == NULL)
  if (parent->field == NULL)
    parent->field = sfield;
    parent->field = sfield;
  else
  else
    {
    {
      struct stag_field *sf = parent->field;
      struct stag_field *sf = parent->field;
      while (sf->next != NULL)
      while (sf->next != NULL)
        sf = sf->next;
        sf = sf->next;
      sf->next = sfield;
      sf->next = sfield;
    }
    }
  /* Only create a symbol for this field if the parent has no name.  */
  /* Only create a symbol for this field if the parent has no name.  */
  if (!strncmp (".fake", parent->name, 5))
  if (!strncmp (".fake", parent->name, 5))
    {
    {
      symbolS *sym = symbol_new (name, absolute_section,
      symbolS *sym = symbol_new (name, absolute_section,
                                 (valueT) offset, &zero_address_frag);
                                 (valueT) offset, &zero_address_frag);
      SF_SET_LOCAL (sym);
      SF_SET_LOCAL (sym);
      symbol_table_insert (sym);
      symbol_table_insert (sym);
    }
    }
}
}
 
 
/* [STAG] .struct       [OFFSET]
/* [STAG] .struct       [OFFSET]
   Start defining structure offsets (symbols in absolute section).  */
   Start defining structure offsets (symbols in absolute section).  */
 
 
static void
static void
tic54x_struct (arg)
tic54x_struct (arg)
     int arg;
     int arg;
{
{
  int start_offset = 0;
  int start_offset = 0;
  int is_union = arg;
  int is_union = arg;
 
 
  if (!current_stag)
  if (!current_stag)
    {
    {
      /* Starting a new struct, switch to absolute section.  */
      /* Starting a new struct, switch to absolute section.  */
      stag_saved_seg = now_seg;
      stag_saved_seg = now_seg;
      stag_saved_subseg = now_subseg;
      stag_saved_subseg = now_subseg;
      subseg_set (absolute_section, 0);
      subseg_set (absolute_section, 0);
    }
    }
  /* Align the current pointer.  */
  /* Align the current pointer.  */
  else if (current_stag->current_bitfield_offset != 0)
  else if (current_stag->current_bitfield_offset != 0)
    {
    {
      ++abs_section_offset;
      ++abs_section_offset;
      current_stag->current_bitfield_offset = 0;
      current_stag->current_bitfield_offset = 0;
    }
    }
 
 
  /* Offset expression is only meaningful for global .structs.  */
  /* Offset expression is only meaningful for global .structs.  */
  if (!is_union)
  if (!is_union)
    {
    {
      /* Offset is ignored in inner structs.  */
      /* Offset is ignored in inner structs.  */
      SKIP_WHITESPACE ();
      SKIP_WHITESPACE ();
      if (!is_end_of_line[(int) *input_line_pointer])
      if (!is_end_of_line[(int) *input_line_pointer])
        start_offset = get_absolute_expression ();
        start_offset = get_absolute_expression ();
      else
      else
        start_offset = 0;
        start_offset = 0;
    }
    }
 
 
  if (current_stag)
  if (current_stag)
    {
    {
      /* Nesting, link to outer one.  */
      /* Nesting, link to outer one.  */
      current_stag->inner = (struct stag *) xmalloc (sizeof (struct stag));
      current_stag->inner = (struct stag *) xmalloc (sizeof (struct stag));
      memset (current_stag->inner, 0, sizeof (struct stag));
      memset (current_stag->inner, 0, sizeof (struct stag));
      current_stag->inner->outer = current_stag;
      current_stag->inner->outer = current_stag;
      current_stag = current_stag->inner;
      current_stag = current_stag->inner;
      if (start_offset)
      if (start_offset)
        as_warn (_("Offset on nested structures is ignored"));
        as_warn (_("Offset on nested structures is ignored"));
      start_offset = abs_section_offset;
      start_offset = abs_section_offset;
    }
    }
  else
  else
    {
    {
      current_stag = (struct stag *) xmalloc (sizeof (struct stag));
      current_stag = (struct stag *) xmalloc (sizeof (struct stag));
      memset (current_stag, 0, sizeof (struct stag));
      memset (current_stag, 0, sizeof (struct stag));
      abs_section_offset = start_offset;
      abs_section_offset = start_offset;
    }
    }
  current_stag->is_union = is_union;
  current_stag->is_union = is_union;
 
 
  if (line_label == NULL)
  if (line_label == NULL)
    {
    {
      static int struct_count = 0;
      static int struct_count = 0;
      char fake[] = ".fake_stagNNNNNNN";
      char fake[] = ".fake_stagNNNNNNN";
      sprintf (fake, ".fake_stag%d", struct_count++);
      sprintf (fake, ".fake_stag%d", struct_count++);
      current_stag->sym = symbol_new (fake, absolute_section,
      current_stag->sym = symbol_new (fake, absolute_section,
                                      (valueT) abs_section_offset,
                                      (valueT) abs_section_offset,
                                      &zero_address_frag);
                                      &zero_address_frag);
    }
    }
  else
  else
    {
    {
      char label[strlen (S_GET_NAME (line_label)) + 1];
      char label[strlen (S_GET_NAME (line_label)) + 1];
      strcpy (label, S_GET_NAME (line_label));
      strcpy (label, S_GET_NAME (line_label));
      current_stag->sym = symbol_new (label, absolute_section,
      current_stag->sym = symbol_new (label, absolute_section,
                                      (valueT) abs_section_offset,
                                      (valueT) abs_section_offset,
                                      &zero_address_frag);
                                      &zero_address_frag);
    }
    }
  current_stag->name = S_GET_NAME (current_stag->sym);
  current_stag->name = S_GET_NAME (current_stag->sym);
  SF_SET_LOCAL (current_stag->sym);
  SF_SET_LOCAL (current_stag->sym);
  /* Nested .structs don't go into the symbol table.  */
  /* Nested .structs don't go into the symbol table.  */
  if (current_stag->outer == NULL)
  if (current_stag->outer == NULL)
    symbol_table_insert (current_stag->sym);
    symbol_table_insert (current_stag->sym);
 
 
  line_label = NULL;
  line_label = NULL;
}
}
 
 
/* [LABEL] .endstruct
/* [LABEL] .endstruct
   finish defining structure offsets; optional LABEL's value will be the size
   finish defining structure offsets; optional LABEL's value will be the size
   of the structure.  */
   of the structure.  */
 
 
static void
static void
tic54x_endstruct (is_union)
tic54x_endstruct (is_union)
     int is_union;
     int is_union;
{
{
  int size;
  int size;
  const char *path =
  const char *path =
    !strncmp (current_stag->name, ".fake", 5) ? "" : current_stag->name;
    !strncmp (current_stag->name, ".fake", 5) ? "" : current_stag->name;
 
 
  if (!current_stag || current_stag->is_union != is_union)
  if (!current_stag || current_stag->is_union != is_union)
    {
    {
      as_bad (_(".end%s without preceding .%s"),
      as_bad (_(".end%s without preceding .%s"),
              is_union ? "union" : "struct",
              is_union ? "union" : "struct",
              is_union ? "union" : "struct");
              is_union ? "union" : "struct");
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  /* Align end of structures.  */
  /* Align end of structures.  */
  if (current_stag->current_bitfield_offset)
  if (current_stag->current_bitfield_offset)
    {
    {
      ++abs_section_offset;
      ++abs_section_offset;
      current_stag->current_bitfield_offset = 0;
      current_stag->current_bitfield_offset = 0;
    }
    }
 
 
  if (current_stag->is_union)
  if (current_stag->is_union)
    size = current_stag->size;
    size = current_stag->size;
  else
  else
    size = abs_section_offset - S_GET_VALUE (current_stag->sym);
    size = abs_section_offset - S_GET_VALUE (current_stag->sym);
  if (line_label != NULL)
  if (line_label != NULL)
    {
    {
      S_SET_VALUE (line_label, size);
      S_SET_VALUE (line_label, size);
      symbol_table_insert (line_label);
      symbol_table_insert (line_label);
      line_label = NULL;
      line_label = NULL;
    }
    }
 
 
  /* Union size has already been calculated.  */
  /* Union size has already been calculated.  */
  if (!current_stag->is_union)
  if (!current_stag->is_union)
    current_stag->size = size;
    current_stag->size = size;
  /* Nested .structs don't get put in the stag table.  */
  /* Nested .structs don't get put in the stag table.  */
  if (current_stag->outer == NULL)
  if (current_stag->outer == NULL)
    {
    {
      hash_insert (stag_hash, current_stag->name, current_stag);
      hash_insert (stag_hash, current_stag->name, current_stag);
      stag_add_field_symbols (current_stag, path,
      stag_add_field_symbols (current_stag, path,
                              S_GET_VALUE (current_stag->sym),
                              S_GET_VALUE (current_stag->sym),
                              NULL, NULL);
                              NULL, NULL);
    }
    }
  current_stag = current_stag->outer;
  current_stag = current_stag->outer;
 
 
  /* If this is a nested .struct/.union, add it as a field to the enclosing
  /* If this is a nested .struct/.union, add it as a field to the enclosing
     one.  otherwise, restore the section we were in.  */
     one.  otherwise, restore the section we were in.  */
  if (current_stag != NULL)
  if (current_stag != NULL)
    {
    {
      stag_add_field (current_stag, current_stag->inner->name,
      stag_add_field (current_stag, current_stag->inner->name,
                      S_GET_VALUE (current_stag->inner->sym),
                      S_GET_VALUE (current_stag->inner->sym),
                      current_stag->inner);
                      current_stag->inner);
    }
    }
  else
  else
    subseg_set (stag_saved_seg, stag_saved_subseg);
    subseg_set (stag_saved_seg, stag_saved_subseg);
}
}
 
 
/* [LABEL]      .tag    STAG
/* [LABEL]      .tag    STAG
   Reference a structure within a structure, as a sized field with an optional
   Reference a structure within a structure, as a sized field with an optional
   label.
   label.
   If used outside of a .struct/.endstruct, overlays the given structure
   If used outside of a .struct/.endstruct, overlays the given structure
   format on the existing allocated space.  */
   format on the existing allocated space.  */
 
 
static void
static void
tic54x_tag (ignore)
tic54x_tag (ignore)
     int ignore ATTRIBUTE_UNUSED;
     int ignore ATTRIBUTE_UNUSED;
{
{
  char *name = input_line_pointer;
  char *name = input_line_pointer;
  int c = get_symbol_end ();
  int c = get_symbol_end ();
  struct stag *stag = (struct stag *) hash_find (stag_hash, name);
  struct stag *stag = (struct stag *) hash_find (stag_hash, name);
 
 
  if (!stag)
  if (!stag)
    {
    {
      if (*name)
      if (*name)
        as_bad (_("Unrecognized struct/union tag '%s'"), name);
        as_bad (_("Unrecognized struct/union tag '%s'"), name);
      else
      else
        as_bad (_(".tag requires a structure tag"));
        as_bad (_(".tag requires a structure tag"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
  if (line_label == NULL)
  if (line_label == NULL)
    {
    {
      as_bad (_("Label required for .tag"));
      as_bad (_("Label required for .tag"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
  else
  else
    {
    {
      char label[strlen (S_GET_NAME (line_label)) + 1];
      char label[strlen (S_GET_NAME (line_label)) + 1];
 
 
      strcpy (label, S_GET_NAME (line_label));
      strcpy (label, S_GET_NAME (line_label));
      if (current_stag != NULL)
      if (current_stag != NULL)
        stag_add_field (current_stag, label,
        stag_add_field (current_stag, label,
                        abs_section_offset - S_GET_VALUE (current_stag->sym),
                        abs_section_offset - S_GET_VALUE (current_stag->sym),
                        stag);
                        stag);
      else
      else
        {
        {
          symbolS *sym = symbol_find (label);
          symbolS *sym = symbol_find (label);
 
 
          if (!sym)
          if (!sym)
            {
            {
              as_bad (_(".tag target '%s' undefined"), label);
              as_bad (_(".tag target '%s' undefined"), label);
              ignore_rest_of_line ();
              ignore_rest_of_line ();
              return;
              return;
            }
            }
          stag_add_field_symbols (stag, S_GET_NAME (sym),
          stag_add_field_symbols (stag, S_GET_NAME (sym),
                                  S_GET_VALUE (stag->sym), sym, stag->name);
                                  S_GET_VALUE (stag->sym), sym, stag->name);
        }
        }
    }
    }
 
 
  /* Bump by the struct size, but only if we're within a .struct section.  */
  /* Bump by the struct size, but only if we're within a .struct section.  */
  if (current_stag != NULL && !current_stag->is_union)
  if (current_stag != NULL && !current_stag->is_union)
    abs_section_offset += stag->size;
    abs_section_offset += stag->size;
 
 
  *input_line_pointer = c;
  *input_line_pointer = c;
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
  line_label = NULL;
  line_label = NULL;
}
}
 
 
/* Handle all .byte, .char, .double, .field, .float, .half, .int, .long,
/* Handle all .byte, .char, .double, .field, .float, .half, .int, .long,
   .short, .string, .ubyte, .uchar, .uhalf, .uint, .ulong, .ushort, .uword,
   .short, .string, .ubyte, .uchar, .uhalf, .uint, .ulong, .ushort, .uword,
   and .word.  */
   and .word.  */
 
 
static void
static void
tic54x_struct_field (type)
tic54x_struct_field (type)
     int type;
     int type;
{
{
  int size;
  int size;
  int count = 1;
  int count = 1;
  int new_bitfield_offset = 0;
  int new_bitfield_offset = 0;
  int field_align = current_stag->current_bitfield_offset != 0;
  int field_align = current_stag->current_bitfield_offset != 0;
  int longword_align = 0;
  int longword_align = 0;
 
 
  SKIP_WHITESPACE ();
  SKIP_WHITESPACE ();
  if (!is_end_of_line[(int) *input_line_pointer])
  if (!is_end_of_line[(int) *input_line_pointer])
    count = get_absolute_expression ();
    count = get_absolute_expression ();
 
 
  switch (type)
  switch (type)
    {
    {
    case 'b':
    case 'b':
    case 'B':
    case 'B':
    case 'c':
    case 'c':
    case 'C':
    case 'C':
    case 'h':
    case 'h':
    case 'H':
    case 'H':
    case 'i':
    case 'i':
    case 'I':
    case 'I':
    case 's':
    case 's':
    case 'S':
    case 'S':
    case 'w':
    case 'w':
    case 'W':
    case 'W':
    case '*': /* String.  */
    case '*': /* String.  */
      size = 1;
      size = 1;
      break;
      break;
    case 'f':
    case 'f':
    case 'l':
    case 'l':
    case 'L':
    case 'L':
      longword_align = 1;
      longword_align = 1;
      size = 2;
      size = 2;
      break;
      break;
    case '.': /* Bitfield.  */
    case '.': /* Bitfield.  */
      size = 0;
      size = 0;
      if (count < 1 || count > 32)
      if (count < 1 || count > 32)
        {
        {
          as_bad (_(".field count '%d' out of range (1 <= X <= 32)"), count);
          as_bad (_(".field count '%d' out of range (1 <= X <= 32)"), count);
          ignore_rest_of_line ();
          ignore_rest_of_line ();
          return;
          return;
        }
        }
      if (current_stag->current_bitfield_offset + count > 16)
      if (current_stag->current_bitfield_offset + count > 16)
        {
        {
          /* Set the appropriate size and new field offset.  */
          /* Set the appropriate size and new field offset.  */
          if (count == 32)
          if (count == 32)
            {
            {
              size = 2;
              size = 2;
              count = 1;
              count = 1;
            }
            }
          else if (count > 16)
          else if (count > 16)
            {
            {
              size = 1;
              size = 1;
              count = 1;
              count = 1;
              new_bitfield_offset = count - 16;
              new_bitfield_offset = count - 16;
            }
            }
          else
          else
            new_bitfield_offset = count;
            new_bitfield_offset = count;
        }
        }
      else
      else
        {
        {
          field_align = 0;
          field_align = 0;
          new_bitfield_offset = current_stag->current_bitfield_offset + count;
          new_bitfield_offset = current_stag->current_bitfield_offset + count;
        }
        }
      break;
      break;
    default:
    default:
      as_bad (_("Unrecognized field type '%c'"), type);
      as_bad (_("Unrecognized field type '%c'"), type);
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  if (field_align)
  if (field_align)
    {
    {
      /* Align to the actual starting position of the field.  */
      /* Align to the actual starting position of the field.  */
      current_stag->current_bitfield_offset = 0;
      current_stag->current_bitfield_offset = 0;
      ++abs_section_offset;
      ++abs_section_offset;
    }
    }
  /* Align to longword boundary.  */
  /* Align to longword boundary.  */
  if (longword_align && (abs_section_offset & 0x1))
  if (longword_align && (abs_section_offset & 0x1))
    ++abs_section_offset;
    ++abs_section_offset;
 
 
  if (line_label == NULL)
  if (line_label == NULL)
    {
    {
      static int fieldno = 0;
      static int fieldno = 0;
      char fake[] = ".fake_fieldNNNNN";
      char fake[] = ".fake_fieldNNNNN";
 
 
      sprintf (fake, ".fake_field%d", fieldno++);
      sprintf (fake, ".fake_field%d", fieldno++);
      stag_add_field (current_stag, fake,
      stag_add_field (current_stag, fake,
                      abs_section_offset - S_GET_VALUE (current_stag->sym),
                      abs_section_offset - S_GET_VALUE (current_stag->sym),
                      NULL);
                      NULL);
    }
    }
  else
  else
    {
    {
      char label[strlen (S_GET_NAME (line_label) + 1)];
      char label[strlen (S_GET_NAME (line_label) + 1)];
 
 
      strcpy (label, S_GET_NAME (line_label));
      strcpy (label, S_GET_NAME (line_label));
      stag_add_field (current_stag, label,
      stag_add_field (current_stag, label,
                      abs_section_offset - S_GET_VALUE (current_stag->sym),
                      abs_section_offset - S_GET_VALUE (current_stag->sym),
                      NULL);
                      NULL);
    }
    }
 
 
  if (current_stag->is_union)
  if (current_stag->is_union)
    {
    {
      /* Note we treat the element as if it were an array of COUNT.  */
      /* Note we treat the element as if it were an array of COUNT.  */
      if (current_stag->size < (unsigned) size * count)
      if (current_stag->size < (unsigned) size * count)
        current_stag->size = size * count;
        current_stag->size = size * count;
    }
    }
  else
  else
    {
    {
      abs_section_offset += (unsigned) size * count;
      abs_section_offset += (unsigned) size * count;
      current_stag->current_bitfield_offset = new_bitfield_offset;
      current_stag->current_bitfield_offset = new_bitfield_offset;
    }
    }
  line_label = NULL;
  line_label = NULL;
}
}
 
 
/* Handle .byte, .word. .int, .long and all variants.  */
/* Handle .byte, .word. .int, .long and all variants.  */
 
 
static void
static void
tic54x_cons (type)
tic54x_cons (type)
     int type;
     int type;
{
{
  unsigned int c;
  unsigned int c;
  int octets;
  int octets;
 
 
  /* If we're within a .struct construct, don't actually allocate space.  */
  /* If we're within a .struct construct, don't actually allocate space.  */
  if (current_stag != NULL)
  if (current_stag != NULL)
    {
    {
      tic54x_struct_field (type);
      tic54x_struct_field (type);
      return;
      return;
    }
    }
 
 
#ifdef md_flush_pending_output
#ifdef md_flush_pending_output
  md_flush_pending_output ();
  md_flush_pending_output ();
#endif
#endif
 
 
  generate_lineno_debug ();
  generate_lineno_debug ();
 
 
  /* Align long words to long word boundaries (4 octets).  */
  /* Align long words to long word boundaries (4 octets).  */
  if (type == 'l' || type == 'L')
  if (type == 'l' || type == 'L')
    {
    {
      frag_align (2, 0, 2);
      frag_align (2, 0, 2);
      /* If there's a label, assign it to the first allocated word.  */
      /* If there's a label, assign it to the first allocated word.  */
      if (line_label != NULL)
      if (line_label != NULL)
        {
        {
          symbol_set_frag (line_label, frag_now);
          symbol_set_frag (line_label, frag_now);
          S_SET_VALUE (line_label, frag_now_fix ());
          S_SET_VALUE (line_label, frag_now_fix ());
        }
        }
    }
    }
 
 
  switch (type)
  switch (type)
    {
    {
    case 'l':
    case 'l':
    case 'L':
    case 'L':
    case 'x':
    case 'x':
      octets = 4;
      octets = 4;
      break;
      break;
    case 'b':
    case 'b':
    case 'B':
    case 'B':
    case 'c':
    case 'c':
    case 'C':
    case 'C':
      octets = 1;
      octets = 1;
      break;
      break;
    default:
    default:
      octets = 2;
      octets = 2;
      break;
      break;
    }
    }
 
 
  do
  do
    {
    {
      if (*input_line_pointer == '"')
      if (*input_line_pointer == '"')
        {
        {
          input_line_pointer++;
          input_line_pointer++;
          while (is_a_char (c = next_char_of_string ()))
          while (is_a_char (c = next_char_of_string ()))
            tic54x_emit_char (c);
            tic54x_emit_char (c);
          know (input_line_pointer[-1] == '\"');
          know (input_line_pointer[-1] == '\"');
        }
        }
      else
      else
        {
        {
          expressionS exp;
          expressionS exp;
 
 
          input_line_pointer = parse_expression (input_line_pointer, &exp);
          input_line_pointer = parse_expression (input_line_pointer, &exp);
          if (exp.X_op == O_constant)
          if (exp.X_op == O_constant)
            {
            {
              offsetT value = exp.X_add_number;
              offsetT value = exp.X_add_number;
              /* Truncate overflows.  */
              /* Truncate overflows.  */
              switch (octets)
              switch (octets)
                {
                {
                case 1:
                case 1:
                  if ((value > 0 && value > 0xFF)
                  if ((value > 0 && value > 0xFF)
                      || (value < 0 && value < - 0x100))
                      || (value < 0 && value < - 0x100))
                    as_warn ("Overflow in expression, truncated to 8 bits");
                    as_warn ("Overflow in expression, truncated to 8 bits");
                  break;
                  break;
                case 2:
                case 2:
                  if ((value > 0 && value > 0xFFFF)
                  if ((value > 0 && value > 0xFFFF)
                      || (value < 0 && value < - 0x10000))
                      || (value < 0 && value < - 0x10000))
                    as_warn ("Overflow in expression, truncated to 16 bits");
                    as_warn ("Overflow in expression, truncated to 16 bits");
                  break;
                  break;
                }
                }
            }
            }
          if (exp.X_op != O_constant && octets < 2)
          if (exp.X_op != O_constant && octets < 2)
            {
            {
              /* Disallow .byte with a non constant expression that will
              /* Disallow .byte with a non constant expression that will
                 require relocation.  */
                 require relocation.  */
              as_bad (_("Relocatable values require at least WORD storage"));
              as_bad (_("Relocatable values require at least WORD storage"));
              ignore_rest_of_line ();
              ignore_rest_of_line ();
              return;
              return;
            }
            }
 
 
          if (exp.X_op != O_constant
          if (exp.X_op != O_constant
              && amode == c_mode
              && amode == c_mode
              && octets == 4)
              && octets == 4)
            {
            {
              /* FIXME -- at one point TI tools used to output REL16
              /* FIXME -- at one point TI tools used to output REL16
                 relocations, but I don't think the latest tools do at all
                 relocations, but I don't think the latest tools do at all
                 The current tools output extended relocations regardless of
                 The current tools output extended relocations regardless of
                 the addressing mode (I actually think that ".c_mode" is
                 the addressing mode (I actually think that ".c_mode" is
                 totally ignored in the latest tools).  */
                 totally ignored in the latest tools).  */
              amode = far_mode;
              amode = far_mode;
              emitting_long = 1;
              emitting_long = 1;
              emit_expr (&exp, 4);
              emit_expr (&exp, 4);
              emitting_long = 0;
              emitting_long = 0;
              amode = c_mode;
              amode = c_mode;
            }
            }
          else
          else
            {
            {
              emitting_long = octets == 4;
              emitting_long = octets == 4;
              emit_expr (&exp, (octets == 1) ? 2 : octets);
              emit_expr (&exp, (octets == 1) ? 2 : octets);
              emitting_long = 0;
              emitting_long = 0;
            }
            }
        }
        }
    }
    }
  while (*input_line_pointer++ == ',');
  while (*input_line_pointer++ == ',');
 
 
  input_line_pointer--;         /* Put terminator back into stream.  */
  input_line_pointer--;         /* Put terminator back into stream.  */
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
/* .global <symbol>[,...,<symbolN>]
/* .global <symbol>[,...,<symbolN>]
   .def    <symbol>[,...,<symbolN>]
   .def    <symbol>[,...,<symbolN>]
   .ref    <symbol>[,...,<symbolN>]
   .ref    <symbol>[,...,<symbolN>]
 
 
   These all identify global symbols.
   These all identify global symbols.
 
 
   .def means the symbol is defined in the current module and can be accessed
   .def means the symbol is defined in the current module and can be accessed
   by other files.  The symbol should be placed in the symbol table.
   by other files.  The symbol should be placed in the symbol table.
 
 
   .ref means the symbol is used in the current module but defined in another
   .ref means the symbol is used in the current module but defined in another
   module.  The linker is to resolve this symbol's definition at link time.
   module.  The linker is to resolve this symbol's definition at link time.
 
 
   .global should act as a .ref or .def, as needed.
   .global should act as a .ref or .def, as needed.
 
 
   global, def and ref all have symbol storage classes of C_EXT.
   global, def and ref all have symbol storage classes of C_EXT.
 
 
   I can't identify any difference in how the "other" c54x assembler treats
   I can't identify any difference in how the "other" c54x assembler treats
   these, so we ignore the type here.  */
   these, so we ignore the type here.  */
 
 
void
void
tic54x_global (type)
tic54x_global (type)
     int type;
     int type;
{
{
  char *name;
  char *name;
  int c;
  int c;
  symbolS *symbolP;
  symbolS *symbolP;
 
 
  if (type == 'r')
  if (type == 'r')
    as_warn (_("Use of .def/.ref is deprecated.  Use .global instead"));
    as_warn (_("Use of .def/.ref is deprecated.  Use .global instead"));
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  do
  do
    {
    {
      name = input_line_pointer;
      name = input_line_pointer;
      c = get_symbol_end ();
      c = get_symbol_end ();
      symbolP = symbol_find_or_make (name);
      symbolP = symbol_find_or_make (name);
 
 
      *input_line_pointer = c;
      *input_line_pointer = c;
      S_SET_STORAGE_CLASS (symbolP, C_EXT);
      S_SET_STORAGE_CLASS (symbolP, C_EXT);
      if (c == ',')
      if (c == ',')
        {
        {
          input_line_pointer++;
          input_line_pointer++;
          if (is_end_of_line[(int) *input_line_pointer])
          if (is_end_of_line[(int) *input_line_pointer])
            c = *input_line_pointer;
            c = *input_line_pointer;
        }
        }
    }
    }
  while (c == ',');
  while (c == ',');
 
 
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
/* Remove the symbol from the local label hash lookup.  */
/* Remove the symbol from the local label hash lookup.  */
 
 
static void
static void
tic54x_remove_local_label (key, value)
tic54x_remove_local_label (key, value)
     const char *key;
     const char *key;
     PTR value ATTRIBUTE_UNUSED;
     PTR value ATTRIBUTE_UNUSED;
{
{
  PTR *elem = hash_delete (local_label_hash[macro_level], key);
  PTR *elem = hash_delete (local_label_hash[macro_level], key);
  free (elem);
  free (elem);
}
}
 
 
/* Reset all local labels.  */
/* Reset all local labels.  */
 
 
static void
static void
tic54x_clear_local_labels (ignored)
tic54x_clear_local_labels (ignored)
     int ignored ATTRIBUTE_UNUSED;
     int ignored ATTRIBUTE_UNUSED;
{
{
  hash_traverse (local_label_hash[macro_level], tic54x_remove_local_label);
  hash_traverse (local_label_hash[macro_level], tic54x_remove_local_label);
}
}
 
 
/* .text
/* .text
   .data
   .data
   .sect "section name"
   .sect "section name"
 
 
   Initialized section
   Initialized section
   make sure local labels get cleared when changing sections
   make sure local labels get cleared when changing sections
 
 
   ARG is 't' for text, 'd' for data, or '*' for a named section
   ARG is 't' for text, 'd' for data, or '*' for a named section
 
 
   For compatibility, '*' sections are SEC_CODE if instructions are
   For compatibility, '*' sections are SEC_CODE if instructions are
   encountered, or SEC_DATA if not.
   encountered, or SEC_DATA if not.
*/
*/
 
 
static void
static void
tic54x_sect (arg)
tic54x_sect (arg)
     int arg;
     int arg;
{
{
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  /* Local labels are cleared when changing sections.  */
  /* Local labels are cleared when changing sections.  */
  tic54x_clear_local_labels (0);
  tic54x_clear_local_labels (0);
 
 
  if (arg == 't')
  if (arg == 't')
    s_text (0);
    s_text (0);
  else if (arg == 'd')
  else if (arg == 'd')
    s_data (0);
    s_data (0);
  else
  else
    {
    {
      char *name = NULL;
      char *name = NULL;
      int len;
      int len;
 
 
      /* If there are quotes, remove them.  */
      /* If there are quotes, remove them.  */
      if (*input_line_pointer == '"')
      if (*input_line_pointer == '"')
        {
        {
          name = demand_copy_C_string (&len);
          name = demand_copy_C_string (&len);
          demand_empty_rest_of_line ();
          demand_empty_rest_of_line ();
          name = strcpy (xmalloc (len + 10), name);
          name = strcpy (xmalloc (len + 10), name);
        }
        }
      else
      else
        {
        {
          int c;
          int c;
          name = input_line_pointer;
          name = input_line_pointer;
          c = get_symbol_end ();
          c = get_symbol_end ();
          len = strlen(name);
          len = strlen(name);
          name = strcpy (xmalloc (len + 10), name);
          name = strcpy (xmalloc (len + 10), name);
          *input_line_pointer = c;
          *input_line_pointer = c;
          demand_empty_rest_of_line ();
          demand_empty_rest_of_line ();
        }
        }
      /* Make sure all named initialized sections flagged properly.  If we
      /* Make sure all named initialized sections flagged properly.  If we
         encounter instructions, we'll flag it with SEC_CODE as well.  */
         encounter instructions, we'll flag it with SEC_CODE as well.  */
      strcat (name, ",\"w\"\n");
      strcat (name, ",\"w\"\n");
      input_scrub_insert_line (name);
      input_scrub_insert_line (name);
      obj_coff_section (0);
      obj_coff_section (0);
 
 
      /* If there was a line label, make sure that it gets assigned the proper
      /* If there was a line label, make sure that it gets assigned the proper
         section.  This is for compatibility, even though the actual behavior
         section.  This is for compatibility, even though the actual behavior
         is not explicitly defined.  For consistency, we make .sect behave
         is not explicitly defined.  For consistency, we make .sect behave
         like .usect, since that is probably what people expect.  */
         like .usect, since that is probably what people expect.  */
      if (line_label != NULL)
      if (line_label != NULL)
        {
        {
          S_SET_SEGMENT (line_label, now_seg);
          S_SET_SEGMENT (line_label, now_seg);
          symbol_set_frag (line_label, frag_now);
          symbol_set_frag (line_label, frag_now);
          S_SET_VALUE (line_label, frag_now_fix ());
          S_SET_VALUE (line_label, frag_now_fix ());
          if (S_GET_STORAGE_CLASS (line_label) != C_EXT)
          if (S_GET_STORAGE_CLASS (line_label) != C_EXT)
            S_SET_STORAGE_CLASS (line_label, C_LABEL);
            S_SET_STORAGE_CLASS (line_label, C_LABEL);
        }
        }
    }
    }
}
}
 
 
/* [symbol] .space space_in_bits
/* [symbol] .space space_in_bits
   [symbol] .bes space_in_bits
   [symbol] .bes space_in_bits
   BES puts the symbol at the *last* word allocated
   BES puts the symbol at the *last* word allocated
 
 
   cribbed from s_space.  */
   cribbed from s_space.  */
 
 
static void
static void
tic54x_space (arg)
tic54x_space (arg)
     int arg;
     int arg;
{
{
  expressionS exp;
  expressionS exp;
  char *p = 0;
  char *p = 0;
  int octets = 0;
  int octets = 0;
  long words;
  long words;
  int bits_per_byte = (OCTETS_PER_BYTE * 8);
  int bits_per_byte = (OCTETS_PER_BYTE * 8);
  int bit_offset = 0;
  int bit_offset = 0;
  symbolS *label = line_label;
  symbolS *label = line_label;
  int bes = arg;
  int bes = arg;
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
#ifdef md_flush_pending_output
#ifdef md_flush_pending_output
  md_flush_pending_output ();
  md_flush_pending_output ();
#endif
#endif
 
 
  /* Read the bit count.  */
  /* Read the bit count.  */
  expression (&exp);
  expression (&exp);
 
 
  /* Some expressions are unresolvable until later in the assembly pass;
  /* Some expressions are unresolvable until later in the assembly pass;
     postpone until relaxation/fixup.  we also have to postpone if a previous
     postpone until relaxation/fixup.  we also have to postpone if a previous
     partial allocation has not been completed yet.  */
     partial allocation has not been completed yet.  */
  if (exp.X_op != O_constant || frag_bit_offset (frag_now, now_seg) == -1)
  if (exp.X_op != O_constant || frag_bit_offset (frag_now, now_seg) == -1)
    {
    {
      struct bit_info *bi = xmalloc (sizeof (struct bit_info));
      struct bit_info *bi = xmalloc (sizeof (struct bit_info));
      char *p;
      char *p;
 
 
      bi->seg = now_seg;
      bi->seg = now_seg;
      bi->type = bes;
      bi->type = bes;
      bi->sym = label;
      bi->sym = label;
      p = frag_var (rs_machine_dependent,
      p = frag_var (rs_machine_dependent,
                    65536 * 2, 1, (relax_substateT) 0,
                    65536 * 2, 1, (relax_substateT) 0,
                    make_expr_symbol (&exp), (offsetT) 0,
                    make_expr_symbol (&exp), (offsetT) 0,
                    (char *) bi);
                    (char *) bi);
      if (p)
      if (p)
        *p = 0;
        *p = 0;
 
 
      return;
      return;
    }
    }
 
 
  /* Reduce the required size by any bit offsets currently left over
  /* Reduce the required size by any bit offsets currently left over
     from a previous .space/.bes/.field directive.  */
     from a previous .space/.bes/.field directive.  */
  bit_offset = frag_now->tc_frag_data;
  bit_offset = frag_now->tc_frag_data;
  if (bit_offset != 0 && bit_offset < 16)
  if (bit_offset != 0 && bit_offset < 16)
    {
    {
      int spare_bits = bits_per_byte - bit_offset;
      int spare_bits = bits_per_byte - bit_offset;
 
 
      if (spare_bits >= exp.X_add_number)
      if (spare_bits >= exp.X_add_number)
        {
        {
          /* Don't have to do anything; sufficient bits have already been
          /* Don't have to do anything; sufficient bits have already been
             allocated; just point the label to the right place.  */
             allocated; just point the label to the right place.  */
          if (label != NULL)
          if (label != NULL)
            {
            {
              symbol_set_frag (label, frag_now);
              symbol_set_frag (label, frag_now);
              S_SET_VALUE (label, frag_now_fix () - 1);
              S_SET_VALUE (label, frag_now_fix () - 1);
              label = NULL;
              label = NULL;
            }
            }
          frag_now->tc_frag_data += exp.X_add_number;
          frag_now->tc_frag_data += exp.X_add_number;
          goto getout;
          goto getout;
        }
        }
      exp.X_add_number -= spare_bits;
      exp.X_add_number -= spare_bits;
      /* Set the label to point to the first word allocated, which in this
      /* Set the label to point to the first word allocated, which in this
         case is the previous word, which was only partially filled.  */
         case is the previous word, which was only partially filled.  */
      if (!bes && label != NULL)
      if (!bes && label != NULL)
        {
        {
          symbol_set_frag (label, frag_now);
          symbol_set_frag (label, frag_now);
          S_SET_VALUE (label, frag_now_fix () - 1);
          S_SET_VALUE (label, frag_now_fix () - 1);
          label = NULL;
          label = NULL;
        }
        }
    }
    }
  /* Convert bits to bytes/words and octets, rounding up.  */
  /* Convert bits to bytes/words and octets, rounding up.  */
  words = ((exp.X_add_number + bits_per_byte - 1) / bits_per_byte);
  words = ((exp.X_add_number + bits_per_byte - 1) / bits_per_byte);
  /* How many do we have left over?  */
  /* How many do we have left over?  */
  bit_offset = exp.X_add_number % bits_per_byte;
  bit_offset = exp.X_add_number % bits_per_byte;
  octets = words * OCTETS_PER_BYTE;
  octets = words * OCTETS_PER_BYTE;
  if (octets < 0)
  if (octets < 0)
    {
    {
      as_warn (_(".space/.bes repeat count is negative, ignored"));
      as_warn (_(".space/.bes repeat count is negative, ignored"));
      goto getout;
      goto getout;
    }
    }
  else if (octets == 0)
  else if (octets == 0)
    {
    {
      as_warn (_(".space/.bes repeat count is zero, ignored"));
      as_warn (_(".space/.bes repeat count is zero, ignored"));
      goto getout;
      goto getout;
    }
    }
 
 
  /* If we are in the absolute section, just bump the offset.  */
  /* If we are in the absolute section, just bump the offset.  */
  if (now_seg == absolute_section)
  if (now_seg == absolute_section)
    {
    {
      abs_section_offset += words;
      abs_section_offset += words;
      if (bes && label != NULL)
      if (bes && label != NULL)
        S_SET_VALUE (label, abs_section_offset - 1);
        S_SET_VALUE (label, abs_section_offset - 1);
      frag_now->tc_frag_data = bit_offset;
      frag_now->tc_frag_data = bit_offset;
      goto getout;
      goto getout;
    }
    }
 
 
  if (!need_pass_2)
  if (!need_pass_2)
    p = frag_var (rs_fill, 1, 1,
    p = frag_var (rs_fill, 1, 1,
                  (relax_substateT) 0, (symbolS *) 0,
                  (relax_substateT) 0, (symbolS *) 0,
                  (offsetT) octets, (char *) 0);
                  (offsetT) octets, (char *) 0);
 
 
  /* Make note of how many bits of this word we've allocated so far.  */
  /* Make note of how many bits of this word we've allocated so far.  */
  frag_now->tc_frag_data = bit_offset;
  frag_now->tc_frag_data = bit_offset;
 
 
  /* .bes puts label at *last* word allocated.  */
  /* .bes puts label at *last* word allocated.  */
  if (bes && label != NULL)
  if (bes && label != NULL)
    {
    {
      symbol_set_frag (label, frag_now);
      symbol_set_frag (label, frag_now);
      S_SET_VALUE (label, frag_now_fix () - 1);
      S_SET_VALUE (label, frag_now_fix () - 1);
    }
    }
 
 
  if (p)
  if (p)
    *p = 0;
    *p = 0;
 
 
 getout:
 getout:
 
 
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
/* [symbol] .usect "section-name", size-in-words
/* [symbol] .usect "section-name", size-in-words
                   [, [blocking-flag] [, alignment-flag]]
                   [, [blocking-flag] [, alignment-flag]]
 
 
   Uninitialized section.
   Uninitialized section.
   Non-zero blocking means that if the section would cross a page (128-word)
   Non-zero blocking means that if the section would cross a page (128-word)
   boundary, it will be page-aligned.
   boundary, it will be page-aligned.
   Non-zero alignment aligns on a longword boundary.
   Non-zero alignment aligns on a longword boundary.
 
 
   Has no effect on the current section.  */
   Has no effect on the current section.  */
 
 
static void
static void
tic54x_usect (x)
tic54x_usect (x)
     int x ATTRIBUTE_UNUSED;
     int x ATTRIBUTE_UNUSED;
{
{
  char c;
  char c;
  char *name;
  char *name;
  char *section_name;
  char *section_name;
  char *p;
  char *p;
  segT seg;
  segT seg;
  int size, blocking_flag, alignment_flag;
  int size, blocking_flag, alignment_flag;
  segT current_seg;
  segT current_seg;
  subsegT current_subseg;
  subsegT current_subseg;
  flagword flags;
  flagword flags;
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  current_seg = now_seg;        /* Save current seg.  */
  current_seg = now_seg;        /* Save current seg.  */
  current_subseg = now_subseg;  /* Save current subseg.  */
  current_subseg = now_subseg;  /* Save current subseg.  */
 
 
  if (*input_line_pointer == '"')
  if (*input_line_pointer == '"')
    input_line_pointer++;
    input_line_pointer++;
  section_name = input_line_pointer;
  section_name = input_line_pointer;
  c = get_symbol_end ();        /* Get terminator.  */
  c = get_symbol_end ();        /* Get terminator.  */
  input_line_pointer++;         /* Skip null symbol terminator.  */
  input_line_pointer++;         /* Skip null symbol terminator.  */
  name = xmalloc (input_line_pointer - section_name + 1);
  name = xmalloc (input_line_pointer - section_name + 1);
  strcpy (name, section_name);
  strcpy (name, section_name);
 
 
  if (*input_line_pointer == ',')
  if (*input_line_pointer == ',')
    ++input_line_pointer;
    ++input_line_pointer;
  else if (c != ',')
  else if (c != ',')
    {
    {
      as_bad (_("Missing size argument"));
      as_bad (_("Missing size argument"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  size = get_absolute_expression ();
  size = get_absolute_expression ();
 
 
  /* Read a possibly present third argument (blocking flag).  */
  /* Read a possibly present third argument (blocking flag).  */
  if (*input_line_pointer == ',')
  if (*input_line_pointer == ',')
    {
    {
      ++input_line_pointer;
      ++input_line_pointer;
      if (*input_line_pointer != ',')
      if (*input_line_pointer != ',')
        blocking_flag = get_absolute_expression ();
        blocking_flag = get_absolute_expression ();
      else
      else
        blocking_flag = 0;
        blocking_flag = 0;
 
 
      /* Read a possibly present fourth argument (alignment flag).  */
      /* Read a possibly present fourth argument (alignment flag).  */
      if (*input_line_pointer == ',')
      if (*input_line_pointer == ',')
        {
        {
          ++input_line_pointer;
          ++input_line_pointer;
          alignment_flag = get_absolute_expression ();
          alignment_flag = get_absolute_expression ();
        }
        }
      else
      else
        alignment_flag = 0;
        alignment_flag = 0;
    }
    }
  else
  else
    blocking_flag = alignment_flag = 0;
    blocking_flag = alignment_flag = 0;
 
 
  seg = subseg_new (name, 0);
  seg = subseg_new (name, 0);
  flags = bfd_get_section_flags (stdoutput, seg) | SEC_ALLOC;
  flags = bfd_get_section_flags (stdoutput, seg) | SEC_ALLOC;
 
 
  if (alignment_flag)
  if (alignment_flag)
    {
    {
      /* s_align eats end of line; restore it.  */
      /* s_align eats end of line; restore it.  */
      s_align_bytes (4);
      s_align_bytes (4);
      --input_line_pointer;
      --input_line_pointer;
    }
    }
 
 
  if (line_label != NULL)
  if (line_label != NULL)
    {
    {
      S_SET_SEGMENT (line_label, seg);
      S_SET_SEGMENT (line_label, seg);
      symbol_set_frag (line_label, frag_now);
      symbol_set_frag (line_label, frag_now);
      S_SET_VALUE (line_label, frag_now_fix ());
      S_SET_VALUE (line_label, frag_now_fix ());
      /* Set scl to label, since that's what TI does.  */
      /* Set scl to label, since that's what TI does.  */
      if (S_GET_STORAGE_CLASS (line_label) != C_EXT)
      if (S_GET_STORAGE_CLASS (line_label) != C_EXT)
        S_SET_STORAGE_CLASS (line_label, C_LABEL);
        S_SET_STORAGE_CLASS (line_label, C_LABEL);
    }
    }
 
 
  seg_info (seg)->bss = 1;      /* Uninitialized data.  */
  seg_info (seg)->bss = 1;      /* Uninitialized data.  */
 
 
  p = frag_var (rs_fill, 1, 1,
  p = frag_var (rs_fill, 1, 1,
                (relax_substateT) 0, (symbolS *) line_label,
                (relax_substateT) 0, (symbolS *) line_label,
                size * OCTETS_PER_BYTE, (char *) 0);
                size * OCTETS_PER_BYTE, (char *) 0);
  *p = 0;
  *p = 0;
 
 
  if (blocking_flag)
  if (blocking_flag)
    flags |= SEC_TIC54X_BLOCK;
    flags |= SEC_TIC54X_BLOCK;
 
 
  if (!bfd_set_section_flags (stdoutput, seg, flags))
  if (!bfd_set_section_flags (stdoutput, seg, flags))
    as_warn ("Error setting flags for \"%s\": %s", name,
    as_warn ("Error setting flags for \"%s\": %s", name,
             bfd_errmsg (bfd_get_error ()));
             bfd_errmsg (bfd_get_error ()));
 
 
  subseg_set (current_seg, current_subseg);     /* Restore current seg.  */
  subseg_set (current_seg, current_subseg);     /* Restore current seg.  */
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
static enum cpu_version
static enum cpu_version
lookup_version (ver)
lookup_version (ver)
     const char *ver;
     const char *ver;
{
{
  enum cpu_version version = VNONE;
  enum cpu_version version = VNONE;
 
 
  if (ver[0] == '5' && ver[1] == '4')
  if (ver[0] == '5' && ver[1] == '4')
    {
    {
      if (strlen (ver) == 3
      if (strlen (ver) == 3
          && (ver[2] == '1' || ver[2] == '2' || ver[2] == '3'
          && (ver[2] == '1' || ver[2] == '2' || ver[2] == '3'
              || ver[2] == '5' || ver[2] == '8' || ver[2] == '9'))
              || ver[2] == '5' || ver[2] == '8' || ver[2] == '9'))
        version = ver[2] - '0';
        version = ver[2] - '0';
      else if (strlen (ver) == 5
      else if (strlen (ver) == 5
               && TOUPPER (ver[3]) == 'L'
               && TOUPPER (ver[3]) == 'L'
               && TOUPPER (ver[4]) == 'P'
               && TOUPPER (ver[4]) == 'P'
               && (ver[2] == '5' || ver[2] == '6'))
               && (ver[2] == '5' || ver[2] == '6'))
        version = ver[2] - '0' + 10;
        version = ver[2] - '0' + 10;
    }
    }
 
 
  return version;
  return version;
}
}
 
 
static void
static void
set_cpu (version)
set_cpu (version)
     enum cpu_version version;
     enum cpu_version version;
{
{
  cpu = version;
  cpu = version;
  if (version == V545LP || version == V546LP)
  if (version == V545LP || version == V546LP)
    {
    {
      symbolS *symbolP = symbol_new ("__allow_lp", absolute_section,
      symbolS *symbolP = symbol_new ("__allow_lp", absolute_section,
                                     (valueT) 1, &zero_address_frag);
                                     (valueT) 1, &zero_address_frag);
      SF_SET_LOCAL (symbolP);
      SF_SET_LOCAL (symbolP);
      symbol_table_insert (symbolP);
      symbol_table_insert (symbolP);
    }
    }
}
}
 
 
/* .version cpu-version
/* .version cpu-version
   cpu-version may be one of the following:
   cpu-version may be one of the following:
   541
   541
   542
   542
   543
   543
   545
   545
   545LP
   545LP
   546LP
   546LP
   548
   548
   549
   549
 
 
   This is for compatibility only.  It currently has no affect on assembly.  */
   This is for compatibility only.  It currently has no affect on assembly.  */
static int cpu_needs_set = 1;
static int cpu_needs_set = 1;
 
 
static void
static void
tic54x_version (x)
tic54x_version (x)
     int x ATTRIBUTE_UNUSED;
     int x ATTRIBUTE_UNUSED;
{
{
  enum cpu_version version = VNONE;
  enum cpu_version version = VNONE;
  enum cpu_version old_version = cpu;
  enum cpu_version old_version = cpu;
  int c;
  int c;
  char *ver;
  char *ver;
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  SKIP_WHITESPACE ();
  SKIP_WHITESPACE ();
  ver = input_line_pointer;
  ver = input_line_pointer;
  while (!is_end_of_line[(int) *input_line_pointer])
  while (!is_end_of_line[(int) *input_line_pointer])
    ++input_line_pointer;
    ++input_line_pointer;
  c = *input_line_pointer;
  c = *input_line_pointer;
  *input_line_pointer = 0;
  *input_line_pointer = 0;
 
 
  version = lookup_version (ver);
  version = lookup_version (ver);
 
 
  if (cpu != VNONE && cpu != version)
  if (cpu != VNONE && cpu != version)
    as_warn (_("CPU version has already been set"));
    as_warn (_("CPU version has already been set"));
 
 
  if (version == VNONE)
  if (version == VNONE)
    {
    {
      as_bad (_("Unrecognized version '%s'"), ver);
      as_bad (_("Unrecognized version '%s'"), ver);
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
  else if (assembly_begun && version != old_version)
  else if (assembly_begun && version != old_version)
    {
    {
      as_bad (_("Changing of CPU version on the fly not supported"));
      as_bad (_("Changing of CPU version on the fly not supported"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  set_cpu (version);
  set_cpu (version);
 
 
  *input_line_pointer = c;
  *input_line_pointer = c;
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
/* 'f' = float, 'x' = xfloat, 'd' = double, 'l' = ldouble.  */
/* 'f' = float, 'x' = xfloat, 'd' = double, 'l' = ldouble.  */
 
 
static void
static void
tic54x_float_cons (type)
tic54x_float_cons (type)
     int type;
     int type;
{
{
  if (current_stag != 0)
  if (current_stag != 0)
    tic54x_struct_field ('f');
    tic54x_struct_field ('f');
 
 
#ifdef md_flush_pending_output
#ifdef md_flush_pending_output
  md_flush_pending_output ();
  md_flush_pending_output ();
#endif
#endif
 
 
  /* Align to long word boundary (4 octets) unless it's ".xfloat".  */
  /* Align to long word boundary (4 octets) unless it's ".xfloat".  */
  if (type != 'x')
  if (type != 'x')
    {
    {
      frag_align (2, 0, 2);
      frag_align (2, 0, 2);
      /* If there's a label, assign it to the first allocated word.  */
      /* If there's a label, assign it to the first allocated word.  */
      if (line_label != NULL)
      if (line_label != NULL)
        {
        {
          symbol_set_frag (line_label, frag_now);
          symbol_set_frag (line_label, frag_now);
          S_SET_VALUE (line_label, frag_now_fix ());
          S_SET_VALUE (line_label, frag_now_fix ());
        }
        }
    }
    }
 
 
  float_cons ('f');
  float_cons ('f');
}
}
 
 
/* The argument is capitalized if it should be zero-terminated
/* The argument is capitalized if it should be zero-terminated
   's' is normal string with upper 8-bits zero-filled, 'p' is packed.
   's' is normal string with upper 8-bits zero-filled, 'p' is packed.
   Code copied from stringer, and slightly modified so that strings are packed
   Code copied from stringer, and slightly modified so that strings are packed
   and encoded into the correct octets.  */
   and encoded into the correct octets.  */
 
 
static void
static void
tic54x_stringer (type)
tic54x_stringer (type)
     int type;
     int type;
{
{
  unsigned int c;
  unsigned int c;
  char *start;
  char *start;
  int append_zero = type == 'S' || type == 'P';
  int append_zero = type == 'S' || type == 'P';
  int packed = type == 'p' || type == 'P';
  int packed = type == 'p' || type == 'P';
  int last_char = -1; /* Packed strings need two bytes at a time to encode.  */
  int last_char = -1; /* Packed strings need two bytes at a time to encode.  */
 
 
  if (current_stag != NULL)
  if (current_stag != NULL)
    {
    {
      tic54x_struct_field ('*');
      tic54x_struct_field ('*');
      return;
      return;
    }
    }
 
 
#ifdef md_flush_pending_output
#ifdef md_flush_pending_output
  md_flush_pending_output ();
  md_flush_pending_output ();
#endif
#endif
 
 
  c = ',';                      /* Do loop.  */
  c = ',';                      /* Do loop.  */
  while (c == ',')
  while (c == ',')
    {
    {
      SKIP_WHITESPACE ();
      SKIP_WHITESPACE ();
      switch (*input_line_pointer)
      switch (*input_line_pointer)
        {
        {
        default:
        default:
          {
          {
            unsigned short value = get_absolute_expression ();
            unsigned short value = get_absolute_expression ();
            FRAG_APPEND_1_CHAR ( value       & 0xFF);
            FRAG_APPEND_1_CHAR ( value       & 0xFF);
            FRAG_APPEND_1_CHAR ((value >> 8) & 0xFF);
            FRAG_APPEND_1_CHAR ((value >> 8) & 0xFF);
            break;
            break;
          }
          }
        case '\"':
        case '\"':
          ++input_line_pointer; /* -> 1st char of string.  */
          ++input_line_pointer; /* -> 1st char of string.  */
          start = input_line_pointer;
          start = input_line_pointer;
          while (is_a_char (c = next_char_of_string ()))
          while (is_a_char (c = next_char_of_string ()))
            {
            {
              if (!packed)
              if (!packed)
                {
                {
                  FRAG_APPEND_1_CHAR (c);
                  FRAG_APPEND_1_CHAR (c);
                  FRAG_APPEND_1_CHAR (0);
                  FRAG_APPEND_1_CHAR (0);
                }
                }
              else
              else
                {
                {
                  /* Packed strings are filled MS octet first.  */
                  /* Packed strings are filled MS octet first.  */
                  if (last_char == -1)
                  if (last_char == -1)
                    last_char = c;
                    last_char = c;
                  else
                  else
                    {
                    {
                      FRAG_APPEND_1_CHAR (c);
                      FRAG_APPEND_1_CHAR (c);
                      FRAG_APPEND_1_CHAR (last_char);
                      FRAG_APPEND_1_CHAR (last_char);
                      last_char = -1;
                      last_char = -1;
                    }
                    }
                }
                }
            }
            }
          if (append_zero)
          if (append_zero)
            {
            {
              if (packed && last_char != -1)
              if (packed && last_char != -1)
                {
                {
                  FRAG_APPEND_1_CHAR (0);
                  FRAG_APPEND_1_CHAR (0);
                  FRAG_APPEND_1_CHAR (last_char);
                  FRAG_APPEND_1_CHAR (last_char);
                  last_char = -1;
                  last_char = -1;
                }
                }
              else
              else
                {
                {
                  FRAG_APPEND_1_CHAR (0);
                  FRAG_APPEND_1_CHAR (0);
                  FRAG_APPEND_1_CHAR (0);
                  FRAG_APPEND_1_CHAR (0);
                }
                }
            }
            }
          know (input_line_pointer[-1] == '\"');
          know (input_line_pointer[-1] == '\"');
          break;
          break;
        }
        }
      SKIP_WHITESPACE ();
      SKIP_WHITESPACE ();
      c = *input_line_pointer;
      c = *input_line_pointer;
      if (!is_end_of_line[c])
      if (!is_end_of_line[c])
        ++input_line_pointer;
        ++input_line_pointer;
    }
    }
 
 
  /* Finish up any leftover packed string.  */
  /* Finish up any leftover packed string.  */
  if (packed && last_char != -1)
  if (packed && last_char != -1)
    {
    {
      FRAG_APPEND_1_CHAR (0);
      FRAG_APPEND_1_CHAR (0);
      FRAG_APPEND_1_CHAR (last_char);
      FRAG_APPEND_1_CHAR (last_char);
    }
    }
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
static void
static void
tic54x_p2align (arg)
tic54x_p2align (arg)
     int arg ATTRIBUTE_UNUSED;
     int arg ATTRIBUTE_UNUSED;
{
{
  as_bad (_("p2align not supported on this target"));
  as_bad (_("p2align not supported on this target"));
}
}
 
 
static void
static void
tic54x_align_words (arg)
tic54x_align_words (arg)
     int arg;
     int arg;
{
{
  /* Only ".align" with no argument is allowed within .struct/.union.  */
  /* Only ".align" with no argument is allowed within .struct/.union.  */
  int count = arg;
  int count = arg;
 
 
  if (!is_end_of_line[(int) *input_line_pointer])
  if (!is_end_of_line[(int) *input_line_pointer])
    {
    {
      if (arg == 2)
      if (arg == 2)
        as_warn (_("Argument to .even ignored"));
        as_warn (_("Argument to .even ignored"));
      else
      else
        count = get_absolute_expression ();
        count = get_absolute_expression ();
    }
    }
 
 
  if (current_stag != NULL && arg == 128)
  if (current_stag != NULL && arg == 128)
    {
    {
      if (current_stag->current_bitfield_offset != 0)
      if (current_stag->current_bitfield_offset != 0)
        {
        {
          current_stag->current_bitfield_offset = 0;
          current_stag->current_bitfield_offset = 0;
          ++abs_section_offset;
          ++abs_section_offset;
        }
        }
      demand_empty_rest_of_line ();
      demand_empty_rest_of_line ();
      return;
      return;
    }
    }
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  s_align_bytes (count << 1);
  s_align_bytes (count << 1);
}
}
 
 
/* Initialize multiple-bit fields withing a single word of memory.  */
/* Initialize multiple-bit fields withing a single word of memory.  */
 
 
static void
static void
tic54x_field (ignore)
tic54x_field (ignore)
     int ignore ATTRIBUTE_UNUSED;
     int ignore ATTRIBUTE_UNUSED;
{
{
  expressionS exp;
  expressionS exp;
  int size = 16;
  int size = 16;
  char *p;
  char *p;
  valueT value;
  valueT value;
  symbolS *label = line_label;
  symbolS *label = line_label;
 
 
  if (current_stag != NULL)
  if (current_stag != NULL)
    {
    {
      tic54x_struct_field ('.');
      tic54x_struct_field ('.');
      return;
      return;
    }
    }
 
 
  input_line_pointer = parse_expression (input_line_pointer, &exp);
  input_line_pointer = parse_expression (input_line_pointer, &exp);
 
 
  if (*input_line_pointer == ',')
  if (*input_line_pointer == ',')
    {
    {
      ++input_line_pointer;
      ++input_line_pointer;
      size = get_absolute_expression ();
      size = get_absolute_expression ();
      if (size < 1 || size > 32)
      if (size < 1 || size > 32)
        {
        {
          as_bad (_("Invalid field size, must be from 1 to 32"));
          as_bad (_("Invalid field size, must be from 1 to 32"));
          ignore_rest_of_line ();
          ignore_rest_of_line ();
          return;
          return;
        }
        }
    }
    }
 
 
  /* Truncate values to the field width.  */
  /* Truncate values to the field width.  */
  if (exp.X_op != O_constant)
  if (exp.X_op != O_constant)
    {
    {
      /* If the expression value is relocatable, the field size *must*
      /* If the expression value is relocatable, the field size *must*
         be 16.  */
         be 16.  */
      if (size != 16)
      if (size != 16)
        {
        {
          as_bad (_("field size must be 16 when value is relocatable"));
          as_bad (_("field size must be 16 when value is relocatable"));
          ignore_rest_of_line ();
          ignore_rest_of_line ();
          return;
          return;
        }
        }
 
 
      frag_now->tc_frag_data = 0;
      frag_now->tc_frag_data = 0;
      emit_expr (&exp, 2);
      emit_expr (&exp, 2);
    }
    }
  else
  else
    {
    {
      unsigned long fmask = (size == 32) ? 0xFFFFFFFF : (1ul << size) - 1;
      unsigned long fmask = (size == 32) ? 0xFFFFFFFF : (1ul << size) - 1;
 
 
      value = exp.X_add_number;
      value = exp.X_add_number;
      exp.X_add_number &= fmask;
      exp.X_add_number &= fmask;
      if (value != (valueT) exp.X_add_number)
      if (value != (valueT) exp.X_add_number)
        as_warn (_("field value truncated"));
        as_warn (_("field value truncated"));
      value = exp.X_add_number;
      value = exp.X_add_number;
      /* Bits are stored MS first.  */
      /* Bits are stored MS first.  */
      while (size >= 16)
      while (size >= 16)
        {
        {
          frag_now->tc_frag_data = 0;
          frag_now->tc_frag_data = 0;
          p = frag_more (2);
          p = frag_more (2);
          md_number_to_chars (p, (value >> (size - 16)) & 0xFFFF, 2);
          md_number_to_chars (p, (value >> (size - 16)) & 0xFFFF, 2);
          size -= 16;
          size -= 16;
        }
        }
      if (size > 0)
      if (size > 0)
        {
        {
          int bit_offset = frag_bit_offset (frag_now, now_seg);
          int bit_offset = frag_bit_offset (frag_now, now_seg);
 
 
          fragS *alloc_frag = bit_offset_frag (frag_now, now_seg);
          fragS *alloc_frag = bit_offset_frag (frag_now, now_seg);
          if (bit_offset == -1)
          if (bit_offset == -1)
            {
            {
              struct bit_info *bi = xmalloc (sizeof (struct bit_info));
              struct bit_info *bi = xmalloc (sizeof (struct bit_info));
              /* We don't know the previous offset at this time, so store the
              /* We don't know the previous offset at this time, so store the
                 info we need and figure it out later.  */
                 info we need and figure it out later.  */
              expressionS size_exp;
              expressionS size_exp;
 
 
              size_exp.X_op = O_constant;
              size_exp.X_op = O_constant;
              size_exp.X_add_number = size;
              size_exp.X_add_number = size;
              bi->seg = now_seg;
              bi->seg = now_seg;
              bi->type = TYPE_FIELD;
              bi->type = TYPE_FIELD;
              bi->value = value;
              bi->value = value;
              p = frag_var (rs_machine_dependent,
              p = frag_var (rs_machine_dependent,
                            4, 1, (relax_substateT) 0,
                            4, 1, (relax_substateT) 0,
                            make_expr_symbol (&size_exp), (offsetT) 0,
                            make_expr_symbol (&size_exp), (offsetT) 0,
                            (char *) bi);
                            (char *) bi);
              goto getout;
              goto getout;
            }
            }
          else if (bit_offset == 0 || bit_offset + size > 16)
          else if (bit_offset == 0 || bit_offset + size > 16)
            {
            {
              /* Align a new field.  */
              /* Align a new field.  */
              p = frag_more (2);
              p = frag_more (2);
              frag_now->tc_frag_data = 0;
              frag_now->tc_frag_data = 0;
              alloc_frag = frag_now;
              alloc_frag = frag_now;
            }
            }
          else
          else
            {
            {
              /* Put the new value entirely within the existing one.  */
              /* Put the new value entirely within the existing one.  */
              p = alloc_frag == frag_now ?
              p = alloc_frag == frag_now ?
                frag_now->fr_literal + frag_now_fix_octets () - 2 :
                frag_now->fr_literal + frag_now_fix_octets () - 2 :
                alloc_frag->fr_literal;
                alloc_frag->fr_literal;
              if (label != NULL)
              if (label != NULL)
                {
                {
                  symbol_set_frag (label, alloc_frag);
                  symbol_set_frag (label, alloc_frag);
                  if (alloc_frag == frag_now)
                  if (alloc_frag == frag_now)
                    S_SET_VALUE (label, frag_now_fix () - 1);
                    S_SET_VALUE (label, frag_now_fix () - 1);
                  label = NULL;
                  label = NULL;
                }
                }
            }
            }
          value <<= 16 - alloc_frag->tc_frag_data - size;
          value <<= 16 - alloc_frag->tc_frag_data - size;
 
 
          /* OR in existing value.  */
          /* OR in existing value.  */
          if (alloc_frag->tc_frag_data)
          if (alloc_frag->tc_frag_data)
            value |= ((unsigned short) p[1] << 8) | p[0];
            value |= ((unsigned short) p[1] << 8) | p[0];
          md_number_to_chars (p, value, 2);
          md_number_to_chars (p, value, 2);
          alloc_frag->tc_frag_data += size;
          alloc_frag->tc_frag_data += size;
          if (alloc_frag->tc_frag_data == 16)
          if (alloc_frag->tc_frag_data == 16)
            alloc_frag->tc_frag_data = 0;
            alloc_frag->tc_frag_data = 0;
        }
        }
    }
    }
 getout:
 getout:
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
/* Ideally, we want to check SEC_LOAD and SEC_HAS_CONTENTS, but those aren't
/* Ideally, we want to check SEC_LOAD and SEC_HAS_CONTENTS, but those aren't
   available yet.  seg_info ()->bss is the next best thing.  */
   available yet.  seg_info ()->bss is the next best thing.  */
 
 
static int
static int
tic54x_initialized_section (seg)
tic54x_initialized_section (seg)
     segT seg;
     segT seg;
{
{
  return !seg_info (seg)->bss;
  return !seg_info (seg)->bss;
}
}
 
 
/* .clink ["section name"]
/* .clink ["section name"]
 
 
   Marks the section as conditionally linked (link only if contents are
   Marks the section as conditionally linked (link only if contents are
   referenced elsewhere.
   referenced elsewhere.
   Without a name, refers to the current initialized section.
   Without a name, refers to the current initialized section.
   Name is required for uninitialized sections.  */
   Name is required for uninitialized sections.  */
 
 
static void
static void
tic54x_clink (ignored)
tic54x_clink (ignored)
     int ignored ATTRIBUTE_UNUSED;
     int ignored ATTRIBUTE_UNUSED;
{
{
  segT seg = now_seg;
  segT seg = now_seg;
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  if (*input_line_pointer == '\"')
  if (*input_line_pointer == '\"')
    {
    {
      char *section_name = ++input_line_pointer;
      char *section_name = ++input_line_pointer;
      char *name;
      char *name;
 
 
      while (is_a_char (next_char_of_string ()))
      while (is_a_char (next_char_of_string ()))
        ;
        ;
      know (input_line_pointer[-1] == '\"');
      know (input_line_pointer[-1] == '\"');
      input_line_pointer[-1] = 0;
      input_line_pointer[-1] = 0;
      name = xmalloc (input_line_pointer - section_name + 1);
      name = xmalloc (input_line_pointer - section_name + 1);
      strcpy (name, section_name);
      strcpy (name, section_name);
 
 
      seg = bfd_get_section_by_name (stdoutput, name);
      seg = bfd_get_section_by_name (stdoutput, name);
      if (seg == NULL)
      if (seg == NULL)
        {
        {
          as_bad (_("Unrecognized section '%s'"), section_name);
          as_bad (_("Unrecognized section '%s'"), section_name);
          ignore_rest_of_line ();
          ignore_rest_of_line ();
          return;
          return;
        }
        }
    }
    }
  else
  else
    {
    {
      if (!tic54x_initialized_section (seg))
      if (!tic54x_initialized_section (seg))
        {
        {
          as_bad (_("Current section is unitialized, "
          as_bad (_("Current section is unitialized, "
                    "section name required for .clink"));
                    "section name required for .clink"));
          ignore_rest_of_line ();
          ignore_rest_of_line ();
          return;
          return;
        }
        }
    }
    }
 
 
  seg->flags |= SEC_TIC54X_CLINK;
  seg->flags |= SEC_TIC54X_CLINK;
 
 
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
/* Change the default include directory to be the current source file's
/* Change the default include directory to be the current source file's
   directory, instead of the current working directory.  If DOT is non-zero,
   directory, instead of the current working directory.  If DOT is non-zero,
   set to "." instead.  */
   set to "." instead.  */
 
 
static void
static void
tic54x_set_default_include (dot)
tic54x_set_default_include (dot)
     int dot;
     int dot;
{
{
  char *dir = ".";
  char *dir = ".";
  char *tmp = NULL;
  char *tmp = NULL;
 
 
  if (!dot)
  if (!dot)
    {
    {
      char *curfile;
      char *curfile;
      unsigned lineno;
      unsigned lineno;
 
 
      as_where (&curfile, &lineno);
      as_where (&curfile, &lineno);
      dir = strcpy (xmalloc (strlen (curfile) + 1), curfile);
      dir = strcpy (xmalloc (strlen (curfile) + 1), curfile);
      tmp = strrchr (dir, '/');
      tmp = strrchr (dir, '/');
    }
    }
  if (tmp != NULL)
  if (tmp != NULL)
    {
    {
      int len;
      int len;
 
 
      *tmp = '\0';
      *tmp = '\0';
      len = strlen (dir);
      len = strlen (dir);
      if (include_dir_count == 0)
      if (include_dir_count == 0)
        {
        {
          include_dirs = (char **) xmalloc (sizeof (*include_dirs));
          include_dirs = (char **) xmalloc (sizeof (*include_dirs));
          include_dir_count = 1;
          include_dir_count = 1;
        }
        }
      include_dirs[0] = dir;
      include_dirs[0] = dir;
      if (len > include_dir_maxlen)
      if (len > include_dir_maxlen)
        include_dir_maxlen = len;
        include_dir_maxlen = len;
    }
    }
  else if (include_dirs != NULL)
  else if (include_dirs != NULL)
    include_dirs[0] = ".";
    include_dirs[0] = ".";
}
}
 
 
/* .include "filename" | filename
/* .include "filename" | filename
   .copy    "filename" | filename
   .copy    "filename" | filename
 
 
   FIXME 'include' file should be omitted from any output listing,
   FIXME 'include' file should be omitted from any output listing,
     'copy' should be included in any output listing
     'copy' should be included in any output listing
   FIXME -- prevent any included files from changing listing (compat only)
   FIXME -- prevent any included files from changing listing (compat only)
   FIXME -- need to include source file directory in search path; what's a
   FIXME -- need to include source file directory in search path; what's a
      good way to do this?
      good way to do this?
 
 
   Entering/exiting included/copied file clears all local labels.  */
   Entering/exiting included/copied file clears all local labels.  */
 
 
static void
static void
tic54x_include (ignored)
tic54x_include (ignored)
     int ignored ATTRIBUTE_UNUSED;
     int ignored ATTRIBUTE_UNUSED;
{
{
  char newblock[] = " .newblock\n";
  char newblock[] = " .newblock\n";
  char *filename;
  char *filename;
  char *input;
  char *input;
  int len, c = -1;
  int len, c = -1;
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  SKIP_WHITESPACE ();
  SKIP_WHITESPACE ();
 
 
  if (*input_line_pointer == '"')
  if (*input_line_pointer == '"')
    {
    {
      filename = demand_copy_C_string (&len);
      filename = demand_copy_C_string (&len);
      demand_empty_rest_of_line ();
      demand_empty_rest_of_line ();
    }
    }
  else
  else
    {
    {
      filename = input_line_pointer;
      filename = input_line_pointer;
      while (!is_end_of_line[(int) *input_line_pointer])
      while (!is_end_of_line[(int) *input_line_pointer])
        ++input_line_pointer;
        ++input_line_pointer;
      c = *input_line_pointer;
      c = *input_line_pointer;
      *input_line_pointer = '\0';
      *input_line_pointer = '\0';
      filename = strcpy (xmalloc (strlen (filename) + 1), filename);
      filename = strcpy (xmalloc (strlen (filename) + 1), filename);
      *input_line_pointer = c;
      *input_line_pointer = c;
      demand_empty_rest_of_line ();
      demand_empty_rest_of_line ();
    }
    }
  /* Insert a partial line with the filename (for the sake of s_include)
  /* Insert a partial line with the filename (for the sake of s_include)
     and a .newblock.
     and a .newblock.
     The included file will be inserted before the newblock, so that the
     The included file will be inserted before the newblock, so that the
     newblock is executed after the included file is processed.  */
     newblock is executed after the included file is processed.  */
  input = xmalloc (sizeof (newblock) + strlen (filename) + 4);
  input = xmalloc (sizeof (newblock) + strlen (filename) + 4);
  sprintf (input, "\"%s\"\n%s", filename, newblock);
  sprintf (input, "\"%s\"\n%s", filename, newblock);
  input_scrub_insert_line (input);
  input_scrub_insert_line (input);
 
 
  tic54x_clear_local_labels (0);
  tic54x_clear_local_labels (0);
 
 
  tic54x_set_default_include (0);
  tic54x_set_default_include (0);
 
 
  s_include (0);
  s_include (0);
}
}
 
 
static void
static void
tic54x_message (type)
tic54x_message (type)
     int type;
     int type;
{
{
  char *msg;
  char *msg;
  char c;
  char c;
  int len;
  int len;
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  if (*input_line_pointer == '"')
  if (*input_line_pointer == '"')
    msg = demand_copy_C_string (&len);
    msg = demand_copy_C_string (&len);
  else
  else
    {
    {
      msg = input_line_pointer;
      msg = input_line_pointer;
      while (!is_end_of_line[(int) *input_line_pointer])
      while (!is_end_of_line[(int) *input_line_pointer])
        ++input_line_pointer;
        ++input_line_pointer;
      c = *input_line_pointer;
      c = *input_line_pointer;
      *input_line_pointer = 0;
      *input_line_pointer = 0;
      msg = strcpy (xmalloc (strlen (msg) + 1), msg);
      msg = strcpy (xmalloc (strlen (msg) + 1), msg);
      *input_line_pointer = c;
      *input_line_pointer = c;
    }
    }
 
 
  switch (type)
  switch (type)
    {
    {
    case 'm':
    case 'm':
      as_tsktsk ("%s", msg);
      as_tsktsk ("%s", msg);
      break;
      break;
    case 'w':
    case 'w':
      as_warn ("%s", msg);
      as_warn ("%s", msg);
      break;
      break;
    case 'e':
    case 'e':
      as_bad ("%s", msg);
      as_bad ("%s", msg);
      break;
      break;
    }
    }
 
 
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
/* .label <symbol>
/* .label <symbol>
   Define a special symbol that refers to the loadtime address rather than the
   Define a special symbol that refers to the loadtime address rather than the
   runtime address within the current section.
   runtime address within the current section.
 
 
   This symbol gets a special storage class so that when it is resolved, it is
   This symbol gets a special storage class so that when it is resolved, it is
   resolved relative to the load address (lma) of the section rather than the
   resolved relative to the load address (lma) of the section rather than the
   run address (vma).  */
   run address (vma).  */
 
 
static void
static void
tic54x_label (ignored)
tic54x_label (ignored)
     int ignored ATTRIBUTE_UNUSED;
     int ignored ATTRIBUTE_UNUSED;
{
{
  char *name = input_line_pointer;
  char *name = input_line_pointer;
  symbolS *symbolP;
  symbolS *symbolP;
  int c;
  int c;
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  c = get_symbol_end ();
  c = get_symbol_end ();
  symbolP = colon (name);
  symbolP = colon (name);
  S_SET_STORAGE_CLASS (symbolP, C_STATLAB);
  S_SET_STORAGE_CLASS (symbolP, C_STATLAB);
 
 
  *input_line_pointer = c;
  *input_line_pointer = c;
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
/* .mmregs
/* .mmregs
   Install all memory-mapped register names into the symbol table as
   Install all memory-mapped register names into the symbol table as
   absolute local symbols.  */
   absolute local symbols.  */
 
 
static void
static void
tic54x_mmregs (ignored)
tic54x_mmregs (ignored)
     int ignored ATTRIBUTE_UNUSED;
     int ignored ATTRIBUTE_UNUSED;
{
{
  symbol *sym;
  symbol *sym;
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  for (sym = (symbol *) mmregs; sym->name; sym++)
  for (sym = (symbol *) mmregs; sym->name; sym++)
    {
    {
      symbolS *symbolP = symbol_new (sym->name, absolute_section,
      symbolS *symbolP = symbol_new (sym->name, absolute_section,
                                     (valueT) sym->value, &zero_address_frag);
                                     (valueT) sym->value, &zero_address_frag);
      SF_SET_LOCAL (symbolP);
      SF_SET_LOCAL (symbolP);
      symbol_table_insert (symbolP);
      symbol_table_insert (symbolP);
    }
    }
}
}
 
 
/* .loop [count]
/* .loop [count]
   Count defaults to 1024.  */
   Count defaults to 1024.  */
 
 
static void
static void
tic54x_loop (count)
tic54x_loop (count)
     int count;
     int count;
{
{
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  SKIP_WHITESPACE ();
  SKIP_WHITESPACE ();
  if (!is_end_of_line[(int) *input_line_pointer])
  if (!is_end_of_line[(int) *input_line_pointer])
    count = get_absolute_expression ();
    count = get_absolute_expression ();
 
 
  do_repeat (count, "LOOP", "ENDLOOP");
  do_repeat (count, "LOOP", "ENDLOOP");
}
}
 
 
/* Normally, endloop gets eaten by the preceding loop.  */
/* Normally, endloop gets eaten by the preceding loop.  */
 
 
static void
static void
tic54x_endloop (ignore)
tic54x_endloop (ignore)
     int ignore ATTRIBUTE_UNUSED;
     int ignore ATTRIBUTE_UNUSED;
{
{
  as_bad (_("ENDLOOP without corresponding LOOP"));
  as_bad (_("ENDLOOP without corresponding LOOP"));
  ignore_rest_of_line ();
  ignore_rest_of_line ();
}
}
 
 
/* .break [condition].  */
/* .break [condition].  */
 
 
static void
static void
tic54x_break (ignore)
tic54x_break (ignore)
     int ignore ATTRIBUTE_UNUSED;
     int ignore ATTRIBUTE_UNUSED;
{
{
  int cond = 1;
  int cond = 1;
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  SKIP_WHITESPACE ();
  SKIP_WHITESPACE ();
  if (!is_end_of_line[(int) *input_line_pointer])
  if (!is_end_of_line[(int) *input_line_pointer])
    cond = get_absolute_expression ();
    cond = get_absolute_expression ();
 
 
  if (cond)
  if (cond)
    end_repeat (substitution_line ? 1 : 0);
    end_repeat (substitution_line ? 1 : 0);
}
}
 
 
static void
static void
set_address_mode (mode)
set_address_mode (mode)
     int mode;
     int mode;
{
{
  amode = mode;
  amode = mode;
  if (mode == far_mode)
  if (mode == far_mode)
    {
    {
      symbolS *symbolP = symbol_new ("__allow_far", absolute_section,
      symbolS *symbolP = symbol_new ("__allow_far", absolute_section,
                                     (valueT) 1, &zero_address_frag);
                                     (valueT) 1, &zero_address_frag);
      SF_SET_LOCAL (symbolP);
      SF_SET_LOCAL (symbolP);
      symbol_table_insert (symbolP);
      symbol_table_insert (symbolP);
    }
    }
}
}
 
 
static int address_mode_needs_set = 1;
static int address_mode_needs_set = 1;
 
 
static void
static void
tic54x_address_mode (mode)
tic54x_address_mode (mode)
     int mode;
     int mode;
{
{
  if (assembly_begun && amode != (unsigned) mode)
  if (assembly_begun && amode != (unsigned) mode)
    {
    {
      as_bad (_("Mixing of normal and extended addressing not supported"));
      as_bad (_("Mixing of normal and extended addressing not supported"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
  if (mode == far_mode && cpu != VNONE && cpu != V548 && cpu != V549)
  if (mode == far_mode && cpu != VNONE && cpu != V548 && cpu != V549)
    {
    {
      as_bad (_("Extended addressing not supported on the specified CPU"));
      as_bad (_("Extended addressing not supported on the specified CPU"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  set_address_mode (mode);
  set_address_mode (mode);
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
/* .sblock "section"|section [,...,"section"|section]
/* .sblock "section"|section [,...,"section"|section]
   Designate initialized sections for blocking.  */
   Designate initialized sections for blocking.  */
 
 
static void
static void
tic54x_sblock (ignore)
tic54x_sblock (ignore)
     int ignore ATTRIBUTE_UNUSED;
     int ignore ATTRIBUTE_UNUSED;
{
{
  int c = ',';
  int c = ',';
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  while (c == ',')
  while (c == ',')
    {
    {
      segT seg;
      segT seg;
      char *name;
      char *name;
 
 
      if (*input_line_pointer == '"')
      if (*input_line_pointer == '"')
        {
        {
          int len;
          int len;
 
 
          name = demand_copy_C_string (&len);
          name = demand_copy_C_string (&len);
        }
        }
      else
      else
        {
        {
          char *section_name = input_line_pointer;
          char *section_name = input_line_pointer;
 
 
          c = get_symbol_end ();
          c = get_symbol_end ();
          name = xmalloc (strlen (section_name) + 1);
          name = xmalloc (strlen (section_name) + 1);
          strcpy (name, section_name);
          strcpy (name, section_name);
          *input_line_pointer = c;
          *input_line_pointer = c;
        }
        }
 
 
      seg = bfd_get_section_by_name (stdoutput, name);
      seg = bfd_get_section_by_name (stdoutput, name);
      if (seg == NULL)
      if (seg == NULL)
        {
        {
          as_bad (_("Unrecognized section '%s'"), name);
          as_bad (_("Unrecognized section '%s'"), name);
          ignore_rest_of_line ();
          ignore_rest_of_line ();
          return;
          return;
        }
        }
      else if (!tic54x_initialized_section (seg))
      else if (!tic54x_initialized_section (seg))
        {
        {
          as_bad (_(".sblock may be used for initialized sections only"));
          as_bad (_(".sblock may be used for initialized sections only"));
          ignore_rest_of_line ();
          ignore_rest_of_line ();
          return;
          return;
        }
        }
      seg->flags |= SEC_TIC54X_BLOCK;
      seg->flags |= SEC_TIC54X_BLOCK;
 
 
      c = *input_line_pointer;
      c = *input_line_pointer;
      if (!is_end_of_line[(int) c])
      if (!is_end_of_line[(int) c])
        ++input_line_pointer;
        ++input_line_pointer;
    }
    }
 
 
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
/* symbol .set value
/* symbol .set value
   symbol .equ value
   symbol .equ value
 
 
   value must be defined externals; no forward-referencing allowed
   value must be defined externals; no forward-referencing allowed
   symbols assigned with .set/.equ may not be redefined.  */
   symbols assigned with .set/.equ may not be redefined.  */
 
 
static void
static void
tic54x_set (ignore)
tic54x_set (ignore)
     int ignore ATTRIBUTE_UNUSED;
     int ignore ATTRIBUTE_UNUSED;
{
{
  symbolS *symbolP;
  symbolS *symbolP;
  char *name;
  char *name;
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  if (!line_label)
  if (!line_label)
    {
    {
      as_bad (_("Symbol missing for .set/.equ"));
      as_bad (_("Symbol missing for .set/.equ"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
  name = xstrdup (S_GET_NAME (line_label));
  name = xstrdup (S_GET_NAME (line_label));
  line_label = NULL;
  line_label = NULL;
  if ((symbolP = symbol_find (name)) == NULL
  if ((symbolP = symbol_find (name)) == NULL
      && (symbolP = md_undefined_symbol (name)) == NULL)
      && (symbolP = md_undefined_symbol (name)) == NULL)
    {
    {
      symbolP = symbol_new (name, absolute_section, 0, &zero_address_frag);
      symbolP = symbol_new (name, absolute_section, 0, &zero_address_frag);
      S_SET_STORAGE_CLASS (symbolP, C_STAT);
      S_SET_STORAGE_CLASS (symbolP, C_STAT);
    }
    }
  free (name);
  free (name);
  S_SET_DATA_TYPE (symbolP, T_INT);
  S_SET_DATA_TYPE (symbolP, T_INT);
  S_SET_SEGMENT (symbolP, absolute_section);
  S_SET_SEGMENT (symbolP, absolute_section);
  symbol_table_insert (symbolP);
  symbol_table_insert (symbolP);
  pseudo_set (symbolP);
  pseudo_set (symbolP);
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
/* .fclist
/* .fclist
   .fcnolist
   .fcnolist
   List false conditional blocks.  */
   List false conditional blocks.  */
 
 
static void
static void
tic54x_fclist (show)
tic54x_fclist (show)
     int show;
     int show;
{
{
  if (show)
  if (show)
    listing &= ~LISTING_NOCOND;
    listing &= ~LISTING_NOCOND;
  else
  else
    listing |= LISTING_NOCOND;
    listing |= LISTING_NOCOND;
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
static void
static void
tic54x_sslist (show)
tic54x_sslist (show)
     int show;
     int show;
{
{
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  listing_sslist = show;
  listing_sslist = show;
}
}
 
 
/* .var SYM[,...,SYMN]
/* .var SYM[,...,SYMN]
   Define a substitution string to be local to a macro.  */
   Define a substitution string to be local to a macro.  */
 
 
static void
static void
tic54x_var (ignore)
tic54x_var (ignore)
     int ignore ATTRIBUTE_UNUSED;
     int ignore ATTRIBUTE_UNUSED;
{
{
  static char empty[] = "";
  static char empty[] = "";
  char *name;
  char *name;
  int c;
  int c;
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  if (macro_level == 0)
  if (macro_level == 0)
    {
    {
      as_bad (_(".var may only be used within a macro definition"));
      as_bad (_(".var may only be used within a macro definition"));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
  do
  do
    {
    {
      if (!ISALPHA (*input_line_pointer))
      if (!ISALPHA (*input_line_pointer))
        {
        {
          as_bad (_("Substitution symbols must begin with a letter"));
          as_bad (_("Substitution symbols must begin with a letter"));
          ignore_rest_of_line ();
          ignore_rest_of_line ();
          return;
          return;
        }
        }
      name = input_line_pointer;
      name = input_line_pointer;
      c = get_symbol_end ();
      c = get_symbol_end ();
      /* .var symbols start out with a null string.  */
      /* .var symbols start out with a null string.  */
      name = strcpy (xmalloc (strlen (name) + 1), name);
      name = strcpy (xmalloc (strlen (name) + 1), name);
      hash_insert (subsym_hash[macro_level], name, empty);
      hash_insert (subsym_hash[macro_level], name, empty);
      *input_line_pointer = c;
      *input_line_pointer = c;
      if (c == ',')
      if (c == ',')
        {
        {
          ++input_line_pointer;
          ++input_line_pointer;
          if (is_end_of_line[(int) *input_line_pointer])
          if (is_end_of_line[(int) *input_line_pointer])
            c = *input_line_pointer;
            c = *input_line_pointer;
        }
        }
    }
    }
  while (c == ',');
  while (c == ',');
 
 
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
/* .mlib <macro library filename>
/* .mlib <macro library filename>
 
 
   Macro libraries are archived (standard AR-format) text macro definitions
   Macro libraries are archived (standard AR-format) text macro definitions
   Expand the file and include it.
   Expand the file and include it.
 
 
   FIXME need to try the source file directory as well.  */
   FIXME need to try the source file directory as well.  */
 
 
static void
static void
tic54x_mlib (ignore)
tic54x_mlib (ignore)
     int ignore ATTRIBUTE_UNUSED;
     int ignore ATTRIBUTE_UNUSED;
{
{
  char *filename;
  char *filename;
  char *path;
  char *path;
  int len, i;
  int len, i;
  bfd *abfd, *mbfd;
  bfd *abfd, *mbfd;
 
 
  ILLEGAL_WITHIN_STRUCT ();
  ILLEGAL_WITHIN_STRUCT ();
 
 
  /* Parse the filename.  */
  /* Parse the filename.  */
  if (*input_line_pointer == '"')
  if (*input_line_pointer == '"')
    {
    {
      if ((filename = demand_copy_C_string (&len)) == NULL)
      if ((filename = demand_copy_C_string (&len)) == NULL)
        return;
        return;
    }
    }
  else
  else
    {
    {
      SKIP_WHITESPACE ();
      SKIP_WHITESPACE ();
      len = 0;
      len = 0;
      while (!is_end_of_line[(int) *input_line_pointer]
      while (!is_end_of_line[(int) *input_line_pointer]
             && !ISSPACE (*input_line_pointer))
             && !ISSPACE (*input_line_pointer))
        {
        {
          obstack_1grow (&notes, *input_line_pointer);
          obstack_1grow (&notes, *input_line_pointer);
          ++input_line_pointer;
          ++input_line_pointer;
          ++len;
          ++len;
        }
        }
      obstack_1grow (&notes, '\0');
      obstack_1grow (&notes, '\0');
      filename = obstack_finish (&notes);
      filename = obstack_finish (&notes);
    }
    }
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
 
 
  tic54x_set_default_include (0);
  tic54x_set_default_include (0);
  path = xmalloc ((unsigned long) len + include_dir_maxlen + 5);
  path = xmalloc ((unsigned long) len + include_dir_maxlen + 5);
 
 
  for (i = 0; i < include_dir_count; i++)
  for (i = 0; i < include_dir_count; i++)
    {
    {
      FILE *try;
      FILE *try;
 
 
      strcpy (path, include_dirs[i]);
      strcpy (path, include_dirs[i]);
      strcat (path, "/");
      strcat (path, "/");
      strcat (path, filename);
      strcat (path, filename);
      if ((try = fopen (path, "r")) != NULL)
      if ((try = fopen (path, "r")) != NULL)
        {
        {
          fclose (try);
          fclose (try);
          break;
          break;
        }
        }
    }
    }
 
 
  if (i >= include_dir_count)
  if (i >= include_dir_count)
    {
    {
      free (path);
      free (path);
      path = filename;
      path = filename;
    }
    }
 
 
  /* FIXME: if path is found, malloc'd storage is not freed.  Of course, this
  /* FIXME: if path is found, malloc'd storage is not freed.  Of course, this
     happens all over the place, and since the assembler doesn't usually keep
     happens all over the place, and since the assembler doesn't usually keep
     running for a very long time, it really doesn't matter.  */
     running for a very long time, it really doesn't matter.  */
  register_dependency (path);
  register_dependency (path);
 
 
  /* Expand all archive entries to temporary files and include them.  */
  /* Expand all archive entries to temporary files and include them.  */
  abfd = bfd_openr (path, NULL);
  abfd = bfd_openr (path, NULL);
  if (!abfd)
  if (!abfd)
    {
    {
      as_bad (_("can't open macro library file '%s' for reading: %s"),
      as_bad (_("can't open macro library file '%s' for reading: %s"),
              path, bfd_errmsg (bfd_get_error ()));
              path, bfd_errmsg (bfd_get_error ()));
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
  if (!bfd_check_format (abfd, bfd_archive))
  if (!bfd_check_format (abfd, bfd_archive))
    {
    {
      as_bad (_("File '%s' not in macro archive format"), path);
      as_bad (_("File '%s' not in macro archive format"), path);
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return;
      return;
    }
    }
 
 
  /* Open each BFD as binary (it should be straight ASCII text).  */
  /* Open each BFD as binary (it should be straight ASCII text).  */
  for (mbfd = bfd_openr_next_archived_file (abfd, NULL);
  for (mbfd = bfd_openr_next_archived_file (abfd, NULL);
       mbfd != NULL; mbfd = bfd_openr_next_archived_file (abfd, mbfd))
       mbfd != NULL; mbfd = bfd_openr_next_archived_file (abfd, mbfd))
    {
    {
      /* Get a size at least as big as the archive member.  */
      /* Get a size at least as big as the archive member.  */
      bfd_size_type size = bfd_get_size (mbfd);
      bfd_size_type size = bfd_get_size (mbfd);
      char *buf = xmalloc (size);
      char *buf = xmalloc (size);
      char *fname = tmpnam (NULL);
      char *fname = tmpnam (NULL);
      FILE *ftmp;
      FILE *ftmp;
 
 
      /* We're not sure how big it is, but it will be smaller than "size".  */
      /* We're not sure how big it is, but it will be smaller than "size".  */
      bfd_bread (buf, size, mbfd);
      bfd_bread (buf, size, mbfd);
 
 
      /* Write to a temporary file, then use s_include to include it
      /* Write to a temporary file, then use s_include to include it
         a bit of a hack.  */
         a bit of a hack.  */
      ftmp = fopen (fname, "w+b");
      ftmp = fopen (fname, "w+b");
      fwrite ((void *) buf, size, 1, ftmp);
      fwrite ((void *) buf, size, 1, ftmp);
      if (buf[size - 1] != '\n')
      if (buf[size - 1] != '\n')
        fwrite ("\n", 1, 1, ftmp);
        fwrite ("\n", 1, 1, ftmp);
      fclose (ftmp);
      fclose (ftmp);
      free (buf);
      free (buf);
      input_scrub_insert_file (fname);
      input_scrub_insert_file (fname);
      unlink (fname);
      unlink (fname);
    }
    }
}
}
 
 
const pseudo_typeS md_pseudo_table[] =
const pseudo_typeS md_pseudo_table[] =
{
{
  { "algebraic", s_ignore                 ,          0 },
  { "algebraic", s_ignore                 ,          0 },
  { "align"    , tic54x_align_words       ,        128 },
  { "align"    , tic54x_align_words       ,        128 },
  { "ascii"    , tic54x_stringer          ,        'p' },
  { "ascii"    , tic54x_stringer          ,        'p' },
  { "asciz"    , tic54x_stringer          ,        'P' },
  { "asciz"    , tic54x_stringer          ,        'P' },
  { "even"     , tic54x_align_words       ,          2 },
  { "even"     , tic54x_align_words       ,          2 },
  { "asg"      , tic54x_asg               ,          0 },
  { "asg"      , tic54x_asg               ,          0 },
  { "eval"     , tic54x_eval              ,          0 },
  { "eval"     , tic54x_eval              ,          0 },
  { "bss"      , tic54x_bss               ,          0 },
  { "bss"      , tic54x_bss               ,          0 },
  { "byte"     , tic54x_cons              ,        'b' },
  { "byte"     , tic54x_cons              ,        'b' },
  { "ubyte"    , tic54x_cons              ,        'B' },
  { "ubyte"    , tic54x_cons              ,        'B' },
  { "char"     , tic54x_cons              ,        'c' },
  { "char"     , tic54x_cons              ,        'c' },
  { "uchar"    , tic54x_cons              ,        'C' },
  { "uchar"    , tic54x_cons              ,        'C' },
  { "clink"    , tic54x_clink             ,          0 },
  { "clink"    , tic54x_clink             ,          0 },
  { "c_mode"   , tic54x_address_mode      ,     c_mode },
  { "c_mode"   , tic54x_address_mode      ,     c_mode },
  { "copy"     , tic54x_include           ,        'c' },
  { "copy"     , tic54x_include           ,        'c' },
  { "include"  , tic54x_include           ,        'i' },
  { "include"  , tic54x_include           ,        'i' },
  { "data"     , tic54x_sect              ,        'd' },
  { "data"     , tic54x_sect              ,        'd' },
  { "double"   , tic54x_float_cons        ,        'd' },
  { "double"   , tic54x_float_cons        ,        'd' },
  { "ldouble"  , tic54x_float_cons        ,        'l' },
  { "ldouble"  , tic54x_float_cons        ,        'l' },
  { "drlist"   , s_ignore                 ,          0 },
  { "drlist"   , s_ignore                 ,          0 },
  { "drnolist" , s_ignore                 ,          0 },
  { "drnolist" , s_ignore                 ,          0 },
  { "emsg"     , tic54x_message           ,        'e' },
  { "emsg"     , tic54x_message           ,        'e' },
  { "mmsg"     , tic54x_message           ,        'm' },
  { "mmsg"     , tic54x_message           ,        'm' },
  { "wmsg"     , tic54x_message           ,        'w' },
  { "wmsg"     , tic54x_message           ,        'w' },
  { "far_mode" , tic54x_address_mode      ,   far_mode },
  { "far_mode" , tic54x_address_mode      ,   far_mode },
  { "fclist"   , tic54x_fclist            ,          1 },
  { "fclist"   , tic54x_fclist            ,          1 },
  { "fcnolist" , tic54x_fclist            ,          0 },
  { "fcnolist" , tic54x_fclist            ,          0 },
  { "field"    , tic54x_field             ,         -1 },
  { "field"    , tic54x_field             ,         -1 },
  { "float"    , tic54x_float_cons        ,        'f' },
  { "float"    , tic54x_float_cons        ,        'f' },
  { "xfloat"   , tic54x_float_cons        ,        'x' },
  { "xfloat"   , tic54x_float_cons        ,        'x' },
  { "global"   , tic54x_global            ,        'g' },
  { "global"   , tic54x_global            ,        'g' },
  { "def"      , tic54x_global            ,        'd' },
  { "def"      , tic54x_global            ,        'd' },
  { "ref"      , tic54x_global            ,        'r' },
  { "ref"      , tic54x_global            ,        'r' },
  { "half"     , tic54x_cons              ,        'h' },
  { "half"     , tic54x_cons              ,        'h' },
  { "uhalf"    , tic54x_cons              ,        'H' },
  { "uhalf"    , tic54x_cons              ,        'H' },
  { "short"    , tic54x_cons              ,        's' },
  { "short"    , tic54x_cons              ,        's' },
  { "ushort"   , tic54x_cons              ,        'S' },
  { "ushort"   , tic54x_cons              ,        'S' },
  { "if"       , s_if                     , (int) O_ne },
  { "if"       , s_if                     , (int) O_ne },
  { "elseif"   , s_elseif                 , (int) O_ne },
  { "elseif"   , s_elseif                 , (int) O_ne },
  { "else"     , s_else                   ,          0 },
  { "else"     , s_else                   ,          0 },
  { "endif"    , s_endif                  ,          0 },
  { "endif"    , s_endif                  ,          0 },
  { "int"      , tic54x_cons              ,        'i' },
  { "int"      , tic54x_cons              ,        'i' },
  { "uint"     , tic54x_cons              ,        'I' },
  { "uint"     , tic54x_cons              ,        'I' },
  { "word"     , tic54x_cons              ,        'w' },
  { "word"     , tic54x_cons              ,        'w' },
  { "uword"    , tic54x_cons              ,        'W' },
  { "uword"    , tic54x_cons              ,        'W' },
  { "label"    , tic54x_label             ,          0 }, /* Loadtime
  { "label"    , tic54x_label             ,          0 }, /* Loadtime
                                                             address.  */
                                                             address.  */
  { "length"   , s_ignore                 ,          0 },
  { "length"   , s_ignore                 ,          0 },
  { "width"    , s_ignore                 ,          0 },
  { "width"    , s_ignore                 ,          0 },
  { "long"     , tic54x_cons              ,        'l' },
  { "long"     , tic54x_cons              ,        'l' },
  { "ulong"    , tic54x_cons              ,        'L' },
  { "ulong"    , tic54x_cons              ,        'L' },
  { "xlong"    , tic54x_cons              ,        'x' },
  { "xlong"    , tic54x_cons              ,        'x' },
  { "loop"     , tic54x_loop              ,       1024 },
  { "loop"     , tic54x_loop              ,       1024 },
  { "break"    , tic54x_break             ,          0 },
  { "break"    , tic54x_break             ,          0 },
  { "endloop"  , tic54x_endloop           ,          0 },
  { "endloop"  , tic54x_endloop           ,          0 },
  { "mlib"     , tic54x_mlib              ,          0 },
  { "mlib"     , tic54x_mlib              ,          0 },
  { "mlist"    , s_ignore                 ,          0 },
  { "mlist"    , s_ignore                 ,          0 },
  { "mnolist"  , s_ignore                 ,          0 },
  { "mnolist"  , s_ignore                 ,          0 },
  { "mmregs"   , tic54x_mmregs            ,          0 },
  { "mmregs"   , tic54x_mmregs            ,          0 },
  { "newblock" , tic54x_clear_local_labels,          0 },
  { "newblock" , tic54x_clear_local_labels,          0 },
  { "option"   , s_ignore                 ,          0 },
  { "option"   , s_ignore                 ,          0 },
  { "p2align"  , tic54x_p2align           ,          0 },
  { "p2align"  , tic54x_p2align           ,          0 },
  { "sblock"   , tic54x_sblock            ,          0 },
  { "sblock"   , tic54x_sblock            ,          0 },
  { "sect"     , tic54x_sect              ,        '*' },
  { "sect"     , tic54x_sect              ,        '*' },
  { "set"      , tic54x_set               ,          0 },
  { "set"      , tic54x_set               ,          0 },
  { "equ"      , tic54x_set               ,          0 },
  { "equ"      , tic54x_set               ,          0 },
  { "space"    , tic54x_space             ,          0 },
  { "space"    , tic54x_space             ,          0 },
  { "bes"      , tic54x_space             ,          1 },
  { "bes"      , tic54x_space             ,          1 },
  { "sslist"   , tic54x_sslist            ,          1 },
  { "sslist"   , tic54x_sslist            ,          1 },
  { "ssnolist" , tic54x_sslist            ,          0 },
  { "ssnolist" , tic54x_sslist            ,          0 },
  { "string"   , tic54x_stringer          ,        's' },
  { "string"   , tic54x_stringer          ,        's' },
  { "pstring"  , tic54x_stringer          ,        'p' },
  { "pstring"  , tic54x_stringer          ,        'p' },
  { "struct"   , tic54x_struct            ,          0 },
  { "struct"   , tic54x_struct            ,          0 },
  { "tag"      , tic54x_tag               ,          0 },
  { "tag"      , tic54x_tag               ,          0 },
  { "endstruct", tic54x_endstruct         ,          0 },
  { "endstruct", tic54x_endstruct         ,          0 },
  { "tab"      , s_ignore                 ,          0 },
  { "tab"      , s_ignore                 ,          0 },
  { "text"     , tic54x_sect              ,        't' },
  { "text"     , tic54x_sect              ,        't' },
  { "union"    , tic54x_struct            ,          1 },
  { "union"    , tic54x_struct            ,          1 },
  { "endunion" , tic54x_endstruct         ,          1 },
  { "endunion" , tic54x_endstruct         ,          1 },
  { "usect"    , tic54x_usect             ,          0 },
  { "usect"    , tic54x_usect             ,          0 },
  { "var"      , tic54x_var               ,          0 },
  { "var"      , tic54x_var               ,          0 },
  { "version"  , tic54x_version           ,          0 },
  { "version"  , tic54x_version           ,          0 },
  {0           , 0                        ,          0 }
  {0           , 0                        ,          0 }
};
};
 
 
int
int
md_parse_option (c, arg)
md_parse_option (c, arg)
     int c;
     int c;
     char *arg;
     char *arg;
{
{
  switch (c)
  switch (c)
    {
    {
    default:
    default:
      return 0;
      return 0;
    case OPTION_COFF_VERSION:
    case OPTION_COFF_VERSION:
      {
      {
        int version = atoi (arg);
        int version = atoi (arg);
 
 
        if (version != 0 && version != 1 && version != 2)
        if (version != 0 && version != 1 && version != 2)
          as_fatal (_("Bad COFF version '%s'"), arg);
          as_fatal (_("Bad COFF version '%s'"), arg);
        /* FIXME -- not yet implemented.  */
        /* FIXME -- not yet implemented.  */
        break;
        break;
      }
      }
    case OPTION_CPU_VERSION:
    case OPTION_CPU_VERSION:
      {
      {
        cpu = lookup_version (arg);
        cpu = lookup_version (arg);
        cpu_needs_set = 1;
        cpu_needs_set = 1;
        if (cpu == VNONE)
        if (cpu == VNONE)
          as_fatal (_("Bad CPU version '%s'"), arg);
          as_fatal (_("Bad CPU version '%s'"), arg);
        break;
        break;
      }
      }
    case OPTION_ADDRESS_MODE:
    case OPTION_ADDRESS_MODE:
      amode = far_mode;
      amode = far_mode;
      address_mode_needs_set = 1;
      address_mode_needs_set = 1;
      break;
      break;
    case OPTION_STDERR_TO_FILE:
    case OPTION_STDERR_TO_FILE:
      {
      {
        char *filename = arg;
        char *filename = arg;
        FILE *fp = fopen (filename, "w+");
        FILE *fp = fopen (filename, "w+");
 
 
        if (fp == NULL)
        if (fp == NULL)
          as_fatal (_("Can't redirect stderr to the file '%s'"), filename);
          as_fatal (_("Can't redirect stderr to the file '%s'"), filename);
        fclose (fp);
        fclose (fp);
        if ((fp = freopen (filename, "w+", stderr)) == NULL)
        if ((fp = freopen (filename, "w+", stderr)) == NULL)
          as_fatal (_("Can't redirect stderr to the file '%s'"), filename);
          as_fatal (_("Can't redirect stderr to the file '%s'"), filename);
        break;
        break;
      }
      }
    }
    }
 
 
  return 1;
  return 1;
}
}
 
 
/* Create a "local" substitution string hash table for a new macro level
/* Create a "local" substitution string hash table for a new macro level
   Some docs imply that macros have to use .newblock in order to be able
   Some docs imply that macros have to use .newblock in order to be able
   to re-use a local label.  We effectively do an automatic .newblock by
   to re-use a local label.  We effectively do an automatic .newblock by
   deleting the local label hash between macro invocations.  */
   deleting the local label hash between macro invocations.  */
 
 
void
void
tic54x_macro_start ()
tic54x_macro_start ()
{
{
  ++macro_level;
  ++macro_level;
  subsym_hash[macro_level] = hash_new ();
  subsym_hash[macro_level] = hash_new ();
  local_label_hash[macro_level] = hash_new ();
  local_label_hash[macro_level] = hash_new ();
}
}
 
 
void
void
tic54x_macro_info (macro)
tic54x_macro_info (macro)
     const macro_entry *macro;
     const macro_entry *macro;
{
{
  const formal_entry *entry;
  const formal_entry *entry;
 
 
  /* Put the formal args into the substitution symbol table.  */
  /* Put the formal args into the substitution symbol table.  */
  for (entry = macro->formals; entry; entry = entry->next)
  for (entry = macro->formals; entry; entry = entry->next)
    {
    {
      char *name = strncpy (xmalloc (entry->name.len + 1),
      char *name = strncpy (xmalloc (entry->name.len + 1),
                            entry->name.ptr, entry->name.len);
                            entry->name.ptr, entry->name.len);
      char *value = strncpy (xmalloc (entry->actual.len + 1),
      char *value = strncpy (xmalloc (entry->actual.len + 1),
                             entry->actual.ptr, entry->actual.len);
                             entry->actual.ptr, entry->actual.len);
 
 
      name[entry->name.len] = '\0';
      name[entry->name.len] = '\0';
      value[entry->actual.len] = '\0';
      value[entry->actual.len] = '\0';
      hash_insert (subsym_hash[macro_level], name, value);
      hash_insert (subsym_hash[macro_level], name, value);
    }
    }
}
}
 
 
/* Get rid of this macro's .var's, arguments, and local labels.  */
/* Get rid of this macro's .var's, arguments, and local labels.  */
 
 
void
void
tic54x_macro_end ()
tic54x_macro_end ()
{
{
  hash_die (subsym_hash[macro_level]);
  hash_die (subsym_hash[macro_level]);
  subsym_hash[macro_level] = NULL;
  subsym_hash[macro_level] = NULL;
  hash_die (local_label_hash[macro_level]);
  hash_die (local_label_hash[macro_level]);
  local_label_hash[macro_level] = NULL;
  local_label_hash[macro_level] = NULL;
  --macro_level;
  --macro_level;
}
}
 
 
static int
static int
subsym_symlen (a, ignore)
subsym_symlen (a, ignore)
     char *a;
     char *a;
     char *ignore ATTRIBUTE_UNUSED;
     char *ignore ATTRIBUTE_UNUSED;
{
{
  return strlen (a);
  return strlen (a);
}
}
 
 
/* Compare symbol A to string B.  */
/* Compare symbol A to string B.  */
 
 
static int
static int
subsym_symcmp (a, b)
subsym_symcmp (a, b)
     char *a;
     char *a;
     char *b;
     char *b;
{
{
  return strcmp (a, b);
  return strcmp (a, b);
}
}
 
 
/* Return the index of the first occurrence of B in A, or zero if none
/* Return the index of the first occurrence of B in A, or zero if none
   assumes b is an integer char value as a string.  Index is one-based.  */
   assumes b is an integer char value as a string.  Index is one-based.  */
 
 
static int
static int
subsym_firstch (a, b)
subsym_firstch (a, b)
     char *a;
     char *a;
     char *b;
     char *b;
{
{
  int val = atoi (b);
  int val = atoi (b);
  char *tmp = strchr (a, val);
  char *tmp = strchr (a, val);
 
 
  return tmp ? tmp - a + 1 : 0;
  return tmp ? tmp - a + 1 : 0;
}
}
 
 
/* Similar to firstch, but returns index of last occurrence of B in A.  */
/* Similar to firstch, but returns index of last occurrence of B in A.  */
 
 
static int
static int
subsym_lastch (a, b)
subsym_lastch (a, b)
     char *a;
     char *a;
     char *b;
     char *b;
{
{
  int val = atoi (b);
  int val = atoi (b);
  char *tmp = strrchr (a, val);
  char *tmp = strrchr (a, val);
 
 
  return tmp ? tmp - a + 1 : 0;
  return tmp ? tmp - a + 1 : 0;
}
}
 
 
/* Returns 1 if string A is defined in the symbol table (NOT the substitution
/* Returns 1 if string A is defined in the symbol table (NOT the substitution
   symbol table).  */
   symbol table).  */
 
 
static int
static int
subsym_isdefed (a, ignore)
subsym_isdefed (a, ignore)
     char *a;
     char *a;
     char *ignore ATTRIBUTE_UNUSED;
     char *ignore ATTRIBUTE_UNUSED;
{
{
  symbolS *symbolP = symbol_find (a);
  symbolS *symbolP = symbol_find (a);
 
 
  return symbolP != NULL;
  return symbolP != NULL;
}
}
 
 
/* Assign first member of comma-separated list B (e.g. "1,2,3") to the symbol
/* Assign first member of comma-separated list B (e.g. "1,2,3") to the symbol
   A, or zero if B is a null string.  Both arguments *must* be substitution
   A, or zero if B is a null string.  Both arguments *must* be substitution
   symbols, unsubstituted.  */
   symbols, unsubstituted.  */
 
 
static int
static int
subsym_ismember (sym, list)
subsym_ismember (sym, list)
     char *sym;
     char *sym;
     char *list;
     char *list;
{
{
  char *elem, *ptr, *listv;
  char *elem, *ptr, *listv;
 
 
  if (!list)
  if (!list)
    return 0;
    return 0;
 
 
  listv = subsym_lookup (list, macro_level);
  listv = subsym_lookup (list, macro_level);
  if (!listv)
  if (!listv)
    {
    {
      as_bad (_("Undefined substitution symbol '%s'"), list);
      as_bad (_("Undefined substitution symbol '%s'"), list);
      ignore_rest_of_line ();
      ignore_rest_of_line ();
      return 0;
      return 0;
    }
    }
 
 
  ptr = elem = xmalloc (strlen (listv) + 1);
  ptr = elem = xmalloc (strlen (listv) + 1);
  strcpy (elem, listv);
  strcpy (elem, listv);
  while (*ptr && *ptr != ',')
  while (*ptr && *ptr != ',')
    ++ptr;
    ++ptr;
  *ptr++ = 0;
  *ptr++ = 0;
 
 
  subsym_create_or_replace (sym, elem);
  subsym_create_or_replace (sym, elem);
 
 
  /* Reassign the list.  */
  /* Reassign the list.  */
  subsym_create_or_replace (list, ptr);
  subsym_create_or_replace (list, ptr);
 
 
  /* Assume this value, docs aren't clear.  */
  /* Assume this value, docs aren't clear.  */
  return *list != 0;
  return *list != 0;
}
}
 
 
/* Return zero if not a constant; otherwise:
/* Return zero if not a constant; otherwise:
   1 if binary
   1 if binary
   2 if octal
   2 if octal
   3 if hexadecimal
   3 if hexadecimal
   4 if character
   4 if character
   5 if decimal.  */
   5 if decimal.  */
 
 
static int
static int
subsym_iscons (a, ignore)
subsym_iscons (a, ignore)
     char *a;
     char *a;
     char *ignore ATTRIBUTE_UNUSED;
     char *ignore ATTRIBUTE_UNUSED;
{
{
  expressionS exp;
  expressionS exp;
 
 
  parse_expression (a, &exp);
  parse_expression (a, &exp);
 
 
  if (exp.X_op == O_constant)
  if (exp.X_op == O_constant)
    {
    {
      int len = strlen (a);
      int len = strlen (a);
 
 
      switch (TOUPPER (a[len - 1]))
      switch (TOUPPER (a[len - 1]))
        {
        {
        case 'B':
        case 'B':
          return 1;
          return 1;
        case 'Q':
        case 'Q':
          return 2;
          return 2;
        case 'H':
        case 'H':
          return 3;
          return 3;
        case '\'':
        case '\'':
          return 4;
          return 4;
        default:
        default:
          break;
          break;
        }
        }
      /* No suffix; either octal, hex, or decimal.  */
      /* No suffix; either octal, hex, or decimal.  */
      if (*a == '0' && len > 1)
      if (*a == '0' && len > 1)
        {
        {
          if (TOUPPER (a[1]) == 'X')
          if (TOUPPER (a[1]) == 'X')
            return 3;
            return 3;
          return 2;
          return 2;
        }
        }
      return 5;
      return 5;
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
/* Return 1 if A is a valid symbol name.  Expects string input.   */
/* Return 1 if A is a valid symbol name.  Expects string input.   */
 
 
static int
static int
subsym_isname (a, ignore)
subsym_isname (a, ignore)
     char *a;
     char *a;
     char *ignore ATTRIBUTE_UNUSED;
     char *ignore ATTRIBUTE_UNUSED;
{
{
  if (!is_name_beginner (*a))
  if (!is_name_beginner (*a))
    return 0;
    return 0;
  while (*a)
  while (*a)
    {
    {
      if (!is_part_of_name (*a))
      if (!is_part_of_name (*a))
        return 0;
        return 0;
      ++a;
      ++a;
    }
    }
  return 1;
  return 1;
}
}
 
 
/* Return whether the string is a register; accepts ar0-7, unless .mmregs has
/* Return whether the string is a register; accepts ar0-7, unless .mmregs has
   been seen; if so, recognize any memory-mapped register.
   been seen; if so, recognize any memory-mapped register.
   Note this does not recognize "A" or "B" accumulators.  */
   Note this does not recognize "A" or "B" accumulators.  */
 
 
static int
static int
subsym_isreg (a, ignore)
subsym_isreg (a, ignore)
     char *a;
     char *a;
     char *ignore ATTRIBUTE_UNUSED;
     char *ignore ATTRIBUTE_UNUSED;
{
{
  if (hash_find (reg_hash, a))
  if (hash_find (reg_hash, a))
    return 1;
    return 1;
  if (hash_find (mmreg_hash, a))
  if (hash_find (mmreg_hash, a))
    return 1;
    return 1;
  return 0;
  return 0;
}
}
 
 
/* Return the structure size, given the stag.  */
/* Return the structure size, given the stag.  */
 
 
static int
static int
subsym_structsz (name, ignore)
subsym_structsz (name, ignore)
     char *name;
     char *name;
     char *ignore ATTRIBUTE_UNUSED;
     char *ignore ATTRIBUTE_UNUSED;
{
{
  struct stag *stag = (struct stag *) hash_find (stag_hash, name);
  struct stag *stag = (struct stag *) hash_find (stag_hash, name);
 
 
  if (stag)
  if (stag)
    return stag->size;
    return stag->size;
 
 
  return 0;
  return 0;
}
}
 
 
/* If anybody actually uses this, they can fix it :)
/* If anybody actually uses this, they can fix it :)
   FIXME I'm not sure what the "reference point" of a structure is.  It might
   FIXME I'm not sure what the "reference point" of a structure is.  It might
   be either the initial offset given .struct, or it may be the offset of the
   be either the initial offset given .struct, or it may be the offset of the
   structure within another structure, or it might be something else
   structure within another structure, or it might be something else
   altogether.  since the TI assembler doesn't seem to ever do anything but
   altogether.  since the TI assembler doesn't seem to ever do anything but
   return zero, we punt and return zero.  */
   return zero, we punt and return zero.  */
 
 
static int
static int
subsym_structacc (stag_name, ignore)
subsym_structacc (stag_name, ignore)
     char *stag_name ATTRIBUTE_UNUSED;
     char *stag_name ATTRIBUTE_UNUSED;
     char *ignore ATTRIBUTE_UNUSED;
     char *ignore ATTRIBUTE_UNUSED;
{
{
  return 0;
  return 0;
}
}
 
 
static float
static float
math_ceil (arg1, ignore)
math_ceil (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (float) ceil (arg1);
  return (float) ceil (arg1);
}
}
 
 
static float
static float
math_cvi (arg1, ignore)
math_cvi (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (int) arg1;
  return (int) arg1;
}
}
 
 
static float
static float
math_floor (arg1, ignore)
math_floor (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (float) floor (arg1);
  return (float) floor (arg1);
}
}
 
 
static float
static float
math_fmod (arg1, arg2)
math_fmod (arg1, arg2)
     float arg1;
     float arg1;
     float arg2;
     float arg2;
{
{
  return (int) arg1 % (int) arg2;
  return (int) arg1 % (int) arg2;
}
}
 
 
static float
static float
math_int (arg1, ignore)
math_int (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return ((float) ((int) arg1)) == arg1;
  return ((float) ((int) arg1)) == arg1;
}
}
 
 
static float
static float
math_round (arg1, ignore)
math_round (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return arg1 > 0 ? (int) (arg1 + 0.5) : (int) (arg1 - 0.5);
  return arg1 > 0 ? (int) (arg1 + 0.5) : (int) (arg1 - 0.5);
}
}
 
 
static float
static float
math_sgn (arg1, ignore)
math_sgn (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (arg1 < 0) ? -1 : (arg1 ? 1 : 0);
  return (arg1 < 0) ? -1 : (arg1 ? 1 : 0);
}
}
 
 
static float
static float
math_trunc (arg1, ignore)
math_trunc (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (int) arg1;
  return (int) arg1;
}
}
 
 
static float
static float
math_acos (arg1, ignore)
math_acos (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (float) acos (arg1);
  return (float) acos (arg1);
}
}
 
 
static float
static float
math_asin (arg1, ignore)
math_asin (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (float) asin (arg1);
  return (float) asin (arg1);
}
}
 
 
static float
static float
math_atan (arg1, ignore)
math_atan (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (float) atan (arg1);
  return (float) atan (arg1);
}
}
 
 
static float
static float
math_atan2 (arg1, arg2)
math_atan2 (arg1, arg2)
     float arg1;
     float arg1;
     float arg2;
     float arg2;
{
{
  return (float) atan2 (arg1, arg2);
  return (float) atan2 (arg1, arg2);
}
}
 
 
static float
static float
math_cosh (arg1, ignore)
math_cosh (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (float) cosh (arg1);
  return (float) cosh (arg1);
}
}
 
 
static float
static float
math_cos (arg1, ignore)
math_cos (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (float) cos (arg1);
  return (float) cos (arg1);
}
}
 
 
static float
static float
math_cvf (arg1, ignore)
math_cvf (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (float) arg1;
  return (float) arg1;
}
}
 
 
static float
static float
math_exp (arg1, ignore)
math_exp (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (float) exp (arg1);
  return (float) exp (arg1);
}
}
 
 
static float
static float
math_fabs (arg1, ignore)
math_fabs (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (float) fabs (arg1);
  return (float) fabs (arg1);
}
}
 
 
/* expr1 * 2^expr2.  */
/* expr1 * 2^expr2.  */
 
 
static float
static float
math_ldexp (arg1, arg2)
math_ldexp (arg1, arg2)
     float arg1;
     float arg1;
     float arg2;
     float arg2;
{
{
  return arg1 * (float) pow (2.0, arg2);
  return arg1 * (float) pow (2.0, arg2);
}
}
 
 
static float
static float
math_log10 (arg1, ignore)
math_log10 (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (float) log10 (arg1);
  return (float) log10 (arg1);
}
}
 
 
static float
static float
math_log (arg1, ignore)
math_log (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (float) log (arg1);
  return (float) log (arg1);
}
}
 
 
static float
static float
math_max (arg1, arg2)
math_max (arg1, arg2)
     float arg1;
     float arg1;
     float arg2;
     float arg2;
{
{
  return (arg1 > arg2) ? arg1 : arg2;
  return (arg1 > arg2) ? arg1 : arg2;
}
}
 
 
static float
static float
math_min (arg1, arg2)
math_min (arg1, arg2)
     float arg1;
     float arg1;
     float arg2;
     float arg2;
{
{
  return (arg1 < arg2) ? arg1 : arg2;
  return (arg1 < arg2) ? arg1 : arg2;
}
}
 
 
static float
static float
math_pow (arg1, arg2)
math_pow (arg1, arg2)
     float arg1;
     float arg1;
     float arg2;
     float arg2;
{
{
  return (float) pow (arg1, arg2);
  return (float) pow (arg1, arg2);
}
}
 
 
static float
static float
math_sin (arg1, ignore)
math_sin (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (float) sin (arg1);
  return (float) sin (arg1);
}
}
 
 
static float
static float
math_sinh (arg1, ignore)
math_sinh (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (float) sinh (arg1);
  return (float) sinh (arg1);
}
}
 
 
static float
static float
math_sqrt (arg1, ignore)
math_sqrt (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (float) sqrt (arg1);
  return (float) sqrt (arg1);
}
}
 
 
static float
static float
math_tan (arg1, ignore)
math_tan (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (float) tan (arg1);
  return (float) tan (arg1);
}
}
 
 
static float
static float
math_tanh (arg1, ignore)
math_tanh (arg1, ignore)
     float arg1;
     float arg1;
     float ignore ATTRIBUTE_UNUSED;
     float ignore ATTRIBUTE_UNUSED;
{
{
  return (float) tanh (arg1);
  return (float) tanh (arg1);
}
}
 
 
/* Built-in substitution symbol functions and math functions.  */
/* Built-in substitution symbol functions and math functions.  */
typedef struct
typedef struct
{
{
  char *name;
  char *name;
  int (*proc) PARAMS ((char *, char *));
  int (*proc) PARAMS ((char *, char *));
  int nargs;
  int nargs;
} subsym_proc_entry;
} subsym_proc_entry;
 
 
static const subsym_proc_entry subsym_procs[] =
static const subsym_proc_entry subsym_procs[] =
{
{
  /* Assembler built-in string substitution functions.  */
  /* Assembler built-in string substitution functions.  */
  { "$symlen", subsym_symlen, 1,  },
  { "$symlen", subsym_symlen, 1,  },
  { "$symcmp", subsym_symcmp, 2,  },
  { "$symcmp", subsym_symcmp, 2,  },
  { "$firstch", subsym_firstch, 2,  },
  { "$firstch", subsym_firstch, 2,  },
  { "$lastch", subsym_lastch, 2,  },
  { "$lastch", subsym_lastch, 2,  },
  { "$isdefed", subsym_isdefed, 1,  },
  { "$isdefed", subsym_isdefed, 1,  },
  { "$ismember", subsym_ismember, 2,  },
  { "$ismember", subsym_ismember, 2,  },
  { "$iscons", subsym_iscons, 1,  },
  { "$iscons", subsym_iscons, 1,  },
  { "$isname", subsym_isname, 1,  },
  { "$isname", subsym_isname, 1,  },
  { "$isreg", subsym_isreg, 1,  },
  { "$isreg", subsym_isreg, 1,  },
  { "$structsz", subsym_structsz, 1,  },
  { "$structsz", subsym_structsz, 1,  },
  { "$structacc", subsym_structacc, 1,  },
  { "$structacc", subsym_structacc, 1,  },
  { NULL, NULL, 0 },
  { NULL, NULL, 0 },
};
};
 
 
typedef struct
typedef struct
{
{
  char *name;
  char *name;
  float (*proc) PARAMS ((float, float));
  float (*proc) PARAMS ((float, float));
  int nargs;
  int nargs;
  int int_return;
  int int_return;
} math_proc_entry;
} math_proc_entry;
 
 
static const math_proc_entry math_procs[] =
static const math_proc_entry math_procs[] =
{
{
  /* Integer-returning built-in math functions.  */
  /* Integer-returning built-in math functions.  */
  { "$cvi", math_cvi, 1, 1 },
  { "$cvi", math_cvi, 1, 1 },
  { "$int", math_int, 1, 1 },
  { "$int", math_int, 1, 1 },
  { "$sgn", math_sgn, 1, 1 },
  { "$sgn", math_sgn, 1, 1 },
 
 
  /* Float-returning built-in math functions.  */
  /* Float-returning built-in math functions.  */
  { "$acos", math_acos, 1, 0 },
  { "$acos", math_acos, 1, 0 },
  { "$asin", math_asin, 1, 0 },
  { "$asin", math_asin, 1, 0 },
  { "$atan", math_atan, 1, 0 },
  { "$atan", math_atan, 1, 0 },
  { "$atan2", math_atan2, 2, 0 },
  { "$atan2", math_atan2, 2, 0 },
  { "$ceil", math_ceil, 1, 0 },
  { "$ceil", math_ceil, 1, 0 },
  { "$cosh", math_cosh, 1, 0 },
  { "$cosh", math_cosh, 1, 0 },
  { "$cos", math_cos, 1, 0 },
  { "$cos", math_cos, 1, 0 },
  { "$cvf", math_cvf, 1, 0 },
  { "$cvf", math_cvf, 1, 0 },
  { "$exp", math_exp, 1, 0 },
  { "$exp", math_exp, 1, 0 },
  { "$fabs", math_fabs, 1, 0 },
  { "$fabs", math_fabs, 1, 0 },
  { "$floor", math_floor, 1, 0 },
  { "$floor", math_floor, 1, 0 },
  { "$fmod", math_fmod, 2, 0 },
  { "$fmod", math_fmod, 2, 0 },
  { "$ldexp", math_ldexp, 2, 0 },
  { "$ldexp", math_ldexp, 2, 0 },
  { "$log10", math_log10, 1, 0 },
  { "$log10", math_log10, 1, 0 },
  { "$log", math_log, 1, 0 },
  { "$log", math_log, 1, 0 },
  { "$max", math_max, 2, 0 },
  { "$max", math_max, 2, 0 },
  { "$min", math_min, 2, 0 },
  { "$min", math_min, 2, 0 },
  { "$pow", math_pow, 2, 0 },
  { "$pow", math_pow, 2, 0 },
  { "$round", math_round, 1, 0 },
  { "$round", math_round, 1, 0 },
  { "$sin", math_sin, 1, 0 },
  { "$sin", math_sin, 1, 0 },
  { "$sinh", math_sinh, 1, 0 },
  { "$sinh", math_sinh, 1, 0 },
  { "$sqrt", math_sqrt, 1, 0 },
  { "$sqrt", math_sqrt, 1, 0 },
  { "$tan", math_tan, 1, 0 },
  { "$tan", math_tan, 1, 0 },
  { "$tanh", math_tanh, 1, 0 },
  { "$tanh", math_tanh, 1, 0 },
  { "$trunc", math_trunc, 1, 0 },
  { "$trunc", math_trunc, 1, 0 },
  { NULL, NULL, 0, 0 },
  { NULL, NULL, 0, 0 },
};
};
 
 
void
void
md_begin ()
md_begin ()
{
{
  template *tm;
  template *tm;
  symbol *sym;
  symbol *sym;
  const subsym_proc_entry *subsym_proc;
  const subsym_proc_entry *subsym_proc;
  const math_proc_entry *math_proc;
  const math_proc_entry *math_proc;
  const char *hash_err;
  const char *hash_err;
  char **symname;
  char **symname;
  char *TIC54X_DIR = getenv ("TIC54X_DIR");
  char *TIC54X_DIR = getenv ("TIC54X_DIR");
  char *A_DIR = TIC54X_DIR ? TIC54X_DIR : getenv ("A_DIR");
  char *A_DIR = TIC54X_DIR ? TIC54X_DIR : getenv ("A_DIR");
 
 
  local_label_id = 0;
  local_label_id = 0;
 
 
  /* Look for A_DIR and add it to the include list.  */
  /* Look for A_DIR and add it to the include list.  */
  if (A_DIR != NULL)
  if (A_DIR != NULL)
    {
    {
      char *tmp = xstrdup (A_DIR);
      char *tmp = xstrdup (A_DIR);
 
 
      do
      do
        {
        {
          char *next = strchr (tmp, ';');
          char *next = strchr (tmp, ';');
 
 
          if (next)
          if (next)
            *next++ = '\0';
            *next++ = '\0';
          add_include_dir (tmp);
          add_include_dir (tmp);
          tmp = next;
          tmp = next;
        }
        }
      while (tmp != NULL);
      while (tmp != NULL);
    }
    }
 
 
  op_hash = hash_new ();
  op_hash = hash_new ();
  for (tm = (template *) tic54x_optab; tm->name; tm++)
  for (tm = (template *) tic54x_optab; tm->name; tm++)
    {
    {
      if (hash_find (op_hash, tm->name))
      if (hash_find (op_hash, tm->name))
        continue;
        continue;
      hash_err = hash_insert (op_hash, tm->name, (char *) tm);
      hash_err = hash_insert (op_hash, tm->name, (char *) tm);
      if (hash_err)
      if (hash_err)
        as_fatal ("Internal Error: Can't hash %s: %s",
        as_fatal ("Internal Error: Can't hash %s: %s",
                  tm->name, hash_err);
                  tm->name, hash_err);
    }
    }
  parop_hash = hash_new ();
  parop_hash = hash_new ();
  for (tm = (template *) tic54x_paroptab; tm->name; tm++)
  for (tm = (template *) tic54x_paroptab; tm->name; tm++)
    {
    {
      if (hash_find (parop_hash, tm->name))
      if (hash_find (parop_hash, tm->name))
        continue;
        continue;
      hash_err = hash_insert (parop_hash, tm->name, (char *) tm);
      hash_err = hash_insert (parop_hash, tm->name, (char *) tm);
      if (hash_err)
      if (hash_err)
        as_fatal ("Internal Error: Can't hash %s: %s",
        as_fatal ("Internal Error: Can't hash %s: %s",
                  tm->name, hash_err);
                  tm->name, hash_err);
    }
    }
  reg_hash = hash_new ();
  reg_hash = hash_new ();
  for (sym = (symbol *) regs; sym->name; sym++)
  for (sym = (symbol *) regs; sym->name; sym++)
    {
    {
      /* Add basic registers to the symbol table.  */
      /* Add basic registers to the symbol table.  */
      symbolS *symbolP = symbol_new (sym->name, absolute_section,
      symbolS *symbolP = symbol_new (sym->name, absolute_section,
                                     (valueT) sym->value, &zero_address_frag);
                                     (valueT) sym->value, &zero_address_frag);
      SF_SET_LOCAL (symbolP);
      SF_SET_LOCAL (symbolP);
      symbol_table_insert (symbolP);
      symbol_table_insert (symbolP);
      hash_err = hash_insert (reg_hash, sym->name, (char *) sym);
      hash_err = hash_insert (reg_hash, sym->name, (char *) sym);
    }
    }
  for (sym = (symbol *) mmregs; sym->name; sym++)
  for (sym = (symbol *) mmregs; sym->name; sym++)
    hash_err = hash_insert (reg_hash, sym->name, (char *) sym);
    hash_err = hash_insert (reg_hash, sym->name, (char *) sym);
  mmreg_hash = hash_new ();
  mmreg_hash = hash_new ();
  for (sym = (symbol *) mmregs; sym->name; sym++)
  for (sym = (symbol *) mmregs; sym->name; sym++)
    hash_err = hash_insert (mmreg_hash, sym->name, (char *) sym);
    hash_err = hash_insert (mmreg_hash, sym->name, (char *) sym);
 
 
  cc_hash = hash_new ();
  cc_hash = hash_new ();
  for (sym = (symbol *) condition_codes; sym->name; sym++)
  for (sym = (symbol *) condition_codes; sym->name; sym++)
    hash_err = hash_insert (cc_hash, sym->name, (char *) sym);
    hash_err = hash_insert (cc_hash, sym->name, (char *) sym);
 
 
  cc2_hash = hash_new ();
  cc2_hash = hash_new ();
  for (sym = (symbol *) cc2_codes; sym->name; sym++)
  for (sym = (symbol *) cc2_codes; sym->name; sym++)
    hash_err = hash_insert (cc2_hash, sym->name, (char *) sym);
    hash_err = hash_insert (cc2_hash, sym->name, (char *) sym);
 
 
  cc3_hash = hash_new ();
  cc3_hash = hash_new ();
  for (sym = (symbol *) cc3_codes; sym->name; sym++)
  for (sym = (symbol *) cc3_codes; sym->name; sym++)
    hash_err = hash_insert (cc3_hash, sym->name, (char *) sym);
    hash_err = hash_insert (cc3_hash, sym->name, (char *) sym);
 
 
  sbit_hash = hash_new ();
  sbit_hash = hash_new ();
  for (sym = (symbol *) status_bits; sym->name; sym++)
  for (sym = (symbol *) status_bits; sym->name; sym++)
    hash_err = hash_insert (sbit_hash, sym->name, (char *) sym);
    hash_err = hash_insert (sbit_hash, sym->name, (char *) sym);
 
 
  misc_symbol_hash = hash_new ();
  misc_symbol_hash = hash_new ();
  for (symname = (char **) misc_symbols; *symname; symname++)
  for (symname = (char **) misc_symbols; *symname; symname++)
    hash_err = hash_insert (misc_symbol_hash, *symname, *symname);
    hash_err = hash_insert (misc_symbol_hash, *symname, *symname);
 
 
  /* Only the base substitution table and local label table are initialized;
  /* Only the base substitution table and local label table are initialized;
     the others (for local macro substitution) get instantiated as needed.  */
     the others (for local macro substitution) get instantiated as needed.  */
  local_label_hash[0] = hash_new ();
  local_label_hash[0] = hash_new ();
  subsym_hash[0] = hash_new ();
  subsym_hash[0] = hash_new ();
  for (subsym_proc = subsym_procs; subsym_proc->name; subsym_proc++)
  for (subsym_proc = subsym_procs; subsym_proc->name; subsym_proc++)
    hash_err = hash_insert (subsym_hash[0], subsym_proc->name,
    hash_err = hash_insert (subsym_hash[0], subsym_proc->name,
                            (char *) subsym_proc);
                            (char *) subsym_proc);
 
 
  math_hash = hash_new ();
  math_hash = hash_new ();
  for (math_proc = math_procs; math_proc->name; math_proc++)
  for (math_proc = math_procs; math_proc->name; math_proc++)
    {
    {
      /* Insert into the main subsym hash for recognition; insert into
      /* Insert into the main subsym hash for recognition; insert into
         the math hash to actually store information.  */
         the math hash to actually store information.  */
      hash_err = hash_insert (subsym_hash[0], math_proc->name,
      hash_err = hash_insert (subsym_hash[0], math_proc->name,
                              (char *) math_proc);
                              (char *) math_proc);
      hash_err = hash_insert (math_hash, math_proc->name,
      hash_err = hash_insert (math_hash, math_proc->name,
                              (char *) math_proc);
                              (char *) math_proc);
    }
    }
  subsym_recurse_hash = hash_new ();
  subsym_recurse_hash = hash_new ();
  stag_hash = hash_new ();
  stag_hash = hash_new ();
}
}
 
 
static int
static int
is_accumulator (operand)
is_accumulator (operand)
     struct opstruct *operand;
     struct opstruct *operand;
{
{
  return strcasecmp (operand->buf, "a") == 0
  return strcasecmp (operand->buf, "a") == 0
    || strcasecmp (operand->buf, "b") == 0;
    || strcasecmp (operand->buf, "b") == 0;
}
}
 
 
/* Return the number of operands found, or -1 on error, copying the
/* Return the number of operands found, or -1 on error, copying the
   operands into the given array and the accompanying expressions into
   operands into the given array and the accompanying expressions into
   the next array.  */
   the next array.  */
 
 
static int
static int
get_operands (operands, line)
get_operands (operands, line)
     struct opstruct operands[];
     struct opstruct operands[];
     char *line;
     char *line;
{
{
  char *lptr = line;
  char *lptr = line;
  int numexp = 0;
  int numexp = 0;
  int expecting_operand = 0;
  int expecting_operand = 0;
  int i;
  int i;
 
 
  while (numexp < MAX_OPERANDS && !is_end_of_line[(int) *lptr])
  while (numexp < MAX_OPERANDS && !is_end_of_line[(int) *lptr])
    {
    {
      int paren_not_balanced = 0;
      int paren_not_balanced = 0;
      char *op_start, *op_end;
      char *op_start, *op_end;
 
 
      while (*lptr && ISSPACE (*lptr))
      while (*lptr && ISSPACE (*lptr))
        ++lptr;
        ++lptr;
      op_start = lptr;
      op_start = lptr;
      while (paren_not_balanced || *lptr != ',')
      while (paren_not_balanced || *lptr != ',')
        {
        {
          if (*lptr == '\0')
          if (*lptr == '\0')
            {
            {
              if (paren_not_balanced)
              if (paren_not_balanced)
                {
                {
                  as_bad ("Unbalanced parenthesis in operand %d", numexp);
                  as_bad ("Unbalanced parenthesis in operand %d", numexp);
                  return -1;
                  return -1;
                }
                }
              else
              else
                break;
                break;
            }
            }
          if (*lptr == '(')
          if (*lptr == '(')
            ++paren_not_balanced;
            ++paren_not_balanced;
          else if (*lptr == ')')
          else if (*lptr == ')')
            --paren_not_balanced;
            --paren_not_balanced;
          ++lptr;
          ++lptr;
        }
        }
      op_end = lptr;
      op_end = lptr;
      if (op_end != op_start)
      if (op_end != op_start)
        {
        {
          int len = op_end - op_start;
          int len = op_end - op_start;
 
 
          strncpy (operands[numexp].buf, op_start, len);
          strncpy (operands[numexp].buf, op_start, len);
          operands[numexp].buf[len] = 0;
          operands[numexp].buf[len] = 0;
          /* Trim trailing spaces; while the preprocessor gets rid of most,
          /* Trim trailing spaces; while the preprocessor gets rid of most,
             there are weird usage patterns that can introduce them
             there are weird usage patterns that can introduce them
             (i.e. using strings for macro args).  */
             (i.e. using strings for macro args).  */
          while (len > 0 && ISSPACE (operands[numexp].buf[len - 1]))
          while (len > 0 && ISSPACE (operands[numexp].buf[len - 1]))
            operands[numexp].buf[--len] = 0;
            operands[numexp].buf[--len] = 0;
          lptr = op_end;
          lptr = op_end;
          ++numexp;
          ++numexp;
        }
        }
      else
      else
        {
        {
          if (expecting_operand || *lptr == ',')
          if (expecting_operand || *lptr == ',')
            {
            {
              as_bad ("Expecting operand after ','");
              as_bad ("Expecting operand after ','");
              return -1;
              return -1;
            }
            }
        }
        }
      if (*lptr == ',')
      if (*lptr == ',')
        {
        {
          if (*++lptr == '\0')
          if (*++lptr == '\0')
            {
            {
              as_bad ("Expecting operand after ','");
              as_bad ("Expecting operand after ','");
              return -1;
              return -1;
            }
            }
          expecting_operand = 1;
          expecting_operand = 1;
        }
        }
    }
    }
 
 
  while (*lptr && ISSPACE (*lptr++))
  while (*lptr && ISSPACE (*lptr++))
    ;
    ;
  if (!is_end_of_line[(int) *lptr])
  if (!is_end_of_line[(int) *lptr])
    {
    {
      as_bad ("Extra junk on line");
      as_bad ("Extra junk on line");
      return -1;
      return -1;
    }
    }
 
 
  /* OK, now parse them into expressions.  */
  /* OK, now parse them into expressions.  */
  for (i = 0; i < numexp; i++)
  for (i = 0; i < numexp; i++)
    {
    {
      memset (&operands[i].exp, 0, sizeof (operands[i].exp));
      memset (&operands[i].exp, 0, sizeof (operands[i].exp));
      if (operands[i].buf[0] == '#')
      if (operands[i].buf[0] == '#')
        {
        {
          /* Immediate.  */
          /* Immediate.  */
          parse_expression (operands[i].buf + 1, &operands[i].exp);
          parse_expression (operands[i].buf + 1, &operands[i].exp);
        }
        }
      else if (operands[i].buf[0] == '@')
      else if (operands[i].buf[0] == '@')
        {
        {
          /* Direct notation.  */
          /* Direct notation.  */
          parse_expression (operands[i].buf + 1, &operands[i].exp);
          parse_expression (operands[i].buf + 1, &operands[i].exp);
        }
        }
      else if (operands[i].buf[0] == '*')
      else if (operands[i].buf[0] == '*')
        {
        {
          /* Indirect.  */
          /* Indirect.  */
          char *paren = strchr (operands[i].buf, '(');
          char *paren = strchr (operands[i].buf, '(');
 
 
          /* Allow immediate syntax in the inner expression.  */
          /* Allow immediate syntax in the inner expression.  */
          if (paren && paren[1] == '#')
          if (paren && paren[1] == '#')
            *++paren = '(';
            *++paren = '(';
 
 
          /* Pull out the lk expression or SP offset, if present.  */
          /* Pull out the lk expression or SP offset, if present.  */
          if (paren != NULL)
          if (paren != NULL)
            {
            {
              int len = strlen (paren);
              int len = strlen (paren);
              char *end = paren + len;
              char *end = paren + len;
              int c;
              int c;
 
 
              while (end[-1] != ')')
              while (end[-1] != ')')
                if (--end <= paren)
                if (--end <= paren)
                  {
                  {
                    as_bad (_("Badly formed address expression"));
                    as_bad (_("Badly formed address expression"));
                    return -1;
                    return -1;
                  }
                  }
              c = *end;
              c = *end;
              *end = '\0';
              *end = '\0';
              parse_expression (paren, &operands[i].exp);
              parse_expression (paren, &operands[i].exp);
              *end = c;
              *end = c;
            }
            }
          else
          else
            operands[i].exp.X_op = O_absent;
            operands[i].exp.X_op = O_absent;
        }
        }
      else
      else
        parse_expression (operands[i].buf, &operands[i].exp);
        parse_expression (operands[i].buf, &operands[i].exp);
    }
    }
 
 
  return numexp;
  return numexp;
}
}
 
 
/* Predicates for different operand types.  */
/* Predicates for different operand types.  */
 
 
static int
static int
is_immediate (operand)
is_immediate (operand)
     struct opstruct *operand;
     struct opstruct *operand;
{
{
  return *operand->buf == '#';
  return *operand->buf == '#';
}
}
 
 
/* This is distinguished from immediate because some numbers must be constants
/* This is distinguished from immediate because some numbers must be constants
   and must *not* have the '#' prefix.  */
   and must *not* have the '#' prefix.  */
 
 
static int
static int
is_absolute (operand)
is_absolute (operand)
     struct opstruct *operand;
     struct opstruct *operand;
{
{
  return operand->exp.X_op == O_constant && !is_immediate (operand);
  return operand->exp.X_op == O_constant && !is_immediate (operand);
}
}
 
 
/* Is this an indirect operand?  */
/* Is this an indirect operand?  */
 
 
static int
static int
is_indirect (operand)
is_indirect (operand)
     struct opstruct *operand;
     struct opstruct *operand;
{
{
  return operand->buf[0] == '*';
  return operand->buf[0] == '*';
}
}
 
 
/* Is this a valid dual-memory operand?  */
/* Is this a valid dual-memory operand?  */
 
 
static int
static int
is_dual (operand)
is_dual (operand)
     struct opstruct *operand;
     struct opstruct *operand;
{
{
  if (is_indirect (operand) && strncasecmp (operand->buf, "*ar", 3) == 0)
  if (is_indirect (operand) && strncasecmp (operand->buf, "*ar", 3) == 0)
    {
    {
      char *tmp = operand->buf + 3;
      char *tmp = operand->buf + 3;
      int arf;
      int arf;
      int valid_mod;
      int valid_mod;
 
 
      arf = *tmp++ - '0';
      arf = *tmp++ - '0';
      /* Only allow *ARx, *ARx-, *ARx+, or *ARx+0%.  */
      /* Only allow *ARx, *ARx-, *ARx+, or *ARx+0%.  */
      valid_mod = *tmp == '\0' ||
      valid_mod = *tmp == '\0' ||
        strcasecmp (tmp, "-") == 0 ||
        strcasecmp (tmp, "-") == 0 ||
        strcasecmp (tmp, "+") == 0 ||
        strcasecmp (tmp, "+") == 0 ||
        strcasecmp (tmp, "+0%") == 0;
        strcasecmp (tmp, "+0%") == 0;
      return arf >= 2 && arf <= 5 && valid_mod;
      return arf >= 2 && arf <= 5 && valid_mod;
    }
    }
  return 0;
  return 0;
}
}
 
 
static int
static int
is_mmreg (operand)
is_mmreg (operand)
     struct opstruct *operand;
     struct opstruct *operand;
{
{
  return (is_absolute (operand)
  return (is_absolute (operand)
          || is_immediate (operand)
          || is_immediate (operand)
          || hash_find (mmreg_hash, operand->buf) != 0);
          || hash_find (mmreg_hash, operand->buf) != 0);
}
}
 
 
static int
static int
is_type (operand, type)
is_type (operand, type)
     struct opstruct *operand;
     struct opstruct *operand;
     enum optype type;
     enum optype type;
{
{
  switch (type)
  switch (type)
    {
    {
    case OP_None:
    case OP_None:
      return operand->buf[0] == 0;
      return operand->buf[0] == 0;
    case OP_Xmem:
    case OP_Xmem:
    case OP_Ymem:
    case OP_Ymem:
      return is_dual (operand);
      return is_dual (operand);
    case OP_Sind:
    case OP_Sind:
      return is_indirect (operand);
      return is_indirect (operand);
    case OP_xpmad_ms7:
    case OP_xpmad_ms7:
      /* This one *must* be immediate.  */
      /* This one *must* be immediate.  */
      return is_immediate (operand);
      return is_immediate (operand);
    case OP_xpmad:
    case OP_xpmad:
    case OP_pmad:
    case OP_pmad:
    case OP_PA:
    case OP_PA:
    case OP_dmad:
    case OP_dmad:
    case OP_Lmem:
    case OP_Lmem:
    case OP_MMR:
    case OP_MMR:
      return 1;
      return 1;
    case OP_Smem:
    case OP_Smem:
      /* Address may be a numeric, indirect, or an expression.  */
      /* Address may be a numeric, indirect, or an expression.  */
      return !is_immediate (operand);
      return !is_immediate (operand);
    case OP_MMRY:
    case OP_MMRY:
    case OP_MMRX:
    case OP_MMRX:
      return is_mmreg (operand);
      return is_mmreg (operand);
    case OP_SRC:
    case OP_SRC:
    case OP_SRC1:
    case OP_SRC1:
    case OP_RND:
    case OP_RND:
    case OP_DST:
    case OP_DST:
      return is_accumulator (operand);
      return is_accumulator (operand);
    case OP_B:
    case OP_B:
      return is_accumulator (operand) && TOUPPER (operand->buf[0]) == 'B';
      return is_accumulator (operand) && TOUPPER (operand->buf[0]) == 'B';
    case OP_A:
    case OP_A:
      return is_accumulator (operand) && TOUPPER (operand->buf[0]) == 'A';
      return is_accumulator (operand) && TOUPPER (operand->buf[0]) == 'A';
    case OP_ARX:
    case OP_ARX:
      return strncasecmp ("ar", operand->buf, 2) == 0
      return strncasecmp ("ar", operand->buf, 2) == 0
        && ISDIGIT (operand->buf[2]);
        && ISDIGIT (operand->buf[2]);
    case OP_SBIT:
    case OP_SBIT:
      return hash_find (sbit_hash, operand->buf) != 0 || is_absolute (operand);
      return hash_find (sbit_hash, operand->buf) != 0 || is_absolute (operand);
    case OP_CC:
    case OP_CC:
      return hash_find (cc_hash, operand->buf) != 0;
      return hash_find (cc_hash, operand->buf) != 0;
    case OP_CC2:
    case OP_CC2:
      return hash_find (cc2_hash, operand->buf) != 0;
      return hash_find (cc2_hash, operand->buf) != 0;
    case OP_CC3:
    case OP_CC3:
      return hash_find (cc3_hash, operand->buf) != 0
      return hash_find (cc3_hash, operand->buf) != 0
        || is_immediate (operand) || is_absolute (operand);
        || is_immediate (operand) || is_absolute (operand);
    case OP_16:
    case OP_16:
      return (is_immediate (operand) || is_absolute (operand))
      return (is_immediate (operand) || is_absolute (operand))
        && operand->exp.X_add_number == 16;
        && operand->exp.X_add_number == 16;
    case OP_N:
    case OP_N:
      /* Allow st0 or st1 instead of a numeric.  */
      /* Allow st0 or st1 instead of a numeric.  */
      return is_absolute (operand) || is_immediate (operand) ||
      return is_absolute (operand) || is_immediate (operand) ||
        strcasecmp ("st0", operand->buf) == 0 ||
        strcasecmp ("st0", operand->buf) == 0 ||
        strcasecmp ("st1", operand->buf) == 0;
        strcasecmp ("st1", operand->buf) == 0;
    case OP_12:
    case OP_12:
    case OP_123:
    case OP_123:
      return is_absolute (operand) || is_immediate (operand);
      return is_absolute (operand) || is_immediate (operand);
    case OP_SHFT:
    case OP_SHFT:
      return (is_immediate (operand) || is_absolute (operand))
      return (is_immediate (operand) || is_absolute (operand))
        && operand->exp.X_add_number >= 0 && operand->exp.X_add_number < 16;
        && operand->exp.X_add_number >= 0 && operand->exp.X_add_number < 16;
    case OP_SHIFT:
    case OP_SHIFT:
      /* Let this one catch out-of-range values.  */
      /* Let this one catch out-of-range values.  */
      return (is_immediate (operand) || is_absolute (operand))
      return (is_immediate (operand) || is_absolute (operand))
        && operand->exp.X_add_number != 16;
        && operand->exp.X_add_number != 16;
    case OP_BITC:
    case OP_BITC:
    case OP_031:
    case OP_031:
    case OP_k8:
    case OP_k8:
      return is_absolute (operand) || is_immediate (operand);
      return is_absolute (operand) || is_immediate (operand);
    case OP_k8u:
    case OP_k8u:
      return is_immediate (operand)
      return is_immediate (operand)
        && operand->exp.X_op == O_constant
        && operand->exp.X_op == O_constant
        && operand->exp.X_add_number >= 0
        && operand->exp.X_add_number >= 0
        && operand->exp.X_add_number < 256;
        && operand->exp.X_add_number < 256;
    case OP_lk:
    case OP_lk:
    case OP_lku:
    case OP_lku:
      /* Allow anything; assumes opcodes are ordered with Smem operands
      /* Allow anything; assumes opcodes are ordered with Smem operands
         versions first.  */
         versions first.  */
      return 1;
      return 1;
    case OP_k5:
    case OP_k5:
    case OP_k3:
    case OP_k3:
    case OP_k9:
    case OP_k9:
      /* Just make sure it's an integer; check range later.  */
      /* Just make sure it's an integer; check range later.  */
      return is_immediate (operand);
      return is_immediate (operand);
    case OP_T:
    case OP_T:
      return strcasecmp ("t", operand->buf) == 0 ||
      return strcasecmp ("t", operand->buf) == 0 ||
        strcasecmp ("treg", operand->buf) == 0;
        strcasecmp ("treg", operand->buf) == 0;
    case OP_TS:
    case OP_TS:
      return strcasecmp ("ts", operand->buf) == 0;
      return strcasecmp ("ts", operand->buf) == 0;
    case OP_ASM:
    case OP_ASM:
      return strcasecmp ("asm", operand->buf) == 0;
      return strcasecmp ("asm", operand->buf) == 0;
    case OP_TRN:
    case OP_TRN:
      return strcasecmp ("trn", operand->buf) == 0;
      return strcasecmp ("trn", operand->buf) == 0;
    case OP_DP:
    case OP_DP:
      return strcasecmp ("dp", operand->buf) == 0;
      return strcasecmp ("dp", operand->buf) == 0;
    case OP_ARP:
    case OP_ARP:
      return strcasecmp ("arp", operand->buf) == 0;
      return strcasecmp ("arp", operand->buf) == 0;
    default:
    default:
      return 0;
      return 0;
    }
    }
}
}
 
 
static int
static int
operands_match (insn, operands, opcount, refoptype, minops, maxops)
operands_match (insn, operands, opcount, refoptype, minops, maxops)
     tic54x_insn *insn;
     tic54x_insn *insn;
     struct opstruct *operands;
     struct opstruct *operands;
     int opcount;
     int opcount;
     const enum optype *refoptype;
     const enum optype *refoptype;
     int minops;
     int minops;
     int maxops;
     int maxops;
{
{
  int op = 0, refop = 0;
  int op = 0, refop = 0;
 
 
  if (opcount == 0 && minops == 0)
  if (opcount == 0 && minops == 0)
    return 1;
    return 1;
 
 
  while (op <= maxops && refop <= maxops)
  while (op <= maxops && refop <= maxops)
    {
    {
      while (!is_type (&operands[op], OPTYPE (refoptype[refop])))
      while (!is_type (&operands[op], OPTYPE (refoptype[refop])))
        {
        {
          /* Skip an optional template operand if it doesn't agree
          /* Skip an optional template operand if it doesn't agree
             with the current operand.  */
             with the current operand.  */
          if (refoptype[refop] & OPT)
          if (refoptype[refop] & OPT)
            {
            {
              ++refop;
              ++refop;
              --maxops;
              --maxops;
              if (refop > maxops)
              if (refop > maxops)
                return 0;
                return 0;
            }
            }
          else
          else
            return 0;
            return 0;
        }
        }
 
 
      /* Save the actual operand type for later use.  */
      /* Save the actual operand type for later use.  */
      operands[op].type = OPTYPE (refoptype[refop]);
      operands[op].type = OPTYPE (refoptype[refop]);
      ++refop;
      ++refop;
      ++op;
      ++op;
      /* Have we matched them all yet?  */
      /* Have we matched them all yet?  */
      if (op == opcount)
      if (op == opcount)
        {
        {
          while (op < maxops)
          while (op < maxops)
            {
            {
              /* If a later operand is *not* optional, no match.  */
              /* If a later operand is *not* optional, no match.  */
              if ((refoptype[refop] & OPT) == 0)
              if ((refoptype[refop] & OPT) == 0)
                return 0;
                return 0;
              /* Flag any implicit default OP_DST operands so we know to add
              /* Flag any implicit default OP_DST operands so we know to add
                 them explicitly when encoding the operand later.  */
                 them explicitly when encoding the operand later.  */
              if (OPTYPE (refoptype[refop]) == OP_DST)
              if (OPTYPE (refoptype[refop]) == OP_DST)
                insn->using_default_dst = 1;
                insn->using_default_dst = 1;
              ++refop;
              ++refop;
              ++op;
              ++op;
            }
            }
 
 
          return 1;
          return 1;
        }
        }
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
/* 16-bit direct memory address
/* 16-bit direct memory address
   Explicit dmad operands are always in last word of insn (usually second
   Explicit dmad operands are always in last word of insn (usually second
   word, but bumped to third if lk addressing is used)
   word, but bumped to third if lk addressing is used)
 
 
   We allow *(dmad) notation because the TI assembler allows it.
   We allow *(dmad) notation because the TI assembler allows it.
 
 
   XPC_CODE:
   XPC_CODE:
   0 for 16-bit addresses
   0 for 16-bit addresses
   1 for full 23-bit addresses
   1 for full 23-bit addresses
   2 for the upper 7 bits of a 23-bit address (LDX).  */
   2 for the upper 7 bits of a 23-bit address (LDX).  */
 
 
static int
static int
encode_dmad (insn, operand, xpc_code)
encode_dmad (insn, operand, xpc_code)
     tic54x_insn *insn;
     tic54x_insn *insn;
     struct opstruct *operand;
     struct opstruct *operand;
     int xpc_code;
     int xpc_code;
{
{
  int op = 1 + insn->is_lkaddr;
  int op = 1 + insn->is_lkaddr;
 
 
  /* Only allow *(dmad) expressions; all others are invalid.  */
  /* Only allow *(dmad) expressions; all others are invalid.  */
  if (is_indirect (operand) && operand->buf[strlen (operand->buf) - 1] != ')')
  if (is_indirect (operand) && operand->buf[strlen (operand->buf) - 1] != ')')
    {
    {
      as_bad (_("Invalid dmad syntax '%s'"), operand->buf);
      as_bad (_("Invalid dmad syntax '%s'"), operand->buf);
      return 0;
      return 0;
    }
    }
 
 
  insn->opcode[op].addr_expr = operand->exp;
  insn->opcode[op].addr_expr = operand->exp;
 
 
  if (insn->opcode[op].addr_expr.X_op == O_constant)
  if (insn->opcode[op].addr_expr.X_op == O_constant)
    {
    {
      valueT value = insn->opcode[op].addr_expr.X_add_number;
      valueT value = insn->opcode[op].addr_expr.X_add_number;
 
 
      if (xpc_code == 1)
      if (xpc_code == 1)
        {
        {
          insn->opcode[0].word &= 0xFF80;
          insn->opcode[0].word &= 0xFF80;
          insn->opcode[0].word |= (value >> 16) & 0x7F;
          insn->opcode[0].word |= (value >> 16) & 0x7F;
          insn->opcode[1].word = value & 0xFFFF;
          insn->opcode[1].word = value & 0xFFFF;
        }
        }
      else if (xpc_code == 2)
      else if (xpc_code == 2)
        insn->opcode[op].word = (value >> 16) & 0xFFFF;
        insn->opcode[op].word = (value >> 16) & 0xFFFF;
      else
      else
        insn->opcode[op].word = value;
        insn->opcode[op].word = value;
    }
    }
  else
  else
    {
    {
      /* Do the fixup later; just store the expression.  */
      /* Do the fixup later; just store the expression.  */
      insn->opcode[op].word = 0;
      insn->opcode[op].word = 0;
      insn->opcode[op].r_nchars = 2;
      insn->opcode[op].r_nchars = 2;
 
 
      if (amode == c_mode)
      if (amode == c_mode)
        insn->opcode[op].r_type = BFD_RELOC_TIC54X_16_OF_23;
        insn->opcode[op].r_type = BFD_RELOC_TIC54X_16_OF_23;
      else if (xpc_code == 1)
      else if (xpc_code == 1)
        {
        {
          /* This relocation spans two words, so adjust accordingly.  */
          /* This relocation spans two words, so adjust accordingly.  */
          insn->opcode[0].addr_expr = operand->exp;
          insn->opcode[0].addr_expr = operand->exp;
          insn->opcode[0].r_type = BFD_RELOC_TIC54X_23;
          insn->opcode[0].r_type = BFD_RELOC_TIC54X_23;
          insn->opcode[0].r_nchars = 4;
          insn->opcode[0].r_nchars = 4;
          insn->opcode[0].unresolved = 1;
          insn->opcode[0].unresolved = 1;
          /* It's really 2 words, but we want to stop encoding after the
          /* It's really 2 words, but we want to stop encoding after the
             first, since we must encode both words at once.  */
             first, since we must encode both words at once.  */
          insn->words = 1;
          insn->words = 1;
        }
        }
      else if (xpc_code == 2)
      else if (xpc_code == 2)
        insn->opcode[op].r_type = BFD_RELOC_TIC54X_MS7_OF_23;
        insn->opcode[op].r_type = BFD_RELOC_TIC54X_MS7_OF_23;
      else
      else
        insn->opcode[op].r_type = BFD_RELOC_TIC54X_16_OF_23;
        insn->opcode[op].r_type = BFD_RELOC_TIC54X_16_OF_23;
 
 
      insn->opcode[op].unresolved = 1;
      insn->opcode[op].unresolved = 1;
    }
    }
 
 
  return 1;
  return 1;
}
}
 
 
/* 7-bit direct address encoding.  */
/* 7-bit direct address encoding.  */
 
 
static int
static int
encode_address (insn, operand)
encode_address (insn, operand)
     tic54x_insn *insn;
     tic54x_insn *insn;
     struct opstruct *operand;
     struct opstruct *operand;
{
{
  /* Assumes that dma addresses are *always* in word 0 of the opcode.  */
  /* Assumes that dma addresses are *always* in word 0 of the opcode.  */
  insn->opcode[0].addr_expr = operand->exp;
  insn->opcode[0].addr_expr = operand->exp;
 
 
  if (operand->exp.X_op == O_constant)
  if (operand->exp.X_op == O_constant)
    insn->opcode[0].word |= (operand->exp.X_add_number & 0x7F);
    insn->opcode[0].word |= (operand->exp.X_add_number & 0x7F);
  else
  else
    {
    {
      if (operand->exp.X_op == O_register)
      if (operand->exp.X_op == O_register)
        as_bad (_("Use the .mmregs directive to use memory-mapped register names such as '%s'"), operand->buf);
        as_bad (_("Use the .mmregs directive to use memory-mapped register names such as '%s'"), operand->buf);
      /* Do the fixup later; just store the expression.  */
      /* Do the fixup later; just store the expression.  */
      insn->opcode[0].r_nchars = 1;
      insn->opcode[0].r_nchars = 1;
      insn->opcode[0].r_type = BFD_RELOC_TIC54X_PARTLS7;
      insn->opcode[0].r_type = BFD_RELOC_TIC54X_PARTLS7;
      insn->opcode[0].unresolved = 1;
      insn->opcode[0].unresolved = 1;
    }
    }
 
 
  return 1;
  return 1;
}
}
 
 
static int
static int
encode_indirect (insn, operand)
encode_indirect (insn, operand)
     tic54x_insn *insn;
     tic54x_insn *insn;
     struct opstruct *operand;
     struct opstruct *operand;
{
{
  int arf;
  int arf;
  int mod;
  int mod;
 
 
  if (insn->is_lkaddr)
  if (insn->is_lkaddr)
    {
    {
      /* lk addresses always go in the second insn word.  */
      /* lk addresses always go in the second insn word.  */
      mod = ((TOUPPER (operand->buf[1]) == 'A') ? 12 :
      mod = ((TOUPPER (operand->buf[1]) == 'A') ? 12 :
             (operand->buf[1] == '(') ? 15 :
             (operand->buf[1] == '(') ? 15 :
             (strchr (operand->buf, '%') != NULL) ? 14 : 13);
             (strchr (operand->buf, '%') != NULL) ? 14 : 13);
      arf = ((mod == 12) ? operand->buf[3] - '0' :
      arf = ((mod == 12) ? operand->buf[3] - '0' :
             (mod == 15) ? 0 : operand->buf[4] - '0');
             (mod == 15) ? 0 : operand->buf[4] - '0');
 
 
      insn->opcode[1].addr_expr = operand->exp;
      insn->opcode[1].addr_expr = operand->exp;
 
 
      if (operand->exp.X_op == O_constant)
      if (operand->exp.X_op == O_constant)
        insn->opcode[1].word = operand->exp.X_add_number;
        insn->opcode[1].word = operand->exp.X_add_number;
      else
      else
        {
        {
          insn->opcode[1].word = 0;
          insn->opcode[1].word = 0;
          insn->opcode[1].r_nchars = 2;
          insn->opcode[1].r_nchars = 2;
          insn->opcode[1].r_type = BFD_RELOC_TIC54X_16_OF_23;
          insn->opcode[1].r_type = BFD_RELOC_TIC54X_16_OF_23;
          insn->opcode[1].unresolved = 1;
          insn->opcode[1].unresolved = 1;
        }
        }
    }
    }
  else if (strncasecmp (operand->buf, "*sp (", 4) == 0)
  else if (strncasecmp (operand->buf, "*sp (", 4) == 0)
    {
    {
      /* Stack offsets look the same as 7-bit direct addressing.  */
      /* Stack offsets look the same as 7-bit direct addressing.  */
      return encode_address (insn, operand);
      return encode_address (insn, operand);
    }
    }
  else
  else
    {
    {
      arf = (TOUPPER (operand->buf[1]) == 'A' ?
      arf = (TOUPPER (operand->buf[1]) == 'A' ?
             operand->buf[3] : operand->buf[4]) - '0';
             operand->buf[3] : operand->buf[4]) - '0';
 
 
      if (operand->buf[1] == '+')
      if (operand->buf[1] == '+')
        {
        {
          mod = 3;                  /* *+ARx  */
          mod = 3;                  /* *+ARx  */
          if (insn->tm->flags & FL_SMR)
          if (insn->tm->flags & FL_SMR)
            as_warn (_("Address mode *+ARx is write-only. "
            as_warn (_("Address mode *+ARx is write-only. "
                       "Results of reading are undefined."));
                       "Results of reading are undefined."));
        }
        }
      else if (operand->buf[4] == '\0')
      else if (operand->buf[4] == '\0')
        mod = 0;             /* *ARx  */
        mod = 0;             /* *ARx  */
      else if (operand->buf[5] == '\0')
      else if (operand->buf[5] == '\0')
        mod = (operand->buf[4] == '-' ? 1 : 2); /* *ARx+ / *ARx-  */
        mod = (operand->buf[4] == '-' ? 1 : 2); /* *ARx+ / *ARx-  */
      else if (operand->buf[6] == '\0')
      else if (operand->buf[6] == '\0')
        {
        {
          if (operand->buf[5] == '0')
          if (operand->buf[5] == '0')
            mod = (operand->buf[4] == '-' ? 5 : 6); /* *ARx+0 / *ARx-0  */
            mod = (operand->buf[4] == '-' ? 5 : 6); /* *ARx+0 / *ARx-0  */
          else
          else
            mod = (operand->buf[4] == '-' ? 8 : 10);/* *ARx+% / *ARx-%  */
            mod = (operand->buf[4] == '-' ? 8 : 10);/* *ARx+% / *ARx-%  */
        }
        }
      else if (TOUPPER (operand->buf[6]) == 'B')
      else if (TOUPPER (operand->buf[6]) == 'B')
        mod = (operand->buf[4] == '-' ? 4 : 7); /* ARx+0B / *ARx-0B  */
        mod = (operand->buf[4] == '-' ? 4 : 7); /* ARx+0B / *ARx-0B  */
      else if (TOUPPER (operand->buf[6]) == '%')
      else if (TOUPPER (operand->buf[6]) == '%')
        mod = (operand->buf[4] == '-' ? 9 : 11); /* ARx+0% / *ARx - 0%  */
        mod = (operand->buf[4] == '-' ? 9 : 11); /* ARx+0% / *ARx - 0%  */
      else
      else
        {
        {
          as_bad (_("Unrecognized indirect address format \"%s\""),
          as_bad (_("Unrecognized indirect address format \"%s\""),
                  operand->buf);
                  operand->buf);
          return 0;
          return 0;
        }
        }
    }
    }
 
 
  insn->opcode[0].word |= 0x80 | (mod << 3) | arf;
  insn->opcode[0].word |= 0x80 | (mod << 3) | arf;
 
 
  return 1;
  return 1;
}
}
 
 
static int
static int
encode_integer (insn, operand, which, min, max, mask)
encode_integer (insn, operand, which, min, max, mask)
     tic54x_insn *insn;
     tic54x_insn *insn;
     struct opstruct *operand;
     struct opstruct *operand;
     int which;
     int which;
     int min;
     int min;
     int max;
     int max;
     unsigned short mask;
     unsigned short mask;
{
{
  long parse, integer;
  long parse, integer;
 
 
  insn->opcode[which].addr_expr = operand->exp;
  insn->opcode[which].addr_expr = operand->exp;
 
 
  if (operand->exp.X_op == O_constant)
  if (operand->exp.X_op == O_constant)
    {
    {
      parse = operand->exp.X_add_number;
      parse = operand->exp.X_add_number;
      /* Hack -- fixup for 16-bit hex quantities that get converted positive
      /* Hack -- fixup for 16-bit hex quantities that get converted positive
         instead of negative.  */
         instead of negative.  */
      if ((parse & 0x8000) && min == -32768 && max == 32767)
      if ((parse & 0x8000) && min == -32768 && max == 32767)
        integer = (short) parse;
        integer = (short) parse;
      else
      else
        integer = parse;
        integer = parse;
 
 
      if (integer >= min && integer <= max)
      if (integer >= min && integer <= max)
        {
        {
          insn->opcode[which].word |= (integer & mask);
          insn->opcode[which].word |= (integer & mask);
          return 1;
          return 1;
        }
        }
      as_bad (_("Operand '%s' out of range (%d <= x <= %d)"),
      as_bad (_("Operand '%s' out of range (%d <= x <= %d)"),
              operand->buf, min, max);
              operand->buf, min, max);
    }
    }
  else
  else
    {
    {
      if (insn->opcode[which].addr_expr.X_op == O_constant)
      if (insn->opcode[which].addr_expr.X_op == O_constant)
        {
        {
          insn->opcode[which].word |=
          insn->opcode[which].word |=
            insn->opcode[which].addr_expr.X_add_number & mask;
            insn->opcode[which].addr_expr.X_add_number & mask;
        }
        }
      else
      else
        {
        {
          /* Do the fixup later; just store the expression.  */
          /* Do the fixup later; just store the expression.  */
          bfd_reloc_code_real_type rtype =
          bfd_reloc_code_real_type rtype =
            (mask == 0x1FF ? BFD_RELOC_TIC54X_PARTMS9 :
            (mask == 0x1FF ? BFD_RELOC_TIC54X_PARTMS9 :
             mask == 0xFFFF ? BFD_RELOC_TIC54X_16_OF_23 :
             mask == 0xFFFF ? BFD_RELOC_TIC54X_16_OF_23 :
             mask == 0x7F ? BFD_RELOC_TIC54X_PARTLS7 : BFD_RELOC_8);
             mask == 0x7F ? BFD_RELOC_TIC54X_PARTLS7 : BFD_RELOC_8);
          int size = (mask == 0x1FF || mask == 0xFFFF) ? 2 : 1;
          int size = (mask == 0x1FF || mask == 0xFFFF) ? 2 : 1;
 
 
          if (rtype == BFD_RELOC_8)
          if (rtype == BFD_RELOC_8)
            as_bad (_("Error in relocation handling"));
            as_bad (_("Error in relocation handling"));
 
 
          insn->opcode[which].r_nchars = size;
          insn->opcode[which].r_nchars = size;
          insn->opcode[which].r_type = rtype;
          insn->opcode[which].r_type = rtype;
          insn->opcode[which].unresolved = 1;
          insn->opcode[which].unresolved = 1;
        }
        }
 
 
      return 1;
      return 1;
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
static int
static int
encode_condition (insn, operand)
encode_condition (insn, operand)
     tic54x_insn *insn;
     tic54x_insn *insn;
     struct opstruct *operand;
     struct opstruct *operand;
{
{
  symbol *cc = (symbol *) hash_find (cc_hash, operand->buf);
  symbol *cc = (symbol *) hash_find (cc_hash, operand->buf);
  if (!cc)
  if (!cc)
    {
    {
      as_bad (_("Unrecognized condition code \"%s\""), operand->buf);
      as_bad (_("Unrecognized condition code \"%s\""), operand->buf);
      return 0;
      return 0;
    }
    }
#define CC_GROUP 0x40
#define CC_GROUP 0x40
#define CC_ACC   0x08
#define CC_ACC   0x08
#define CATG_A1  0x07
#define CATG_A1  0x07
#define CATG_B1  0x30
#define CATG_B1  0x30
#define CATG_A2  0x30
#define CATG_A2  0x30
#define CATG_B2  0x0C
#define CATG_B2  0x0C
#define CATG_C2  0x03
#define CATG_C2  0x03
  /* Disallow group 1 conditions mixed with group 2 conditions
  /* Disallow group 1 conditions mixed with group 2 conditions
     if group 1, allow only one category A and one category B
     if group 1, allow only one category A and one category B
     if group 2, allow only one each of category A, B, and C.  */
     if group 2, allow only one each of category A, B, and C.  */
  if (((insn->opcode[0].word & 0xFF) != 0))
  if (((insn->opcode[0].word & 0xFF) != 0))
    {
    {
      if ((insn->opcode[0].word & CC_GROUP) != (cc->value & CC_GROUP))
      if ((insn->opcode[0].word & CC_GROUP) != (cc->value & CC_GROUP))
        {
        {
          as_bad (_("Condition \"%s\" does not match preceding group"),
          as_bad (_("Condition \"%s\" does not match preceding group"),
                  operand->buf);
                  operand->buf);
          return 0;
          return 0;
        }
        }
      if (insn->opcode[0].word & CC_GROUP)
      if (insn->opcode[0].word & CC_GROUP)
        {
        {
          if ((insn->opcode[0].word & CC_ACC) != (cc->value & CC_ACC))
          if ((insn->opcode[0].word & CC_ACC) != (cc->value & CC_ACC))
            {
            {
              as_bad (_("Condition \"%s\" uses a different accumulator from "
              as_bad (_("Condition \"%s\" uses a different accumulator from "
                        "a preceding condition"),
                        "a preceding condition"),
                      operand->buf);
                      operand->buf);
              return 0;
              return 0;
            }
            }
          if ((insn->opcode[0].word & CATG_A1) && (cc->value & CATG_A1))
          if ((insn->opcode[0].word & CATG_A1) && (cc->value & CATG_A1))
            {
            {
              as_bad (_("Only one comparison conditional allowed"));
              as_bad (_("Only one comparison conditional allowed"));
              return 0;
              return 0;
            }
            }
          if ((insn->opcode[0].word & CATG_B1) && (cc->value & CATG_B1))
          if ((insn->opcode[0].word & CATG_B1) && (cc->value & CATG_B1))
            {
            {
              as_bad (_("Only one overflow conditional allowed"));
              as_bad (_("Only one overflow conditional allowed"));
              return 0;
              return 0;
            }
            }
        }
        }
      else if (   ((insn->opcode[0].word & CATG_A2) && (cc->value & CATG_A2))
      else if (   ((insn->opcode[0].word & CATG_A2) && (cc->value & CATG_A2))
               || ((insn->opcode[0].word & CATG_B2) && (cc->value & CATG_B2))
               || ((insn->opcode[0].word & CATG_B2) && (cc->value & CATG_B2))
               || ((insn->opcode[0].word & CATG_C2) && (cc->value & CATG_C2)))
               || ((insn->opcode[0].word & CATG_C2) && (cc->value & CATG_C2)))
        {
        {
          as_bad (_("Duplicate %s conditional"), operand->buf);
          as_bad (_("Duplicate %s conditional"), operand->buf);
          return 0;
          return 0;
        }
        }
    }
    }
 
 
  insn->opcode[0].word |= cc->value;
  insn->opcode[0].word |= cc->value;
  return 1;
  return 1;
}
}
 
 
static int
static int
encode_cc3 (insn, operand)
encode_cc3 (insn, operand)
     tic54x_insn *insn;
     tic54x_insn *insn;
     struct opstruct *operand;
     struct opstruct *operand;
{
{
  symbol *cc3 = (symbol *) hash_find (cc3_hash, operand->buf);
  symbol *cc3 = (symbol *) hash_find (cc3_hash, operand->buf);
  int value = cc3 ? cc3->value : operand->exp.X_add_number << 8;
  int value = cc3 ? cc3->value : operand->exp.X_add_number << 8;
 
 
  if ((value & 0x0300) != value)
  if ((value & 0x0300) != value)
    {
    {
      as_bad (_("Unrecognized condition code \"%s\""), operand->buf);
      as_bad (_("Unrecognized condition code \"%s\""), operand->buf);
      return 0;
      return 0;
    }
    }
  insn->opcode[0].word |= value;
  insn->opcode[0].word |= value;
  return 1;
  return 1;
}
}
 
 
static int
static int
encode_arx (insn, operand)
encode_arx (insn, operand)
     tic54x_insn *insn;
     tic54x_insn *insn;
     struct opstruct *operand;
     struct opstruct *operand;
{
{
  int arf = strlen (operand->buf) >= 3 ? operand->buf[2] - '0' : -1;
  int arf = strlen (operand->buf) >= 3 ? operand->buf[2] - '0' : -1;
 
 
  if (strncasecmp ("ar", operand->buf, 2) || arf < 0 || arf > 7)
  if (strncasecmp ("ar", operand->buf, 2) || arf < 0 || arf > 7)
    {
    {
      as_bad (_("Invalid auxiliary register (use AR0-AR7)"));
      as_bad (_("Invalid auxiliary register (use AR0-AR7)"));
      return 0;
      return 0;
    }
    }
  insn->opcode[0].word |= arf;
  insn->opcode[0].word |= arf;
  return 1;
  return 1;
}
}
 
 
static int
static int
encode_cc2 (insn, operand)
encode_cc2 (insn, operand)
     tic54x_insn *insn;
     tic54x_insn *insn;
     struct opstruct *operand;
     struct opstruct *operand;
{
{
  symbol *cc2 = (symbol *) hash_find (cc2_hash, operand->buf);
  symbol *cc2 = (symbol *) hash_find (cc2_hash, operand->buf);
 
 
  if (!cc2)
  if (!cc2)
    {
    {
      as_bad (_("Unrecognized condition code \"%s\""), operand->buf);
      as_bad (_("Unrecognized condition code \"%s\""), operand->buf);
      return 0;
      return 0;
    }
    }
  insn->opcode[0].word |= cc2->value;
  insn->opcode[0].word |= cc2->value;
  return 1;
  return 1;
}
}
 
 
static int
static int
encode_operand (insn, type, operand)
encode_operand (insn, type, operand)
     tic54x_insn *insn;
     tic54x_insn *insn;
     enum optype type;
     enum optype type;
     struct opstruct *operand;
     struct opstruct *operand;
{
{
  int ext = (insn->tm->flags & FL_EXT) != 0;
  int ext = (insn->tm->flags & FL_EXT) != 0;
 
 
  if (type == OP_MMR && operand->exp.X_op != O_constant)
  if (type == OP_MMR && operand->exp.X_op != O_constant)
    {
    {
      /* Disallow long-constant addressing for memory-mapped addressing.  */
      /* Disallow long-constant addressing for memory-mapped addressing.  */
      if (insn->is_lkaddr)
      if (insn->is_lkaddr)
        {
        {
          as_bad (_("lk addressing modes are invalid for memory-mapped "
          as_bad (_("lk addressing modes are invalid for memory-mapped "
                    "register addressing"));
                    "register addressing"));
          return 0;
          return 0;
        }
        }
      type = OP_Smem;
      type = OP_Smem;
      /* Warn about *+ARx when used with MMR operands.  */
      /* Warn about *+ARx when used with MMR operands.  */
      if (strncasecmp (operand->buf, "*+ar", 4) == 0)
      if (strncasecmp (operand->buf, "*+ar", 4) == 0)
        {
        {
          as_warn (_("Address mode *+ARx is not allowed in memory-mapped "
          as_warn (_("Address mode *+ARx is not allowed in memory-mapped "
                     "register addressing.  Resulting behavior is "
                     "register addressing.  Resulting behavior is "
                     "undefined."));
                     "undefined."));
        }
        }
    }
    }
 
 
  switch (type)
  switch (type)
    {
    {
    case OP_None:
    case OP_None:
      return 1;
      return 1;
    case OP_dmad:
    case OP_dmad:
      /* 16-bit immediate value.  */
      /* 16-bit immediate value.  */
      return encode_dmad (insn, operand, 0);
      return encode_dmad (insn, operand, 0);
    case OP_SRC:
    case OP_SRC:
      if (TOUPPER (*operand->buf) == 'B')
      if (TOUPPER (*operand->buf) == 'B')
        {
        {
          insn->opcode[ext ? (1 + insn->is_lkaddr) : 0].word |= (1 << 9);
          insn->opcode[ext ? (1 + insn->is_lkaddr) : 0].word |= (1 << 9);
          if (insn->using_default_dst)
          if (insn->using_default_dst)
            insn->opcode[ext ? (1 + insn->is_lkaddr) : 0].word |= (1 << 8);
            insn->opcode[ext ? (1 + insn->is_lkaddr) : 0].word |= (1 << 8);
        }
        }
      return 1;
      return 1;
    case OP_RND:
    case OP_RND:
      /* Make sure this agrees with the OP_DST operand.  */
      /* Make sure this agrees with the OP_DST operand.  */
      if (!((TOUPPER (operand->buf[0]) == 'B') ^
      if (!((TOUPPER (operand->buf[0]) == 'B') ^
            ((insn->opcode[0].word & (1 << 8)) != 0)))
            ((insn->opcode[0].word & (1 << 8)) != 0)))
        {
        {
          as_bad (_("Destination accumulator for each part of this parallel "
          as_bad (_("Destination accumulator for each part of this parallel "
                    "instruction must be different"));
                    "instruction must be different"));
          return 0;
          return 0;
        }
        }
      return 1;
      return 1;
    case OP_SRC1:
    case OP_SRC1:
    case OP_DST:
    case OP_DST:
      if (TOUPPER (operand->buf[0]) == 'B')
      if (TOUPPER (operand->buf[0]) == 'B')
        insn->opcode[ext ? (1 + insn->is_lkaddr) : 0].word |= (1 << 8);
        insn->opcode[ext ? (1 + insn->is_lkaddr) : 0].word |= (1 << 8);
      return 1;
      return 1;
    case OP_Xmem:
    case OP_Xmem:
    case OP_Ymem:
    case OP_Ymem:
      {
      {
        int mod = (operand->buf[4] == '\0' ? 0 : /* *arx  */
        int mod = (operand->buf[4] == '\0' ? 0 : /* *arx  */
                   operand->buf[4] == '-' ? 1 : /* *arx-  */
                   operand->buf[4] == '-' ? 1 : /* *arx-  */
                   operand->buf[5] == '\0' ? 2 : 3); /* *arx+, *arx+0%  */
                   operand->buf[5] == '\0' ? 2 : 3); /* *arx+, *arx+0%  */
        int arf = operand->buf[3] - '0' - 2;
        int arf = operand->buf[3] - '0' - 2;
        int code = (mod << 2) | arf;
        int code = (mod << 2) | arf;
        insn->opcode[0].word |= (code << (type == OP_Xmem ? 4 : 0));
        insn->opcode[0].word |= (code << (type == OP_Xmem ? 4 : 0));
        return 1;
        return 1;
      }
      }
    case OP_Lmem:
    case OP_Lmem:
    case OP_Smem:
    case OP_Smem:
      if (!is_indirect (operand))
      if (!is_indirect (operand))
        return encode_address (insn, operand);
        return encode_address (insn, operand);
      /* Fall through.  */
      /* Fall through.  */
    case OP_Sind:
    case OP_Sind:
      return encode_indirect (insn, operand);
      return encode_indirect (insn, operand);
    case OP_xpmad_ms7:
    case OP_xpmad_ms7:
      return encode_dmad (insn, operand, 2);
      return encode_dmad (insn, operand, 2);
    case OP_xpmad:
    case OP_xpmad:
      return encode_dmad (insn, operand, 1);
      return encode_dmad (insn, operand, 1);
    case OP_PA:
    case OP_PA:
    case OP_pmad:
    case OP_pmad:
      return encode_dmad (insn, operand, 0);
      return encode_dmad (insn, operand, 0);
    case OP_ARX:
    case OP_ARX:
      return encode_arx (insn, operand);
      return encode_arx (insn, operand);
    case OP_MMRX:
    case OP_MMRX:
    case OP_MMRY:
    case OP_MMRY:
    case OP_MMR:
    case OP_MMR:
      {
      {
        int value = operand->exp.X_add_number;
        int value = operand->exp.X_add_number;
 
 
        if (type == OP_MMR)
        if (type == OP_MMR)
          insn->opcode[0].word |= value;
          insn->opcode[0].word |= value;
        else
        else
          {
          {
            if (value < 16 || value > 24)
            if (value < 16 || value > 24)
              {
              {
                as_bad (_("Memory mapped register \"%s\" out of range"),
                as_bad (_("Memory mapped register \"%s\" out of range"),
                        operand->buf);
                        operand->buf);
                return 0;
                return 0;
              }
              }
            if (type == OP_MMRX)
            if (type == OP_MMRX)
              insn->opcode[0].word |= (value - 16) << 4;
              insn->opcode[0].word |= (value - 16) << 4;
            else
            else
              insn->opcode[0].word |= (value - 16);
              insn->opcode[0].word |= (value - 16);
          }
          }
        return 1;
        return 1;
      }
      }
    case OP_B:
    case OP_B:
    case OP_A:
    case OP_A:
      return 1;
      return 1;
    case OP_SHFT:
    case OP_SHFT:
      return encode_integer (insn, operand, ext + insn->is_lkaddr,
      return encode_integer (insn, operand, ext + insn->is_lkaddr,
                             0, 15, 0xF);
                             0, 15, 0xF);
    case OP_SHIFT:
    case OP_SHIFT:
      return encode_integer (insn, operand, ext + insn->is_lkaddr,
      return encode_integer (insn, operand, ext + insn->is_lkaddr,
                             -16, 15, 0x1F);
                             -16, 15, 0x1F);
    case OP_lk:
    case OP_lk:
      return encode_integer (insn, operand, 1 + insn->is_lkaddr,
      return encode_integer (insn, operand, 1 + insn->is_lkaddr,
                             -32768, 32767, 0xFFFF);
                             -32768, 32767, 0xFFFF);
    case OP_CC:
    case OP_CC:
      return encode_condition (insn, operand);
      return encode_condition (insn, operand);
    case OP_CC2:
    case OP_CC2:
      return encode_cc2 (insn, operand);
      return encode_cc2 (insn, operand);
    case OP_CC3:
    case OP_CC3:
      return encode_cc3 (insn, operand);
      return encode_cc3 (insn, operand);
    case OP_BITC:
    case OP_BITC:
      return encode_integer (insn, operand, 0, 0, 15, 0xF);
      return encode_integer (insn, operand, 0, 0, 15, 0xF);
    case OP_k8:
    case OP_k8:
      return encode_integer (insn, operand, 0, -128, 127, 0xFF);
      return encode_integer (insn, operand, 0, -128, 127, 0xFF);
    case OP_123:
    case OP_123:
      {
      {
        int value = operand->exp.X_add_number;
        int value = operand->exp.X_add_number;
        int code;
        int code;
        if (value < 1 || value > 3)
        if (value < 1 || value > 3)
          {
          {
            as_bad (_("Invalid operand (use 1, 2, or 3)"));
            as_bad (_("Invalid operand (use 1, 2, or 3)"));
            return 0;
            return 0;
          }
          }
        code = value == 1 ? 0 : value == 2 ? 0x2 : 0x1;
        code = value == 1 ? 0 : value == 2 ? 0x2 : 0x1;
        insn->opcode[0].word |= (code << 8);
        insn->opcode[0].word |= (code << 8);
        return 1;
        return 1;
      }
      }
    case OP_031:
    case OP_031:
      return encode_integer (insn, operand, 0, 0, 31, 0x1F);
      return encode_integer (insn, operand, 0, 0, 31, 0x1F);
    case OP_k8u:
    case OP_k8u:
      return encode_integer (insn, operand, 0, 0, 255, 0xFF);
      return encode_integer (insn, operand, 0, 0, 255, 0xFF);
    case OP_lku:
    case OP_lku:
      return encode_integer (insn, operand, 1 + insn->is_lkaddr,
      return encode_integer (insn, operand, 1 + insn->is_lkaddr,
                             0, 65535, 0xFFFF);
                             0, 65535, 0xFFFF);
    case OP_SBIT:
    case OP_SBIT:
      {
      {
        symbol *sbit = (symbol *) hash_find (sbit_hash, operand->buf);
        symbol *sbit = (symbol *) hash_find (sbit_hash, operand->buf);
        int value = is_absolute (operand) ?
        int value = is_absolute (operand) ?
          operand->exp.X_add_number : (sbit ? sbit->value : -1);
          operand->exp.X_add_number : (sbit ? sbit->value : -1);
        int reg = 0;
        int reg = 0;
 
 
        if (insn->opcount == 1)
        if (insn->opcount == 1)
          {
          {
            if (!sbit)
            if (!sbit)
              {
              {
                as_bad (_("A status register or status bit name is required"));
                as_bad (_("A status register or status bit name is required"));
                return 0;
                return 0;
              }
              }
            /* Guess the register based on the status bit; "ovb" is the last
            /* Guess the register based on the status bit; "ovb" is the last
               status bit defined for st0.  */
               status bit defined for st0.  */
            if (sbit > (symbol *) hash_find (sbit_hash, "ovb"))
            if (sbit > (symbol *) hash_find (sbit_hash, "ovb"))
              reg = 1;
              reg = 1;
          }
          }
        if (value == -1)
        if (value == -1)
          {
          {
            as_bad (_("Unrecognized status bit \"%s\""), operand->buf);
            as_bad (_("Unrecognized status bit \"%s\""), operand->buf);
            return 0;
            return 0;
          }
          }
        insn->opcode[0].word |= value;
        insn->opcode[0].word |= value;
        insn->opcode[0].word |= (reg << 9);
        insn->opcode[0].word |= (reg << 9);
        return 1;
        return 1;
      }
      }
    case OP_N:
    case OP_N:
      if (strcasecmp (operand->buf, "st0") == 0
      if (strcasecmp (operand->buf, "st0") == 0
          || strcasecmp (operand->buf, "st1") == 0)
          || strcasecmp (operand->buf, "st1") == 0)
        {
        {
          insn->opcode[0].word |=
          insn->opcode[0].word |=
            ((unsigned short) (operand->buf[2] - '0')) << 9;
            ((unsigned short) (operand->buf[2] - '0')) << 9;
          return 1;
          return 1;
        }
        }
      else if (operand->exp.X_op == O_constant
      else if (operand->exp.X_op == O_constant
               && (operand->exp.X_add_number == 0
               && (operand->exp.X_add_number == 0
                   || operand->exp.X_add_number == 1))
                   || operand->exp.X_add_number == 1))
        {
        {
          insn->opcode[0].word |=
          insn->opcode[0].word |=
            ((unsigned short) (operand->exp.X_add_number)) << 9;
            ((unsigned short) (operand->exp.X_add_number)) << 9;
          return 1;
          return 1;
        }
        }
      as_bad (_("Invalid status register \"%s\""), operand->buf);
      as_bad (_("Invalid status register \"%s\""), operand->buf);
      return 0;
      return 0;
    case OP_k5:
    case OP_k5:
      return encode_integer (insn, operand, 0, -16, 15, 0x1F);
      return encode_integer (insn, operand, 0, -16, 15, 0x1F);
    case OP_k3:
    case OP_k3:
      return encode_integer (insn, operand, 0, 0, 7, 0x7);
      return encode_integer (insn, operand, 0, 0, 7, 0x7);
    case OP_k9:
    case OP_k9:
      return encode_integer (insn, operand, 0, 0, 0x1FF, 0x1FF);
      return encode_integer (insn, operand, 0, 0, 0x1FF, 0x1FF);
    case OP_12:
    case OP_12:
      if (operand->exp.X_add_number != 1
      if (operand->exp.X_add_number != 1
          && operand->exp.X_add_number != 2)
          && operand->exp.X_add_number != 2)
        {
        {
          as_bad (_("Operand \"%s\" out of range (use 1 or 2)"), operand->buf);
          as_bad (_("Operand \"%s\" out of range (use 1 or 2)"), operand->buf);
          return 0;
          return 0;
        }
        }
      insn->opcode[0].word |= (operand->exp.X_add_number - 1) << 9;
      insn->opcode[0].word |= (operand->exp.X_add_number - 1) << 9;
      return 1;
      return 1;
    case OP_16:
    case OP_16:
    case OP_T:
    case OP_T:
    case OP_TS:
    case OP_TS:
    case OP_ASM:
    case OP_ASM:
    case OP_TRN:
    case OP_TRN:
    case OP_DP:
    case OP_DP:
    case OP_ARP:
    case OP_ARP:
      /* No encoding necessary.  */
      /* No encoding necessary.  */
      return 1;
      return 1;
    default:
    default:
      return 0;
      return 0;
    }
    }
 
 
  return 1;
  return 1;
}
}
 
 
static void
static void
emit_insn (insn)
emit_insn (insn)
     tic54x_insn *insn;
     tic54x_insn *insn;
{
{
  int i;
  int i;
  flagword oldflags = bfd_get_section_flags (stdoutput, now_seg);
  flagword oldflags = bfd_get_section_flags (stdoutput, now_seg);
  flagword flags = oldflags | SEC_CODE;
  flagword flags = oldflags | SEC_CODE;
 
 
  if (! bfd_set_section_flags (stdoutput, now_seg, flags))
  if (! bfd_set_section_flags (stdoutput, now_seg, flags))
        as_warn (_("error setting flags for \"%s\": %s"),
        as_warn (_("error setting flags for \"%s\": %s"),
                 bfd_section_name (stdoutput, now_seg),
                 bfd_section_name (stdoutput, now_seg),
                 bfd_errmsg (bfd_get_error ()));
                 bfd_errmsg (bfd_get_error ()));
 
 
  for (i = 0; i < insn->words; i++)
  for (i = 0; i < insn->words; i++)
    {
    {
      int size = (insn->opcode[i].unresolved
      int size = (insn->opcode[i].unresolved
                  && insn->opcode[i].r_type == BFD_RELOC_TIC54X_23) ? 4 : 2;
                  && insn->opcode[i].r_type == BFD_RELOC_TIC54X_23) ? 4 : 2;
      char *p = frag_more (size);
      char *p = frag_more (size);
 
 
      if (size == 2)
      if (size == 2)
        md_number_to_chars (p, (valueT) insn->opcode[i].word, 2);
        md_number_to_chars (p, (valueT) insn->opcode[i].word, 2);
      else
      else
        md_number_to_chars (p, (valueT) insn->opcode[i].word << 16, 4);
        md_number_to_chars (p, (valueT) insn->opcode[i].word << 16, 4);
 
 
      if (insn->opcode[i].unresolved)
      if (insn->opcode[i].unresolved)
        fix_new_exp (frag_now, p - frag_now->fr_literal,
        fix_new_exp (frag_now, p - frag_now->fr_literal,
                     insn->opcode[i].r_nchars, &insn->opcode[i].addr_expr,
                     insn->opcode[i].r_nchars, &insn->opcode[i].addr_expr,
                     FALSE, insn->opcode[i].r_type);
                     FALSE, insn->opcode[i].r_type);
    }
    }
}
}
 
 
/* Convert the operand strings into appropriate opcode values
/* Convert the operand strings into appropriate opcode values
   return the total number of words used by the instruction.  */
   return the total number of words used by the instruction.  */
 
 
static int
static int
build_insn (insn)
build_insn (insn)
     tic54x_insn *insn;
     tic54x_insn *insn;
{
{
  int i;
  int i;
 
 
  /* Only non-parallel instructions support lk addressing.  */
  /* Only non-parallel instructions support lk addressing.  */
  if (!(insn->tm->flags & FL_PAR))
  if (!(insn->tm->flags & FL_PAR))
    {
    {
      for (i = 0; i < insn->opcount; i++)
      for (i = 0; i < insn->opcount; i++)
        {
        {
          if ((OPTYPE (insn->operands[i].type) == OP_Smem
          if ((OPTYPE (insn->operands[i].type) == OP_Smem
               || OPTYPE (insn->operands[i].type) == OP_Lmem
               || OPTYPE (insn->operands[i].type) == OP_Lmem
               || OPTYPE (insn->operands[i].type) == OP_Sind)
               || OPTYPE (insn->operands[i].type) == OP_Sind)
              && strchr (insn->operands[i].buf, '(')
              && strchr (insn->operands[i].buf, '(')
              /* Don't mistake stack-relative addressing for lk addressing.  */
              /* Don't mistake stack-relative addressing for lk addressing.  */
              && strncasecmp (insn->operands[i].buf, "*sp (", 4) != 0)
              && strncasecmp (insn->operands[i].buf, "*sp (", 4) != 0)
            {
            {
              insn->is_lkaddr = 1;
              insn->is_lkaddr = 1;
              insn->lkoperand = i;
              insn->lkoperand = i;
              break;
              break;
            }
            }
        }
        }
    }
    }
  insn->words = insn->tm->words + insn->is_lkaddr;
  insn->words = insn->tm->words + insn->is_lkaddr;
 
 
  insn->opcode[0].word = insn->tm->opcode;
  insn->opcode[0].word = insn->tm->opcode;
  if (insn->tm->flags & FL_EXT)
  if (insn->tm->flags & FL_EXT)
    insn->opcode[1 + insn->is_lkaddr].word = insn->tm->opcode2;
    insn->opcode[1 + insn->is_lkaddr].word = insn->tm->opcode2;
 
 
  for (i = 0; i < insn->opcount; i++)
  for (i = 0; i < insn->opcount; i++)
    {
    {
      enum optype type = insn->operands[i].type;
      enum optype type = insn->operands[i].type;
 
 
      if (!encode_operand (insn, type, &insn->operands[i]))
      if (!encode_operand (insn, type, &insn->operands[i]))
        return 0;
        return 0;
    }
    }
  if (insn->tm->flags & FL_PAR)
  if (insn->tm->flags & FL_PAR)
    for (i = 0; i < insn->paropcount; i++)
    for (i = 0; i < insn->paropcount; i++)
      {
      {
        enum optype partype = insn->paroperands[i].type;
        enum optype partype = insn->paroperands[i].type;
 
 
        if (!encode_operand (insn, partype, &insn->paroperands[i]))
        if (!encode_operand (insn, partype, &insn->paroperands[i]))
          return 0;
          return 0;
      }
      }
 
 
  emit_insn (insn);
  emit_insn (insn);
 
 
  return insn->words;
  return insn->words;
}
}
 
 
static int
static int
optimize_insn (insn)
optimize_insn (insn)
     tic54x_insn *insn;
     tic54x_insn *insn;
{
{
  /* Optimize some instructions, helping out the brain-dead programmer.  */
  /* Optimize some instructions, helping out the brain-dead programmer.  */
#define is_zero(op) ((op).exp.X_op == O_constant && (op).exp.X_add_number == 0)
#define is_zero(op) ((op).exp.X_op == O_constant && (op).exp.X_add_number == 0)
  if (strcasecmp (insn->tm->name, "add") == 0)
  if (strcasecmp (insn->tm->name, "add") == 0)
    {
    {
      if (insn->opcount > 1
      if (insn->opcount > 1
          && is_accumulator (&insn->operands[insn->opcount - 2])
          && is_accumulator (&insn->operands[insn->opcount - 2])
          && is_accumulator (&insn->operands[insn->opcount - 1])
          && is_accumulator (&insn->operands[insn->opcount - 1])
          && strcasecmp (insn->operands[insn->opcount - 2].buf,
          && strcasecmp (insn->operands[insn->opcount - 2].buf,
                         insn->operands[insn->opcount - 1].buf) == 0)
                         insn->operands[insn->opcount - 1].buf) == 0)
        {
        {
          --insn->opcount;
          --insn->opcount;
          insn->using_default_dst = 1;
          insn->using_default_dst = 1;
          return 1;
          return 1;
        }
        }
 
 
      /* Try to collapse if Xmem and shift count is zero.  */
      /* Try to collapse if Xmem and shift count is zero.  */
      if ((OPTYPE (insn->tm->operand_types[0]) == OP_Xmem
      if ((OPTYPE (insn->tm->operand_types[0]) == OP_Xmem
           && OPTYPE (insn->tm->operand_types[1]) == OP_SHFT
           && OPTYPE (insn->tm->operand_types[1]) == OP_SHFT
           && is_zero (insn->operands[1]))
           && is_zero (insn->operands[1]))
          /* Or if Smem, shift is zero or absent, and SRC == DST.  */
          /* Or if Smem, shift is zero or absent, and SRC == DST.  */
          || (OPTYPE (insn->tm->operand_types[0]) == OP_Smem
          || (OPTYPE (insn->tm->operand_types[0]) == OP_Smem
              && OPTYPE (insn->tm->operand_types[1]) == OP_SHIFT
              && OPTYPE (insn->tm->operand_types[1]) == OP_SHIFT
              && is_type (&insn->operands[1], OP_SHIFT)
              && is_type (&insn->operands[1], OP_SHIFT)
              && is_zero (insn->operands[1]) && insn->opcount == 3))
              && is_zero (insn->operands[1]) && insn->opcount == 3))
        {
        {
          insn->operands[1] = insn->operands[2];
          insn->operands[1] = insn->operands[2];
          insn->opcount = 2;
          insn->opcount = 2;
          return 1;
          return 1;
        }
        }
    }
    }
  else if (strcasecmp (insn->tm->name, "ld") == 0)
  else if (strcasecmp (insn->tm->name, "ld") == 0)
    {
    {
      if (insn->opcount == 3 && insn->operands[0].type != OP_SRC)
      if (insn->opcount == 3 && insn->operands[0].type != OP_SRC)
        {
        {
          if ((OPTYPE (insn->tm->operand_types[1]) == OP_SHIFT
          if ((OPTYPE (insn->tm->operand_types[1]) == OP_SHIFT
               || OPTYPE (insn->tm->operand_types[1]) == OP_SHFT)
               || OPTYPE (insn->tm->operand_types[1]) == OP_SHFT)
              && is_zero (insn->operands[1])
              && is_zero (insn->operands[1])
              && (OPTYPE (insn->tm->operand_types[0]) != OP_lk
              && (OPTYPE (insn->tm->operand_types[0]) != OP_lk
                  || (insn->operands[0].exp.X_op == O_constant
                  || (insn->operands[0].exp.X_op == O_constant
                      && insn->operands[0].exp.X_add_number <= 255
                      && insn->operands[0].exp.X_add_number <= 255
                      && insn->operands[0].exp.X_add_number >= 0)))
                      && insn->operands[0].exp.X_add_number >= 0)))
            {
            {
              insn->operands[1] = insn->operands[2];
              insn->operands[1] = insn->operands[2];
              insn->opcount = 2;
              insn->opcount = 2;
              return 1;
              return 1;
            }
            }
        }
        }
    }
    }
  else if (strcasecmp (insn->tm->name, "sth") == 0
  else if (strcasecmp (insn->tm->name, "sth") == 0
           || strcasecmp (insn->tm->name, "stl") == 0)
           || strcasecmp (insn->tm->name, "stl") == 0)
    {
    {
      if ((OPTYPE (insn->tm->operand_types[1]) == OP_SHIFT
      if ((OPTYPE (insn->tm->operand_types[1]) == OP_SHIFT
           || OPTYPE (insn->tm->operand_types[1]) == OP_SHFT)
           || OPTYPE (insn->tm->operand_types[1]) == OP_SHFT)
          && is_zero (insn->operands[1]))
          && is_zero (insn->operands[1]))
        {
        {
          insn->operands[1] = insn->operands[2];
          insn->operands[1] = insn->operands[2];
          insn->opcount = 2;
          insn->opcount = 2;
          return 1;
          return 1;
        }
        }
    }
    }
  else if (strcasecmp (insn->tm->name, "sub") == 0)
  else if (strcasecmp (insn->tm->name, "sub") == 0)
    {
    {
      if (insn->opcount > 1
      if (insn->opcount > 1
          && is_accumulator (&insn->operands[insn->opcount - 2])
          && is_accumulator (&insn->operands[insn->opcount - 2])
          && is_accumulator (&insn->operands[insn->opcount - 1])
          && is_accumulator (&insn->operands[insn->opcount - 1])
          && strcasecmp (insn->operands[insn->opcount - 2].buf,
          && strcasecmp (insn->operands[insn->opcount - 2].buf,
                         insn->operands[insn->opcount - 1].buf) == 0)
                         insn->operands[insn->opcount - 1].buf) == 0)
        {
        {
          --insn->opcount;
          --insn->opcount;
          insn->using_default_dst = 1;
          insn->using_default_dst = 1;
          return 1;
          return 1;
        }
        }
 
 
      if (   ((OPTYPE (insn->tm->operand_types[0]) == OP_Smem
      if (   ((OPTYPE (insn->tm->operand_types[0]) == OP_Smem
            && OPTYPE (insn->tm->operand_types[1]) == OP_SHIFT)
            && OPTYPE (insn->tm->operand_types[1]) == OP_SHIFT)
           || (OPTYPE (insn->tm->operand_types[0]) == OP_Xmem
           || (OPTYPE (insn->tm->operand_types[0]) == OP_Xmem
            && OPTYPE (insn->tm->operand_types[1]) == OP_SHFT))
            && OPTYPE (insn->tm->operand_types[1]) == OP_SHFT))
          && is_zero (insn->operands[1])
          && is_zero (insn->operands[1])
          && insn->opcount == 3)
          && insn->opcount == 3)
        {
        {
          insn->operands[1] = insn->operands[2];
          insn->operands[1] = insn->operands[2];
          insn->opcount = 2;
          insn->opcount = 2;
          return 1;
          return 1;
        }
        }
    }
    }
  return 0;
  return 0;
}
}
 
 
/* Find a matching template if possible, and get the operand strings.  */
/* Find a matching template if possible, and get the operand strings.  */
 
 
static int
static int
tic54x_parse_insn (insn, line)
tic54x_parse_insn (insn, line)
     tic54x_insn *insn;
     tic54x_insn *insn;
     char *line;
     char *line;
{
{
  insn->tm = (template *) hash_find (op_hash, insn->mnemonic);
  insn->tm = (template *) hash_find (op_hash, insn->mnemonic);
  if (!insn->tm)
  if (!insn->tm)
    {
    {
      as_bad (_("Unrecognized instruction \"%s\""), insn->mnemonic);
      as_bad (_("Unrecognized instruction \"%s\""), insn->mnemonic);
      return 0;
      return 0;
    }
    }
 
 
  insn->opcount = get_operands (insn->operands, line);
  insn->opcount = get_operands (insn->operands, line);
  if (insn->opcount < 0)
  if (insn->opcount < 0)
    return 0;
    return 0;
 
 
  /* Check each variation of operands for this mnemonic.  */
  /* Check each variation of operands for this mnemonic.  */
  while (insn->tm->name && strcasecmp (insn->tm->name, insn->mnemonic) == 0)
  while (insn->tm->name && strcasecmp (insn->tm->name, insn->mnemonic) == 0)
    {
    {
      if (insn->opcount >= insn->tm->minops
      if (insn->opcount >= insn->tm->minops
          && insn->opcount <= insn->tm->maxops
          && insn->opcount <= insn->tm->maxops
          && operands_match (insn, &insn->operands[0], insn->opcount,
          && operands_match (insn, &insn->operands[0], insn->opcount,
                             insn->tm->operand_types,
                             insn->tm->operand_types,
                             insn->tm->minops, insn->tm->maxops))
                             insn->tm->minops, insn->tm->maxops))
        {
        {
          /* SUCCESS! now try some optimizations.  */
          /* SUCCESS! now try some optimizations.  */
          if (optimize_insn (insn))
          if (optimize_insn (insn))
            {
            {
              insn->tm = (template *) hash_find (op_hash,
              insn->tm = (template *) hash_find (op_hash,
                                                 insn->mnemonic);
                                                 insn->mnemonic);
              continue;
              continue;
            }
            }
 
 
          return 1;
          return 1;
        }
        }
      ++(insn->tm);
      ++(insn->tm);
    }
    }
  as_bad (_("Unrecognized operand list '%s' for instruction '%s'"),
  as_bad (_("Unrecognized operand list '%s' for instruction '%s'"),
          line, insn->mnemonic);
          line, insn->mnemonic);
  return 0;
  return 0;
}
}
 
 
/* We set this in start_line_hook, 'cause if we do a line replacement, we
/* We set this in start_line_hook, 'cause if we do a line replacement, we
   won't be able to see the next line.  */
   won't be able to see the next line.  */
static int parallel_on_next_line_hint = 0;
static int parallel_on_next_line_hint = 0;
 
 
/* See if this is part of a parallel instruction
/* See if this is part of a parallel instruction
   Look for a subsequent line starting with "||".  */
   Look for a subsequent line starting with "||".  */
 
 
static int
static int
next_line_shows_parallel (next_line)
next_line_shows_parallel (next_line)
     char *next_line;
     char *next_line;
{
{
  /* Look for the second half.  */
  /* Look for the second half.  */
  while (ISSPACE (*next_line))
  while (ISSPACE (*next_line))
    ++next_line;
    ++next_line;
 
 
  return (next_line[0] == PARALLEL_SEPARATOR
  return (next_line[0] == PARALLEL_SEPARATOR
          && next_line[1] == PARALLEL_SEPARATOR);
          && next_line[1] == PARALLEL_SEPARATOR);
}
}
 
 
static int
static int
tic54x_parse_parallel_insn_firstline (insn, line)
tic54x_parse_parallel_insn_firstline (insn, line)
     tic54x_insn *insn;
     tic54x_insn *insn;
     char *line;
     char *line;
{
{
  insn->tm = (template *) hash_find (parop_hash, insn->mnemonic);
  insn->tm = (template *) hash_find (parop_hash, insn->mnemonic);
  if (!insn->tm)
  if (!insn->tm)
    {
    {
      as_bad (_("Unrecognized parallel instruction \"%s\""),
      as_bad (_("Unrecognized parallel instruction \"%s\""),
              insn->mnemonic);
              insn->mnemonic);
      return 0;
      return 0;
    }
    }
 
 
  while (insn->tm->name && strcasecmp (insn->tm->name,
  while (insn->tm->name && strcasecmp (insn->tm->name,
                                       insn->mnemonic) == 0)
                                       insn->mnemonic) == 0)
    {
    {
      insn->opcount = get_operands (insn->operands, line);
      insn->opcount = get_operands (insn->operands, line);
      if (insn->opcount < 0)
      if (insn->opcount < 0)
        return 0;
        return 0;
      if (insn->opcount == 2
      if (insn->opcount == 2
          && operands_match (insn, &insn->operands[0], insn->opcount,
          && operands_match (insn, &insn->operands[0], insn->opcount,
                             insn->tm->operand_types, 2, 2))
                             insn->tm->operand_types, 2, 2))
        {
        {
          return 1;
          return 1;
        }
        }
      ++(insn->tm);
      ++(insn->tm);
    }
    }
  /* Didn't find a matching parallel; try for a normal insn.  */
  /* Didn't find a matching parallel; try for a normal insn.  */
  return 0;
  return 0;
}
}
 
 
/* Parse the second line of a two-line parallel instruction.  */
/* Parse the second line of a two-line parallel instruction.  */
 
 
static int
static int
tic54x_parse_parallel_insn_lastline (insn, line)
tic54x_parse_parallel_insn_lastline (insn, line)
     tic54x_insn *insn;
     tic54x_insn *insn;
     char *line;
     char *line;
{
{
  int valid_mnemonic = 0;
  int valid_mnemonic = 0;
 
 
  insn->paropcount = get_operands (insn->paroperands, line);
  insn->paropcount = get_operands (insn->paroperands, line);
  while (insn->tm->name && strcasecmp (insn->tm->name,
  while (insn->tm->name && strcasecmp (insn->tm->name,
                                       insn->mnemonic) == 0)
                                       insn->mnemonic) == 0)
    {
    {
      if (strcasecmp (insn->tm->parname, insn->parmnemonic) == 0)
      if (strcasecmp (insn->tm->parname, insn->parmnemonic) == 0)
        {
        {
          valid_mnemonic = 1;
          valid_mnemonic = 1;
 
 
          if (insn->paropcount >= insn->tm->minops
          if (insn->paropcount >= insn->tm->minops
              && insn->paropcount <= insn->tm->maxops
              && insn->paropcount <= insn->tm->maxops
              && operands_match (insn, insn->paroperands,
              && operands_match (insn, insn->paroperands,
                                 insn->paropcount,
                                 insn->paropcount,
                                 insn->tm->paroperand_types,
                                 insn->tm->paroperand_types,
                                 insn->tm->minops, insn->tm->maxops))
                                 insn->tm->minops, insn->tm->maxops))
            return 1;
            return 1;
        }
        }
      ++(insn->tm);
      ++(insn->tm);
    }
    }
  if (valid_mnemonic)
  if (valid_mnemonic)
    as_bad (_("Invalid operand (s) for parallel instruction \"%s\""),
    as_bad (_("Invalid operand (s) for parallel instruction \"%s\""),
            insn->parmnemonic);
            insn->parmnemonic);
  else
  else
    as_bad (_("Unrecognized parallel instruction combination \"%s || %s\""),
    as_bad (_("Unrecognized parallel instruction combination \"%s || %s\""),
            insn->mnemonic, insn->parmnemonic);
            insn->mnemonic, insn->parmnemonic);
 
 
  return 0;
  return 0;
}
}
 
 
/* If quotes found, return copy of line up to closing quote;
/* If quotes found, return copy of line up to closing quote;
   otherwise up until terminator.
   otherwise up until terminator.
   If it's a string, pass as-is; otherwise attempt substitution symbol
   If it's a string, pass as-is; otherwise attempt substitution symbol
   replacement on the value.  */
   replacement on the value.  */
 
 
static char *
static char *
subsym_get_arg (line, terminators, str, nosub)
subsym_get_arg (line, terminators, str, nosub)
     char *line;
     char *line;
     char *terminators;
     char *terminators;
     char **str;
     char **str;
     int nosub;
     int nosub;
{
{
  char *ptr = line;
  char *ptr = line;
  char *endp;
  char *endp;
  int is_string = *line == '"';
  int is_string = *line == '"';
  int is_char = ISDIGIT (*line);
  int is_char = ISDIGIT (*line);
 
 
  if (is_char)
  if (is_char)
    {
    {
      while (ISDIGIT (*ptr))
      while (ISDIGIT (*ptr))
        ++ptr;
        ++ptr;
      endp = ptr;
      endp = ptr;
      *str = xmalloc (ptr - line + 1);
      *str = xmalloc (ptr - line + 1);
      strncpy (*str, line, ptr - line);
      strncpy (*str, line, ptr - line);
      (*str)[ptr - line] = 0;
      (*str)[ptr - line] = 0;
    }
    }
  else if (is_string)
  else if (is_string)
    {
    {
      char *savedp = input_line_pointer;
      char *savedp = input_line_pointer;
      int len;
      int len;
 
 
      input_line_pointer = ptr;
      input_line_pointer = ptr;
      *str = demand_copy_C_string (&len);
      *str = demand_copy_C_string (&len);
      endp = input_line_pointer;
      endp = input_line_pointer;
      input_line_pointer = savedp;
      input_line_pointer = savedp;
 
 
      /* Do forced substitutions if requested.  */
      /* Do forced substitutions if requested.  */
      if (!nosub && **str == ':')
      if (!nosub && **str == ':')
        *str = subsym_substitute (*str, 1);
        *str = subsym_substitute (*str, 1);
    }
    }
  else
  else
    {
    {
      char *term = terminators;
      char *term = terminators;
      char *value = NULL;
      char *value = NULL;
 
 
      while (*ptr && *ptr != *term)
      while (*ptr && *ptr != *term)
        {
        {
          if (!*term)
          if (!*term)
            {
            {
              term = terminators;
              term = terminators;
              ++ptr;
              ++ptr;
            }
            }
          else
          else
            ++term;
            ++term;
        }
        }
      endp = ptr;
      endp = ptr;
      *str = xmalloc (ptr - line + 1);
      *str = xmalloc (ptr - line + 1);
      strncpy (*str, line, ptr - line);
      strncpy (*str, line, ptr - line);
      (*str)[ptr - line] = 0;
      (*str)[ptr - line] = 0;
      /* Do simple substitution, if available.  */
      /* Do simple substitution, if available.  */
      if (!nosub && (value = subsym_lookup (*str, macro_level)) != NULL)
      if (!nosub && (value = subsym_lookup (*str, macro_level)) != NULL)
        *str = value;
        *str = value;
    }
    }
 
 
  return endp;
  return endp;
}
}
 
 
/* Replace the given substitution string.
/* Replace the given substitution string.
   We start at the innermost macro level, so that existing locals remain local
   We start at the innermost macro level, so that existing locals remain local
   Note: we're treating macro args identically to .var's; I don't know if
   Note: we're treating macro args identically to .var's; I don't know if
   that's compatible w/TI's assembler.  */
   that's compatible w/TI's assembler.  */
 
 
static void
static void
subsym_create_or_replace (name, value)
subsym_create_or_replace (name, value)
     char *name;
     char *name;
     char *value;
     char *value;
{
{
  int i;
  int i;
 
 
  for (i = macro_level; i > 0; i--)
  for (i = macro_level; i > 0; i--)
    {
    {
      if (hash_find (subsym_hash[i], name))
      if (hash_find (subsym_hash[i], name))
        {
        {
          hash_replace (subsym_hash[i], name, value);
          hash_replace (subsym_hash[i], name, value);
          return;
          return;
        }
        }
    }
    }
  if (hash_find (subsym_hash[0], name))
  if (hash_find (subsym_hash[0], name))
    hash_replace (subsym_hash[0], name, value);
    hash_replace (subsym_hash[0], name, value);
  else
  else
    hash_insert (subsym_hash[0], name, value);
    hash_insert (subsym_hash[0], name, value);
}
}
 
 
/* Look up the substitution string replacement for the given symbol.
/* Look up the substitution string replacement for the given symbol.
   Start with the innermost macro substitution table given and work
   Start with the innermost macro substitution table given and work
   outwards.  */
   outwards.  */
 
 
static char *
static char *
subsym_lookup (name, nest_level)
subsym_lookup (name, nest_level)
     char *name;
     char *name;
     int nest_level;
     int nest_level;
{
{
  char *value = hash_find (subsym_hash[nest_level], name);
  char *value = hash_find (subsym_hash[nest_level], name);
 
 
  if (value || nest_level == 0)
  if (value || nest_level == 0)
    return value;
    return value;
 
 
  return subsym_lookup (name, nest_level - 1);
  return subsym_lookup (name, nest_level - 1);
}
}
 
 
/* Do substitution-symbol replacement on the given line (recursively).
/* Do substitution-symbol replacement on the given line (recursively).
   return the argument if no substitution was done
   return the argument if no substitution was done
 
 
   Also look for built-in functions ($func (arg)) and local labels.
   Also look for built-in functions ($func (arg)) and local labels.
 
 
   If FORCED is set, look for forced substitutions of the form ':SYMBOL:'.  */
   If FORCED is set, look for forced substitutions of the form ':SYMBOL:'.  */
 
 
static char *
static char *
subsym_substitute (line, forced)
subsym_substitute (line, forced)
     char * line;
     char * line;
     int forced;
     int forced;
{
{
  /* For each apparent symbol, see if it's a substitution symbol, and if so,
  /* For each apparent symbol, see if it's a substitution symbol, and if so,
     replace it in the input.  */
     replace it in the input.  */
  char *replacement; /* current replacement for LINE.  */
  char *replacement; /* current replacement for LINE.  */
  char *head; /* Start of line.  */
  char *head; /* Start of line.  */
  char *ptr; /* Current examination point.  */
  char *ptr; /* Current examination point.  */
  int changed = 0; /* Did we make a substitution?  */
  int changed = 0; /* Did we make a substitution?  */
  int eval_line = 0; /* Is this line a .eval/.asg statement?  */
  int eval_line = 0; /* Is this line a .eval/.asg statement?  */
  int eval_symbol = 0; /* Are we in the middle of the symbol for
  int eval_symbol = 0; /* Are we in the middle of the symbol for
                          .eval/.asg?  */
                          .eval/.asg?  */
  char *eval_end = NULL;
  char *eval_end = NULL;
  int recurse = 1;
  int recurse = 1;
  int line_conditional = 0;
  int line_conditional = 0;
  char *tmp;
  char *tmp;
 
 
  /* Work with a copy of the input line.  */
  /* Work with a copy of the input line.  */
  replacement = xmalloc (strlen (line) + 1);
  replacement = xmalloc (strlen (line) + 1);
  strcpy (replacement, line);
  strcpy (replacement, line);
 
 
  ptr = head = replacement;
  ptr = head = replacement;
 
 
  /* Flag lines where we might need to replace a single '=' with two;
  /* Flag lines where we might need to replace a single '=' with two;
     GAS uses single '=' to assign macro args values, and possibly other
     GAS uses single '=' to assign macro args values, and possibly other
     places, so limit what we replace.  */
     places, so limit what we replace.  */
  if (strstr (line, ".if")
  if (strstr (line, ".if")
      || strstr (line, ".elseif")
      || strstr (line, ".elseif")
      || strstr (line, ".break"))
      || strstr (line, ".break"))
    line_conditional = 1;
    line_conditional = 1;
 
 
  /* Watch out for .eval, so that we avoid doing substitution on the
  /* Watch out for .eval, so that we avoid doing substitution on the
     symbol being assigned a value.  */
     symbol being assigned a value.  */
  if (strstr (line, ".eval") || strstr (line, ".asg"))
  if (strstr (line, ".eval") || strstr (line, ".asg"))
    eval_line = 1;
    eval_line = 1;
 
 
  /* If it's a macro definition, don't do substitution on the argument
  /* If it's a macro definition, don't do substitution on the argument
     names.  */
     names.  */
  if (strstr (line, ".macro"))
  if (strstr (line, ".macro"))
    return line;
    return line;
 
 
  while (!is_end_of_line[(int) *ptr])
  while (!is_end_of_line[(int) *ptr])
    {
    {
      int current_char = *ptr;
      int current_char = *ptr;
 
 
      /* Need to update this since LINE may have been modified.  */
      /* Need to update this since LINE may have been modified.  */
      if (eval_line)
      if (eval_line)
        eval_end = strrchr (ptr, ',');
        eval_end = strrchr (ptr, ',');
 
 
      /* Replace triple double quotes with bounding quote/escapes.  */
      /* Replace triple double quotes with bounding quote/escapes.  */
      if (current_char == '"' && ptr[1] == '"' && ptr[2] == '"')
      if (current_char == '"' && ptr[1] == '"' && ptr[2] == '"')
        {
        {
          ptr[1] = '\\';
          ptr[1] = '\\';
          tmp = strstr (ptr + 2, "\"\"\"");
          tmp = strstr (ptr + 2, "\"\"\"");
          if (tmp)
          if (tmp)
            tmp[0] = '\\';
            tmp[0] = '\\';
          changed = 1;
          changed = 1;
        }
        }
 
 
      /* Replace a single '=' with a '==';
      /* Replace a single '=' with a '==';
         for compatibility with older code only.  */
         for compatibility with older code only.  */
      if (line_conditional && current_char == '=')
      if (line_conditional && current_char == '=')
        {
        {
          if (ptr[1] == '=')
          if (ptr[1] == '=')
            {
            {
              ptr += 2;
              ptr += 2;
              continue;
              continue;
            }
            }
          *ptr++ = '\0';
          *ptr++ = '\0';
          tmp = xmalloc (strlen (head) + 2 + strlen (ptr) + 1);
          tmp = xmalloc (strlen (head) + 2 + strlen (ptr) + 1);
          sprintf (tmp, "%s==%s", head, ptr);
          sprintf (tmp, "%s==%s", head, ptr);
          /* Continue examining after the '=='.  */
          /* Continue examining after the '=='.  */
          ptr = tmp + strlen (head) + 2;
          ptr = tmp + strlen (head) + 2;
          free (replacement);
          free (replacement);
          head = replacement = tmp;
          head = replacement = tmp;
          changed = 1;
          changed = 1;
        }
        }
 
 
      /* Flag when we've reached the symbol part of .eval/.asg.  */
      /* Flag when we've reached the symbol part of .eval/.asg.  */
      if (eval_line && ptr >= eval_end)
      if (eval_line && ptr >= eval_end)
        eval_symbol = 1;
        eval_symbol = 1;
 
 
      /* For each apparent symbol, see if it's a substitution symbol, and if
      /* For each apparent symbol, see if it's a substitution symbol, and if
         so, replace it in the input.  */
         so, replace it in the input.  */
      if ((forced && current_char == ':')
      if ((forced && current_char == ':')
          || (!forced && is_name_beginner (current_char)))
          || (!forced && is_name_beginner (current_char)))
        {
        {
          char *name; /* Symbol to be replaced.  */
          char *name; /* Symbol to be replaced.  */
          char *savedp = input_line_pointer;
          char *savedp = input_line_pointer;
          int c;
          int c;
          char *value = NULL;
          char *value = NULL;
          char *tail; /* Rest of line after symbol.  */
          char *tail; /* Rest of line after symbol.  */
 
 
          /* Skip the colon.  */
          /* Skip the colon.  */
          if (forced)
          if (forced)
            ++ptr;
            ++ptr;
 
 
          name = input_line_pointer = ptr;
          name = input_line_pointer = ptr;
          c = get_symbol_end ();
          c = get_symbol_end ();
          /* '?' is not normally part of a symbol, but it IS part of a local
          /* '?' is not normally part of a symbol, but it IS part of a local
             label.  */
             label.  */
          if (c == '?')
          if (c == '?')
            {
            {
              *input_line_pointer++ = c;
              *input_line_pointer++ = c;
              c = *input_line_pointer;
              c = *input_line_pointer;
              *input_line_pointer = '\0';
              *input_line_pointer = '\0';
            }
            }
          /* Avoid infinite recursion; if a symbol shows up a second time for
          /* Avoid infinite recursion; if a symbol shows up a second time for
             substitution, leave it as is.  */
             substitution, leave it as is.  */
          if (hash_find (subsym_recurse_hash, name) == NULL)
          if (hash_find (subsym_recurse_hash, name) == NULL)
            value = subsym_lookup (name, macro_level);
            value = subsym_lookup (name, macro_level);
          else
          else
            as_warn (_("%s symbol recursion stopped at "
            as_warn (_("%s symbol recursion stopped at "
                       "second appearance of '%s'"),
                       "second appearance of '%s'"),
                     forced ? "Forced substitution" : "Substitution", name);
                     forced ? "Forced substitution" : "Substitution", name);
          ptr = tail = input_line_pointer;
          ptr = tail = input_line_pointer;
          input_line_pointer = savedp;
          input_line_pointer = savedp;
 
 
          /* Check for local labels; replace them with the appropriate
          /* Check for local labels; replace them with the appropriate
             substitution.  */
             substitution.  */
          if ((*name == '$' && ISDIGIT (name[1]) && name[2] == '\0')
          if ((*name == '$' && ISDIGIT (name[1]) && name[2] == '\0')
              || name[strlen (name) - 1] == '?')
              || name[strlen (name) - 1] == '?')
            {
            {
              /* Use an existing identifier for that label if, available, or
              /* Use an existing identifier for that label if, available, or
                 create a new, unique identifier.  */
                 create a new, unique identifier.  */
              value = hash_find (local_label_hash[macro_level], name);
              value = hash_find (local_label_hash[macro_level], name);
              if (value == NULL)
              if (value == NULL)
                {
                {
                  char digit[11];
                  char digit[11];
                  char *namecopy = strcpy (xmalloc (strlen (name) + 1), name);
                  char *namecopy = strcpy (xmalloc (strlen (name) + 1), name);
 
 
                  value = strcpy (xmalloc (strlen (name) + sizeof (digit) + 1),
                  value = strcpy (xmalloc (strlen (name) + sizeof (digit) + 1),
                                  name);
                                  name);
                  if (*value != '$')
                  if (*value != '$')
                    value[strlen (value) - 1] = '\0';
                    value[strlen (value) - 1] = '\0';
                  sprintf (digit, ".%d", local_label_id++);
                  sprintf (digit, ".%d", local_label_id++);
                  strcat (value, digit);
                  strcat (value, digit);
                  hash_insert (local_label_hash[macro_level], namecopy, value);
                  hash_insert (local_label_hash[macro_level], namecopy, value);
                }
                }
              /* Indicate where to continue looking for substitutions.  */
              /* Indicate where to continue looking for substitutions.  */
              ptr = tail;
              ptr = tail;
            }
            }
          /* Check for built-in subsym and math functions.  */
          /* Check for built-in subsym and math functions.  */
          else if (value != NULL && *name == '$')
          else if (value != NULL && *name == '$')
            {
            {
              subsym_proc_entry *entry = (subsym_proc_entry *) value;
              subsym_proc_entry *entry = (subsym_proc_entry *) value;
              math_proc_entry *math_entry = hash_find (math_hash, name);
              math_proc_entry *math_entry = hash_find (math_hash, name);
              char *arg1, *arg2 = NULL;
              char *arg1, *arg2 = NULL;
 
 
              *ptr = c;
              *ptr = c;
              if (entry == NULL)
              if (entry == NULL)
                {
                {
                  as_bad (_("Unrecognized substitution symbol function"));
                  as_bad (_("Unrecognized substitution symbol function"));
                  break;
                  break;
                }
                }
              else if (*ptr != '(')
              else if (*ptr != '(')
                {
                {
                  as_bad (_("Missing '(' after substitution symbol function"));
                  as_bad (_("Missing '(' after substitution symbol function"));
                  break;
                  break;
                }
                }
              ++ptr;
              ++ptr;
              if (math_entry != NULL)
              if (math_entry != NULL)
                {
                {
                  float arg1, arg2 = 0;
                  float arg1, arg2 = 0;
                  volatile float fresult;
                  volatile float fresult;
 
 
                  arg1 = (float) strtod (ptr, &ptr);
                  arg1 = (float) strtod (ptr, &ptr);
                  if (math_entry->nargs == 2)
                  if (math_entry->nargs == 2)
                    {
                    {
                      if (*ptr++ != ',')
                      if (*ptr++ != ',')
                        {
                        {
                          as_bad (_("Expecting second argument"));
                          as_bad (_("Expecting second argument"));
                          break;
                          break;
                        }
                        }
                      arg2 = (float) strtod (ptr, &ptr);
                      arg2 = (float) strtod (ptr, &ptr);
                    }
                    }
                  fresult = (*math_entry->proc) (arg1, arg2);
                  fresult = (*math_entry->proc) (arg1, arg2);
                  value = xmalloc (128);
                  value = xmalloc (128);
                  if (math_entry->int_return)
                  if (math_entry->int_return)
                    sprintf (value, "%d", (int) fresult);
                    sprintf (value, "%d", (int) fresult);
                  else
                  else
                    sprintf (value, "%f", fresult);
                    sprintf (value, "%f", fresult);
                  if (*ptr++ != ')')
                  if (*ptr++ != ')')
                    {
                    {
                      as_bad (_("Extra junk in function call, expecting ')'"));
                      as_bad (_("Extra junk in function call, expecting ')'"));
                      break;
                      break;
                    }
                    }
                  /* Don't bother recursing; the replacement isn't a
                  /* Don't bother recursing; the replacement isn't a
                     symbol.  */
                     symbol.  */
                  recurse = 0;
                  recurse = 0;
                }
                }
              else
              else
                {
                {
                  int val;
                  int val;
                  int arg_type[2] = { *ptr == '"' , 0 };
                  int arg_type[2] = { *ptr == '"' , 0 };
                  int ismember = !strcmp (entry->name, "$ismember");
                  int ismember = !strcmp (entry->name, "$ismember");
 
 
                  /* Parse one or two args, which must be a substitution
                  /* Parse one or two args, which must be a substitution
                     symbol, string or a character-string constant.  */
                     symbol, string or a character-string constant.  */
                  /* For all functions, a string or substitution symbol may be
                  /* For all functions, a string or substitution symbol may be
                     used, with the following exceptions:
                     used, with the following exceptions:
                     firstch/lastch: 2nd arg must be character constant
                     firstch/lastch: 2nd arg must be character constant
                     ismember: both args must be substitution symbols.  */
                     ismember: both args must be substitution symbols.  */
                  ptr = subsym_get_arg (ptr, ",)", &arg1, ismember);
                  ptr = subsym_get_arg (ptr, ",)", &arg1, ismember);
                  if (!arg1)
                  if (!arg1)
                    break;
                    break;
                  if (entry->nargs == 2)
                  if (entry->nargs == 2)
                    {
                    {
                      if (*ptr++ != ',')
                      if (*ptr++ != ',')
                        {
                        {
                          as_bad (_("Function expects two arguments"));
                          as_bad (_("Function expects two arguments"));
                          break;
                          break;
                        }
                        }
                      /* Character constants are converted to numerics
                      /* Character constants are converted to numerics
                         by the preprocessor.  */
                         by the preprocessor.  */
                      arg_type[1] = (ISDIGIT (*ptr)) ? 2 : (*ptr == '"');
                      arg_type[1] = (ISDIGIT (*ptr)) ? 2 : (*ptr == '"');
                      ptr = subsym_get_arg (ptr, ")", &arg2, ismember);
                      ptr = subsym_get_arg (ptr, ")", &arg2, ismember);
                    }
                    }
                  /* Args checking.  */
                  /* Args checking.  */
                  if ((!strcmp (entry->name, "$firstch")
                  if ((!strcmp (entry->name, "$firstch")
                       || !strcmp (entry->name, "$lastch"))
                       || !strcmp (entry->name, "$lastch"))
                      && arg_type[1] != 2)
                      && arg_type[1] != 2)
                    {
                    {
                      as_bad (_("Expecting character constant argument"));
                      as_bad (_("Expecting character constant argument"));
                      break;
                      break;
                    }
                    }
                  if (ismember
                  if (ismember
                      && (arg_type[0] != 0 || arg_type[1] != 0))
                      && (arg_type[0] != 0 || arg_type[1] != 0))
                    {
                    {
                      as_bad (_("Both arguments must be substitution symbols"));
                      as_bad (_("Both arguments must be substitution symbols"));
                      break;
                      break;
                    }
                    }
                  if (*ptr++ != ')')
                  if (*ptr++ != ')')
                    {
                    {
                      as_bad (_("Extra junk in function call, expecting ')'"));
                      as_bad (_("Extra junk in function call, expecting ')'"));
                      break;
                      break;
                    }
                    }
                  val = (*entry->proc) (arg1, arg2);
                  val = (*entry->proc) (arg1, arg2);
                  value = xmalloc (64);
                  value = xmalloc (64);
                  sprintf (value, "%d", val);
                  sprintf (value, "%d", val);
                }
                }
              /* Fix things up to replace the entire expression, not just the
              /* Fix things up to replace the entire expression, not just the
                 function name.  */
                 function name.  */
              tail = ptr;
              tail = ptr;
              c = *tail;
              c = *tail;
            }
            }
 
 
          if (value != NULL && !eval_symbol)
          if (value != NULL && !eval_symbol)
            {
            {
              /* Replace the symbol with its string replacement and
              /* Replace the symbol with its string replacement and
                 continue.  Recursively replace VALUE until either no
                 continue.  Recursively replace VALUE until either no
                 substitutions are performed, or a substitution that has been
                 substitutions are performed, or a substitution that has been
                 previously made is encountered again.
                 previously made is encountered again.
 
 
                 put the symbol into the recursion hash table so we only
                 put the symbol into the recursion hash table so we only
                 try to replace a symbol once.  */
                 try to replace a symbol once.  */
              if (recurse)
              if (recurse)
                {
                {
                  hash_insert (subsym_recurse_hash, name, name);
                  hash_insert (subsym_recurse_hash, name, name);
                  value = subsym_substitute (value, macro_level > 0);
                  value = subsym_substitute (value, macro_level > 0);
                  hash_delete (subsym_recurse_hash, name);
                  hash_delete (subsym_recurse_hash, name);
                }
                }
 
 
              /* Temporarily zero-terminate where the symbol started.  */
              /* Temporarily zero-terminate where the symbol started.  */
              *name = 0;
              *name = 0;
              if (forced)
              if (forced)
                {
                {
                  if (c == '(')
                  if (c == '(')
                    {
                    {
                      /* Subscripted substitution symbol -- use just the
                      /* Subscripted substitution symbol -- use just the
                         indicated portion of the string; the description
                         indicated portion of the string; the description
                         kinda indicates that forced substitution is not
                         kinda indicates that forced substitution is not
                         supposed to be recursive, but I'm not sure.  */
                         supposed to be recursive, but I'm not sure.  */
                      unsigned beg, len = 1; /* default to a single char */
                      unsigned beg, len = 1; /* default to a single char */
                      char *newval = strcpy (xmalloc (strlen (value) + 1),
                      char *newval = strcpy (xmalloc (strlen (value) + 1),
                                             value);
                                             value);
 
 
                      savedp = input_line_pointer;
                      savedp = input_line_pointer;
                      input_line_pointer = tail + 1;
                      input_line_pointer = tail + 1;
                      beg = get_absolute_expression ();
                      beg = get_absolute_expression ();
                      if (beg < 1)
                      if (beg < 1)
                        {
                        {
                          as_bad (_("Invalid subscript (use 1 to %d)"),
                          as_bad (_("Invalid subscript (use 1 to %d)"),
                                  (int) strlen (value));
                                  (int) strlen (value));
                          break;
                          break;
                        }
                        }
                      if (*input_line_pointer == ',')
                      if (*input_line_pointer == ',')
                        {
                        {
                          ++input_line_pointer;
                          ++input_line_pointer;
                          len = get_absolute_expression ();
                          len = get_absolute_expression ();
                          if (beg + len > strlen (value))
                          if (beg + len > strlen (value))
                            {
                            {
                              as_bad (_("Invalid length (use 0 to %d"),
                              as_bad (_("Invalid length (use 0 to %d"),
                                      (int) strlen (value) - beg);
                                      (int) strlen (value) - beg);
                              break;
                              break;
                            }
                            }
                        }
                        }
                      newval += beg - 1;
                      newval += beg - 1;
                      newval[len] = 0;
                      newval[len] = 0;
                      tail = input_line_pointer;
                      tail = input_line_pointer;
                      if (*tail++ != ')')
                      if (*tail++ != ')')
                        {
                        {
                          as_bad (_("Missing ')' in subscripted substitution "
                          as_bad (_("Missing ')' in subscripted substitution "
                                    "symbol expression"));
                                    "symbol expression"));
                          break;
                          break;
                        }
                        }
                      c = *tail;
                      c = *tail;
                      input_line_pointer = savedp;
                      input_line_pointer = savedp;
 
 
                      value = newval;
                      value = newval;
                    }
                    }
                  name[-1] = 0;
                  name[-1] = 0;
                }
                }
              tmp = xmalloc (strlen (head) + strlen (value) +
              tmp = xmalloc (strlen (head) + strlen (value) +
                             strlen (tail + 1) + 2);
                             strlen (tail + 1) + 2);
              strcpy (tmp, head);
              strcpy (tmp, head);
              strcat (tmp, value);
              strcat (tmp, value);
              /* Make sure forced substitutions are properly terminated.  */
              /* Make sure forced substitutions are properly terminated.  */
              if (forced)
              if (forced)
                {
                {
                  if (c != ':')
                  if (c != ':')
                    {
                    {
                      as_bad (_("Missing forced substitution terminator ':'"));
                      as_bad (_("Missing forced substitution terminator ':'"));
                      break;
                      break;
                    }
                    }
                  ++tail;
                  ++tail;
                }
                }
              else
              else
                /* Restore the character after the symbol end.  */
                /* Restore the character after the symbol end.  */
                *tail = c;
                *tail = c;
              strcat (tmp, tail);
              strcat (tmp, tail);
              /* Continue examining after the replacement value.  */
              /* Continue examining after the replacement value.  */
              ptr = tmp + strlen (head) + strlen (value);
              ptr = tmp + strlen (head) + strlen (value);
              free (replacement);
              free (replacement);
              head = replacement = tmp;
              head = replacement = tmp;
              changed = 1;
              changed = 1;
            }
            }
          else
          else
            *ptr = c;
            *ptr = c;
        }
        }
      else
      else
        {
        {
          ++ptr;
          ++ptr;
        }
        }
    }
    }
 
 
  if (changed)
  if (changed)
    return replacement;
    return replacement;
  else
  else
    return line;
    return line;
}
}
 
 
/* We use this to handle substitution symbols
/* We use this to handle substitution symbols
   hijack input_line_pointer, replacing it with our substituted string.
   hijack input_line_pointer, replacing it with our substituted string.
 
 
   .sslist should enable listing the line after replacements are made...
   .sslist should enable listing the line after replacements are made...
 
 
   returns the new buffer limit.  */
   returns the new buffer limit.  */
 
 
void
void
tic54x_start_line_hook ()
tic54x_start_line_hook ()
{
{
  char *line, *endp;
  char *line, *endp;
  char *replacement = NULL;
  char *replacement = NULL;
 
 
  /* Work with a copy of the input line, including EOL char.  */
  /* Work with a copy of the input line, including EOL char.  */
  endp = input_line_pointer;
  endp = input_line_pointer;
  while (!is_end_of_line[(int) *endp++])
  while (!is_end_of_line[(int) *endp++])
    ;
    ;
  line = xmalloc (endp - input_line_pointer + 1);
  line = xmalloc (endp - input_line_pointer + 1);
  strncpy (line, input_line_pointer, endp - input_line_pointer + 1);
  strncpy (line, input_line_pointer, endp - input_line_pointer + 1);
  line[endp - input_line_pointer] = 0;
  line[endp - input_line_pointer] = 0;
 
 
  /* Scan ahead for parallel insns.  */
  /* Scan ahead for parallel insns.  */
  parallel_on_next_line_hint = next_line_shows_parallel (endp + 1);
  parallel_on_next_line_hint = next_line_shows_parallel (endp + 1);
 
 
  /* If within a macro, first process forced replacements.  */
  /* If within a macro, first process forced replacements.  */
  if (macro_level > 0)
  if (macro_level > 0)
    replacement = subsym_substitute (line, 1);
    replacement = subsym_substitute (line, 1);
  else
  else
    replacement = line;
    replacement = line;
  replacement = subsym_substitute (replacement, 0);
  replacement = subsym_substitute (replacement, 0);
 
 
  if (replacement != line)
  if (replacement != line)
    {
    {
      char *tmp = replacement;
      char *tmp = replacement;
      char *comment = strchr (replacement, ';');
      char *comment = strchr (replacement, ';');
      char endc = replacement[strlen (replacement) - 1];
      char endc = replacement[strlen (replacement) - 1];
 
 
      /* Clean up the replacement; we'd prefer to have this done by the
      /* Clean up the replacement; we'd prefer to have this done by the
         standard preprocessing equipment (maybe do_scrub_chars?)
         standard preprocessing equipment (maybe do_scrub_chars?)
         but for now, do a quick-and-dirty.  */
         but for now, do a quick-and-dirty.  */
      if (comment != NULL)
      if (comment != NULL)
        {
        {
          comment[0] = endc;
          comment[0] = endc;
          comment[1] = 0;
          comment[1] = 0;
          --comment;
          --comment;
        }
        }
      else
      else
        comment = replacement + strlen (replacement) - 1;
        comment = replacement + strlen (replacement) - 1;
 
 
      /* Trim trailing whitespace.  */
      /* Trim trailing whitespace.  */
      while (ISSPACE (*comment))
      while (ISSPACE (*comment))
        {
        {
          comment[0] = endc;
          comment[0] = endc;
          comment[1] = 0;
          comment[1] = 0;
          --comment;
          --comment;
        }
        }
 
 
      /* Compact leading whitespace.  */
      /* Compact leading whitespace.  */
      while (ISSPACE (tmp[0]) && ISSPACE (tmp[1]))
      while (ISSPACE (tmp[0]) && ISSPACE (tmp[1]))
        ++tmp;
        ++tmp;
 
 
      input_line_pointer = endp;
      input_line_pointer = endp;
      input_scrub_insert_line (tmp);
      input_scrub_insert_line (tmp);
      free (replacement);
      free (replacement);
      free (line);
      free (line);
      /* Keep track of whether we've done a substitution.  */
      /* Keep track of whether we've done a substitution.  */
      substitution_line = 1;
      substitution_line = 1;
    }
    }
  else
  else
    {
    {
      /* No change.  */
      /* No change.  */
      free (line);
      free (line);
      substitution_line = 0;
      substitution_line = 0;
    }
    }
}
}
 
 
/* This is the guts of the machine-dependent assembler.  STR points to a
/* This is the guts of the machine-dependent assembler.  STR 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 (line)
md_assemble (line)
     char *line;
     char *line;
{
{
  static int repeat_slot = 0;
  static int repeat_slot = 0;
  static int delay_slots = 0; /* How many delay slots left to fill?  */
  static int delay_slots = 0; /* How many delay slots left to fill?  */
  static int is_parallel = 0;
  static int is_parallel = 0;
  static tic54x_insn insn;
  static tic54x_insn insn;
  char *lptr;
  char *lptr;
  char *savedp = input_line_pointer;
  char *savedp = input_line_pointer;
  int c;
  int c;
 
 
  input_line_pointer = line;
  input_line_pointer = line;
  c = get_symbol_end ();
  c = get_symbol_end ();
 
 
  if (cpu == VNONE)
  if (cpu == VNONE)
    cpu = V542;
    cpu = V542;
  if (address_mode_needs_set)
  if (address_mode_needs_set)
    {
    {
      set_address_mode (amode);
      set_address_mode (amode);
      address_mode_needs_set = 0;
      address_mode_needs_set = 0;
    }
    }
  if (cpu_needs_set)
  if (cpu_needs_set)
    {
    {
      set_cpu (cpu);
      set_cpu (cpu);
      cpu_needs_set = 0;
      cpu_needs_set = 0;
    }
    }
  assembly_begun = 1;
  assembly_begun = 1;
 
 
  if (is_parallel)
  if (is_parallel)
    {
    {
      is_parallel = 0;
      is_parallel = 0;
 
 
      strcpy (insn.parmnemonic, line);
      strcpy (insn.parmnemonic, line);
      lptr = input_line_pointer;
      lptr = input_line_pointer;
      *lptr = c;
      *lptr = c;
      input_line_pointer = savedp;
      input_line_pointer = savedp;
 
 
      if (tic54x_parse_parallel_insn_lastline (&insn, lptr))
      if (tic54x_parse_parallel_insn_lastline (&insn, lptr))
        {
        {
          int words = build_insn (&insn);
          int words = build_insn (&insn);
 
 
          if (delay_slots != 0)
          if (delay_slots != 0)
            {
            {
              if (words > delay_slots)
              if (words > delay_slots)
                {
                {
                  as_bad (_("Instruction does not fit in available delay "
                  as_bad (_("Instruction does not fit in available delay "
                            "slots (%d-word insn, %d slots left)"),
                            "slots (%d-word insn, %d slots left)"),
                          words, delay_slots);
                          words, delay_slots);
                  delay_slots = 0;
                  delay_slots = 0;
                  return;
                  return;
                }
                }
              delay_slots -= words;
              delay_slots -= words;
            }
            }
        }
        }
      return;
      return;
    }
    }
 
 
  memset (&insn, 0, sizeof (insn));
  memset (&insn, 0, sizeof (insn));
  strcpy (insn.mnemonic, line);
  strcpy (insn.mnemonic, line);
  lptr = input_line_pointer;
  lptr = input_line_pointer;
  *lptr = c;
  *lptr = c;
  input_line_pointer = savedp;
  input_line_pointer = savedp;
 
 
  /* See if this line is part of a parallel instruction; if so, either this
  /* See if this line is part of a parallel instruction; if so, either this
     line or the next line will have the "||" specifier preceding the
     line or the next line will have the "||" specifier preceding the
     mnemonic, and we look for it in the parallel insn hash table.  */
     mnemonic, and we look for it in the parallel insn hash table.  */
  if (strstr (line, "||") != NULL || parallel_on_next_line_hint)
  if (strstr (line, "||") != NULL || parallel_on_next_line_hint)
    {
    {
      char *tmp = strstr (line, "||");
      char *tmp = strstr (line, "||");
      if (tmp != NULL)
      if (tmp != NULL)
        *tmp = '\0';
        *tmp = '\0';
 
 
      if (tic54x_parse_parallel_insn_firstline (&insn, lptr))
      if (tic54x_parse_parallel_insn_firstline (&insn, lptr))
        {
        {
          is_parallel = 1;
          is_parallel = 1;
          /* If the parallel part is on the same line, process it now,
          /* If the parallel part is on the same line, process it now,
             otherwise let the assembler pick up the next line for us.  */
             otherwise let the assembler pick up the next line for us.  */
          if (tmp != NULL)
          if (tmp != NULL)
            {
            {
              while (ISSPACE (tmp[2]))
              while (ISSPACE (tmp[2]))
                ++tmp;
                ++tmp;
              md_assemble (tmp + 2);
              md_assemble (tmp + 2);
            }
            }
        }
        }
      else
      else
        {
        {
          as_bad (_("Unrecognized parallel instruction '%s'"), line);
          as_bad (_("Unrecognized parallel instruction '%s'"), line);
        }
        }
      return;
      return;
    }
    }
 
 
  if (tic54x_parse_insn (&insn, lptr))
  if (tic54x_parse_insn (&insn, lptr))
    {
    {
      int words;
      int words;
 
 
      if ((insn.tm->flags & FL_LP)
      if ((insn.tm->flags & FL_LP)
          && cpu != V545LP && cpu != V546LP)
          && cpu != V545LP && cpu != V546LP)
        {
        {
          as_bad (_("Instruction '%s' requires an LP cpu version"),
          as_bad (_("Instruction '%s' requires an LP cpu version"),
                  insn.tm->name);
                  insn.tm->name);
          return;
          return;
        }
        }
      if ((insn.tm->flags & FL_FAR)
      if ((insn.tm->flags & FL_FAR)
          && amode != far_mode)
          && amode != far_mode)
        {
        {
          as_bad (_("Instruction '%s' requires far mode addressing"),
          as_bad (_("Instruction '%s' requires far mode addressing"),
                  insn.tm->name);
                  insn.tm->name);
          return;
          return;
        }
        }
 
 
      words = build_insn (&insn);
      words = build_insn (&insn);
 
 
      /* Is this instruction in a delay slot?  */
      /* Is this instruction in a delay slot?  */
      if (delay_slots)
      if (delay_slots)
        {
        {
          if (words > delay_slots)
          if (words > delay_slots)
            {
            {
              as_warn (_("Instruction does not fit in available delay "
              as_warn (_("Instruction does not fit in available delay "
                         "slots (%d-word insn, %d slots left). "
                         "slots (%d-word insn, %d slots left). "
                         "Resulting behavior is undefined."),
                         "Resulting behavior is undefined."),
                       words, delay_slots);
                       words, delay_slots);
              delay_slots = 0;
              delay_slots = 0;
              return;
              return;
            }
            }
          /* Branches in delay slots are not allowed.  */
          /* Branches in delay slots are not allowed.  */
          if (insn.tm->flags & FL_BMASK)
          if (insn.tm->flags & FL_BMASK)
            {
            {
              as_warn (_("Instructions which cause PC discontinuity are not "
              as_warn (_("Instructions which cause PC discontinuity are not "
                         "allowed in a delay slot. "
                         "allowed in a delay slot. "
                         "Resulting behavior is undefined."));
                         "Resulting behavior is undefined."));
            }
            }
          delay_slots -= words;
          delay_slots -= words;
        }
        }
 
 
      /* Is this instruction the target of a repeat?  */
      /* Is this instruction the target of a repeat?  */
      if (repeat_slot)
      if (repeat_slot)
        {
        {
          if (insn.tm->flags & FL_NR)
          if (insn.tm->flags & FL_NR)
            as_warn (_("'%s' is not repeatable. "
            as_warn (_("'%s' is not repeatable. "
                       "Resulting behavior is undefined."),
                       "Resulting behavior is undefined."),
                     insn.tm->name);
                     insn.tm->name);
          else if (insn.is_lkaddr)
          else if (insn.is_lkaddr)
            as_warn (_("Instructions using long offset modifiers or absolute "
            as_warn (_("Instructions using long offset modifiers or absolute "
                       "addresses are not repeatable. "
                       "addresses are not repeatable. "
                       "Resulting behavior is undefined."));
                       "Resulting behavior is undefined."));
          repeat_slot = 0;
          repeat_slot = 0;
        }
        }
 
 
      /* Make sure we check the target of a repeat instruction.  */
      /* Make sure we check the target of a repeat instruction.  */
      if (insn.tm->flags & B_REPEAT)
      if (insn.tm->flags & B_REPEAT)
        {
        {
          repeat_slot = 1;
          repeat_slot = 1;
          /* FIXME -- warn if repeat_slot == 1 at EOF.  */
          /* FIXME -- warn if repeat_slot == 1 at EOF.  */
        }
        }
      /* Make sure we check our delay slots for validity.  */
      /* Make sure we check our delay slots for validity.  */
      if (insn.tm->flags & FL_DELAY)
      if (insn.tm->flags & FL_DELAY)
        {
        {
          delay_slots = 2;
          delay_slots = 2;
          /* FIXME -- warn if delay_slots != 0 at EOF.  */
          /* FIXME -- warn if delay_slots != 0 at EOF.  */
        }
        }
    }
    }
}
}
 
 
/* Do a final adjustment on the symbol table; in this case, make sure we have
/* Do a final adjustment on the symbol table; in this case, make sure we have
   a ".file" symbol.  */
   a ".file" symbol.  */
 
 
void
void
tic54x_adjust_symtab ()
tic54x_adjust_symtab ()
{
{
  if (symbol_rootP == NULL
  if (symbol_rootP == NULL
      || S_GET_STORAGE_CLASS (symbol_rootP) != C_FILE)
      || S_GET_STORAGE_CLASS (symbol_rootP) != C_FILE)
    {
    {
      char *filename;
      char *filename;
      unsigned lineno;
      unsigned lineno;
      as_where (&filename, &lineno);
      as_where (&filename, &lineno);
      c_dot_file_symbol (filename, 0);
      c_dot_file_symbol (filename, 0);
    }
    }
}
}
 
 
/* In order to get gas to ignore any | chars at the start of a line,
/* In order to get gas to ignore any | chars at the start of a line,
   this function returns true if a | is found in a line.
   this function returns true if a | is found in a line.
   This lets us process parallel instructions, which span two lines.  */
   This lets us process parallel instructions, which span two lines.  */
 
 
int
int
tic54x_unrecognized_line (int c)
tic54x_unrecognized_line (int c)
{
{
  return c == PARALLEL_SEPARATOR;
  return c == PARALLEL_SEPARATOR;
}
}
 
 
/* Watch for local labels of the form $[0-9] and [_a-zA-Z][_a-zA-Z0-9]*?
/* Watch for local labels of the form $[0-9] and [_a-zA-Z][_a-zA-Z0-9]*?
   Encode their names so that only we see them and can map them to the
   Encode their names so that only we see them and can map them to the
   appropriate places.
   appropriate places.
   FIXME -- obviously this isn't done yet.  These locals still show up in the
   FIXME -- obviously this isn't done yet.  These locals still show up in the
   symbol table.  */
   symbol table.  */
void
void
tic54x_define_label (sym)
tic54x_define_label (sym)
     symbolS *sym;
     symbolS *sym;
{
{
  /* Just in case we need this later; note that this is not necessarily the
  /* Just in case we need this later; note that this is not necessarily the
     same thing as line_label...
     same thing as line_label...
     When aligning or assigning labels to fields, sometimes the label is
     When aligning or assigning labels to fields, sometimes the label is
     assigned other than the address at which the label appears.
     assigned other than the address at which the label appears.
     FIXME -- is this really needed? I think all the proper label assignment
     FIXME -- is this really needed? I think all the proper label assignment
     is done in tic54x_cons.  */
     is done in tic54x_cons.  */
  last_label_seen = sym;
  last_label_seen = sym;
}
}
 
 
/* Try to parse something that normal parsing failed at.  */
/* Try to parse something that normal parsing failed at.  */
 
 
symbolS *
symbolS *
tic54x_undefined_symbol (name)
tic54x_undefined_symbol (name)
     char *name;
     char *name;
{
{
  symbol *sym;
  symbol *sym;
 
 
  /* Not sure how to handle predefined symbols.  */
  /* Not sure how to handle predefined symbols.  */
  if ((sym = (symbol *) hash_find (cc_hash, name)) != NULL ||
  if ((sym = (symbol *) hash_find (cc_hash, name)) != NULL ||
      (sym = (symbol *) hash_find (cc2_hash, name)) != NULL ||
      (sym = (symbol *) hash_find (cc2_hash, name)) != NULL ||
      (sym = (symbol *) hash_find (cc3_hash, name)) != NULL ||
      (sym = (symbol *) hash_find (cc3_hash, name)) != NULL ||
      (sym = (symbol *) hash_find (misc_symbol_hash, name)) != NULL ||
      (sym = (symbol *) hash_find (misc_symbol_hash, name)) != NULL ||
      (sym = (symbol *) hash_find (sbit_hash, name)) != NULL)
      (sym = (symbol *) hash_find (sbit_hash, name)) != NULL)
    {
    {
      return symbol_new (name, reg_section,
      return symbol_new (name, reg_section,
                         (valueT) sym->value,
                         (valueT) sym->value,
                         &zero_address_frag);
                         &zero_address_frag);
    }
    }
 
 
  if ((sym = (symbol *) hash_find (reg_hash, name)) != NULL ||
  if ((sym = (symbol *) hash_find (reg_hash, name)) != NULL ||
      (sym = (symbol *) hash_find (mmreg_hash, name)) != NULL ||
      (sym = (symbol *) hash_find (mmreg_hash, name)) != NULL ||
      !strcasecmp (name, "a") || !strcasecmp (name, "b"))
      !strcasecmp (name, "a") || !strcasecmp (name, "b"))
    {
    {
      return symbol_new (name, reg_section,
      return symbol_new (name, reg_section,
                         (valueT) sym ? sym->value : 0,
                         (valueT) sym ? sym->value : 0,
                         &zero_address_frag);
                         &zero_address_frag);
    }
    }
 
 
  return NULL;
  return NULL;
}
}
 
 
/* Parse a name in an expression before the expression parser takes a stab at
/* Parse a name in an expression before the expression parser takes a stab at
   it.  */
   it.  */
 
 
int
int
tic54x_parse_name (name, exp)
tic54x_parse_name (name, exp)
     char *name ATTRIBUTE_UNUSED;
     char *name ATTRIBUTE_UNUSED;
     expressionS *exp ATTRIBUTE_UNUSED;
     expressionS *exp ATTRIBUTE_UNUSED;
{
{
  return 0;
  return 0;
}
}
 
 
char *
char *
md_atof (int type, char *literalP, int *sizeP)
md_atof (int type, char *literalP, int *sizeP)
{
{
  /* Target data is little-endian, but floats are stored
  /* Target data is little-endian, but floats are stored
     big-"word"ian.  ugh.  */
     big-"word"ian.  ugh.  */
  return ieee_md_atof (type, literalP, sizeP, TRUE);
  return ieee_md_atof (type, literalP, sizeP, TRUE);
}
}
 
 
arelent *
arelent *
tc_gen_reloc (section, fixP)
tc_gen_reloc (section, fixP)
     asection *section;
     asection *section;
     fixS *fixP;
     fixS *fixP;
{
{
  arelent *rel;
  arelent *rel;
  bfd_reloc_code_real_type code = fixP->fx_r_type;
  bfd_reloc_code_real_type code = fixP->fx_r_type;
  asymbol *sym = symbol_get_bfdsym (fixP->fx_addsy);
  asymbol *sym = symbol_get_bfdsym (fixP->fx_addsy);
 
 
  rel = (arelent *) xmalloc (sizeof (arelent));
  rel = (arelent *) xmalloc (sizeof (arelent));
  rel->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
  rel->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
  *rel->sym_ptr_ptr = sym;
  *rel->sym_ptr_ptr = sym;
  /* We assume that all rel->address are host byte offsets.  */
  /* We assume that all rel->address are host byte offsets.  */
  rel->address = fixP->fx_frag->fr_address + fixP->fx_where;
  rel->address = fixP->fx_frag->fr_address + fixP->fx_where;
  rel->address /= OCTETS_PER_BYTE;
  rel->address /= OCTETS_PER_BYTE;
  rel->howto = bfd_reloc_type_lookup (stdoutput, code);
  rel->howto = bfd_reloc_type_lookup (stdoutput, code);
  if (!strcmp (sym->name, section->name))
  if (!strcmp (sym->name, section->name))
    rel->howto += HOWTO_BANK;
    rel->howto += HOWTO_BANK;
 
 
  if (!rel->howto)
  if (!rel->howto)
    {
    {
      const char *name = S_GET_NAME (fixP->fx_addsy);
      const char *name = S_GET_NAME (fixP->fx_addsy);
      if (name == NULL)
      if (name == NULL)
        name = "<unknown>";
        name = "<unknown>";
      as_fatal ("Cannot generate relocation type for symbol %s, code %s",
      as_fatal ("Cannot generate relocation type for symbol %s, code %s",
                name, bfd_get_reloc_code_name (code));
                name, bfd_get_reloc_code_name (code));
      return NULL;
      return NULL;
    }
    }
  return rel;
  return rel;
}
}
 
 
/* Handle cons expressions.  */
/* Handle cons expressions.  */
 
 
void
void
tic54x_cons_fix_new (frag, where, octets, exp)
tic54x_cons_fix_new (frag, where, octets, exp)
     fragS *frag;
     fragS *frag;
     int where;
     int where;
     int octets;
     int octets;
     expressionS *exp;
     expressionS *exp;
{
{
  bfd_reloc_code_real_type r;
  bfd_reloc_code_real_type r;
 
 
  switch (octets)
  switch (octets)
    {
    {
    default:
    default:
      as_bad (_("Unsupported relocation size %d"), octets);
      as_bad (_("Unsupported relocation size %d"), octets);
      r = BFD_RELOC_TIC54X_16_OF_23;
      r = BFD_RELOC_TIC54X_16_OF_23;
      break;
      break;
    case 2:
    case 2:
      r = BFD_RELOC_TIC54X_16_OF_23;
      r = BFD_RELOC_TIC54X_16_OF_23;
      break;
      break;
    case 4:
    case 4:
      /* TI assembler always uses this, regardless of addressing mode.  */
      /* TI assembler always uses this, regardless of addressing mode.  */
      if (emitting_long)
      if (emitting_long)
        r = BFD_RELOC_TIC54X_23;
        r = BFD_RELOC_TIC54X_23;
      else
      else
        /* We never want to directly generate this; this is provided for
        /* We never want to directly generate this; this is provided for
           stabs support only.  */
           stabs support only.  */
        r = BFD_RELOC_32;
        r = BFD_RELOC_32;
      break;
      break;
    }
    }
  fix_new_exp (frag, where, octets, exp, 0, r);
  fix_new_exp (frag, where, octets, exp, 0, r);
}
}
 
 
/* Attempt to simplify or even eliminate a fixup.
/* Attempt to simplify or even eliminate a fixup.
   To indicate that a fixup has been eliminated, set fixP->fx_done.
   To indicate that a fixup has been eliminated, set fixP->fx_done.
 
 
   If fixp->fx_addsy is non-NULL, we'll have to generate a reloc entry.   */
   If fixp->fx_addsy is non-NULL, we'll have to generate a reloc entry.   */
 
 
void
void
md_apply_fix (fixP, valP, seg)
md_apply_fix (fixP, valP, seg)
     fixS *fixP;
     fixS *fixP;
     valueT * valP;
     valueT * valP;
     segT seg ATTRIBUTE_UNUSED;
     segT seg ATTRIBUTE_UNUSED;
{
{
  char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
  char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
  valueT val = * valP;
  valueT val = * valP;
 
 
  switch (fixP->fx_r_type)
  switch (fixP->fx_r_type)
    {
    {
    default:
    default:
      as_fatal ("Bad relocation type: 0x%02x", fixP->fx_r_type);
      as_fatal ("Bad relocation type: 0x%02x", fixP->fx_r_type);
      return;
      return;
    case BFD_RELOC_TIC54X_MS7_OF_23:
    case BFD_RELOC_TIC54X_MS7_OF_23:
      val = (val >> 16) & 0x7F;
      val = (val >> 16) & 0x7F;
      /* Fall through.  */
      /* Fall through.  */
    case BFD_RELOC_TIC54X_16_OF_23:
    case BFD_RELOC_TIC54X_16_OF_23:
    case BFD_RELOC_16:
    case BFD_RELOC_16:
      bfd_put_16 (stdoutput, val, buf);
      bfd_put_16 (stdoutput, val, buf);
      /* Indicate what we're actually writing, so that we don't get warnings
      /* Indicate what we're actually writing, so that we don't get warnings
         about exceeding available space.  */
         about exceeding available space.  */
      *valP = val & 0xFFFF;
      *valP = val & 0xFFFF;
      break;
      break;
    case BFD_RELOC_TIC54X_PARTLS7:
    case BFD_RELOC_TIC54X_PARTLS7:
      bfd_put_16 (stdoutput,
      bfd_put_16 (stdoutput,
                  (bfd_get_16 (stdoutput, buf) & 0xFF80) | (val & 0x7F),
                  (bfd_get_16 (stdoutput, buf) & 0xFF80) | (val & 0x7F),
                  buf);
                  buf);
      /* Indicate what we're actually writing, so that we don't get warnings
      /* Indicate what we're actually writing, so that we don't get warnings
         about exceeding available space.  */
         about exceeding available space.  */
      *valP = val & 0x7F;
      *valP = val & 0x7F;
      break;
      break;
    case BFD_RELOC_TIC54X_PARTMS9:
    case BFD_RELOC_TIC54X_PARTMS9:
      /* TI assembler doesn't shift its encoding for relocatable files, and is
      /* TI assembler doesn't shift its encoding for relocatable files, and is
         thus incompatible with this implementation's relocatable files.  */
         thus incompatible with this implementation's relocatable files.  */
      bfd_put_16 (stdoutput,
      bfd_put_16 (stdoutput,
                  (bfd_get_16 (stdoutput, buf) & 0xFE00) | (val >> 7),
                  (bfd_get_16 (stdoutput, buf) & 0xFE00) | (val >> 7),
                  buf);
                  buf);
      break;
      break;
    case BFD_RELOC_32:
    case BFD_RELOC_32:
    case BFD_RELOC_TIC54X_23:
    case BFD_RELOC_TIC54X_23:
      bfd_put_32 (stdoutput,
      bfd_put_32 (stdoutput,
                  (bfd_get_32 (stdoutput, buf) & 0xFF800000) | val,
                  (bfd_get_32 (stdoutput, buf) & 0xFF800000) | val,
                  buf);
                  buf);
      break;
      break;
    }
    }
 
 
  if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
  if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
    fixP->fx_done = 1;
    fixP->fx_done = 1;
}
}
 
 
/* This is our chance to record section alignment
/* This is our chance to record section alignment
   don't need to do anything here, since BFD does the proper encoding.  */
   don't need to do anything here, since BFD does the proper encoding.  */
 
 
valueT
valueT
md_section_align (segment, section_size)
md_section_align (segment, section_size)
     segT segment ATTRIBUTE_UNUSED;
     segT segment ATTRIBUTE_UNUSED;
     valueT section_size;
     valueT section_size;
{
{
  return section_size;
  return section_size;
}
}
 
 
long
long
md_pcrel_from (fixP)
md_pcrel_from (fixP)
     fixS *fixP ATTRIBUTE_UNUSED;
     fixS *fixP ATTRIBUTE_UNUSED;
{
{
  return 0;
  return 0;
}
}
 
 
/* Mostly little-endian, but longwords (4 octets) get MS word stored
/* Mostly little-endian, but longwords (4 octets) get MS word stored
   first.  */
   first.  */
 
 
void
void
tic54x_number_to_chars (buf, val, n)
tic54x_number_to_chars (buf, val, n)
     char *buf;
     char *buf;
     valueT val;
     valueT val;
     int n;
     int n;
{
{
  if (n != 4)
  if (n != 4)
    number_to_chars_littleendian (buf, val, n);
    number_to_chars_littleendian (buf, val, n);
  else
  else
    {
    {
      number_to_chars_littleendian (buf    , val >> 16   , 2);
      number_to_chars_littleendian (buf    , val >> 16   , 2);
      number_to_chars_littleendian (buf + 2, val & 0xFFFF, 2);
      number_to_chars_littleendian (buf + 2, val & 0xFFFF, 2);
    }
    }
}
}
 
 
int
int
tic54x_estimate_size_before_relax (frag, seg)
tic54x_estimate_size_before_relax (frag, seg)
     fragS *frag ATTRIBUTE_UNUSED;
     fragS *frag ATTRIBUTE_UNUSED;
     segT seg ATTRIBUTE_UNUSED;
     segT seg ATTRIBUTE_UNUSED;
{
{
  return 0;
  return 0;
}
}
 
 
/* We use this to handle bit allocations which we couldn't handle before due
/* We use this to handle bit allocations which we couldn't handle before due
   to symbols being in different frags.  return number of octets added.  */
   to symbols being in different frags.  return number of octets added.  */
 
 
int
int
tic54x_relax_frag (frag, stretch)
tic54x_relax_frag (frag, stretch)
     fragS *frag;
     fragS *frag;
     long stretch ATTRIBUTE_UNUSED;
     long stretch ATTRIBUTE_UNUSED;
{
{
  symbolS *sym = frag->fr_symbol;
  symbolS *sym = frag->fr_symbol;
  int growth = 0;
  int growth = 0;
  int i;
  int i;
 
 
  if (sym != NULL)
  if (sym != NULL)
    {
    {
      struct bit_info *bi = (struct bit_info *) frag->fr_opcode;
      struct bit_info *bi = (struct bit_info *) frag->fr_opcode;
      int bit_offset = frag_bit_offset (frag_prev (frag, bi->seg), bi->seg);
      int bit_offset = frag_bit_offset (frag_prev (frag, bi->seg), bi->seg);
      int size = S_GET_VALUE (sym);
      int size = S_GET_VALUE (sym);
      fragS *prev_frag = bit_offset_frag (frag_prev (frag, bi->seg), bi->seg);
      fragS *prev_frag = bit_offset_frag (frag_prev (frag, bi->seg), bi->seg);
      int available = 16 - bit_offset;
      int available = 16 - bit_offset;
 
 
      if (symbol_get_frag (sym) != &zero_address_frag
      if (symbol_get_frag (sym) != &zero_address_frag
          || S_IS_COMMON (sym)
          || S_IS_COMMON (sym)
          || !S_IS_DEFINED (sym))
          || !S_IS_DEFINED (sym))
        as_bad_where (frag->fr_file, frag->fr_line,
        as_bad_where (frag->fr_file, frag->fr_line,
                      _("non-absolute value used with .space/.bes"));
                      _("non-absolute value used with .space/.bes"));
 
 
      if (size < 0)
      if (size < 0)
        {
        {
          as_warn (_("negative value ignored in %s"),
          as_warn (_("negative value ignored in %s"),
                   bi->type == TYPE_SPACE ? ".space" :
                   bi->type == TYPE_SPACE ? ".space" :
                   bi->type == TYPE_BES ? ".bes" : ".field");
                   bi->type == TYPE_BES ? ".bes" : ".field");
          growth = 0;
          growth = 0;
          frag->tc_frag_data = frag->fr_fix = 0;
          frag->tc_frag_data = frag->fr_fix = 0;
          return 0;
          return 0;
        }
        }
 
 
      if (bi->type == TYPE_FIELD)
      if (bi->type == TYPE_FIELD)
        {
        {
          /* Bit fields of 16 or larger will have already been handled.  */
          /* Bit fields of 16 or larger will have already been handled.  */
          if (bit_offset != 0 && available >= size)
          if (bit_offset != 0 && available >= size)
            {
            {
              char *p = prev_frag->fr_literal;
              char *p = prev_frag->fr_literal;
 
 
              valueT value = bi->value;
              valueT value = bi->value;
              value <<= available - size;
              value <<= available - size;
              value |= ((unsigned short) p[1] << 8) | p[0];
              value |= ((unsigned short) p[1] << 8) | p[0];
              md_number_to_chars (p, value, 2);
              md_number_to_chars (p, value, 2);
              if ((prev_frag->tc_frag_data += size) == 16)
              if ((prev_frag->tc_frag_data += size) == 16)
                prev_frag->tc_frag_data = 0;
                prev_frag->tc_frag_data = 0;
              if (bi->sym)
              if (bi->sym)
                symbol_set_frag (bi->sym, prev_frag);
                symbol_set_frag (bi->sym, prev_frag);
              /* This frag is no longer used.  */
              /* This frag is no longer used.  */
              growth = -frag->fr_fix;
              growth = -frag->fr_fix;
              frag->fr_fix = 0;
              frag->fr_fix = 0;
              frag->tc_frag_data = 0;
              frag->tc_frag_data = 0;
            }
            }
          else
          else
            {
            {
              char *p = frag->fr_literal;
              char *p = frag->fr_literal;
 
 
              valueT value = bi->value << (16 - size);
              valueT value = bi->value << (16 - size);
              md_number_to_chars (p, value, 2);
              md_number_to_chars (p, value, 2);
              if ((frag->tc_frag_data = size) == 16)
              if ((frag->tc_frag_data = size) == 16)
                frag->tc_frag_data = 0;
                frag->tc_frag_data = 0;
              growth = 0;
              growth = 0;
            }
            }
        }
        }
      else
      else
        {
        {
          if (bit_offset != 0 && bit_offset < 16)
          if (bit_offset != 0 && bit_offset < 16)
            {
            {
              if (available >= size)
              if (available >= size)
                {
                {
                  if ((prev_frag->tc_frag_data += size) == 16)
                  if ((prev_frag->tc_frag_data += size) == 16)
                    prev_frag->tc_frag_data = 0;
                    prev_frag->tc_frag_data = 0;
                  if (bi->sym)
                  if (bi->sym)
                    symbol_set_frag (bi->sym, prev_frag);
                    symbol_set_frag (bi->sym, prev_frag);
                  /* This frag is no longer used.  */
                  /* This frag is no longer used.  */
                  growth = -frag->fr_fix;
                  growth = -frag->fr_fix;
                  frag->fr_fix = 0;
                  frag->fr_fix = 0;
                  frag->tc_frag_data = 0;
                  frag->tc_frag_data = 0;
                  goto getout;
                  goto getout;
                }
                }
              if (bi->type == TYPE_SPACE && bi->sym)
              if (bi->type == TYPE_SPACE && bi->sym)
                symbol_set_frag (bi->sym, prev_frag);
                symbol_set_frag (bi->sym, prev_frag);
              size -= available;
              size -= available;
            }
            }
          growth = (size + 15) / 16 * OCTETS_PER_BYTE - frag->fr_fix;
          growth = (size + 15) / 16 * OCTETS_PER_BYTE - frag->fr_fix;
          for (i = 0; i < growth; i++)
          for (i = 0; i < growth; i++)
            frag->fr_literal[i] = 0;
            frag->fr_literal[i] = 0;
          frag->fr_fix = growth;
          frag->fr_fix = growth;
          frag->tc_frag_data = size % 16;
          frag->tc_frag_data = size % 16;
          /* Make sure any BES label points to the LAST word allocated.  */
          /* Make sure any BES label points to the LAST word allocated.  */
          if (bi->type == TYPE_BES && bi->sym)
          if (bi->type == TYPE_BES && bi->sym)
            S_SET_VALUE (bi->sym, frag->fr_fix / OCTETS_PER_BYTE - 1);
            S_SET_VALUE (bi->sym, frag->fr_fix / OCTETS_PER_BYTE - 1);
        }
        }
    getout:
    getout:
      frag->fr_symbol = 0;
      frag->fr_symbol = 0;
      frag->fr_opcode = 0;
      frag->fr_opcode = 0;
      free ((void *) bi);
      free ((void *) bi);
    }
    }
  return growth;
  return growth;
}
}
 
 
void
void
tic54x_convert_frag (abfd, seg, frag)
tic54x_convert_frag (abfd, seg, frag)
     bfd *abfd ATTRIBUTE_UNUSED;
     bfd *abfd ATTRIBUTE_UNUSED;
     segT seg ATTRIBUTE_UNUSED;
     segT seg ATTRIBUTE_UNUSED;
     fragS *frag;
     fragS *frag;
{
{
  /* Offset is in bytes.  */
  /* Offset is in bytes.  */
  frag->fr_offset = (frag->fr_next->fr_address
  frag->fr_offset = (frag->fr_next->fr_address
                     - frag->fr_address
                     - frag->fr_address
                     - frag->fr_fix) / frag->fr_var;
                     - frag->fr_fix) / frag->fr_var;
  if (frag->fr_offset < 0)
  if (frag->fr_offset < 0)
    {
    {
      as_bad_where (frag->fr_file, frag->fr_line,
      as_bad_where (frag->fr_file, frag->fr_line,
                    _("attempt to .space/.bes backwards? (%ld)"),
                    _("attempt to .space/.bes backwards? (%ld)"),
                    (long) frag->fr_offset);
                    (long) frag->fr_offset);
    }
    }
  frag->fr_type = rs_space;
  frag->fr_type = rs_space;
}
}
 
 
/* We need to avoid having labels defined for certain directives/pseudo-ops
/* We need to avoid having labels defined for certain directives/pseudo-ops
   since once the label is defined, it's in the symbol table for good.  TI
   since once the label is defined, it's in the symbol table for good.  TI
   syntax puts the symbol *before* the pseudo (which is kinda like MRI syntax,
   syntax puts the symbol *before* the pseudo (which is kinda like MRI syntax,
   I guess, except I've never seen a definition of MRI syntax).
   I guess, except I've never seen a definition of MRI syntax).
 
 
   C is the character that used to be at *REST, which points to the end of the
   C is the character that used to be at *REST, which points to the end of the
   label.
   label.
 
 
   Don't allow labels to start with '.'  */
   Don't allow labels to start with '.'  */
 
 
int
int
tic54x_start_label (c, rest)
tic54x_start_label (c, rest)
     int c;
     int c;
     char *rest;
     char *rest;
{
{
  /* If within .struct/.union, no auto line labels, please.  */
  /* If within .struct/.union, no auto line labels, please.  */
  if (current_stag != NULL)
  if (current_stag != NULL)
    return 0;
    return 0;
 
 
  /* Disallow labels starting with "."  */
  /* Disallow labels starting with "."  */
  if (c != ':')
  if (c != ':')
    {
    {
      char *label = rest;
      char *label = rest;
 
 
      while (!is_end_of_line[(int) label[-1]])
      while (!is_end_of_line[(int) label[-1]])
        --label;
        --label;
      if (*label == '.')
      if (*label == '.')
        {
        {
          as_bad (_("Invalid label '%s'"), label);
          as_bad (_("Invalid label '%s'"), label);
          return 0;
          return 0;
        }
        }
    }
    }
 
 
  if (is_end_of_line[(int) c])
  if (is_end_of_line[(int) c])
    return 1;
    return 1;
 
 
  if (ISSPACE (c))
  if (ISSPACE (c))
    while (ISSPACE (c = *++rest))
    while (ISSPACE (c = *++rest))
      ;
      ;
  if (c == '.')
  if (c == '.')
    {
    {
      /* Don't let colon () define a label for any of these...  */
      /* Don't let colon () define a label for any of these...  */
      return (strncasecmp (rest, ".tag", 4) != 0 || !ISSPACE (rest[4]))
      return (strncasecmp (rest, ".tag", 4) != 0 || !ISSPACE (rest[4]))
        && (strncasecmp (rest, ".struct", 7) != 0 || !ISSPACE (rest[7]))
        && (strncasecmp (rest, ".struct", 7) != 0 || !ISSPACE (rest[7]))
        && (strncasecmp (rest, ".union", 6) != 0 || !ISSPACE (rest[6]))
        && (strncasecmp (rest, ".union", 6) != 0 || !ISSPACE (rest[6]))
        && (strncasecmp (rest, ".macro", 6) != 0 || !ISSPACE (rest[6]))
        && (strncasecmp (rest, ".macro", 6) != 0 || !ISSPACE (rest[6]))
        && (strncasecmp (rest, ".set", 4) != 0 || !ISSPACE (rest[4]))
        && (strncasecmp (rest, ".set", 4) != 0 || !ISSPACE (rest[4]))
        && (strncasecmp (rest, ".equ", 4) != 0 || !ISSPACE (rest[4]));
        && (strncasecmp (rest, ".equ", 4) != 0 || !ISSPACE (rest[4]));
    }
    }
 
 
  return 1;
  return 1;
}
}
 
 

powered by: WebSVN 2.1.0

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