From 75f580857a7c65525a98a701c65d0814be04155f Mon Sep 17 00:00:00 2001 From: Barney Stratford Date: Mon, 7 Jul 2014 16:15:19 +0100 Subject: [PATCH] Adds support for writing values to AVR system I/O registers. * elf32-avr.c: Handle R_AVR_PORT5 and R_AVR_PORT6. * reloc.c: Add BFD_RELOC_AVR_PORT5 and BFD_RELOC_AVR_PORT6. * bfd-in2.h: Regenerate. * libbfd.h: Regenerate. * avr.h: Add R_AVR_PORT5 and R_AVR_PORT6. * config/tc-avr.c (avr_operand): Permit referring to r26-r31 by name as [xyz][hl]. Permit using a symbol whoes name begins with `r' to refer to a register. Allow arbitrary expressions for the P and p operators. (md_apply_fix): Check the BFD_RELOC_AVR_PORT5 and BFD_RELOC_AVR_PORT6 relocations. --- bfd/ChangeLog | 7 +++ bfd/bfd-in2.h | 8 ++++ bfd/elf32-avr.c | 53 +++++++++++++++++++++- bfd/libbfd.h | 2 + bfd/reloc.c | 10 +++++ gas/ChangeLog | 9 ++++ gas/config/tc-avr.c | 119 +++++++++++++++++++++++++++++--------------------- include/elf/ChangeLog | 4 ++ include/elf/avr.h | 2 + 9 files changed, 162 insertions(+), 52 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 014944f..f6bc88b 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,10 @@ +2014-07-07 Barney Stratford + + * elf32-avr.c: Handle R_AVR_PORT5 and R_AVR_PORT6. + * reloc.c: Add BFD_RELOC_AVR_PORT5 and BFD_RELOC_AVR_PORT6. + * bfd-in2.h: Regenerate. + * libbfd.h: Regenerate. + 2014-07-04 Alan Modra * Makefile.am: Update "configure.in" comments. diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index 9efc368..ec19492 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -4480,6 +4480,14 @@ value. */ lds and sts instructions supported only tiny core. */ BFD_RELOC_AVR_LDS_STS_16, +/* This is a 6 bit reloc for the AVR that stores an I/O register +number for the IN and OUT instructions */ + BFD_RELOC_AVR_PORT6, + +/* This is a 5 bit reloc for the AVR that stores an I/O register +number for the SBIC, SBIS, SBI and CBI instructions */ + BFD_RELOC_AVR_PORT5, + /* Renesas RL78 Relocations. */ BFD_RELOC_RL78_NEG8, BFD_RELOC_RL78_NEG16, diff --git a/bfd/elf32-avr.c b/bfd/elf32-avr.c index 9ca0c46..54d67bf 100644 --- a/bfd/elf32-avr.c +++ b/bfd/elf32-avr.c @@ -613,7 +613,34 @@ static reloc_howto_type elf_avr_howto_table[] = FALSE, /* partial_inplace */ 0xffff, /* src_mask */ 0xffff, /* dst_mask */ - FALSE) /* pcrel_offset */ + FALSE), /* pcrel_offset */ + + HOWTO (R_AVR_PORT6, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 6, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_AVR_PORT6", /* name */ + FALSE, /* partial_inplace */ + 0xffffff, /* src_mask */ + 0xffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_AVR_PORT5, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 5, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_AVR_PORT5", /* name */ + FALSE, /* partial_inplace */ + 0xffffff, /* src_mask */ + 0xffffff, /* dst_mask */ + FALSE) /* pcrel_offset */ }; /* Map BFD reloc types to AVR ELF reloc types. */ @@ -659,7 +686,9 @@ static const struct avr_reloc_map avr_reloc_map[] = { BFD_RELOC_AVR_DIFF8, R_AVR_DIFF8 }, { BFD_RELOC_AVR_DIFF16, R_AVR_DIFF16 }, { BFD_RELOC_AVR_DIFF32, R_AVR_DIFF32 }, - { BFD_RELOC_AVR_LDS_STS_16, R_AVR_LDS_STS_16} + { BFD_RELOC_AVR_LDS_STS_16, R_AVR_LDS_STS_16}, + { BFD_RELOC_AVR_PORT6, R_AVR_PORT6}, + { BFD_RELOC_AVR_PORT5, R_AVR_PORT5} }; /* Meant to be filled one day with the wrap around address for the @@ -1248,6 +1277,26 @@ avr_final_link_relocate (reloc_howto_type * howto, bfd_put_16 (input_bfd, x, contents); break; + case R_AVR_PORT6: + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation + rel->r_addend; + if ((srel & 0xffff) > 0x3f) + return bfd_reloc_outofrange; + x = bfd_get_16 (input_bfd, contents); + x = (x & 0xf9f0) | ((srel & 0x30) << 5) | (srel & 0x0f); + bfd_put_16 (input_bfd, x, contents); + break; + + case R_AVR_PORT5: + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation + rel->r_addend; + if ((srel & 0xffff) > 0x1f) + return bfd_reloc_outofrange; + x = bfd_get_16 (input_bfd, contents); + x = (x & 0xff07) | ((srel & 0x1f) << 3); + bfd_put_16 (input_bfd, x, contents); + break; + default: r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, diff --git a/bfd/libbfd.h b/bfd/libbfd.h index 9452d12..3cca8c9 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -2042,6 +2042,8 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@", "BFD_RELOC_AVR_DIFF16", "BFD_RELOC_AVR_DIFF32", "BFD_RELOC_AVR_LDS_STS_16", + "BFD_RELOC_AVR_PORT6", + "BFD_RELOC_AVR_PORT5", "BFD_RELOC_RL78_NEG8", "BFD_RELOC_RL78_NEG16", "BFD_RELOC_RL78_NEG24", diff --git a/bfd/reloc.c b/bfd/reloc.c index 9a77966..86ff2cd 100644 --- a/bfd/reloc.c +++ b/bfd/reloc.c @@ -4795,6 +4795,16 @@ ENUMDOC This is a 7 bit reloc for the AVR that stores SRAM address for 16bit lds and sts instructions supported only tiny core. ENUM + BFD_RELOC_AVR_PORT6 +ENUMDOC + This is a 6 bit reloc for the AVR that stores an I/O register + number for the IN and OUT instructions +ENUM + BFD_RELOC_AVR_PORT5 +ENUMDOC + This is a 5 bit reloc for the AVR that stores an I/O register + number for the SBIC, SBIS, SBI and CBI instructions +ENUM BFD_RELOC_RL78_NEG8 ENUMX BFD_RELOC_RL78_NEG16 diff --git a/gas/ChangeLog b/gas/ChangeLog index f2f25f2..ec38771 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,12 @@ +2014-07-07 Barney Stratford + + * config/tc-avr.c (avr_operand): Permit referring to r26-r31 by + name as [xyz][hl]. Permit using a symbol whoes name begins with + ‘r’ to refer to a register. + Allow arbitrary expressions for the P and p operators. + (md_apply_fix): Check the BFD_RELOC_AVR_PORT5 and + BFD_RELOC_AVR_PORT6 relocations. + 2014-07-04 Alan Modra * doc/internals.texi: Update "configure.in" comments. diff --git a/gas/config/tc-avr.c b/gas/config/tc-avr.c index 9ed7de8..dfe66c6 100644 --- a/gas/config/tc-avr.c +++ b/gas/config/tc-avr.c @@ -345,8 +345,8 @@ struct avr_opt_s int all_opcodes; /* -mall-opcodes: accept all known AVR opcodes. */ int no_skip_bug; /* -mno-skip-bug: no warnings for skipping 2-word insns. */ int no_wrap; /* -mno-wrap: reject rjmp/rcall with 8K wrap-around. */ - int link_relax; /* -mlink-relax: generate relocations for linker - relaxation. */ + int link_relax; /* -mlink-relax: generate relocations for linker + relaxation. */ }; static struct avr_opt_s avr_opt = { 0, 0, 0, 0 }; @@ -861,27 +861,40 @@ avr_operand (struct avr_opcodes_s *opcode, case 'r': case 'a': case 'v': - if (*str == 'r' || *str == 'R') - { - char r_name[20]; + { + char * old_str = str; + char *lower; + char r_name[20]; - str = extract_word (str, r_name, sizeof (r_name)); - op_mask = 0xff; - if (ISDIGIT (r_name[1])) - { - if (r_name[2] == '\0') - op_mask = r_name[1] - '0'; - else if (r_name[1] != '0' - && ISDIGIT (r_name[2]) - && r_name[3] == '\0') - op_mask = (r_name[1] - '0') * 10 + r_name[2] - '0'; - } - } - else - { - op_mask = avr_get_constant (str, 31); - str = input_line_pointer; - } + str = extract_word (str, r_name, sizeof (r_name)); + for (lower = r_name; *lower; ++lower) + { + if (*lower >= 'A' && *lower <= 'Z') + *lower += 'a' - 'A'; + } + + if (r_name[0] == 'r' && ISDIGIT (r_name[1]) && r_name[2] == 0) + /* Single-digit register number, ie r0-r9. */ + op_mask = r_name[1] - '0'; + else if (r_name[0] == 'r' && ISDIGIT (r_name[1]) + && ISDIGIT (r_name[2]) && r_name[3] == 0) + /* Double-digit register number, ie r10 - r32. */ + op_mask = (r_name[1] - '0') * 10 + r_name[2] - '0'; + else if (r_name[0] >= 'x' && r_name[0] <= 'z' + && (r_name[1] == 'l' || r_name[1] == 'h') && r_name[2] == 0) + /* Registers r26-r31 referred to by name, ie xl, xh, yl, yh, zl, zh. */ + op_mask = (r_name[0] - 'x') * 2 + (r_name[1] == 'h') + 26; + else if ((*op == 'v' || *op == 'w') + && r_name[0] >= 'x' && r_name[0] <= 'z' && r_name[1] == 0) + /* For the movw and addiw instructions, refer to registers x, y and z by name. */ + op_mask = (r_name[0] - 'x') * 2 + 26; + else + { + /* Numeric or symbolic constant register number. */ + op_mask = avr_get_constant (old_str, 31); + str = input_line_pointer; + } + } if (avr_mcu->mach == bfd_mach_avrtiny) { @@ -1080,23 +1093,15 @@ avr_operand (struct avr_opcodes_s *opcode, break; case 'P': - { - unsigned int x; - - x = avr_get_constant (str, 63); - str = input_line_pointer; - op_mask |= (x & 0xf) | ((x & 0x30) << 5); - } + str = parse_exp (str, &op_expr); + fix_new_exp (frag_now, where, opcode->insn_size * 2, + &op_expr, FALSE, BFD_RELOC_AVR_PORT6); break; case 'p': - { - unsigned int x; - - x = avr_get_constant (str, 31); - str = input_line_pointer; - op_mask |= x << 3; - } + str = parse_exp (str, &op_expr); + fix_new_exp (frag_now, where, opcode->insn_size * 2, + &op_expr, FALSE, BFD_RELOC_AVR_PORT5); break; case 'E': @@ -1237,7 +1242,7 @@ relaxable_section (asection *sec) return (sec->flags & SEC_DEBUGGING) == 0; } -/* Does whatever the xtensa port does. */ +/* Does whatever the xtensa port does. */ int avr_validate_fix_sub (fixS *fix) { @@ -1267,7 +1272,7 @@ avr_validate_fix_sub (fixS *fix) /* If linkrelax is turned on, and the symbol to relocate against is in a relaxable segment, don't compute the value - - generate a relocation instead. */ + generate a relocation instead. */ int avr_force_relocation (fixS *fix) { @@ -1310,8 +1315,8 @@ md_apply_fix (fixS *fixP, valueT * valP, segT seg) expression. fixP->fx_addsy holds the section start symbol, fixP->fx_offset holds sym2's offset, and fixP->fx_subsy holds sym1. Calculate the current difference and write value, - but leave fx_offset as is - during relaxation, - fx_offset - value gives sym1's value */ + but leave fx_offset as is - during relaxation, + fx_offset - value gives sym1's value. */ switch (fixP->fx_r_type) { @@ -1340,7 +1345,7 @@ md_apply_fix (fixS *fixP, valueT * valP, segT seg) /* For the DIFF relocs, write the value into the object file while still keeping fx_done FALSE, as both the difference (recorded in the object file) - and the sym offset (part of fixP) are needed at link relax time */ + and the sym offset (part of fixP) are needed at link relax time. */ where = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where; switch (fixP->fx_r_type) { @@ -1548,6 +1553,20 @@ md_apply_fix (fixS *fixP, valueT * valP, segT seg) as_fatal (_("line %d: unknown relocation type: 0x%x"), fixP->fx_line, fixP->fx_r_type); break; + + case BFD_RELOC_AVR_PORT6: + if (value > 63) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("operand out of range: %ld"), value); + bfd_putl16 ((bfd_vma) insn | ((value & 0x30) << 5) | (value & 0x0f), where); + break; + + case BFD_RELOC_AVR_PORT5: + if (value > 31) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("operand out of range: %ld"), value); + bfd_putl16 ((bfd_vma) insn | ((value & 0x1f) << 3), where); + break; } } else @@ -1628,9 +1647,9 @@ md_assemble (char *str) if (opcode && !avr_opt.all_opcodes) { - /* Check if the instruction's ISA bit is ON in the ISA bits of the part + /* Check if the instruction's ISA bit is ON in the ISA bits of the part specified by the user. If not look for other instructions - specifications with same mnemonic who's ISA bits matches. + specifications with same mnemonic who's ISA bits matches. This requires include/opcode/avr.h to have the instructions with same mnenomic to be specified in sequence. */ @@ -1638,15 +1657,15 @@ md_assemble (char *str) while ((opcode->isa & avr_mcu->isa) != opcode->isa) { opcode++; - + if (opcode->name && strcmp(op, opcode->name)) { - as_bad (_("illegal opcode %s for mcu %s"), + as_bad (_("illegal opcode %s for mcu %s"), opcode->name, avr_mcu->name); return; } } - } + } if (opcode == NULL) { @@ -1786,10 +1805,10 @@ avr_cons_fix_new (fragS *frag, static bfd_boolean mcu_has_3_byte_pc (void) { - int mach = avr_mcu->mach; + int mach = avr_mcu->mach; - return mach == bfd_mach_avr6 - || mach == bfd_mach_avrxmega6 + return mach == bfd_mach_avr6 + || mach == bfd_mach_avrxmega6 || mach == bfd_mach_avrxmega7; } @@ -1813,7 +1832,7 @@ avr_allow_local_subtract (expressionS * left, expressionS * right, segT section) { - /* If we are not in relaxation mode, subtraction is OK. */ + /* If we are not in relaxation mode, subtraction is OK. */ if (!linkrelax) return TRUE; diff --git a/include/elf/ChangeLog b/include/elf/ChangeLog index 2db10e1..35d4f4b 100644 --- a/include/elf/ChangeLog +++ b/include/elf/ChangeLog @@ -1,3 +1,7 @@ +2014-07-07 Barney Stratford + + * avr.h: Add R_AVR_PORT5 and R_AVR_PORT6. + 2014-07-01 Barney Stratford Senthil Kumar Selvaraj Pitchumani Sivanupandi diff --git a/include/elf/avr.h b/include/elf/avr.h index 0f3ed03..f2f500d 100644 --- a/include/elf/avr.h +++ b/include/elf/avr.h @@ -85,6 +85,8 @@ START_RELOC_NUMBERS (elf_avr_reloc_type) RELOC_NUMBER (R_AVR_DIFF16, 31) RELOC_NUMBER (R_AVR_DIFF32, 32) RELOC_NUMBER (R_AVR_LDS_STS_16, 33) + RELOC_NUMBER (R_AVR_PORT6, 34) + RELOC_NUMBER (R_AVR_PORT5, 35) END_RELOC_NUMBERS (R_AVR_max) #endif /* _ELF_AVR_H */ -- 2.7.4