blob: 3c2649f072f8a07e74564c80f7603965ec3ac8d8 [file] [log] [blame]
/* tc-tic54x.c -- Assembly code for the Texas Instruments TMS320C54X
Copyright 1999, 2000 Free Software Foundation, Inc.
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"));
#if 0
fprintf (stream, _("-mcoff-version={0|1|2} Select COFF version\n"));
#endif
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 },
#if 0
{ "mcoff-version", required_argument, NULL, OPTION_COFF_VERSION },
#endif
{ "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 (c)
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 (stag, path, base_offset, rootsym, root_stag_name)
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 (parent, name, offset, stag)
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 (ver)
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 (version)
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 (type)
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 (arg)
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 (type)
int type;
{
char *msg;
char c;
int len;
ILLEGAL_WITHIN_STRUCT ();
if (*input_line_pointer == '"')
msg = demand_copy_C_string (&len);
else
{
msg = input_line_pointer;
while (!is_end_of_line[(int) *input_line_pointer])
++input_line_pointer;
c = *input_line_pointer;
*input_line_pointer = 0;
msg = strcpy (xmalloc (strlen (msg) + 1), msg);
*input_line_pointer = c;
}
switch (type)
{
case 'm':
as_tsktsk ("%s", msg);
break;
case 'w':
as_warn ("%s", msg);
break;
case 'e':
as_bad ("%s", msg);
break;
}
demand_empty_rest_of_line ();
}
/* .label <symbol>
Define a special symbol that refers to the loadtime address rather than the
runtime address within the current section.
This symbol gets a special storage class so that when it is resolved, it is
resolved relative to the load address (lma) of the section rather than the
run address (vma). */
static void
tic54x_label (ignored)
int ignored ATTRIBUTE_UNUSED;
{
char *name = input_line_pointer;
symbolS *symbolP;
int c;
ILLEGAL_WITHIN_STRUCT ();
c = get_symbol_end ();
symbolP = colon (name);
S_SET_STORAGE_CLASS (symbolP, C_STATLAB);
*input_line_pointer = c;
demand_empty_rest_of_line ();
}
/* .mmregs
Install all memory-mapped register names into the symbol table as
absolute local symbols. */
static void
tic54x_mmregs (ignored)
int ignored ATTRIBUTE_UNUSED;
{
symbol *sym;
ILLEGAL_WITHIN_STRUCT ();
for (sym = (symbol *) mmregs; sym->name; sym++)
{
symbolS *symbolP = symbol_new (sym->name, absolute_section,
(valueT) sym->value, &zero_address_frag);
SF_SET_LOCAL (symbolP);
symbol_table_insert (symbolP);
}
}
/* .loop [count]
Count defaults to 1024. */
static void
tic54x_loop (int count)
{
ILLEGAL_WITHIN_STRUCT ();
SKIP_WHITESPACE ();
if (!is_end_of_line[(int) *input_line_pointer])
count = get_absolute_expression ();
do_repeat (count, "LOOP", "ENDLOOP");
}
/* Normally, endloop gets eaten by the preceding loop. */
static void
tic54x_endloop (ignore)
int ignore ATTRIBUTE_UNUSED;
{
as_bad (_("ENDLOOP without corresponding LOOP"));
ignore_rest_of_line ();
}
/* .break [condition]. */
static void
tic54x_break (ignore)
int ignore ATTRIBUTE_UNUSED;
{
int cond = 1;
ILLEGAL_WITHIN_STRUCT ();
SKIP_WHITESPACE ();
if (!is_end_of_line[(int) *input_line_pointer])
cond = get_absolute_expression ();
if (cond)
end_repeat (substitution_line ? 1 : 0);
}
static void
set_address_mode (mode)
int mode;
{
amode = mode;
if (mode == far_mode)
{
symbolS *symbolP = symbol_new ("__allow_far", absolute_section,
(valueT) 1, &zero_address_frag);
SF_SET_LOCAL (symbolP);
symbol_table_insert (symbolP);
}
}
static int address_mode_needs_set = 1;
static void
tic54x_address_mode (mode)
int mode;
{
if (assembly_begun && amode != (unsigned) mode)
{
as_bad (_("Mixing of normal and extended addressing not supported"));
ignore_rest_of_line ();
return;
}
if (mode == far_mode && cpu != VNONE && cpu != V548 && cpu != V549)
{
as_bad (_("Extended addressing not supported on the specified CPU"));
ignore_rest_of_line ();
return;
}
set_address_mode (mode);
demand_empty_rest_of_line ();
}
/* .sblock "section"|section [,...,"section"|section]
Designate initialized sections for blocking. */
static void
tic54x_sblock (ignore)
int ignore ATTRIBUTE_UNUSED;
{
int c = ',';
ILLEGAL_WITHIN_STRUCT ();
while (c == ',')
{
segT seg;
char *name;
if (*input_line_pointer == '"')
{
int len;
name = demand_copy_C_string (&len);
}
else
{
char *section_name = input_line_pointer;
c = get_symbol_end ();
name = xmalloc (strlen (section_name) + 1);
strcpy (name, section_name);
*input_line_pointer = c;
}
seg = bfd_get_section_by_name (stdoutput, name);
if (seg == NULL)
{
as_bad (_("Unrecognized section '%s'"), name);
ignore_rest_of_line ();
return;
}
else if (!tic54x_initialized_section (seg))
{
as_bad (_(".sblock may be used for initialized sections only"));
ignore_rest_of_line ();
return;
}
seg->flags |= SEC_BLOCK;
c = *input_line_pointer;
if (!is_end_of_line[(int) c])
++input_line_pointer;
}
demand_empty_rest_of_line ();
}
/* symbol .set value
symbol .equ value
value must be defined externals; no forward-referencing allowed
symbols assigned with .set/.equ may not be redefined. */
static void
tic54x_set (ignore)
int ignore ATTRIBUTE_UNUSED;
{
symbolS *symbolP;
char *name;
ILLEGAL_WITHIN_STRUCT ();
if (!line_label)
{
as_bad (_("Symbol missing for .set/.equ"));
ignore_rest_of_line ();
return;
}
name = xstrdup (S_GET_NAME (line_label));
line_label = NULL;
if ((symbolP = symbol_find (name)) == NULL
&& (symbolP = md_undefined_symbol (name)) == NULL)
{
symbolP = symbol_new (name, absolute_section, 0, &zero_address_frag);
S_SET_STORAGE_CLASS (symbolP, C_STAT);
}
free (name);
S_SET_DATA_TYPE (symbolP, T_INT);
S_SET_SEGMENT (symbolP, absolute_section);
symbol_table_insert (symbolP);
pseudo_set (symbolP);
demand_empty_rest_of_line ();
}
/* .fclist
.fcnolist
List false conditional blocks. */
static void
tic54x_fclist (int show)
{
if (show)
listing &= ~LISTING_NOCOND;
else
listing |= LISTING_NOCOND;
demand_empty_rest_of_line ();
}
static void
tic54x_sslist (int show)
{
ILLEGAL_WITHIN_STRUCT ();
listing_sslist = show;
}
/* .var SYM[,...,SYMN]
Define a substitution string to be local to a macro. */
static void
tic54x_var (ignore)
int ignore ATTRIBUTE_UNUSED;
{
static char empty[] = "";
char *name;
int c;
ILLEGAL_WITHIN_STRUCT ();
if (macro_level == 0)
{
as_bad (_(".var may only be used within a macro definition"));
ignore_rest_of_line ();
return;
}
do
{
if (!isalpha (*input_line_pointer))
{
as_bad (_("Substitution symbols must begin with a letter"));
ignore_rest_of_line ();
return;
}
name = input_line_pointer;
c = get_symbol_end ();
/* .var symbols start out with a null string. */
name = strcpy (xmalloc (strlen (name) + 1), name);
hash_insert (subsym_hash[macro_level], name, empty);
*input_line_pointer = c;
if (c == ',')
{
++input_line_pointer;
if (is_end_of_line[(int) *input_line_pointer])
c = *input_line_pointer;
}
}
while (c == ',');
demand_empty_rest_of_line ();
}
/* .mlib <macro library filename>
Macro libraries are archived (standard AR-format) text macro definitions
Expand the file and include it.
FIXME need to try the source file directory as well. */
static void
tic54x_mlib (ignore)
int ignore ATTRIBUTE_UNUSED;
{
char *filename;
char *path;
int len, i;
bfd *abfd, *mbfd;
ILLEGAL_WITHIN_STRUCT ();
/* Parse the filename. */
if (*input_line_pointer == '"')
{
if ((filename = demand_copy_C_string (&len)) == NULL)
return;
}
else
{
SKIP_WHITESPACE ();
len = 0;
while (!is_end_of_line[(int) *input_line_pointer]
&& !isspace (*input_line_pointer))
{
obstack_1grow (&notes, *input_line_pointer);
++input_line_pointer;
++len;
}
obstack_1grow (&notes, '\0');
filename = obstack_finish (&notes);
}
demand_empty_rest_of_line ();
tic54x_set_default_include (0);
path = xmalloc ((unsigned long) len + include_dir_maxlen + 5);
for (i = 0; i < include_dir_count; i++)
{
FILE *try;
strcpy (path, include_dirs[i]);
strcat (path, "/");
strcat (path, filename);
if ((try = fopen (path, "r")) != NULL)
{
fclose (try);
break;
}
}
if (i >= include_dir_count)
{
free (path);
path = filename;
}
/* FIXME: if path is found, malloc'd storage is not freed. Of course, this
happens all over the place, and since the assembler doesn't usually keep
running for a very long time, it really doesn't matter. */
register_dependency (path);
/* Expand all archive entries to temporary files and include them. */
abfd = bfd_openr (path, NULL);
if (!abfd)
{
as_bad (_("Can't open macro library file '%s' for reading."), path);
as_perror ("%s", path);
ignore_rest_of_line ();
return;
}
if (!bfd_check_format (abfd, bfd_archive))
{
as_bad (_("File '%s' not in macro archive format"), path);
ignore_rest_of_line ();
return;
}
/* Open each BFD as binary (it should be straight ASCII text). */
for (mbfd = bfd_openr_next_archived_file (abfd, NULL);
mbfd != NULL; mbfd = bfd_openr_next_archived_file (abfd, mbfd))
{
/* Get a size at least as big as the archive member. */
bfd_size_type size = bfd_get_size (mbfd);
char *buf = xmalloc (size);
char *fname = tmpnam (NULL);
FILE *ftmp;
/* We're not sure how big it is, but it will be smaller than "size". */
bfd_read (buf, size, 1, mbfd);
/* Write to a temporary file, then use s_include to include it
a bit of a hack. */
ftmp = fopen (fname, "w+b");
fwrite ((void *) buf, size, 1, ftmp);
if (buf[size - 1] != '\n')
fwrite ("\n", 1, 1, ftmp);
fclose (ftmp);
free (buf);
input_scrub_insert_file (fname);
unlink (fname);
}
}
const pseudo_typeS md_pseudo_table[] =
{
{ "algebraic", s_ignore , 0 },
{ "align" , tic54x_align_words , 128 },
{ "even" , tic54x_align_words , 2 },
{ "asg" , tic54x_asg , 0 },
{ "eval" , tic54x_eval , 0 },
{ "bss" , tic54x_bss , 0 },
{ "byte" , tic54x_cons , 'b' },
{ "ubyte" , tic54x_cons , 'B' },
{ "char" , tic54x_cons , 'c' },
{ "uchar" , tic54x_cons , 'C' },
{ "clink" , tic54x_clink , 0 },
{ "c_mode" , tic54x_address_mode , c_mode },
{ "copy" , tic54x_include , 'c' },
{ "include" , tic54x_include , 'i' },
{ "data" , tic54x_sect , 'd' },
{ "double" , tic54x_float_cons , 'd' },
{ "ldouble" , tic54x_float_cons , 'l' },
{ "drlist" , s_ignore , 0 },
{ "drnolist" , s_ignore , 0 },
{ "emsg" , tic54x_message , 'e' },
{ "mmsg" , tic54x_message , 'm' },
{ "wmsg" , tic54x_message , 'w' },
#if 0
{ "end" , s_end , 0 },
#endif
{ "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 },
#if 0
{ "list" , listing_list , 1 },
{ "nolist" , listing_list , 0 },
#endif
{ "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 },
#if 0
{ "page" , listing_eject , 0 },
#endif
{ "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' },
#if 0
{ "title" , listing_title , 0 },
#endif
{ "union" , tic54x_struct , 1 },
{ "endunion" , tic54x_endstruct , 1 },
{ "usect" , tic54x_usect , 0 },
{ "var" , tic54x_var , 0 },
{ "version" , tic54x_version , 0 },
{0 , 0 , 0 }
};
#if 0
/* For debugging, strings for each operand type. */
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 (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 (a, b)
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 (a, b)
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 (sym, list)
char *sym;
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 (sym, 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) :