This is the mail archive of the binutils@sourceware.cygnus.com mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

[PATCH] tic54x gas target


These patches implement GAS for the tic54x.

About 3Mb of test files have been excised for brevity.


Index: gas/ChangeLog
===================================================================
RCS file: /cvs/src/src/gas/ChangeLog,v
retrieving revision 1.407
diff -d -c -p -b -w -r1.407 ChangeLog
*** ChangeLog	2000/06/19 01:22:39	1.407
--- ChangeLog	2000/06/19 20:51:25
***************
*** 1,3 ****
--- 1,22 ----
+ 2000-06-19  Timothy Wall  <twall@cygnus.com>
+ 
+ 	* doc/internals.texi (CPU backend): Add @itemx for
+ 	TC_START_LABEL_WITHOUT_COLON. 
+ 	* doc/c-tic54x.texi: New.
+ 	* doc/as.texinfo: Add tic54x features and include primary tic54x
+ 	documentation file. 
+ 	* doc/all.texi: Add C54X.
+ 	* doc/Makefile.am (CPU_DOCS): Add c-tic54x.texi.
+ 	* doc/Makefile.in: Regenerate.
+ 	* configure: Add tic54x.
+ 	* config/tc-tic54x.[ch]: New.
+ 	* config/obj-coff.h: Add tic54x.
+ 	* Makefile.am: (CPU_TYPES): Add tic54x.
+ 	(TARGET_CPU_CFILES): Add 'tc-tic54x.c'.
+ 	(TARGET_CPU_HFILES): Add 'tc-tic54x.h'.
+ 	(as_new_LDADD): Add -lm.
+ 	* Makefile.in: Regenerate.
+ 	
  2000-06-18  Stephane Carrez  <stcarrez@worldnet.fr>
  
  	* doc/Makefile.am (CPU_DOCS): Added 68hc11 file.
*************** Fri Apr 21 13:20:53 2000  Richard Hender
*** 746,752 ****
  			  Jim Wilson  <wilson@cygnus.com>
  
  	* Makefile.am (CPU_TYPES): Add ia64.
! 	(TARGET_CPU_CFILES): Add cofnig/tc-ia64.c.
  	(TARGET_CPU_HFILES): Add config/tc-ia64.h.
  	* Makefile.in: Rebuild.
  	* app.c (do_scrub_chars): Handle DOUBLESLASH_COMMENTS.
--- 765,771 ----
  			  Jim Wilson  <wilson@cygnus.com>
  
  	* Makefile.am (CPU_TYPES): Add ia64.
! 	(TARGET_CPU_CFILES): Add config/tc-ia64.c.
  	(TARGET_CPU_HFILES): Add config/tc-ia64.h.
  	* Makefile.in: Rebuild.
  	* app.c (do_scrub_chars): Handle DOUBLESLASH_COMMENTS.
Index: gas/Makefile.am
===================================================================
RCS file: /cvs/src/src/gas/Makefile.am,v
retrieving revision 1.23
diff -d -c -p -b -w -r1.23 Makefile.am
*** Makefile.am	2000/06/19 01:22:39	1.23
--- Makefile.am	2000/06/19 20:51:25
*************** CPU_TYPES = \
*** 68,73 ****
--- 68,74 ----
  	sparc \
  	tahoe \
  	tic30 \
+ 	tic54x \
  	tic80 \
  	vax \
  	w65 \
*************** TARGET_CPU_CFILES = \
*** 240,245 ****
--- 241,247 ----
  	config/tc-sparc.c \
  	config/tc-tahoe.c \
  	config/tc-tic30.c \
+ 	config/tc-tic54x.c \
  	config/tc-tic80.c \
  	config/tc-vax.c \
  	config/tc-w65.c \
*************** TARGET_CPU_HFILES = \
*** 278,283 ****
--- 280,286 ----
  	config/tc-sparc.h \
  	config/tc-tahoe.h \
  	config/tc-tic30.h \
+ 	config/tc-tic54x.h \
  	config/tc-tic80.h \
  	config/tc-vax.h \
  	config/tc-w65.h \
*************** $(OBJS): @ALL_OBJ_DEPS@
*** 445,451 ****
  
  as_new_SOURCES = $(GAS_CFILES)
  as_new_LDADD = $(TARG_CPU_O) $(OBJ_FORMAT_O) $(ATOF_TARG_O) \
! 	$(extra_objects) $(GASLIBS) $(INTLLIBS)
  as_new_DEPENDENCIES = $(TARG_CPU_O) $(OBJ_FORMAT_O) $(ATOF_TARG_O) \
  	$(extra_objects) $(GASLIBS) $(INTLDEPS)
  
--- 448,454 ----
  
  as_new_SOURCES = $(GAS_CFILES)
  as_new_LDADD = $(TARG_CPU_O) $(OBJ_FORMAT_O) $(ATOF_TARG_O) \
! 	$(extra_objects) $(GASLIBS) $(INTLLIBS) -lm
  as_new_DEPENDENCIES = $(TARG_CPU_O) $(OBJ_FORMAT_O) $(ATOF_TARG_O) \
  	$(extra_objects) $(GASLIBS) $(INTLDEPS)
  
*************** DEPTC_tic30_elf = $(INCDIR)/bin-bugs.h $
*** 1264,1269 ****
--- 1267,1276 ----
    $(srcdir)/config/obj-elf.h $(BFDDIR)/elf-bfd.h $(INCDIR)/elf/common.h \
    $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h \
    $(srcdir)/config/tc-tic30.h emul.h $(INCDIR)/opcode/tic30.h
+ DEPTC_tic54x_coff = $(INCDIR)/bin-bugs.h $(INCDIR)/progress.h \
+   $(srcdir)/config/obj-coff.h $(srcdir)/config/tc-tic54x.h \
+   $(INCDIR)/coff/internal.h $(INCDIR)/coff/tic54x.h $(BFDDIR)/libcoff.h \
+   $(INCDIR)/bfdlink.h emul.h $(INCDIR)/opcode/tic54x.h
  DEPTC_tic80_coff = $(INCDIR)/bin-bugs.h $(INCDIR)/progress.h \
    $(srcdir)/config/obj-coff.h $(srcdir)/config/tc-tic80.h \
    $(INCDIR)/coff/internal.h $(INCDIR)/coff/tic80.h $(BFDDIR)/libcoff.h \
*************** DEPOBJ_tic30_elf = $(INCDIR)/bin-bugs.h 
*** 1658,1663 ****
--- 1665,1674 ----
    $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h \
    $(srcdir)/config/tc-tic30.h emul.h subsegs.h $(INCDIR)/obstack.h \
    $(INCDIR)/aout/aout64.h
+ DEPOBJ_tic54x_coff = $(INCDIR)/bin-bugs.h $(INCDIR)/progress.h \
+   $(srcdir)/config/obj-coff.h $(srcdir)/config/tc-tic54x.h \
+   $(INCDIR)/coff/internal.h $(INCDIR)/coff/tic54x.h $(BFDDIR)/libcoff.h \
+   $(INCDIR)/bfdlink.h emul.h $(INCDIR)/obstack.h subsegs.h
  DEPOBJ_tic80_coff = $(INCDIR)/bin-bugs.h $(INCDIR)/progress.h \
    $(srcdir)/config/obj-coff.h $(srcdir)/config/tc-tic80.h \
    $(INCDIR)/coff/internal.h $(INCDIR)/coff/tic80.h $(BFDDIR)/libcoff.h \
*************** DEP_tic30_coff = $(srcdir)/config/obj-co
*** 1914,1919 ****
--- 1925,1933 ----
  DEP_tic30_elf = $(srcdir)/config/obj-elf.h $(BFDDIR)/elf-bfd.h \
    $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \
    $(INCDIR)/bfdlink.h $(srcdir)/config/tc-tic30.h
+ DEP_tic54x_coff = $(srcdir)/config/obj-coff.h $(srcdir)/config/tc-tic54x.h \
+   $(INCDIR)/coff/internal.h $(INCDIR)/coff/tic54x.h $(BFDDIR)/libcoff.h \
+   $(INCDIR)/bfdlink.h
  DEP_tic80_coff = $(srcdir)/config/obj-coff.h $(srcdir)/config/tc-tic80.h \
    $(INCDIR)/coff/internal.h $(INCDIR)/coff/tic80.h $(BFDDIR)/libcoff.h \
    $(INCDIR)/bfdlink.h
Index: gas/Makefile.in
===================================================================
RCS file: /cvs/src/src/gas/Makefile.in,v
retrieving revision 1.25
diff -d -c -p -b -w -r1.25 Makefile.in
*** Makefile.in	2000/06/19 01:22:39	1.25
--- Makefile.in	2000/06/19 20:51:25
*************** CPU_TYPES = \
*** 172,177 ****
--- 172,178 ----
  	sparc \
  	tahoe \
  	tic30 \
+ 	tic54x \
  	tic80 \
  	vax \
  	w65 \
*************** TARGET_CPU_CFILES = \
*** 350,355 ****
--- 351,357 ----
  	config/tc-sparc.c \
  	config/tc-tahoe.c \
  	config/tc-tic30.c \
+ 	config/tc-tic54x.c \
  	config/tc-tic80.c \
  	config/tc-vax.c \
  	config/tc-w65.c \
*************** TARGET_CPU_HFILES = \
*** 389,394 ****
--- 391,397 ----
  	config/tc-sparc.h \
  	config/tc-tahoe.h \
  	config/tc-tic30.h \
+ 	config/tc-tic54x.h \
  	config/tc-tic80.h \
  	config/tc-vax.h \
  	config/tc-w65.h \
*************** STAGESTUFF = *.o $(noinst_PROGRAMS)
*** 552,558 ****
  
  as_new_SOURCES = $(GAS_CFILES)
  as_new_LDADD = $(TARG_CPU_O) $(OBJ_FORMAT_O) $(ATOF_TARG_O) \
! 	$(extra_objects) $(GASLIBS) $(INTLLIBS)
  
  as_new_DEPENDENCIES = $(TARG_CPU_O) $(OBJ_FORMAT_O) $(ATOF_TARG_O) \
  	$(extra_objects) $(GASLIBS) $(INTLDEPS)
--- 555,561 ----
  
  as_new_SOURCES = $(GAS_CFILES)
  as_new_LDADD = $(TARG_CPU_O) $(OBJ_FORMAT_O) $(ATOF_TARG_O) \
! 	$(extra_objects) $(GASLIBS) $(INTLLIBS) -lm
  
  as_new_DEPENDENCIES = $(TARG_CPU_O) $(OBJ_FORMAT_O) $(ATOF_TARG_O) \
  	$(extra_objects) $(GASLIBS) $(INTLDEPS)
*************** DEPTC_tic30_elf = $(INCDIR)/bin-bugs.h $
*** 1049,1054 ****
--- 1052,1062 ----
    $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h \
    $(srcdir)/config/tc-tic30.h emul.h $(INCDIR)/opcode/tic30.h
  
+ DEPTC_tic54x_coff = $(INCDIR)/bin-bugs.h $(INCDIR)/progress.h \
+   $(srcdir)/config/obj-coff.h $(srcdir)/config/tc-tic54x.h \
+   $(INCDIR)/coff/internal.h $(INCDIR)/coff/ti.h $(BFDDIR)/libcoff.h \
+   $(INCDIR)/bfdlink.h emul.h $(INCDIR)/opcode/tic54x.h
+ 
  DEPTC_tic80_coff = $(INCDIR)/bin-bugs.h $(INCDIR)/progress.h \
    $(srcdir)/config/obj-coff.h $(srcdir)/config/tc-tic80.h \
    $(INCDIR)/coff/internal.h $(INCDIR)/coff/tic80.h $(BFDDIR)/libcoff.h \
*************** DEPOBJ_tic30_elf = $(INCDIR)/bin-bugs.h 
*** 1533,1538 ****
--- 1541,1551 ----
    $(srcdir)/config/tc-tic30.h emul.h subsegs.h $(INCDIR)/obstack.h \
    $(INCDIR)/aout/aout64.h
  
+ DEPOBJ_tic54x_coff = $(INCDIR)/bin-bugs.h $(INCDIR)/progress.h \
+   $(srcdir)/config/obj-coff.h $(srcdir)/config/tc-tic54x.h \
+   $(INCDIR)/coff/internal.h $(INCDIR)/coff/ti.h $(BFDDIR)/libcoff.h \
+   $(INCDIR)/bfdlink.h emul.h $(INCDIR)/obstack.h subsegs.h
+ 
  DEPOBJ_tic80_coff = $(INCDIR)/bin-bugs.h $(INCDIR)/progress.h \
    $(srcdir)/config/obj-coff.h $(srcdir)/config/tc-tic80.h \
    $(INCDIR)/coff/internal.h $(INCDIR)/coff/tic80.h $(BFDDIR)/libcoff.h \
*************** DEP_tic30_coff = $(srcdir)/config/obj-co
*** 1877,1882 ****
--- 1890,1899 ----
  DEP_tic30_elf = $(srcdir)/config/obj-elf.h $(BFDDIR)/elf-bfd.h \
    $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \
    $(INCDIR)/bfdlink.h $(srcdir)/config/tc-tic30.h
+ 
+ DEP_tic54x_coff = $(srcdir)/config/obj-coff.h $(srcdir)/config/tc-tic54x.h \
+   $(INCDIR)/coff/internal.h $(INCDIR)/coff/ti.h $(BFDDIR)/libcoff.h \
+   $(INCDIR)/bfdlink.h
  
  DEP_tic80_coff = $(srcdir)/config/obj-coff.h $(srcdir)/config/tc-tic80.h \
    $(INCDIR)/coff/internal.h $(INCDIR)/coff/tic80.h $(BFDDIR)/libcoff.h \
Index: gas/configure
===================================================================
RCS file: /cvs/src/src/gas/configure,v
retrieving revision 1.34
diff -d -c -p -b -w -r1.34 configure
*** configure	2000/06/19 01:22:39	1.34
--- configure	2000/06/19 20:51:26
*************** EOF
*** 1934,1939 ****
--- 1934,1941 ----
  
        tic30-*-*aout*)	    fmt=aout bfd_gas=yes ;;
        tic30-*-*coff*)       fmt=coff bfd_gas=yes ;;
+       tic54x-*-* | c54x*-*-*)  
+ 			    fmt=coff bfd_gas=yes ;;
        tic80-*-*)	    fmt=coff ;;
  
        v850-*-*)		    fmt=elf bfd_gas=yes ;;
Index: gas/configure.in
===================================================================
RCS file: /cvs/src/src/gas/configure.in,v
retrieving revision 1.31
diff -d -c -p -b -w -r1.31 configure.in
*** configure.in	2000/06/19 01:22:39	1.31
--- configure.in	2000/06/19 20:51:26
*************** changequote([,])dnl
*** 393,398 ****
--- 393,400 ----
  
        tic30-*-*aout*)	    fmt=aout bfd_gas=yes ;;
        tic30-*-*coff*)       fmt=coff bfd_gas=yes ;;
+       tic54x-*-* | c54x*-*-*)
+ 			    fmt=coff bfd_gas=yes ;;
        tic80-*-*)	    fmt=coff ;;
  
        v850-*-*)		    fmt=elf bfd_gas=yes ;;
Index: gas/config/obj-coff.h
===================================================================
RCS file: /cvs/src/src/gas/config/obj-coff.h,v
retrieving revision 1.7
diff -d -c -p -b -w -r1.7 obj-coff.h
*** obj-coff.h	2000/05/22 21:19:43	1.7
--- obj-coff.h	2000/06/19 20:51:26
***************
*** 149,154 ****
--- 149,159 ----
  #define TARGET_FORMAT "coff-tic30"
  #endif
  
+ #ifdef TC_TIC54X
+ #include "coff/tic54x.h"
+ #define TARGET_FORMAT "coff1-c54x"
+ #endif
+ 
  #ifdef TC_TIC80
  #include "coff/tic80.h"
  #define TARGET_FORMAT "coff-tic80"
