This is the mail archive of the
binutils@sourceware.cygnus.com
mailing list for the binutils project.
[PATCH] tic54x gas target
- To: binutils at sourceware dot cygnus dot com
- Subject: [PATCH] tic54x gas target
- From: Timothy Wall <twall at cygnus dot com>
- Date: Mon, 19 Jun 2000 19:06:39 -0400
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 (¬es, *input_line_pointer);
+ ++input_line_pointer;
+ ++len;
+ }
+ obstack_1grow (¬es, '\0');
+ filename = obstack_finish (¬es);
+ }
+ 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"
+ }
+