#include "as.h"
#include "safe-ctype.h"
#include "dwarf2dbg.h"
#include "opcode/crx.h"
#include "elf/crx.h"
#define WORD_SHIFT 16
#define REG_SIZE 4
#define INSN_MAX_SIZE 3
#define MAX_REGS_IN_MASK16 8
#define streq(a, b) (strcmp (a, b) == 0)
#define strneq(a, b, c) (strncmp (a, b, c) == 0)
#define CRX_PRINT(BYTE, NUM, SHIFT) output_opcode[BYTE] |= (NUM << SHIFT)
typedef enum
{
OP_LEGAL = 0,
OP_OUT_OF_RANGE,
OP_NOT_EVEN,
OP_ILLEGAL_DISPU4,
OP_ILLEGAL_CST4,
OP_NOT_UPPER_64KB
}
op_err;
static struct hash_control *crx_inst_hash;
static struct hash_control *reg_hash;
static struct hash_control *copreg_hash;
const inst *instruction;
long output_opcode[2];
int relocatable;
char ins_parse[MAX_INST_LEN];
int cur_arg_num;
const char comment_chars[] = "#";
const char line_comment_chars[] = "#";
const char line_separator_chars[] = ";";
const char EXP_CHARS[] = "eE";
const char FLT_CHARS[] = "f'";
const char *md_shortopts = "";
struct option md_longopts[] =
{
{NULL, no_argument, NULL, 0}
};
size_t md_longopts_size = sizeof (md_longopts);
const pseudo_typeS md_pseudo_table[] =
{
{"align", s_align_bytes, 0},
{0, 0, 0}
};
const relax_typeS md_relax_table[] =
{
{0xfa, -0x100, 2, 1},
{0xfffe, -0x10000, 4, 2},
{0xfffffffe, -0xfffffffe, 6, 0},
{0xfffe, -0x10000, 4, 4},
{0xfffffffe, -0xfffffffe, 6, 0},
{0xfe, -0x100, 4, 6},
{0xfffffe, -0x1000000, 6, 0}
};
static void reset_vars (char *);
static reg get_register (char *);
static copreg get_copregister (char *);
static argtype get_optype (operand_type);
static int get_opbits (operand_type);
static int get_opflags (operand_type);
static int get_number_of_operands (void);
static void parse_operand (char *, ins *);
static int gettrap (char *);
static void handle_LoadStor (char *);
static int get_cinv_parameters (char *);
static long getconstant (long, int);
static op_err check_range (long *, int, unsigned int, int);
static int getreg_image (reg);
static void parse_operands (ins *, char *);
static void parse_insn (ins *, char *);
static void print_operand (int, int, argument *);
static void print_constant (int, int, argument *);
static int exponent2scale (int);
static void mask_reg (int, unsigned short *);
static void process_label_constant (char *, ins *);
static void set_operand (char *, ins *);
static char * preprocess_reglist (char *, int *);
static int assemble_insn (char *, ins *);
static void print_insn (ins *);
static void warn_if_needed (ins *);
static int adjust_if_needed (ins *);
static int
get_opbits (operand_type op)
{
if (op < MAX_OPRD)
return crx_optab[op].bit_size;
else
return 0;
}
static argtype
get_optype (operand_type op)
{
if (op < MAX_OPRD)
return crx_optab[op].arg_type;
else
return nullargs;
}
static int
get_opflags (operand_type op)
{
if (op < MAX_OPRD)
return crx_optab[op].flags;
else
return 0;
}
static reg
get_register (char *reg_name)
{
const reg_entry *reg;
reg = (const reg_entry *) hash_find (reg_hash, reg_name);
if (reg != NULL)
return reg->value.reg_val;
else
return nullregister;
}
static copreg
get_copregister (char *copreg_name)
{
const reg_entry *copreg;
copreg = (const reg_entry *) hash_find (copreg_hash, copreg_name);
if (copreg != NULL)
return copreg->value.copreg_val;
else
return nullcopregister;
}
valueT
md_section_align (segT seg, valueT val)
{
if (seg == text_section)
return (val + 1) & ~1;
return val;
}
void
md_operand (expressionS * exp)
{
char c = *input_line_pointer;
switch (c)
{
case '*':
input_line_pointer++;
expression (exp);
break;
default:
break;
}
}
static void
reset_vars (char *op)
{
cur_arg_num = relocatable = 0;
memset (& output_opcode, '\0', sizeof (output_opcode));
strcpy (ins_parse, op);
}
#define SWITCH_TABLE(fix) \
( (fix)->fx_addsy != NULL \
&& (fix)->fx_subsy != NULL \
&& S_GET_SEGMENT ((fix)->fx_addsy) == \
S_GET_SEGMENT ((fix)->fx_subsy) \
&& S_GET_SEGMENT (fix->fx_addsy) != undefined_section \
&& ( (fix)->fx_r_type == BFD_RELOC_CRX_NUM8 \
|| (fix)->fx_r_type == BFD_RELOC_CRX_NUM16 \
|| (fix)->fx_r_type == BFD_RELOC_CRX_NUM32))
int
crx_force_relocation (fixS *fix)
{
if (generic_force_reloc (fix) || SWITCH_TABLE (fix))
return 1;
return 0;
}
arelent *
tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS * fixP)
{
arelent * reloc;
reloc = xmalloc (sizeof (arelent));
reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
*reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
reloc->addend = fixP->fx_offset;
if (fixP->fx_subsy != NULL)
{
if (SWITCH_TABLE (fixP))
{
reloc->addend = (S_GET_VALUE (fixP->fx_addsy)
- S_GET_VALUE (fixP->fx_subsy) + fixP->fx_offset);
switch (fixP->fx_r_type)
{
case BFD_RELOC_CRX_NUM8:
fixP->fx_r_type = BFD_RELOC_CRX_SWITCH8;
break;
case BFD_RELOC_CRX_NUM16:
fixP->fx_r_type = BFD_RELOC_CRX_SWITCH16;
break;
case BFD_RELOC_CRX_NUM32:
fixP->fx_r_type = BFD_RELOC_CRX_SWITCH32;
break;
default:
abort ();
break;
}
}
else
{
as_bad_where (fixP->fx_file, fixP->fx_line,
_("can't resolve `%s' {%s section} - `%s' {%s section}"),
fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : "0",
segment_name (fixP->fx_addsy
? S_GET_SEGMENT (fixP->fx_addsy)
: absolute_section),
S_GET_NAME (fixP->fx_subsy),
segment_name (S_GET_SEGMENT (fixP->fx_addsy)));
}
}
assert ((int) fixP->fx_r_type > 0);
reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
if (reloc->howto == (reloc_howto_type *) NULL)
{
as_bad_where (fixP->fx_file, fixP->fx_line,
_("internal error: reloc %d (`%s') not supported by object file format"),
fixP->fx_r_type,
bfd_get_reloc_code_name (fixP->fx_r_type));
return NULL;
}
assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
return reloc;
}
int
md_estimate_size_before_relax (fragS *fragp, asection *seg)
{
relax_substateT subtype;
relax_substateT rlx_state[] = {0, 2,
3, 4,
5, 6};
for (subtype = 0; subtype < ARRAY_SIZE (rlx_state); subtype += 2)
{
if (fragp->fr_subtype == rlx_state[subtype]
&& (!S_IS_DEFINED (fragp->fr_symbol)
|| seg != S_GET_SEGMENT (fragp->fr_symbol)))
{
fragp->fr_subtype = rlx_state[subtype + 1];
break;
}
}
if (fragp->fr_subtype >= ARRAY_SIZE (md_relax_table))
abort ();
return md_relax_table[fragp->fr_subtype].rlx_length;
}
void
md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, fragS *fragP)
{
char *opcode = fragP->fr_literal + fragP->fr_fix;
bfd_reloc_code_real_type reloc;
subseg_change (sec, 0);
switch (fragP->fr_subtype)
{
case 0:
reloc = BFD_RELOC_CRX_REL8;
break;
case 1:
*opcode = 0x7e;
reloc = BFD_RELOC_CRX_REL16;
break;
case 2:
*opcode = 0x7f;
reloc = BFD_RELOC_CRX_REL32;
break;
case 3:
reloc = BFD_RELOC_CRX_REL16;
break;
case 4:
*++opcode = 0x31;
reloc = BFD_RELOC_CRX_REL32;
break;
case 5:
reloc = BFD_RELOC_CRX_REL8_CMP;
break;
case 6:
*++opcode = 0x31;
reloc = BFD_RELOC_CRX_REL24;
break;
default:
abort ();
break;
}
fix_new (fragP, fragP->fr_fix,
bfd_get_reloc_size (bfd_reloc_type_lookup (stdoutput, reloc)),
fragP->fr_symbol, fragP->fr_offset, 1, reloc);
fragP->fr_var = 0;
fragP->fr_fix += md_relax_table[fragP->fr_subtype].rlx_length;
}
int
md_parse_option (int c ATTRIBUTE_UNUSED, char *arg ATTRIBUTE_UNUSED)
{
return 0;
}
void
md_show_usage (FILE *stream ATTRIBUTE_UNUSED)
{
return;
}
char *
md_atof (int type, char *litP, int *sizeP)
{
int prec;
LITTLENUM_TYPE words[4];
char *t;
int i;
switch (type)
{
case 'f':
prec = 2;
break;
case 'd':
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 * 2;
if (! target_big_endian)
{
for (i = prec - 1; i >= 0; i--)
{
md_number_to_chars (litP, (valueT) words[i], 2);
litP += 2;
}
}
else
{
for (i = 0; i < prec; i++)
{
md_number_to_chars (litP, (valueT) words[i], 2);
litP += 2;
}
}
return NULL;
}
void
md_apply_fix3 (fixS *fixP, valueT *valP, segT seg)
{
valueT val = * valP;
char *buf = fixP->fx_frag->fr_literal + fixP->fx_where;
fixP->fx_offset = 0;
switch (fixP->fx_r_type)
{
case BFD_RELOC_CRX_NUM8:
bfd_put_8 (stdoutput, (unsigned char) val, buf);
break;
case BFD_RELOC_CRX_NUM16:
bfd_put_16 (stdoutput, val, buf);
break;
case BFD_RELOC_CRX_NUM32:
bfd_put_32 (stdoutput, val, buf);
break;
default:
abort ();
break;
}
fixP->fx_done = 0;
if (fixP->fx_addsy == NULL
&& fixP->fx_pcrel == 0)
fixP->fx_done = 1;
if (fixP->fx_pcrel == 1
&& fixP->fx_addsy != NULL
&& S_GET_SEGMENT (fixP->fx_addsy) == seg)
fixP->fx_done = 1;
}
long
md_pcrel_from (fixS *fixp)
{
return fixp->fx_frag->fr_address + fixp->fx_where;
}
void
md_begin (void)
{
const char *hashret = NULL;
int i = 0;
if ((crx_inst_hash = hash_new ()) == NULL)
as_fatal (_("Virtual memory exhausted"));
while (crx_instruction[i].mnemonic != NULL)
{
const char *mnemonic = crx_instruction[i].mnemonic;
hashret = hash_insert (crx_inst_hash, mnemonic,
(PTR) &crx_instruction[i]);
if (hashret != NULL && *hashret != '\0')
as_fatal (_("Can't hash `%s': %s\n"), crx_instruction[i].mnemonic,
*hashret == 0 ? _("(unknown reason)") : hashret);
do
{
++i;
}
while (crx_instruction[i].mnemonic != NULL
&& streq (crx_instruction[i].mnemonic, mnemonic));
}
if ((reg_hash = hash_new ()) == NULL)
as_fatal (_("Virtual memory exhausted"));
{
const reg_entry *regtab;
for (regtab = crx_regtab;
regtab < (crx_regtab + NUMREGS); regtab++)
{
hashret = hash_insert (reg_hash, regtab->name, (PTR) regtab);
if (hashret)
as_fatal (_("Internal Error: Can't hash %s: %s"),
regtab->name,
hashret);
}
}
if ((copreg_hash = hash_new ()) == NULL)
as_fatal (_("Virtual memory exhausted"));
{
const reg_entry *copregtab;
for (copregtab = crx_copregtab; copregtab < (crx_copregtab + NUMCOPREGS);
copregtab++)
{
hashret = hash_insert (copreg_hash, copregtab->name, (PTR) copregtab);
if (hashret)
as_fatal (_("Internal Error: Can't hash %s: %s"),
copregtab->name,
hashret);
}
}
linkrelax = 1;
}
static void
process_label_constant (char *str, ins * crx_ins)
{
char *saved_input_line_pointer;
argument *cur_arg = &crx_ins->arg[cur_arg_num];
saved_input_line_pointer = input_line_pointer;
input_line_pointer = str;
expression (&crx_ins->exp);
switch (crx_ins->exp.X_op)
{
case O_big:
case O_absent:
as_bad (_("missing or invalid displacement expression `%s' taken as 0"),
str);
crx_ins->exp.X_op = O_constant;
crx_ins->exp.X_add_number = 0;
crx_ins->exp.X_add_symbol = (symbolS *) 0;
crx_ins->exp.X_op_symbol = (symbolS *) 0;
case O_constant:
cur_arg->X_op = O_constant;
cur_arg->constant = crx_ins->exp.X_add_number;
break;
case O_symbol:
case O_subtract:
case O_add:
cur_arg->X_op = O_symbol;
crx_ins->rtype = BFD_RELOC_NONE;
relocatable = 1;
switch (cur_arg->type)
{
case arg_cr:
if (IS_INSN_TYPE (LD_STOR_INS_INC))
crx_ins->rtype = BFD_RELOC_CRX_REGREL12;
else if (IS_INSN_TYPE (CSTBIT_INS)
|| IS_INSN_TYPE (STOR_IMM_INS))
crx_ins->rtype = BFD_RELOC_CRX_REGREL28;
else
crx_ins->rtype = BFD_RELOC_CRX_REGREL32;
break;
case arg_idxr:
crx_ins->rtype = BFD_RELOC_CRX_REGREL22;
break;
case arg_c:
if (IS_INSN_MNEMONIC ("bal") || IS_INSN_TYPE (DCR_BRANCH_INS))
crx_ins->rtype = BFD_RELOC_CRX_REL16;
else if (IS_INSN_TYPE (BRANCH_INS))
crx_ins->rtype = BFD_RELOC_CRX_REL8;
else if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (STOR_IMM_INS)
|| IS_INSN_TYPE (CSTBIT_INS))
crx_ins->rtype = BFD_RELOC_CRX_ABS32;
else if (IS_INSN_TYPE (BRANCH_NEQ_INS))
crx_ins->rtype = BFD_RELOC_CRX_REL4;
else if (IS_INSN_TYPE (CMPBR_INS) || IS_INSN_TYPE (COP_BRANCH_INS))
crx_ins->rtype = BFD_RELOC_CRX_REL8_CMP;
break;
case arg_ic:
if (IS_INSN_TYPE (ARITH_INS))
crx_ins->rtype = BFD_RELOC_CRX_IMM32;
else if (IS_INSN_TYPE (ARITH_BYTE_INS))
crx_ins->rtype = BFD_RELOC_CRX_IMM16;
break;
default:
break;
}
break;
default:
cur_arg->X_op = crx_ins->exp.X_op;
break;
}
input_line_pointer = saved_input_line_pointer;
return;
}
static int
exponent2scale (int val)
{
int exponent;
if (val == 0)
return 0;
for (exponent = 0; (val != 1); val >>= 1, exponent++)
;
return exponent;
}
static void
set_operand (char *operand, ins * crx_ins)
{
char *operandS;
char *operandE;
expressionS scale;
int scale_val;
char *input_save, c;
argument *cur_arg = &crx_ins->arg[cur_arg_num];
operandS = operandE = operand;
switch (cur_arg->type)
{
case arg_sc:
case arg_ic:
operandS++;
case arg_c:
process_label_constant (operandS, crx_ins);
if (cur_arg->type != arg_ic)
cur_arg->type = arg_c;
break;
case arg_icr:
operandS++;
case arg_cr:
while (*operandE != '(')
operandE++;
*operandE = '\0';
process_label_constant (operandS, crx_ins);
operandS = operandE;
case arg_rbase:
operandS++;
while (*operandE != ')')
operandE++;
*operandE = '\0';
if ((cur_arg->r = get_register (operandS)) == nullregister)
as_bad (_("Illegal register `%s' in Instruction `%s'"),
operandS, ins_parse);
if (cur_arg->type != arg_rbase)
cur_arg->type = arg_cr;
break;
case arg_idxr:
while (*operandE != '(')
operandE++;
*operandE = '\0';
process_label_constant (operandS, crx_ins);
operandS = ++operandE;
while ((*operandE != ',') && (! ISSPACE (*operandE)))
operandE++;
*operandE++ = '\0';
if ((cur_arg->r = get_register (operandS)) == nullregister)
as_bad (_("Illegal register `%s' in Instruction `%s'"),
operandS, ins_parse);
while (ISSPACE (*operandE))
operandE++;
operandS = operandE;
while ((*operandE != ')') && (*operandE != ','))
operandE++;
c = *operandE;
*operandE++ = '\0';
if ((cur_arg->i_r = get_register (operandS)) == nullregister)
as_bad (_("Illegal register `%s' in Instruction `%s'"),
operandS, ins_parse);
while (ISSPACE (*operandE))
operandE++;
operandS = operandE;
if (c == ')')
cur_arg->scale = 0;
else
{
while (*operandE != ')')
operandE++;
*operandE = '\0';
input_save = input_line_pointer;
input_line_pointer = operandS;
expression (&scale);
input_line_pointer = input_save;
scale_val = scale.X_add_number;
if (scale_val != 1 && scale_val != 2
&& scale_val != 4 && scale_val != 8)
as_bad (_("Illegal Scale - `%d'"), scale_val);
cur_arg->scale = exponent2scale (scale_val);
}
break;
default:
break;
}
}
static void
parse_operand (char *operand, ins * crx_ins)
{
int ret_val;
argument *cur_arg = &crx_ins->arg[cur_arg_num];
cur_arg->type = nullargs;
if ((ret_val = get_register (operand)) != nullregister)
{
cur_arg->type = arg_r;
cur_arg->r = ret_val;
cur_arg->X_op = O_register;
return;
}
if ((ret_val = get_copregister (operand)) != nullcopregister)
{
cur_arg->type = arg_copr;
if (ret_val >= cs0)
cur_arg->type = arg_copsr;
cur_arg->cr = ret_val;
cur_arg->X_op = O_register;
return;
}
switch (operand[0])
{
case '$':
if (strchr (operand, '(') != NULL)
cur_arg->type = arg_icr;
else
cur_arg->type = arg_ic;
goto set_params;
break;
case '*':
cur_arg->type = arg_sc;
goto set_params;
break;
case '(':
cur_arg->type = arg_rbase;
goto set_params;
break;
default:
break;
}
if (strchr (operand, '(') != NULL)
{
if (strchr (operand, ',') != NULL
&& (strchr (operand, ',') > strchr (operand, '(')))
cur_arg->type = arg_idxr;
else
cur_arg->type = arg_cr;
}
else
cur_arg->type = arg_c;
goto set_params;
set_params:
cur_arg->constant = 0;
set_operand (operand, crx_ins);
}
static void
parse_operands (ins * crx_ins, char *operands)
{
char *operandS;
char *operandH, *operandT;
int allocated = 0;
char *operand[MAX_OPERANDS];
int op_num = 0;
int bracket_flag = 0;
int sq_bracket_flag = 0;
operandS = operandH = operandT = (INST_HAS_REG_LIST) ?
preprocess_reglist (operands, &allocated) : operands;
while (*operandT != '\0')
{
if (*operandT == ',' && bracket_flag != 1 && sq_bracket_flag != 1)
{
*operandT++ = '\0';
operand[op_num++] = strdup (operandH);
operandH = operandT;
continue;
}
if (*operandT == ' ')
as_bad (_("Illegal operands (whitespace): `%s'"), ins_parse);
if (*operandT == '(')
bracket_flag = 1;
else if (*operandT == '[')
sq_bracket_flag = 1;
if (*operandT == ')')
{
if (bracket_flag)
bracket_flag = 0;
else
as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
}
else if (*operandT == ']')
{
if (sq_bracket_flag)
sq_bracket_flag = 0;
else
as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
}
if (bracket_flag == 1 && *operandT == ')')
bracket_flag = 0;
else if (sq_bracket_flag == 1 && *operandT == ']')
sq_bracket_flag = 0;
operandT++;
}
operand[op_num++] = strdup (operandH);
crx_ins->nargs = op_num;
if (bracket_flag || sq_bracket_flag)
as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
for (op_num = 0; op_num < crx_ins->nargs; op_num++)
{
cur_arg_num = op_num;
parse_operand (operand[op_num], crx_ins);
free (operand[op_num]);
}
if (allocated)
free (operandS);
}
static int
gettrap (char *s)
{
const trap_entry *trap;
for (trap = crx_traps; trap < (crx_traps + NUMTRAPS); trap++)
if (strcasecmp (trap->name, s) == 0)
return trap->entry;
as_bad (_("Unknown exception: `%s'"), s);
return 0;
}
static void
handle_LoadStor (char *operands)
{
if (strstr (operands, ")+") != NULL)
{
while (! IS_INSN_TYPE (LD_STOR_INS_INC))
instruction++;
return;
}
if (strstr (operands, "$") != NULL)
while (! IS_INSN_TYPE (STOR_IMM_INS))
instruction++;
}
static void
parse_insn (ins *insn, char *operands)
{
int i;
for (i = 0; no_op_insn[i] != NULL; i++)
{
if (streq (no_op_insn[i], instruction->mnemonic))
{
insn->nargs = 0;
return;
}
}
if (IS_INSN_MNEMONIC ("excp") || IS_INSN_MNEMONIC ("cinv"))
{
insn->nargs = 1;
insn->arg[0].type = arg_ic;
insn->arg[0].constant = IS_INSN_MNEMONIC ("excp") ?
gettrap (operands) : get_cinv_parameters (operands);
insn->arg[0].X_op = O_constant;
return;
}
if (IS_INSN_TYPE (LD_STOR_INS))
handle_LoadStor (operands);
if (operands != NULL)
parse_operands (insn, operands);
}
static int
get_cinv_parameters (char * operand)
{
char *p = operand;
int d_used = 0, i_used = 0, u_used = 0, b_used = 0;
while (*++p != ']')
{
if (*p == ',' || *p == ' ')
continue;
if (*p == 'd')
d_used = 1;
else if (*p == 'i')
i_used = 1;
else if (*p == 'u')
u_used = 1;
else if (*p == 'b')
b_used = 1;
else
as_bad (_("Illegal `cinv' parameter: `%c'"), *p);
}
return ((b_used ? 8 : 0)
+ (d_used ? 4 : 0)
+ (i_used ? 2 : 0)
+ (u_used ? 1 : 0));
}
static int
getreg_image (reg r)
{
const reg_entry *reg;
char *reg_name;
int is_procreg = 0;
if (((IS_INSN_MNEMONIC ("mtpr")) && (cur_arg_num == 1))
|| ((IS_INSN_MNEMONIC ("mfpr")) && (cur_arg_num == 0)) )
is_procreg = 1;
if (r < MAX_REG)
reg = &crx_regtab[r];
else if (r < MAX_COPREG)
reg = &crx_copregtab[r-MAX_REG];
else
{
as_bad (_("Unknown register: `%d'"), r);
return 0;
}
reg_name = reg->name;
#define IMAGE_ERR \
as_bad (_("Illegal register (`%s') in Instruction: `%s'"), \
reg_name, ins_parse); \
break;
switch (reg->type)
{
case CRX_U_REGTYPE:
if (is_procreg || (instruction->flags & USER_REG))
return reg->image;
else
IMAGE_ERR;
case CRX_CFG_REGTYPE:
if (is_procreg)
return reg->image;
else
IMAGE_ERR;
case CRX_R_REGTYPE:
if (! is_procreg)
return reg->image;
else
IMAGE_ERR;
case CRX_C_REGTYPE:
case CRX_CS_REGTYPE:
return reg->image;
break;
default:
IMAGE_ERR;
}
return 0;
}
static long
getconstant (long x, int nbits)
{
return (x & ((((1 << (nbits - 1)) - 1) << 1) | 1));
}
static void
print_constant (int nbits, int shift, argument *arg)
{
unsigned long mask = 0;
long constant = getconstant (arg->constant, nbits);
switch (nbits)
{
case 32:
case 28:
case 24:
case 22:
mask = (1 << (nbits - 16)) - 1;
CRX_PRINT (0, (constant >> WORD_SHIFT) & mask, 0);
CRX_PRINT (1, (constant & 0xFFFF), WORD_SHIFT);
break;
case 16:
case 12:
if (arg->type == arg_cr)
{
CRX_PRINT (0, constant, 0);
break;
}
if ((instruction->size > 2) && (shift == WORD_SHIFT))
CRX_PRINT (1, constant, WORD_SHIFT);
else
CRX_PRINT (0, constant, shift);
break;
default:
CRX_PRINT (0, constant, shift);
break;
}
}
static void
print_operand (int nbits, int shift, argument *arg)
{
switch (arg->type)
{
case arg_r:
CRX_PRINT (0, getreg_image (arg->r), shift);
break;
case arg_copr:
if (arg->cr < c0 || arg->cr > c15)
as_bad (_("Illegal Co-processor register in Instruction `%s' "),
ins_parse);
CRX_PRINT (0, getreg_image (arg->cr), shift);
break;
case arg_copsr:
if (arg->cr < cs0 || arg->cr > cs15)
as_bad (_("Illegal Co-processor special register in Instruction `%s' "),
ins_parse);
CRX_PRINT (0, getreg_image (arg->cr), shift);
break;
case arg_idxr:
CRX_PRINT (0, getreg_image (arg->r), 12);
CRX_PRINT (0, getreg_image (arg->i_r), 8);
CRX_PRINT (0, arg->scale, 6);
case arg_ic:
case arg_c:
print_constant (nbits, shift, arg);
break;
case arg_rbase:
CRX_PRINT (0, getreg_image (arg->r), shift);
break;
case arg_cr:
if (instruction->flags & DISPU4MAP)
print_constant (nbits, shift + REG_SIZE, arg);
else
print_constant (nbits, shift, arg);
CRX_PRINT (0, getreg_image (arg->r), shift);
break;
default:
break;
}
}
static int
get_number_of_operands (void)
{
int i;
for (i = 0; instruction->operands[i].op_type && i < MAX_OPERANDS; i++)
;
return i;
}
static op_err
check_range (long *num, int bits, int unsigned flags, int update)
{
long min, max;
int retval = OP_LEGAL;
int bin;
long upper_64kb = 0xFFFF0000;
long value = *num;
if (flags & OP_EVEN)
{
if (value % 2)
return OP_NOT_EVEN;
}
if (flags & OP_UPPER_64KB)
{
if ((value & upper_64kb) == upper_64kb)
{
value -= upper_64kb;
if (update)
*num = value;
}
else
return OP_NOT_UPPER_64KB;
}
if (flags & OP_SHIFT)
{
value >>= 1;
if (update)
*num = value;
}
else if (flags & OP_SHIFT_DEC)
{
value = (value >> 1) - 1;
if (update)
*num = value;
}
if (flags & OP_ESC)
{
if (value == 0x7e || value == 0x7f)
return OP_OUT_OF_RANGE;
}
if (flags & OP_DISPU4)
{
int is_dispu4 = 0;
int mul = (instruction->flags & DISPUB4) ? 1
: (instruction->flags & DISPUW4) ? 2
: (instruction->flags & DISPUD4) ? 4 : 0;
for (bin = 0; bin < cst4_maps; bin++)
{
if (value == (mul * bin))
{
is_dispu4 = 1;
if (update)
*num = bin;
break;
}
}
if (!is_dispu4)
retval = OP_ILLEGAL_DISPU4;
}
else if (flags & OP_CST4)
{
int is_cst4 = 0;
for (bin = 0; bin < cst4_maps; bin++)
{
if (value == cst4_map[bin])
{
is_cst4 = 1;
if (update)
*num = bin;
break;
}
}
if (!is_cst4)
retval = OP_ILLEGAL_CST4;
}
else if (flags & OP_SIGNED)
{
max = (1 << (bits - 1)) - 1;
min = - (1 << (bits - 1));
if ((value > max) || (value < min))
retval = OP_OUT_OF_RANGE;
}
else if (flags & OP_UNSIGNED)
{
max = ((((1 << (bits - 1)) - 1) << 1) | 1);
min = 0;
if (((unsigned long) value > (unsigned long) max)
|| ((unsigned long) value < (unsigned long) min))
retval = OP_OUT_OF_RANGE;
}
return retval;
}
static int
assemble_insn (char *mnemonic, ins *insn)
{
argtype cur_type[MAX_OPERANDS];
unsigned int cur_size[MAX_OPERANDS];
unsigned int cur_flags[MAX_OPERANDS];
unsigned int ins_type;
int match = 0;
int i;
int found_same_number_of_operands = 0;
int found_same_argument_types = 0;
int found_const_within_range = 0;
int invalid_optype = -1;
int invalid_const = -1;
op_err op_error, const_err = OP_LEGAL;
#define GET_CURRENT_DATA(FUNC, ARRAY) \
for (i = 0; i < insn->nargs; i++) \
ARRAY[i] = FUNC (instruction->operands[i].op_type)
#define GET_CURRENT_TYPE GET_CURRENT_DATA(get_optype, cur_type)
#define GET_CURRENT_SIZE GET_CURRENT_DATA(get_opbits, cur_size)
#define GET_CURRENT_FLAGS GET_CURRENT_DATA(get_opflags, cur_flags)
if (insn->nargs == 0)
{
output_opcode[0] = BIN (instruction->match, instruction->match_bits);
return 1;
}
ins_type = CRX_INS_TYPE(instruction->flags);
while (
match != 1
&& instruction->mnemonic != NULL
&& IS_INSN_MNEMONIC (mnemonic)
&& IS_INSN_TYPE(ins_type))
{
if (get_number_of_operands () != insn->nargs)
goto next_insn;
found_same_number_of_operands = 1;
GET_CURRENT_TYPE;
GET_CURRENT_SIZE;
GET_CURRENT_FLAGS;
for (i = 0; i < insn->nargs; i++)
{
if (cur_type[i] != insn->arg[i].type)
{
if (invalid_optype == -1)
invalid_optype = i + 1;
goto next_insn;
}
}
found_same_argument_types = 1;
for (i = 0; i < insn->nargs; i++)
{
int j = instruction->flags & REVERSE_MATCH ?
i == 0 ? 1 :
i == 1 ? 0 : i :
i;
if ((insn->arg[j].X_op == O_constant)
&& (op_error = check_range (&insn->arg[j].constant, cur_size[j],
cur_flags[j], 0)))
{
if (invalid_const == -1)
{
invalid_const = j + 1;
const_err = op_error;
}
goto next_insn;
}
else if ((insn->arg[j].X_op == O_symbol)
&& ((bfd_reloc_type_lookup (stdoutput, insn->rtype))->bitsize
> cur_size[j]))
goto next_insn;
}
found_const_within_range = 1;
match = 1;
break;
next_insn:
instruction++;
}
if (!match)
{
if (!found_same_number_of_operands)
as_bad (_("Incorrect number of operands"));
else if (!found_same_argument_types)
as_bad (_("Illegal type of operand (arg %d)"), invalid_optype);
else if (!found_const_within_range)
{
switch (const_err)
{
case OP_OUT_OF_RANGE:
as_bad (_("Operand out of range (arg %d)"), invalid_const);
break;
case OP_NOT_EVEN:
as_bad (_("Operand has odd displacement (arg %d)"), invalid_const);
break;
case OP_ILLEGAL_DISPU4:
as_bad (_("Invalid DISPU4 operand value (arg %d)"), invalid_const);
break;
case OP_ILLEGAL_CST4:
as_bad (_("Invalid CST4 operand value (arg %d)"), invalid_const);
break;
case OP_NOT_UPPER_64KB:
as_bad (_("Operand value is not within upper 64 KB (arg %d)"),
invalid_const);
break;
default:
as_bad (_("Illegal operand (arg %d)"), invalid_const);
break;
}
}
return 0;
}
else
{
warn_if_needed (insn);
if (adjust_if_needed (insn))
GET_CURRENT_SIZE;
for (i = 0; i < insn->nargs; i++)
{
int j = instruction->flags & REVERSE_MATCH ?
i == 0 ? 1 :
i == 1 ? 0 : i :
i;
if ((insn->arg[j].X_op == O_constant)
&& (check_range (&insn->arg[j].constant, cur_size[j],
cur_flags[j], 1) != OP_LEGAL))
as_fatal (_("Illegal operand (arg %d)"), j+1);
}
output_opcode[0] = BIN (instruction->match, instruction->match_bits);
for (i = 0; i < insn->nargs; i++)
{
cur_arg_num = i;
print_operand (cur_size[i], instruction->operands[i].shift,
&insn->arg[i]);
}
}
return 1;
}
void
warn_if_needed (ins *insn)
{
if (IS_INSN_TYPE (LD_STOR_INS_INC))
{
if ((insn->arg[0].type == arg_r) || (insn->arg[1].type == arg_r))
if (insn->arg[0].r == insn->arg[1].r)
as_bad (_("Same src/dest register is used (`r%d'), result is undefined"),
insn->arg[0].r);
}
if (instruction->flags & NO_SP)
{
if (getreg_image (insn->arg[0].r) == getreg_image (sp))
as_bad (_("`%s' has undefined result"), ins_parse);
}
if (instruction->flags & NO_RPTR)
{
if ((1 << getreg_image (insn->arg[0].r)) & insn->arg[1].constant)
as_bad (_("Same src/dest register is used (`r%d'), result is undefined"),
getreg_image (insn->arg[0].r));
}
}
int
adjust_if_needed (ins *insn)
{
int ret_value = 0;
if (IS_INSN_MNEMONIC ("addub"))
{
if ((instruction->operands[0].op_type == cst4)
&& instruction->operands[1].op_type == regr)
{
if (insn->arg[0].constant == 0 && insn->arg[1].r == r0)
{
instruction++;
ret_value = 1;
}
}
}
if (IS_INSN_TYPE (CSTBIT_INS))
{
if ((instruction->operands[1].op_type == rbase_disps12)
&& (insn->arg[1].X_op == O_constant)
&& (insn->arg[1].constant == 0))
{
instruction--;
ret_value = 1;
}
}
return ret_value;
}
static void
mask_reg (int r, unsigned short int *mask)
{
if ((reg)r > (reg)sp)
{
as_bad (_("Invalid Register in Register List"));
return;
}
*mask |= (1 << r);
}
static char *
preprocess_reglist (char *param, int *allocated)
{
char reg_name[MAX_REGNAME_LEN];
char *regP;
int reg_counter = 0;
unsigned short int mask = 0;
char *new_param;
char *paramP = param;
char maskstring[10];
int hi_found = 0, lo_found = 0;
reg r;
copreg cr;
if (strchr (paramP, '{') == NULL)
return param;
if (strchr (paramP, '}') == NULL)
as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
while (*paramP++ != '{');
new_param = (char *)xcalloc (MAX_INST_LEN, sizeof (char));
*allocated = 1;
strncpy (new_param, param, paramP - param - 1);
while (*paramP != '}')
{
regP = paramP;
memset (®_name, '\0', sizeof (reg_name));
while (ISALNUM (*paramP))
paramP++;
strncpy (reg_name, regP, paramP - regP);
if (IS_INSN_TYPE (COP_REG_INS))
{
if (((cr = get_copregister (reg_name)) == nullcopregister)
|| (crx_copregtab[cr-MAX_REG].type != CRX_C_REGTYPE))
as_fatal (_("Illegal register `%s' in cop-register list"), reg_name);
mask_reg (getreg_image (cr - c0), &mask);
}
else if (IS_INSN_TYPE (COPS_REG_INS))
{
if (((cr = get_copregister (reg_name)) == nullcopregister)
|| (crx_copregtab[cr-MAX_REG].type != CRX_CS_REGTYPE))
as_fatal (_("Illegal register `%s' in cop-special-register list"),
reg_name);
mask_reg (getreg_image (cr - cs0), &mask);
}
else if (instruction->flags & USER_REG)
{
if (streq(reg_name, "uhi"))
{
hi_found = 1;
goto next_inst;
}
else if (streq(reg_name, "ulo"))
{
lo_found = 1;
goto next_inst;
}
else if (((r = get_register (reg_name)) == nullregister)
|| (crx_regtab[r].type != CRX_U_REGTYPE))
as_fatal (_("Illegal register `%s' in user register list"), reg_name);
mask_reg (getreg_image (r - u0), &mask);
}
else
{
if (streq(reg_name, "hi"))
{
hi_found = 1;
goto next_inst;
}
else if (streq(reg_name, "lo"))
{
lo_found = 1;
goto next_inst;
}
else if (((r = get_register (reg_name)) == nullregister)
|| (crx_regtab[r].type != CRX_R_REGTYPE))
as_fatal (_("Illegal register `%s' in register list"), reg_name);
mask_reg (getreg_image (r - r0), &mask);
}
if (++reg_counter > MAX_REGS_IN_MASK16)
as_bad (_("Maximum %d bits may be set in `mask16' operand"),
MAX_REGS_IN_MASK16);
next_inst:
while (!ISALNUM (*paramP) && *paramP != '}')
paramP++;
}
if (*++paramP != '\0')
as_warn (_("rest of line ignored; first ignored character is `%c'"),
*paramP);
switch (hi_found + lo_found)
{
case 0:
if (mask == 0)
as_bad (_("Illegal `mask16' operand, operation is undefined - `%s'"),
ins_parse);
break;
case 1:
as_bad (_("HI/LO registers should be specified together"));
break;
case 2:
if (mask != 0)
as_bad (_("HI/LO registers should be specified without additional registers"));
default:
break;
}
sprintf (maskstring, "$0x%x", mask);
strcat (new_param, maskstring);
return new_param;
}
void
print_insn (ins *insn)
{
unsigned int i, j, insn_size;
char *this_frag;
unsigned short words[4];
int addr_mod;
for (i = 0, j = 0; i < 2; i++)
{
words[j++] = (output_opcode[i] >> 16) & 0xFFFF;
words[j++] = output_opcode[i] & 0xFFFF;
}
if ((instruction->flags & RELAXABLE) && relocatable)
{
int relax_subtype;
insn_size = INSN_MAX_SIZE;
if (IS_INSN_TYPE (BRANCH_INS))
relax_subtype = 0;
else if (IS_INSN_TYPE (DCR_BRANCH_INS) || IS_INSN_MNEMONIC ("bal"))
relax_subtype = 3;
else if (IS_INSN_TYPE (CMPBR_INS) || IS_INSN_TYPE (COP_BRANCH_INS))
relax_subtype = 5;
else
abort ();
this_frag = frag_var (rs_machine_dependent, insn_size * 2,
4, relax_subtype,
insn->exp.X_add_symbol,
insn->exp.X_add_number,
0);
}
else
{
insn_size = instruction->size;
this_frag = frag_more (insn_size * 2);
if ((relocatable) && (insn->rtype != BFD_RELOC_NONE))
{
reloc_howto_type *reloc_howto;
int size;
reloc_howto = bfd_reloc_type_lookup (stdoutput, insn->rtype);
if (!reloc_howto)
abort ();
size = bfd_get_reloc_size (reloc_howto);
if (size < 1 || size > 4)
abort ();
fix_new_exp (frag_now, this_frag - frag_now->fr_literal,
size, &insn->exp, reloc_howto->pc_relative,
insn->rtype);
}
}
addr_mod = frag_now_fix () & 1;
if (frag_now->has_code && frag_now->insn_addr != addr_mod)
as_bad (_("instruction address is not a multiple of 2"));
frag_now->insn_addr = addr_mod;
frag_now->has_code = 1;
for (i = 0; i < insn_size; i++)
{
md_number_to_chars (this_frag, (valueT) words[i], 2);
this_frag += 2;
}
}
void
md_assemble (char *op)
{
ins crx_ins;
char *param;
char c;
reset_vars (op);
for (param = op; *param != 0 && !ISSPACE (*param); param++)
;
c = *param;
*param++ = '\0';
instruction = (const inst *) hash_find (crx_inst_hash, op);
if (instruction == NULL)
{
as_bad (_("Unknown opcode: `%s'"), op);
return;
}
dwarf2_emit_insn (0);
parse_insn (&crx_ins, param);
if (assemble_insn (op, &crx_ins) == 0)
return;
print_insn (&crx_ins);
}