Thu Jul 25 12:03:33 1996 Martin M. Hunt <hunt@pizza.cygnus.com>

* tc-d10v.c: Fix packaging bug. Added range checking.
	Added kludge for divs instruction.  Fixed minor problem with
	multiple text sections.
	* tc-d10v.h (d10v_cleanup): Change prototype.
This commit is contained in:
Martin Hunt 1996-07-25 19:15:14 +00:00
parent 7c09665202
commit ab48956f99
1 changed files with 149 additions and 54 deletions

View File

@ -58,7 +58,7 @@ static int register_name PARAMS ((expressionS *expressionP));
static int postfix PARAMS ((char *p)); static int postfix PARAMS ((char *p));
static bfd_reloc_code_real_type get_reloc PARAMS ((struct d10v_operand *op)); static bfd_reloc_code_real_type get_reloc PARAMS ((struct d10v_operand *op));
static int get_operands PARAMS ((expressionS exp[])); static int get_operands PARAMS ((expressionS exp[]));
static unsigned long build_insn PARAMS ((struct d10v_opcode *opcode, expressionS *opers)); static unsigned long build_insn PARAMS ((struct d10v_opcode *opcode, expressionS *opers, unsigned long insn));
static void write_long PARAMS ((struct d10v_opcode *opcode, unsigned long insn, Fixups *fx)); static void write_long PARAMS ((struct d10v_opcode *opcode, unsigned long insn, Fixups *fx));
static void write_1_short PARAMS ((struct d10v_opcode *opcode, unsigned long insn, Fixups *fx)); static void write_1_short PARAMS ((struct d10v_opcode *opcode, unsigned long insn, Fixups *fx));
static int write_2_short PARAMS ((struct d10v_opcode *opcode1, unsigned long insn1, static int write_2_short PARAMS ((struct d10v_opcode *opcode1, unsigned long insn1,
@ -144,6 +144,35 @@ register_name (expressionP)
return 0; return 0;
} }
static int
check_range (num, bits, sign)
unsigned long num;
int bits;
int sign;
{
long min, max;
int retval=0;
if (sign)
{
max = (1 << (bits - 1)) - 1;
min = - (1 << (bits - 1));
if (((long)num > max) || ((long)num < min))
retval = 1;
}
else
{
max = (1 << bits) - 1;
min = 0;
if ((num > max) || (num < min))
retval = 1;
}
return retval;
}
void void
md_show_usage (stream) md_show_usage (stream)
FILE *stream; FILE *stream;
@ -372,6 +401,10 @@ d10v_insert_operand (insn, op_type, value, left)
/* truncate to the proper number of bits */ /* truncate to the proper number of bits */
/* FIXME: overflow checking here? */ /* FIXME: overflow checking here? */
if (check_range (value, bits, d10v_operands[op_type].flags & OPERAND_SIGNED))
as_bad("operand out of range: %d",value);
value &= 0x7FFFFFFF >> (31 - bits); value &= 0x7FFFFFFF >> (31 - bits);
insn |= (value << shift); insn |= (value << shift);
@ -383,15 +416,23 @@ d10v_insert_operand (insn, op_type, value, left)
and the array of operand expressions and returns the instruction */ and the array of operand expressions and returns the instruction */
static unsigned long static unsigned long
build_insn (opcode, opers) build_insn (opcode, opers, insn)
struct d10v_opcode *opcode; struct d10v_opcode *opcode;
expressionS *opers; expressionS *opers;
unsigned long insn;
{ {
int i, bits, shift, flags; int i, bits, shift, flags, format;
unsigned long insn;
unsigned int number; unsigned int number;
insn = opcode->opcode;
/* the insn argument is only used for the DIVS kludge */
if (insn)
format = LONG_R;
else
{
insn = opcode->opcode;
format = opcode->format;
}
for (i=0;opcode->operands[i];i++) for (i=0;opcode->operands[i];i++)
{ {
flags = d10v_operands[opcode->operands[i]].flags; flags = d10v_operands[opcode->operands[i]].flags;
@ -402,7 +443,7 @@ build_insn (opcode, opers)
if (flags & OPERAND_REG) if (flags & OPERAND_REG)
{ {
number &= REGISTER_MASK; number &= REGISTER_MASK;
if (opcode->format == LONG_L) if (format == LONG_L)
shift += 15; shift += 15;
} }
@ -427,10 +468,18 @@ build_insn (opcode, opers)
} }
/* truncate to the proper number of bits */ /* truncate to the proper number of bits */
/* FIXME: overflow checking here? */ if ((opers[i].X_op == O_constant) && check_range (number, bits, flags & OPERAND_SIGNED))
as_bad("operand out of range: %d",number);
number &= 0x7FFFFFFF >> (31 - bits); number &= 0x7FFFFFFF >> (31 - bits);
insn = insn | (number << shift); insn = insn | (number << shift);
} }
/* kludge: for DIVS, we need to put the operands in twice */
/* on the second pass, format is changed to LONG_R to force */
/* the second set of operands to not be shifted over 15 */
if ((opcode->opcode == OPCODE_DIVS) && (format==LONG_L))
insn = build_insn (opcode, opers, insn);
return insn; return insn;
} }
@ -480,7 +529,14 @@ write_1_short (opcode, insn, fx)
char *f = frag_more(4); char *f = frag_more(4);
int i; int i;
insn |= FM00 | (NOP << 15); /* the other container needs to be NOP */
/* according to 4.3.1: for FM=00, sub-instructions performed only
by IU cannot be encoded in L-container. */
if (opcode->unit == IU)
insn |= FM00 | (NOP << 15); /* right container */
else
insn = FM00 | (insn << 15) | NOP; /* left container */
/* printf("INSN: %08x\n",insn); */ /* printf("INSN: %08x\n",insn); */
number_to_chars_bigendian (f, insn, 4); number_to_chars_bigendian (f, insn, 4);
for (i=0; i < fx->fc; i++) for (i=0; i < fx->fc; i++)
@ -720,7 +776,7 @@ do_assemble (str, opcode)
*opcode = (struct d10v_opcode *)hash_find (d10v_hash, name); *opcode = (struct d10v_opcode *)hash_find (d10v_hash, name);
if (*opcode == NULL) if (*opcode == NULL)
{ {
as_bad ("unknown opcode"); as_fatal ("unknown opcode: %s",name);
return; return;
} }
@ -730,56 +786,94 @@ do_assemble (str, opcode)
/* get all the operands and save them as expressions */ /* get all the operands and save them as expressions */
numops = get_operands (myops); numops = get_operands (myops);
/* now search the opcode table table for one with operands */ /* now see if the operand is a fake. If so, find the correct size */
/* that match what we've got */ /* instruction, if possible */
do match = 0;
if ((*opcode)->format == OPCODE_FAKE)
{ {
match = 1; int opnum = (*opcode)->operands[0];
for (i = 0; (*opcode)->operands[i]; i++) if (myops[opnum].X_op == O_constant)
{ {
int flags = d10v_operands[(*opcode)->operands[i]].flags; next_opcode=(*opcode)+1;
for (i=0; (*opcode)->operands[i+1]; i++)
if (myops[i].X_op==0)
{ {
match=0; int bits = d10v_operands[next_opcode->operands[opnum]].bits;
break; int flags = d10v_operands[next_opcode->operands[opnum]].flags;
if (!check_range (myops[opnum].X_add_number, bits, flags & OPERAND_SIGNED))
{
match = 1;
break;
}
next_opcode++;
} }
}
if (flags & OPERAND_REG) else
{
/* not a constant, so use a long instruction */
next_opcode = (*opcode)+2;
match = 1;
}
if (match)
*opcode = next_opcode;
else
as_fatal ("value out of range");
}
else
{
/* now search the opcode table table for one with operands */
/* that match what we've got */
while (!match)
{
match = 1;
for (i = 0; (*opcode)->operands[i]; i++)
{ {
if ((myops[i].X_op != O_register) || int flags = d10v_operands[(*opcode)->operands[i]].flags;
((flags & OPERAND_ACC) != (myops[i].X_add_number & OPERAND_ACC)) || int X_op = myops[i].X_op;
((flags & OPERAND_FLAG) != (myops[i].X_add_number & OPERAND_FLAG)) || int num = myops[i].X_add_number;
((flags & OPERAND_CONTROL) != (myops[i].X_add_number & OPERAND_CONTROL)))
if (X_op==0)
{ {
match=0; match=0;
break; break;
} }
}
if (flags & OPERAND_REG)
if (((flags & OPERAND_MINUS) && ((myops[i].X_op != O_absent) || (myops[i].X_add_number != OPERAND_MINUS))) || {
((flags & OPERAND_PLUS) && ((myops[i].X_op != O_absent) || (myops[i].X_add_number != OPERAND_PLUS))) || if ((X_op != O_register) ||
((flags & OPERAND_ATMINUS) && ((myops[i].X_op != O_absent) || (myops[i].X_add_number != OPERAND_ATMINUS))) || ((flags & OPERAND_ACC) != (num & OPERAND_ACC)) ||
((flags & OPERAND_ATPAR) && ((myops[i].X_op != O_absent) || (myops[i].X_add_number != OPERAND_ATPAR))) || ((flags & OPERAND_FLAG) != (num & OPERAND_FLAG)) ||
((flags & OPERAND_ATSIGN) && ((myops[i].X_op != O_absent) || (myops[i].X_add_number != OPERAND_ATSIGN)))) ((flags & OPERAND_CONTROL) != (num & OPERAND_CONTROL)))
{ {
match=0; match=0;
break; break;
}
}
if (((flags & OPERAND_MINUS) && ((X_op != O_absent) || (num != OPERAND_MINUS))) ||
((flags & OPERAND_PLUS) && ((X_op != O_absent) || (num != OPERAND_PLUS))) ||
((flags & OPERAND_ATMINUS) && ((X_op != O_absent) || (num != OPERAND_ATMINUS))) ||
((flags & OPERAND_ATPAR) && ((X_op != O_absent) || (num != OPERAND_ATPAR))) ||
((flags & OPERAND_ATSIGN) && ((X_op != O_absent) || (num != OPERAND_ATSIGN))))
{
match=0;
break;
}
} }
/* we're only done if the operands matched AND there
are no more to check */
if (match && myops[i].X_op==0)
break;
next_opcode = (*opcode)+1;
if (next_opcode->opcode == 0)
break;
if (strcmp(next_opcode->name, (*opcode)->name))
break;
(*opcode) = next_opcode;
} }
}
/* we're only done if the operands matched AND there
are no more to check */
if (match && myops[i].X_op==0)
break;
next_opcode = (*opcode)+1;
if (next_opcode->opcode == 0)
break;
if (strcmp(next_opcode->name, (*opcode)->name))
break;
(*opcode) = next_opcode;
} while (!match);
if (!match) if (!match)
{ {
@ -812,8 +906,8 @@ do_assemble (str, opcode)
/* at this point, we have "opcode" pointing to the opcode entry in the /* at this point, we have "opcode" pointing to the opcode entry in the
d10v opcode table, with myops filled out with the operands. */ d10v opcode table, with myops filled out with the operands. */
insn = build_insn ((*opcode), myops); insn = build_insn ((*opcode), myops, 0);
/* printf("sub-insn = %lx\n",insn); */ /* printf("sub-insn = %lx\n",insn); */
return (insn); return (insn);
} }
@ -947,12 +1041,13 @@ md_apply_fix3 (fixp, valuep, seg)
instructions to see if it can package them with the next instruction, there may instructions to see if it can package them with the next instruction, there may
be a short instruction that still needs written. */ be a short instruction that still needs written. */
int int
d10v_cleanup() d10v_cleanup (done)
int done;
{ {
segT seg; segT seg;
subsegT subseg; subsegT subseg;
if (prev_opcode) if ( prev_opcode && (done || (now_seg == prev_seg) && (now_subseg == prev_subseg)))
{ {
seg = now_seg; seg = now_seg;
subseg = now_subseg; subseg = now_subseg;