Index: gas/config/tc-tic54x.c
===================================================================
RCS file: tc-tic54x.c
diff -N tc-tic54x.c
*** /dev/null	Tue May  5 13:32:27 1998
--- tc-tic54x.c	Mon Jun 19 13:51:27 2000
***************
*** 0 ****
--- 1,5569 ----
+ /* tc-tic54x.c -- Assembly code for the Texas Instruments TMS320C54X
+    Copyright (C) 1999, 2000 Free Software Foundation.
+    Contributed by Timothy Wall (twall@cygnus.com)
+ 
+    This file is part of GAS, the GNU Assembler.
+ 
+    GAS is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+ 
+    GAS is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+ 
+    You should have received a copy of the GNU General Public License
+    along with GAS; see the file COPYING.  If not, write to the Free
+    Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+    02111-1307, USA.  */
+ 
+ /*
+    Texas Instruments TMS320C54X machine specific gas.
+    Written by Timothy Wall (twall@alum.mit.edu).
+ 
+    Valuable things to do:
+    Pipeline conflict warnings
+    We encode/decode "ld #_label, dp" differently in relocatable files
+      This means we're not compatible with TI output containing those
+      expressions.  We store the upper nine bits; TI stores the lower nine
+      bits.  How they recover the original upper nine bits is beyond me.
+ 
+    Tests to add to expect testsuite:
+      '=' and '==' with .if, .elseif, and .break
+ 
+    Incompatibilities (mostly trivial):
+    We don't allow '''
+    We fill text section with zeroes instead of "nop"s
+    We don't convert '' or "" to a single instance
+    We don't convert '' to '\0'
+    We don't allow strings with .byte/.half/.short/.long
+    Probably details of the subsym stuff are different
+    TI sets labels to be data type 4 (T_INT); GAS uses T_NULL
+  */
+ 
+ #include <stdlib.h>
+ #include <limits.h>
+ #include <errno.h>
+ #include "as.h"
+ #include "sb.h"
+ #include "macro.h"
+ #include "subsegs.h"
+ #include "struc-symbol.h"
+ #include "opcode/tic54x.h"
+ #include "obj-coff.h"
+ #include <math.h>
+ 
+ #define MAX_LINE 256 /* lines longer than this are truncated by TI's asm */
+ 
+ const char comment_chars[] = ";";
+ const char line_comment_chars[] = ";*#"; /* at column zero only */
+ const char line_separator_chars[] = "";/* not permitted */
+ 
+ /* characters which indicate that this is a floating point constant */
+ const char FLT_CHARS[] = "fF"; 
+ /* Characters that can be used to separate mantissa from exp in FP nums */
+ const char EXP_CHARS[] = "eE";
+ 
+ /* only word (et al.), align, or conditionals are allowed within
+    .struct/.union */
+ #define ILLEGAL_WITHIN_STRUCT() \
+ do if (current_stag != NULL){ \
+ as_bad (_("pseudo-op illegal within .struct/.union"));return; } while (0)
+ 
+ void
+ md_show_usage (stream)
+   FILE *stream;
+ {
+   fprintf (stream, _("C54x-specific command line  options:\n"));
+   fprintf (stream, _("-mfar-mode | -mf          Use extended addressing\n"));
+   fprintf (stream, _("-mcpu=<CPU version>       Specify the CPU version\n"));
+   /*  fprintf (stream, _("-mcoff-version={0|1|2}    Select COFF version\n"));*/
+   fprintf (stream, _("-merrors-to-file <filename>\n"));
+   fprintf (stream, _("-me <filename>            Redirect errors to a file\n"));
+ }
+ 
+ const char *md_shortopts = "";
+ enum cpu_version { 
+   VNONE = 0, V541 = 1, V542 = 2, V543 = 3, V545 = 5, V548 = 8, V549 = 9,
+   V545LP = 15, V546LP = 16
+ };
+ 
+ enum address_mode {
+   c_mode,   /* 16-bit addresses */                         
+   far_mode  /* >16-bit addresses */
+ };
+ 
+ #define OPTION_ADDRESS_MODE     (OPTION_MD_BASE)
+ #define OPTION_CPU_VERSION      (OPTION_ADDRESS_MODE+1)
+ #define OPTION_COFF_VERSION     (OPTION_CPU_VERSION+1)
+ #define OPTION_STDERR_TO_FILE   (OPTION_COFF_VERSION+1)
+ 
+ struct option md_longopts[] = 
+ {
+   { "mfar-mode",       no_argument,         NULL, OPTION_ADDRESS_MODE },
+   { "mf",              no_argument,         NULL, OPTION_ADDRESS_MODE },
+   { "mcpu",            required_argument,   NULL, OPTION_CPU_VERSION },
+   /*  { "mcoff-version",   required_argument,   NULL, OPTION_COFF_VERSION },*/
+   { "merrors-to-file", required_argument,   NULL, OPTION_STDERR_TO_FILE },
+   { "me",              required_argument,   NULL, OPTION_STDERR_TO_FILE },
+   { NULL, no_argument, NULL, 0},
+ };
+ 
+ size_t md_longopts_size = sizeof (md_longopts);
+ 
+ static int assembly_begun = 0; 
+ /* Addressing mode is not entirely implemented; the latest rev of the Other
+    assembler doesn't seem to make any distinction whatsoever; all relocations
+    are stored as extended relocatiosn.  Older versions used REL16 vs 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
+    instruction (RND) for LP devices only, and several for devices with
+    extended addressing only.  We include it for compatibility.
+  */
+ static enum address_mode amode = c_mode;
+ static enum cpu_version cpu  = VNONE;
+ 
+ /* include string substitutions in listing? */
+ static int listing_sslist = 0;
+ /* did we do subsym substitutions on the line? */
+ static int substitution_line = 0;
+ /* last label seen */
+ static symbolS *last_label_seen = NULL;
+ /* this ensures that all new labels are unique */
+ static int local_label_id;
+ 
+ static struct hash_control *subsym_recurse_hash; /* prevent infinite recurse */
+ static struct hash_control *math_hash; /* built-in math functions */
+ /* allow maximum levels of macro nesting; level 0 is the main substitution
+    symbol table.  The other assembler only does 32 levels, so there! */
+ static struct hash_control *subsym_hash[100];
+ /* 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
+ 
+    We do our own local label handling 'cuz it's subtly different from the
+    stock GAS handling.
+ 
+    We use our own macro nesting counter, since GAS overloads it when expanding
+    other things (like conditionals and repeat loops).
+  */
+ static int macro_level = 0;
+ static struct hash_control *local_label_hash[100];
+ /* keep track of struct/union tags */
+ static struct hash_control *stag_hash;
+ static struct hash_control *op_hash;
+ static struct hash_control *parop_hash;
+ static struct hash_control *reg_hash;
+ static struct hash_control *mmreg_hash;
+ static struct hash_control *cc_hash;
+ static struct hash_control *cc2_hash;
+ static struct hash_control *cc3_hash;
+ static struct hash_control *sbit_hash;
+ static struct hash_control *misc_symbol_hash;
+ 
+ static char *subsym_substitute PARAMS ((char *line, int forced));
+ static char *subsym_lookup PARAMS ((char *name, int nest_level));
+ static void subsym_create_or_replace PARAMS ((char *name, char *value));
+ static float math_ceil PARAMS ((float, float));
+ static float math_cvi PARAMS ((float, float));
+ static float math_floor PARAMS ((float, float));
+ static float math_fmod PARAMS ((float, float));
+ static float math_int PARAMS ((float, float));
+ static float math_round PARAMS ((float, float));
+ static float math_sgn PARAMS ((float, float));
+ static float math_trunc PARAMS ((float, float));
+ static float math_acos PARAMS ((float, float));
+ static float math_asin PARAMS ((float, float));
+ static float math_atan PARAMS ((float, float));
+ static float math_atan2 PARAMS ((float, float));
+ static float math_cosh PARAMS ((float, float));
+ static float math_cos PARAMS ((float, float));
+ static float math_cvf PARAMS ((float, float));
+ static float math_exp PARAMS ((float, float));
+ static float math_fabs PARAMS ((float, float));
+ static float math_ldexp PARAMS ((float, float));
+ static float math_log10 PARAMS ((float, float));
+ static float math_log PARAMS ((float, float));
+ static float math_max PARAMS ((float, float));
+ static float math_pow PARAMS ((float, float));
+ static float math_sin PARAMS ((float, float));
+ static float math_sinh PARAMS ((float, float));
+ static float math_sqrt PARAMS ((float, float));
+ static float math_tan PARAMS ((float, float));
+ static float math_tanh PARAMS ((float, float));
+ 
+ static struct stag {
+   symbolS *sym;                     // symbol for this stag; value is offset
+   const char *name;                 // shortcut to symbol name
+   bfd_vma size;                     // size of struct/union
+   int current_bitfield_offset;         // temporary for tracking fields
+   int is_union;
+   struct stag_field {               // list of fields
+     const char *name;
+     bfd_vma offset;                 // of start of this field
+     int bitfield_offset;               // of start of this field
+     struct stag *stag;              // if field is struct/union
+     struct stag_field *next;
+   } *field;
+   /* for nesting; used only in stag construction */
+   struct stag *inner;               // enclosed .struct
+   struct stag *outer;               // enclosing .struct
+ } *current_stag = NULL;
+ 
+ static segT stag_saved_seg;
+ static subsegT stag_saved_subseg;
+ 
+ /* output a single character (upper octect is zero) */
+ static void 
+ tic54x_emit_char (char c)
+ {
+   expressionS exp;
+ 
+   exp.X_op = O_constant;
+   exp.X_add_number = c;
+   emit_expr (&exp, 2);
+ }
+ 
+ /* walk backwards in the frag chain */
+ static fragS *
+ frag_prev (frag, seg)
+   fragS *frag;
+   segT seg;
+ {
+   segment_info_type *seginfo = seg_info (seg);
+   fragS *fragp;
+ 
+   for (fragp = seginfo->frchainP->frch_root;fragp;fragp = fragp->fr_next)
+     if (fragp->fr_next == frag)
+       return fragp;
+   return NULL;
+ }
+ 
+ static fragS *
+ bit_offset_frag (frag, seg)
+   fragS *frag;
+   segT seg;
+ {
+   while (frag != NULL)
+     {
+       if (frag->fr_fix == 0 
+           && frag->fr_opcode == NULL 
+           && frag->tc_frag_data == 0)
+         frag = frag_prev (frag, seg);
+       else
+         return frag;
+     }
+   return NULL;
+ }
+ 
+ /* return the number of bits allocated in the most recent word, or zero if
+    none. .field/.space/.bes may leave words partially allocated */
+ static int
+ frag_bit_offset (frag, seg)
+   fragS *frag;
+   segT seg;
+ {
+   frag = bit_offset_frag (frag, seg);
+   if (frag)
+     {
+       return frag->fr_opcode != NULL ? -1 : frag->tc_frag_data;
+     }
+   return 0;
+ }
+ 
+ /* read an expression from a C string; returns a pointer past the end of the
+    expression  */
+ static char *
+ parse_expression (char *str, expressionS * exp)
+ {
+   char *s;
+   char *tmp;
+ 
+   tmp = input_line_pointer;	/* Save line pointer.  */
+   input_line_pointer = str;
+   expression (exp);
+   s = input_line_pointer;
+   input_line_pointer = tmp;	/* Restore line pointer.  */
+   return s;			/* Return pointer to where parsing stopped.  */
+ }
+ 
+ /* .asg "character-string"|character-string, symbol 
+    
+    .eval is the only pseudo-op allowed to perform arithmetic on substitution
+    symbols.  all other use of symbols defined with .asg are currently
+    unsupported. 
+ */
+ static void 
+ tic54x_asg (x)
+   int x ATTRIBUTE_UNUSED;
+ {
+   int c;
+   char *name;
+   char *str;
+   char *tmp;
+   int quoted = *input_line_pointer == '"';
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   if (quoted)
+     {
+       int len;
+       str = demand_copy_C_string (&len);
+       c = *input_line_pointer;
+     }
+   else
+     {
+       str = input_line_pointer;
+       while ((c = *input_line_pointer) != ',')
+         {
+           if (is_end_of_line[(int)*input_line_pointer])
+             break;
+           ++input_line_pointer;
+         }
+       *input_line_pointer = 0;
+     }
+   if (c != ',')
+     {
+       as_bad (_("Comma and symbol expected for '.asg STRING, SYMBOL'"));
+       ignore_rest_of_line ();
+       return;
+     }
+ 
+   name = ++input_line_pointer;
+   c = get_symbol_end ();	/* Get terminator.  */
+   if (!isalpha (*name))
+     {
+       as_bad ("symbols assigned with .asg must begin with a letter");
+       ignore_rest_of_line ();
+       return;
+     }
+ 
+   tmp = xmalloc (strlen (str) + 1);
+   strcpy (tmp, str);
+   str = tmp;
+   tmp = xmalloc (strlen (name) + 1);
+   strcpy (tmp, name);
+   name = tmp;
+   subsym_create_or_replace (name, str);
+   *input_line_pointer = c;
+   demand_empty_rest_of_line ();
+ }
+ 
+ /* .eval expression, symbol 
+    There's something screwy about this.  The other assembler sometimes does and
+    sometimes doesn't substitute symbols defined with .eval.  
+    We'll put the symbols into the subsym table as well as the normal symbol
+    table, since that's what works best.
+ */
+ static void 
+ tic54x_eval (x)
+   int x ATTRIBUTE_UNUSED;
+ {
+   char c;
+   int value;
+   char *name;
+   symbolS *symbolP;
+   char valuestr[32], *tmp;
+   int quoted;
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   SKIP_WHITESPACE ();
+ 
+   quoted = *input_line_pointer == '"';
+   if (quoted)
+     ++input_line_pointer;
+   value = get_absolute_expression ();
+   if (quoted)
+     {
+       if (*input_line_pointer != '"')
+         {
+           as_bad (_("Unterminated string after absolute expression"));
+           ignore_rest_of_line ();
+           return;
+         }
+       ++input_line_pointer;
+     }
+   if (*input_line_pointer++ != ',')
+     {
+       as_bad (_("Comma and symbol expected for '.eval EXPR, SYMBOL'"));
+       ignore_rest_of_line ();
+       return;
+     }
+   name = input_line_pointer;
+   c = get_symbol_end ();	/* Get terminator.  */
+   tmp = xmalloc (strlen (name)+1);
+   name = strcpy (tmp, name);
+   *input_line_pointer = c;
+ 
+   if (!isalpha (*name))
+     {
+       as_bad (_("symbols assigned with .eval must begin with a letter"));
+       ignore_rest_of_line ();
+       return;
+     }
+   symbolP = symbol_new (name, absolute_section,
+ 			(valueT) value, &zero_address_frag);
+   SF_SET_LOCAL (symbolP);
+   symbol_table_insert (symbolP);
+ 
+   /* 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
+      to match their behavior */
+   sprintf (valuestr, "%d", value);
+   tmp = xmalloc (strlen (valuestr) + 1);
+   strcpy (tmp, valuestr);
+   subsym_create_or_replace (name, tmp);
+ 
+   demand_empty_rest_of_line ();
+ }
+ 
+ /* .bss symbol, size [, [blocking flag] [, alignment flag] 
+ 
+    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
+       (not yet implemented).
+ 
+    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
+       allocated crosses the page boundary (128 words).
+       if there's not enough space, create a hole and align with the next page
+       boundary. 
+       (not yet implemented)
+ 
+  */
+ static void 
+ tic54x_bss (x)
+   int x ATTRIBUTE_UNUSED;
+ {
+   char c;
+   char *name;
+   char *p;
+   int words;
+   segT current_seg;
+   subsegT current_subseg;
+   symbolS *symbolP;
+   int block = 0;
+   int align = 0;
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   current_seg = now_seg;	/* save current seg.  */
+   current_subseg = now_subseg;	/* save current subseg.  */
+ 
+   name = input_line_pointer;
+   c = get_symbol_end ();	/* Get terminator.  */
+   if (c != ',')
+     {
+       as_bad (".bss size argument missing\n");
+       ignore_rest_of_line ();
+       return;
+     }
+ 
+   ++input_line_pointer;
+   words = get_absolute_expression ();
+   if (words < 0)
+     {
+       as_bad (".bss size %d < 0!", words);
+       ignore_rest_of_line ();
+       return;
+     }
+ 
+   if (*input_line_pointer == ',')
+     {
+       /* the blocking flag may be missing */
+       ++input_line_pointer;
+       if (*input_line_pointer != ',')
+           block = get_absolute_expression ();
+       else
+           block = 0;
+ 
+       if (*input_line_pointer == ',')
+         {
+           ++input_line_pointer;
+           align = get_absolute_expression ();
+         }
+       else
+         align = 0;
+     }
+   else
+     block = align = 0;
+ 
+   subseg_set (bss_section, 0);
+   symbolP = symbol_find_or_make (name);
+ 
+   if (S_GET_SEGMENT (symbolP) == bss_section)
+     symbolP->sy_frag->fr_symbol = (symbolS *) NULL;
+ 
+   symbol_set_frag (symbolP, frag_now);
+   p = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
+ 		(offsetT)(words << 1), (char *) 0);
+   *p = 0;			/* fill char.  */
+ 
+   S_SET_SEGMENT (symbolP, bss_section);
+ 
+   /* The symbol may already have been created with a preceding
+      ".globl" directive -- be careful not to step on storage class
+      in that case.  Otherwise, set it to static.  */
+   if (S_GET_STORAGE_CLASS (symbolP) != C_EXT)
+     S_SET_STORAGE_CLASS (symbolP, C_STAT);
+ 
+   if (align)
+     {
+       /* s_align eats end of line; restore it */
+       s_align_bytes (4);
+       --input_line_pointer;
+     }
+ 
+   if (block)
+       bss_section->flags |= SEC_BLOCK;
+ 
+   subseg_set (current_seg, current_subseg);	/* restore current seg.  */
+   demand_empty_rest_of_line ();
+ }
+ 
+ static void
+ stag_add_field_symbols (struct stag *stag,
+                         const char *path, 
+                         bfd_vma base_offset, 
+                         symbolS *rootsym,
+                         const char *root_stag_name)
+ {
+   char prefix[strlen (path) + 2];
+   struct stag_field *field = stag->field;
+   
+   /* construct a symbol for every field contained within this structure 
+      including fields within structure fields
+    */
+   strcpy (prefix, path);
+   if (*path)
+     strcat (prefix, ".");
+ 
+   while (field != NULL)
+     {
+       int len = strlen (prefix) + strlen (field->name) + 2;
+       char *name = xmalloc (len);
+       strcpy (name, prefix);
+       strcat (name, field->name);
+ 
+       if (rootsym == NULL)
+         {
+           symbolS *sym;
+           sym = symbol_new (name, absolute_section, 
+                             (field->stag ? field->offset :
+                              (valueT)(base_offset + field->offset)), 
+                             &zero_address_frag);
+           SF_SET_LOCAL (sym);
+           symbol_table_insert (sym);
+         }
+       else
+         {
+           char *replacement = xmalloc (strlen (name) + strlen (stag->name) + 2);
+           strcpy (replacement, S_GET_NAME (rootsym));
+           strcat (replacement, "+");
+           strcat (replacement, root_stag_name);
+           strcat (replacement, name + strlen (S_GET_NAME (rootsym)));
+           hash_insert (subsym_hash[0], name, replacement);
+         }
+ 
+       /* recurse if the field is a structure 
+          note the field offset is relative to the outermost struct
+        */
+       if (field->stag != NULL)
+         stag_add_field_symbols (field->stag, name, 
+                                 field->offset, 
+                                 rootsym, root_stag_name);
+       field = field->next;
+     }
+ }
+ 
+ /* keep track of stag fields so that when structures are nested we can add the
+    complete dereferencing symbols to the symbol table */
+ static void
+ stag_add_field (struct stag *parent, const char *name, bfd_vma offset, 
+                 struct stag *stag)
+ {
+   struct stag_field *sfield = xmalloc (sizeof (struct stag_field));
+ 
+   memset (sfield, 0, sizeof (*sfield));
+   sfield->name = strcpy (xmalloc (strlen (name)+1), name);
+   sfield->offset = offset;
+   sfield->bitfield_offset = parent->current_bitfield_offset;
+   sfield->stag = stag;
+   if (parent->field == NULL)
+     parent->field = sfield;
+   else {
+     struct stag_field *sf = parent->field;
+     while (sf->next != NULL)
+       sf = sf->next;
+     sf->next = sfield;
+   }
+   /* only create a symbol for this field if the parent has no name */
+   if (!strncmp (".fake", parent->name, 5))
+     {
+       symbolS *sym = symbol_new (name, absolute_section, 
+                                  (valueT)offset, &zero_address_frag);
+       SF_SET_LOCAL (sym);
+       symbol_table_insert (sym);
+     }
+ }
+ 
+ /* [STAG] .struct       [OFFSET]
+    start defining structure offsets (symbols in absolute section)
+ 
+  */
+ static void
+ tic54x_struct (int arg)
+ {
+   int start_offset = 0;
+   int is_union = arg;
+ 
+   if (!current_stag)
+     {
+       /* starting a new struct, switch to absolute section */
+       stag_saved_seg = now_seg;
+       stag_saved_subseg = now_subseg;
+       subseg_set (absolute_section, 0);
+     }
+   /* align the current pointer */
+   else if (current_stag->current_bitfield_offset != 0)
+     {
+       ++abs_section_offset;
+       current_stag->current_bitfield_offset = 0;
+     }
+ 
+   /* offset expression is only meaningful for global .structs */
+   if (!is_union)
+     {
+       /* offset is ignored in inner structs */
+       SKIP_WHITESPACE ();
+       if (!is_end_of_line[(int)*input_line_pointer])
+         start_offset = get_absolute_expression ();
+       else
+         start_offset = 0;
+     }
+ 
+   if (current_stag)
+     {
+       /* nesting, link to outer one */
+       current_stag->inner = (struct stag *)xmalloc (sizeof (struct stag));
+       memset (current_stag->inner, 0, sizeof (struct stag));
+       current_stag->inner->outer = current_stag;
+       current_stag = current_stag->inner;
+       if (start_offset)
+         as_warn (_("Offset on nested structures is ignored"));
+       start_offset = abs_section_offset;
+     }
+   else 
+     {
+       current_stag = (struct stag *)xmalloc (sizeof (struct stag));
+       memset (current_stag, 0, sizeof (struct stag));
+       abs_section_offset = start_offset;
+     }
+   current_stag->is_union = is_union;
+ 
+   if (line_label == NULL)
+     {
+       static int struct_count = 0;
+       char fake[] = ".fake_stagNNNNNNN";
+       sprintf (fake, ".fake_stag%d", struct_count++);
+       current_stag->sym = symbol_new (fake, absolute_section,
+                                       (valueT)abs_section_offset,
+                                       &zero_address_frag);
+     }
+   else
+     {
+       char label[strlen (S_GET_NAME (line_label)) + 1];
+       strcpy (label, S_GET_NAME (line_label));
+       current_stag->sym = symbol_new (label, absolute_section,
+                                       (valueT)abs_section_offset,
+                                       &zero_address_frag);
+     }
+   current_stag->name = S_GET_NAME (current_stag->sym);
+   SF_SET_LOCAL (current_stag->sym);
+   /* nested .structs don't go into the symbol table */
+   if (current_stag->outer == NULL)
+     symbol_table_insert (current_stag->sym);
+ 
+   line_label = NULL;
+ }
+ 
+ /* [LABEL] .endstruct 
+    finish defining structure offsets; optional LABEL's value will be the size
+    of the structure
+  */
+ static void
+ tic54x_endstruct (int is_union)
+ {
+   int size;
+   const char *path = 
+     !strncmp (current_stag->name, ".fake", 5) ? "" : current_stag->name;
+   if (!current_stag || current_stag->is_union != is_union)
+     {
+       as_bad (_(".end%s without preceding .%s"), 
+               is_union ? "union" : "struct",
+               is_union ? "union" : "struct");
+       ignore_rest_of_line ();
+       return;
+     }
+ 
+   /* align end of structures */
+   if (current_stag->current_bitfield_offset)
+     {
+       ++abs_section_offset;
+       current_stag->current_bitfield_offset = 0;
+     }
+ 
+   if (current_stag->is_union)
+     size = current_stag->size;
+   else
+     size = abs_section_offset - S_GET_VALUE (current_stag->sym);
+   if (line_label != NULL)
+     {
+       S_SET_VALUE (line_label, size);
+       symbol_table_insert (line_label);
+       line_label = NULL;
+     }
+ 
+   /* union size has already been calculated */
+   if (!current_stag->is_union)
+     current_stag->size = size;
+   /* nested .structs don't get put in the stag table */
+   if (current_stag->outer == NULL)
+     {
+       hash_insert (stag_hash, current_stag->name, current_stag);
+       stag_add_field_symbols (current_stag, path, 
+                               S_GET_VALUE (current_stag->sym),
+                               NULL, NULL);
+     }
+   current_stag = current_stag->outer;
+ 
+   /* if this is a nested .struct/.union, add it as a field to the enclosing
+      one.  otherwise, restore the section we were in   */
+   if (current_stag != NULL)
+     {
+       stag_add_field (current_stag, current_stag->inner->name,
+                       S_GET_VALUE (current_stag->inner->sym), 
+                       current_stag->inner);
+     }
+   else
+     subseg_set (stag_saved_seg, stag_saved_subseg);
+ }
+ 
+ /* [LABEL]      .tag    STAG
+    Reference a structure within a structure, as a sized field with an optional
+    label. 
+    If used outside of a .struct/.endstruct, overlays the given structure
+    format on the existing allocated space.
+  */
+ static void
+ tic54x_tag (ignore)
+   int ignore ATTRIBUTE_UNUSED;
+ {
+   char *name = input_line_pointer;
+   int c = get_symbol_end ();
+   struct stag *stag = (struct stag *)hash_find (stag_hash, name);
+ 
+   if (!stag)
+     {
+       if (*name)
+         as_bad (_("Unrecognized struct/union tag '%s'"), name);
+       else
+         as_bad (_(".tag requires a structure tag"));
+       ignore_rest_of_line ();
+       return;
+     }
+   if (line_label == NULL)
+     {
+       as_bad (_("Label required for .tag"));
+       ignore_rest_of_line ();
+       return;
+     }
+   else
+     {
+       char label[strlen (S_GET_NAME (line_label))+1];
+       strcpy (label,  S_GET_NAME (line_label));
+       if (current_stag != NULL)
+         stag_add_field (current_stag, label, 
+                         abs_section_offset - S_GET_VALUE (current_stag->sym), 
+                         stag);
+       else
+         {
+           symbolS *sym = symbol_find (label);
+           if (!sym)
+             {
+               as_bad (_(".tag target '%s' undefined"), label);
+               ignore_rest_of_line ();
+               return;
+             }
+           stag_add_field_symbols (stag, S_GET_NAME (sym), 
+                                   S_GET_VALUE (stag->sym), sym, stag->name);
+         }
+     }
+     
+   /* bump by the struct size, but only if we're within a .struct section */
+   if (current_stag != NULL && !current_stag->is_union)
+     abs_section_offset += stag->size;
+ 
+   *input_line_pointer = c;
+   demand_empty_rest_of_line ();
+   line_label = NULL;
+ }
+ 
+ /* handle all .byte, .char, .double, .field, .float, .half, .int, .long,
+    .short, .string, .ubyte, .uchar, .uhalf, .uint, .ulong, .ushort, .uword,
+    and .word 
+ */
+ static void
+ tic54x_struct_field (int type)
+ {
+   int size;
+   int count = 1;
+   int new_bitfield_offset = 0;
+   int field_align = current_stag->current_bitfield_offset != 0;
+   int longword_align = 0;
+ 
+   SKIP_WHITESPACE ();
+   if (!is_end_of_line[(int)*input_line_pointer])
+     count = get_absolute_expression ();
+ 
+   switch (type)
+     {
+     case 'b':
+     case 'B':
+     case 'c':
+     case 'C':
+     case 'h':
+     case 'H':
+     case 'i':
+     case 'I':
+     case 's':
+     case 'S':
+     case 'w':
+     case 'W':
+     case '*': /* string */
+       size = 1;
+       break;
+     case 'f':
+     case 'l':
+     case 'L':
+       longword_align = 1;
+       size = 2;
+       break;
+     case '.': /* bitfield */
+       size = 0;
+       if (count < 1 || count > 32)
+         {
+           as_bad (_(".field count '%d' out of range (1 <= X <= 32)"), count);
+           ignore_rest_of_line ();
+           return;
+         }
+       if (current_stag->current_bitfield_offset + count > 16)
+         {
+           /* set the appropriate size and new field offset */
+           if (count == 32)
+             {
+               size = 2; count = 1;
+             }
+           else if (count > 16)
+             {
+               size = 1; count = 1;
+               new_bitfield_offset = count - 16;
+             }
+           else
+             {
+               new_bitfield_offset = count;
+             }
+         }
+       else
+         {
+           field_align = 0;
+           new_bitfield_offset = current_stag->current_bitfield_offset + count;
+         }
+       break;
+     default:
+       as_bad (_("Unrecognized field type '%c'"), type);
+       ignore_rest_of_line ();
+       return;
+     }
+ 
+   if (field_align)
+     {
+       /* align to the actual starting position of the field */
+       current_stag->current_bitfield_offset = 0;
+       ++abs_section_offset;
+     }
+   /* align to longword boundary */
+   if (longword_align && (abs_section_offset & 0x1))
+     ++abs_section_offset;
+ 
+   if (line_label == NULL)
+     {
+       static int fieldno = 0;
+       char fake[] = ".fake_fieldNNNNN";
+       sprintf (fake, ".fake_field%d", fieldno++);
+       stag_add_field (current_stag, fake, 
+                       abs_section_offset - S_GET_VALUE (current_stag->sym), 
+                       NULL);
+     }
+   else
+     {
+       char label[strlen (S_GET_NAME (line_label) + 1)];
+       strcpy (label, S_GET_NAME (line_label));
+       stag_add_field (current_stag, label, 
+                       abs_section_offset - S_GET_VALUE (current_stag->sym), 
+                       NULL);
+     }
+ 
+   if (current_stag->is_union)
+     {
+       // Note we treat the element as if it were an array of COUNT
+       if (current_stag->size < (unsigned)size * count)
+         current_stag->size = size * count;
+     }
+   else
+     {
+       abs_section_offset += (unsigned)size * count;
+       current_stag->current_bitfield_offset = new_bitfield_offset;
+     }
+   line_label = NULL;
+ }
+ 
+ /* Handle .byte, .word. .int, .long and all variants */
+ int emitting_long = 0;
+ static void 
+ tic54x_cons (int type)
+ {
+   register unsigned int c;
+   int octets;
+ 
+   /* if we're within a .struct construct, don't actually allocate space */
+   if (current_stag != NULL)
+     {
+       tic54x_struct_field (type);
+       return;
+     }
+ 
+ #ifdef md_flush_pending_output
+   md_flush_pending_output ();
+ #endif
+ 
+   generate_lineno_debug ();
+ 
+   /* align long words to long word boundaries (4 octets) */
+   if (type == 'l' || type == 'L')
+     {
+       frag_align (2, 0, 2);
+       /* if there's a label, assign it to the first allocated word */
+       if (line_label != NULL)
+         {
+           symbol_set_frag (line_label, frag_now);
+           S_SET_VALUE (line_label, frag_now_fix ());
+         }
+     }
+ 
+   switch (type)
+     {
+     case 'l':
+     case 'L':
+     case 'x':
+       octets = 4;
+       break;
+     case 'b':
+     case 'B':
+     case 'c':
+     case 'C':
+       octets = 1;
+       break;
+     default:
+       octets = 2;
+       break;
+     }
+ 
+   do
+     {
+       if (*input_line_pointer == '"')
+ 	{
+ 	  input_line_pointer++;
+ 	  while (is_a_char (c = next_char_of_string ()))
+ 	    tic54x_emit_char (c);
+ 	  know (input_line_pointer[-1] == '\"');
+ 	}
+       else
+ 	{
+ 	  expressionS exp;
+ 
+ 	  input_line_pointer = parse_expression (input_line_pointer, &exp);
+ 	  if (exp.X_op == O_constant)
+ 	    {
+               offsetT value = exp.X_add_number;
+               /* truncate overflows */
+ 	      switch (octets)
+ 		{
+ 		case 1:
+                   if ((value > 0 && value > 0xFF)
+                       || (value < 0 && value < - 0x100))
+                     as_warn ("Overflow in expression, truncated to 8 bits");
+ 		  break;
+ 		case 2:
+                   if ((value > 0 && value > 0xFFFF)
+                       || (value < 0 && value < - 0x10000))
+                     as_warn ("Overflow in expression, truncated to 16 bits");
+ 		  break;
+ 		}
+ 	    }
+           if (exp.X_op != O_constant && octets < 2)
+             {
+               /* Disallow .byte with a non constant expression that will
+                  require relocation.  */ 
+               as_bad (_("Relocatable values require at least WORD storage"));
+               ignore_rest_of_line ();
+               return;
+             }
+ 
+           if (exp.X_op != O_constant 
+               && amode == c_mode
+               && octets == 4)
+             {
+               /* FIXME -- at one point TI tools used to output REL16
+                  relocations, but I don't think the latest tools do at all 
+                  The current tools output extended relocations regardless of
+                  the addresing mode (I actually think that ".c_mode" is
+                  totally ignored in the latest tools) */
+               amode = far_mode;
+               emitting_long = 1;
+               emit_expr (&exp, 4);
+               emitting_long = 0;
+               amode = c_mode;
+             }
+           else
+             {
+               emitting_long = octets == 4;
+               emit_expr (&exp, (octets == 1) ? 2 : octets);
+               emitting_long = 0;
+             }
+ 	}
+     }
+   while (*input_line_pointer++ == ',');
+ 
+   input_line_pointer--;		/* Put terminator back into stream.  */
+   demand_empty_rest_of_line ();
+ }
+ 
+ /* .global <symbol>[,...,<symbolN>]
+    .def    <symbol>[,...,<symbolN>]
+    .ref    <symbol>[,...,<symbolN>]
+ 
+    These all identify global symbols. 
+ 
+    .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.
+ 
+    .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.
+ 
+    .global should act as a .ref or .def, as needed.
+ 
+    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
+    these, so we ignore the type here.
+ */
+ void
+ tic54x_global (type)
+      int type;
+ {
+   char *name;
+   int c;
+   symbolS *symbolP;
+ 
+   if (type == 'r')
+       as_warn (_("Use of .def/.ref is deprecated.  Use .global instead"));
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   do
+     {
+       name = input_line_pointer;
+       c = get_symbol_end ();
+       symbolP = symbol_find_or_make (name);
+ 
+       *input_line_pointer = c;
+       S_SET_STORAGE_CLASS (symbolP, C_EXT);
+       if (c == ',')
+ 	{
+ 	  input_line_pointer++;
+ 	  if (is_end_of_line[(int)*input_line_pointer])
+ 	    c = *input_line_pointer;
+ 	}
+     }
+   while (c == ',');
+ 
+   demand_empty_rest_of_line ();
+ }
+ 
+ /* remove the symbol from the local label hash lookup */
+ static void
+ tic54x_remove_local_label (key, value)
+   const char *key;
+   PTR value ATTRIBUTE_UNUSED;
+ {
+   PTR *elem = hash_delete (local_label_hash[macro_level], key);
+   free (elem);
+ }
+ 
+ /* Reset all local labels */
+ static void 
+ tic54x_clear_local_labels (ignored)
+   int ignored ATTRIBUTE_UNUSED;
+ {
+   hash_traverse (local_label_hash[macro_level], tic54x_remove_local_label);
+ }
+ 
+ /* 
+    .text
+    .data
+    .sect "section name"
+ 
+    Initialized section
+    make sure local labels get cleared when changing sections 
+ 
+    ARG is 't' for text, 'd' for data, or '*' for a named section
+ 
+    For compatibility, '*' sections have SEC_DATA set instead of SEC_CODE
+ */
+ static void
+ tic54x_sect (int arg)
+ {
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   /* local labels are cleared when changing sections */
+   tic54x_clear_local_labels (0);
+ 
+   if (arg == 't')
+     s_text (0);
+   else if (arg == 'd')
+     s_data (0);
+   else
+     {
+       char *name = NULL;
+       int len;
+       /* if there are quotes, remove them */
+       if (*input_line_pointer == '"')
+         {
+           name = demand_copy_C_string (&len);
+           demand_empty_rest_of_line ();
+           name = strcpy (xmalloc (len+10), name);
+         }
+       else 
+         {
+           int c;
+           name = input_line_pointer;
+           c = get_symbol_end ();
+           name = strcpy (xmalloc (len+10), name);
+           *input_line_pointer = c;
+           demand_empty_rest_of_line ();
+         }
+       /* make sure all named initialized sections are SEC_DATA */
+       strcat (name, ",\"w\"\n");
+       input_scrub_insert_line (name);
+       obj_coff_section (0);
+ 
+       /* if there was a line label, make sure that it gets assigned the proper
+          section.  This is for compatibility, even though the actual behavior
+          is not explicitly defined.  For consistency, we make .sect behave
+          like .usect, since that is probably what people expect */
+       if (line_label != NULL)
+         {
+           S_SET_SEGMENT (line_label, now_seg);
+           symbol_set_frag (line_label, frag_now);
+           S_SET_VALUE (line_label, frag_now_fix ());
+           if (S_GET_STORAGE_CLASS (line_label) != C_EXT)
+               S_SET_STORAGE_CLASS (line_label, C_LABEL);
+         }
+     }
+ }
+ 
+ /* [symbol] .space space_in_bits 
+    [symbol] .bes space_in_bits
+    BES puts the symbol at the *last* word allocated 
+ 
+    cribbed from s_space
+ */
+ static void
+ tic54x_space (int arg)
+ {
+   expressionS exp;
+   char *p = 0;
+   int octets = 0;
+   long words;
+   int bits_per_byte = (OCTETS_PER_BYTE * 8);
+   int bit_offset = 0;
+   symbolS *label = line_label;
+   int bes = arg;
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+ #ifdef md_flush_pending_output
+   md_flush_pending_output ();
+ #endif
+ 
+   /* read the bit count */
+   expression (&exp);
+ 
+   /* some expressions are unresolvable until later in the assembly pass;
+      postpone until relaxation/fixup.  we also have to postpone if a previous
+      partial allocation has not been completed yet.
+   */
+   if (exp.X_op != O_constant || frag_bit_offset (frag_now, now_seg) == -1)
+     {
+       struct bit_info *bi = xmalloc (sizeof (struct bit_info));
+       char *p;
+ 
+       bi->seg = now_seg;
+       bi->type = bes;
+       bi->sym = label;
+       p = frag_var (rs_machine_dependent, 
+                     65536*2, 1, (relax_substateT) 0,
+                     make_expr_symbol (&exp), (offsetT) 0, 
+                     (char *) bi);
+       if (p)
+         *p = 0;
+ 
+       return;
+     }
+ 
+   /* reduce the required size by any bit offsets currently left over
+      from a previous .space/.bes/.field directive */
+   bit_offset = frag_now->tc_frag_data;
+   if (bit_offset != 0 && bit_offset < 16)
+     {
+       int spare_bits = bits_per_byte - bit_offset;
+       if (spare_bits >= exp.X_add_number)
+         {
+           /* don't have to do anything; sufficient bits have already been
+              allocated; just point the label to the right place */ 
+           if (label != NULL)
+             {
+               symbol_set_frag (label, frag_now);
+               S_SET_VALUE (label, frag_now_fix () - 1);
+               label = NULL;
+             }
+           frag_now->tc_frag_data += exp.X_add_number;
+           goto getout;
+         }
+       exp.X_add_number -= spare_bits;
+       /* set the label to point to the first word allocated, which in this
+          case is the previous word, which was only partially filled */
+       if (!bes && label != NULL)
+         {
+           symbol_set_frag (label, frag_now);
+           S_SET_VALUE (label, frag_now_fix () - 1);
+           label = NULL;
+         }
+     }
+   /* convert bits to bytes/words and octets, rounding up */
+   words = ((exp.X_add_number + bits_per_byte - 1) / bits_per_byte);
+   /* how many do we have left over? */
+   bit_offset = exp.X_add_number % bits_per_byte;
+   octets = words * OCTETS_PER_BYTE;
+   if (octets < 0)
+     {
+       as_warn (_(".space/.bes repeat count is negative, ignored"));
+       goto getout;
+     }
+   else if (octets == 0)
+     {
+       as_warn (_(".space/.bes repeat count is zero, ignored"));
+       goto getout;
+     }
+   
+   /* If we are in the absolute section, just bump the offset.  */
+   if (now_seg == absolute_section)
+     {
+       abs_section_offset += words;
+       if (bes && label != NULL)
+         S_SET_VALUE (label, abs_section_offset - 1);
+       frag_now->tc_frag_data = bit_offset;
+       goto getout;
+     }
+   
+   if (!need_pass_2)
+     p = frag_var (rs_fill, 1, 1, 
+                   (relax_substateT) 0, (symbolS *) 0, 
+                   (offsetT) octets, (char *) 0);
+ 
+   /* make note of how many bits of this word we've allocated so far */
+   frag_now->tc_frag_data = bit_offset;
+ 
+   /* .bes puts label at *last* word allocated */
+   if (bes && label != NULL)
+     {
+       symbol_set_frag (label, frag_now);
+       S_SET_VALUE (label, frag_now_fix ()-1);
+     }
+   
+   if (p)
+     *p = 0;
+ 
+  getout:
+ 
+   demand_empty_rest_of_line ();
+ }
+ 
+ /* [symbol] .usect "section-name", size-in-words 
+                    [, [blocking-flag] [, alignment-flag]] 
+ 
+    Unitialized section.
+    Non-zero blocking means that if the section would cross a page (128-word)
+    boundary, it will be page-aligned.
+    Non-zero alignment aligns on a longword boundary.
+ 
+    Has no effect on the current section.
+ */
+ static void 
+ tic54x_usect (x)
+   int x ATTRIBUTE_UNUSED;
+ {
+   char c;
+   char *name;
+   char *section_name;
+   char *p;
+   segT seg;
+   int size, blocking_flag, alignment_flag;
+   segT current_seg;
+   subsegT current_subseg;
+   flagword flags;
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   current_seg = now_seg;	/* save current seg.  */
+   current_subseg = now_subseg;	/* save current subseg.  */
+ 
+   if (*input_line_pointer == '"')
+     input_line_pointer++;
+   section_name = input_line_pointer;
+   c = get_symbol_end ();	/* Get terminator.  */
+   input_line_pointer++;		/* Skip null symbol terminator.  */
+   name = xmalloc (input_line_pointer - section_name + 1);
+   strcpy (name, section_name);
+ 
+   if (*input_line_pointer == ',')
+     ++input_line_pointer;
+   else if (c != ',')
+     {
+       as_bad (_("Missing size argument"));
+       ignore_rest_of_line ();
+       return;
+     }
+ 
+   size = get_absolute_expression ();
+ 
+   /* read a possibly present third argument (blocking flag) */
+   if (*input_line_pointer == ',')
+     {
+       ++input_line_pointer;
+       if (*input_line_pointer != ',')
+         blocking_flag = get_absolute_expression ();
+       else
+         blocking_flag = 0;
+ 
+       /* read a possibly present fourth argument (alignment flag) */
+       if (*input_line_pointer == ',')
+         {
+           ++input_line_pointer;
+           alignment_flag = get_absolute_expression ();
+         }
+       else
+         alignment_flag = 0;
+     }
+   else
+     blocking_flag = alignment_flag = 0;
+ 
+   seg = subseg_new (name, 0);
+   flags = bfd_get_section_flags (stdoutput, seg) | SEC_ALLOC;
+ 
+   if (alignment_flag)
+     {
+       /* s_align eats end of line; restore it */
+       s_align_bytes (4);
+       --input_line_pointer;
+     }
+ 
+   if (line_label != NULL)
+     {
+       S_SET_SEGMENT (line_label, seg);
+       symbol_set_frag (line_label, frag_now);
+       S_SET_VALUE (line_label, frag_now_fix ());
+       /* set scl to label, since that's what TI does */
+       if (S_GET_STORAGE_CLASS (line_label) != C_EXT)
+         S_SET_STORAGE_CLASS (line_label, C_LABEL);
+     }
+ 
+   seg_info (seg)->bss = 1;	/* Uninitialized data.  */
+ 
+   p = frag_var (rs_fill, 1, 1, 
+                 (relax_substateT) 0, (symbolS *) line_label,
+                 size * OCTETS_PER_BYTE, (char *) 0);
+   *p = 0;
+ 
+   if (blocking_flag)
+     flags |= SEC_BLOCK;
+ 
+   if (!bfd_set_section_flags (stdoutput, seg, flags))
+     as_warn ("Error setting flags for \"%s\": %s", name,
+ 	     bfd_errmsg (bfd_get_error ()));
+ 
+   subseg_set (current_seg, current_subseg);	/* Restore current seg.  */
+   demand_empty_rest_of_line ();
+ } 
+ 
+ static enum cpu_version
+ lookup_version (const char *ver)
+ {
+   enum cpu_version version = VNONE;
+   if (ver[0] == '5' && ver[1] == '4')
+     {
+       if (strlen (ver) == 3 &&
+           (ver[2] == '1' || ver[2] == '2' || ver[2] == '3' ||
+            ver[2] == '5' || ver[2] == '8' || ver[2] == '9'))
+         version = ver[2] - '0';
+       else if (strlen (ver) == 5 &&
+                toupper (ver[3]) == 'L' &&
+                toupper (ver[4]) == 'P' &&
+                (ver[2] == '5' || ver[2] == '6'))
+         version = ver[2] - '0' + 10;
+     }
+ 
+   return version;
+ }
+ 
+ static void
+ set_cpu (enum cpu_version version)
+ {
+   cpu = version;
+   if (version == V545LP || version == V546LP)
+     {
+       symbolS *symbolP = symbol_new ("__allow_lp", absolute_section,
+                                     (valueT)1, &zero_address_frag);
+       SF_SET_LOCAL (symbolP);
+       symbol_table_insert (symbolP);
+     }
+ }
+ 
+ /* .version cpu-version 
+    cpu-version may be one of the following:
+    541
+    542
+    543
+    545
+    545LP
+    546LP
+    548
+    549
+ 
+    This is for compatibility only.  It currently has no affect on assembly.
+ 
+  */
+ static int cpu_needs_set = 1;
+ static void 
+ tic54x_version (x)
+   int x ATTRIBUTE_UNUSED;
+ {
+   enum cpu_version version = VNONE;
+   enum cpu_version old_version = cpu;
+   int c;
+   char *ver;
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   SKIP_WHITESPACE ();
+   ver = input_line_pointer;
+   while (!is_end_of_line[(int)*input_line_pointer])
+     ++input_line_pointer;
+   c = *input_line_pointer;
+   *input_line_pointer = 0;
+   
+   version = lookup_version (ver);
+ 
+   if (cpu != VNONE && cpu != version)
+     as_warn (_("CPU version has already been set"));
+ 
+   if (version == VNONE)
+     {
+       as_bad (_("Unrecognized version '%s'"), ver);
+       ignore_rest_of_line ();
+       return;
+     }
+   else if (assembly_begun && version != old_version)
+     {
+       as_bad (_("Changing of CPU version on the fly not supported"));
+       ignore_rest_of_line ();
+       return;
+     }
+ 
+   set_cpu (version);
+ 
+   *input_line_pointer = c;
+   demand_empty_rest_of_line ();
+ }
+ 
+ /* 'f' = float, 'x' = xfloat, 'd' = double, 'l' = ldouble */
+ static void
+ tic54x_float_cons (int type)
+ {
+   if (current_stag != 0)
+     {
+       tic54x_struct_field ('f');
+     }
+ 
+ #ifdef md_flush_pending_output
+   md_flush_pending_output ();
+ #endif
+   /* align to long word boundary (4 octets) unless it's ".xfloat" */
+   if (type != 'x')
+     {
+       frag_align (2, 0, 2);
+       /* if there's a label, assign it to the first allocated word */
+       if (line_label != NULL)
+         {
+           symbol_set_frag (line_label, frag_now);
+           S_SET_VALUE (line_label, frag_now_fix ());
+         }
+     }
+ 
+   float_cons ('f');
+ }
+ 
+ /* The argument is capitalized if it should be zero-terminated 
+    's' is normal string with upper 8-bits zero-filled, 'p' is packed.
+    Code copied from read.c, and slightly modified so that strings are packed
+    and encoded into the correct octets.
+ */
+ static void
+ tic54x_stringer (int type)
+ {
+   register unsigned int c;
+   char *start;
+   int append_zero = type == 'S' || type == 'P';
+   int packed = type == 'p' || type == 'P';
+   int last_char = -1; /* packed strings need two bytes at a time to encode */
+ 
+   if (current_stag != NULL)
+     {
+       tic54x_struct_field ('*');
+       return;
+     }
+ 
+ #ifdef md_flush_pending_output
+   md_flush_pending_output ();
+ #endif
+ 
+   c = ',';			/* Do loop. */
+   while (c == ',')
+     {
+       SKIP_WHITESPACE ();
+       switch (*input_line_pointer)
+ 	{
+         default:
+           {
+             unsigned short value = get_absolute_expression ();
+             FRAG_APPEND_1_CHAR (value&0xFF);
+             FRAG_APPEND_1_CHAR ((value>>8)&0xFF);
+             break;
+           }
+ 	case '\"':
+ 	  ++input_line_pointer;	/*->1st char of string. */
+ 	  start = input_line_pointer;
+ 	  while (is_a_char (c = next_char_of_string ()))
+ 	    {
+               if (!packed)
+                 {
+                   FRAG_APPEND_1_CHAR (c);
+                   FRAG_APPEND_1_CHAR (0);
+                 }
+               else
+                 {
+                   /* packed strings are filled MS octet first */
+                   if (last_char == -1)
+                     last_char = c;
+                   else
+                     {
+                       FRAG_APPEND_1_CHAR (c);
+                       FRAG_APPEND_1_CHAR (last_char);
+                       last_char = -1;
+                     }
+                 }
+ 	    }
+ 	  if (append_zero)
+             {
+               if (packed && last_char != -1)
+                 {
+                   FRAG_APPEND_1_CHAR (0);
+                   FRAG_APPEND_1_CHAR (last_char);
+                   last_char = -1;
+                 }
+               else
+                 {
+                   FRAG_APPEND_1_CHAR (0);
+                   FRAG_APPEND_1_CHAR (0);
+                 }
+             }
+ 	  know (input_line_pointer[-1] == '\"');
+ 	  break;
+ 	}
+       SKIP_WHITESPACE ();
+       c = *input_line_pointer;
+       if (!is_end_of_line[c])
+         ++input_line_pointer;
+     }
+ 
+   /* finish up any leftover packed string */
+   if (packed && last_char != -1)
+     {
+       FRAG_APPEND_1_CHAR (0);
+       FRAG_APPEND_1_CHAR (last_char);
+     }
+   demand_empty_rest_of_line ();
+ }
+ 
+ static void
+ tic54x_p2align (arg)
+   int arg ATTRIBUTE_UNUSED;
+ {
+   as_bad (_("p2align not supported on this target"));
+ }
+ 
+ static void
+ tic54x_align_words (int arg)
+ {
+   /* only ".align" with no argument is allowed within .struct/.union */
+   int count = arg;
+ 
+   if (!is_end_of_line[(int)*input_line_pointer])
+     {
+       if (arg == 2)
+         as_warn (_("Argument to .even ignored"));
+       else
+         count = get_absolute_expression ();
+     }
+ 
+   if (current_stag != NULL && arg == 128)
+     {
+       if (current_stag->current_bitfield_offset != 0)
+         {
+           current_stag->current_bitfield_offset = 0;
+           ++abs_section_offset;
+         }
+       demand_empty_rest_of_line ();
+       return;
+     }
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   s_align_bytes (count << 1);
+ }
+ 
+ /* initialize multiple-bit fields withing a single word of memory 
+  */
+ static void
+ tic54x_field (ignore)
+   int ignore ATTRIBUTE_UNUSED;
+ {
+   expressionS exp;
+   int size = 16;
+   char *p;
+   valueT value;
+   symbolS *label = line_label;
+ 
+   if (current_stag != NULL)
+     {
+       tic54x_struct_field ('.');
+       return;
+     }
+ 
+   input_line_pointer = parse_expression (input_line_pointer, &exp);
+ 
+   if (*input_line_pointer == ',')
+     {
+       ++input_line_pointer;
+       size = get_absolute_expression ();
+       if (size < 1 || size > 32)
+         {
+           as_bad (_("Invalid field size, must be from 1 to 32"));
+           ignore_rest_of_line ();
+           return;
+         }
+     }
+ 
+   /* truncate values to the field width */
+   if (exp.X_op != O_constant)
+     {
+       /* if the expression value is relocatable, the field size *must* be 16 */
+       if (size != 16)
+         {
+           as_bad (_("field size must be 16 when value is relocatable"));
+           ignore_rest_of_line ();
+           return;
+         }
+ 
+       frag_now->tc_frag_data = 0;
+       emit_expr (&exp, 2);
+     }
+   else
+     {
+       unsigned long fmask = (size == 32) ? 0xFFFFFFFF : (1ul << size) - 1;
+       value = exp.X_add_number;
+       exp.X_add_number &= fmask;
+       if (value != (valueT)exp.X_add_number)
+         as_warn (_("field value truncated"));
+       value = exp.X_add_number;
+       /* bits are stored MS first */
+       while (size >= 16)
+         {
+           frag_now->tc_frag_data = 0;
+           p = frag_more (2);
+           md_number_to_chars (p, (value >> (size - 16)) & 0xFFFF, 2);
+           size -= 16;
+         }
+       if (size > 0)
+         {
+           int bit_offset = frag_bit_offset (frag_now, now_seg);
+           fragS *alloc_frag = bit_offset_frag (frag_now, now_seg);
+           if (bit_offset == -1)
+             {
+               struct bit_info *bi = xmalloc (sizeof (struct bit_info));
+               /* we don't know the previous offset at this time, so store the
+                  info we need and figure it out later */
+               expressionS size_exp;
+               size_exp.X_op = O_constant;
+               size_exp.X_add_number = size;
+               bi->seg = now_seg;
+               bi->type = TYPE_FIELD;
+               bi->value = value;
+               p = frag_var (rs_machine_dependent,
+                             4, 1, (relax_substateT) 0,
+                             make_expr_symbol (&size_exp), (offsetT) 0,
+                             (char *) bi);
+               goto getout;
+             }
+           else if (bit_offset == 0 || bit_offset + size > 16)
+             {
+               /* align a new field */
+               p = frag_more (2);
+               frag_now->tc_frag_data = 0;
+               alloc_frag = frag_now;
+             }
+           else
+             {
+               /* put the new value entirely within the existing one */
+               p = alloc_frag == frag_now ? 
+                 frag_now->fr_literal + frag_now_fix_octets () - 2 :
+                 alloc_frag->fr_literal;
+               if (label != NULL)
+                 {
+                   symbol_set_frag (label, alloc_frag);
+                   if (alloc_frag == frag_now)
+                     S_SET_VALUE (label, frag_now_fix () - 1);
+                   label = NULL;
+                 }
+             }
+           value <<= 16 - alloc_frag->tc_frag_data - size;
+ 
+           /* OR in existing value */
+           if (alloc_frag->tc_frag_data)
+             value |= ((unsigned short)p[1]<<8) | p[0];
+           md_number_to_chars (p, value, 2);
+           alloc_frag->tc_frag_data += size;
+           if (alloc_frag->tc_frag_data == 16)
+             alloc_frag->tc_frag_data = 0;
+         }
+     }
+  getout:
+   demand_empty_rest_of_line ();
+ }
+ 
+ /* 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 */
+ static int
+ tic54x_initialized_section (seg)
+   segT seg;
+ {
+   return !seg_info (seg)->bss;
+ }
+ 
+ /* .clink ["section name"] 
+ 
+    Marks the section as conditionally linked (link only if contents are
+    referenced elsewhere.
+    Without a name, refers to the current initialized section.
+    Name is required for uninitialized sections.
+ 
+  */
+ static void
+ tic54x_clink (ignored)
+   int ignored ATTRIBUTE_UNUSED;
+ {
+   segT seg = now_seg;
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   if (*input_line_pointer == '\"')
+     {
+       char *section_name = ++input_line_pointer;
+       char *name;
+       while (is_a_char (next_char_of_string ()))
+         ;
+       know (input_line_pointer[-1] == '\"');
+       input_line_pointer[-1] = 0;
+       name = xmalloc (input_line_pointer - section_name + 1);
+       strcpy (name, section_name);
+ 
+       seg = bfd_get_section_by_name (stdoutput, name);
+       if (seg == NULL)
+         {
+           as_bad (_("Unrecognized section '%s'"), section_name);
+           ignore_rest_of_line ();
+           return;
+         }
+     }
+   else
+     {
+       if (!tic54x_initialized_section (seg))
+         {
+           as_bad (_("Current section is unitialized, "
+                     "section name required for .clink"));
+           ignore_rest_of_line ();
+           return;
+         }
+     }
+ 
+   seg->flags |= SEC_CLINK;
+ 
+   demand_empty_rest_of_line ();
+ }
+ 
+ /* change the default include directory to be the current source file's
+    directory, instead of the current working directory.  If DOT is non-zero,
+    set to "." instead  */
+ static void
+ tic54x_set_default_include (dot)
+   int dot;
+ {
+   char *dir = ".";
+   char *tmp = NULL;
+   
+   if (!dot)
+     {
+       char *curfile;
+       unsigned lineno;
+       
+       as_where (&curfile, &lineno);
+       dir = strcpy (xmalloc (strlen (curfile)+1), curfile);
+       tmp = strrchr (dir, '/');
+     }
+   if (tmp != NULL)
+     {
+       int len;
+       *tmp = '\0';
+       len = strlen (dir);
+       if (include_dir_count == 0)
+         {
+           include_dirs = (char **) xmalloc (sizeof (*include_dirs));
+           include_dir_count = 1;
+         }
+       include_dirs[0] = dir;
+       if (len > include_dir_maxlen)
+         include_dir_maxlen = len;
+     }
+   else if (include_dirs != NULL)
+     include_dirs[0] = ".";
+ }
+ 
+ /* 
+    .include "filename" | filename 
+    .copy    "filename" | filename
+ 
+    FIXME 'include' file should be omitted from any output listing, 
+      'copy' should be included in any output listing
+    FIXME -- prevent any included files from changing listing (compat only)
+    FIXME -- need to include source file directory in search path; what's a
+       good way to do this?
+ 
+    Entering/exiting included/copied file clears all local labels
+ */
+ static void
+ tic54x_include (ignored)
+   int ignored ATTRIBUTE_UNUSED;
+ {
+   char newblock[] = " .newblock\n";
+   char *filename;
+   char *input;
+   int len, c = -1;
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+   
+   SKIP_WHITESPACE ();
+ 
+   if (*input_line_pointer == '"')
+     {
+       filename = demand_copy_C_string (&len);
+       demand_empty_rest_of_line ();
+     }
+   else
+     {
+       filename = input_line_pointer;
+       while (!is_end_of_line[(int)*input_line_pointer])
+         ++input_line_pointer;
+       c = *input_line_pointer;
+       *input_line_pointer = '\0';
+       filename = strcpy (xmalloc (strlen (filename)+1), filename);
+       *input_line_pointer = c;
+       demand_empty_rest_of_line ();
+     }
+   /* Insert a partial line with the filename (for the sake of s_include)
+      and a .newblock.
+      The included file will be inserted before the newblock, so that the
+      newblock is executed after the included file is processed */
+   input = xmalloc (sizeof (newblock) + strlen (filename) + 4);
+   sprintf (input, "\"%s\"\n%s", filename, newblock);
+   input_scrub_insert_line (input);
+ 
+   tic54x_clear_local_labels (0);
+ 
+   tic54x_set_default_include (0);
+ 
+   s_include (0);
+ }
+ 
+ static void
+ tic54x_message (int type)
+ {
+   char *msg;
+   char c;
+   int len;
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   if (*input_line_pointer == '"')
+     msg = demand_copy_C_string (&len);
+   else
+     {
+       msg = input_line_pointer;
+       while (! is_end_of_line[(int)*input_line_pointer])
+         ++input_line_pointer;
+       c = *input_line_pointer;
+       *input_line_pointer = 0;
+       msg = strcpy (xmalloc (strlen (msg) + 1), msg);
+       *input_line_pointer = c;
+     }
+ 
+   switch (type)
+     {
+     case 'm':
+       as_tsktsk ("%s", msg);
+       break;
+     case 'w':
+       as_warn ("%s", msg);
+       break;
+     case 'e':
+       as_bad ("%s", msg);
+       break;
+     }
+ 
+   demand_empty_rest_of_line ();
+ }
+ 
+ /* .label <symbol> 
+    define a special symbol that refers to the loadtime address rather than the
+    runtime address within the current section.
+ 
+    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
+    run address (vma)
+  */
+ static void
+ tic54x_label (ignored)
+   int ignored ATTRIBUTE_UNUSED;
+ {
+   char *name = input_line_pointer;
+   symbolS *symbolP;
+   int c;
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   c = get_symbol_end ();
+   symbolP = colon (name);
+   S_SET_STORAGE_CLASS (symbolP, C_STATLAB);
+ 
+   *input_line_pointer = c;
+   demand_empty_rest_of_line ();
+ }
+ 
+ /*
+   .mmregs
+   install all memory-mapped register names into the symbol table as absolute
+   local symbols.
+  */
+ static void
+ tic54x_mmregs (ignored)
+   int ignored ATTRIBUTE_UNUSED;
+ {
+   symbol *sym;
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   for (sym = (symbol *)mmregs; sym->name; sym++)
+     {
+       symbolS *symbolP = symbol_new (sym->name, absolute_section,
+                                      (valueT) sym->value, &zero_address_frag);
+       SF_SET_LOCAL (symbolP);
+       symbol_table_insert (symbolP);
+     }
+ }
+ 
+ /*
+   .loop [count]
+   count defaults to 1024
+  */
+ static void
+ tic54x_loop (int count)
+ {
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   SKIP_WHITESPACE ();
+   if (!is_end_of_line[(int)*input_line_pointer])
+       count = get_absolute_expression ();
+ 
+   do_repeat (count, "LOOP", "ENDLOOP");
+ }
+ 
+ /* normally, endloop gets eaten by the preceding loop */
+ static void
+ tic54x_endloop (ignore)
+   int ignore ATTRIBUTE_UNUSED;
+ {
+   as_bad (_("ENDLOOP without corresponding LOOP"));
+   ignore_rest_of_line ();
+ }
+ 
+ /*
+   .break [condition]
+ */
+ static void
+ tic54x_break (ignore)
+   int ignore ATTRIBUTE_UNUSED;
+ {
+   int cond = 1;
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   SKIP_WHITESPACE ();
+   if (!is_end_of_line[(int)*input_line_pointer])
+     {
+       cond = get_absolute_expression ();
+     }
+   if (cond)
+     {
+       end_repeat (substitution_line ? 1 : 0);
+     }
+ }
+ 
+ static void
+ set_address_mode (mode)
+   int mode;
+ {
+   amode = mode;
+   if (mode == far_mode)
+     {
+       symbolS *symbolP = symbol_new ("__allow_far", absolute_section, 
+                                     (valueT)1, &zero_address_frag);
+       SF_SET_LOCAL (symbolP);
+       symbol_table_insert (symbolP);
+     }
+ }
+ 
+ static int address_mode_needs_set = 1;
+ static void
+ tic54x_address_mode (mode)
+   int mode;
+ {
+   if (assembly_begun && amode != (unsigned)mode)
+     {
+       as_bad (_("Mixing of normal and extended addressing not supported"));
+       ignore_rest_of_line ();
+       return;
+     }
+   if (mode == far_mode && cpu != VNONE && cpu != V548 && cpu != V549)
+     {
+       as_bad (_("Extended addressing not supported on the specified CPU"));
+       ignore_rest_of_line ();
+       return;
+     }
+ 
+   set_address_mode (mode);
+   demand_empty_rest_of_line ();
+ }
+ 
+ /* .sblock "section"|section [,...,"section"|section]
+    designate initialized sections for blocking
+  */
+ static void
+ tic54x_sblock (ignore)
+   int ignore ATTRIBUTE_UNUSED;
+ {
+   int c = ',';
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   while (c == ',')
+     {
+       segT seg;
+       char *name;
+       
+       if (*input_line_pointer == '"')
+         {
+           int len;
+           name = demand_copy_C_string (&len);
+         }
+       else
+         {
+           char *section_name = input_line_pointer;
+           c = get_symbol_end ();
+           name = xmalloc (strlen (section_name)+1);
+           strcpy (name, section_name);
+           *input_line_pointer = c;
+         }
+ 
+       seg = bfd_get_section_by_name (stdoutput, name);
+       if (seg == NULL)
+         {
+           as_bad (_("Unrecognized section '%s'"), name);
+           ignore_rest_of_line ();
+           return;
+         }
+       else if (!tic54x_initialized_section (seg))
+         {
+           as_bad (_(".sblock may be used for initialized sections only"));
+           ignore_rest_of_line ();
+           return;
+         }
+       seg->flags |= SEC_BLOCK;
+ 
+       c = *input_line_pointer;
+       if (!is_end_of_line[(int)c])
+         ++input_line_pointer;
+     }
+ 
+   demand_empty_rest_of_line ();
+ }
+ 
+ /* symbol .set value
+    symbol .equ value 
+ 
+    value must be defined externals; no forward-referencing allowed
+    symbols assigned with .set/.equ may not be redefined
+ */
+ static void
+ tic54x_set (ignore)
+   int ignore ATTRIBUTE_UNUSED;
+ {
+   symbolS *symbolP;
+   char *name;
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   if (!line_label)
+     {
+       as_bad (_("Symbol missing for .set/.equ"));
+       ignore_rest_of_line ();
+       return;
+     }
+   name = xstrdup (S_GET_NAME (line_label));
+   line_label = NULL;
+   if ((symbolP = symbol_find (name)) == NULL
+       && (symbolP = md_undefined_symbol (name)) == NULL)
+     {
+       symbolP = symbol_new (name, absolute_section, 0, &zero_address_frag);
+       S_SET_STORAGE_CLASS (symbolP, C_STAT);
+     }
+   free (name);
+   S_SET_DATA_TYPE (symbolP, T_INT);
+   S_SET_SEGMENT (symbolP, absolute_section);
+   symbol_table_insert (symbolP);
+   pseudo_set (symbolP);
+   demand_empty_rest_of_line ();
+ }
+ 
+ /* .fclist
+    .fcnolist
+    list false conditional blocks
+ */
+ static void
+ tic54x_fclist (int show)
+ {
+   if (show)
+     listing &= ~LISTING_NOCOND;
+   else
+     listing |= LISTING_NOCOND;
+   demand_empty_rest_of_line ();
+ }
+ 
+ static void
+ tic54x_sslist (int show)
+ {
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   listing_sslist = show;
+ }
+ 
+ /* .var SYM[,...,SYMN] 
+    define a substitution string to be local to a macro
+ */
+ static void
+ tic54x_var (ignore)
+   int ignore ATTRIBUTE_UNUSED;
+ {
+   static char empty[] = "";
+   char *name;
+   int c;
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   if (macro_level == 0)
+     {
+       as_bad (_(".var may only be used within a macro definition"));
+       ignore_rest_of_line ();
+       return;
+     }
+   do 
+     {
+       if (!isalpha (*input_line_pointer))
+         {
+           as_bad (_("Substitution symbols must begin with a letter"));
+           ignore_rest_of_line ();
+           return;
+         }
+       name = input_line_pointer;
+       c = get_symbol_end ();
+       /* .var symbols start out with a null string */
+       name = strcpy (xmalloc (strlen (name)+1), name);
+       hash_insert (subsym_hash[macro_level], name, empty);
+       *input_line_pointer = c;
+       if (c == ',')
+         {
+           ++input_line_pointer;
+           if (is_end_of_line[(int)*input_line_pointer])
+             c = *input_line_pointer;
+         }
+     }
+   while (c == ',');
+ 
+   demand_empty_rest_of_line ();
+ }
+ 
+ /* .mlib <macro library filename> 
+ 
+    Macro libraries are archived (standard AR-format) text macro definitions
+    Expand the file and include it.
+ 
+    FIXME need to try the source file directory as well
+  */
+ static void
+ tic54x_mlib (ignore)
+   int ignore ATTRIBUTE_UNUSED;
+ {
+   char *filename;
+   char *path;
+   int len, i;
+   bfd *abfd, *mbfd;
+ 
+   ILLEGAL_WITHIN_STRUCT ();
+ 
+   /* parse the filename */
+   if (*input_line_pointer == '"')
+     {
+       if ((filename = demand_copy_C_string (&len)) == NULL)
+         return;
+     }
+   else
+     {
+       SKIP_WHITESPACE ();
+       len = 0;
+       while (! is_end_of_line[(int)*input_line_pointer]
+              && !isspace (*input_line_pointer))
+         {
+           obstack_1grow (&notes, *input_line_pointer);
+           ++input_line_pointer;
+           ++len;
+         }
+       obstack_1grow (&notes, '\0');
+       filename = obstack_finish (&notes);
+     }
+   demand_empty_rest_of_line ();
+ 
+   tic54x_set_default_include (0);
+   path = xmalloc ((unsigned long) len + include_dir_maxlen + 5);
+   for (i=0;i < include_dir_count; i++)
+     {
+       FILE *try;
+       strcpy (path, include_dirs[i]);
+       strcat (path, "/");
+       strcat (path, filename);
+       if ((try = fopen (path, "r")) != NULL)
+         {
+           fclose (try);
+           break;
+         }
+   }
+   if (i >= include_dir_count)
+     {
+       free (path);
+       path = filename;
+     }
+ 
+   /* 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
+      running for a very long time, it really doesn't matter... */ 
+   register_dependency (path);
+ 
+   /* expand all archive entries to temporary files and include them */
+   abfd = bfd_openr (path, NULL);
+   if (!abfd)
+     {
+       as_bad (_("Can't open macro library file '%s' for reading."), path);
+       as_perror ("%s", path);
+       ignore_rest_of_line ();
+       return;
+     }
+   if (!bfd_check_format (abfd, bfd_archive))
+     {
+       as_bad (_("File '%s' not in macro archive format"), path);
+       ignore_rest_of_line ();
+       return;
+     }
+ 
+   /* open each BFD as binary (it should be straight ASCII text) */
+   for (mbfd = bfd_openr_next_archived_file (abfd, NULL);
+        mbfd != NULL; mbfd = bfd_openr_next_archived_file (abfd, mbfd))
+     {
+       /* get a size at least as big as the archive member */
+       bfd_size_type size = bfd_get_size (mbfd);
+       char *buf = xmalloc (size);
+       char *fname = tmpnam (NULL);
+       FILE *ftmp;
+ 
+       /* we're not sure how big it is, but it will be smaller than "size" */
+       bfd_read (buf, size, 1, mbfd);
+ 
+       /* write to a temporary file, then use s_include to include it 
+          a bit of a hack...
+        */
+       ftmp = fopen (fname, "w+b");
+       fwrite ((void *)buf, size, 1, ftmp);
+       if (buf[size-1] != '\n')
+         fwrite ("\n", 1, 1, ftmp);
+       fclose (ftmp);
+       free (buf);
+       input_scrub_insert_file (fname);
+       unlink (fname);
+     }
+ }
+ 
+ const pseudo_typeS md_pseudo_table[] = 
+ {
+   { "algebraic", s_ignore, 0 },
+   { "align", tic54x_align_words, 128 }, 
+   { "even", tic54x_align_words, 2 }, 
+   { "asg", tic54x_asg, 0 },
+   { "eval", tic54x_eval, 0 },
+   { "bss", tic54x_bss, 0 },
+   { "byte", tic54x_cons, 'b' },
+   { "ubyte", tic54x_cons, 'B' },
+   { "char", tic54x_cons, 'c' },
+   { "uchar", tic54x_cons, 'C' },
+   { "clink", tic54x_clink, 0 },
+   { "c_mode", tic54x_address_mode, c_mode },
+   { "copy", tic54x_include, 'c' },
+   { "include", tic54x_include, 'i' },
+   { "data", tic54x_sect, 'd' },
+   { "double", tic54x_float_cons, 'd' },
+   { "ldouble", tic54x_float_cons, 'l' },
+   { "drlist", s_ignore, 0 },
+   { "drnolist", s_ignore, 0 },
+   { "emsg", tic54x_message, 'e' },
+   { "mmsg", tic54x_message, 'm' },
+   { "wmsg", tic54x_message, 'w' },
+   //{ "end",     s_end,                   0 }, 
+   { "far_mode", tic54x_address_mode, far_mode }, 
+   { "fclist",   tic54x_fclist, 1 },
+   { "fcnolist", tic54x_fclist, 0 },
+   { "field", tic54x_field, -1 },
+   { "float", tic54x_float_cons, 'f' },
+   { "xfloat",  tic54x_float_cons, 'x' },
+   { "global", tic54x_global, 'g' },
+   { "def", tic54x_global, 'd' },
+   { "ref", tic54x_global, 'r' },
+   { "half", tic54x_cons, 'h' },
+   { "uhalf", tic54x_cons, 'H' },
+   { "short", tic54x_cons, 's' },
+   { "ushort", tic54x_cons, 'S' },
+   { "if", s_if, (int)O_ne },
+   { "elseif", s_elseif, (int)O_ne },
+   { "else", s_else, 0 },
+   { "endif", s_endif, 0 },
+   { "int", tic54x_cons, 'i' },
+   { "uint", tic54x_cons, 'I' },
+   { "word", tic54x_cons, 'w' },
+   { "uword", tic54x_cons, 'W' },
+   { "label", tic54x_label, 0 }, /* loadtime address */
+   { "length",   s_ignore, 0 }, 
+   { "width",    s_ignore, 0 }, 
+   //{ "list",     listing_list,      1 }, 
+   //{ "nolist",   listing_list,      0 }, 
+   { "long", tic54x_cons, 'l' }, 
+   { "ulong", tic54x_cons, 'L' }, 
+   { "xlong", tic54x_cons, 'x' }, 
+   { "loop", tic54x_loop, 1024 }, 
+   { "break", tic54x_break, 0 }, 
+   { "endloop", tic54x_endloop, 0 }, 
+   { "mlib", tic54x_mlib, 0 }, 
+   { "mlist",     s_ignore, 0 }, 
+   { "mnolist",     s_ignore, 0 }, 
+   { "mmregs", tic54x_mmregs, 0 }, 
+   { "newblock", tic54x_clear_local_labels, 0 }, 
+   { "option",     s_ignore, 0 }, 
+   { "p2align", tic54x_p2align, 0 },
+   //{ "page",     listing_eject,      0 }, 
+   { "sblock", tic54x_sblock, 0 }, 
+   { "sect", tic54x_sect, '*' }, 
+   { "set", tic54x_set, 0 }, 
+   { "equ", tic54x_set, 0 }, 
+   { "space", tic54x_space, 0 }, 
+   { "bes", tic54x_space, 1 }, 
+   { "sslist", tic54x_sslist, 1 }, 
+   { "ssnolist", tic54x_sslist, 0 }, 
+   { "string", tic54x_stringer, 's' },
+   { "pstring", tic54x_stringer, 'p' },
+   { "struct", tic54x_struct, 0 }, 
+   { "tag", tic54x_tag, 0 }, 
+   { "endstruct", tic54x_endstruct, 0 }, 
+   { "tab",     s_ignore, 0 }, 
+   { "text", tic54x_sect, 't' }, 
+   //{ "title",     listing_title,      0 }, 
+   { "union", tic54x_struct, 1 }, 
+   { "endunion", tic54x_endstruct, 1 }, 
+   { "usect", tic54x_usect, 0 }, 
+   { "var", tic54x_var, 0 }, 
+   { "version", tic54x_version, 0 }, 
+   {0, 0, 0}
+ };
+ 
+ /* for debugging, strings for each operand type */
+ #if 0
+ static const char *optypes[] = 
+ {
+   "none", "Xmem", "Ymem", "pmad", "dmad", "Smem", "Lmem", "MMR", "PA",
+   "Sind", "xpmad", "xpmad+", "MMRX", "MMRY", 
+   "SRC1", "SRC", "RND", "DST", 
+   "ARX", 
+   "SHIFT", "SHFT",
+   "B", "A", "lk", "TS", "k8", "16", "BITC", "CC", "CC2", "CC3", "123", "031", 
+   "k5", "k8u", "ASM", "T", "DP", "ARP", "k3", "lku", "N", "SBIT", "12",
+   "k9", "TRN",
+ };
+ #endif
+ 
+ int
+ md_parse_option (c, arg)
+   int c;
+   char *arg;
+ {
+   switch (c)
+     {
+     default:
+       return 0;
+     case OPTION_COFF_VERSION:
+       {
+         int version = atoi (arg);
+         if (version != 0 && version != 1 && version != 2)
+           as_fatal (_("Bad COFF version '%s'"), arg);
+         /* FIXME -- not yet implemented */
+         break;
+       }
+     case OPTION_CPU_VERSION:
+       {
+         cpu = lookup_version (arg);
+         cpu_needs_set = 1;
+         if (cpu == VNONE)
+           as_fatal (_("Bad CPU version '%s'"), arg);
+         break;
+       }
+     case OPTION_ADDRESS_MODE:
+       amode = far_mode;
+       address_mode_needs_set = 1;
+       break;
+     case OPTION_STDERR_TO_FILE:
+       {
+         char *filename = arg;
+         FILE *fp = fopen (filename, "w+");
+         if (fp == NULL)
+           as_fatal (_("Can't redirect stderr to the file '%s'"), filename);
+         fclose (fp);
+         if ((fp = freopen (filename, "w+", stderr)) == NULL)
+           as_fatal (_("Can't redirect stderr to the file '%s'"), filename);
+         break;
+       }
+     }
+ 
+   return 1;
+ }
+ 
+ /* 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
+    to re-use a local label.  We effectively do an automatic .newblock by
+    deleting the local label hash between macro invocations.
+  */
+ void 
+ tic54x_macro_start ()
+ {
+   ++macro_level;
+   subsym_hash[macro_level] = hash_new ();
+   local_label_hash[macro_level] = hash_new ();
+ }
+ 
+ void
+ tic54x_macro_info (void *info)
+ {
+   struct formal_struct
+   {
+     struct formal_struct *next;	/* next formal in list */
+     sb name;			/* name of the formal */
+     sb def;			/* the default value */
+     sb actual;			/* the actual argument (changed on each expansion) */
+     int index;			/* the index of the formal 0..formal_count-1 */
+   } *entry;
+   struct macro_struct
+   {
+     sb sub;			/* substitution text. */
+     int formal_count;		/* number of formal args. */
+     struct formal_struct *formals;	/* pointer to list of formal_structs */
+     struct hash_control *formal_hash; /* hash table of formals. */
+   } *macro;
+ 
+   macro = (struct macro_struct *)info;
+ 
+   /* put the formal args into the substitution symbol table */
+   for (entry = macro->formals; entry; entry = entry->next)
+     {
+       char *name = strncpy (xmalloc (entry->name.len + 1),
+                             entry->name.ptr, entry->name.len);
+       char *value = strncpy (xmalloc (entry->actual.len + 1),
+                              entry->actual.ptr, entry->actual.len);
+       name[entry->name.len] = '\0';
+       value[entry->actual.len] = '\0';
+       hash_insert (subsym_hash[macro_level], name, value);
+     }
+ }
+ 
+ /* get rid of this macro's .var's, arguments, and local labels */
+ void
+ tic54x_macro_end ()
+ {
+   hash_die (subsym_hash[macro_level]);
+   subsym_hash[macro_level] = NULL;
+   hash_die (local_label_hash[macro_level]);
+   local_label_hash[macro_level] = NULL;
+   --macro_level;
+ }
+ 
+ static int
+ subsym_symlen (a, ignore)
+   char *a;
+   char *ignore ATTRIBUTE_UNUSED;
+ {
+   return strlen (a);
+ }
+ 
+ /* compare symbol A to string B */
+ static int
+ subsym_symcmp (char *a, char *b)
+ {
+   return strcmp (a, b);
+ }
+ 
+ /* return the index of the first occurence of B in A, or zero if none 
+    assumes b is an integer char value as a string.  index is one-based */
+ static int
+ subsym_firstch (char *a, char *b)
+ {
+   int val = atoi (b);
+   char *tmp = strchr (a, val);
+   
+   return tmp ? tmp - a + 1 : 0;
+ }
+ 
+ /* similar to firstch, but returns index of last occurrence of B in A */
+ static int
+ subsym_lastch (a, b)
+   char *a;
+   char *b;
+ {
+   int val = atoi (b);
+   char *tmp = strrchr (a, val);
+ 
+   return tmp ? tmp - a + 1 : 0;
+ }
+ 
+ /* returns 1 if string A is defined in the symbol table (NOT the substitution
+    symbol table) */
+ static int
+ subsym_isdefed (a, ignore)
+   char *a;
+   char *ignore ATTRIBUTE_UNUSED;
+ {
+   symbolS *symbolP = symbol_find (a);
+ 
+   return symbolP != NULL;
+ }
+ 
+ /* 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
+    symbols, unsubstituted  */
+ static int
+ subsym_ismember (char *symbol, char *list)
+ {
+   char *elem, *ptr, *listv;
+ 
+   if (!list)
+     return 0;
+ 
+   listv = subsym_lookup (list, macro_level);
+   if (!listv)
+     {
+       as_bad (_("Undefined substitution symbol '%s'"), list);
+       ignore_rest_of_line ();
+       return 0;
+     }
+ 
+   ptr = elem = xmalloc (strlen (listv)+1);
+   strcpy (elem, listv);
+   while (*ptr && *ptr != ',')
+     ++ptr;
+   *ptr++ = 0;
+ 
+   subsym_create_or_replace (symbol, elem);
+ 
+   /* reassign the list */
+   subsym_create_or_replace (list, ptr);
+   
+   /* assume this value, docs aren't clear */
+   return *list != 0;
+ }
+ 
+ /* return zero if not a constant; otherwise:
+    1 if binary
+    2 if octal
+    3 if hexadecimal
+    4 if character
+    5 if decimal
+ */
+ static int
+ subsym_iscons (a, ignore)
+   char *a;
+   char *ignore ATTRIBUTE_UNUSED;
+ {
+   expressionS exp;
+ 
+   parse_expression (a, &exp);
+ 
+   if (exp.X_op == O_constant)
+     {
+       int len = strlen (a);
+ 
+       switch (toupper (a[len-1]))
+         {
+         case 'B':
+           return 1;
+         case 'Q':
+           return 2;
+         case 'H':
+           return 3;
+         case '\'':
+           return 4;
+         default:
+           break;
+         }
+       /* no suffix; either octal, hex, or decimal */
+       if (*a == '0' && len > 1)
+         {
+           if (toupper (a[1]) == 'X')
+             return 3;
+           return 2;
+         }
+       return 5;
+     }
+ 
+   return 0;
+ }
+ 
+ /* return 1 if A is a valid symbol name.  Expects string input   */
+ static int
+ subsym_isname (a, ignore)
+   char *a;
+   char *ignore ATTRIBUTE_UNUSED;
+ {
+   if (!is_name_beginner (*a))
+     return 0;
+   while (*a)
+     {
+       if (!is_part_of_name (*a))
+         return 0;
+       ++a;
+     }
+   return 1;
+ }
+ 
+ /* return whether the string is a register; accepts ar0-7, unless .mmregs has
+    been seen; if so, recognize any memory-mapped register.
+    Note this does not recognize "A" or "B" accumulators */
+ static int
+ subsym_isreg (a, ignore)
+   char *a;
+   char *ignore ATTRIBUTE_UNUSED;
+ {
+   if (hash_find (reg_hash, a))
+     return 1;
+   if (hash_find (mmreg_hash, a))
+     return 1;
+   return 0;
+ }
+ 
+ /* Return the structrure size, given the stag */
+ static int
+ subsym_structsz (name, ignore)
+   char *name;
+   char *ignore ATTRIBUTE_UNUSED;
+ {
+   struct stag *stag = (struct stag *)hash_find (stag_hash, name);
+   if (stag)
+     return stag->size;
+ 
+   return 0;
+ }
+ 
+ /* If anybody actually uses this, they can fix it :)
+    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
+    structure within another structure, or it might be something else
+    altogether.  since the TI assembler doesn't seem to ever do anything but
+    return zero, we punt and return zero.
+  */
+ static int
+ subsym_structacc (stag_name, ignore)
+   char *stag_name ATTRIBUTE_UNUSED;
+   char *ignore ATTRIBUTE_UNUSED;
+ {
+   return 0;
+ }
+ 
+ static float
+ math_ceil (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (float)ceil (arg1);
+ }
+ static float
+ math_cvi (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (int)arg1;
+ }
+ static float
+ math_floor (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (float)floor (arg1);
+ }
+ static float
+ math_fmod (float arg1, float arg2)
+ {
+   return (int)arg1 % (int)arg2;
+ }
+ static float
+ math_int (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return ((float)((int)arg1)) == arg1;
+ }
+ static float
+ math_round (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return arg1 > 0 ? (int)(arg1 + 0.5) : (int)(arg1 - 0.5);
+ }
+ static float
+ math_sgn (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (arg1 < 0) ? -1 : (arg1 ? 1 : 0);
+ }
+ static float
+ math_trunc (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (int)arg1;
+ }
+ 
+ static float
+ math_acos (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (float)acos (arg1);
+ }
+ static float
+ math_asin (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (float)asin (arg1);
+ }
+ static float
+ math_atan (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (float)atan (arg1);
+ }
+ static float
+ math_atan2(float arg1, float arg2)
+ {
+   return (float)atan2 (arg1, arg2);
+ }
+ static float
+ math_cosh (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (float)cosh (arg1);
+ }
+ static float
+ math_cos (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (float)cos (arg1);
+ }
+ static float
+ math_cvf (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (float)arg1;
+ }
+ static float
+ math_exp (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (float)exp (arg1);
+ }
+ static float
+ math_fabs (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (float)fabs (arg1);
+ }
+ /* expr1 * 2^expr2 */
+ static float
+ math_ldexp (float arg1, float arg2)
+ {
+   return arg1 * (float)pow (2.0, arg2);
+ }
+ static float
+ math_log10 (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (float)log10 (arg1);
+ }
+ static float
+ math_log (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (float)log (arg1);
+ }
+ static float
+ math_max (float arg1, float arg2)
+ {
+   return (arg1 > arg2) ? arg1 : arg2;
+ }
+ static float
+ math_min (float arg1, float arg2)
+ {
+   return (arg1 < arg2) ? arg1 : arg2;
+ }
+ static float
+ math_pow (float arg1, float arg2)
+ {
+   return (float)pow (arg1, arg2);
+ }
+ static float
+ math_sin (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (float)sin (arg1);
+ }
+ static float
+ math_sinh (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (float)sinh (arg1);
+ }
+ static float
+ math_sqrt (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (float)sqrt (arg1);
+ }
+ static float
+ math_tan (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (float)tan (arg1);
+ }
+ static float
+ math_tanh (arg1, ignore)
+   float arg1;
+   float ignore ATTRIBUTE_UNUSED;
+ {
+   return (float)tanh (arg1);
+ }
+ 
+ /* built-in substitution symbol functions and math functions */
+ typedef struct 
+ {
+   char *name;
+   int (*proc)(char *, char *);
+   int nargs;
+ } subsym_proc_entry;
+ 
+ static const subsym_proc_entry subsym_procs[] = {
+   /* assembler built-in string substitution functions */
+   { "$symlen", subsym_symlen, 1,  },
+   { "$symcmp", subsym_symcmp, 2,  },
+   { "$firstch", subsym_firstch, 2,  },
+   { "$lastch", subsym_lastch, 2,  },
+   { "$isdefed", subsym_isdefed, 1,  },
+   { "$ismember", subsym_ismember, 2,  },
+   { "$iscons", subsym_iscons, 1,  },
+   { "$isname", subsym_isname, 1,  },
+   { "$isreg", subsym_isreg, 1,  },
+   { "$structsz", subsym_structsz, 1,  },
+   { "$structacc", subsym_structacc, 1,  },
+   { NULL, NULL, 0 },
+ };
+ 
+ typedef struct
+ {
+   char *name;
+   float (*proc)(float, float);
+   int nargs;
+   int int_return;
+ } math_proc_entry;
+ 
+ static const math_proc_entry math_procs[] = {
+   /* integer-returning built-in math functions */
+   { "$cvi", math_cvi, 1, 1 },
+   { "$int", math_int, 1, 1 },
+   { "$sgn", math_sgn, 1, 1 },
+ 
+   /* float-returning built-in math functions */
+   { "$acos", math_acos, 1, 0 },
+   { "$asin", math_asin, 1, 0 },
+   { "$atan", math_atan, 1, 0 },
+   { "$atan2", math_atan2, 2, 0 }, 
+   { "$ceil", math_ceil, 1, 0 },
+   { "$cosh", math_cosh, 1, 0 },
+   { "$cos", math_cos, 1, 0 },
+   { "$cvf", math_cvf, 1, 0 },
+   { "$exp", math_exp, 1, 0 },
+   { "$fabs", math_fabs, 1, 0 },
+   { "$floor", math_floor, 1, 0 },
+   { "$fmod", math_fmod, 2, 0 },
+   { "$ldexp", math_ldexp, 2, 0 },
+   { "$log10", math_log10, 1, 0 },
+   { "$log", math_log, 1, 0 },
+   { "$max", math_max, 2, 0 },
+   { "$min", math_min, 2, 0 },
+   { "$pow", math_pow, 2, 0 },
+   { "$round", math_round, 1, 0 },
+   { "$sin", math_sin, 1, 0 },
+   { "$sinh", math_sinh, 1, 0 },
+   { "$sqrt", math_sqrt, 1, 0 },
+   { "$tan", math_tan, 1, 0 },
+   { "$tanh", math_tanh, 1, 0 },
+   { "$trunc", math_trunc, 1, 0 },
+   { NULL, NULL, 0, 0 },
+ };
+ 
+ void
+ md_begin ()
+ {
+   template *opcode;
+   partemplate *paropcode;
+   symbol *sym;
+   const subsym_proc_entry *subsym_proc;
+   const math_proc_entry *math_proc;
+   const char *hash_err;
+   char **symname;
+   char *TIC54X_DIR = getenv ("TIC54X_DIR");
+   char *A_DIR = TIC54X_DIR ? TIC54X_DIR : getenv ("A_DIR");
+ 
+   local_label_id = 0;
+ 
+   /* look for A_DIR and add it to the include list */
+   if (A_DIR != NULL)
+     {
+       char *tmp = xstrdup (A_DIR);
+       do {
+         char *next = strchr (tmp, ';');
+         if (next)
+           *next++ = '\0';
+         add_include_dir (tmp);
+         tmp = next;
+       } while (tmp != NULL);
+     }
+ 
+   op_hash = hash_new ();
+   for (opcode=(template *)tic54x_optab; opcode->name; opcode++)
+     {
+       if (hash_find (op_hash, opcode->name))
+         continue;
+       hash_err = hash_insert (op_hash, opcode->name, (char *)opcode);
+       if (hash_err)
+         as_fatal ("Internal Error: Can't hash %s: %s", 
+                   opcode->name, hash_err);
+     }
+   parop_hash = hash_new ();
+   for (paropcode=(partemplate *)tic54x_paroptab; paropcode->name; paropcode++)
+     {
+       if (hash_find (parop_hash, paropcode->name))
+         continue;
+       hash_err = hash_insert (parop_hash, paropcode->name, (char *)paropcode);
+       if (hash_err)
+         as_fatal ("Internal Error: Can't hash %s: %s", 
+                   paropcode->name, hash_err);
+     }
+   reg_hash = hash_new ();
+   for (sym = (symbol *)regs; sym->name; sym++)
+     {
+       /* add basic registers to the symbol table */
+       symbolS *symbolP = symbol_new (sym->name, absolute_section,
+                                      (valueT)sym->value, &zero_address_frag);
+       SF_SET_LOCAL (symbolP);
+       symbol_table_insert (symbolP);
+       hash_err = hash_insert (reg_hash, sym->name, (char *)sym);
+     }
+   for (sym = (symbol *)mmregs; sym->name; sym++)
+     hash_err = hash_insert (reg_hash, sym->name, (char *)sym);
+   mmreg_hash = hash_new ();
+   for (sym = (symbol *)mmregs; sym->name; sym++)
+     {
+       hash_err = hash_insert (mmreg_hash, sym->name, (char *)sym);
+     }
+   cc_hash = hash_new ();
+   for (sym = (symbol *)condition_codes; sym->name; sym++)
+     {
+       hash_err = hash_insert (cc_hash, sym->name, (char *)sym);
+     }
+   cc2_hash = hash_new ();
+   for (sym = (symbol *)cc2_codes; sym->name; sym++)
+     {
+       hash_err = hash_insert (cc2_hash, sym->name, (char *)sym);
+     }
+   cc3_hash = hash_new ();
+   for (sym = (symbol *)cc3_codes; sym->name; sym++)
+     {
+       hash_err = hash_insert (cc3_hash, sym->name, (char *)sym);
+     }
+   sbit_hash = hash_new ();
+   for (sym = (symbol *)status_bits; sym->name; sym++)
+     {
+       hash_err = hash_insert (sbit_hash, sym->name, (char *)sym);
+     }
+   misc_symbol_hash = hash_new ();
+   for (symname = (char **)misc_symbols; *symname; symname++)
+     {
+       hash_err = hash_insert (misc_symbol_hash, *symname, *symname);
+     }
+   /* only the base substitution table and local label table are initialized;
+      the others (for local macro substitution) get instantiated as needed */
+   local_label_hash[0] = hash_new ();
+   subsym_hash[0] = hash_new ();
+   for (subsym_proc = subsym_procs; subsym_proc->name; subsym_proc++)
+     {
+       hash_err = hash_insert (subsym_hash[0], subsym_proc->name, 
+                               (char *)subsym_proc);
+     }
+   math_hash = hash_new ();
+   for (math_proc = math_procs; math_proc->name; math_proc++)
+     {
+       /* insert into the main subsym hash for recognition; insert into
+          the math hash to actually store information */
+       hash_err = hash_insert (subsym_hash[0], math_proc->name,
+                               (char *)math_proc);
+       hash_err = hash_insert (math_hash, math_proc->name,
+                               (char *)math_proc);
+     }
+   subsym_recurse_hash = hash_new ();
+   stag_hash = hash_new ();
+ }
+ 
+ typedef struct _tic54x_insn {
+   const template *tm;               /* opcode template */
+   const partemplate *ptm;           /* parallel opcode template */
+ 
+   char mnemonic[MAX_LINE];          /* opcode name/mnemonic */
+   char parmnemonic[MAX_LINE];       /* 2nd mnemonic of parallel insn */
+ 
+   int opcount;
+   struct opstruct {
+     char buf[MAX_LINE];
+     enum optype type;
+     expressionS exp;
+   } operands[MAX_OPERANDS];
+ 
+   int paropcount;
+   struct opstruct paroperands[MAX_OPERANDS];
+ 
+   int is_lkaddr;
+   int lkoperand;
+   int words;                        /* size of insn in 16-bit words */
+   int using_default_dst;            /* do we need to explicitly set an 
+                                        omitted OP_DST operand? */
+   struct {
+     unsigned short word;            /* final encoded opcode data */
+     int unresolved;
+     int r_nchars;                     /* relocation size */
+     bfd_reloc_code_real_type r_type; /* relocation type */
+     expressionS addr_expr;          /* storage for unresolved expressions */
+   } opcode[3];
+ } tic54x_insn;
+ 
+ static int encode_operand (tic54x_insn *, enum optype, struct opstruct *);
+ static int encode_dmad (tic54x_insn *, struct opstruct *, int);
+ static int operands_match (tic54x_insn *, struct opstruct *, int, 
+                            const enum optype *, int, int);
+ static int encode_address (tic54x_insn *, struct opstruct *);
+ 
+ static int
+ is_accumulator (struct opstruct *operand)
+ {
+   return strcasecmp (operand->buf, "a") == 0 
+     || strcasecmp (operand->buf, "b") == 0;
+ }
+ 
+ /* return the number of operands found, or -1 on error, copying the operands
+    into the given array and the accompanying expressions into the next array */
+ static int
+ get_operands (operands, line)
+   struct opstruct operands[];
+   char *line;
+ {
+   char *lptr = line;
+   int numexp = 0;
+   int expecting_operand = 0;
+   int i;
+ 
+   while (numexp < MAX_OPERANDS && !is_end_of_line[(int)*lptr])
+     {
+       int paren_not_balanced = 0;
+       char *op_start, *op_end;
+       while (*lptr && isspace (*lptr))
+         ++lptr;
+       op_start = lptr;
+       while (paren_not_balanced || *lptr != ',')
+         {
+           if (*lptr == '\0')
+             {
+               if (paren_not_balanced)
+                 {
+                   as_bad ("Unbalanced parenthesis in operand %d", numexp);
+                   return -1;
+                 }
+               else
+                 break;
+             }
+           if (*lptr == '(')
+             ++paren_not_balanced;
+           else if (*lptr == ')')
+             --paren_not_balanced;
+           ++lptr;
+         }
+       op_end = lptr;
+       if (op_end != op_start)
+         {
+           int len = op_end - op_start;
+           strncpy (operands[numexp].buf, op_start, len);
+           operands[numexp].buf[len] = 0;
+           /* trim trailing spaces; while the preprocessor gets rid of most,
+              there are weird usage patterns that can introduce them
+              (i.e. using strings for macro args) */
+           while (len > 0 && isspace (operands[numexp].buf[len-1]))
+             operands[numexp].buf[--len] = 0;
+           lptr = op_end;
+           ++numexp;
+         }
+       else 
+         {
+           if (expecting_operand || *lptr == ',')
+             {
+               as_bad ("Expecting operand after ','");
+               return -1;
+             }
+         }
+       if (*lptr == ',')
+         {
+           if (*++lptr == '\0')
+             {
+               as_bad ("Expecting operand after ','");
+               return -1;
+             }
+           expecting_operand = 1;
+         }
+     }
+ 
+   while (*lptr && isspace (*lptr++))
+     ;
+   if (!is_end_of_line[(int)*lptr])
+     {
+       as_bad ("Extra junk on line");
+       return -1;
+     }
+ 
+   // ok, now parse them into expressions
+   for (i=0;i < numexp;i++)
+     {
+       memset (&operands[i].exp, 0, sizeof (operands[i].exp));
+       if (operands[i].buf[0] == '#')
+         {
+           /* immediate */
+           parse_expression (operands[i].buf + 1, &operands[i].exp);
+         }
+       else if (operands[i].buf[0] == '@')
+         {
+           /* direct notation */
+           parse_expression (operands[i].buf + 1, &operands[i].exp);
+         }
+       else if (operands[i].buf[0] == '*')
+         {
+           /* indirect */
+           char *paren = strchr (operands[i].buf, '(');
+           /* allow immediate syntax in the inner expression */
+           if (paren && paren[1] == '#')
+               *++paren = '(';
+ 
+           /* pull out the lk expression or SP offset, if present */
+           if (paren != NULL)
+             {
+               int len = strlen (paren);
+               char *end = paren + len;
+               int c;
+               while (end[-1] != ')')
+                 if (--end <= paren)
+                   {
+                     as_bad (_("Badly formed address expression"));
+                     return -1;
+                   }
+               c = *end;
+               *end = '\0';
+               parse_expression (paren, &operands[i].exp);
+               *end = c;
+             }
+           else
+             operands[i].exp.X_op = O_absent;
+         }
+       else
+         {
+           parse_expression (operands[i].buf, &operands[i].exp);
+         }
+     }
+ 
+   return numexp;
+ }
+ 
+ /*
+  * Predicates for different operand types
+  */
+ static int
+ is_immediate (struct opstruct *operand)
+ {
+   return  *operand->buf == '#';
+ }
+ 
+ /* This is distinguished from immediate because some numbers must be constants
+    and must *not* have the '#' prefix */
+ static int
+ is_absolute (struct opstruct *operand)
+ {
+   return operand->exp.X_op == O_constant && !is_immediate (operand);
+ }
+ 
+ /* is this an indirect operand? */
+ static int
+ is_indirect (struct opstruct *operand)
+ {
+   return operand->buf[0] == '*';
+ }
+ 
+ /* is this a valid dual-memory operand? */
+ static int
+ is_dual (struct opstruct *operand)
+ {
+   if (is_indirect (operand) && strncasecmp (operand->buf, "*ar", 3) == 0)
+     {
+       char *tmp = operand->buf + 3;
+       int arf;
+       int valid_mod;
+       arf = *tmp++ - '0';
+       /* only allow *ARx, *ARx-, *ARx+, or *ARx+0% */
+       valid_mod = *tmp == '\0' ||
+         strcasecmp (tmp, "-") == 0 ||
+         strcasecmp (tmp, "+") == 0 || 
+         strcasecmp (tmp, "+0%") == 0;
+       return arf >= 2 && arf <= 5 && valid_mod;
+     }
+   return 0;
+ }
+ 
+ static int
+ is_mmreg (struct opstruct *operand)
+ {
+   return is_absolute (operand) || is_immediate (operand)
+     || hash_find (mmreg_hash, operand->buf) != 0;
+ }
+ 
+ static int
+ is_type (operand, type)
+   struct opstruct *operand;
+   enum optype type;
+ {
+   switch (type)
+     {
+     case OP_None:
+       return operand->buf[0] == 0;
+     case OP_Xmem:
+     case OP_Ymem:
+       return is_dual (operand);
+     case OP_Sind:
+       return is_indirect (operand);
+     case OP_xpmad_ms7:
+       /* this one *must* be immediate */
+       return is_immediate (operand);
+     case OP_xpmad:
+     case OP_pmad:
+     case OP_PA:
+     case OP_dmad:
+     case OP_Lmem:
+     case OP_MMR:
+       return 1;
+     case OP_Smem:
+       /* address may be a numeric, indirect, or an expression */
+       return !is_immediate (operand);
+     case OP_MMRY:
+     case OP_MMRX:
+       return is_mmreg (operand);
+     case OP_SRC:
+     case OP_SRC1:
+     case OP_RND:
+     case OP_DST:
+       return is_accumulator (operand);
+     case OP_B:
+       return is_accumulator (operand) && toupper (operand->buf[0]) == 'B';
+     case OP_A:
+       return is_accumulator (operand) && toupper (operand->buf[0]) == 'A';
+     case OP_ARX:
+       return strncasecmp ("ar", operand->buf, 2) == 0 
+         && isdigit (operand->buf[2]);
+     case OP_SBIT:
+       return hash_find (sbit_hash, operand->buf) != 0 || is_absolute (operand);
+     case OP_CC:
+       return hash_find (cc_hash, operand->buf) != 0;
+     case OP_CC2:
+       return hash_find (cc2_hash, operand->buf) != 0;
+     case OP_CC3:
+       return hash_find (cc3_hash, operand->buf) != 0 
+         || is_immediate (operand) || is_absolute (operand);
+     case OP_16:
+       return (is_immediate (operand) || is_absolute (operand))
+         && operand->exp.X_add_number == 16;
+     case OP_N:
+       /* allow st0 or st1 instead of a numeric */
+       return is_absolute (operand) || is_immediate (operand) ||
+         strcasecmp ("st0", operand->buf) == 0 ||
+         strcasecmp ("st1", operand->buf) == 0;
+     case OP_12:
+     case OP_123:
+       return is_absolute (operand) || is_immediate (operand);
+     case OP_SHFT:
+       return (is_immediate (operand) || is_absolute (operand))
+         && operand->exp.X_add_number >= 0 && operand->exp.X_add_number < 16;
+     case OP_SHIFT:
+       /* let this one catch out-of-range values */
+       return (is_immediate (operand) || is_absolute (operand))
+         && operand->exp.X_add_number != 16;
+     case OP_BITC:
+     case OP_031:
+     case OP_k8:
+       return is_absolute (operand) || is_immediate (operand);
+     case OP_k8u:
+       return is_immediate (operand) 
+         && operand->exp.X_op == O_constant 
+         && operand->exp.X_add_number >= 0
+         && operand->exp.X_add_number < 256;
+     case OP_lk:
+     case OP_lku:
+       /* allow anything; assumes opcodes are ordered with Smem operands 
+          versions first */
+       return 1;
+     case OP_k5:
+     case OP_k3:
+     case OP_k9:
+       /* just make sure it's an integer; check range later */
+       return is_immediate (operand);
+     case OP_T:
+       return strcasecmp ("t", operand->buf) == 0 || 
+         strcasecmp ("treg", operand->buf) == 0;
+     case OP_TS:
+       return strcasecmp ("ts", operand->buf) == 0;
+     case OP_ASM:
+       return strcasecmp ("asm", operand->buf) == 0;
+     case OP_TRN:
+       return strcasecmp ("trn", operand->buf) == 0;
+     case OP_DP:
+       return strcasecmp ("dp", operand->buf) == 0;
+     case OP_ARP:
+       return strcasecmp ("arp", operand->buf) == 0;
+     default:
+       return 0;
+     }
+ }
+ 
+ static int
+ operands_match (insn, operands, opcount, refoptype, minops, maxops)
+   tic54x_insn *insn;
+   struct opstruct *operands;
+   int opcount;
+   const enum optype *refoptype;
+   int minops, maxops;
+ {
+   int op = 0, refop = 0;
+ 
+   if (opcount == 0 && minops == 0)
+     {
+       return 1;
+     }
+ 
+   while (op <= maxops && refop <= maxops)
+     {
+       while (!is_type (&operands[op], OPTYPE (refoptype[refop])))
+         {
+           /* skip an optional template operand if it doesn't agree
+              with the current operand
+            */
+           if (refoptype[refop] & OPT)
+             {
+               ++refop;
+               --maxops;
+               if (refop > maxops)
+                 return 0;
+             }
+           else
+             return 0;
+         }
+ 
+       /* save the actual operand type for later use */
+       operands[op].type = OPTYPE (refoptype[refop]);
+       ++refop;
+       ++op;
+       /* have we matched them all yet? */
+       if (op == opcount)
+         {
+           while (op < maxops)
+             {
+               /* if a later operand is *not* optional, no match */
+               if ((refoptype[refop] & OPT) == 0)
+                 return 0;
+               /* flag any implicit default OP_DST operands so we know to add
+                  them explicitly when encoding the operand later */
+               if (OPTYPE (refoptype[refop]) == OP_DST)
+                 insn->using_default_dst = 1;
+               ++refop;
+               ++op;
+             }
+ 
+           return 1;
+         }
+     }
+ 
+   return 0;
+ }
+ 
+ /* 16-bit direct memory address 
+    Explicit dmad operands are always in last word of insn (usually second
+    word, but bumped to third if lk addressing is used)
+ 
+    We allow *(dmad) notation because the TI assembler allows it.
+ 
+    XPC_CODE: 
+    0 for 16-bit addresses
+    1 for full 23-bit addresses
+    2 for the upper 7 bits of a 23-bit address (LDX)
+ */
+ static int
+ encode_dmad (insn, operand, xpc_code)
+   tic54x_insn *insn;
+   struct opstruct *operand;
+   int xpc_code;
+ {
+   int op = 1 + insn->is_lkaddr;
+ 
+   /* only allow *(dmad) expressions; all others are invalid */
+   if (is_indirect (operand) && operand->buf[strlen (operand->buf)-1] != ')')
+     {
+       as_bad (_("Invalid dmad syntax '%s'"), operand->buf);
+       return 0;
+     }
+ 
+   insn->opcode[op].addr_expr = operand->exp;
+ 
+   if (insn->opcode[op].addr_expr.X_op == O_constant)
+     {
+       valueT value = insn->opcode[op].addr_expr.X_add_number;
+       if (xpc_code == 1)
+         {
+           insn->opcode[0].word &= 0xFF80;
+           insn->opcode[0].word |= (value >> 16) & 0x7F;
+           insn->opcode[1].word = value & 0xFFFF;
+         }
+       else if (xpc_code == 2)
+         insn->opcode[op].word = (value >> 16) & 0xFFFF;
+       else
+         insn->opcode[op].word = value;
+     }
+   else
+     {
+       /* do the fixup later; just store the expression */
+       insn->opcode[op].word = 0;
+       insn->opcode[op].r_nchars = 2;
+ 
+       if (amode == c_mode)
+         insn->opcode[op].r_type = BFD_RELOC_TIC54X_16_OF_23;
+       else if (xpc_code == 1)
+         {
+           /* this relocation spans two words, so adjust accordingly */
+           insn->opcode[0].addr_expr = operand->exp;
+           insn->opcode[0].r_type = BFD_RELOC_TIC54X_23;
+           insn->opcode[0].r_nchars = 4;
+           insn->opcode[0].unresolved = 1;
+           /* it's really 2 words, but we want to stop encoding after the
+              first, since we must encode both words at once */
+           insn->words = 1;
+         }
+       else if (xpc_code == 2)
+         insn->opcode[op].r_type = BFD_RELOC_TIC54X_MS7_OF_23;
+       else
+         insn->opcode[op].r_type = BFD_RELOC_TIC54X_16_OF_23;
+ 
+       insn->opcode[op].unresolved = 1;
+     }
+ 
+   return 1;
+ }
+ 
+ 
+ /* 7-bit direct address encoding */
+ static int
+ encode_address (insn, operand)
+   tic54x_insn *insn;
+   struct opstruct *operand;
+ {
+   /* Assumes that dma addresses are *always* in word 0 of the opcode */
+   insn->opcode[0].addr_expr = operand->exp;
+ 
+   if (operand->exp.X_op == O_constant)
+     insn->opcode[0].word |= (operand->exp.X_add_number & 0x7F);
+   else
+     {
+       /* do the fixup later; just store the expression */
+       insn->opcode[0].r_nchars = 1;
+       insn->opcode[0].r_type = BFD_RELOC_TIC54X_PARTLS7;
+       insn->opcode[0].unresolved = 1;
+     }
+ 
+   return 1;
+ }
+ 
+ static int
+ encode_indirect (insn, operand)
+   tic54x_insn *insn;
+   struct opstruct *operand;
+ {
+   int arf;
+   int mod;
+ 
+   if (insn->is_lkaddr)
+     {
+       /* lk addresses always go in the second insn word */
+       mod = ((toupper (operand->buf[1]) == 'A') ? 12 :
+              (operand->buf[1] == '(') ? 15 :
+              (strchr (operand->buf, '%') != NULL) ? 14 : 13);
+       arf = ((mod == 12) ? operand->buf[3] - '0' :
+              (mod == 15) ? 0 : operand->buf[4] - '0');
+       
+       insn->opcode[1].addr_expr = operand->exp;
+ 
+       if (operand->exp.X_op == O_constant)
+         insn->opcode[1].word = operand->exp.X_add_number;
+       else
+         {
+           insn->opcode[1].word = 0;
+           insn->opcode[1].r_nchars = 2;
+           insn->opcode[1].r_type = BFD_RELOC_TIC54X_16_OF_23;
+           insn->opcode[1].unresolved = 1;
+         }
+     }
+   else if (strncasecmp (operand->buf, "*sp (", 4) == 0)
+     {
+       /* stack offsets look the same as 7-bit direct addressing */
+       return encode_address (insn, operand);
+     }
+   else
+     {
+       arf = (toupper (operand->buf[1]) == 'A' ? 
+              operand->buf[3] : operand->buf[4]) - '0';
+       
+       if (operand->buf[1] == '+') 
+         {
+           mod = 3;                    /* *+ARx */
+           if (insn->tm->flags & FL_SMR)
+             as_warn (_("Address mode *+ARx is write-only. "
+                        "Results of reading are undefined."));
+         }
+       else if (operand->buf[4] == '\0') 
+         mod = 0;                    /* *ARx */
+       else if (operand->buf[5] == '\0')
+         mod = (operand->buf[4] == '-' ? 1 : 2); /* *ARx+ / *ARx- */
+       else if (operand->buf[6] == '\0')
+         {
+           if (operand->buf[5] == '0')
+             mod = (operand->buf[4] == '-' ? 5 : 6); /* *ARx+0 / *ARx-0 */
+           else
+             mod = (operand->buf[4] == '-' ? 8 : 10);/* *ARx+% / *ARx-% */
+         }
+       else if (toupper (operand->buf[6]) == 'B')
+         mod = (operand->buf[4] == '-' ? 4 : 7); /* ARx+0B / *ARx-0B */
+       else if (toupper (operand->buf[6]) == '%')
+         mod = (operand->buf[4] == '-' ? 9 : 11); /* ARx+0% / *ARx - 0% */
+       else
+         {
+           as_bad (_("Unrecognized indirect address format \"%s\""), 
+                   operand->buf);
+           return 0;
+         }
+     }      
+ 
+   insn->opcode[0].word |= 0x80 | (mod<<3) | arf;
+ 
+   return 1;
+ }
+ 
+ static int
+ encode_integer (insn, operand, which, min, max, mask)
+   tic54x_insn *insn;
+   struct opstruct *operand;
+   int which, min, max;
+   unsigned short mask;
+ {
+   long parse, integer;
+ 
+   insn->opcode[which].addr_expr = operand->exp;
+ 
+   if (operand->exp.X_op == O_constant)
+     {
+       parse = operand->exp.X_add_number;
+       /* hack -- fixup for 16-bit hex quantities that get converted positive
+          instead of negative */
+       if ((parse & 0x8000) && min == -32768 && max == 32767)
+         integer = (short)parse;
+       else
+         integer = parse;
+ 
+       if (integer >= min && integer <= max)
+         {
+           insn->opcode[which].word |= (integer & mask);
+           return 1;
+         }
+       as_bad (_("Operand '%s' out of range (%d <= x <= %d)"), 
+               operand->buf, min, max);
+     }
+   else
+     {
+       if (insn->opcode[which].addr_expr.X_op == O_constant)
+         {
+           insn->opcode[which].word |= 
+             insn->opcode[which].addr_expr.X_add_number & mask;
+         }
+       else
+         {
+           /* do the fixup later; just store the expression */
+           bfd_reloc_code_real_type rtype = 
+             (mask == 0x1FF ? BFD_RELOC_TIC54X_PARTMS9 : 
+              mask == 0xFFFF ? BFD_RELOC_TIC54X_16_OF_23 :
+              mask == 0x7F ? BFD_RELOC_TIC54X_PARTLS7 : BFD_RELOC_8);
+           int size = (mask == 0x1FF || mask == 0xFFFF) ? 2 : 1;
+ 
+           if (rtype == BFD_RELOC_8)
+             as_bad (_("Error in relocation handling"));
+ 
+           insn->opcode[which].r_nchars = size;
+           insn->opcode[which].r_type = rtype;
+           insn->opcode[which].unresolved = 1;
+         }
+ 
+       return 1;
+     }
+ 
+   return 0;
+ }
+ 
+ static int
+ encode_condition (insn, operand)
+   tic54x_insn *insn;
+   struct opstruct *operand;
+ {
+   symbol *cc = (symbol *)hash_find (cc_hash, operand->buf);
+   if (!cc)
+     {
+       as_bad (_("Unrecognized condition code \"%s\""), operand->buf);
+       return 0;
+     }
+ #define CC_GROUP 0x40
+ #define CC_ACC   0x08
+ #define CATG_A1  0x07
+ #define CATG_B1  0x30
+ #define CATG_A2  0x30
+ #define CATG_B2  0x0C
+ #define CATG_C2  0x03
+   /* disallow group 1 conditions mixed with group 2 conditions
+      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 (((insn->opcode[0].word & 0xFF) != 0))
+     {
+       if ((insn->opcode[0].word & CC_GROUP) != (cc->value & CC_GROUP))
+         {
+           as_bad (_("Condition \"%s\" does not match preceding group"), 
+                   operand->buf);
+           return 0;
+         }
+       if (insn->opcode[0].word & CC_GROUP)
+         {
+           if ((insn->opcode[0].word & CC_ACC) != (cc->value & CC_ACC))
+             {
+               as_bad (_("Condition \"%s\" uses a different accumulator from "
+                         "a preceding condition"), 
+                       operand->buf);
+               return 0;
+             }
+           if ((insn->opcode[0].word & CATG_A1) && (cc->value & CATG_A1))
+             {
+               as_bad (_("Only one comparison conditional allowed"));
+               return 0;
+             }
+           if ((insn->opcode[0].word & CATG_B1) && (cc->value & CATG_B1))
+             {
+               as_bad (_("Only one overflow conditional allowed"));
+               return 0;
+             }
+         }
+       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_C2) && (cc->value & CATG_C2)))
+         {
+           as_bad (_("Duplicate %s conditional"), operand->buf);
+           return 0;
+         }
+     }
+ 
+   insn->opcode[0].word |= cc->value;
+   return 1;
+ }
+ 
+ static int
+ encode_cc3 (insn, operand)
+   tic54x_insn *insn;
+   struct opstruct *operand;
+ {
+   symbol *cc3 = (symbol *)hash_find (cc3_hash, operand->buf);
+   int value = cc3 ? cc3->value : operand->exp.X_add_number << 8;
+ 
+   if ((value & 0x0300) != value)
+     {
+       as_bad (_("Unrecognized condition code \"%s\""), operand->buf);
+       return 0;
+     }
+   insn->opcode[0].word |= value;
+   return 1;
+ }
+ 
+ static int
+ encode_arx (insn, operand)
+   tic54x_insn *insn;
+   struct opstruct *operand;
+ {
+   int arf = strlen (operand->buf) >= 3 ? operand->buf[2] - '0' : -1;
+   if (strncasecmp ("ar", operand->buf, 2) || arf < 0 || arf > 7)
+     {
+       as_bad (_("Invalid auxiliary register (use AR0-AR7)"));
+       return 0;
+     }
+   insn->opcode[0].word |= arf;
+   return 1;
+ }
+ 
+ static int
+ encode_cc2 (insn, operand)
+   tic54x_insn *insn;
+   struct opstruct *operand;
+ {
+   symbol *cc2 = (symbol *)hash_find (cc2_hash, operand->buf);
+   if (!cc2)
+     {
+       as_bad (_("Unrecognized condition code \"%s\""), operand->buf);
+       return 0;
+     }
+   insn->opcode[0].word |= cc2->value;
+   return 1;
+ }
+ 
+ static int
+ encode_operand (insn, type, operand)
+   tic54x_insn *insn;
+   enum optype type;
+   struct opstruct *operand;
+ {
+   int ext = insn->tm && ((insn->tm->flags & FL_EXT) != 0);
+ 
+   if (type == OP_MMR && operand->exp.X_op != O_constant)
+     {
+       /* disallow long-constant addressing for memory-mapped addressing */
+       if (insn->is_lkaddr)
+         {
+           as_bad (_("lk addressing modes are invalid for memory-mapped "
+                     "register addressing"));
+           return 0;
+         }
+       type = OP_Smem;
+       /* warn about *+ARx when used with MMR operands */
+       if (strncasecmp (operand->buf, "*+ar", 4) == 0)
+         {
+           as_warn (_("Address mode *+ARx is not allowed in memory-mapped "
+                      "register addressing.  Resulting behavior is "
+                      "undefined."));
+         }
+     }
+ 
+   switch (type)
+     {
+     case OP_None:
+       return 1;
+     case OP_dmad:
+       /* 16-bit immediate value */
+       return encode_dmad (insn, operand, 0);
+     case OP_SRC:
+       if (toupper (*operand->buf) == 'B')
+         {
+           insn->opcode[ext ?  (1 + insn->is_lkaddr) : 0].word |= (1<<9);
+           if (insn->using_default_dst)
+             insn->opcode[ext ? (1 + insn->is_lkaddr) : 0].word |= (1<<8);
+         }
+       return 1;
+     case OP_RND:
+       /* make sure this agrees with with the OP_DST operand */
+       if (!((toupper (operand->buf[0]) == 'B') ^
+             ((insn->opcode[0].word & (1<<8)) != 0)))
+         {
+           as_bad (_("Destination accumulator for each part of this parallel "
+                     "instruction must be different"));
+           return 0;
+         }
+       return 1;
+     case OP_SRC1:
+     case OP_DST:
+       if (toupper (operand->buf[0]) == 'B')
+         insn->opcode[ext ? (1 + insn->is_lkaddr) : 0].word |= (1<<8);
+       return 1;
+     case OP_Xmem:
+     case OP_Ymem:
+     {
+       int mod = (operand->buf[4] == '\0' ? 0 : /* *arx */
+                  operand->buf[4] == '-' ? 1 : /* *arx- */
+                  operand->buf[5] == '\0' ? 2 : 3); /* *arx+, *arx+0% */
+       int arf = operand->buf[3] - '0' - 2;
+       int code = (mod << 2)|arf;
+       insn->opcode[0].word |= (code << (type == OP_Xmem ? 4 : 0));
+       return 1;
+     }
+     case OP_Lmem:
+     case OP_Smem:
+       if (!is_indirect (operand))
+         return encode_address (insn, operand);
+       /* fall through */
+     case OP_Sind:
+       return encode_indirect (insn, operand);
+     case OP_xpmad_ms7:
+       return encode_dmad (insn, operand, 2);
+     case OP_xpmad:
+       return encode_dmad (insn, operand, 1);
+     case OP_PA:
+     case OP_pmad:
+       return encode_dmad (insn, operand, 0);
+     case OP_ARX:
+       return encode_arx (insn, operand);
+     case OP_MMRX:
+     case OP_MMRY:
+     case OP_MMR:
+     {
+       int value = operand->exp.X_add_number;
+       if (type == OP_MMR)
+         insn->opcode[0].word |= value;
+       else {
+         if (value < 16 || value > 24)
+           {
+             as_bad (_("Memory mapped register \"%s\" out of range"), 
+                     operand->buf);
+             return 0;
+           }
+         if (type == OP_MMRX)
+           insn->opcode[0].word |= (value - 16) << 4;
+         else
+           insn->opcode[0].word |= (value - 16);
+       }
+       return 1;
+     }
+     case OP_B:
+     case OP_A:
+       return 1;
+     case OP_SHFT:
+       return encode_integer (insn, operand, ext + insn->is_lkaddr, 
+                              0, 15, 0xF);
+     case OP_SHIFT:
+       return encode_integer (insn, operand, ext + insn->is_lkaddr, 
+                              -16, 15, 0x1F);
+     case OP_lk:
+       return encode_integer (insn, operand, 1 + insn->is_lkaddr,
+                              -32768, 32767, 0xFFFF);
+     case OP_CC:
+       return encode_condition (insn, operand);
+     case OP_CC2:
+       return encode_cc2 (insn, operand);
+     case OP_CC3:
+       return encode_cc3 (insn, operand);
+     case OP_BITC:
+       return encode_integer (insn, operand, 0, 0, 15, 0xF);
+     case OP_k8:
+       return encode_integer (insn, operand, 0, -128, 127, 0xFF);
+     case OP_123:
+     {
+       int value = operand->exp.X_add_number;
+       int code;
+       if (value < 1 || value > 3)
+         {
+           as_bad (_("Invalid operand (use 1, 2, or 3)"));
+           return 0;
+         }
+       code = value == 1 ? 0 : value == 2 ? 0x2 : 0x1;
+       insn->opcode[0].word |= (code << 8);
+       return 1;
+     }
+     case OP_031:
+       return encode_integer (insn, operand, 0, 0, 31, 0x1F);
+     case OP_k8u:
+       return encode_integer (insn, operand, 0, 0, 255, 0xFF);
+     case OP_lku:
+       return encode_integer (insn, operand, 1 + insn->is_lkaddr, 
+                              0, 65535, 0xFFFF);
+     case OP_SBIT:
+     {
+       symbol *sbit = (symbol *)hash_find (sbit_hash, operand->buf);
+       int value = is_absolute (operand) ? 
+         operand->exp.X_add_number : (sbit ? sbit->value : -1);
+       int reg = 0;
+       
+       if (insn->opcount == 1)
+         {
+           if (!sbit)
+             {
+               as_bad (_("A status register or status bit name is required"));
+               return 0;
+             }
+           /* guess the register based on the status bit; "ovb" is the last
+              status bit defined for st0 */
+           if (sbit > (symbol *)hash_find (sbit_hash, "ovb"))
+             reg = 1;
+         }
+       if (value == -1)
+         {
+           as_bad (_("Unrecognized status bit \"%s\""), operand->buf);
+           return 0;
+         }
+       insn->opcode[0].word |= value;
+       insn->opcode[0].word |= (reg << 9);
+       return 1;
+     }
+     case OP_N:
+       if (strcasecmp (operand->buf, "st0") == 0
+           || strcasecmp (operand->buf, "st1") == 0)
+         {
+           insn->opcode[0].word |= ((unsigned short)(operand->buf[2] - '0'))<<9;
+           return 1;
+         }
+       else if (operand->exp.X_op == O_constant
+                && (operand->exp.X_add_number == 0 
+                    || operand->exp.X_add_number == 1))
+         {
+           insn->opcode[0].word |= ((unsigned short)
+                                    (operand->exp.X_add_number))<<9;
+           return 1;
+         }
+       as_bad (_("Invalid status register \"%s\""), operand->buf);
+       return 0;
+     case OP_k5:
+       return encode_integer (insn, operand, 0, -16, 15, 0x1F);
+     case OP_k3:
+       return encode_integer (insn, operand, 0, 0, 7, 0x7);
+     case OP_k9:
+       return encode_integer (insn, operand, 0, 0, 0x1FF, 0x1FF);
+     case OP_12:
+       if (operand->exp.X_add_number != 1 
+           && operand->exp.X_add_number != 2)
+         {
+           as_bad (_("Operand \"%s\" out of range (use 1 or 2)"), operand->buf);
+           return 0;
+         }
+       insn->opcode[0].word |= (operand->exp.X_add_number - 1) << 9;
+       return 1;
+     case OP_16:
+     case OP_T:
+     case OP_TS:
+     case OP_ASM:
+     case OP_TRN:
+     case OP_DP:
+     case OP_ARP:
+       /* no encoding necessary */
+       return 1;
+     default:
+       return 0;
+     }
+ 
+   return 1;
+ }
+ 
+ static void
+ emit_insn (insn)
+   tic54x_insn *insn;
+ {
+   int i;
+   for (i=0;i < insn->words;i++)
+     {
+       int size = (insn->opcode[i].unresolved 
+                   && insn->opcode[i].r_type == BFD_RELOC_TIC54X_23) ? 4 : 2;
+       char *p = frag_more (size);
+ 
+       if (size == 2)
+         md_number_to_chars (p, (valueT)insn->opcode[i].word, 2);
+       else
+         md_number_to_chars (p, (valueT)insn->opcode[i].word << 16, 4);
+         
+       if (insn->opcode[i].unresolved)
+         fix_new_exp (frag_now, p - frag_now->fr_literal,
+                      insn->opcode[i].r_nchars, &insn->opcode[i].addr_expr,
+                      false, insn->opcode[i].r_type);
+     }
+ }
+ 
+ /* convert the operand strings into appropriate opcode values 
+    return the total number of words used by the instruction
+  */
+ static int
+ build_insn (insn)
+   tic54x_insn *insn;
+ {
+   int i;
+ 
+   /* only non-parallel instructions support lk addressing */
+   if (insn->tm)
+     {
+       for (i=0; i < insn->opcount; i++)
+         {
+           if ((OPTYPE (insn->operands[i].type) == OP_Smem ||
+                OPTYPE (insn->operands[i].type) == OP_Lmem ||
+                OPTYPE (insn->operands[i].type) == OP_Sind) &&
+               strchr (insn->operands[i].buf, '(') &&
+               /* don't mistake stack-relative addressing for lk addressing */
+               strncasecmp (insn->operands[i].buf, "*sp (", 4) != 0)
+             {
+               insn->is_lkaddr = 1;
+               insn->lkoperand = i;
+               break;
+             }
+         }
+     }
+   insn->words = 
+     (insn->tm ? insn->tm->words : insn->ptm->words) + insn->is_lkaddr;
+ 
+   insn->opcode[0].word = insn->tm ? insn->tm->opcode : insn->ptm->opcode;
+   if (insn->tm && (insn->tm->flags & FL_EXT))
+       insn->opcode[1 + insn->is_lkaddr].word = insn->tm->opcode2;
+ 
+   for (i=0; i < insn->opcount; i++)
+     {
+       enum optype type = insn->operands[i].type;
+       if (!encode_operand (insn, type, &insn->operands[i]))
+         return 0;
+     }
+   if (insn->ptm) for (i=0; i < insn->paropcount; i++)
+     {
+       enum optype partype = insn->paroperands[i].type;
+       if (!encode_operand (insn, partype, &insn->paroperands[i]))
+         return 0;
+     }
+ 
+   emit_insn (insn);
+ 
+   return insn->words;
+ }
+ 
+ static int
+ optimize_insn (insn)
+   tic54x_insn *insn;
+ {
+   /* 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)
+   if (strcasecmp (insn->tm->name, "add") == 0)
+     {
+       if (insn->opcount > 1 &&
+           is_accumulator (&insn->operands[insn->opcount-2]) &&
+           is_accumulator (&insn->operands[insn->opcount-1]) &&
+           strcasecmp (insn->operands[insn->opcount-2].buf,
+                      insn->operands[insn->opcount-1].buf) == 0)
+         {
+           --insn->opcount;
+           insn->using_default_dst = 1;
+           return 1;
+         }
+       
+       /* try to collapse if Xmem and shift count is zero */
+       if ((OPTYPE (insn->tm->operand_types[0]) == OP_Xmem &&
+            OPTYPE (insn->tm->operand_types[1]) == OP_SHFT &&
+            is_zero (insn->operands[1])) ||
+           /* Or if Smem, shift is zero or absent, and SRC == DST */
+           (OPTYPE (insn->tm->operand_types[0]) == OP_Smem &&
+            OPTYPE (insn->tm->operand_types[1]) == OP_SHIFT &&
+            is_type (&insn->operands[1], OP_SHIFT) && 
+            is_zero (insn->operands[1]) && insn->opcount == 3))
+         {
+           insn->operands[1] = insn->operands[2];
+           insn->opcount = 2;
+           return 1;
+         }
+     }
+   else if (strcasecmp (insn->tm->name, "ld") == 0)
+     {
+       if (insn->opcount == 3 && insn->operands[0].type != OP_SRC)
+         {
+           if ((OPTYPE (insn->tm->operand_types[1]) == OP_SHIFT ||
+                OPTYPE (insn->tm->operand_types[1]) == OP_SHFT) &&
+               is_zero (insn->operands[1]) &&
+               (OPTYPE (insn->tm->operand_types[0]) != OP_lk ||
+                (insn->operands[0].exp.X_op == O_constant &&
+                 insn->operands[0].exp.X_add_number <= 255 &&
+                 insn->operands[0].exp.X_add_number >= 0)))
+             {
+               insn->operands[1] = insn->operands[2];
+               insn->opcount = 2;
+               return 1;
+             }
+         }
+     }
+   else if (strcasecmp (insn->tm->name, "sth") == 0 ||
+            strcasecmp (insn->tm->name, "stl") == 0)
+     {
+       if ((OPTYPE (insn->tm->operand_types[1]) == OP_SHIFT ||
+            OPTYPE (insn->tm->operand_types[1]) == OP_SHFT) &&
+           is_zero (insn->operands[1]))
+         {
+           insn->operands[1] = insn->operands[2];
+           insn->opcount = 2;
+           return 1;
+         }
+     }
+   else if (strcasecmp (insn->tm->name, "sub") == 0)
+     {
+       if (insn->opcount > 1 &&
+           is_accumulator (&insn->operands[insn->opcount-2]) &&
+           is_accumulator (&insn->operands[insn->opcount-1]) &&
+           strcasecmp (insn->operands[insn->opcount-2].buf,
+                      insn->operands[insn->opcount-1].buf) == 0)
+         {
+           --insn->opcount;
+           insn->using_default_dst = 1;
+           return 1;
+         }
+       
+       if (((OPTYPE (insn->tm->operand_types[0]) == OP_Smem &&
+             OPTYPE (insn->tm->operand_types[1]) == OP_SHIFT) ||
+            (OPTYPE (insn->tm->operand_types[0]) == OP_Xmem &&
+             OPTYPE (insn->tm->operand_types[1]) == OP_SHFT)) &&
+           is_zero (insn->operands[1]) &&
+           insn->opcount == 3)
+         {
+           insn->operands[1] = insn->operands[2];
+           insn->opcount = 2;
+           return 1;
+         }
+     }
+   return 0;
+ }
+ 
+ /* Find a matching template if possible, and get the operand strings */
+ static int
+ tic54x_parse_insn (insn, line)
+   tic54x_insn *insn;
+   char *line;
+ {
+   insn->tm = (template *)hash_find (op_hash, insn->mnemonic);
+   if (!insn->tm)
+     {
+       as_bad (_("Unrecognized instruction \"%s\""), insn->mnemonic);
+       return 0;
+     }
+ 
+   insn->opcount = get_operands (insn->operands, line);
+   if (insn->opcount < 0)
+       return 0;
+ 
+   /* check each variation of operands for this mnemonic */
+   while (insn->tm->name && strcasecmp (insn->tm->name, insn->mnemonic) == 0)
+     {
+       if (insn->opcount >= insn->tm->minops &&
+           insn->opcount <= insn->tm->maxops &&
+           operands_match (insn, &insn->operands[0], insn->opcount,
+                           insn->tm->operand_types, 
+                           insn->tm->minops, insn->tm->maxops))
+         {
+           /* SUCCESS! now try some optimizations */
+           if (optimize_insn (insn))
+             {
+               insn->tm = (template *)hash_find (op_hash, 
+                                                 insn->mnemonic);
+               continue;
+             }
+ 
+           return 1;
+         }
+       ++(insn->tm);
+     }
+   as_bad (_("Unrecognized operand list '%s' for instruction '%s'"), 
+             line, insn->mnemonic);
+   return 0;
+ }
+ 
+ /* we set this in start_line_hook, 'cause if we do a line replacement, we
+    won't be able to see the next line */
+ static int parallel_on_next_line_hint = 0;
+ /* See if this is part of a parallel instruction
+    Look for a subsequent line starting with "||"
+ */
+ static int
+ next_line_shows_parallel (next_line)
+   char *next_line;
+ {
+   /* look for the second half */
+   while (isspace (*next_line))
+     ++next_line;
+ 
+   return (next_line[0] == PARALLEL_SEPARATOR &&
+           next_line[1] == PARALLEL_SEPARATOR);
+ }
+ 
+ static int
+ tic54x_parse_parallel_insn_firstline (insn, line)
+   tic54x_insn *insn;
+   char *line;
+ {
+   insn->ptm = (partemplate *)hash_find (parop_hash, insn->mnemonic);
+   if (!insn->ptm)
+     {
+       as_bad (_("Unrecognized parallel instruction \"%s\""), 
+               insn->mnemonic);
+       return 0;
+     }
+ 
+   while (insn->ptm->name && strcasecmp (insn->ptm->name,
+                                        insn->mnemonic) == 0)
+     {
+       insn->opcount = get_operands (insn->operands, line);
+       if (insn->opcount < 0)
+         return 0;
+       if (insn->opcount == 2 && 
+           operands_match (insn, &insn->operands[0], insn->opcount,
+                           insn->ptm->operand_types, 2, 2))
+         {
+           return 1;
+         }
+       ++(insn->ptm);
+     }
+   /* didn't find a matching parallel; try for a normal insn */
+   return 0;
+ }
+ 
+ /* parse the second line of a two-line parallel instruction */
+ static int
+ tic54x_parse_parallel_insn_lastline (insn, line)
+   tic54x_insn *insn;
+   char *line;
+ {
+   int valid_mnemonic = 0;
+   
+   insn->paropcount = get_operands (insn->paroperands, line);
+   while (insn->ptm->name && strcasecmp (insn->ptm->name,
+                                        insn->mnemonic) == 0)
+     {
+       if (strcasecmp (insn->ptm->parname, insn->parmnemonic) == 0)
+         {
+           valid_mnemonic = 1;
+           if (insn->paropcount >= insn->ptm->minops &&
+               insn->paropcount <= insn->ptm->maxops &&
+               operands_match (insn, insn->paroperands, 
+                               insn->paropcount,
+                               insn->ptm->paroperand_types,
+                               insn->ptm->minops, insn->ptm->maxops))
+             {
+               return 1;
+             }
+         }
+       ++(insn->ptm);
+     }
+   if (valid_mnemonic)
+     as_bad (_("Invalid operand (s) for parallel instruction \"%s\""),
+             insn->parmnemonic);
+   else
+     as_bad (_("Unrecognized parallel instruction combination \"%s || %s\""),
+             insn->mnemonic, insn->parmnemonic);
+ 
+   return 0;
+ }
+ 
+ /* if quotes found, return copy of line up to closing quote;
+    otherwise up until terminator 
+    if it's a string, pass as-is; otherwise attempt substitution symbol
+    replacement on the value
+ */
+ static char *
+ subsym_get_arg (char *line, char *terminators, char **str, int nosub)
+ {
+   char *ptr = line;
+   char *endp;
+   int is_string = *line == '"';
+   int is_char = isdigit (*line);
+ 
+   if (is_char)
+     {
+       while (isdigit (*ptr))
+         ++ptr;
+       endp = ptr;
+       *str = xmalloc (ptr - line + 1);
+       strncpy (*str, line, ptr - line);
+       (*str)[ptr - line] = 0;
+     }
+   else if (is_string)
+     {
+       char *savedp = input_line_pointer;
+       int len;
+       input_line_pointer = ptr;
+       *str = demand_copy_C_string (&len);
+       endp = input_line_pointer;
+       input_line_pointer = savedp;
+ 
+       /* do forced substitutions if requested */
+       if (!nosub && **str == ':')
+         *str = subsym_substitute (*str, 1);
+     }
+   else
+     {
+       char *term = terminators;
+       char *value = NULL;
+ 
+       while (*ptr && *ptr != *term)
+         {
+           if (!*term)
+             {
+               term = terminators;
+               ++ptr;
+             }
+           else
+             ++term;
+         }
+       endp = ptr;
+       *str = xmalloc (ptr - line + 1);
+       strncpy (*str, line, ptr - line);
+       (*str)[ptr - line] = 0;
+       /* do simple substitution, if available */
+       if (!nosub && (value = subsym_lookup (*str, macro_level)) != NULL)
+           *str = value;
+     }
+ 
+   return endp;
+ }
+ 
+ /* replace the given substitution string.
+    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
+    that's compatible w/TI's assembler.
+ */
+ static void
+ subsym_create_or_replace (char *name, char *value)
+ {
+   int i;
+ 
+   for (i=macro_level;i > 0;i--)
+     {
+       if (hash_find (subsym_hash[i], name))
+         {
+           hash_replace (subsym_hash[i], name, value);
+           return;
+         }
+     }
+   if (hash_find (subsym_hash[0], name))
+     hash_replace (subsym_hash[0], name, value);
+   else
+     hash_insert (subsym_hash[0], name, value);
+ }
+ 
+ /* look up the substitution string replacement for the given symbol 
+    start with the innermost macro substituion table given and work outwards */
+ static char *
+ subsym_lookup (char *name, int nest_level)
+ {
+   char *value = hash_find (subsym_hash[nest_level], name);
+ 
+   if (value || nest_level == 0)
+     return value;
+ 
+   return subsym_lookup (name, nest_level-1);
+ }
+ 
+ /* do substitution-symbol replacement on the given line (recursively).
+    return the argument if no substitution was done 
+ 
+    Also look for built-in functions ($func (arg)) and local labels.
+ 
+    if FORCED is set, look for forced substitutions of the form ':SYMBOL:'.
+ */
+ static char *
+ subsym_substitute (char *line, int forced)
+ {
+   /* for each apparent symbol, see if it's a substitution symbol, and if so,
+      replace it in the input */
+   char *replacement; /* current replacement for LINE */
+   char *head; /* start of line */
+   char *ptr; /* current examination point */
+   int changed = 0; /* did we make a substitution? */
+   int eval_line = 0; /* is this line a .eval/.asg statement? */
+   int eval_symbol = 0; /* are we in the middle of the symbol for .eval/.asg? */
+   char *eval_end = NULL;
+   int recurse = 1;
+   int line_conditional = 0;
+   char *tmp;
+ 
+   /* work with a copy of the input line */
+   replacement = xmalloc (strlen (line) + 1);
+   strcpy (replacement, line);
+ 
+   ptr = head = replacement;
+ 
+   /* flag lines where we might need to replace a single '=' with two;
+      GAS uses single '=' to assign macro args values, and possibly other
+      places, so limit what we replace */
+   if (strstr (line, ".if") 
+       || strstr (line, ".elseif") 
+       || strstr (line, ".break"))
+     {
+       line_conditional = 1;
+     }
+ 
+   /* watch out for .eval, so that we avoid doing substitution on the
+      symbol being assigned a value */
+   if (strstr (line, ".eval") || strstr (line, ".asg"))
+       eval_line = 1;
+ 
+   /* if it's a macro definition, don't do substitution on the argument names */
+   if (strstr (line, ".macro"))
+     return line;
+ 
+   while (!is_end_of_line[(int)*ptr])
+     {
+       int current_char = *ptr;
+ 
+       /* need to update this since LINE may have been modified */
+       if (eval_line)
+           eval_end = strrchr (ptr, ',');
+ 
+       /* replace triple double quotes with bounding quote/escapes */
+       if (current_char == '"' && ptr[1] == '"' && ptr[2] == '"')
+         {
+           ptr[1] = '\\';
+           tmp = strstr (ptr+2, "\"\"\"");
+           if (tmp)
+             tmp[0] = '\\';
+           changed = 1;
+         }
+ 
+       /* replace a single '=' with a '==';
+          for compatibility with older code only */
+       if (line_conditional && current_char == '=') 
+         {
+           if (ptr[1] == '=')
+             {
+               ptr += 2; 
+               continue;
+             }
+           *ptr++ = '\0';
+           tmp = xmalloc (strlen (head) + 2 + strlen (ptr) + 1);
+           sprintf (tmp, "%s==%s", head, ptr);
+           /* continue examining after the '==' */
+           ptr = tmp + strlen (head) + 2;
+           free (replacement);
+           head = replacement = tmp;
+           changed = 1;
+         }
+ 
+       /* flag when we've reached the symbol part of .eval/.asg */
+       if (eval_line && ptr >= eval_end)
+         eval_symbol = 1;
+ 
+       /* for each apparent symbol, see if it's a substitution symbol, and if
+          so, replace it in the input 
+       */
+       if ((forced && current_char == ':')
+           || (!forced && is_name_beginner (current_char)))
+         {
+           char *name; /* symbol to be replaced */
+           char *savedp = input_line_pointer;
+           int c;
+           char *value = NULL;
+           char *tail; /* rest of line after symbol */
+           
+           /* skip the colon */
+           if (forced)
+             ++ptr;
+ 
+           name = input_line_pointer = ptr;
+           c = get_symbol_end ();
+           /* '?' is not normally part of a symbol, but it IS part of a local
+              label 
+            */ 
+           if (c == '?')
+             {
+               *input_line_pointer++ = c;
+               c = *input_line_pointer;
+               *input_line_pointer = '\0';
+             }
+           /* avoid infinite recursion; if a symbol shows up a second time for
+              substitution, leave it as is */
+           if (hash_find (subsym_recurse_hash, name) == NULL)
+             value = subsym_lookup (name, macro_level);
+           else
+             as_warn (_("%s symbol recursion stopped at "
+                        "second appearance of '%s'"), 
+                      forced ? "Forced substitution": "Substitution", name);
+           ptr = tail = input_line_pointer;
+           input_line_pointer = savedp;
+ 
+           /* check for local labels; replace them with the appropriate
+              substitution */
+           if ((*name == '$' && isdigit (name[1]) && name[2] == '\0') 
+               || name[strlen (name)-1] == '?')
+             {
+               /* use an existing identifier for that label if, available, or
+                  create a new, unique identifier */
+               value = hash_find (local_label_hash[macro_level], name);
+               if (value == NULL)
+                 {
+                   char digit[11];
+                   char *namecopy = strcpy (xmalloc (strlen (name) + 1), name);
+                   value = strcpy (xmalloc (strlen (name) + sizeof (digit) + 1),
+                                   name);
+                   if (*value != '$')
+                     value[strlen (value)-1] = '\0';
+                   sprintf (digit, ".%d", local_label_id++);
+                   strcat (value, digit);
+                   hash_insert (local_label_hash[macro_level], namecopy, value);
+                 }
+               /* indicate where to continue looking for substitutions */
+               ptr = tail;
+             }
+           /* check for built-in subsym and math functions */
+           else if (value != NULL && *name == '$')
+             {
+               subsym_proc_entry *entry = (subsym_proc_entry *)value;
+               math_proc_entry *math_entry = hash_find (math_hash, name);
+               char *arg1, *arg2 = NULL;
+ 
+               *ptr = c;
+               if (entry == NULL)
+                 {
+                   as_bad (_("Unrecognized substitution symbol function"));
+                   break;
+                 }
+               else if (*ptr != '(')
+                 {
+                   as_bad (_("Missing '(' after substitution symbol function"));
+                   break;
+                 }
+               ++ptr;
+               if (math_entry != NULL)
+                 {
+                   float arg1, arg2 = 0;
+                   volatile float fresult;
+ 
+                   arg1 = (float)strtod (ptr, &ptr);
+                   if (math_entry->nargs == 2)
+                     {
+                       if (*ptr++ != ',')
+                         {
+                           as_bad (_("Expecting second argument"));
+                           break;
+                         }
+                       arg2 = (float)strtod (ptr, &ptr);
+                     }
+                   fresult = (*math_entry->proc)(arg1, arg2);
+                   value = xmalloc (128);
+                   if (math_entry->int_return)
+                     sprintf (value, "%d", (int)fresult);
+                   else
+                     sprintf (value, "%f", fresult);
+                   if (*ptr++ != ')')
+                     {
+                       as_bad (_("Extra junk in function call, expecting ')'"));
+                       break;
+                     }
+                   /* don't bother recursing; the replacement isn't a symbol */
+                   recurse = 0;
+                 }
+               else
+                 {
+                   int val;
+                   int arg_type[2] = {*ptr == '"',0};
+                   int ismember = !strcmp (entry->name, "$ismember");
+                   /* parse one or two args, which must be a substitution
+                      symbol, string or a character-string constant */
+                   /* for all functions, a string or substitution symbol may be
+                      used, with the following exceptions:
+                      firstch/lastch: 2nd arg must be character constant
+                      ismember: both args must be substitution symbols
+                   */
+                   ptr = subsym_get_arg (ptr, ",)", &arg1, ismember);
+                   if (!arg1)
+                     break;
+                   if (entry->nargs == 2)
+                     {
+                       if (*ptr++ != ',')
+                         {
+                           as_bad (_("Function expects two arguments"));
+                           break;
+                         }
+                       /* character constants are converted to numerics
+                          by the preprocessor */
+                       arg_type[1] = (isdigit (*ptr)) ? 2 : (*ptr == '"');
+                       ptr = subsym_get_arg (ptr, ")", &arg2, ismember);
+                     }
+                   /* args checking */
+                   if ((!strcmp (entry->name, "$firstch")
+                        || !strcmp (entry->name, "$lastch")) 
+                       && arg_type[1] != 2)
+                     {
+                       as_bad (_("Expecting character constant argument"));
+                       break;
+                     }
+                   if (ismember
+                       && (arg_type[0] != 0 || arg_type[1] != 0))
+                     {
+                       as_bad (_("Both arguments must be substitution symbols"));
+                       break;
+                     }
+                   if (*ptr++ != ')')
+                     {
+                       as_bad (_("Extra junk in function call, expecting ')'"));
+                       break;
+                     }
+                   val = (*entry->proc)(arg1, arg2);
+                   value = xmalloc (64);
+                   sprintf (value, "%d", val);
+                 }
+               /* fix things up to replace the entire expression, not just the
+                  function name  */
+               tail = ptr;
+               c = *tail;
+             }
+ 
+           if (value != NULL && !eval_symbol)
+             {
+               /* Replace the symbol with its string replacement and
+                  continue.  Recursively replace VALUE until either no
+                  substitutions are performed, or a substitution that has been
+                  previously made is encountered again.
+ 
+                  put the symbol into the recursion hash table so we only
+                  try to replace a symbol once
+               */
+               if (recurse)
+                 {
+                   hash_insert (subsym_recurse_hash, name, name);
+                   value = subsym_substitute (value, macro_level > 0);
+                   hash_delete (subsym_recurse_hash, name);
+                 }
+ 
+               /* temporarily zero-terminate where the symbol started */
+               *name = 0; 
+               if (forced)
+                 {
+                   if (c == '(')
+                     {
+                       /* subscripted substitution symbol -- use just the
+                          indicated portion of the string; the description
+                          kinda indicates that forced substituion is not
+                          supposed to be recursive, but I'm not sure. 
+                       */
+                       unsigned beg, len = 1; /* default to a single char */
+                       char *newval = strcpy (xmalloc (strlen (value)+1), value);
+ 
+                       savedp = input_line_pointer;
+                       input_line_pointer = tail + 1;
+                       beg = get_absolute_expression ();
+                       if (beg < 1)
+                         {
+                           as_bad (_("Invalid subscript (use 1 to %d)"), 
+                                   strlen (value));
+                           break;
+                         }
+                       if (*input_line_pointer == ',')
+                         {
+                           ++input_line_pointer;
+                           len = get_absolute_expression ();
+                           if (beg + len > strlen (value))
+                             {
+                               as_bad (_("Invalid length (use 0 to %d"),
+                                      strlen (value) - beg);
+                               break;
+                             }
+                         }
+                       newval += beg - 1;
+                       newval[len] = 0;
+                       tail = input_line_pointer;
+                       if (*tail++ != ')')
+                         {
+                           as_bad (_("Missing ')' in subscripted substitution "
+                                     "symbol expression"));
+                           break;
+                         }
+                       c = *tail;
+                       input_line_pointer = savedp;
+ 
+                       value = newval;
+                     }
+                   name[-1] = 0;
+                 }
+               tmp = xmalloc (strlen (head) + strlen (value) + 
+                              strlen (tail+1) + 2);
+               strcpy (tmp, head);
+               strcat (tmp, value);
+               /* Make sure forced substitutions are properly terminated */
+               if (forced)
+                 {
+                   if (c != ':')
+                     {
+                       as_bad (_("Missing forced substitution terminator ':'"));
+                       break;
+                     }
+                   ++tail;
+ #if 0
+                   /* Try to replace required whitespace 
+                      eliminated by the preprocessor; technically, a forced
+                      substitution could come anywhere, even mid-symbol,
+                      e.g. if x is "0", 'sym:x:end' should result in 'sym0end',
+                      but 'sym:x: end' should result in 'sym0 end'.
+                      FIXME -- this should really be fixed in the preprocessor,
+                      but would require several new states;
+                      KEEP_WHITE_AROUND_COLON does part of the job, but isn't
+                      complete 
+                   */
+                   if ((is_part_of_name (tail[1])
+                        && tail[1] != '.'
+                        && tail[1] != '$') 
+                       || tail[1] == '\0' || tail[1] == ',' || tail[1] == '"')
+                     ++tail;
+                   else
+                     *tail = ' ';
+ #endif
+                 }
+               else
+                 /* restore the character after the symbol end */
+                 *tail = c;
+               strcat (tmp, tail);
+               /* continue examining after the replacement value */
+               ptr = tmp + strlen (head) + strlen (value);
+               free (replacement);
+               head = replacement = tmp;
+               changed = 1;
+             }
+           else
+             *ptr = c;
+         }
+       else
+         {
+           ++ptr;
+         }
+     }
+ 
+   if (changed)
+     return replacement;
+   else
+     return line;
+ }
+ 
+ /* we use this to handle substitution symbols 
+    hijack input_line_pointer, replacing it with our substituted string.
+ 
+    .sslist should enable listing the line after replacements are made...
+ 
+    returns the new buffer limit
+ */
+ void
+ tic54x_start_line_hook ()
+ {
+   char *line, *endp;
+   char *replacement = NULL;
+ 
+   /* work with a copy of the input line, including EOL char */
+   endp = input_line_pointer;
+   while (!is_end_of_line[(int)*endp++])
+     ;
+   line = xmalloc (endp - input_line_pointer + 1);
+   strncpy (line, input_line_pointer, endp - input_line_pointer + 1);
+   line[endp - input_line_pointer] = 0;
+ 
+   /* scan ahead for parallel insns */
+   parallel_on_next_line_hint = next_line_shows_parallel (endp + 1);
+ 
+   /* if within a macro, first process forced replacements */
+   if (macro_level > 0)
+     replacement = subsym_substitute (line, 1);
+   else
+     replacement = line;
+   replacement = subsym_substitute (replacement, 0);
+ 
+   if (replacement != line)
+     {
+       char *tmp = replacement;
+       char *comment = strchr (replacement,';');
+       char endc = replacement[strlen (replacement)-1];
+ 
+       /* clean up the replacement; we'd prefer to have this done by the
+          standard preprocessing equipment (maybe do_scrub_chars?)
+          but for now, do a quick-and-dirty
+       */
+       if (comment != NULL)
+         {
+           comment[0] = endc;
+           comment[1] = 0;
+           --comment;
+         }
+       else 
+         comment = replacement + strlen (replacement) - 1;
+ 
+       /* trim trailing whitespace */
+       while (isspace (*comment))
+         {
+           comment[0] = endc;
+           comment[1] = 0;
+           --comment;
+         }
+ 
+       /* compact leading whitespace */
+       while (isspace (tmp[0]) && isspace (tmp[1]))
+         ++tmp;
+ 
+       input_line_pointer = endp;
+       input_scrub_insert_line (tmp);
+       free (replacement);
+       free (line);
+       /* keep track of whether we've done a substitution */
+       substitution_line = 1;
+     }
+   else
+     {
+       /* no change */
+       free (line);
+       substitution_line = 0;
+     }
+ }
+ 
+ /* This is the guts of the machine-dependent assembler.  STR points to a
+    machine dependent instruction.  This function is supposed to emit
+    the frags/bytes it assembles to.
+  */
+ void
+ md_assemble (line)
+   char *line;
+ {
+   static int repeat_slot = 0;
+   static int delay_slots = 0; /* how many delay slots left to fill? */
+   static int is_parallel = 0;
+   static tic54x_insn insn;
+   char *lptr;
+   char *savedp = input_line_pointer;
+   int c;
+ 
+   input_line_pointer = line;
+   c = get_symbol_end ();
+ 
+   if (cpu == VNONE)
+     cpu = V542;
+   if (address_mode_needs_set)
+     {
+       set_address_mode (amode);
+       address_mode_needs_set = 0;
+     }
+   if (cpu_needs_set)
+     {
+       set_cpu (cpu);
+       cpu_needs_set = 0;
+     }
+   assembly_begun = 1;
+ 
+   if (is_parallel)
+     {
+       is_parallel = 0;
+ 
+       strcpy (insn.parmnemonic, line);
+       lptr = input_line_pointer;
+       *lptr = c;
+       input_line_pointer = savedp;
+ 
+       if (tic54x_parse_parallel_insn_lastline (&insn, lptr))
+         {
+           int words = build_insn (&insn);
+ 
+           if (delay_slots != 0)
+             {
+               if (words > delay_slots)
+                 {
+                   as_bad (_("Instruction does not fit in available delay "
+                             "slots (%d-word insn, %d slots left)"), 
+                             words, delay_slots);
+                   delay_slots = 0;
+                   return;
+                 }
+               delay_slots -= words;
+             }
+         }
+       return;
+     }
+ 
+   memset (&insn, 0, sizeof (insn));
+   strcpy (insn.mnemonic, line);
+   lptr = input_line_pointer;
+   *lptr = c;
+   input_line_pointer = savedp;
+   
+   /* 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
+      mnemonic, and we look for it in the parallel insn hash table */
+   if (strstr (line, "||") != NULL || parallel_on_next_line_hint)
+     {
+       char *tmp = strstr (line, "||");
+       if (tmp != NULL)
+         *tmp = '\0';
+ 
+       if (tic54x_parse_parallel_insn_firstline (&insn, lptr))
+         {
+           is_parallel = 1;
+           /* if the parallel part is on the same line, process it now,
+              otherwise let the assembler pick up the next line for us */
+           if (tmp != NULL)
+             {
+               while (isspace (tmp[2]))
+                 ++tmp;
+               md_assemble (tmp+2);
+             }
+         }
+       else
+         {
+           as_bad (_("Unrecognized parallel instruction '%s'"), line);
+         }
+       return;
+     }
+ 
+   if (tic54x_parse_insn (&insn, lptr))
+     {
+       int words;
+ 
+       if ((insn.tm->flags & FL_LP) 
+           && cpu != V545LP && cpu != V546LP)
+         {
+           as_bad (_("Instruction '%s' requires an LP cpu version"),
+                   insn.tm->name);
+           return;
+         }
+       if ((insn.tm->flags & FL_FAR) 
+           && amode != far_mode)
+         {
+           as_bad (_("Instruction '%s' requires far mode addressing"),
+                   insn.tm->name);
+           return;
+         }
+ 
+       words = build_insn (&insn);
+ 
+       /* Is this instruction in a delay slot? */
+       if (delay_slots)
+         {
+           if (words > delay_slots)
+             {
+               as_warn (_("Instruction does not fit in available delay "
+                          "slots (%d-word insn, %d slots left). "
+                          "Resulting behavior is undefined."), 
+                        words, delay_slots);
+               delay_slots = 0;
+               return;
+             }
+           /* branches in delay slots are not allowed */
+           if (insn.tm->flags & FL_BMASK)
+             {
+               as_warn (_("Instructions which cause PC discontinuity are not "
+                          "allowed in a delay slot. "
+                          "Resulting behavior is undefined."));
+             }
+           delay_slots -= words;
+         }
+       /* Is this instruction the target of a repeat? */
+       if (repeat_slot)
+         {
+           if (insn.tm->flags & FL_NR)
+             as_warn (_("'%s' is not repeatable. "
+                        "Resulting behavior is undefined."), 
+                      insn.tm->name);
+           else if (insn.is_lkaddr)
+             as_warn (_("Instructions using long offset modifiers or absolute "
+                        "addresses are not repeatable. "
+                        "Resulting behavior is undefined."));
+           repeat_slot = 0;
+         }
+       
+       /* make sure we check the target of a repeat instruction */
+       if (insn.tm->flags & B_REPEAT)
+         {
+           repeat_slot = 1;
+           /* FIXME -- warn if repeat_slot == 1 at EOF */
+         }
+       /* make sure we check our delay slots for validity */
+       if (insn.tm->flags & FL_DELAY)
+         {
+           delay_slots = 2;
+           /* FIXME -- warn if delay_slots != 0 at EOF */
+         }
+     }
+ }
+ 
+ /* Do a final adjustment on the symbol table; in this case, make sure we have
+    a ".file" symbol */
+ void
+ tic54x_adjust_symtab ()
+ {
+   if (symbol_rootP == NULL
+       || S_GET_STORAGE_CLASS (symbol_rootP) != C_FILE)
+     {
+       char *filename;
+       unsigned lineno;
+       as_where (&filename, &lineno);
+       c_dot_file_symbol (filename);
+     }
+ }
+ 
+ /* 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 lets us process parallel instructions, which span two lines
+ */
+ int
+ tic54x_unrecognized_line (int c)
+ {
+   return c == PARALLEL_SEPARATOR;
+ }
+ 
+ /* 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
+    appropriate places.
+    FIXME -- obviously this isn't done yet.  These locals still show up in the
+    symbol table.
+  */
+ void
+ tic54x_define_label (symbolS *sym)
+ {
+   //static int local_label_count = 0;
+   //const char *name = S_GET_NAME (sym);
+ 
+   /* just in case we need this later; note that this is not necessarily the
+      same thing as line_label... 
+      When aligning or assigning labels to fields, sometimes the label is
+      assigned other than the address at which the label appears.
+      FIXME -- is this really needed? I think all the proper label assignment
+      is done in tic54x_cons.
+   */
+   last_label_seen = sym;
+ }
+ 
+ /* Try to parse something that normal parsing failed at. */
+ symbolS *
+ tic54x_undefined_symbol (name)
+      char *name;
+ {
+   symbol *sym;
+ 
+   /* not sure how to handle predefined symbols */
+   if ((sym = (symbol *)hash_find (cc_hash, name)) != NULL ||
+       (sym = (symbol *)hash_find (cc2_hash, name)) != NULL ||
+       (sym = (symbol *)hash_find (cc3_hash, name)) != NULL ||
+       (sym = (symbol *)hash_find (misc_symbol_hash, name)) != NULL ||
+       (sym = (symbol *)hash_find (sbit_hash, name)) != NULL)
+     {
+       return symbol_new (name, reg_section, 
+                          (valueT) sym->value,
+                          &zero_address_frag);
+     }
+ 
+   if ((sym = (symbol *)hash_find (reg_hash, name)) != NULL ||
+       (sym = (symbol *)hash_find (mmreg_hash, name)) != NULL ||
+       !strcasecmp (name, "a") || !strcasecmp (name, "b"))
+     {
+       return symbol_new (name, reg_section, 
+                          (valueT) sym ? sym->value : 0,
+                          &zero_address_frag);
+     }
+ 
+   return NULL;
+ }
+ 
+ /* parse a name in an expression before the expression parser takes a stab at
+    it */
+ int
+ tic54x_parse_name (name, exp)
+      char *name ATTRIBUTE_UNUSED;
+      expressionS *exp ATTRIBUTE_UNUSED;
+ {
+ #if 0
+   symbol *sym = (symbol *)hash_find (mmreg_hash, name);
+   /* if it's a MMREG, replace it with its constant value */
+   if (sym)
+     {
+       exp->X_op = O_constant;
+       exp->X_add_number = sym->value;
+       return 1;
+     }
+ #endif
+   return 0;
+ }
+ 
+ char *
+ md_atof (type, literalP, sizeP)
+      int type;
+      char *literalP;
+      int *sizeP;
+ {
+ #define MAX_LITTLENUMS 2
+   LITTLENUM_TYPE words[MAX_LITTLENUMS];
+   LITTLENUM_TYPE *word;
+   /* only one precision on the c54x */
+   int prec = 2;
+   char *t = atof_ieee (input_line_pointer, type, words);
+   if (t)
+     input_line_pointer = t;
+   *sizeP = 4;
+ 
+   /* target data is little-endian, but floats are stored big-"word"ian.  ugh */
+   for (word = words; prec--;)
+     {
+       md_number_to_chars (literalP, (long)(*word++), sizeof (LITTLENUM_TYPE));
+       literalP += sizeof (LITTLENUM_TYPE);
+     }
+ 
+   return 0;
+ }
+ 
+ arelent *
+ tc_gen_reloc (section, fixP)
+      asection *section;
+      fixS *fixP;
+ {
+   arelent *rel;
+   bfd_reloc_code_real_type code = fixP->fx_r_type;
+   asymbol *sym = symbol_get_bfdsym (fixP->fx_addsy);
+ 
+   rel = (arelent *) xmalloc (sizeof (arelent));
+   rel->sym_ptr_ptr = (asymbol **)xmalloc (sizeof (asymbol *));
+   *rel->sym_ptr_ptr = sym;
+   /* We assume that all rel->address are host byte offsets */
+   rel->address = fixP->fx_frag->fr_address + fixP->fx_where;
+   rel->address /= OCTETS_PER_BYTE;
+   rel->howto = bfd_reloc_type_lookup (stdoutput, code);
+   if (!strcmp (sym->name, section->name))
+     rel->howto += HOWTO_BANK;
+ 
+   if (!rel->howto)
+     {
+       const char *name = S_GET_NAME (fixP->fx_addsy);
+       if (name == NULL)
+ 	name = "<unknown>";
+       as_fatal ("Cannot generate relocation type for symbol %s, code %s", 
+                 name, bfd_get_reloc_code_name (code));
+       return NULL;
+     }
+   return rel;
+ }
+ 
+ /* handle cons expressions */
+ void
+ tic54x_cons_fix_new (fragS *frag, int where, int octets, expressionS *exp)
+ {
+   bfd_reloc_code_real_type r;
+   switch (octets)
+     {
+     default:
+       as_bad (_("Unsupported relocation size %d"), octets);
+       r = BFD_RELOC_TIC54X_16_OF_23;
+       break;
+     case 2:
+       r = BFD_RELOC_TIC54X_16_OF_23;
+       break;
+     case 4:
+       /* TI assembler always uses this, regardless of addressing mode */
+       if (emitting_long)
+         r = BFD_RELOC_TIC54X_23;
+       else
+         /* we never want to directly generate this; this is provided for 
+            stabs support only */
+         r = BFD_RELOC_32;
+       break;
+     }
+   fix_new_exp (frag, where, octets, exp, 0, r);
+ }
+ 
+ /* Attempt to simplify or even eliminate a fixup.  
+    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.
+    */
+ int
+ md_apply_fix (fixP, valP)
+      fixS *fixP;
+      valueT *valP;
+ {
+   char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+   valueT val = *valP;
+ 
+   switch (fixP->fx_r_type)
+     {
+     default:
+       as_fatal ("Bad relocation type: 0x%02x", fixP->fx_r_type);
+       return 0;
+     case BFD_RELOC_TIC54X_MS7_OF_23:
+       val = (val >> 16) & 0x7F;
+       /* fall through */
+     case BFD_RELOC_TIC54X_16_OF_23:
+     case BFD_RELOC_16:
+       bfd_put_16 (stdoutput, val, buf);
+       /* indicate what we're actually writing, so that we don't get warnings
+          about exceeding available space */
+       *valP = val & 0xFFFF;
+       break;
+     case BFD_RELOC_TIC54X_PARTLS7:
+       bfd_put_16 (stdoutput,
+                   (bfd_get_16 (stdoutput, buf) & 0xFF80) | (val & 0x7F),
+                   buf);
+       /* indicate what we're actually writing, so that we don't get warnings
+          about exceeding available space */
+       *valP = val & 0x7F;
+       break;
+     case BFD_RELOC_TIC54X_PARTMS9:
+       /* TI assembler doesn't shift its encoding for relocatable files, and is
+          thus incompatible with this implementation's relocatable files */ 
+       bfd_put_16 (stdoutput, 
+                   (bfd_get_16 (stdoutput, buf) & 0xFE00) | (val >> 7), 
+                   buf);
+       break;
+     case BFD_RELOC_32:
+     case BFD_RELOC_TIC54X_23:
+       bfd_put_32 (stdoutput,
+                   (bfd_get_32 (stdoutput, buf) & 0xFF800000) | val,
+                   buf);
+       break;
+     }
+ 
+   return 0; /* return value is ignored */
+ }
+ 
+ /* this is our chance to record section alignment 
+    don't need to do anything here, since BFD does the proper encoding
+  */
+ valueT
+ md_section_align (segment, section_size)
+      segT segment ATTRIBUTE_UNUSED;
+      valueT section_size;
+ {
+   return section_size;
+ }
+ 
+ long
+ md_pcrel_from (fixP)
+      fixS *fixP ATTRIBUTE_UNUSED;
+ {
+   return 0;
+ }
+ 
+ #if defined OBJ_COFF
+ 
+ short
+ tc_coff_fix2rtype (fixP)
+      fixS *fixP;
+ {
+   return (fixP->fx_r_type);
+ }
+ 
+ #endif	/* OBJ_COFF */
+ 
+ /* mostly little-endian, but longwords (4 octets) get MS word stored first */
+ void
+ tic54x_number_to_chars (buf, val, n)
+   char *buf;
+   valueT val;
+   int n;
+ {
+   if (n != 4)
+       number_to_chars_littleendian (buf, val, n);
+   else
+     {
+       number_to_chars_littleendian (buf, val>>16, 2);
+       number_to_chars_littleendian (buf+2, val&0xFFFF, 2);
+     }
+ }
+ 
+ int 
+ tic54x_estimate_size_before_relax (frag, seg)
+   fragS *frag ATTRIBUTE_UNUSED;
+   segT seg ATTRIBUTE_UNUSED;
+ {
+   return 0;
+ }
+ 
+ /* 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. */
+ int 
+ tic54x_relax_frag (frag, stretch)
+   fragS *frag;
+   long stretch ATTRIBUTE_UNUSED;
+ {
+   symbolS *sym = frag->fr_symbol;
+   int growth = 0;
+   int i;
+ 
+   if (sym != NULL)
+     {
+       struct bit_info *bi = (struct bit_info *)frag->fr_opcode;
+       int bit_offset = frag_bit_offset (frag_prev (frag, bi->seg), bi->seg);
+       int size = S_GET_VALUE (sym);
+       fragS *prev_frag = bit_offset_frag (frag_prev (frag, bi->seg), bi->seg);
+       int available = 16 - bit_offset;
+ 
+       if (symbol_get_frag (sym) != &zero_address_frag
+           || S_IS_COMMON (sym)
+           || !S_IS_DEFINED (sym))
+         as_bad_where (frag->fr_file, frag->fr_line,
+                       _("non-absolute value used with .space/.bes"));
+ 
+       if (size < 0)
+         {
+           as_warn (_("negative value ignored in %s"),
+                    bi->type == TYPE_SPACE ? ".space" :
+                    bi->type == TYPE_BES ? ".bes" : ".field");
+           growth = 0;
+           frag->tc_frag_data = frag->fr_fix = 0;
+           return 0;
+         }
+ 
+       if (bi->type == TYPE_FIELD)
+         {
+           /* bit fields of 16 or larger will have already been handled */
+           if (bit_offset != 0 && available >= size)
+             {
+               char *p = prev_frag->fr_literal;
+               valueT value = bi->value;
+               value <<= available - size;
+               value |= ((unsigned short)p[1]<<8) | p[0];
+               md_number_to_chars (p, value, 2);
+               if ((prev_frag->tc_frag_data += size) == 16)
+                 prev_frag->tc_frag_data = 0;
+               if (bi->sym)
+                 symbol_set_frag (bi->sym, prev_frag);
+               /* this frag is no longer used */
+               growth = -frag->fr_fix;
+               frag->fr_fix = 0;
+               frag->tc_frag_data = 0;
+             }
+           else
+             {
+               char *p = frag->fr_literal;
+               valueT value = bi->value << (16 - size);
+               md_number_to_chars (p, value, 2);
+               if ((frag->tc_frag_data = size) == 16)
+                 frag->tc_frag_data = 0;
+               growth = 0;
+             }
+         }
+       else
+         {
+           if (bit_offset != 0 && bit_offset < 16)
+             {
+               if (available >= size)
+                 {
+                   if ((prev_frag->tc_frag_data += size) == 16)
+                     prev_frag->tc_frag_data = 0;
+                   if (bi->sym)
+                     symbol_set_frag (bi->sym, prev_frag);
+                   /* this frag is no longer used */
+                   growth = -frag->fr_fix;
+                   frag->fr_fix = 0;
+                   frag->tc_frag_data = 0;
+                   goto getout;
+                 }
+               if (bi->type == TYPE_SPACE && bi->sym)
+                 symbol_set_frag (bi->sym, prev_frag);
+               size -= available;
+             }
+           growth = (size + 15) / 16 * OCTETS_PER_BYTE - frag->fr_fix;
+           for (i=0;i < growth;i++)
+             frag->fr_literal[i] = 0;
+           frag->fr_fix = growth;
+           frag->tc_frag_data = size % 16;
+           /* make sure any BES label points to the LAST word allocated */
+           if (bi->type == TYPE_BES && bi->sym)
+             S_SET_VALUE (bi->sym, frag->fr_fix / OCTETS_PER_BYTE - 1);
+         }
+     getout:
+       frag->fr_symbol = 0;
+       frag->fr_opcode = 0;
+       free ((void *)bi);
+     }
+   return growth;
+ }
+ 
+ void
+ tic54x_convert_frag (abfd, seg, frag)
+   bfd *abfd ATTRIBUTE_UNUSED;
+   segT seg ATTRIBUTE_UNUSED;
+   fragS *frag;
+ {
+   /* offset is in bytes */
+   frag->fr_offset = (frag->fr_next->fr_address 
+                      - frag->fr_address
+                      - frag->fr_fix) / frag->fr_var;
+   if (frag->fr_offset < 0)
+     {
+       as_bad_where (frag->fr_file, frag->fr_line,
+                     _("attempt to .space/.bes backwards? (%ld)"),
+                     (long) frag->fr_offset);
+     }
+   frag->fr_type = rs_space;
+ }
+ 
+ /* 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
+    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).
+ 
+    C is the character that used to be at *REST, which points to the end of the
+    label. 
+ 
+    Don't allow labels to start with '.'
+ */
+ int
+ tic54x_start_label (c, rest)
+   int c;
+   char *rest;
+ {
+   /* if within .struct/.union, no auto line labels, please */
+   if (current_stag != NULL)
+     return 0;
+ 
+   /* disallow labels starting with "." */
+   if (c != ':')
+     {
+       char *label = rest;
+       while (!is_end_of_line[(int)label[-1]])
+         --label;
+       if (*label == '.')
+         {
+           as_bad (_("Invalid label '%s'"), label);
+           return 0;
+         }
+     }
+ 
+   if (is_end_of_line[(int)c])
+     return 1;
+ 
+   if (isspace (c))
+     while (isspace (c = *++rest))
+       ;
+   if (c == '.')
+     {
+       /* don't let colon () define a label for any of these... */
+       return (strncasecmp (rest, ".tag", 4) != 0 || !isspace (rest[4]))
+         && (strncasecmp (rest, ".struct", 7) != 0 || !isspace (rest[7]))
+         && (strncasecmp (rest, ".union", 6) != 0 || !isspace (rest[6]))
+         && (strncasecmp (rest, ".macro", 6) != 0 || !isspace (rest[6]))
+         && (strncasecmp (rest, ".set", 4) != 0 || !isspace (rest[4]))
+         && (strncasecmp (rest, ".equ", 4) != 0 || !isspace (rest[4]));
+     }
+ 
+   return 1;
+ }
Index: gas/config/tc-tic54x.h
===================================================================
RCS file: tc-tic54x.h
diff -N tc-tic54x.h
*** /dev/null	Tue May  5 13:32:27 1998
--- tc-tic54x.h	Mon Jun 19 13:51:27 2000
***************
*** 0 ****
--- 1,128 ----
+ /* tc-tic54x.h -- Header file for tc-tic54x.c
+    Copyright (C) 1999, 2000 Free Software Foundation.
+    Contributed by Timothy Wall (twall@alum.mit.edu)
+ 
+    This file is part of GAS, the GNU Assembler.
+ 
+    GAS is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2, or (at your option)
+    any later version.
+ 
+    GAS is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+ 
+    You should have received a copy of the GNU General Public License
+    along with GAS; see the file COPYING.  If not, write to the Free
+    Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+    02111-1307, USA.  */
+ 
+ #ifndef _TC_TIC54X_H_
+ #define _TC_TIC54X_H_
+ 
+ /* select the proper coff format (see obj-coff.h) */
+ #define TC_TIC54X 
+ 
+ #define TARGET_BYTES_BIG_ENDIAN	0
+ #define OCTETS_PER_BYTE_POWER 1
+ 
+ #define TARGET_ARCH		bfd_arch_tic54x
+ #define BFD_ARCH                TARGET_ARCH
+ 
+ #define WORKING_DOT_WORD        1
+ 
+ #define MAX_OPERANDS 4
+ #define PARALLEL_SEPARATOR '|'
+ #define LABELS_WITHOUT_COLONS 1
+ /* accept 0FFFFh, 1010b, etc. */
+ #define NUMBERS_WITH_SUFFIX 1
+ /* $ is section program counter */
+ #define DOLLAR_DOT 1
+ /* accept parallel lines like
+        add #1,a  ||  ld #1, b  
+        (may also be split across lines)
+ */
+ #define DOUBLEBAR_PARALLEL 1
+ /* affects preprocessor */
+ #define KEEP_WHITE_AROUND_COLON 1
+ 
+ /* We need the extra field in the fixup struct to put the relocation in. */
+ 
+ #define NEED_FX_R_TYPE
+ 
+ struct bit_info
+ {
+   segT seg;
+ #define TYPE_SPACE 0
+ #define TYPE_BES   1
+ #define TYPE_FIELD 2
+   int type;
+   symbolS *sym;
+   valueT value;
+   char *where;
+   int offset;
+ };
+ 
+ /* We sometimes need to keep track of bit offsets within words */
+ #define TC_FRAG_TYPE int
+ #define TC_FRAG_INIT(FRAGP) do {(FRAGP)->tc_frag_data = 0;}while(0)
+ 
+ /* tell GAS whether the given token is indeed a code label */
+ #define TC_START_LABEL_WITHOUT_COLON(c,ptr) tic54x_start_label(c,ptr)
+ extern int tic54x_start_label PARAMS((int, char *));
+ 
+ /* custom handling for relocations in cons expressions */
+ #define TC_CONS_FIX_NEW(FRAG,OFF,LEN,EXP) tic54x_cons_fix_new(FRAG,OFF,LEN,EXP)
+ extern void tic54x_cons_fix_new PARAMS((fragS *,int,int,expressionS *));
+ 
+ /* Define md_number_to_chars as the appropriate standard big endian or
+    little endian function.  Mostly littleendian, but longwords and floats are
+    stored MS word first.
+ */
+ 
+ #define md_number_to_chars tic54x_number_to_chars
+ extern void tic54x_number_to_chars PARAMS((char *, valueT, int));
+ #define tc_adjust_symtab() tic54x_adjust_symtab()
+ extern void tic54x_adjust_symtab PARAMS(());
+ #define tc_unrecognized_line(ch) tic54x_unrecognized_line(ch)
+ extern int tic54x_unrecognized_line PARAMS((int ch));
+ #define md_parse_name(s,e) tic54x_parse_name(s,e)
+ extern int tic54x_parse_name PARAMS((char *name, expressionS *e));
+ #define md_undefined_symbol(s) tic54x_undefined_symbol(s)
+ extern symbolS *tic54x_undefined_symbol PARAMS((char *name));
+ #define md_macro_start() tic54x_macro_start()
+ extern void tic54x_macro_start ();
+ #define md_macro_end() tic54x_macro_end()
+ extern void tic54x_macro_end ();
+ #define md_macro_info(args) tic54x_macro_info(args)
+ extern void tic54x_macro_info PARAMS((void *macro));
+ #define tc_frob_label(sym) tic54x_define_label (sym)
+ extern void tic54x_define_label PARAMS((symbolS *));
+ 
+ #define md_start_line_hook() tic54x_start_line_hook() 
+ extern void tic54x_start_line_hook ();
+ 
+ #define md_estimate_size_before_relax(f,s) \
+ tic54x_estimate_size_before_relax(f,s)
+ extern int tic54x_estimate_size_before_relax(fragS *, segT);
+ 
+ #define md_relax_frag(f,s) tic54x_relax_frag(f,s)
+ extern int tic54x_relax_frag(fragS *, long);
+ 
+ #define md_convert_frag(b,s,f)	tic54x_convert_frag(b,s,f)
+ extern void tic54x_convert_frag(bfd *, segT, fragS *);
+ 
+ /* Other things we don't support... */
+ 
+ /* Define away the call to md_operand in the expression parsing code.
+    This is called whenever the expression parser can't parse the input
+    and gives the assembler backend a chance to deal with it instead. */
+ 
+ #define md_operand(X)
+ 
+ /* spruce up the listing output */
+ #define LISTING_WORD_SIZE 2
+ 
+ #endif
Index: gas/doc/Makefile.am
===================================================================
RCS file: /cvs/src/src/gas/doc/Makefile.am,v
retrieving revision 1.5
diff -d -c -p -b -w -r1.5 Makefile.am
*** Makefile.am	2000/06/19 01:22:40	1.5
--- Makefile.am	2000/06/19 20:51:27
*************** CPU_DOCS = \
*** 33,38 ****
--- 33,39 ----
  	c-pj.texi \
  	c-sh.texi \
  	c-sparc.texi \
