diff options
Diffstat (limited to 'gas/config/tc-m68hc11.c')
-rw-r--r-- | gas/config/tc-m68hc11.c | 162 |
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: |