#include "as.h"
#include "safe-ctype.h"
#include "opcode/tic80.h"
#define internal_error(what) \
as_fatal (_("internal error:%s:%d: %s\n"), __FILE__, __LINE__, what)
#define internal_error_a(what,arg) \
as_fatal (_("internal error:%s:%d: %s %ld\n"), __FILE__, __LINE__, what, arg)
const char comment_chars[] = ";";
const char line_comment_chars[] = ";*#";
const char line_separator_chars[] = "";
const char EXP_CHARS[] = "eE";
const char FLT_CHARS[] = "fF";
const pseudo_typeS md_pseudo_table[] = {
{ "align", s_align_bytes, 4 },
{ "word", cons, 4 },
{ "bss", s_lcomm_bytes, 1 },
{ "sect", obj_coff_section, 0},
{ "section", obj_coff_section, 0},
{ NULL, NULL, 0 }
};
static struct hash_control *tic80_hash;
static struct tic80_opcode * find_opcode PARAMS ((struct tic80_opcode *, expressionS []));
static void build_insn PARAMS ((struct tic80_opcode *, expressionS *));
static int get_operands PARAMS ((expressionS exp[]));
static int const_overflow PARAMS ((unsigned long num, int bits, int flags));
static int tic80_relax = 0;
int
md_estimate_size_before_relax (fragP, segment_type)
fragS *fragP ATTRIBUTE_UNUSED;
segT segment_type ATTRIBUTE_UNUSED;
{
internal_error (_("Relaxation is a luxury we can't afford"));
return (-1);
}
symbolS *
md_undefined_symbol (name)
char *name ATTRIBUTE_UNUSED;
{
return 0;
}
#define MAX_LITTLENUMS 4
char *
md_atof (type, litP, sizeP)
int 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;
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 (NULL);
}
static int
const_overflow (num, bits, flags)
unsigned long num;
int bits;
int flags;
{
long min, max;
int retval = 0;
if (bits >= 32)
return retval;
if (flags & TIC80_OPERAND_SIGNED)
{
max = (1 << (bits - 1)) - 1;
min = - (1 << (bits - 1));
retval = (long) num > max || (long) num < min;
}
else
{
max = (1 << bits) - 1;
retval = num > (unsigned long) max;
}
return retval;
}
static int
get_operands (exp)
expressionS exp[];
{
char *p = input_line_pointer;
int numexp = 0;
int parens = 0;
while (*p)
{
while (*p == ' ' || *p == '\t' || *p == ',')
p++;
if (*p == 0 || *p == '\n' || *p == '\r')
break;
if (*p == ':')
{
p++;
exp[numexp].X_op = O_absent;
if (*p == 'm')
{
p++;
exp[numexp].X_add_number = TIC80_OPERAND_M_SI | TIC80_OPERAND_M_LI;
}
else if (*p == 's')
{
p++;
exp[numexp].X_add_number = TIC80_OPERAND_SCALED;
}
else
{
as_bad (_("':' not followed by 'm' or 's'"));
}
numexp++;
continue;
}
if (*p == '(')
{
if (++parens != 1)
as_bad (_("paren nesting"));
p++;
continue;
}
if (*p == ')')
{
if (--parens < 0)
as_bad (_("mismatched parenthesis"));
p++;
continue;
}
input_line_pointer = p;
expression (&exp[numexp]);
if (exp[numexp].X_op == O_illegal)
{
as_bad (_("illegal operand"));
}
else if (exp[numexp].X_op == O_absent)
{
as_bad (_("missing operand"));
}
numexp++;
p = input_line_pointer;
}
if (parens)
{
exp[numexp].X_op = O_absent;
exp[numexp++].X_add_number = TIC80_OPERAND_PARENS;
}
exp[numexp].X_op = O_illegal;
return (numexp);
}
static struct tic80_opcode *
find_opcode (opcode, myops)
struct tic80_opcode *opcode;
expressionS myops[];
{
int numexp;
int expi;
int opi;
int match = 0;
struct tic80_opcode *opc = opcode;
const struct tic80_opcode *end;
numexp = get_operands (myops);
end = tic80_opcodes + tic80_num_opcodes;
while (!match && (opc < end) && (strcmp (opc->name, opcode->name) == 0))
{
match = 1;
for (expi = 0, opi = -1; (expi < numexp) && match; expi++)
{
int bits, flags, X_op, num;
X_op = myops[expi].X_op;
num = myops[expi].X_add_number;
if (X_op != O_absent)
{
opi++;
}
flags = tic80_operands[opc->operands[opi]].flags;
bits = tic80_operands[opc->operands[opi]].bits;
switch (X_op)
{
case O_register:
if (((flags & TIC80_OPERAND_GPR) != (num & TIC80_OPERAND_GPR)) ||
((flags & TIC80_OPERAND_FPA) != (num & TIC80_OPERAND_FPA)) ||
((flags & TIC80_OPERAND_CR) != (num & TIC80_OPERAND_CR)) ||
((flags & TIC80_OPERAND_EVEN) && (num & 1)) ||
const_overflow (num & ~TIC80_OPERAND_MASK, bits, flags))
{
match = 0;
}
break;
case O_constant:
if ((flags & TIC80_OPERAND_ENDMASK) && (num == 32))
{
num = 0;
}
if ((flags & (TIC80_OPERAND_FPA | TIC80_OPERAND_GPR)) ||
const_overflow (num, bits, flags))
{
match = 0;
}
break;
case O_symbol:
if ((bits < 32) && (flags & TIC80_OPERAND_PCREL)
&& !tic80_relax)
{
}
else if ((bits == 32))
{
}
else
{
match = 0;
}
break;
case O_absent:
if (!((num & flags & TIC80_OPERAND_M_SI)
|| (num & flags & TIC80_OPERAND_M_LI)
|| (num & flags & TIC80_OPERAND_SCALED)))
{
match = 0;
}
break;
case O_big:
if ((num > 0) || !(flags & TIC80_OPERAND_FLOAT))
{
match = 0;
}
break;
case O_illegal:
case O_symbol_rva:
case O_uminus:
case O_bit_not:
case O_logical_not:
case O_multiply:
case O_divide:
case O_modulus:
case O_left_shift:
case O_right_shift:
case O_bit_inclusive_or:
case O_bit_or_not:
case O_bit_exclusive_or:
case O_bit_and:
case O_add:
case O_subtract:
case O_eq:
case O_ne:
case O_lt:
case O_le:
case O_ge:
case O_gt:
case O_logical_and:
case O_logical_or:
case O_max:
default:
internal_error_a (_("unhandled expression type"), (long) X_op);
}
}
if (!match)
opc++;
}
return (match ? opc : NULL);
}
static void
build_insn (opcode, opers)
struct tic80_opcode *opcode;
expressionS *opers;
{
int expi;
int opi;
unsigned long insn[2];
char *f;
fragS *ffrag;
char *fx = NULL;
fragS *fxfrag;
insn[0] = opcode->opcode;
insn[1] = 0;
f = frag_more (4);
ffrag = frag_now;
for (expi = 0, opi = -1; opers[expi].X_op != O_illegal; expi++)
{
int bits, shift, flags, X_op, num;
X_op = opers[expi].X_op;
num = opers[expi].X_add_number;
if (X_op != O_absent)
{
opi++;
}
flags = tic80_operands[opcode->operands[opi]].flags;
bits = tic80_operands[opcode->operands[opi]].bits;
shift = tic80_operands[opcode->operands[opi]].shift;
switch (X_op)
{
case O_register:
num &= ~TIC80_OPERAND_MASK;
insn[0] = insn[0] | (num << shift);
break;
case O_constant:
if ((flags & TIC80_OPERAND_ENDMASK) && (num == 32))
{
num = 0;
}
else if ((flags & TIC80_OPERAND_BITNUM))
{
num = (~num & 0x1F);
}
if (bits < 32)
{
num &= (1 << bits) - 1;
insn[0] = insn[0] | (num << shift);
}
else
{
fx = frag_more (4);
fxfrag = frag_now;
insn[1] = num;
}
break;
case O_symbol:
if (bits == 32)
{
fx = frag_more (4);
fxfrag = frag_now;
insn[1] = 0;
if (flags & TIC80_OPERAND_PCREL)
{
fix_new_exp (fxfrag,
fx - (fxfrag->fr_literal),
4,
&opers[expi],
1,
R_MPPCR);
}
else
{
fix_new_exp (fxfrag,
fx - (fxfrag->fr_literal),
4,
&opers[expi],
0,
R_RELLONGX);
}
}
else if (flags & TIC80_OPERAND_PCREL)
{
fix_new_exp (ffrag,
f - (ffrag->fr_literal),
4,
&opers[expi],
1,
R_MPPCR15W);
}
else
{
internal_error (_("symbol reloc that is not PC relative or 32 bits"));
}
break;
case O_absent:
if ((num & TIC80_OPERAND_M_SI)
&& (flags & TIC80_OPERAND_M_SI))
{
insn[0] = insn[0] | (1 << 17);
}
else if ((num & TIC80_OPERAND_M_LI)
&& (flags & TIC80_OPERAND_M_LI))
{
insn[0] = insn[0] | (1 << 15);
}
else if ((num & TIC80_OPERAND_SCALED)
&& (flags & TIC80_OPERAND_SCALED))
{
insn[0] = insn[0] | (1 << 11);
}
else if ((num & TIC80_OPERAND_PARENS)
&& (flags & TIC80_OPERAND_PARENS))
{
}
else
{
internal_error_a (_("unhandled operand modifier"),
(long) opers[expi].X_add_number);
}
break;
case O_big:
fx = frag_more (4);
fxfrag = frag_now;
{
int precision = 2;
long exponent_bits = 8L;
LITTLENUM_TYPE words[2];
gen_to_words (words, precision, exponent_bits);
insn[1] = (words[0] << 16) | words[1];
}
break;
case O_illegal:
case O_symbol_rva:
case O_uminus:
case O_bit_not:
case O_logical_not:
case O_multiply:
case O_divide:
case O_modulus:
case O_left_shift:
case O_right_shift:
case O_bit_inclusive_or:
case O_bit_or_not:
case O_bit_exclusive_or:
case O_bit_and:
case O_add:
case O_subtract:
case O_eq:
case O_ne:
case O_lt:
case O_le:
case O_ge:
case O_gt:
case O_logical_and:
case O_logical_or:
case O_max:
default:
internal_error_a (_("unhandled expression"), (long) X_op);
break;
}
}
md_number_to_chars (f, insn[0], 4);
if (fx != NULL)
{
md_number_to_chars (fx, insn[1], 4);
}
}
void
md_assemble (str)
char *str;
{
char *scan;
char *input_line_save;
struct tic80_opcode *opcode;
expressionS myops[16];
assert (str);
while (ISSPACE (*str))
str++;
for (scan = str; *scan != '\000' && !ISSPACE (*scan); scan++)
;
if (*scan != '\000')
*scan++ = '\000';
if ((opcode = (struct tic80_opcode *) hash_find (tic80_hash, str)) == NULL)
{
as_bad (_("Invalid mnemonic: '%s'"), str);
return;
}
str = scan;
while (ISSPACE (*scan))
scan++;
input_line_save = input_line_pointer;
input_line_pointer = str;
opcode = find_opcode (opcode, myops);
if (opcode == NULL)
as_bad (_("Invalid operands: '%s'"), input_line_save);
input_line_pointer = input_line_save;
build_insn (opcode, myops);
}
void
md_begin ()
{
char *prev_name = "";
register const struct tic80_opcode *op;
register const struct tic80_opcode *op_end;
const struct predefined_symbol *pdsp;
extern int coff_flags;
coff_flags |= F_AR32WR;
tic80_hash = hash_new ();
op_end = tic80_opcodes + tic80_num_opcodes;
for (op = tic80_opcodes; op < op_end; op++)
{
if (strcmp (prev_name, op->name) != 0)
{
prev_name = (char *) op->name;
hash_insert (tic80_hash, op->name, (char *) op);
}
}
pdsp = NULL;
while ((pdsp = tic80_next_predefined_symbol (pdsp)) != NULL)
{
segT segment;
valueT valu;
int symtype;
symtype = PDS_VALUE (pdsp) & TIC80_OPERAND_MASK;
switch (symtype)
{
case TIC80_OPERAND_GPR:
case TIC80_OPERAND_FPA:
case TIC80_OPERAND_CR:
segment = reg_section;
valu = PDS_VALUE (pdsp);
break;
case TIC80_OPERAND_CC:
case TIC80_OPERAND_BITNUM:
segment = absolute_section;
valu = PDS_VALUE (pdsp) & ~TIC80_OPERAND_MASK;
break;
default:
internal_error_a (_("unhandled predefined symbol bits"),
(long) symtype);
break;
}
symbol_table_insert (symbol_create (PDS_NAME (pdsp), segment, valu,
&zero_address_frag));
}
}
const char *md_shortopts = "";
struct option md_longopts[] = {
#define OPTION_RELAX (OPTION_MD_BASE)
{"relax", no_argument, NULL, OPTION_RELAX},
#define OPTION_NO_RELAX (OPTION_RELAX + 1)
{"no-relax", no_argument, NULL, OPTION_NO_RELAX},
{NULL, no_argument, NULL, 0}
};
size_t md_longopts_size = sizeof (md_longopts);
int
md_parse_option (c, arg)
int c;
char *arg ATTRIBUTE_UNUSED;
{
switch (c)
{
case OPTION_RELAX:
tic80_relax = 1;
break;
case OPTION_NO_RELAX:
tic80_relax = 0;
break;
default:
return (0);
}
return (1);
}
void
md_show_usage (stream)
FILE *stream;
{
fprintf (stream, "\
TIc80 options:\n\
-relax alter PC relative branch instructions to use long form when needed\n\
-no-relax always use short PC relative branch instructions, error on overflow\n");
}
void
md_apply_fix3 (fixP, valP, seg)
fixS *fixP;
valueT * valP;
segT seg ATTRIBUTE_UNUSED;
{
long val = * (long *) valP;
char *dest = fixP->fx_frag->fr_literal + fixP->fx_where;
int overflow;
switch (fixP->fx_r_type)
{
case R_RELLONGX:
md_number_to_chars (dest, (valueT) val, 4);
break;
case R_MPPCR:
val >>= 2;
val += 1;
md_number_to_chars (dest, (valueT) val, 4);
break;
case R_MPPCR15W:
overflow = (val < -65536L) || (val > 65532L);
if (overflow)
{
as_bad_where (fixP->fx_file, fixP->fx_line,
_("PC offset 0x%lx outside range 0x%lx-0x%lx"),
val, -65536L, 65532L);
}
else
{
val >>= 2;
*dest++ = val & 0xFF;
val >>= 8;
*dest = (*dest & 0x80) | (val & 0x7F);
}
break;
case R_ABS:
md_number_to_chars (dest, (valueT) val, fixP->fx_size);
break;
default:
internal_error_a (_("unhandled relocation type in fixup"),
(long) fixP->fx_r_type);
break;
}
if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
fixP->fx_done = 1;
}
long
md_pcrel_from (fixP)
fixS *fixP;
{
return (fixP->fx_frag->fr_address + fixP->fx_where);
}
void
md_convert_frag (headers, seg, fragP)
object_headers *headers ATTRIBUTE_UNUSED;
segT seg ATTRIBUTE_UNUSED;
fragS *fragP ATTRIBUTE_UNUSED;
{
internal_error (_("md_convert_frag() not implemented yet"));
abort ();
}
void
tc_coff_symbol_emit_hook (ignore)
symbolS *ignore ATTRIBUTE_UNUSED;
{
}
#if defined OBJ_COFF
short
tc_coff_fix2rtype (fixP)
fixS *fixP;
{
return (fixP->fx_r_type);
}
#endif