+         c-tic54x.texi \
  	c-vax.texi \
  	c-v850.texi \
  	c-z8k.texi
Index: gas/doc/Makefile.in
===================================================================
RCS file: /cvs/src/src/gas/doc/Makefile.in,v
retrieving revision 1.10
diff -d -c -p -b -w -r1.10 Makefile.in
*** Makefile.in	2000/06/19 01:22:40	1.10
--- Makefile.in	2000/06/19 20:51:27
*************** CPU_DOCS = \
*** 136,141 ****
--- 136,142 ----
  	c-pj.texi \
  	c-sh.texi \
  	c-sparc.texi \
+         c-tic54x.texi \
  	c-vax.texi \
  	c-v850.texi \
  	c-z8k.texi
Index: gas/doc/all.texi
===================================================================
RCS file: /cvs/src/src/gas/doc/all.texi,v
retrieving revision 1.4
diff -d -c -p -b -w -r1.4 all.texi
*** all.texi	2000/06/19 01:22:40	1.4
--- all.texi	2000/06/19 20:51:27
***************
*** 44,49 ****
--- 44,50 ----
  @set PJ
  @set SH
  @set SPARC
+ @set C54X
  @set V850
  @set VAX
  @set VXWORKS
Index: gas/doc/as.texinfo
===================================================================
RCS file: /cvs/src/src/gas/doc/as.texinfo,v
retrieving revision 1.14
diff -d -c -p -b -w -r1.14 as.texinfo
*** as.texinfo	2000/06/19 01:22:40	1.14
--- as.texinfo	2000/06/19 20:51:28
*************** Here is a brief summary of how to invoke
*** 249,254 ****
--- 249,258 ----
     -Av8plus | -Av8plusa | -Av9 | -Av9a ]
   [ -xarch=v8plus | -xarch=v8plusa ] [ -bump ] [ -32 | -64 ]
  @end ifset
