#include "as.h"
#include "safe-ctype.h"
#include "opcode/a29k.h"
#define machine_opcode a29k_opcode
#define machine_opcodes a29k_opcodes
#define machine_ip a29k_ip
#define machine_it a29k_it
#define IMMEDIATE_BIT 0x01000000
#define ABSOLUTE_BIT 0x01000000
#define CE_BIT 0x00800000
#define UI_BIT 0x00000080
static struct hash_control *op_hash = NULL;
struct machine_it
{
char *error;
unsigned long opcode;
struct nlist *nlistp;
expressionS exp;
int pcrel;
int reloc_offset;
int reloc;
}
the_insn;
static void machine_ip PARAMS ((char *str));
#ifndef OBJ_COFF
static void s_data1 PARAMS ((void));
static void s_use PARAMS ((int));
#endif
static void insert_sreg PARAMS ((char *, int));
static void define_some_regs PARAMS ((void));
static char *parse_operand PARAMS ((char *, expressionS *, int));
const pseudo_typeS
md_pseudo_table[] =
{
{"align", s_align_bytes, 4},
{"block", s_space, 0},
{"cputype", s_ignore, 0},
{"reg", s_lsym, 0},
{"space", s_ignore, 0},
{"sect", s_ignore, 0},
#ifndef OBJ_COFF
{"use", s_use, 0},
#endif
{"word", cons, 4},
{NULL, 0, 0},
};
#if defined(BFD_HEADERS)
#ifdef RELSZ
const int md_reloc_size = RELSZ;
#else
const int md_reloc_size = 12;
#endif
#else
const int md_reloc_size = 12;
#endif
const char comment_chars[] = ";";
const char line_comment_chars[] = "#";
const char line_separator_chars[] = "@";
const char EXP_CHARS[] = "eE";
const char FLT_CHARS[] = "rRsSfFdDxXpP";
#define ANNUL (1 << 29)
#ifndef OBJ_COFF
static void
s_use (ignore)
int ignore;
{
if (strncmp (input_line_pointer, ".text", 5) == 0)
{
input_line_pointer += 5;
s_text (0);
return;
}
if (strncmp (input_line_pointer, ".data", 5) == 0)
{
input_line_pointer += 5;
s_data (0);
return;
}
if (strncmp (input_line_pointer, ".data1", 6) == 0)
{
input_line_pointer += 6;
s_data1 ();
return;
}
if (strncmp (input_line_pointer, ".lit", 4) == 0)
{
input_line_pointer += 4;
subseg_set (SEG_DATA, 200);
demand_empty_rest_of_line ();
return;
}
as_bad (_("Unknown segment type"));
demand_empty_rest_of_line ();
}
static void
s_data1 ()
{
subseg_set (SEG_DATA, 1);
demand_empty_rest_of_line ();
}
#endif
static void
insert_sreg (regname, regnum)
char *regname;
int regnum;
{
char buf[80];
int i;
symbol_table_insert (symbol_new (regname, SEG_REGISTER, (valueT) regnum,
&zero_address_frag));
for (i = 0; regname[i]; i++)
buf[i] = TOUPPER (regname[i]);
buf[i] = '\0';
symbol_table_insert (symbol_new (buf, SEG_REGISTER, (valueT) regnum,
&zero_address_frag));
}
static void
define_some_regs ()
{
#define SREG 256
insert_sreg ("vab", SREG + 0);
insert_sreg ("ops", SREG + 1);
insert_sreg ("cps", SREG + 2);
insert_sreg ("cfg", SREG + 3);
insert_sreg ("cha", SREG + 4);
insert_sreg ("chd", SREG + 5);
insert_sreg ("chc", SREG + 6);
insert_sreg ("rbp", SREG + 7);
insert_sreg ("tmc", SREG + 8);
insert_sreg ("tmr", SREG + 9);
insert_sreg ("pc0", SREG + 10);
insert_sreg ("pc1", SREG + 11);
insert_sreg ("pc2", SREG + 12);
insert_sreg ("mmu", SREG + 13);
insert_sreg ("lru", SREG + 14);
insert_sreg ("rsn", SREG + 15);
insert_sreg ("rma0", SREG + 16);
insert_sreg ("rmc0", SREG + 17);
insert_sreg ("rma1", SREG + 18);
insert_sreg ("rmc1", SREG + 19);
insert_sreg ("spc0", SREG + 20);
insert_sreg ("spc1", SREG + 21);
insert_sreg ("spc2", SREG + 22);
insert_sreg ("iba0", SREG + 23);
insert_sreg ("ibc0", SREG + 24);
insert_sreg ("iba1", SREG + 25);
insert_sreg ("ibc1", SREG + 26);
insert_sreg ("dba", SREG + 27);
insert_sreg ("dbc", SREG + 28);
insert_sreg ("cir", SREG + 29);
insert_sreg ("cdr", SREG + 30);
insert_sreg ("ipc", SREG + 128);
insert_sreg ("ipa", SREG + 129);
insert_sreg ("ipb", SREG + 130);
insert_sreg ("q", SREG + 131);
insert_sreg ("alu", SREG + 132);
insert_sreg ("bp", SREG + 133);
insert_sreg ("fc", SREG + 134);
insert_sreg ("cr", SREG + 135);
insert_sreg ("fpe", SREG + 160);
insert_sreg ("inte", SREG + 161);
insert_sreg ("fps", SREG + 162);
insert_sreg ("exop", SREG + 164);
}
void
md_begin ()
{
register const char *retval = NULL;
int lose = 0;
register int skipnext = 0;
register unsigned int i;
register char *strend, *strend2;
op_hash = hash_new ();
for (i = 0; i < num_opcodes; i++)
{
const char *name = machine_opcodes[i].name;
if (skipnext)
{
skipnext = 0;
continue;
}
if (!strcmp (name, machine_opcodes[i + 1].name))
{
if ((machine_opcodes[i].opcode & 0x01000000) != 0
|| (machine_opcodes[i + 1].opcode & 0x01000000) == 0
|| ((machine_opcodes[i].opcode | 0x01000000)
!= machine_opcodes[i + 1].opcode))
goto bad_table;
strend = machine_opcodes[i].args + strlen (machine_opcodes[i].args) - 1;
strend2 = machine_opcodes[i + 1].args + strlen (machine_opcodes[i + 1].args) - 1;
switch (*strend)
{
case 'b':
if (*strend2 != 'i')
goto bad_table;
break;
case 'P':
if (*strend2 != 'A')
goto bad_table;
break;
default:
bad_table:
fprintf (stderr, "internal error: can't handle opcode %s\n",
name);
lose = 1;
}
skipnext = 1;
}
retval = hash_insert (op_hash, name, (PTR) &machine_opcodes[i]);
if (retval != NULL)
{
fprintf (stderr, "internal error: can't hash `%s': %s\n",
machine_opcodes[i].name, retval);
lose = 1;
}
}
if (lose)
as_fatal (_("Broken assembler. No assembly attempted."));
define_some_regs ();
}
void
md_assemble (str)
char *str;
{
char *toP;
know (str);
machine_ip (str);
toP = frag_more (4);
md_number_to_chars (toP, the_insn.opcode, 4);
if (the_insn.reloc != NO_RELOC)
{
fix_new_exp (frag_now,
(toP - frag_now->fr_literal + the_insn.reloc_offset),
4,
&the_insn.exp,
the_insn.pcrel,
the_insn.reloc);
}
}
static char *
parse_operand (s, operandp, opt)
char *s;
expressionS *operandp;
int opt;
{
char *save = input_line_pointer;
char *new;
input_line_pointer = s;
expression (operandp);
if (operandp->X_op == O_absent && ! opt)
as_bad (_("missing operand"));
new = input_line_pointer;
input_line_pointer = save;
return new;
}
static void
machine_ip (str)
char *str;
{
char *s;
const char *args;
struct machine_opcode *insn;
char *argsStart;
unsigned long opcode;
expressionS the_operand;
expressionS *operand = &the_operand;
unsigned int reg;
s = str;
if (ISALPHA (*s))
for (; ISALNUM (*s); ++s)
*s = TOLOWER (*s);
switch (*s)
{
case '\0':
break;
case ' ':
*s++ = '\0';
break;
default:
as_bad (_("Unknown opcode: `%s'"), str);
return;
}
if ((insn = (struct machine_opcode *) hash_find (op_hash, str)) == NULL)
{
as_bad (_("Unknown opcode `%s'."), str);
return;
}
argsStart = s;
opcode = insn->opcode;
memset (&the_insn, '\0', sizeof (the_insn));
the_insn.reloc = NO_RELOC;
if (insn->args[0] != '\0')
{
s = parse_operand (s, operand, insn->args[0] == 'I');
}
for (args = insn->args;; ++args)
{
switch (*args)
{
case '\0':
if (*s == '\0')
{
the_insn.opcode = opcode;
return;
}
as_bad (_("Too many operands: %s"), s);
break;
case ',':
if (*s++ == ',')
{
s = parse_operand (s, operand, args[1] == 'I');
continue;
}
break;
case 'v':
if (operand->X_op == O_constant)
{
if (operand->X_add_number < 256)
{
opcode |= (operand->X_add_number << 16);
continue;
}
else
{
as_bad (_("Immediate value of %ld is too large"),
(long) operand->X_add_number);
continue;
}
}
the_insn.reloc = RELOC_8;
the_insn.reloc_offset = 1;
the_insn.exp = *operand;
continue;
case 'b':
case 'i':
if (operand->X_op == O_register)
goto general_reg;
if ((insn->opcode | IMMEDIATE_BIT) != (insn + 1)->opcode)
break;
opcode |= IMMEDIATE_BIT;
if (operand->X_op == O_constant)
{
if (operand->X_add_number < 256)
{
opcode |= operand->X_add_number;
continue;
}
else
{
as_bad (_("Immediate value of %ld is too large"),
(long) operand->X_add_number);
continue;
}
}
the_insn.reloc = RELOC_8;
the_insn.reloc_offset = 3;
the_insn.exp = *operand;
continue;
case 'a':
case 'c':
general_reg:
if (operand->X_op != O_register)
break;
know (operand->X_add_symbol == 0);
know (operand->X_op_symbol == 0);
reg = operand->X_add_number;
if (reg >= SREG)
break;
switch (*args)
{
case 'a':
opcode |= reg << 8;
continue;
case 'b':
case 'i':
opcode |= reg;
continue;
case 'c':
opcode |= reg << 16;
continue;
}
as_fatal (_("failed sanity check."));
break;
case 'x':
case 'X':
if (operand->X_op == O_constant)
{
opcode |= (operand->X_add_number & 0xFF) << 0 |
((operand->X_add_number & 0xFF00) << 8);
continue;
}
the_insn.reloc = RELOC_CONST;
the_insn.exp = *operand;
continue;
case 'h':
if (operand->X_op == O_constant)
{
opcode |= (operand->X_add_number & 0x00FF0000) >> 16 |
(((unsigned long) operand->X_add_number
& 0xFF000000) >> 8);
continue;
}
the_insn.reloc = RELOC_CONSTH;
the_insn.exp = *operand;
continue;
case 'P':
case 'A':
if (operand->X_op == O_constant)
{
if ((insn->opcode | ABSOLUTE_BIT) != (insn + 1)->opcode)
break;
{
bfd_vma v, mask;
mask = 0x1ffff;
v = operand->X_add_number & ~ mask;
if (v)
as_bad ("call/jmp target out of range");
}
opcode |= ABSOLUTE_BIT |
(operand->X_add_number & 0x0003FC00) << 6 |
((operand->X_add_number & 0x000003FC) >> 2);
continue;
}
the_insn.reloc = RELOC_JUMPTARG;
the_insn.exp = *operand;
the_insn.pcrel = 1;
continue;
case 'e':
if (operand->X_op == O_constant)
{
if (operand->X_add_number == 0)
continue;
if (operand->X_add_number == 1)
{
opcode |= CE_BIT;
continue;
}
}
break;
case 'n':
if (operand->X_op == O_constant &&
operand->X_add_number < 128)
{
opcode |= (operand->X_add_number << 16);
continue;
}
break;
case 's':
if (operand->X_op != O_register)
break;
if (operand->X_add_number < SREG)
break;
opcode |= (operand->X_add_number & 0xFF) << 8;
continue;
case 'u':
if (operand->X_op == O_constant)
{
if (operand->X_add_number == 0)
continue;
if (operand->X_add_number == 1)
{
opcode |= UI_BIT;
continue;
}
}
break;
case 'r':
if (operand->X_op == O_constant &&
operand->X_add_number < 8)
{
opcode |= operand->X_add_number << 4;
continue;
}
break;
case 'I':
if (operand->X_op == O_absent)
continue;
else if (operand->X_op == O_constant
&& operand->X_add_number < 4)
{
opcode |= operand->X_add_number << 16;
continue;
}
break;
case 'd':
if (operand->X_op == O_constant &&
operand->X_add_number < 4)
{
opcode |= operand->X_add_number << 2;
continue;
}
break;
case 'f':
if (operand->X_op == O_constant &&
operand->X_add_number < 4)
{
opcode |= operand->X_add_number << 0;
continue;
}
break;
case 'C':
if (operand->X_op == O_constant &&
operand->X_add_number < 4)
{
opcode |= operand->X_add_number << 16;
continue;
}
break;
case 'F':
if (operand->X_op == O_constant &&
operand->X_add_number < 16)
{
opcode |= operand->X_add_number << 18;
continue;
}
break;
default:
BAD_CASE (*args);
}
as_bad ("Invalid operands");
return;
}
}
#define MAX_LITTLENUMS 6
char *
md_atof (type, litP, sizeP)
char type;
char *litP;
int *sizeP;
{
int prec;
LITTLENUM_TYPE words[MAX_LITTLENUMS];
LITTLENUM_TYPE *wordP;
char *t;
switch (type)
{
case 'f':
case 'F':
case 's':
case 'S':
prec = 2;
break;
case 'd':
case 'D':
case 'r':
case 'R':
prec = 4;
break;
case 'x':
case 'X':
prec = 6;
break;
case 'p':
case 'P':
prec = 6;
break;
default:
*sizeP = 0;
return "Bad call to MD_ATOF()";
}
t = atof_ieee (input_line_pointer, type, words);
if (t)
input_line_pointer = t;
*sizeP = prec * sizeof (LITTLENUM_TYPE);
for (wordP = words; prec--;)
{
md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE));
litP += sizeof (LITTLENUM_TYPE);
}
return 0;
}
void
md_number_to_chars (buf, val, n)
char *buf;
valueT val;
int n;
{
number_to_chars_bigendian (buf, val, n);
}
void
md_apply_fix3 (fixP, valP, seg)
fixS *fixP;
valueT *valP;
segT seg ATTRIBUTE_UNUSED;
{
valueT val = *valP;
char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
fixP->fx_addnumber = val;
know (fixP->fx_size == 4);
know (fixP->fx_r_type < NO_RELOC);
if (fixP->fx_r_type == RELOC_WDISP30 && fixP->fx_addsy)
val += fixP->fx_where + fixP->fx_frag->fr_address;
switch (fixP->fx_r_type)
{
case RELOC_32:
buf[0] = val >> 24;
buf[1] = val >> 16;
buf[2] = val >> 8;
buf[3] = val;
break;
case RELOC_8:
buf[0] = val;
break;
case RELOC_WDISP30:
val = (val >> 2) + 1;
buf[0] |= (val >> 24) & 0x3f;
buf[1] = (val >> 16);
buf[2] = val >> 8;
buf[3] = val;
break;
case RELOC_HI22:
buf[1] |= (val >> 26) & 0x3f;
buf[2] = val >> 18;
buf[3] = val >> 10;
break;
case RELOC_LO10:
buf[2] |= (val >> 8) & 0x03;
buf[3] = val;
break;
case RELOC_BASE13:
buf[2] |= (val >> 8) & 0x1f;
buf[3] = val;
break;
case RELOC_WDISP22:
val = (val >> 2) + 1;
case RELOC_BASE22:
buf[1] |= (val >> 16) & 0x3f;
buf[2] = val >> 8;
buf[3] = val;
break;
case RELOC_JUMPTARG:
if (!fixP->fx_done)
{
if (fixP->fx_pcrel
&& val != 0
&& val == - (fixP->fx_frag->fr_address + fixP->fx_where))
as_bad_where
(fixP->fx_file, fixP->fx_line,
"the linker will not handle this relocation correctly");
}
else if (fixP->fx_pcrel)
{
if (val + 0x20000 > 0x3ffff)
as_bad_where (fixP->fx_file, fixP->fx_line,
"call/jmp target out of range");
}
else
abort ();
buf[1] = val >> 10;
buf[3] = val >> 2;
break;
case RELOC_CONST:
buf[1] = val >> 8;
buf[3] = val;
break;
case RELOC_CONSTH:
buf[1] = val >> 24;
buf[3] = val >> 16;
break;
case NO_RELOC:
default:
as_bad (_("bad relocation type: 0x%02x"), fixP->fx_r_type);
break;
}
if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
fixP->fx_done = 1;
}
#ifdef OBJ_COFF
short
tc_coff_fix2rtype (fixP)
fixS *fixP;
{
switch (fixP->fx_r_type)
{
case RELOC_32:
return (R_WORD);
case RELOC_8:
return (R_BYTE);
case RELOC_CONST:
return (R_ILOHALF);
case RELOC_CONSTH:
return (R_IHIHALF);
case RELOC_JUMPTARG:
return (R_IREL);
default:
printf (_("need %o3\n"), fixP->fx_r_type);
abort ();
}
return (0);
}
#endif
void
md_convert_frag (headers, seg, fragP)
object_headers *headers ATTRIBUTE_UNUSED;
segT seg ATTRIBUTE_UNUSED;
register fragS *fragP ATTRIBUTE_UNUSED;
{
as_fatal (_("a29k_convert_frag\n"));
}
int
md_estimate_size_before_relax (fragP, segtype)
register fragS *fragP ATTRIBUTE_UNUSED;
segT segtype ATTRIBUTE_UNUSED;
{
as_fatal (_("a29k_estimate_size_before_relax\n"));
return 0;
}
#ifdef OBJ_AOUT
void
tc_aout_fix_to_chars (where, fixP, segment_address_in_file)
char *where;
fixS *fixP;
relax_addressT segment_address_in_file;
{
long r_symbolnum;
know (fixP->fx_r_type < NO_RELOC);
know (fixP->fx_addsy != NULL);
md_number_to_chars (where,
fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file,
4);
r_symbolnum = (S_IS_DEFINED (fixP->fx_addsy)
? S_GET_TYPE (fixP->fx_addsy)
: fixP->fx_addsy->sy_number);
where[4] = (r_symbolnum >> 16) & 0x0ff;
where[5] = (r_symbolnum >> 8) & 0x0ff;
where[6] = r_symbolnum & 0x0ff;
where[7] = (((!S_IS_DEFINED (fixP->fx_addsy)) << 7) & 0x80) | (0 & 0x60) | (fixP->fx_r_type & 0x1F);
md_number_to_chars (&where[8], fixP->fx_addnumber, 4);
}
#endif
const char *md_shortopts = "";
struct option md_longopts[] = {
{NULL, no_argument, NULL, 0}
};
size_t md_longopts_size = sizeof (md_longopts);
int
md_parse_option (c, arg)
int c ATTRIBUTE_UNUSED;
char *arg ATTRIBUTE_UNUSED;
{
return 0;
}
void
md_show_usage (stream)
FILE *stream ATTRIBUTE_UNUSED;
{
}
int
a29k_unrecognized_line (c)
int c;
{
int lab;
char *s;
if (c != '$'
|| ! ISDIGIT (input_line_pointer[0]))
return 0;
s = input_line_pointer;
lab = 0;
while (ISDIGIT (*s))
{
lab = lab * 10 + *s - '0';
++s;
}
if (*s != ':')
{
return 0;
}
if (dollar_label_defined (lab))
{
as_bad (_("label \"$%d\" redefined"), lab);
return 0;
}
define_dollar_label (lab);
colon (dollar_label_name (lab, 0));
input_line_pointer = s + 1;
return 1;
}
symbolS *
md_undefined_symbol (name)
char *name;
{
long regnum;
char testbuf[5 + 5];
if (name[0] == 'g' || name[0] == 'G'
|| name[0] == 'l' || name[0] == 'L'
|| name[0] == 's' || name[0] == 'S')
{
if (name[1] == 'r' || name[1] == 'R')
{
long maxreg;
regnum = atol (&name[2]);
if (name[0] == 's' || name[0] == 'S')
maxreg = 255;
else
maxreg = 127;
if (regnum > maxreg)
return NULL;
sprintf (testbuf, "%ld", regnum);
if (strcmp (testbuf, &name[2]) != 0)
return NULL;
if (name[0] == 'l' || name[0] == 'L')
regnum += 128;
else if (name[0] == 's' || name[0] == 'S')
regnum += SREG;
return (symbol_new (name, SEG_REGISTER, (valueT) regnum,
&zero_address_frag));
}
}
return NULL;
}
void
md_operand (expressionP)
expressionS *expressionP;
{
if (input_line_pointer[0] == '%' && input_line_pointer[1] == '%')
{
input_line_pointer += 2;
(void) expression (expressionP);
if (expressionP->X_op != O_constant
|| expressionP->X_add_number > 255)
as_bad (_("Invalid expression after %%%%\n"));
expressionP->X_op = O_register;
}
else if (input_line_pointer[0] == '&')
{
input_line_pointer++;
(void) expression (expressionP);
if (expressionP->X_op != O_register)
as_bad (_("Invalid register in & expression"));
else
expressionP->X_op = O_constant;
}
else if (input_line_pointer[0] == '$'
&& ISDIGIT (input_line_pointer[1]))
{
long lab;
char *name;
symbolS *sym;
++input_line_pointer;
lab = (long) get_absolute_expression ();
if (dollar_label_defined (lab))
{
name = dollar_label_name (lab, 0);
sym = symbol_find (name);
}
else
{
name = dollar_label_name (lab, 1);
sym = symbol_find_or_make (name);
}
expressionP->X_op = O_symbol;
expressionP->X_add_symbol = sym;
expressionP->X_add_number = 0;
}
else if (input_line_pointer[0] == '$')
{
char *s;
char type;
int fieldnum, fieldlimit;
LITTLENUM_TYPE floatbuf[8];
s = input_line_pointer;
++s;
fieldnum = 0;
if (strncmp (s, "double", sizeof "double" - 1) == 0)
{
s += sizeof "double" - 1;
type = 'd';
fieldlimit = 2;
}
else if (strncmp (s, "float", sizeof "float" - 1) == 0)
{
s += sizeof "float" - 1;
type = 'f';
fieldlimit = 1;
}
else if (strncmp (s, "extend", sizeof "extend" - 1) == 0)
{
s += sizeof "extend" - 1;
type = 'x';
fieldlimit = 4;
}
else
{
return;
}
if (ISDIGIT (*s))
{
fieldnum = *s - '0';
++s;
}
if (fieldnum >= fieldlimit)
return;
SKIP_WHITESPACE ();
if (*s != '(')
return;
++s;
SKIP_WHITESPACE ();
s = atof_ieee (s, type, floatbuf);
if (s == NULL)
return;
s = s;
SKIP_WHITESPACE ();
if (*s != ')')
return;
++s;
SKIP_WHITESPACE ();
input_line_pointer = s;
expressionP->X_op = O_constant;
expressionP->X_unsigned = 1;
expressionP->X_add_number = ((floatbuf[fieldnum * 2]
<< LITTLENUM_NUMBER_OF_BITS)
+ floatbuf[fieldnum * 2 + 1]);
}
}
valueT
md_section_align (segment, size)
segT segment ATTRIBUTE_UNUSED;
valueT size;
{
return size;
}
long
md_pcrel_from (fixP)
fixS *fixP;
{
return fixP->fx_where + fixP->fx_frag->fr_address;
}