binutils-gdb/gas/config/tc-avr.c
Nick Clifton d727e8c26e Add support for attiny261, attiny461, attiny861, attiny25, attiny45,
attiny85, attiny24, attiny44, attiny84, at90pwm2, at90pwm3, atmega164,
atmega324, atmega644, atmega329, atmega3290, atmega649, atmega6490,
atmega406, atmega640, atmega1280, atmega1281, at90can32, at90can64,
at90usb646, at90usb647, at90usb1286 and at90usb1287.
Move atmega48 and atmega88 from AVR_ISA_M8 to AVR_ISA_PWMx.
2006-04-07 15:18:08 +00:00

1382 lines
34 KiB
C

/* tc-avr.c -- Assembler code for the ATMEL AVR
Copyright 1999, 2000, 2001, 2002, 2004, 2005, 2006
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, 51 Franklin Street - Fifth Floor,
Boston, MA 02110-1301, USA. */
#include <stdio.h>
#include "as.h"
#include "safe-ctype.h"
#include "subsegs.h"
#include "libiberty.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_M103, bfd_mach_avr3},
{"avr4", AVR_ISA_M8, bfd_mach_avr4},
{"avr5", AVR_ISA_ALL, bfd_mach_avr5},
{"at90s1200", AVR_ISA_1200, bfd_mach_avr1},
{"attiny10", AVR_ISA_TINY1, bfd_mach_avr1}, /* XXX -> tn11 */
{"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_2xxx, 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_2xxx, 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},
{"atmega603", AVR_ISA_M603, bfd_mach_avr3}, /* XXX -> m103 */
{"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},
{"atmega48", AVR_ISA_PWMx, bfd_mach_avr4},
{"atmega8", AVR_ISA_M8, bfd_mach_avr4},
{"atmega83", AVR_ISA_M8, bfd_mach_avr4}, /* XXX -> m8535 */
{"atmega85", AVR_ISA_M8, bfd_mach_avr4}, /* XXX -> m8 */
{"atmega88", AVR_ISA_PWMx, bfd_mach_avr4},
{"atmega8515",AVR_ISA_M8, bfd_mach_avr4},
{"atmega8535",AVR_ISA_M8, bfd_mach_avr4},
{"at90pwm2", AVR_ISA_PWMx, bfd_mach_avr4},
{"at90pwm3", 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},
{"atmega164", AVR_ISA_M323, bfd_mach_avr5},
{"atmega165", AVR_ISA_M323, bfd_mach_avr5},
{"atmega168", AVR_ISA_M323, bfd_mach_avr5},
{"atmega169", AVR_ISA_M323, bfd_mach_avr5},
{"atmega32", AVR_ISA_M323, bfd_mach_avr5},
{"atmega323", AVR_ISA_M323, bfd_mach_avr5},
{"atmega324", AVR_ISA_M323, bfd_mach_avr5},
{"atmega325", AVR_ISA_M323, bfd_mach_avr5},
{"atmega329", AVR_ISA_M323, bfd_mach_avr5},
{"atmega3250",AVR_ISA_M323, bfd_mach_avr5},
{"atmega3290",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},
{"atmega128", AVR_ISA_M128, bfd_mach_avr5},
{"atmega1280",AVR_ISA_M128, bfd_mach_avr5},
{"atmega1281",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},
{"at90can32" ,AVR_ISA_M323, bfd_mach_avr5},
{"at90can64" ,AVR_ISA_M323, bfd_mach_avr5},
{"at90can128",AVR_ISA_M128, 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},
{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, 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 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;
}
/* 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 (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;
}
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 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;
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 == '(')
{
int neg_p = 0;
++str;
if (strncmp ("pm(", str, 3) == 0
|| strncmp ("-(pm(", str, 5) == 0)
{
if (HAVE_PM_P (mod))
{
++mod;
++closes;
}
else
as_bad (_("illegal expression"));
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--);
return neg_p ? EXP_MOD_NEG_RELOC (mod) : EXP_MOD_RELOC (mod);
}
}
}
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;
}
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)
which is used for addressing to a 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_name = "pm";
int len = strlen (pm_name);
if (strncasecmp (input_line_pointer, pm_name, 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;
}
}