+ @ifset TIC54X
+  [ -mcpu=54[123589] | -mcpu=54[56]lp ] [ -mfar-mode | -mf ] 
+  [ -merrors-to-file <filename> | -me <filename> ]
+ @end ifset
  @ifset Z8000
  @c Z8000 has no machine-dependent assembler options
  @end ifset
*************** Warn when the assembler switches to anot
*** 623,628 ****
--- 627,648 ----
  @end table
  @end ifset
  
+ @ifset TIC54X
+ The following options are available when @value{AS} is configured for the 'c54x
+ architecture. 
+ 
+ @table @code
+ @item -mfar-mode
+ Enable extended addressing mode.  All addresses and relocations will assume
+ extended addressing (usually 23 bits).
+ @item -mcpu=@var{CPU_VERSION}
+ Sets the CPU version being compiled for.
+ @item -merrors-to-file @var{FILENAME}
+ Redirect error output to a file, for broken systems which don't support such
+ behaviour in the shell.
+ @end table
+ @end ifset
+ 
  @ifset MIPS
  The following options are available when @value{AS} is configured for
  a MIPS processor.
*************** subject, see the hardware manufacturer's
*** 5031,5036 ****
--- 5051,5059 ----
  @ifset SPARC
  * Sparc-Dependent::             SPARC Dependent Features
  @end ifset
