summaryrefslogtreecommitdiff
path: root/gas/config/tc-m68hc11.c
diff options
context:
space:
mode:
authorStephane Carrez <stcarrez@nerim.fr>2002-12-01 11:02:10 +0000
committerStephane Carrez <stcarrez@nerim.fr>2002-12-01 11:02:10 +0000
commit895d65875ec6813208ddaa7edc72b413176f83a0 (patch)
tree56ff2b538cb02f4c3b19e6651bcf9467485611d2 /gas/config/tc-m68hc11.c
parent7896e751a04074e543387dc2ece970fa592be906 (diff)
downloadbinutils-redhat-895d65875ec6813208ddaa7edc72b413176f83a0.tar.gz
Fix Bug savannah/1825:
* config/tc-m68hc11.h (md_relax_frag): Define to support relaxations that are not pc-relative. (m68hc11_relax_frag): Declare. * config/tc-m68hc11.c (build_indexed_byte): Use a frag_var to handle the offsetable indexed addressing modes (n,r). (build_insn): Cleanup some locals. (m68hc11_relax_frag): New function imported from tc-cris.c to handle relaxation of difference between two symbols of same section. (md_convert_frag): For INDEXED_OFFSET relaxs, use the displacement only when this is a PC-relative operand and the offset is not absolute. (md_estimate_size_before_relax): Convert the INDEXED_OFFSET,UNDEF frag to INDEXED_OFFSET,STATE_BITS5 when the symbol is absolute; this will be handled by m68hc11_relax_frag.
Diffstat (limited to 'gas/config/tc-m68hc11.c')
-rw-r--r--gas/config/tc-m68hc11.c162
1 files changed, 136 insertions, 26 deletions
diff --git a/gas/config/tc-m68hc11.c b/gas/config/tc-m68hc11.c
index 34dbf6b41f..2586902ff1 100644
--- a/gas/config/tc-m68hc11.c
+++ b/gas/config/tc-m68hc11.c
@@ -1933,14 +1933,21 @@ build_indexed_byte (op, format, move_insn)
}
else if (op->reg1 != REG_PC)
{
- byte = (byte << 3) | 0xe2;
+ symbolS *sym;
+ offsetT off;
+
f = frag_more (1);
number_to_chars_bigendian (f, byte, 1);
-
- f = frag_more (2);
- fix_new_exp (frag_now, f - frag_now->fr_literal, 2,
- &op->exp, FALSE, BFD_RELOC_16);
- number_to_chars_bigendian (f, 0, 2);
+ sym = op->exp.X_add_symbol;
+ off = op->exp.X_add_number;
+ if (op->exp.X_op != O_symbol)
+ {
+ sym = make_expr_symbol (&op->exp);
+ off = 0;
+ }
+ frag_var (rs_machine_dependent, 2, 2,
+ ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_UNDF),
+ sym, off, f);
}
else
{
@@ -2060,17 +2067,12 @@ build_insn (opcode, operands, nb_operands)
char *f;
long format;
int move_insn = 0;
- fragS *frag;
- int where;
/* Put the page code instruction if there is one. */
format = opcode->format;
- frag = frag_now;
- where = frag_now_fix ();
-
if (format & M6811_OP_BRANCH)
- fix_new (frag, where, 1,
+ fix_new (frag_now, frag_now_fix (), 1,
&abs_symbol, 0, 1, BFD_RELOC_M68HC11_RL_JUMP);
if (format & OP_EXTENDED)
@@ -2732,6 +2734,97 @@ tc_gen_reloc (section, fixp)
return reloc;
}
+/* We need a port-specific relaxation function to cope with sym2 - sym1
+ relative expressions with both symbols in the same segment (but not
+ necessarily in the same frag as this insn), for example:
+ ldab sym2-(sym1-2),pc
+ sym1:
+ The offset can be 5, 9 or 16 bits long. */
+
+long
+m68hc11_relax_frag (seg, fragP, stretch)
+ segT seg ATTRIBUTE_UNUSED;
+ fragS *fragP;
+ long stretch ATTRIBUTE_UNUSED;
+{
+ long growth;
+ offsetT aim = 0;
+ symbolS *symbolP;
+ const relax_typeS *this_type;
+ const relax_typeS *start_type;
+ relax_substateT next_state;
+ relax_substateT this_state;
+ const relax_typeS *table = TC_GENERIC_RELAX_TABLE;
+
+ /* We only have to cope with frags as prepared by
+ md_estimate_size_before_relax. The STATE_BITS16 case may geet here
+ because of the different reasons that it's not relaxable. */
+ switch (fragP->fr_subtype)
+ {
+ case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS16):
+ /* When we get to this state, the frag won't grow any more. */
+ return 0;
+
+ case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS5):
+ case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS9):
+ if (fragP->fr_symbol == NULL
+ || S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
+ as_fatal (_("internal inconsistency problem in %s: fr_symbol %lx"),
+ __FUNCTION__, (long) fragP->fr_symbol);
+ symbolP = fragP->fr_symbol;
+ if (symbol_resolved_p (symbolP))
+ as_fatal (_("internal inconsistency problem in %s: resolved symbol"),
+ __FUNCTION__);
+ aim = S_GET_VALUE (symbolP);
+ break;
+
+ default:
+ as_fatal (_("internal inconsistency problem in %s: fr_subtype %d"),
+ __FUNCTION__, fragP->fr_subtype);
+ }
+
+ /* The rest is stolen from relax_frag. There's no obvious way to
+ share the code, but fortunately no requirement to keep in sync as
+ long as fragP->fr_symbol does not have its segment changed. */
+
+ this_state = fragP->fr_subtype;
+ start_type = this_type = table + this_state;
+
+ if (aim < 0)
+ {
+ /* Look backwards. */
+ for (next_state = this_type->rlx_more; next_state;)
+ if (aim >= this_type->rlx_backward)
+ next_state = 0;
+ else
+ {
+ /* Grow to next state. */
+ this_state = next_state;
+ this_type = table + this_state;
+ next_state = this_type->rlx_more;
+ }
+ }
+ else
+ {
+ /* Look forwards. */
+ for (next_state = this_type->rlx_more; next_state;)
+ if (aim <= this_type->rlx_forward)
+ next_state = 0;
+ else
+ {
+ /* Grow to next state. */
+ this_state = next_state;
+ this_type = table + this_state;
+ next_state = this_type->rlx_more;
+ }
+ }
+
+ growth = this_type->rlx_length - start_type->rlx_length;
+ if (growth != 0)
+ fragP->fr_subtype = this_state;
+ return growth;
+}
+
void
md_convert_frag (abfd, sec, fragP)
bfd *abfd ATTRIBUTE_UNUSED;
@@ -2799,25 +2892,32 @@ md_convert_frag (abfd, sec, fragP)
break;
case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS5):
+ if (fragP->fr_opcode[0] == 3
+ && fragP->fr_symbol != 0
+ && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
+ value = disp;
fragP->fr_opcode[0] = fragP->fr_opcode[0] << 6;
- if ((fragP->fr_opcode[0] & 0x0ff) == 0x0c0)
- fragP->fr_opcode[0] |= disp & 0x1f;
- else
- fragP->fr_opcode[0] |= value & 0x1f;
+ fragP->fr_opcode[0] |= value & 0x1f;
break;
case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS9):
+ if (fragP->fr_opcode[0] == 3
+ && fragP->fr_symbol != 0
+ && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
+ value = disp;
fragP->fr_opcode[0] = (fragP->fr_opcode[0] << 3);
fragP->fr_opcode[0] |= 0xE0;
- fix_new (fragP, fragP->fr_fix, 1,
- fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_8);
+ fragP->fr_opcode[0] |= (value >> 8) & 1;
+ fragP->fr_opcode[1] = value;
fragP->fr_fix += 1;
break;
case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS16):
fragP->fr_opcode[0] = (fragP->fr_opcode[0] << 3);
fragP->fr_opcode[0] |= 0xe2;
- if ((fragP->fr_opcode[0] & 0x0ff) == 0x0fa)
+ if ((fragP->fr_opcode[0] & 0x0ff) == 0x0fa
+ && fragP->fr_symbol != 0
+ && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
{
fixp = fix_new (fragP, fragP->fr_fix, 2,
fragP->fr_symbol, fragP->fr_offset,
@@ -2927,13 +3027,23 @@ md_estimate_size_before_relax (fragP, segment)
case STATE_INDEXED_OFFSET:
assert (current_architecture & cpu6812);
- /* Switch the indexed operation to 16-bit mode. */
- fragP->fr_opcode[0] = fragP->fr_opcode[0] << 3;
- fragP->fr_opcode[0] |= 0xe2;
- fragP->fr_fix++;
- fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
- fragP->fr_offset, 0, BFD_RELOC_16);
- fragP->fr_fix++;
+ if (fragP->fr_symbol
+ && S_GET_SEGMENT (fragP->fr_symbol) == absolute_section)
+ {
+ fragP->fr_subtype = ENCODE_RELAX (STATE_INDEXED_OFFSET,
+ STATE_BITS5);
+ /* Return the size of the variable part of the frag. */
+ return md_relax_table[fragP->fr_subtype].rlx_length;
+ }
+ else
+ {
+ /* Switch the indexed operation to 16-bit mode. */
+ fragP->fr_opcode[0] = fragP->fr_opcode[0] << 3;
+ fragP->fr_opcode[0] |= 0xe2;
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, 0, BFD_RELOC_16);
+ fragP->fr_fix += 2;
+ }
break;
case STATE_XBCC_BRANCH: