OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-old/] [binutils-2.18.50/] [gas/] [config/] [tc-avr.c] - Diff between revs 156 and 816

Only display areas with differences | Details | Blame | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.