📄 tc-avr.c
字号:
/* tc-avr.c -- Assembler code for the ATMEL AVR Copyright 1999, 2000 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 2, 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <stdio.h>#include <ctype.h>#include "as.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;};static struct mcu_type_s mcu_types[] ={ {"avr1", AVR_ISA_TINY1, bfd_mach_avr1}, {"avr2", AVR_ISA_2xxx, bfd_mach_avr2}, {"avr3", AVR_ISA_M103, bfd_mach_avr3}, {"avr4", AVR_ISA_M83, bfd_mach_avr4}, {"avr5", AVR_ISA_ALL, bfd_mach_avr5}, {"at90s1200", AVR_ISA_1200, bfd_mach_avr1}, {"attiny10", AVR_ISA_TINY1, 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}, {"attiny22" , AVR_ISA_2xxx, bfd_mach_avr2}, {"at90s2343", AVR_ISA_2xxx, bfd_mach_avr2}, {"at90s4433", AVR_ISA_2xxx, bfd_mach_avr2}, {"at90s4414", AVR_ISA_2xxx, bfd_mach_avr2}, {"at90s4434", AVR_ISA_2xxx, bfd_mach_avr2}, {"at90s8515", AVR_ISA_2xxx, bfd_mach_avr2}, {"at90s8535", AVR_ISA_2xxx, bfd_mach_avr2}, {"at90c8534", AVR_ISA_2xxx, bfd_mach_avr2}, {"atmega603", AVR_ISA_M603, bfd_mach_avr3}, {"atmega103", AVR_ISA_M103, bfd_mach_avr3}, {"atmega83", AVR_ISA_M83, bfd_mach_avr4}, {"atmega85", AVR_ISA_M83, bfd_mach_avr4}, {"atmega161", AVR_ISA_M161, bfd_mach_avr5}, {"atmega163", AVR_ISA_M161, bfd_mach_avr5}, {"atmega32", AVR_ISA_M161, bfd_mach_avr5}, {"at94k", AVR_ISA_94K, bfd_mach_avr5}, {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 dummy);/* 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))static void show_mcu_list PARAMS ((FILE *));static char *skip_space PARAMS ((char *));static char *extract_word PARAMS ((char *, char *, int));static unsigned int avr_operand PARAMS ((struct avr_opcodes_s *, int, char *, char **));static unsigned int avr_operands PARAMS ((struct avr_opcodes_s *, char **));static unsigned int avr_get_constant PARAMS ((char *, int));static char *parse_exp PARAMS ((char *, expressionS *));static bfd_reloc_code_real_type avr_ldi_expression PARAMS ((expressionS *));#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_pmstruct 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_LO8_LDI, -BFD_RELOC_AVR_LO8_LDI_NEG, 0}, {"hhi8", -BFD_RELOC_AVR_HI8_LDI, -BFD_RELOC_AVR_HI8_LDI_NEG, 0},};/* 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'#define OPTION_ALL_OPCODES (OPTION_MD_BASE + 1)#define OPTION_NO_SKIP_BUG (OPTION_MD_BASE + 2)#define OPTION_NO_WRAP (OPTION_MD_BASE + 3)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 voidshow_mcu_list (stream) 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 (s) 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;}intmd_estimate_size_before_relax (fragp, seg) fragS *fragp ATTRIBUTE_UNUSED; asection *seg ATTRIBUTE_UNUSED;{ abort (); return 0;}voidmd_show_usage (stream) 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, ATmega603\n" " avr4 - ATmega83, ATmega85\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 voidavr_set_arch (dummy) int dummy ATTRIBUTE_UNUSED;{ char *str; str = (char *) alloca (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);}intmd_parse_option (c, arg) 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 (name) char *name ATTRIBUTE_UNUSED;{ return 0;}/* Turn a string in input_line_pointer into a floating point constant of type TYPE, and store the appropriate bytes in *LITP. The number of LITTLENUMS emitted is stored in *SIZEP. An error message is returned, or NULL on OK. */char *md_atof (type, litP, sizeP) int type; char *litP; int *sizeP;{ int prec; LITTLENUM_TYPE words[4]; LITTLENUM_TYPE *wordP; char *t; switch (type) { case 'f': prec = 2; break; case 'd': prec = 4; break; default: *sizeP = 0; return _("bad call to md_atof"); } t = atof_ieee (input_line_pointer, type, words); if (t) input_line_pointer = t; *sizeP = prec * sizeof (LITTLENUM_TYPE); /* This loop outputs the LITTLENUMs in REVERSE order. */ for (wordP = words + prec - 1; prec--;) { md_number_to_chars (litP, (valueT) (*wordP--), sizeof (LITTLENUM_TYPE)); litP += sizeof (LITTLENUM_TYPE); } return NULL;}voidmd_convert_frag (abfd, sec, fragP) bfd *abfd ATTRIBUTE_UNUSED; asection *sec ATTRIBUTE_UNUSED; fragS *fragP ATTRIBUTE_UNUSED;{ abort ();}voidmd_begin (){ 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 < sizeof (exp_mod) / sizeof (exp_mod[0]); ++i) hash_insert (avr_mod_hash, EXP_MOD_NAME (i), (void *) (i + 10)); 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 intavr_get_constant (str, max) 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"));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -