URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [gnu-old/] [binutils-2.18.50/] [gas/] [config/] [tc-avr.c] - Rev 816
Compare with Previous | Blame | View Log
/* tc-avr.c -- Assembler code for the ATMEL AVR Copyright 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. Contributed by Denis Chertykov <denisc@overta.ru> This file is part of GAS, the GNU Assembler. GAS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GAS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GAS; see the file COPYING. If not, write to the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "as.h" #include "safe-ctype.h" #include "subsegs.h" struct avr_opcodes_s { char * name; char * constraints; int insn_size; /* In words. */ int isa; unsigned int bin_opcode; }; #define AVR_INSN(NAME, CONSTR, OPCODE, SIZE, ISA, BIN) \ {#NAME, CONSTR, SIZE, ISA, BIN}, struct avr_opcodes_s avr_opcodes[] = { #include "opcode/avr.h" {NULL, NULL, 0, 0, 0} }; const char comment_chars[] = ";"; const char line_comment_chars[] = "#"; const char line_separator_chars[] = "$"; const char *md_shortopts = "m:"; struct mcu_type_s { char *name; int isa; int mach; }; /* XXX - devices that don't seem to exist (renamed, replaced with larger ones, or planned but never produced), left here for compatibility. TODO: hide them in show_mcu_list output? */ static struct mcu_type_s mcu_types[] = { {"avr1", AVR_ISA_TINY1, bfd_mach_avr1}, {"avr2", AVR_ISA_TINY2, bfd_mach_avr2}, {"avr3", AVR_ISA_AVR3, bfd_mach_avr3}, {"avr4", AVR_ISA_M8, bfd_mach_avr4}, {"avr5", AVR_ISA_ALL, bfd_mach_avr5}, {"avr6", AVR_ISA_ALL, bfd_mach_avr6}, {"at90s1200", AVR_ISA_1200, bfd_mach_avr1}, {"attiny11", AVR_ISA_TINY1, bfd_mach_avr1}, {"attiny12", AVR_ISA_TINY1, bfd_mach_avr1}, {"attiny15", AVR_ISA_TINY1, bfd_mach_avr1}, {"attiny28", AVR_ISA_TINY1, bfd_mach_avr1}, {"at90s2313", AVR_ISA_2xxx, bfd_mach_avr2}, {"at90s2323", AVR_ISA_2xxx, bfd_mach_avr2}, {"at90s2333", AVR_ISA_2xxx, bfd_mach_avr2}, /* XXX -> 4433 */ {"at90s2343", AVR_ISA_2xxx, bfd_mach_avr2}, {"attiny22", AVR_ISA_2xxx, bfd_mach_avr2}, /* XXX -> 2343 */ {"attiny26", AVR_ISA_2xxe, bfd_mach_avr2}, {"at90s4433", AVR_ISA_2xxx, bfd_mach_avr2}, {"at90s4414", AVR_ISA_2xxx, bfd_mach_avr2}, /* XXX -> 8515 */ {"at90s4434", AVR_ISA_2xxx, bfd_mach_avr2}, /* XXX -> 8535 */ {"at90s8515", AVR_ISA_2xxx, bfd_mach_avr2}, {"at90s8535", AVR_ISA_2xxx, bfd_mach_avr2}, {"at90c8534", AVR_ISA_2xxx, bfd_mach_avr2}, {"at86rf401", AVR_ISA_RF401, bfd_mach_avr2}, {"attiny13", AVR_ISA_TINY2, bfd_mach_avr2}, {"attiny2313", AVR_ISA_TINY2, bfd_mach_avr2}, {"attiny261", AVR_ISA_TINY2, bfd_mach_avr2}, {"attiny461", AVR_ISA_TINY2, bfd_mach_avr2}, {"attiny861", AVR_ISA_TINY2, bfd_mach_avr2}, {"attiny24", AVR_ISA_TINY2, bfd_mach_avr2}, {"attiny44", AVR_ISA_TINY2, bfd_mach_avr2}, {"attiny84", AVR_ISA_TINY2, bfd_mach_avr2}, {"attiny25", AVR_ISA_TINY2, bfd_mach_avr2}, {"attiny45", AVR_ISA_TINY2, bfd_mach_avr2}, {"attiny85", AVR_ISA_TINY2, bfd_mach_avr2}, {"attiny43u", AVR_ISA_TINY2, bfd_mach_avr2}, {"attiny48", AVR_ISA_TINY2, bfd_mach_avr2}, {"attiny88", AVR_ISA_TINY2, bfd_mach_avr2}, {"atmega103", AVR_ISA_M103, bfd_mach_avr3}, {"at43usb320", AVR_ISA_M103, bfd_mach_avr3}, {"at43usb355", AVR_ISA_M603, bfd_mach_avr3}, {"at76c711", AVR_ISA_M603, bfd_mach_avr3}, {"at90usb82", AVR_ISA_USB162, bfd_mach_avr3}, {"at90usb162", AVR_ISA_USB162, bfd_mach_avr3}, {"attiny167", AVR_ISA_TINY3, bfd_mach_avr3}, {"atmega48", AVR_ISA_PWMx, bfd_mach_avr4}, {"atmega48p", AVR_ISA_PWMx, bfd_mach_avr4}, {"atmega8", AVR_ISA_M8, bfd_mach_avr4}, {"atmega88", AVR_ISA_PWMx, bfd_mach_avr4}, {"atmega88p", AVR_ISA_PWMx, bfd_mach_avr4}, {"atmega8515", AVR_ISA_M8, bfd_mach_avr4}, {"atmega8535", AVR_ISA_M8, bfd_mach_avr4}, {"atmega8hva", AVR_ISA_PWMx, bfd_mach_avr4}, {"at90pwm1", AVR_ISA_PWMx, bfd_mach_avr4}, {"at90pwm2", AVR_ISA_PWMx, bfd_mach_avr4}, {"at90pwm2b", AVR_ISA_PWMx, bfd_mach_avr4}, {"at90pwm3", AVR_ISA_PWMx, bfd_mach_avr4}, {"at90pwm3b", AVR_ISA_PWMx, bfd_mach_avr4}, {"atmega16", AVR_ISA_M323, bfd_mach_avr5}, {"atmega161", AVR_ISA_M161, bfd_mach_avr5}, {"atmega162", AVR_ISA_M323, bfd_mach_avr5}, {"atmega163", AVR_ISA_M161, bfd_mach_avr5}, {"atmega164p", AVR_ISA_M323, bfd_mach_avr5}, {"atmega165", AVR_ISA_M323, bfd_mach_avr5}, {"atmega165p", AVR_ISA_M323, bfd_mach_avr5}, {"atmega168", AVR_ISA_M323, bfd_mach_avr5}, {"atmega168p", AVR_ISA_M323, bfd_mach_avr5}, {"atmega169", AVR_ISA_M323, bfd_mach_avr5}, {"atmega169p", AVR_ISA_M323, bfd_mach_avr5}, {"atmega32", AVR_ISA_M323, bfd_mach_avr5}, {"atmega323", AVR_ISA_M323, bfd_mach_avr5}, {"atmega324p", AVR_ISA_M323, bfd_mach_avr5}, {"atmega325", AVR_ISA_M323, bfd_mach_avr5}, {"atmega325p", AVR_ISA_M323, bfd_mach_avr5}, {"atmega328p", AVR_ISA_M323, bfd_mach_avr5}, {"atmega329", AVR_ISA_M323, bfd_mach_avr5}, {"atmega329p", AVR_ISA_M323, bfd_mach_avr5}, {"atmega3250", AVR_ISA_M323, bfd_mach_avr5}, {"atmega3250p",AVR_ISA_M323, bfd_mach_avr5}, {"atmega3290", AVR_ISA_M323, bfd_mach_avr5}, {"atmega3290p",AVR_ISA_M323, bfd_mach_avr5}, {"atmega32hvb",AVR_ISA_M323, bfd_mach_avr5}, {"atmega406", AVR_ISA_M323, bfd_mach_avr5}, {"atmega64", AVR_ISA_M323, bfd_mach_avr5}, {"atmega640", AVR_ISA_M323, bfd_mach_avr5}, {"atmega644", AVR_ISA_M323, bfd_mach_avr5}, {"atmega644p", AVR_ISA_M323, bfd_mach_avr5}, {"atmega128", AVR_ISA_M128, bfd_mach_avr5}, {"atmega1280", AVR_ISA_M128, bfd_mach_avr5}, {"atmega1281", AVR_ISA_M128, bfd_mach_avr5}, {"atmega1284p",AVR_ISA_M128, bfd_mach_avr5}, {"atmega645", AVR_ISA_M323, bfd_mach_avr5}, {"atmega649", AVR_ISA_M323, bfd_mach_avr5}, {"atmega6450", AVR_ISA_M323, bfd_mach_avr5}, {"atmega6490", AVR_ISA_M323, bfd_mach_avr5}, {"atmega16hva",AVR_ISA_M323, bfd_mach_avr5}, {"at90can32" , AVR_ISA_M323, bfd_mach_avr5}, {"at90can64" , AVR_ISA_M323, bfd_mach_avr5}, {"at90can128", AVR_ISA_M128, bfd_mach_avr5}, {"at90pwm216", AVR_ISA_M323, bfd_mach_avr5}, {"at90pwm316", AVR_ISA_M323, bfd_mach_avr5}, {"atmega32c1", AVR_ISA_M323, bfd_mach_avr5}, {"atmega32m1", AVR_ISA_M323, bfd_mach_avr5}, {"atmega32u4", AVR_ISA_M323, bfd_mach_avr5}, {"at90usb646", AVR_ISA_M323, bfd_mach_avr5}, {"at90usb647", AVR_ISA_M323, bfd_mach_avr5}, {"at90usb1286",AVR_ISA_M128, bfd_mach_avr5}, {"at90usb1287",AVR_ISA_M128, bfd_mach_avr5}, {"at94k", AVR_ISA_94K, bfd_mach_avr5}, {"atmega2560", AVR_ISA_ALL, bfd_mach_avr6}, {"atmega2561", AVR_ISA_ALL, bfd_mach_avr6}, {NULL, 0, 0} }; /* Current MCU type. */ static struct mcu_type_s default_mcu = {"avr2", AVR_ISA_2xxx,bfd_mach_avr2}; static struct mcu_type_s * avr_mcu = & default_mcu; /* AVR target-specific switches. */ 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. */ }; static struct avr_opt_s avr_opt = { 0, 0, 0 }; const char EXP_CHARS[] = "eE"; const char FLT_CHARS[] = "dD"; static void avr_set_arch (int); /* The target specific pseudo-ops which we support. */ const pseudo_typeS md_pseudo_table[] = { {"arch", avr_set_arch, 0}, { NULL, NULL, 0} }; #define LDI_IMMEDIATE(x) (((x) & 0xf) | (((x) << 4) & 0xf00)) #define EXP_MOD_NAME(i) exp_mod[i].name #define EXP_MOD_RELOC(i) exp_mod[i].reloc #define EXP_MOD_NEG_RELOC(i) exp_mod[i].neg_reloc #define HAVE_PM_P(i) exp_mod[i].have_pm struct exp_mod_s { char * name; bfd_reloc_code_real_type reloc; bfd_reloc_code_real_type neg_reloc; int have_pm; }; static struct exp_mod_s exp_mod[] = { {"hh8", BFD_RELOC_AVR_HH8_LDI, BFD_RELOC_AVR_HH8_LDI_NEG, 1}, {"pm_hh8", BFD_RELOC_AVR_HH8_LDI_PM, BFD_RELOC_AVR_HH8_LDI_PM_NEG, 0}, {"hi8", BFD_RELOC_AVR_HI8_LDI, BFD_RELOC_AVR_HI8_LDI_NEG, 1}, {"pm_hi8", BFD_RELOC_AVR_HI8_LDI_PM, BFD_RELOC_AVR_HI8_LDI_PM_NEG, 0}, {"lo8", BFD_RELOC_AVR_LO8_LDI, BFD_RELOC_AVR_LO8_LDI_NEG, 1}, {"pm_lo8", BFD_RELOC_AVR_LO8_LDI_PM, BFD_RELOC_AVR_LO8_LDI_PM_NEG, 0}, {"hlo8", BFD_RELOC_AVR_HH8_LDI, BFD_RELOC_AVR_HH8_LDI_NEG, 0}, {"hhi8", BFD_RELOC_AVR_MS8_LDI, BFD_RELOC_AVR_MS8_LDI_NEG, 0}, }; /* A union used to store indicies into the exp_mod[] array in a hash table which expects void * data types. */ typedef union { void * ptr; int index; } mod_index; /* Opcode hash table. */ static struct hash_control *avr_hash; /* Reloc modifiers hash control (hh8,hi8,lo8,pm_xx). */ static struct hash_control *avr_mod_hash; #define OPTION_MMCU 'm' enum options { OPTION_ALL_OPCODES = OPTION_MD_BASE + 1, OPTION_NO_SKIP_BUG, OPTION_NO_WRAP }; struct option md_longopts[] = { { "mmcu", required_argument, NULL, OPTION_MMCU }, { "mall-opcodes", no_argument, NULL, OPTION_ALL_OPCODES }, { "mno-skip-bug", no_argument, NULL, OPTION_NO_SKIP_BUG }, { "mno-wrap", no_argument, NULL, OPTION_NO_WRAP }, { NULL, no_argument, NULL, 0 } }; size_t md_longopts_size = sizeof (md_longopts); /* Display nicely formatted list of known MCU names. */ static void show_mcu_list (FILE *stream) { int i, x; fprintf (stream, _("Known MCU names:")); x = 1000; for (i = 0; mcu_types[i].name; i++) { int len = strlen (mcu_types[i].name); x += len + 1; if (x < 75) fprintf (stream, " %s", mcu_types[i].name); else { fprintf (stream, "\n %s", mcu_types[i].name); x = len + 2; } } fprintf (stream, "\n"); } static inline char * skip_space (char *s) { while (*s == ' ' || *s == '\t') ++s; return s; } /* Extract one word from FROM and copy it to TO. */ static char * extract_word (char *from, char *to, int limit) { char *op_start; char *op_end; int size = 0; /* Drop leading whitespace. */ from = skip_space (from); *to = 0; /* Find the op code end. */ for (op_start = op_end = from; *op_end != 0 && is_part_of_name (*op_end);) { to[size++] = *op_end++; if (size + 1 >= limit) break; } to[size] = 0; return op_end; } int md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED, asection *seg ATTRIBUTE_UNUSED) { abort (); return 0; } void md_show_usage (FILE *stream) { fprintf (stream, _("AVR options:\n" " -mmcu=[avr-name] select microcontroller variant\n" " [avr-name] can be:\n" " avr1 - AT90S1200, ATtiny1x, ATtiny28\n" " avr2 - AT90S2xxx, AT90S4xxx, AT90S8xxx, ATtiny22\n" " avr3 - ATmega103\n" " avr4 - ATmega8, ATmega88\n" " avr5 - ATmega161, ATmega163, ATmega32, AT94K\n" " or immediate microcontroller name.\n")); fprintf (stream, _(" -mall-opcodes accept all AVR opcodes, even if not supported by MCU\n" " -mno-skip-bug disable warnings for skipping two-word instructions\n" " (default for avr4, avr5)\n" " -mno-wrap reject rjmp/rcall instructions with 8K wrap-around\n" " (default for avr3, avr5)\n")); show_mcu_list (stream); } static void avr_set_arch (int dummy ATTRIBUTE_UNUSED) { char str[20]; input_line_pointer = extract_word (input_line_pointer, str, 20); md_parse_option (OPTION_MMCU, str); bfd_set_arch_mach (stdoutput, TARGET_ARCH, avr_mcu->mach); } int md_parse_option (int c, char *arg) { switch (c) { case OPTION_MMCU: { int i; char *s = alloca (strlen (arg) + 1); { char *t = s; char *arg1 = arg; do *t = TOLOWER (*arg1++); while (*t++); } for (i = 0; mcu_types[i].name; ++i) if (strcmp (mcu_types[i].name, s) == 0) break; if (!mcu_types[i].name) { show_mcu_list (stderr); as_fatal (_("unknown MCU: %s\n"), arg); } /* It is OK to redefine mcu type within the same avr[1-5] bfd machine type - this for allows passing -mmcu=... via gcc ASM_SPEC as well as .arch ... in the asm output at the same time. */ if (avr_mcu == &default_mcu || avr_mcu->mach == mcu_types[i].mach) avr_mcu = &mcu_types[i]; else as_fatal (_("redefinition of mcu type `%s' to `%s'"), avr_mcu->name, mcu_types[i].name); return 1; } case OPTION_ALL_OPCODES: avr_opt.all_opcodes = 1; return 1; case OPTION_NO_SKIP_BUG: avr_opt.no_skip_bug = 1; return 1; case OPTION_NO_WRAP: avr_opt.no_wrap = 1; return 1; } return 0; } symbolS * md_undefined_symbol (char *name ATTRIBUTE_UNUSED) { return NULL; } char * md_atof (int type, char *litP, int *sizeP) { return ieee_md_atof (type, litP, sizeP, FALSE); } void md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, asection *sec ATTRIBUTE_UNUSED, fragS *fragP ATTRIBUTE_UNUSED) { abort (); } void md_begin (void) { unsigned int i; struct avr_opcodes_s *opcode; avr_hash = hash_new (); /* Insert unique names into hash table. This hash table then provides a quick index to the first opcode with a particular name in the opcode table. */ for (opcode = avr_opcodes; opcode->name; opcode++) hash_insert (avr_hash, opcode->name, (char *) opcode); avr_mod_hash = hash_new (); for (i = 0; i < ARRAY_SIZE (exp_mod); ++i) { mod_index m; m.index = i + 10; hash_insert (avr_mod_hash, EXP_MOD_NAME (i), m.ptr); } bfd_set_arch_mach (stdoutput, TARGET_ARCH, avr_mcu->mach); } /* Resolve STR as a constant expression and return the result. If result greater than MAX then error. */ static unsigned int avr_get_constant (char *str, int max) { expressionS ex; str = skip_space (str); input_line_pointer = str; expression (& ex); if (ex.X_op != O_constant) as_bad (_("constant value required")); if (ex.X_add_number > max || ex.X_add_number < 0) as_bad (_("number must be positive and less than %d"), max + 1); return ex.X_add_number; } /* Parse for ldd/std offset. */ static void avr_offset_expression (expressionS *exp) { char *str = input_line_pointer; char *tmp; char op[8]; tmp = str; str = extract_word (str, op, sizeof (op)); input_line_pointer = tmp; expression (exp); /* Warn about expressions that fail to use lo8 (). */ if (exp->X_op == O_constant) { int x = exp->X_add_number; if (x < -255 || x > 255) as_warn (_("constant out of 8-bit range: %d"), x); } } /* Parse ordinary expression. */ static char * parse_exp (char *s, expressionS *op) { input_line_pointer = s; expression (op); if (op->X_op == O_absent) as_bad (_("missing operand")); return input_line_pointer; } /* Parse special expressions (needed for LDI command): xx8 (address) xx8 (-address) pm_xx8 (address) pm_xx8 (-address) where xx is: hh, hi, lo. */ static bfd_reloc_code_real_type avr_ldi_expression (expressionS *exp) { char *str = input_line_pointer; char *tmp; char op[8]; int mod; int linker_stubs_should_be_generated = 0; tmp = str; str = extract_word (str, op, sizeof (op)); if (op[0]) { mod_index m; m.ptr = hash_find (avr_mod_hash, op); mod = m.index; if (mod) { int closes = 0; mod -= 10; str = skip_space (str); if (*str == '(') { bfd_reloc_code_real_type reloc_to_return; int neg_p = 0; ++str; if (strncmp ("pm(", str, 3) == 0 || strncmp ("gs(",str,3) == 0 || strncmp ("-(gs(",str,5) == 0 || strncmp ("-(pm(", str, 5) == 0) { if (HAVE_PM_P (mod)) { ++mod; ++closes; } else as_bad (_("illegal expression")); if (str[0] == 'g' || str[2] == 'g') linker_stubs_should_be_generated = 1; if (*str == '-') { neg_p = 1; ++closes; str += 5; } else str += 3; } if (*str == '-' && *(str + 1) == '(') { neg_p ^= 1; ++closes; str += 2; } input_line_pointer = str; expression (exp); do { if (*input_line_pointer != ')') { as_bad (_("`)' required")); break; } input_line_pointer++; } while (closes--); reloc_to_return = neg_p ? EXP_MOD_NEG_RELOC (mod) : EXP_MOD_RELOC (mod); if (linker_stubs_should_be_generated) { switch (reloc_to_return) { case BFD_RELOC_AVR_LO8_LDI_PM: reloc_to_return = BFD_RELOC_AVR_LO8_LDI_GS; break; case BFD_RELOC_AVR_HI8_LDI_PM: reloc_to_return = BFD_RELOC_AVR_HI8_LDI_GS; break; default: as_warn (_("expression dangerous with linker stubs")); } } return reloc_to_return; } } } input_line_pointer = tmp; expression (exp); /* Warn about expressions that fail to use lo8 (). */ if (exp->X_op == O_constant) { int x = exp->X_add_number; if (x < -255 || x > 255) as_warn (_("constant out of 8-bit range: %d"), x); } return BFD_RELOC_AVR_LDI; } /* Parse one instruction operand. Return operand bitmask. Also fixups can be generated. */ static unsigned int avr_operand (struct avr_opcodes_s *opcode, int where, char *op, char **line) { expressionS op_expr; unsigned int op_mask = 0; char *str = skip_space (*line); switch (*op) { /* Any register operand. */ case 'w': case 'd': case 'r': case 'a': case 'v': if (*str == 'r' || *str == 'R') { 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; } if (op_mask <= 31) { switch (*op) { case 'a': if (op_mask < 16 || op_mask > 23) as_bad (_("register r16-r23 required")); op_mask -= 16; break; case 'd': if (op_mask < 16) as_bad (_("register number above 15 required")); op_mask -= 16; break; case 'v': if (op_mask & 1) as_bad (_("even register number required")); op_mask >>= 1; break; case 'w': if ((op_mask & 1) || op_mask < 24) as_bad (_("register r24, r26, r28 or r30 required")); op_mask = (op_mask - 24) >> 1; break; } break; } as_bad (_("register name or number from 0 to 31 required")); break; case 'e': { char c; if (*str == '-') { str = skip_space (str + 1); op_mask = 0x1002; } c = TOLOWER (*str); if (c == 'x') op_mask |= 0x100c; else if (c == 'y') op_mask |= 0x8; else if (c != 'z') as_bad (_("pointer register (X, Y or Z) required")); str = skip_space (str + 1); if (*str == '+') { ++str; if (op_mask & 2) as_bad (_("cannot both predecrement and postincrement")); op_mask |= 0x1001; } /* avr1 can do "ld r,Z" and "st Z,r" but no other pointer registers, no predecrement, no postincrement. */ if (!avr_opt.all_opcodes && (op_mask & 0x100F) && !(avr_mcu->isa & AVR_ISA_SRAM)) as_bad (_("addressing mode not supported")); } break; case 'z': if (*str == '-') as_bad (_("can't predecrement")); if (! (*str == 'z' || *str == 'Z')) as_bad (_("pointer register Z required")); str = skip_space (str + 1); if (*str == '+') { ++str; op_mask |= 1; } /* attiny26 can do "lpm" and "lpm r,Z" but not "lpm r,Z+". */ if (!avr_opt.all_opcodes && (op_mask & 0x0001) && !(avr_mcu->isa & AVR_ISA_MOVW)) as_bad (_("postincrement not supported")); break; case 'b': { char c = TOLOWER (*str++); if (c == 'y') op_mask |= 0x8; else if (c != 'z') as_bad (_("pointer register (Y or Z) required")); str = skip_space (str); if (*str++ == '+') { input_line_pointer = str; avr_offset_expression (& op_expr); str = input_line_pointer; fix_new_exp (frag_now, where, 3, &op_expr, FALSE, BFD_RELOC_AVR_6); } } break; case 'h': str = parse_exp (str, &op_expr); fix_new_exp (frag_now, where, opcode->insn_size * 2, &op_expr, FALSE, BFD_RELOC_AVR_CALL); break; case 'L': str = parse_exp (str, &op_expr); fix_new_exp (frag_now, where, opcode->insn_size * 2, &op_expr, TRUE, BFD_RELOC_AVR_13_PCREL); break; case 'l': str = parse_exp (str, &op_expr); fix_new_exp (frag_now, where, opcode->insn_size * 2, &op_expr, TRUE, BFD_RELOC_AVR_7_PCREL); break; case 'i': str = parse_exp (str, &op_expr); fix_new_exp (frag_now, where + 2, opcode->insn_size * 2, &op_expr, FALSE, BFD_RELOC_16); break; case 'M': { bfd_reloc_code_real_type r_type; input_line_pointer = str; r_type = avr_ldi_expression (&op_expr); str = input_line_pointer; fix_new_exp (frag_now, where, 3, &op_expr, FALSE, r_type); } break; case 'n': { unsigned int x; x = ~avr_get_constant (str, 255); str = input_line_pointer; op_mask |= (x & 0xf) | ((x << 4) & 0xf00); } break; case 'K': input_line_pointer = str; avr_offset_expression (& op_expr); str = input_line_pointer; fix_new_exp (frag_now, where, 3, & op_expr, FALSE, BFD_RELOC_AVR_6_ADIW); break; case 'S': case 's': { unsigned int x; x = avr_get_constant (str, 7); str = input_line_pointer; if (*op == 'S') x <<= 4; op_mask |= x; } break; case 'P': { unsigned int x; x = avr_get_constant (str, 63); str = input_line_pointer; op_mask |= (x & 0xf) | ((x & 0x30) << 5); } break; case 'p': { unsigned int x; x = avr_get_constant (str, 31); str = input_line_pointer; op_mask |= x << 3; } break; case '?': break; default: as_bad (_("unknown constraint `%c'"), *op); } *line = str; return op_mask; } /* Parse instruction operands. Return binary opcode. */ static unsigned int avr_operands (struct avr_opcodes_s *opcode, char **line) { char *op = opcode->constraints; unsigned int bin = opcode->bin_opcode; char *frag = frag_more (opcode->insn_size * 2); char *str = *line; int where = frag - frag_now->fr_literal; static unsigned int prev = 0; /* Previous opcode. */ /* Opcode have operands. */ if (*op) { unsigned int reg1 = 0; unsigned int reg2 = 0; int reg1_present = 0; int reg2_present = 0; /* Parse first operand. */ if (REGISTER_P (*op)) reg1_present = 1; reg1 = avr_operand (opcode, where, op, &str); ++op; /* Parse second operand. */ if (*op) { if (*op == ',') ++op; if (*op == '=') { reg2 = reg1; reg2_present = 1; } else { if (REGISTER_P (*op)) reg2_present = 1; str = skip_space (str); if (*str++ != ',') as_bad (_("`,' required")); str = skip_space (str); reg2 = avr_operand (opcode, where, op, &str); } if (reg1_present && reg2_present) reg2 = (reg2 & 0xf) | ((reg2 << 5) & 0x200); else if (reg2_present) reg2 <<= 4; } if (reg1_present) reg1 <<= 4; bin |= reg1 | reg2; } /* Detect undefined combinations (like ld r31,Z+). */ if (!avr_opt.all_opcodes && AVR_UNDEF_P (bin)) as_warn (_("undefined combination of operands")); if (opcode->insn_size == 2) { /* Warn if the previous opcode was cpse/sbic/sbis/sbrc/sbrs (AVR core bug, fixed in the newer devices). */ if (!(avr_opt.no_skip_bug || (avr_mcu->isa & (AVR_ISA_MUL | AVR_ISA_MOVW))) && AVR_SKIP_P (prev)) as_warn (_("skipping two-word instruction")); bfd_putl32 ((bfd_vma) bin, frag); } else bfd_putl16 ((bfd_vma) bin, frag); prev = bin; *line = str; return bin; } /* GAS will call this function for each section at the end of the assembly, to permit the CPU backend to adjust the alignment of a section. */ valueT md_section_align (asection *seg, valueT addr) { int align = bfd_get_section_alignment (stdoutput, seg); return ((addr + (1 << align) - 1) & (-1 << align)); } /* If you define this macro, it should return the offset between the address of a PC relative fixup and the position from which the PC relative adjustment should be made. On many processors, the base of a PC relative instruction is the next instruction, so this macro would return the length of an instruction. */ long md_pcrel_from_section (fixS *fixp, segT sec) { if (fixp->fx_addsy != (symbolS *) NULL && (!S_IS_DEFINED (fixp->fx_addsy) || (S_GET_SEGMENT (fixp->fx_addsy) != sec))) return 0; return fixp->fx_frag->fr_address + fixp->fx_where; } /* GAS will call this for each fixup. It should store the correct value in the object file. */ void md_apply_fix (fixS *fixP, valueT * valP, segT seg) { unsigned char *where; unsigned long insn; long value = *valP; if (fixP->fx_addsy == (symbolS *) NULL) fixP->fx_done = 1; else if (fixP->fx_pcrel) { segT s = S_GET_SEGMENT (fixP->fx_addsy); if (s == seg || s == absolute_section) { value += S_GET_VALUE (fixP->fx_addsy); fixP->fx_done = 1; } } /* We don't actually support subtracting a symbol. */ if (fixP->fx_subsy != (symbolS *) NULL) as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex")); switch (fixP->fx_r_type) { default: fixP->fx_no_overflow = 1; break; case BFD_RELOC_AVR_7_PCREL: case BFD_RELOC_AVR_13_PCREL: case BFD_RELOC_32: case BFD_RELOC_16: case BFD_RELOC_AVR_CALL: break; } if (fixP->fx_done) { /* Fetch the instruction, insert the fully resolved operand value, and stuff the instruction back again. */ where = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where; insn = bfd_getl16 (where); switch (fixP->fx_r_type) { case BFD_RELOC_AVR_7_PCREL: if (value & 1) as_bad_where (fixP->fx_file, fixP->fx_line, _("odd address operand: %ld"), value); /* Instruction addresses are always right-shifted by 1. */ value >>= 1; --value; /* Correct PC. */ if (value < -64 || value > 63) as_bad_where (fixP->fx_file, fixP->fx_line, _("operand out of range: %ld"), value); value = (value << 3) & 0x3f8; bfd_putl16 ((bfd_vma) (value | insn), where); break; case BFD_RELOC_AVR_13_PCREL: if (value & 1) as_bad_where (fixP->fx_file, fixP->fx_line, _("odd address operand: %ld"), value); /* Instruction addresses are always right-shifted by 1. */ value >>= 1; --value; /* Correct PC. */ if (value < -2048 || value > 2047) { /* No wrap for devices with >8K of program memory. */ if ((avr_mcu->isa & AVR_ISA_MEGA) || avr_opt.no_wrap) as_bad_where (fixP->fx_file, fixP->fx_line, _("operand out of range: %ld"), value); } value &= 0xfff; bfd_putl16 ((bfd_vma) (value | insn), where); break; case BFD_RELOC_32: bfd_putl16 ((bfd_vma) value, where); break; case BFD_RELOC_16: bfd_putl16 ((bfd_vma) value, where); break; case BFD_RELOC_AVR_16_PM: bfd_putl16 ((bfd_vma) (value >> 1), where); break; case BFD_RELOC_AVR_LDI: if (value > 255) as_bad_where (fixP->fx_file, fixP->fx_line, _("operand out of range: %ld"), value); bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value), where); break; case BFD_RELOC_AVR_6: if ((value > 63) || (value < 0)) as_bad_where (fixP->fx_file, fixP->fx_line, _("operand out of range: %ld"), value); bfd_putl16 ((bfd_vma) insn | ((value & 7) | ((value & (3 << 3)) << 7) | ((value & (1 << 5)) << 8)), where); break; case BFD_RELOC_AVR_6_ADIW: if ((value > 63) || (value < 0)) as_bad_where (fixP->fx_file, fixP->fx_line, _("operand out of range: %ld"), value); bfd_putl16 ((bfd_vma) insn | (value & 0xf) | ((value & 0x30) << 2), where); break; case BFD_RELOC_AVR_LO8_LDI: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value), where); break; case BFD_RELOC_AVR_HI8_LDI: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 8), where); break; case BFD_RELOC_AVR_MS8_LDI: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 24), where); break; case BFD_RELOC_AVR_HH8_LDI: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 16), where); break; case BFD_RELOC_AVR_LO8_LDI_NEG: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value), where); break; case BFD_RELOC_AVR_HI8_LDI_NEG: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 8), where); break; case BFD_RELOC_AVR_MS8_LDI_NEG: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 24), where); break; case BFD_RELOC_AVR_HH8_LDI_NEG: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 16), where); break; case BFD_RELOC_AVR_LO8_LDI_PM: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 1), where); break; case BFD_RELOC_AVR_HI8_LDI_PM: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 9), where); break; case BFD_RELOC_AVR_HH8_LDI_PM: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 17), where); break; case BFD_RELOC_AVR_LO8_LDI_PM_NEG: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 1), where); break; case BFD_RELOC_AVR_HI8_LDI_PM_NEG: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 9), where); break; case BFD_RELOC_AVR_HH8_LDI_PM_NEG: bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 17), where); break; case BFD_RELOC_AVR_CALL: { unsigned long x; x = bfd_getl16 (where); if (value & 1) as_bad_where (fixP->fx_file, fixP->fx_line, _("odd address operand: %ld"), value); value >>= 1; x |= ((value & 0x10000) | ((value << 3) & 0x1f00000)) >> 16; bfd_putl16 ((bfd_vma) x, where); bfd_putl16 ((bfd_vma) (value & 0xffff), where + 2); } break; default: as_fatal (_("line %d: unknown relocation type: 0x%x"), fixP->fx_line, fixP->fx_r_type); break; } } else { switch (fixP->fx_r_type) { case -BFD_RELOC_AVR_HI8_LDI_NEG: case -BFD_RELOC_AVR_HI8_LDI: case -BFD_RELOC_AVR_LO8_LDI_NEG: case -BFD_RELOC_AVR_LO8_LDI: as_bad_where (fixP->fx_file, fixP->fx_line, _("only constant expression allowed")); fixP->fx_done = 1; break; default: break; } } } /* GAS will call this to generate a reloc, passing the resulting reloc to `bfd_install_relocation'. This currently works poorly, as `bfd_install_relocation' often does the wrong thing, and instances of `tc_gen_reloc' have been written to work around the problems, which in turns makes it difficult to fix `bfd_install_relocation'. */ /* If while processing a fixup, a reloc really needs to be created then it is done here. */ arelent * tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp) { arelent *reloc; if (fixp->fx_addsy && fixp->fx_subsy) { long value = 0; if ((S_GET_SEGMENT (fixp->fx_addsy) != S_GET_SEGMENT (fixp->fx_subsy)) || S_GET_SEGMENT (fixp->fx_addsy) == undefined_section) { as_bad_where (fixp->fx_file, fixp->fx_line, "Difference of symbols in different sections is not supported"); return NULL; } /* We are dealing with two symbols defined in the same section. Let us fix-up them here. */ value += S_GET_VALUE (fixp->fx_addsy); value -= S_GET_VALUE (fixp->fx_subsy); /* When fx_addsy and fx_subsy both are zero, md_apply_fix only takes it's second operands for the fixup value. */ fixp->fx_addsy = NULL; fixp->fx_subsy = NULL; md_apply_fix (fixp, (valueT *) &value, NULL); return NULL; } 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->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, _("reloc %d not supported by object file format"), (int) fixp->fx_r_type); return NULL; } if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) reloc->address = fixp->fx_offset; reloc->addend = fixp->fx_offset; return reloc; } void md_assemble (char *str) { struct avr_opcodes_s *opcode; char op[11]; str = skip_space (extract_word (str, op, sizeof (op))); if (!op[0]) as_bad (_("can't find opcode ")); opcode = (struct avr_opcodes_s *) hash_find (avr_hash, op); if (opcode == NULL) { as_bad (_("unknown opcode `%s'"), op); return; } /* Special case for opcodes with optional operands (lpm, elpm) - version with operands exists in avr_opcodes[] in the next entry. */ if (*str && *opcode->constraints == '?') ++opcode; if (!avr_opt.all_opcodes && (opcode->isa & avr_mcu->isa) != opcode->isa) as_bad (_("illegal opcode %s for mcu %s"), opcode->name, avr_mcu->name); /* We used to set input_line_pointer to the result of get_operands, but that is wrong. Our caller assumes we don't change it. */ { char *t = input_line_pointer; avr_operands (opcode, &str); if (*skip_space (str)) as_bad (_("garbage at end of line")); input_line_pointer = t; } } /* Flag to pass `pm' mode between `avr_parse_cons_expression' and `avr_cons_fix_new'. */ static int exp_mod_pm = 0; /* Parse special CONS expression: pm (expression) or alternatively: gs (expression). These are used for addressing program memory. Relocation: BFD_RELOC_AVR_16_PM. */ void avr_parse_cons_expression (expressionS *exp, int nbytes) { char *tmp; exp_mod_pm = 0; tmp = input_line_pointer = skip_space (input_line_pointer); if (nbytes == 2) { char *pm_name1 = "pm"; char *pm_name2 = "gs"; int len = strlen (pm_name1); /* len must be the same for both pm identifiers. */ if (strncasecmp (input_line_pointer, pm_name1, len) == 0 || strncasecmp (input_line_pointer, pm_name2, len) == 0) { input_line_pointer = skip_space (input_line_pointer + len); if (*input_line_pointer == '(') { input_line_pointer = skip_space (input_line_pointer + 1); exp_mod_pm = 1; expression (exp); if (*input_line_pointer == ')') ++input_line_pointer; else { as_bad (_("`)' required")); exp_mod_pm = 0; } return; } input_line_pointer = tmp; } } expression (exp); } void avr_cons_fix_new (fragS *frag, int where, int nbytes, expressionS *exp) { if (exp_mod_pm == 0) { if (nbytes == 2) fix_new_exp (frag, where, nbytes, exp, FALSE, BFD_RELOC_16); else if (nbytes == 4) fix_new_exp (frag, where, nbytes, exp, FALSE, BFD_RELOC_32); else as_bad (_("illegal %srelocation size: %d"), "", nbytes); } else { if (nbytes == 2) fix_new_exp (frag, where, nbytes, exp, FALSE, BFD_RELOC_AVR_16_PM); else as_bad (_("illegal %srelocation size: %d"), "`pm' ", nbytes); exp_mod_pm = 0; } }