+ @ifset TIC54X
+ * TIC54X-Dependent::            TI TMS320C54x Dependent Features
+ @end ifset
  @ifset V850
  * V850-Dependent::              V850 Dependent Features
  @end ifset
*************** family.
*** 5221,5226 ****
--- 5244,5253 ----
  @include c-sparc.texi
  @end ifset
  
+ @ifset TIC54X
+ @include c-tic54x.texi
+ @end ifset
+ 
  @ifset Z8000
  @include c-z8k.texi
  @end ifset
*************** Linas Vepstas added GAS support for the 
*** 5523,5528 ****
--- 5550,5558 ----
  
  Richard Henderson rewrote the Alpha assembler. Klaus Kaempf wrote GAS and BFD
  support for openVMS/Alpha.
+ 
+ Timothy Wall, Michael Hayes, and Greg Smart contributed to the various tic*
+ flavors.
  
  Several engineers at Cygnus Support have also provided many small bug fixes and
  configuration enhancements.
Index: gas/doc/c-tic54x.texi
===================================================================
RCS file: c-tic54x.texi
diff -N c-tic54x.texi
*** /dev/null	Tue May  5 13:32:27 1998
--- c-tic54x.texi	Mon Jun 19 13:51:28 2000
***************
*** 0 ****
--- 1,767 ----
+ @c Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+ @c This is part of the GAS manual.
+ @c For copying conditions, see the file as.texinfo.
+ @c TI TMS320C54X description by Timothy Wall, twall@cygnus.com
+ @ifset GENERIC
+ @page
+ @node C54X-Dependent
+ @chapter C54X Dependent Features
+ @end ifset
+ @ifclear GENERIC
+ @node Machine Dependencies
+ @chapter C54X Dependent Features
+ @end ifclear
+ 
+ @cindex C54X support
+ @menu
+ * C54X-Opts::              Command-line Options
+ * C54X-Block::             Blocking
+ * C54X-Env::               Environment Settings
+ * C54X-Constants::         Constants Syntax
+ * C54X-Subsyms::           String Substitution
+ * C54X-Locals::            Local Label Syntax
+ * C54X-Builtins::          Builtin Assembler Math Functions
+ * C54X-Ext::               Extended Addressing Support
+ * C54X-Directives::        Directives
+ * C54X-Macros::            Macro Features
+ * C54X-MMRegs::            Memory-mapped Registers
+ @end menu
+ 
+ @node C54X-Opts
+ @section Options
+ 
+ @cindex options, C54X
+ @cindex C54X options
+ The 'c54x version of @code{@value{AS}} has a few machine-dependent options.
+ 
+ @cindex @samp{-mfar-mode} option, far-mode
+ @cindex @samp{-mf} option, far-mode
+ You can use the @samp{-mfar-mode} option to enable extended addressing mode.
+ All addresses will be assumed to be > 16 bits, and the appropriate
+ relocation types will be used.  This option is equivalent to using the
+ @samp{.far_mode} directive in the assembly code.  If you do not use the
+ @samp{-mfar-mode} option, all references will be assumed to be 16 bits.  
+ This option may be abbreviated to @samp{-mf}.
+ 
+ @cindex @samp{-mcpu} option, cpu
+ You can use the @samp{-mcpu} option to specify a particular CPU.
+ This option is equivalent to using the @samp{.version} directive in the
+ assembly code.  For recognized CPU codes, see
+ @xref{C54X-Directives,,@code{.version}}.  The default CPU version is
+ @samp{542}.   
+ 
+ @cindex @samp{-merrors-to-file} option, stderr redirect
+ @cindex @samp{-me} option, stderr redirect
+ You can use the @samp{-merrors-to-file} option to redirect error output
+ to a file (this provided for those deficient environments which don't
+ provide adequate output redirection).  This option may be abbreviated to
+ @samp{-me}. 
+ 
+ @node C54X-Block
+ @section Blocking
+ A blocked section or memory block is guaranteed not to cross the blocking
+ boundary (usually a page, or 128 words) if it is smaller than the
+ blocking size, or to start on a page boundary if it is larger than the
+ blocking size.
+ 
+ @node C54X-Env
+ @section Environment Settings
+ 
+ @cindex environment settings, C54X
+ @cindex @samp{A_DIR} environment variable, C54X
+ @cindex @samp{C54XDSP_DIR} environment variable, C54X
+ @samp{C54XDSP_DIR} and @samp{A_DIR} are semicolon-separated
+ paths which are added to the list of directories normally searched for
+ source and include files.  @samp{C54XDSP_DIR} will override @samp{A_DIR}.
+ 
+ @node C54X-Constants
+ @section Constants Syntax
+ 
+ @cindex constants, C54X
+ The C54X version of @code{@value{AS}} allows the following additional
+ constant formats, using a suffix to indicate the radix:
+ @smallexample
+ @cindex binary constants, C54X
+ 
+ Binary                  @code{000000B, 011000b}
+ Octal                   @code{10Q, 224q}
+ Hexadecimal             @code{45h, 0FH}
+ 
+ @end smallexample
+ 
+ @node C54X-Subsyms
+ @section String Substitution
+ A subset of allowable symbols (which we'll call subsyms) may be assigned
+ arbitrary string values.  This is roughly equivalent to C preprocessor
+ #define macros.  When @code{@value{AS}} encounters one of these
+ symbols, the symbol is replaced in the input stream by its string value.
+ Subsym names @strong{must} begin with a letter. 
+ 
+ Subsyms may be defined using the @code{.asg} and @code{.eval} directives
+ (@xref{C54X-Directives,,@code{.asg}},
+ @xref{C54X-Directives,,@code{.eval}}. 
+ 
+ Expansion is recursive until a previously encountered symbol is seen, at
+ which point substitution stops.  
+ 
+ In this example, x is replaced with SYM2; SYM2 is replaced with SYM1, and SYM1
+ is replaced with x.  At this point, x has already been encountered
+ and the substitution stops.
+ 
+ @smallexample @code
+  .asg   "x",SYM1 
+  .asg   "SYM1",SYM2
+  .asg   "SYM2",x
+  add    x,a             ; final code assembled is "add  x, a"
+ @end smallexample
+ 
+ Macro parameters are converted to subsyms; a side effect of this is the normal
+ @code{@value{AS}} '\ARG' dereferencing syntax is unnecessary.  Subsyms
+ defined within a macro will have global scope, unless the @code{.var}
+ directive is used to identify the subsym as a local macro variable
+ @pxref{C54X-Directives,,@code{.var}}.
+ 
+ Substitution may be forced in situations where replacement might be
+ ambiguous by placing colons on either side of the subsym.  The following
+ code: 
+ 
+ @smallexample @code
+  .eval  "10",x
+ LAB:X:  add     #x, a
+ @end smallexample
+ 
+ When assembled becomes:
+ 
+ @smallexample @code
+ LAB10  add     #10, a
+ @end smallexample
+ 
+ Smaller parts of the string assigned to a subsym may be accessed with
+ the following syntax:
+ 
+ @table @code
+ @item @code{:@var{symbol}(@var{char_index}):}
+ Evaluates to a single-character string, the character at @var{char_index}.
+ @item @code{:@var{symbol}(@var{start},@var{length}):}
+ Evaluates to a substring of @var{symbol} beginning at @var{start} with
+ length @var{length}.
+ @end table
+ 
+ @node C54X-Locals
+ @section Local Labels
+ Local labels may be defined in two ways:
+ 
+ @itemize @bullet
+ @item
+ $N, where N is a decimal number between 0 and 9
+ @item
+ LABEL?, where LABEL is any legal symbol name.  
+ @end itemize
+ 
+ Local labels thus defined may be redefined or automatically generated.
+ The scope of a local label is based on when it may be undefined or reset.
+ This happens when one of the following situations is encountered:
+ 
+ @itemize @bullet
+ @item
+ .newblock directive @pxref{C54X-Directives,,@code{.newblock}}
+ @item
+ The current section is changed (.sect, .text, or .data)
+ @item
+ Entering or leaving an included file
+ @item
+ The macro scope where the label was defined is exited
+ @end itemize
+ 
+ @node C54X-Builtins
+ @section Math Builtins
+ 
+ @cindex math builtins, C54X
+ @cindex C54X builtin math functions
+ @cindex builtin math functions, C54X
+ 
+ The following built-in functions may be used to generate a
+ floating-point value.  All return a floating-point value except
+ @samp{$cvi}, @samp{$int}, and @samp{$sgn}, which return an integer
+ value. 
+ 
+ @table @code
+ @cindex @code{$acos} math builtin, C54X
+ @item @code{$acos(@var{expr})}
+ Returns the floating point arccosine of @var{expr}.
+ 
+ @cindex @code{$asin} math builtin, C54X
+ @item @code{$asin(@var{expr})}
+ Returns the floating point arcsine of @var{expr}.
+ 
+ @cindex @code{$atan} math builtin, C54X
+ @item @code{$atan(@var{expr})}
+ Returns the floating point arctangent of @var{expr}.
+ 
+ @cindex @code{$atan2} math builtin, C54X
+ @item @code{$atan2(@var{expr1},@var{expr2})}
+ Returns the floating point arctangent of @var{expr1} / @var{expr2}.
+ 
+ @cindex @code{$ceil} math builtin, C54X
+ @item @code{$ceil(@var{expr})}
+ Returns the smallest integer not less than @var{expr} as floating point.
+ 
+ @cindex @code{$cosh} math builtin, C54X
+ @item @code{$cosh(@var{expr})}
+ Returns the floating point hyperbolic cosine of @var{expr}.
+ 
+ @cindex @code{$cos} math builtin, C54X
+ @item @code{$cos(@var{expr})}
+ Returns the floating point cosine of @var{expr}.
+ 
+ @cindex @code{$cvf} math builtin, C54X
+ @item @code{$cvf(@var{expr})}
+ Returns the integer value @var{expr} converted to floating-point.
+ 
+ @cindex @code{$cvi} math builtin, C54X
+ @item @code{$cvi(@var{expr})}
+ Returns the floating point value @var{expr} converted to integer.
+ 
+ @cindex @code{$exp} math builtin, C54X
+ @item @code{$exp(@var{expr})}
+ Returns the floating point value e ^ @var{expr}.
+ 
+ @cindex @code{$fabs} math builtin, C54X
+ @item @code{$fabs(@var{expr})}
+ Returns the floating point absolute value of @var{expr}.
+ 
+ @cindex @code{$floor} math builtin, C54X
+ @item @code{$floor(@var{expr})}
+ Returns the largest integer that is not greater than @var{expr} as
+ floating point.
+ 
+ @cindex @code{$fmod} math builtin, C54X
+ @item @code{$fmod(@var{expr1},@var{expr2})}
+ Returns the floating point remainder of @var{expr1} / @var{expr2}.
+ 
+ @cindex @code{$int} math builtin, C54X
+ @item @code{$int(@var{expr})}
+ Returns 1 if @var{expr} evaluates to an integer, zero otherwise.
+ 
+ @cindex @code{$ldexp} math builtin, C54X
+ @item @code{$ldexp(@var{expr1},@var{expr2})}
+ Returns the floating point value @var{expr1} * 2 ^ @var{expr2}.
+ 
+ @cindex @code{$log10} math builtin, C54X
+ @item @code{$log10(@var{expr})}
+ Returns the base 10 logarithm of @var{expr}.
+ 
+ @cindex @code{$log} math builtin, C54X
+ @item @code{$log(@var{expr})}
+ Returns the natural logarithm of @var{expr}.
+ 
+ @cindex @code{$max} math builtin, C54X
+ @item @code{$max(@var{expr1},@var{expr2})}
+ Returns the floating point maximum of @var{expr1} and @var{expr2}.
+ 
+ @cindex @code{$min} math builtin, C54X
+ @item @code{$min(@var{expr1},@var{expr2})}
+ Returns the floating point minimum of @var{expr1} and @var{expr2}.
+ 
+ @cindex @code{$pow} math builtin, C54X
+ @item @code{$pow(@var{expr1},@var{expr2})}
+ Returns the floating point value @var{expr1} ^ @var{expr2}.
+ 
+ @cindex @code{$round} math builtin, C54X
+ @item @code{$round(@var{expr})}
+ Returns the nearest integer to @var{expr} as a floating point number.
+ 
+ @cindex @code{$sgn} math builtin, C54X
+ @item @code{$sgn(@var{expr})}
+ Returns -1, 0, or 1 based on the sign of @var{expr}.
+ 
+ @cindex @code{$sin} math builtin, C54X
+ @item @code{$sin(@var{expr})}
+ Returns the floating point sine of @var{expr}.
+ 
+ @cindex @code{$sinh} math builtin, C54X
+ @item @code{$sinh(@var{expr})}
+ Returns the floating point hyperbolic sine of @var{expr}.
+ 
+ @cindex @code{$sqrt} math builtin, C54X
+ @item @code{$sqrt(@var{expr})}
+ Returns the floating point square root of @var{expr}.
+ 
+ @cindex @code{$tan} math builtin, C54X
+ @item @code{$tan(@var{expr})}
+ Returns the floating point tangent of @var{expr}.
+ 
+ @cindex @code{$tanh} math builtin, C54X
+ @item @code{$tanh(@var{expr})}
+ Returns the floating point hyperbolic tangent of @var{expr}.
+ 
+ @cindex @code{$trunc} math builtin, C54X
+ @item @code{$trunc(@var{expr})}
+ Returns the integer value of @var{expr} truncated towards zero as
+ floating point.
+ 
+ @end table
+ 
+ @node C54X-Ext
+ @section Extended Addressing
+ The @code{LDX} pseudo-op is provided for loading the extended addressing bits
+ of a label or address.  For example, if an address @code{_label} resides
+ in extended program memory, the value of @code{_label} may be loaded as
+ follows:
+ @smallexample @code
+  ldx     #_label,16,a    ; loads extended bits of _label
+  or      #_label,a       ; loads lower 16 bits of _label
+  bacc    a               ; full address is in accumulator A
+ @end smallexample
+ 
+ @node C54X-Directives
+ @section Directives
+ 
+ @cindex machine directives, C54X
+ @cindex C54X machine directives
+ 
+ @table @code
+ 
+ @cindex @code{align} directive, C54X
+ @cindex @code{even} directive, C54X
+ @item .align [@var{size}]
+ @itemx .even
+ Align the section program counter on the next boundary, based on
+ @var{size}.  @var{size} may be any power of 2.  @code{.even} is
+ equivalent to @code{.align} with a @var{size} of 2.
+ @table @code
+ @item 1
+ Align SPC to word boundary
+ @item 2
+ Align SPC to longword boundary (same as .even)
+ @item 128       
+ Align SPC to page boundary
+ @end table
+ 
+ @cindex @code{asg} directive, C54X
+ @item .asg @var{string}, @var{name}
+ Assign @var{name} the string @var{string}.  String replacement is
+ performed on @var{string} before assignment.
+ 
+ @cindex @code{eval} directive, C54X
+ @itemx .eval @var{string}, @var{name}
+ Evaluate the contents of string @var{string} and assign the result as a
+ string to the subsym @var{name}.  String replacement is performed on
+ @var{string} before assignment. 
+ 
+ @cindex @code{bss} directive, C54X
+ @item .bss @var{symbol}, @var{size} [, [@var{blocking_flag}] [,@var{alignment_flag}]]
+ Reserve space for @var{symbol} in the .bss section.  @var{size} is in
+ words.  If present, @var{blocking_flag} indicates the allocated space
+ should be aligned on a page boundary if it would otherwise cross a page
+ boundary.  If present, @var{alignment_flag} causes the assembler to
+ allocate @var{size} on a long word boundary.
+ 
+ @cindex @code{byte} directive, C54X
+ @cindex @code{ubyte} directive, C54X
+ @cindex @code{char} directive, C54X
+ @cindex @code{uchar} directive, C54X
+ @item .byte @var{value} [,...,@var{value_n}]
+ @itemx .ubyte @var{value} [,...,@var{value_n}]
+ @itemx .char @var{value} [,...,@var{value_n}]
+ @itemx .uchar @var{value} [,...,@var{value_n}]
+ Place one or more bytes into consecutive words of the current section.
+ The upper 8 bits of each word is zero-filled.  If a label is used, it
+ points to the word allocated for the first byte encountered.
+ 
+ @cindex @code{clink} directive, C54X
+ @item .clink ["@var{section_name}"]
+ Set STYP_CLINK flag for this section, which indicates to the linker that
+ if no symbols from this section are referenced, the section should not
+ be included in the link.  If @var{section_name} is omitted, the current
+ section is used.
+ 
+ @cindex @code{c_mode} directive, C54X
+ @item .c_mode
+ TBD.
+ 
+ @cindex @code{copy} directive, C54X
+ @item .copy "@var{filename}" | @var{filename}
+ @itemx .include "@var{filename}" | @var{filename}
+ Read source statements from @var{filename}.  The normal include search
+ path is used.  Normally .copy will cause statements from the included
+ file to be printed in the assembly listing and .include will not, but
+ this distinction is not currently implemented.
+ 
+ @cindex @code{data} directive, C54X
+ @item .data
+ Begin assembling code into the .data section.
+ 
+ @cindex @code{double} directive, C54X
+ @cindex @code{ldouble} directive, C54X
+ @cindex @code{float} directive, C54X
+ @cindex @code{xfloat} directive, C54X
+ @item .double @var{value} [,...,@var{value_n}]
+ @itemx .ldouble @var{value} [,...,@var{value_n}]
+ @itemx .float @var{value} [,...,@var{value_n}]
+ @itemx .xfloat @var{value} [,...,@var{value_n}]
+ Place an IEEE single-precision floating-point representation of one or
+ more floating-point values into the current section.  All but
+ @code{.xfloat} align the result on a longword boundary.  Values are
+ stored most-significant word first. 
+ 
+ @cindex @code{drlist} directive, C54X
+ @cindex @code{drnolist} directive, C54X
+ @item .drlist
+ @itemx .drnolist
+ Control printing of directives to the listing file.  Ignored.
+ 
+ @cindex @code{emsg} directive, C54X
+ @cindex @code{mmsg} directive, C54X
+ @cindex @code{wmsg} directive, C54X
+ @item .emsg @var{string}
+ @itemx .mmsg @var{string}
+ @itemx .wmsg @var{string}
+ Emit a user-defined error, message, or warning, respectively.
+ 
+ @cindex @code{far_mode} directive, C54X
+ @item .far_mode
+ Use extended addressing when assembling statements.  This should appear
+ only once per file, and is equivalent to the -mfar-mode option @pxref{C54X-Opts,,@code{-mfar-mode}}.
+ 
+ @cindex @code{fclist} directive, C54X
+ @cindex @code{fcnolist} directive, C54X
+ @item .fclist
+ @itemx .fcnolist
+ Control printing of false conditional blocks to the listing file.
+ 
+ @cindex @code{field} directive, C54X
+ @item .field @var{value} [,@var{size}]
+ Initialize a bitfield of @var{size} bits in the current section.  If
+ @var{value} is relocatable, then @var{size} must be 16.  @var{size}
+ defaults to 16 bits.  If @var{value} does not fit into @var{size} bits,
+ the value will be truncated.  Successive @code{.field} directives will
+ pack starting at the current word, filling the most significant bits
+ first, and aligning to the start of the next word if the field size does
+ not fit into the space remaining in the current word.  A @code{.align}
+ directive with an operand of 1 will force the next @code{.field}
+ directive to begin packing into a new word.  If a label is used, it
+ points to the word that contains the specified field.
+ 
+ @cindex @code{global} directive, C54X
+ @cindex @code{def} directive, C54X
+ @cindex @code{ref} directive, C54X
+ @item .global @var{symbol} [,...,@var{symbol_n}]
+ @itemx .def @var{symbol} [,...,@var{symbol_n}]
+ @itemx .ref @var{symbol} [,...,@var{symbol_n}]
+ @code{.def} nominally identifies a symbol defined in the current file
+ and availalbe to other files.  @code{.ref} identifies a symbol used in
+ the current file but defined elsewhere.  Both map to the standard
+ @code{.global} directive.
+ 
+ @cindex @code{half} directive, C54X
+ @cindex @code{uhalf} directive, C54X
+ @cindex @code{short} directive, C54X
+ @cindex @code{ushort} directive, C54X
+ @cindex @code{int} directive, C54X
+ @cindex @code{uint} directive, C54X
+ @cindex @code{word} directive, C54X
+ @cindex @code{uword} directive, C54X
+ @item .half @var{value} [,...,@var{value_n}]
+ @itemx .uhalf @var{value} [,...,@var{value_n}]
+ @itemx .short @var{value} [,...,@var{value_n}]
+ @itemx .ushort @var{value} [,...,@var{value_n}]
+ @itemx .int @var{value} [,...,@var{value_n}]
+ @itemx .uint @var{value} [,...,@var{value_n}]
+ @itemx .word @var{value} [,...,@var{value_n}]
+ @itemx .uword @var{value} [,...,@var{value_n}]
+ Place one or more values into consecutive words of the current section.
+ If a label is used, it points to the word allocated for the first value
+ encountered. 
+ 
+ @cindex @code{label} directive, C54X
+ @item .label @var{symbol}
+ Define a special @var{symbol} to refer to the load time address of the
+ current section program counter.
+ 
+ @cindex @code{length} directive, C54X
+ @cindex @code{width} directive, C54X
+ @item .length
+ @itemx .width
+ Set the page length and width of the output listing file.  Ignored.
+ 
+ @cindex @code{list} directive, C54X
+ @cindex @code{nolist} directive, C54X
+ @item .list
+ @itemx .nolist
+ Control whether the source listing is printed.  Ignored.
+ 
+ @cindex @code{long} directive, C54X
+ @cindex @code{ulong} directive, C54X
+ @cindex @code{xlong} directive, C54X
+ @item .long @var{value} [,...,@var{value_n}]
+ @itemx .ulong @var{value} [,...,@var{value_n}]
+ @itemx .xlong @var{value} [,...,@var{value_n}]
+ Place one or more 32-bit values into consecutive words in the current
+ section.  The most significant word is stored first.  @code{.long} and
+ @code{.ulong} align the result on a longword boundary; @code{xlong} does
+ not. 
+ 
+ @cindex @code{loop} directive, C54X
+ @cindex @code{break} directive, C54X
+ @cindex @code{endloop} directive, C54X
+ @item .loop [@var{count}]
+ @itemx .break [@var{condition}]
+ @itemx .endloop
+ Repeatedly assemble a block of code.  @code{.loop} begins the block, and
+ @code{.endloop} marks its termination.  @var{count} defaults to 1024,
+ and indicates the number of times the block should be repeated.
+ @code{.break} terminates the loop so that assembly begins after the
+ @code{.endloop} directive.  The optional @var{condition} will cause the
+ loop to terminate only if it evaluates to zero.
+ 
+ @cindex @code{macro} directive, C54X
+ @cindex @code{endm} directive, C54X
+ @item @var{macro_name} .macro [@var{param1}][,...@var{param_n}]
+ @itemx [.mexit]
+ @itemx .endm
+ See the section on macros for more explanation (@xref{C54X-Macros}.
+ 
+ @cindex @code{mlib} directive, C54X
+ @item .mlib "@var{filename}" | @var{filename}
+ Load the macro library @var{filename}.  @var{filename} must be an
+ archived library (BFD ar-compatible) of text files, expected to contain
+ only macro definitions.   The standard include search path is used.
+ 
+ @cindex @code{mlist} directive, C54X
+ @cindex @code{mnolist} directive, C54X
+ @item .mlist
+ @item .mnolist
+ Control whether to include macro and loop block expansions in the
+ listing output.  Ignored.
+ 
+ @cindex @code{mmregs} directive, C54X
+ @item .mmregs
+ Define global symbolic names for the 'c54x registers.  Supposedly
+ equivalent to executing @code{.set} directives for each register with
+ its memory-mapped value, but in reality is provided only for
+ compatibility and does nothing.
+ 
+ @cindex @code{newblock} directive, C54X
+ @item .newblock
+ This directive resets any C54X local labels currently defined.  Normal
+ @code{@value{AS}} local labels are unaffected.
+ 
+ @cindex @code{option} directive, C54X
+ @item .option @var{option_list}
+ Set listing options.  Ignored.
+ 
+ @cindex @code{sblock} directive, C54X
+ @item .sblock "@var{section_name}" | @var{section_name} [,"@var{name_n}" | @var{name_n}]
+ Designate @var{section_name} for blocking.  Blocking guarantees that a
+ section will start on a page boundary (128 words) if it would otherwise
+ cross a page boundary.  Only initialized sections may be designated with
+ this directive.  See also @xref{C54X-Block}.
+ 
+ @cindex @code{sect} directive, C54X
+ @item .sect "@var{section_name}"
+ Define a named initialized section and make it the current section.
+ 
+ @cindex @code{set} directive, C54X
+ @cindex @code{equ} directive, C54X
+ @item @var{symbol} .set "@var{value}"
+ @itemx @var{symbol} .equ "@var{value}"
+ Equate a constant @var{value} to a @var{symbol}, which is placed in the
+ symbol table.  @var{symbol} may not be previously defined.  
+ 
+ @cindex @code{space} directive, C54X
+ @cindex @code{bes} directive, C54X
+ @item .space @var{size_in_bits}
+ @itemx .bes @var{size_in_bits}
+ Reserve the given number of bits in the current section and zero-fill
+ them.  If a label is used with @code{.space}, it points to the
+ @strong{first} word reserved.  With @code{.bes}, the label points to the
+ @strong{last} word reserved.
+ 
+ @cindex @code{sslist} directive, C54X
+ @cindex @code{ssnolist} directive, C54X
+ @item .sslist
+ @itemx .ssnolist
+ Controls the inclusion of subsym replacement in the listing output.  Ignored.
+ 
+ @cindex @code{string} directive, C54X
+ @cindex @code{pstring} directive, C54X
+ @item .string "@var{string}" [,...,"@var{string_n}"]
+ @itemx .pstring "@var{string}" [,...,"@var{string_n}"]
+ Place 8-bit characters from @var{string} into the current section.
+ @code{.string} zero-fills the upper 8 bits of each word, while
+ @code{.pstring} puts two characters into each word, filling the
+ most-significant bits first.  Unused space is zero-filled.  If a label
+ is used, it points to the first word initialized.
+ 
+ @cindex @code{struct} directive, C54X
+ @cindex @code{tag} directive, C54X
+ @cindex @code{endstruct} directive, C54X
+ @item [@var{stag}] .struct [@var{offset}]
+ @itemx [@var{name_1}] element [@var{count_1}]
+ @itemx [@var{name_2}] element [@var{count_2}]
+ @itemx [@var{tname}] .tag @var{stagx} [@var{tcount}]
+ @itemx ...
+ @itemx [@var{name_n}] element [@var{count_n}]
+ @itemx [@var{ssize}] .endstruct
+ @itemx @var{label} .tag [@var{stag}]
+ Assign symbolic offsets to the elements of a structure.  @var{stag}
+ defines a symbol to use to reference the structure.  @var{offset}
+ indicates a starting value to use for the first element encountered;
+ otherwise it defaults to zero.  Each element can have a named offset,
+ @var{name}, which is a symbol assigned the value of the element's offset
+ into the structure.  If @var{stag} is missing, these become global
+ symbols.  @var{count} adjusts the offset that many times, as if
+ @code{element} were an array.  @code{element} may be one of
+ @code{.byte}, @code{.word}, @code{.long}, @code{.float}, or any
+ equivalent of those, and the structure offset is adjusted accordingly.
+ @code{.field} and @code{.string} are also allowed; the size of
+ @code{.field} is one bit, and @code{.string} is considered to be one
+ word in size.  Only element descriptors, structure/union tags,
+ @code{.align} and conditioanl assembly directies are allowed within
+ @code{.struct}/@code{.endstruct}.  @code{.align} aligns member offsets
+ to word boundaries only.  @var{ssize}, if provided, will always be
+ assigned the size of the structure.
+ 
+ The @code{.tag} directive, in addition to being used to define a
+ structure/union element within a structure, may be used to apply a
+ structure to a symbol.  Once applied to @var{label}, the individual
+ structure elements may be applied to @var{label} to produce the desired
+ offsets using @var{label} as the structure base. 
+ 
+ @cindex @code{tab} directive, C54X
+ @item .tab
+ Set the tab size in the output listing.  Ignored.
+ 
+ @cindex @code{union} directive, C54X
+ @cindex @code{tag} directive, C54X
+ @cindex @code{endunion} directive, C54X
+ @item [@var{utag}] .union 
+ @itemx [@var{name_1}] element [@var{count_1}]
+ @itemx [@var{name_2}] element [@var{count_2}]
+ @itemx [@var{tname}] .tag @var{utagx}[,@var{tcount}]
+ @itemx ...
+ @itemx [@var{name_n}] element [@var{count_n}]
+ @itemx [@var{usize}] .endstruct
+ @itemx @var{label} .tag [@var{utag}]
+ Similar to @code{.struct}, but the offset after each element is reset to
+ zero, and the @var{usize} is set to the maximum of all defined elements.
+ Starting offset for the union is always zero.
+ 
+ @cindex @code{usect} directive, C54X
+ @item [@var{symbol}] .usect "@var{section_name}", @var{size}, [,[@var{blocking_flag}] [,@var{alignment_flag}]]
+ Reserve space for variables in a named, uninitialzed section (similar to
+ .bss).  @code{.usect} allows definitions sections independent of .bss.
+ @var{symbol} points to the first location reserved by this allocation.
+ The symbol may be used as a variable name.  @var{size} is the allocated
+ size in words.  @var{blocking_flag} indicates whether to block this
+ section on a page boundary (128 words) (@pxref{C54X-Block}).
+ @var{alignment flag} indicates whether the section should be
+ longword-aligned.  
+ 
+ @cindex @code{var} directive, C54X
+ @item .var @var{sym}[,..., @var{sym_n}]
+ Define a subsym to be a local variable within a macro.  See
+ @xref{C54X-Macros}. 
+ 
+ @cindex @code{version} directive, C54X
+ @item .version @var{version}
+ Set which processor to build instructions for.  Though the following
+ values are accepted, the op is ignored.
+ @table @code
+ @item 541
+ @itemx 542
+ @itemx 543
+ @itemx 545
+ @itemx 545LP
+ @itemx 546LP
+ @itemx 548
+ @itemx 549
+ @end table
+ @end table
+ 
+ @node C54X-Macros
+ @section Macros
+ 
+ @cindex C54X-specific macros
+ @cindex macros, C54X
+ Macros do not require explicit dereferencing of arguments (i.e. \ARG).  
+ 
+ During macro expansion, the macro parameters are converted to subsyms.
+ If the number of arguments passed the macro invocation exceeds the
+ number of parameters defined, the last parameter is assigned the string
+ equivalent of all remaining arguments.  If fewer arguments are given
+ than parameters, the missing parameters are assigned empty strings.  To
+ include a comma in an argument, you must enclose the agument in quotes.
+ 
+ @cindex subsym builtins, C54X
+ @cindex C54X subsym builtins
+ @cindex builtin subsym functions, C54X
+ The following built-in subsym functions allow examination of the string
+ value of subsyms (or ordinary strings).  The arguments are strings
+ unless otherwise indicated (subsyms passed as args will be replaced by
+ the strings they represent).
+ @table @code
+ @cindex @code{$symlen} subsym builtin, C54X
+ @item @code{$symlen(@var{str})}
+ Returns the length of @var{str}.
+ 
+ @cindex @code{$symcmp} subsym builtin, C54X
+ @item @code{$symcmp(@var{str1},@var{str2})}
+ Returns 0 if @var{str1} == @var{str2}, non-zero otherwise.  
+ 
+ @cindex @code{$firstch} subsym builtin, C54X
+ @item @code{$firstch(@var{str},@var{ch})}
+ Returns index of the first occurrence of character constant @var{ch} in
+ @var{str}. 
+ 
+ @cindex @code{$lastch} subsym builtin, C54X
+ @item @code{$lastch(@var{str},@var{ch})}
+ Returns index of the last occurrence of character constant @var{ch} in
+ @var{str}. 
+ 
+ @cindex @code{$isdefed} subsym builtin, C54X
+ @item @code{$isdefed(@var{symbol})}
+ Returns zero if the symbol @var{symbol} is not in the symbol table,
+ non-zero otherwise.
+ 
+ @cindex @code{$ismember} subsym builtin, C54X
+ @item @code{$ismember(@var{symbol},@var{list})}
+ Assign the first member of comma-separated string @var{list} to
+ @var{symbol}; @var{list} is reassigned the remainder of the list.  Returns
+ zero if @var{list} is a null string.  Both arguments must be subsyms.
+ 
+ @cindex @code{$iscons} subsym builtin, C54X
+ @item @code{$iscons(@var{expr})}
+ Returns 1 if string @var{expr} is binary, 2 if octal, 3 if hexadecimal,
+ 4 if a character, 5 if decimal, and zero if not an integer.
+ 
+ @cindex @code{$isname} subsym builtin, C54X
+ @item @code{$isname(@var{name})}
+ Returns 1 if @var{name} is a valid symbol name, zero otherwise.
+ 
+ @cindex @code{$isreg} subsym builtin, C54X
+ @item @code{$isreg(@var{reg})}
+ Returns 1 if @var{reg} is a valid predefined register name (AR0-AR7 only).
+ 
+ @cindex @code{$structsz} subsym builtin, C54X
+ @item @code{$structsz(@var{stag})}
+ Returns the size of the structure or union represented by @var{stag}.
+ 
+ @cindex @code{$structacc} subsym builtin, C54X
+ @item @code{$structacc(@var{stag})}
+ Returns the reference point of the structure or union represented by
+ @var{stag}.   Always returns zero.
+ 
+ @end table
+ 
+ @node C54X-MMRegs
+ @section Memory-mapped Registers
+ 
+ @cindex C54X memory-mapped registers
+ @cindex registers, C54X memory-mapped
+ @cindex memory-mapped registers, C54X
+ The following symbols are recognized as memory-mapped registers:
+ 
+ @table @code
+ @end table
\ No newline at end of file
Index: gas/doc/internals.texi
===================================================================
RCS file: /cvs/src/src/gas/doc/internals.texi,v
retrieving revision 1.13
diff -d -c -p -b -w -r1.13 internals.texi
*** internals.texi	2000/06/09 00:00:04	1.13
--- internals.texi	2000/06/19 20:51:29
*************** If you define this macro, GAS will assum
*** 991,996 ****
--- 991,997 ----
  is a label, even if it does not have a colon.
  
  @item TC_START_LABEL
+ @itemx TC_START_LABEL_WITHOUT_COLON
  @cindex TC_START_LABEL
  You may define this macro to control what GAS considers to be a label.  The
  default definition is to accept any name followed by a colon character.
Index: gas/testsuite/ChangeLog
===================================================================
RCS file: /cvs/src/src/gas/testsuite/ChangeLog,v
retrieving revision 1.73
diff -d -c -p -b -w -r1.73 ChangeLog
*** ChangeLog	2000/06/19 01:22:40	1.73
--- ChangeLog	2000/06/19 20:51:29
***************
*** 1,3 ****
--- 1,9 ----
+ 2000-06-19  Timothy Wall  <twall@cygnus.com>
+ 
+ 	* gas/macros/macros.exp: Avoid tests which are not useful on tic54x.
+ 	* gas/all/gas.exp: Make adjustments for tic54x target.
+ 	* gas/ieee-fp/x930509a.exp: Ditto.
+ 	
  2000-06-18  Stephane Carrez  <stcarrez@worldnet.fr>
  
  	* gas/m68k/all.exp: Don't execute tests if the target is m68hc11&12.
Index: gas/testsuite/gas/all/gas.exp
===================================================================
RCS file: /cvs/src/src/gas/testsuite/gas/all/gas.exp,v
retrieving revision 1.3
diff -d -c -p -b -w -r1.3 gas.exp
*** gas.exp	2000/03/27 23:47:09	1.3
--- gas.exp	2000/06/19 20:51:29
*************** gas_test "p2425.s" ""   "" "pcrel values
*** 11,17 ****
  # Therefore this test (as it is currently written) is completely bogus
  # for any PA target.  Do not bother trying to run it and just claim
  # it fails.
! if [istarget hppa*-*-*] then {
      setup_xfail *-*-*
      fail "simplifiable double subtraction"
  } else {
--- 11,22 ----
  # Therefore this test (as it is currently written) is completely bogus
  # for any PA target.  Do not bother trying to run it and just claim
  # it fails.
! #
! # The C54x uses ".space" to allocate bits, and requires absolute expressions;
! # The ".space" directive is taken care of in the C54x-specific tests, so fail
! #  here 
! #
! if { [istarget hppa*-*-*] || [istarget *c54x*-*-*] } then {
      setup_xfail *-*-*
      fail "simplifiable double subtraction"
  } else {
*************** proc do_930509a {} {
*** 79,85 ****
  
  # This test is meaningless for the PA; the difference of two symbols
  # must not be resolved by the assembler.
! if ![istarget hppa*-*-*] then {
      # the vax fails because VMS can apparently actually handle this
      # case in relocs, so gas doesn't handle it itself.
      setup_xfail "vax*-*-vms*"
--- 84,92 ----
  
  # This test is meaningless for the PA; the difference of two symbols
  # must not be resolved by the assembler.
! # C54x assembler (for compatibility) does not allow differences between
! # forward references
! if { ![istarget hppa*-*-*] && ![istarget *c54x*-*-*] } then {
      # the vax fails because VMS can apparently actually handle this
      # case in relocs, so gas doesn't handle it itself.
      setup_xfail "vax*-*-vms*"
*************** if ![istarget hppa*-*-*] then {
*** 88,95 ****
--- 95,105 ----
      do_930509a
  }
  
+ # ".struct" and ".align" have different meanings on c54x
+ # These directives are done in the c54x-specific tests instead
  case $target_triplet in {
      { hppa*-*-* } { }
+     { *c54x*-*-* } { }
      default {
  	run_dump_test struct
  	run_dump_test align
*************** case $target_triplet in {
*** 100,106 ****
  # We omit m88k COFF because it uses weird pseudo-op names.
  # We omit the ARM toolchains because they define locals to
  #  start with '.', which eliminates .eos, .text etc from the output.
! if {   ([istarget *-*-coff*] && ![istarget m88*-*-*] && ![istarget *arm*-*-coff] && ![istarget thumb*-*-coff]) \
       ||([istarget *-*-pe*] && ![istarget arm*-*-pe*] && ![istarget thumb*-*-pe*]) \
       || [istarget a29k-*-udi*] \
       || [istarget a29k-*-ebmon*] \
--- 110,117 ----
  # We omit m88k COFF because it uses weird pseudo-op names.
  # We omit the ARM toolchains because they define locals to
  #  start with '.', which eliminates .eos, .text etc from the output.
! # Omit c54x, since .tag and .def mean something different on that target
! if {   ([istarget *-*-coff*] && ![istarget m88*-*-*] && ![istarget *arm*-*-coff] && ![istarget thumb*-*-coff] && ![istarget *c54x*-*-coff]) \
       ||([istarget *-*-pe*] && ![istarget arm*-*-pe*] && ![istarget thumb*-*-pe*]) \
       || [istarget a29k-*-udi*] \
       || [istarget a29k-*-ebmon*] \
*************** proc test_cond {} {
*** 137,143 ****
--- 148,157 ----
      }
  }
  
+ # again, p2align doesn't work on c54x target
+ if ![istarget *c54x*-*-*] then {
      test_cond
+ }
  
  # FIXME: this is here cause of a bug in DejaGnu 1.1.1. When it is no longer
  #        in use, then this can be removed.
Index: gas/testsuite/gas/macros/macros.exp
===================================================================
RCS file: /cvs/src/src/gas/testsuite/gas/macros/macros.exp,v
retrieving revision 1.2
diff -d -c -p -b -w -r1.2 macros.exp
*** macros.exp	2000/06/13 21:23:28	1.2
--- macros.exp	2000/06/19 20:51:30
*************** if ![istarget hppa*-*-*] {
*** 4,21 ****
      run_dump_test test1
  }
  
  run_dump_test test2
  
  run_dump_test test3
  
  run_dump_test irp
- 
  run_dump_test rept
  
  gas_test_error "err.s" "" "macro infinite recursion"
  
  case $target_triplet in {
      { hppa*-*-* } { }
      default {
  	run_dump_test semi
      }
--- 4,26 ----
      run_dump_test test1
  }
  
+ if ![istarget *c54x*-*-*] {
      run_dump_test test2
+ }
  
  run_dump_test test3
  
+ if ![istarget *c54x*-*-*] {
      run_dump_test irp
      run_dump_test rept
+ }
+ 
  
  gas_test_error "err.s" "" "macro infinite recursion"
  
  case $target_triplet in {
      { hppa*-*-* } { }
+     { *c54x*-*-* } { }
      default {
  	run_dump_test semi
      }
Index: gas/testsuite/gas/tic54x/tic54x.exp
===================================================================
RCS file: tic54x.exp
diff -N tic54x.exp
*** /dev/null	Tue May  5 13:32:27 1998
--- tic54x.exp	Mon Jun 19 13:53:44 2000
***************
*** 0 ****
--- 1,30 ----
+ #
+ # TI TMS320C54X tests.
+ #
+ if [istarget *c54x*-*-*] then {
+     run_dump_test "address"
+     run_dump_test "addrfar"
+     run_dump_test "align"
+     run_dump_test "asg"
+     run_dump_test "cons"
+     run_dump_test "consfar"
+     run_dump_test "extaddr"
+     run_dump_test "field"
+     run_dump_test "labels"
+     run_dump_test "loop"
+     run_dump_test "lp"
+     run_dump_test "macro"
+     run_dump_test "math"
+     run_dump_test "opcodes"
+     run_dump_test "sections"
+     run_dump_test "set"
+     run_dump_test "struct"
+     run_dump_test "subsym"
+ 
+     gas_test_error "macro1.s" "" "c54x macro argument manipulation"
+     gas_test_error "subsym1.s" "" "c54x subsym recursion"
+ 
+     # The longest one, run it last
+     run_dump_test "all-opcodes"
+ }
+ 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]