/* tc-msp430.c -- Assembler code for the Texas Instruments MSP430

  Copyright (C) 2002-2013 Free Software Foundation, Inc.
  Contributed by Dmitry Diky <diwil@mail.ru>

  This file is part of GAS, the GNU Assembler.

  GAS is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 3, or (at your option)
  any later version.

  GAS is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with GAS; see the file COPYING.  If not, write to
  the Free Software Foundation, 51 Franklin Street - Fifth Floor,
  Boston, MA 02110-1301, USA.  */

#include "as.h"
#include <limits.h>
#define PUSH_1X_WORKAROUND
#include "subsegs.h"
#include "opcode/msp430.h"
#include "safe-ctype.h"
#include "dwarf2dbg.h"
#include "elf/msp430.h"

/* We will disable polymorphs by default because it is dangerous.
   The potential problem here is the following: assume we got the
   following code:

	jump .l1
	nop
	jump  subroutine	; external symbol
      .l1:
	nop
	ret

   In case of assembly time relaxation we'll get:
	0: jmp .l1 <.text +0x08> (reloc deleted)
	2: nop
	4: br subroutine
    .l1:
	8: nop
	10: ret

   If the 'subroutine' is within +-1024 bytes range then linker
   will produce:
	0: jmp .text +0x08
	2: nop
	4: jmp subroutine
	.l1:
	6: nop
	8: ret	; 'jmp .text +0x08' will land here. WRONG!!!

   The workaround is the following:
   1. Declare global var enable_polymorphs which set to 1 via option -mp.
   2. Declare global var enable_relax	which set to 1 via option -mQ.

   If polymorphs are enabled, and relax isn't, treat all jumps as long jumps,
   do not delete any relocs and leave them for linker.

   If relax is enabled, relax at assembly time and kill relocs as necessary.  */

int msp430_enable_relax;
int msp430_enable_polys;

/*  Set linkrelax here to avoid fixups in most sections.  */
int linkrelax = 1;

/* GCC uses the some condition codes which we'll
   implement as new polymorph instructions.

   COND	EXPL	   SHORT JUMP	LONG JUMP
   ===============================================
   eq	==	   jeq 		jne +4; br lab
   ne	!=	   jne 		jeq +4; br lab

   ltn honours no-overflow flag
   ltn	<	   jn 		jn +2;  jmp +4; br lab

   lt	<	   jl 		jge +4;	br lab
   ltu	<	   jlo 		lhs +4; br lab
   le	<= see below
   leu	<= see below

   gt	>  see below
   gtu	>  see below
   ge	>=	   jge 		jl +4; br lab
   geu	>=	   jhs 		jlo +4; br lab
   ===============================================

   Therefore, new opcodes are (BranchEQ -> beq; and so on...)
   beq,bne,blt,bltn,bltu,bge,bgeu
   'u' means unsigned compares

   Also, we add 'jump' instruction:
   jump	UNCOND	-> jmp		br lab

   They will have fmt == 4, and insn_opnumb == number of instruction.  */

struct rcodes_s
{
  char * name;
  int    index;	/* Corresponding insn_opnumb.  */
  int    sop;	/* Opcode if jump length is short.  */
  long   lpos;	/* Label position.  */
  long   lop0;	/* Opcode 1 _word_ (16 bits).  */
  long   lop1;	/* Opcode second word.  */
  long   lop2;	/* Opcode third word.  */
};

#define MSP430_RLC(n,i,sop,o1) \
  {#n, i, sop, 2, (o1 + 2), 0x4010, 0}

static struct rcodes_s msp430_rcodes[] =
{
  MSP430_RLC (beq,  0, 0x2400, 0x2000),
  MSP430_RLC (bne,  1, 0x2000, 0x2400),
  MSP430_RLC (blt,  2, 0x3800, 0x3400),
  MSP430_RLC (bltu, 3, 0x2800, 0x2c00),
  MSP430_RLC (bge,  4, 0x3400, 0x3800),
  MSP430_RLC (bgeu, 5, 0x2c00, 0x2800),
  {"bltn",          6, 0x3000, 3, 0x3000 + 1, 0x3c00 + 2,0x4010},
  {"jump",          7, 0x3c00, 1, 0x4010, 0, 0},
  {0,0,0,0,0,0,0}
};

#undef  MSP430_RLC
#define MSP430_RLC(n,i,sop,o1) \
  {#n, i, sop, 2, (o1 + 2), 0x0030, 0}

static struct rcodes_s msp430x_rcodes[] =
{
  MSP430_RLC (beq,  0, 0x2400,    0x2000),
  MSP430_RLC (bne,  1, 0x2000,    0x2400),
  MSP430_RLC (blt,  2, 0x3800,    0x3400),
  MSP430_RLC (bltu, 3, 0x2800,    0x2c00),
  MSP430_RLC (bge,  4, 0x3400,    0x3800),
  MSP430_RLC (bgeu, 5, 0x2c00,    0x2800),
  {"bltn",          6, 0x3000, 3, 0x0030 + 1, 0x3c00 + 2, 0x3000},
  {"jump",          7, 0x3c00, 1, 0x0030,     0,          0},
  {0,0,0,0,0,0,0}
};
#undef MSP430_RLC

/* More difficult than above and they have format 5.

   COND	EXPL	SHORT			LONG
   =================================================================
   gt	>	jeq +2; jge label	jeq +6; jl  +4; br label
   gtu	>	jeq +2; jhs label	jeq +6; jlo +4; br label
   leu	<=	jeq label; jlo label	jeq +2; jhs +4; br label
   le	<=	jeq label; jl  label	jeq +2; jge +4; br label
   =================================================================  */

struct hcodes_s
{
  char * name;
  int    index;		/* Corresponding insn_opnumb.  */
  int    tlab;		/* Number of labels in short mode.  */
  int    op0;		/* Opcode for first word of short jump.  */
  int    op1;		/* Opcode for second word of short jump.  */
  int    lop0;		/* Opcodes for long jump mode.  */
  int    lop1;
  int    lop2;
};

static struct hcodes_s msp430_hcodes[] =
{
  {"bgt",  0, 1, 0x2401, 0x3400, 0x2403, 0x3802, 0x4010 },
  {"bgtu", 1, 1, 0x2401, 0x2c00, 0x2403, 0x2802, 0x4010 },
  {"bleu", 2, 2, 0x2400, 0x2800, 0x2401, 0x2c02, 0x4010 },
  {"ble",  3, 2, 0x2400, 0x3800, 0x2401, 0x3402, 0x4010 },
  {0,0,0,0,0,0,0,0}
};

static struct hcodes_s msp430x_hcodes[] =
{
  {"bgt",  0, 1, 0x2401, 0x3400, 0x2403, 0x3802, 0x0030 },
  {"bgtu", 1, 1, 0x2401, 0x2c00, 0x2403, 0x2802, 0x0030 },
  {"bleu", 2, 2, 0x2400, 0x2800, 0x2401, 0x2c02, 0x0030 },
  {"ble",  3, 2, 0x2400, 0x3800, 0x2401, 0x3402, 0x0030 },
  {0,0,0,0,0,0,0,0}
};

const char comment_chars[] = ";";
const char line_comment_chars[] = "#";
const char line_separator_chars[] = "{";
const char EXP_CHARS[] = "eE";
const char FLT_CHARS[] = "dD";

/* Handle  long expressions.  */
extern LITTLENUM_TYPE generic_bignum[];

static struct hash_control *msp430_hash;

/* Relaxations.  */
#define STATE_UNCOND_BRANCH	1	/* jump */
#define STATE_NOOV_BRANCH	3	/* bltn */
#define STATE_SIMPLE_BRANCH	2	/* bne, beq, etc... */
#define STATE_EMUL_BRANCH	4

#define CNRL	2
#define CUBL	4
#define CNOL	8
#define CSBL	6
#define CEBL	4

/* Length.  */
#define STATE_BITS10	1	/* wild guess. short jump */
#define STATE_WORD	2	/* 2 bytes pc rel. addr. more */
#define STATE_UNDEF	3	/* cannot handle this yet. convert to word mode */

#define ENCODE_RELAX(what,length) (((what) << 2) + (length))
#define RELAX_STATE(s)            ((s) & 3)
#define RELAX_LEN(s)	          ((s) >> 2)
#define RELAX_NEXT(a,b)	          ENCODE_RELAX (a, b + 1)

relax_typeS md_relax_table[] =
{
  /* Unused.  */
  {1, 1, 0, 0},
  {1, 1, 0, 0},
  {1, 1, 0, 0},
  {1, 1, 0, 0},

  /* Unconditional jump.  */
  {1, 1, 8, 5},
  {1024, -1024, CNRL, RELAX_NEXT (STATE_UNCOND_BRANCH, STATE_BITS10)},	/* state 10 bits displ */
  {0, 0, CUBL, RELAX_NEXT (STATE_UNCOND_BRANCH, STATE_WORD)},		/* state word */
  {1, 1, CUBL, 0},							/* state undef */

  /* Simple branches.  */
  {0, 0, 8, 9},
  {1024, -1024, CNRL, RELAX_NEXT (STATE_SIMPLE_BRANCH, STATE_BITS10)},	/* state 10 bits displ */
  {0, 0, CSBL, RELAX_NEXT (STATE_SIMPLE_BRANCH, STATE_WORD)},		/* state word */
  {1, 1, CSBL, 0},

  /* blt no overflow branch.  */
  {1, 1, 8, 13},
  {1024, -1024, CNRL, RELAX_NEXT (STATE_NOOV_BRANCH, STATE_BITS10)},	/* state 10 bits displ */
  {0, 0, CNOL, RELAX_NEXT (STATE_NOOV_BRANCH, STATE_WORD)},		/* state word */
  {1, 1, CNOL, 0},

  /* Emulated branches.  */
  {1, 1, 8, 17},
  {1020, -1020, CEBL, RELAX_NEXT (STATE_EMUL_BRANCH, STATE_BITS10)},	/* state 10 bits displ */
  {0, 0, CNOL, RELAX_NEXT (STATE_EMUL_BRANCH, STATE_WORD)},		/* state word */
  {1, 1, CNOL, 0}
};


#define MAX_OP_LEN	256

typedef enum msp_isa
{
  MSP_ISA_430,
  MSP_ISA_430X,
  MSP_ISA_430Xv2
} msp_isa;

struct mcu_type_s
{
  char *  name;
  msp_isa isa;
};

static struct mcu_type_s mcu_types[] =
{
  {"msp430afe221", MSP_ISA_430},
  {"msp430afe222", MSP_ISA_430},
  {"msp430afe223", MSP_ISA_430},
  {"msp430afe231", MSP_ISA_430},
  {"msp430afe232", MSP_ISA_430},
  {"msp430afe233", MSP_ISA_430},
  {"msp430afe251", MSP_ISA_430},
  {"msp430afe252", MSP_ISA_430},
  {"msp430afe253", MSP_ISA_430},
  {"msp430c091",   MSP_ISA_430},
  {"msp430c092",   MSP_ISA_430},
  {"msp430c111",   MSP_ISA_430},
  {"msp430c1111",  MSP_ISA_430},
  {"msp430c112",   MSP_ISA_430},
  {"msp430c1121",  MSP_ISA_430},
  {"msp430e112",   MSP_ISA_430},
  {"msp430c1331", MSP_ISA_430},
  {"msp430c1351", MSP_ISA_430},
  {"msp430c311s", MSP_ISA_430},
  {"msp430c312",  MSP_ISA_430},
  {"msp430c313",  MSP_ISA_430},
  {"msp430c314",  MSP_ISA_430},
  {"msp430c315", MSP_ISA_430},
  {"msp430c323", MSP_ISA_430},
  {"msp430c325", MSP_ISA_430},
  {"msp430c336", MSP_ISA_430},
  {"msp430c337", MSP_ISA_430},
  {"msp430c412", MSP_ISA_430},
  {"msp430c413", MSP_ISA_430},
  {"msp430e313", MSP_ISA_430},
  {"msp430e315", MSP_ISA_430},
  {"msp430e325", MSP_ISA_430},
  {"msp430e337", MSP_ISA_430},
  {"msp430f110", MSP_ISA_430},
  {"msp430f1101", MSP_ISA_430},
  {"msp430f1101a", MSP_ISA_430},
  {"msp430f1111", MSP_ISA_430},
  {"msp430f1111a", MSP_ISA_430},
  {"msp430f112", MSP_ISA_430},
  {"msp430f1121", MSP_ISA_430},
  {"msp430f1121a", MSP_ISA_430},
  {"msp430f1122", MSP_ISA_430},
  {"msp430f1132", MSP_ISA_430},
  {"msp430f122", MSP_ISA_430},
  {"msp430f1222", MSP_ISA_430},
  {"msp430f123", MSP_ISA_430},
  {"msp430f1232", MSP_ISA_430},
  {"msp430f133", MSP_ISA_430},
  {"msp430f135", MSP_ISA_430},
  {"msp430f147", MSP_ISA_430},
  {"msp430f1471", MSP_ISA_430},
  {"msp430f148", MSP_ISA_430},
  {"msp430f1481", MSP_ISA_430},
  {"msp430f149", MSP_ISA_430},
  {"msp430f1491", MSP_ISA_430},
  {"msp430f155", MSP_ISA_430},
  {"msp430f156", MSP_ISA_430},
  {"msp430f157", MSP_ISA_430},
  {"msp430f1610", MSP_ISA_430},
  {"msp430f1611", MSP_ISA_430},
  {"msp430f1612", MSP_ISA_430},
  {"msp430f167", MSP_ISA_430},
  {"msp430f168", MSP_ISA_430},
  {"msp430f169", MSP_ISA_430},
  {"msp430f2001", MSP_ISA_430},
  {"msp430f2002", MSP_ISA_430},
  {"msp430f2003", MSP_ISA_430},
  {"msp430f2011", MSP_ISA_430},
  {"msp430f2012", MSP_ISA_430},
  {"msp430f2013", MSP_ISA_430},
  {"msp430f2101", MSP_ISA_430},
  {"msp430f2111", MSP_ISA_430},
  {"msp430f2112", MSP_ISA_430},
  {"msp430f2121", MSP_ISA_430},
  {"msp430f2122", MSP_ISA_430},
  {"msp430f2131", MSP_ISA_430},
  {"msp430f2132", MSP_ISA_430},
  {"msp430f2232", MSP_ISA_430},
  {"msp430f2234", MSP_ISA_430},
  {"msp430f2252", MSP_ISA_430},
  {"msp430f2254", MSP_ISA_430},
  {"msp430f2272", MSP_ISA_430},
  {"msp430f2274", MSP_ISA_430},
  {"msp430f233", MSP_ISA_430},
  {"msp430f2330", MSP_ISA_430},
  {"msp430f235", MSP_ISA_430},
  {"msp430f2350", MSP_ISA_430},
  {"msp430f2370", MSP_ISA_430},
  {"msp430f2410", MSP_ISA_430},
  {"msp430f247", MSP_ISA_430},
  {"msp430f2471", MSP_ISA_430},
  {"msp430f248", MSP_ISA_430},
  {"msp430f2481", MSP_ISA_430},
  {"msp430f249", MSP_ISA_430},
  {"msp430f2491", MSP_ISA_430},
  {"msp430f412", MSP_ISA_430},
  {"msp430f413", MSP_ISA_430},
  {"msp430f4132", MSP_ISA_430},
  {"msp430f415", MSP_ISA_430},
  {"msp430f4152", MSP_ISA_430},
  {"msp430f417", MSP_ISA_430},
  {"msp430f423", MSP_ISA_430},
  {"msp430f423a", MSP_ISA_430},
  {"msp430f425",  MSP_ISA_430},
  {"msp430f4250", MSP_ISA_430},
  {"msp430f425a", MSP_ISA_430},
  {"msp430f4260", MSP_ISA_430},
  {"msp430f427",  MSP_ISA_430},
  {"msp430f4270", MSP_ISA_430},
  {"msp430f427a", MSP_ISA_430},
  {"msp430f435", MSP_ISA_430},
  {"msp430f4351", MSP_ISA_430},
  {"msp430f436", MSP_ISA_430},
  {"msp430f4361", MSP_ISA_430},
  {"msp430f437", MSP_ISA_430},
  {"msp430f4371", MSP_ISA_430},
  {"msp430f438", MSP_ISA_430},
  {"msp430f439", MSP_ISA_430},
  {"msp430f447", MSP_ISA_430},
  {"msp430f448", MSP_ISA_430},
  {"msp430f4481", MSP_ISA_430},
  {"msp430f449", MSP_ISA_430},
  {"msp430f4491", MSP_ISA_430},
  {"msp430f477", MSP_ISA_430},
  {"msp430f478", MSP_ISA_430},
  {"msp430f4783", MSP_ISA_430},
  {"msp430f4784", MSP_ISA_430},
  {"msp430f479", MSP_ISA_430},
  {"msp430f4793", MSP_ISA_430},
  {"msp430f4794", MSP_ISA_430},
  {"msp430fe423", MSP_ISA_430},
  {"msp430fe4232", MSP_ISA_430},
  {"msp430fe423a", MSP_ISA_430},
  {"msp430fe4242", MSP_ISA_430},
  {"msp430fe425", MSP_ISA_430},
  {"msp430fe4252", MSP_ISA_430},
  {"msp430fe425a", MSP_ISA_430},
  {"msp430fe427", MSP_ISA_430},
  {"msp430fe4272", MSP_ISA_430},
  {"msp430fe427a", MSP_ISA_430},
  {"msp430fg4250", MSP_ISA_430},
  {"msp430fg4260", MSP_ISA_430},
  {"msp430fg4270", MSP_ISA_430},
  {"msp430fg437", MSP_ISA_430},
  {"msp430fg438", MSP_ISA_430},
  {"msp430fg439", MSP_ISA_430},
  {"msp430fg477", MSP_ISA_430},
  {"msp430fg478", MSP_ISA_430},
  {"msp430fg479", MSP_ISA_430},
  {"msp430fw423", MSP_ISA_430},
  {"msp430fw425", MSP_ISA_430},
  {"msp430fw427", MSP_ISA_430},
  {"msp430fw428", MSP_ISA_430},
  {"msp430fw429", MSP_ISA_430},
  {"msp430g2001", MSP_ISA_430},
  {"msp430g2101", MSP_ISA_430},
  {"msp430g2102", MSP_ISA_430},
  {"msp430g2111", MSP_ISA_430},
  {"msp430g2112", MSP_ISA_430},
  {"msp430g2113", MSP_ISA_430},
  {"msp430g2121", MSP_ISA_430},
  {"msp430g2131", MSP_ISA_430},
  {"msp430g2132", MSP_ISA_430},
  {"msp430g2152", MSP_ISA_430},
  {"msp430g2153", MSP_ISA_430},
  {"msp430g2201", MSP_ISA_430},
  {"msp430g2202", MSP_ISA_430},
  {"msp430g2203", MSP_ISA_430},
  {"msp430g2210", MSP_ISA_430},
  {"msp430g2211", MSP_ISA_430},
  {"msp430g2212", MSP_ISA_430},
  {"msp430g2213", MSP_ISA_430},
  {"msp430g2221", MSP_ISA_430},
  {"msp430g2230", MSP_ISA_430},
  {"msp430g2231", MSP_ISA_430},
  {"msp430g2232", MSP_ISA_430},
  {"msp430g2233", MSP_ISA_430},
  {"msp430g2252", MSP_ISA_430},
  {"msp430g2253", MSP_ISA_430},
  {"msp430g2302", MSP_ISA_430},
  {"msp430g2303", MSP_ISA_430},
  {"msp430g2312", MSP_ISA_430},
  {"msp430g2313", MSP_ISA_430},
  {"msp430g2332", MSP_ISA_430},
  {"msp430g2333", MSP_ISA_430},
  {"msp430g2352", MSP_ISA_430},
  {"msp430g2353", MSP_ISA_430},
  {"msp430g2402", MSP_ISA_430},
  {"msp430g2403", MSP_ISA_430},
  {"msp430g2412", MSP_ISA_430},
  {"msp430g2413", MSP_ISA_430},
  {"msp430g2432", MSP_ISA_430},
  {"msp430g2433", MSP_ISA_430},
  {"msp430g2444", MSP_ISA_430},
  {"msp430g2452", MSP_ISA_430},
  {"msp430g2453", MSP_ISA_430},
  {"msp430g2513", MSP_ISA_430},
  {"msp430g2533", MSP_ISA_430},
  {"msp430g2544", MSP_ISA_430},
  {"msp430g2553", MSP_ISA_430},
  {"msp430g2744", MSP_ISA_430},
  {"msp430g2755", MSP_ISA_430},
  {"msp430g2855", MSP_ISA_430},
  {"msp430g2955", MSP_ISA_430},
  {"msp430l092",  MSP_ISA_430},
  {"msp430p112",  MSP_ISA_430},
  {"msp430p313",  MSP_ISA_430},
  {"msp430p315",  MSP_ISA_430},
  {"msp430p315s", MSP_ISA_430},
  {"msp430p325",  MSP_ISA_430},
  {"msp430p337",  MSP_ISA_430},
  {"msp430tch5e", MSP_ISA_430},

  {"msp430cg4616", MSP_ISA_430X},
  {"msp430cg4617", MSP_ISA_430X},
  {"msp430cg4618", MSP_ISA_430X},
  {"msp430cg4619", MSP_ISA_430X},
  {"msp430f2416", MSP_ISA_430X},
  {"msp430f2417", MSP_ISA_430X},
  {"msp430f2418", MSP_ISA_430X},
  {"msp430f2419", MSP_ISA_430X},
  {"msp430f2616", MSP_ISA_430X},
  {"msp430f2617", MSP_ISA_430X},
  {"msp430f2618", MSP_ISA_430X},
  {"msp430f2619", MSP_ISA_430X},
  {"msp430f47126", MSP_ISA_430X},
  {"msp430f47127", MSP_ISA_430X},
  {"msp430f47163", MSP_ISA_430X},
  {"msp430f47173", MSP_ISA_430X},
  {"msp430f47183", MSP_ISA_430X},
  {"msp430f47193", MSP_ISA_430X},
  {"msp430f47166", MSP_ISA_430X},
  {"msp430f47176", MSP_ISA_430X},
  {"msp430f47186", MSP_ISA_430X},
  {"msp430f47196", MSP_ISA_430X},
  {"msp430f47167", MSP_ISA_430X},
  {"msp430f47177", MSP_ISA_430X},
  {"msp430f47187", MSP_ISA_430X},
  {"msp430f47197", MSP_ISA_430X},
  {"msp430f46161", MSP_ISA_430X},
  {"msp430f46171", MSP_ISA_430X},
  {"msp430f46181", MSP_ISA_430X},
  {"msp430f46191", MSP_ISA_430X},
  {"msp430f4616", MSP_ISA_430X},
  {"msp430f4617", MSP_ISA_430X},
  {"msp430f4618", MSP_ISA_430X},
  {"msp430f4619", MSP_ISA_430X},
  {"msp430fg4616", MSP_ISA_430X},
  {"msp430fg4617", MSP_ISA_430X},
  {"msp430fg4618", MSP_ISA_430X},
  {"msp430fg4619", MSP_ISA_430X},

  {"msp430f5418", MSP_ISA_430Xv2},
  {"msp430f5419", MSP_ISA_430Xv2},
  {"msp430f5435", MSP_ISA_430Xv2},
  {"msp430f5436", MSP_ISA_430Xv2},
  {"msp430f5437", MSP_ISA_430Xv2},
  {"msp430f5438", MSP_ISA_430Xv2},
  {"msp430f5418a", MSP_ISA_430Xv2},
  {"msp430f5419a", MSP_ISA_430Xv2},
  {"msp430f5435a", MSP_ISA_430Xv2},
  {"msp430f5436a", MSP_ISA_430Xv2},
  {"msp430f5437a", MSP_ISA_430Xv2},
  {"msp430f5438a", MSP_ISA_430Xv2},
  {"msp430f5212", MSP_ISA_430Xv2},
  {"msp430f5213", MSP_ISA_430Xv2},
  {"msp430f5214", MSP_ISA_430Xv2},
  {"msp430f5217", MSP_ISA_430Xv2},
  {"msp430f5218", MSP_ISA_430Xv2},
  {"msp430f5219", MSP_ISA_430Xv2},
  {"msp430f5222", MSP_ISA_430Xv2},
  {"msp430f5223", MSP_ISA_430Xv2},
  {"msp430f5224", MSP_ISA_430Xv2},
  {"msp430f5227", MSP_ISA_430Xv2},
  {"msp430f5228", MSP_ISA_430Xv2},
  {"msp430f5229", MSP_ISA_430Xv2},
  {"msp430f5304", MSP_ISA_430Xv2},
  {"msp430f5308", MSP_ISA_430Xv2},
  {"msp430f5309", MSP_ISA_430Xv2},
  {"msp430f5310", MSP_ISA_430Xv2},
  {"msp430f5340", MSP_ISA_430Xv2},
  {"msp430f5341", MSP_ISA_430Xv2},
  {"msp430f5342", MSP_ISA_430Xv2},
  {"msp430f5324", MSP_ISA_430Xv2},
  {"msp430f5325", MSP_ISA_430Xv2},
  {"msp430f5326", MSP_ISA_430Xv2},
  {"msp430f5327", MSP_ISA_430Xv2},
  {"msp430f5328", MSP_ISA_430Xv2},
  {"msp430f5329", MSP_ISA_430Xv2},
  {"msp430f5500", MSP_ISA_430Xv2},
  {"msp430f5501", MSP_ISA_430Xv2},
  {"msp430f5502", MSP_ISA_430Xv2},
  {"msp430f5503", MSP_ISA_430Xv2},
  {"msp430f5504", MSP_ISA_430Xv2},
  {"msp430f5505", MSP_ISA_430Xv2},
  {"msp430f5506", MSP_ISA_430Xv2},
  {"msp430f5507", MSP_ISA_430Xv2},
  {"msp430f5508", MSP_ISA_430Xv2},
  {"msp430f5509", MSP_ISA_430Xv2},
  {"msp430f5510", MSP_ISA_430Xv2},
  {"msp430f5513", MSP_ISA_430Xv2},
  {"msp430f5514", MSP_ISA_430Xv2},
  {"msp430f5515", MSP_ISA_430Xv2},
  {"msp430f5517", MSP_ISA_430Xv2},
  {"msp430f5519", MSP_ISA_430Xv2},
  {"msp430f5521", MSP_ISA_430Xv2},
  {"msp430f5522", MSP_ISA_430Xv2},
  {"msp430f5524", MSP_ISA_430Xv2},
  {"msp430f5525", MSP_ISA_430Xv2},
  {"msp430f5526", MSP_ISA_430Xv2},
  {"msp430f5527", MSP_ISA_430Xv2},
  {"msp430f5528", MSP_ISA_430Xv2},
  {"msp430f5529", MSP_ISA_430Xv2},
  {"cc430f5133", MSP_ISA_430Xv2},
  {"cc430f5135", MSP_ISA_430Xv2},
  {"cc430f5137", MSP_ISA_430Xv2},
  {"cc430f6125", MSP_ISA_430Xv2},
  {"cc430f6126", MSP_ISA_430Xv2},
  {"cc430f6127", MSP_ISA_430Xv2},
  {"cc430f6135", MSP_ISA_430Xv2},
  {"cc430f6137", MSP_ISA_430Xv2},
  {"cc430f5123", MSP_ISA_430Xv2},
  {"cc430f5125", MSP_ISA_430Xv2},
  {"cc430f5143", MSP_ISA_430Xv2},
  {"cc430f5145", MSP_ISA_430Xv2},
  {"cc430f5147", MSP_ISA_430Xv2},
  {"cc430f6143", MSP_ISA_430Xv2},
  {"cc430f6145", MSP_ISA_430Xv2},
  {"cc430f6147", MSP_ISA_430Xv2},
  {"msp430f5333", MSP_ISA_430Xv2},
  {"msp430f5335", MSP_ISA_430Xv2},
  {"msp430f5336", MSP_ISA_430Xv2},
  {"msp430f5338", MSP_ISA_430Xv2},
  {"msp430f5630", MSP_ISA_430Xv2},
  {"msp430f5631", MSP_ISA_430Xv2},
  {"msp430f5632", MSP_ISA_430Xv2},
  {"msp430f5633", MSP_ISA_430Xv2},
  {"msp430f5634", MSP_ISA_430Xv2},
  {"msp430f5635", MSP_ISA_430Xv2},
  {"msp430f5636", MSP_ISA_430Xv2},
  {"msp430f5637", MSP_ISA_430Xv2},
  {"msp430f5638", MSP_ISA_430Xv2},
  {"msp430f6433", MSP_ISA_430Xv2},
  {"msp430f6435", MSP_ISA_430Xv2},
  {"msp430f6436", MSP_ISA_430Xv2},
  {"msp430f6438", MSP_ISA_430Xv2},
  {"msp430f6630", MSP_ISA_430Xv2},
  {"msp430f6631", MSP_ISA_430Xv2},
  {"msp430f6632", MSP_ISA_430Xv2},
  {"msp430f6633", MSP_ISA_430Xv2},
  {"msp430f6634", MSP_ISA_430Xv2},
  {"msp430f6635", MSP_ISA_430Xv2},
  {"msp430f6636", MSP_ISA_430Xv2},
  {"msp430f6637", MSP_ISA_430Xv2},
  {"msp430f6638", MSP_ISA_430Xv2},
  {"msp430f5358", MSP_ISA_430Xv2},
  {"msp430f5359", MSP_ISA_430Xv2},
  {"msp430f5658", MSP_ISA_430Xv2},
  {"msp430f5659", MSP_ISA_430Xv2},
  {"msp430f6458", MSP_ISA_430Xv2},
  {"msp430f6459", MSP_ISA_430Xv2},
  {"msp430f6658", MSP_ISA_430Xv2},
  {"msp430f6659", MSP_ISA_430Xv2},
  {"msp430f5131", MSP_ISA_430Xv2},
  {"msp430f5151", MSP_ISA_430Xv2},
  {"msp430f5171", MSP_ISA_430Xv2},
  {"msp430f5132", MSP_ISA_430Xv2},
  {"msp430f5152", MSP_ISA_430Xv2},
  {"msp430f5172", MSP_ISA_430Xv2},
  {"msp430f6720", MSP_ISA_430Xv2},
  {"msp430f6721", MSP_ISA_430Xv2},
  {"msp430f6723", MSP_ISA_430Xv2},
  {"msp430f6724", MSP_ISA_430Xv2},
  {"msp430f6725", MSP_ISA_430Xv2},
  {"msp430f6726", MSP_ISA_430Xv2},
  {"msp430f6730", MSP_ISA_430Xv2},
  {"msp430f6731", MSP_ISA_430Xv2},
  {"msp430f6733", MSP_ISA_430Xv2},
  {"msp430f6734", MSP_ISA_430Xv2},
  {"msp430f6735", MSP_ISA_430Xv2},
  {"msp430f6736", MSP_ISA_430Xv2},
  {"msp430f67451", MSP_ISA_430Xv2},
  {"msp430f67651", MSP_ISA_430Xv2},
  {"msp430f67751", MSP_ISA_430Xv2},
  {"msp430f67461", MSP_ISA_430Xv2},
  {"msp430f67661", MSP_ISA_430Xv2},
  {"msp430f67761", MSP_ISA_430Xv2},
  {"msp430f67471", MSP_ISA_430Xv2},
  {"msp430f67671", MSP_ISA_430Xv2},
  {"msp430f67771", MSP_ISA_430Xv2},
  {"msp430f67481", MSP_ISA_430Xv2},
  {"msp430f67681", MSP_ISA_430Xv2},
  {"msp430f67781", MSP_ISA_430Xv2},
  {"msp430f67491", MSP_ISA_430Xv2},
  {"msp430f67691", MSP_ISA_430Xv2},
  {"msp430f67791", MSP_ISA_430Xv2},
  {"msp430f6745", MSP_ISA_430Xv2},
  {"msp430f6765", MSP_ISA_430Xv2},
  {"msp430f6775", MSP_ISA_430Xv2},
  {"msp430f6746", MSP_ISA_430Xv2},
  {"msp430f6766", MSP_ISA_430Xv2},
  {"msp430f6776", MSP_ISA_430Xv2},
  {"msp430f6747", MSP_ISA_430Xv2},
  {"msp430f6767", MSP_ISA_430Xv2},
  {"msp430f6777", MSP_ISA_430Xv2},
  {"msp430f6748", MSP_ISA_430Xv2},
  {"msp430f6768", MSP_ISA_430Xv2},
  {"msp430f6778", MSP_ISA_430Xv2},
  {"msp430f6749", MSP_ISA_430Xv2},
  {"msp430f6769", MSP_ISA_430Xv2},
  {"msp430f6779", MSP_ISA_430Xv2},
  {"msp430fr5720", MSP_ISA_430Xv2},
  {"msp430fr5721", MSP_ISA_430Xv2},
  {"msp430fr5722", MSP_ISA_430Xv2},
  {"msp430fr5723", MSP_ISA_430Xv2},
  {"msp430fr5724", MSP_ISA_430Xv2},
  {"msp430fr5725", MSP_ISA_430Xv2},
  {"msp430fr5726", MSP_ISA_430Xv2},
  {"msp430fr5727", MSP_ISA_430Xv2},
  {"msp430fr5728", MSP_ISA_430Xv2},
  {"msp430fr5729", MSP_ISA_430Xv2},
  {"msp430fr5730", MSP_ISA_430Xv2},
  {"msp430fr5731", MSP_ISA_430Xv2},
  {"msp430fr5732", MSP_ISA_430Xv2},
  {"msp430fr5733", MSP_ISA_430Xv2},
  {"msp430fr5734", MSP_ISA_430Xv2},
  {"msp430fr5735", MSP_ISA_430Xv2},
  {"msp430fr5736", MSP_ISA_430Xv2},
  {"msp430fr5737", MSP_ISA_430Xv2},
  {"msp430fr5738", MSP_ISA_430Xv2},
  {"msp430fr5739", MSP_ISA_430Xv2},
  {"msp430bt5190", MSP_ISA_430Xv2},
  {"msp430fr5949", MSP_ISA_430Xv2},
  {"msp430fr5969", MSP_ISA_430Xv2},
  {"msp430sl5438a", MSP_ISA_430Xv2},

  /* Generic names.  */
  {"msp430",    MSP_ISA_430},
  {"msp430X",   MSP_ISA_430X},
  {"msp430Xv2", MSP_ISA_430Xv2},
    
  {NULL, 0}
};

static struct mcu_type_s default_mcu   = { "msp430x11", MSP_ISA_430 };
static struct mcu_type_s msp430x_mcu   = { "msp430x",   MSP_ISA_430X };
static struct mcu_type_s msp430xv2_mcu = { "msp430xv2", MSP_ISA_430Xv2 };

static struct mcu_type_s * msp430_mcu = & default_mcu;

static inline bfd_boolean
target_is_430x (void)
{
  return msp430_mcu->isa >= MSP_ISA_430X;
}

static inline bfd_boolean
target_is_430xv2 (void)
{
  return msp430_mcu->isa == MSP_ISA_430Xv2;
}

/* Generate a 16-bit relocation.
   For the 430X we generate a relocation without linkwer range checking
   if the value is being used in an extended (ie 20-bit) instruction.
   For the 430 we generate a relocation without assembler range checking
   if we are handling an immediate value or a byte-width instruction.  */
#undef  CHECK_RELOC_MSP430
#define CHECK_RELOC_MSP430				     \
  (target_is_430x ()					     \
   ? (extended_op ? BFD_RELOC_16 : BFD_RELOC_MSP430X_ABS16)  \
   : ((imm_op || byte_op)				     \
      ? BFD_RELOC_MSP430_16_BYTE : BFD_RELOC_MSP430_16))

/* Generate a 16-bit pc-relative relocation.
   For the 430X we generate a relocation without linkwer range checking.
   For the 430 we generate a relocation without assembler range checking
   if we are handling an immediate value or a byte-width instruction.  */
#undef  CHECK_RELOC_MSP430_PCREL
#define CHECK_RELOC_MSP430_PCREL			     \
  (target_is_430x ()					     \
   ? BFD_RELOC_MSP430X_PCR16				     \
   : (imm_op || byte_op)				     \
   ? BFD_RELOC_MSP430_16_PCREL_BYTE : BFD_RELOC_MSP430_16_PCREL)

/* Profiling capability:
   It is a performance hit to use gcc's profiling approach for this tiny target.
   Even more -- jtag hardware facility does not perform any profiling functions.
   However we've got gdb's built-in simulator where we can do anything.
   Therefore my suggestion is:

   We define new section ".profiler" which holds all profiling information.
   We define new pseudo operation .profiler which will instruct assembler to
   add new profile entry to the object file. Profile should take place at the
   present address.

   Pseudo-op format:

      .profiler flags,function_to_profile [, cycle_corrector, extra]

   where 'flags' is a combination of the following chars:
	    s - function Start
	    x - function eXit
	    i - function is in Init section
	    f - function is in Fini section
	    l - Library call
	    c - libC standard call
	    d - stack value Demand (saved at run-time in simulator)
	    I - Interrupt service routine
	    P - Prologue start
	    p - Prologue end
	    E - Epilogue start
	    e - Epilogue end
	    j - long Jump/ sjlj unwind
	    a - an Arbitrary code fragment
	    t - exTra parameter saved (constant value like frame size)
	  '""' optional: "sil" == sil

      function_to_profile - function's address
      cycle_corrector     - a value which should be added to the cycle
			      counter, zero if omitted
      extra - some extra parameter, zero if omitted.

      For example:
      ------------------------------
	.global fxx
	.type fxx,@function
      fxx:
      .LFrameOffset_fxx=0x08
      .profiler "scdP", fxx	; function entry.
				; we also demand stack value to be displayed
	push r11
	push r10
	push r9
	push r8
      .profiler "cdp",fxx,0, .LFrameOffset_fxx	; check stack value at this point
						; (this is a prologue end)
						; note, that spare var filled with the frame size
	mov r15,r8
	....
      .profiler cdE,fxx		; check stack
	pop r8
	pop r9
	pop r10
	pop r11
      .profiler xcde,fxx,3	; exit adds 3 to the cycle counter
      ret			; cause 'ret' insn takes 3 cycles
      -------------------------------

      This profiling approach does not produce any overhead and
      absolutely harmless.
      So, even profiled code can be uploaded to the MCU.  */
#define MSP430_PROFILER_FLAG_ENTRY	1	/* s */
#define MSP430_PROFILER_FLAG_EXIT	2	/* x */
#define MSP430_PROFILER_FLAG_INITSECT	4	/* i */
#define MSP430_PROFILER_FLAG_FINISECT	8	/* f */
#define MSP430_PROFILER_FLAG_LIBCALL	0x10	/* l */
#define MSP430_PROFILER_FLAG_STDCALL	0x20	/* c */
#define MSP430_PROFILER_FLAG_STACKDMD	0x40	/* d */
#define MSP430_PROFILER_FLAG_ISR	0x80	/* I */
#define MSP430_PROFILER_FLAG_PROLSTART	0x100	/* P */
#define MSP430_PROFILER_FLAG_PROLEND	0x200	/* p */
#define MSP430_PROFILER_FLAG_EPISTART	0x400	/* E */
#define MSP430_PROFILER_FLAG_EPIEND	0x800	/* e */
#define MSP430_PROFILER_FLAG_JUMP	0x1000	/* j */
#define MSP430_PROFILER_FLAG_FRAGMENT	0x2000	/* a */
#define MSP430_PROFILER_FLAG_EXTRA	0x4000	/* t */
#define MSP430_PROFILER_FLAG_notyet	0x8000	/* ? */

static int
pow2value (int y)
{
  int n = 0;
  unsigned int x;

  x = y;

  if (!x)
    return 1;

  for (; x; x = x >> 1)
    if (x & 1)
      n++;

  return n == 1;
}

/* 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;
}


/* Delete spaces from s: X ( r 1  2)  => X(r12).  */

static void
del_spaces (char * s)
{
  while (*s)
    {
      if (ISSPACE (*s))
	{
	  char *m = s + 1;

	  while (ISSPACE (*m) && *m)
	    m++;
	  memmove (s, m, strlen (m) + 1);
	}
      else
	s++;
    }
}

static inline char *
skip_space (char * s)
{
  while (ISSPACE (*s))
    ++s;
  return s;
}

/* Extract one word from FROM and copy it to TO. Delimiters are ",;\n"  */

static char *
extract_operand (char * from, char * to, int limit)
{
  int size = 0;

  /* Drop leading whitespace.  */
  from = skip_space (from);

  while (size < limit && *from)
    {
      *(to + size) = *from;
      if (*from == ',' || *from == ';' || *from == '\n')
	break;
      from++;
      size++;
    }

  *(to + size) = 0;
  del_spaces (to);

  from++;

  return from;
}

static void
msp430_profiler (int dummy ATTRIBUTE_UNUSED)
{
  char   buffer[1024];
  char   f[32];
  char * str = buffer;
  char * flags = f;
  int    p_flags = 0;
  char * halt;
  int    ops = 0;
  int    left;
  char * s;
  segT   seg;
  int    subseg;
  char * end = 0;
  expressionS exp;
  expressionS exp1;

  s = input_line_pointer;
  end = input_line_pointer;

  while (*end && *end != '\n')
    end++;

  while (*s && *s != '\n')
    {
      if (*s == ',')
	ops++;
      s++;
    }

  left = 3 - ops;

  if (ops < 1)
    {
      as_bad (_(".profiler pseudo requires at least two operands."));
      input_line_pointer = end;
      return;
    }

  input_line_pointer = extract_operand (input_line_pointer, flags, 32);

  while (*flags)
    {
      switch (*flags)
	{
	case '"':
	  break;
	case 'a':
	  p_flags |= MSP430_PROFILER_FLAG_FRAGMENT;
	  break;
	case 'j':
	  p_flags |= MSP430_PROFILER_FLAG_JUMP;
	  break;
	case 'P':
	  p_flags |= MSP430_PROFILER_FLAG_PROLSTART;
	  break;
	case 'p':
	  p_flags |= MSP430_PROFILER_FLAG_PROLEND;
	  break;
	case 'E':
	  p_flags |= MSP430_PROFILER_FLAG_EPISTART;
	  break;
	case 'e':
	  p_flags |= MSP430_PROFILER_FLAG_EPIEND;
	  break;
	case 's':
	  p_flags |= MSP430_PROFILER_FLAG_ENTRY;
	  break;
	case 'x':
	  p_flags |= MSP430_PROFILER_FLAG_EXIT;
	  break;
	case 'i':
	  p_flags |= MSP430_PROFILER_FLAG_INITSECT;
	  break;
	case 'f':
	  p_flags |= MSP430_PROFILER_FLAG_FINISECT;
	  break;
	case 'l':
	  p_flags |= MSP430_PROFILER_FLAG_LIBCALL;
	  break;
	case 'c':
	  p_flags |= MSP430_PROFILER_FLAG_STDCALL;
	  break;
	case 'd':
	  p_flags |= MSP430_PROFILER_FLAG_STACKDMD;
	  break;
	case 'I':
	  p_flags |= MSP430_PROFILER_FLAG_ISR;
	  break;
	case 't':
	  p_flags |= MSP430_PROFILER_FLAG_EXTRA;
	  break;
	default:
	  as_warn (_("unknown profiling flag - ignored."));
	  break;
	}
      flags++;
    }

  if (p_flags
      && (   ! pow2value (p_flags & (  MSP430_PROFILER_FLAG_ENTRY
				     | MSP430_PROFILER_FLAG_EXIT))
	  || ! pow2value (p_flags & (  MSP430_PROFILER_FLAG_PROLSTART
				     | MSP430_PROFILER_FLAG_PROLEND
				     | MSP430_PROFILER_FLAG_EPISTART
				     | MSP430_PROFILER_FLAG_EPIEND))
	  || ! pow2value (p_flags & (  MSP430_PROFILER_FLAG_INITSECT
				     | MSP430_PROFILER_FLAG_FINISECT))))
    {
      as_bad (_("ambiguous flags combination - '.profiler' directive ignored."));
      input_line_pointer = end;
      return;
    }

  /* Generate temp symbol which denotes current location.  */
  if (now_seg == absolute_section)	/* Paranoia ?  */
    {
      exp1.X_op = O_constant;
      exp1.X_add_number = abs_section_offset;
      as_warn (_("profiling in absolute section?"));
    }
  else
    {
      exp1.X_op = O_symbol;
      exp1.X_add_symbol = symbol_temp_new_now ();
      exp1.X_add_number = 0;
    }

  /* Generate a symbol which holds flags value.  */
  exp.X_op = O_constant;
  exp.X_add_number = p_flags;

  /* Save current section.  */
  seg = now_seg;
  subseg = now_subseg;

  /* Now go to .profiler section.  */
  obj_elf_change_section (".profiler", SHT_PROGBITS, 0, 0, 0, 0, 0);

  /* Save flags.  */
  emit_expr (& exp, 2);

  /* Save label value.  */
  emit_expr (& exp1, 2);

  while (ops--)
    {
      /* Now get profiling info.  */
      halt = extract_operand (input_line_pointer, str, 1024);
      /* Process like ".word xxx" directive.  */
      parse_exp (str, & exp);
      emit_expr (& exp, 2);
      input_line_pointer = halt;
    }

  /* Fill the rest with zeros.  */
  exp.X_op = O_constant;
  exp.X_add_number = 0;
  while (left--)
    emit_expr (& exp, 2);

  /* Return to current section.  */
  subseg_set (seg, subseg);
}

static char *
extract_word (char * from, char * to, int limit)
{
  char *op_end;
  int size = 0;

  /* Drop leading whitespace.  */
  from = skip_space (from);
  *to = 0;

  /* Find the op code end.  */
  for (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;
}

#define OPTION_MMCU 'm'
#define OPTION_RELAX 'Q'
#define OPTION_POLYMORPHS 'P'
#define OPTION_LARGE 'l'
static bfd_boolean large_model = FALSE;
#define OPTION_NO_INTR_NOPS 'N'
static bfd_boolean gen_interrupt_nops = TRUE;
#define OPTION_MCPU 'c'
#define OPTION_MOVE_DATA 'd'
static bfd_boolean move_data = FALSE;

static void
msp430_set_arch (int option)
{
  char *str = (char *) alloca (32);	/* 32 for good measure.  */

  input_line_pointer = extract_word (input_line_pointer, str, 32);

  md_parse_option (option, str);
  bfd_set_arch_mach (stdoutput, TARGET_ARCH,
		     target_is_430x () ? bfd_mach_msp430x : bfd_mach_msp11);
}

static void
show_mcu_list (FILE * stream)
{
  int i;

  fprintf (stream, _("Known MCU names:\n"));

  for (i = 0; mcu_types[i].name; i++)
    {
      fprintf (stream, "%14.14s", mcu_types[i].name);
      if ((i % 6) == 5)
	fprintf (stream, "\n");
    }

  fprintf (stream, "\n");
}

int
md_parse_option (int c, char * arg)
{
  int i;

  switch (c)
    {
    case OPTION_MMCU:
      if (arg == NULL)
	as_fatal (_("MCU option requires a name\n"));

      for (i = 0; mcu_types[i].name; ++i)
	if (strcasecmp (mcu_types[i].name, arg) == 0)
	  break;

      if (mcu_types[i].name == NULL)
	{
	  show_mcu_list (stderr);
	  as_fatal (_("unknown MCU: %s\n"), arg);
	}

      /* Allow switching to the same or a lesser architecture.  */
      if (msp430_mcu == &default_mcu || msp430_mcu->isa >= mcu_types[i].isa)
	msp430_mcu = mcu_types + i;
      else
	as_fatal (_("redefinition of mcu type '%s' to '%s'"),
		  msp430_mcu->name, mcu_types[i].name);
      return 1;

    case OPTION_MCPU:
      if (strcmp (arg, "430") == 0)
	msp430_mcu = & default_mcu;
      else if (strcmp (arg, "430x") == 0
	       || strcmp (arg, "430X") == 0)
	msp430_mcu = & msp430x_mcu;
      else if (strcasecmp (arg, "430xv2") == 0)
	msp430_mcu = & msp430xv2_mcu;
      else
	as_fatal (_("unrecognised argument to -mcpu option '%s'"), arg);

      return 1;
      
    case OPTION_RELAX:
      msp430_enable_relax = 1;
      return 1;

    case OPTION_POLYMORPHS:
      msp430_enable_polys = 1;
      return 1;

    case OPTION_LARGE:
      large_model = TRUE;
      return 1;

    case OPTION_NO_INTR_NOPS:
      gen_interrupt_nops = FALSE;
      return 1;

    case OPTION_MOVE_DATA:
      move_data = TRUE;
      return 1;
    }

  return 0;
}

static void
msp430_section (int arg)
{
  char * saved_ilp = input_line_pointer;
  char * name = obj_elf_section_name ();

  if (strncmp (name, ".bss", 4) == 0
      || strncmp (name, ".gnu.linkonce.b.", 16) == 0)
    (void) symbol_find_or_make ("__crt0_init_bss");

  if (move_data
      && (strncmp (name, ".data", 5) == 0
	  || strncmp (name, ".gnu.linkonce.d.", 16) == 0))
    (void) symbol_find_or_make ("__crt0_movedata");

  input_line_pointer = saved_ilp;
  obj_elf_section (arg);
}

const pseudo_typeS md_pseudo_table[] =
{
  {"arch", msp430_set_arch, OPTION_MMCU},
  {"cpu", msp430_set_arch, OPTION_MCPU},
  {"profiler", msp430_profiler, 0},
  {"section", msp430_section, 0},
  {"section.s", msp430_section, 0},
  {"sect", msp430_section, 0},
  {"sect.s", msp430_section, 0},
  {"pushsection", msp430_section, 1},
  {NULL, NULL, 0}
};

const char *md_shortopts = "mm:,mP,mQ,ml,mN";

struct option md_longopts[] =
{
  {"mmcu", required_argument, NULL, OPTION_MMCU},
  {"mcpu", required_argument, NULL, OPTION_MCPU},
  {"mP", no_argument, NULL, OPTION_POLYMORPHS},
  {"mQ", no_argument, NULL, OPTION_RELAX},
  {"ml", no_argument, NULL, OPTION_LARGE},
  {"mN", no_argument, NULL, OPTION_NO_INTR_NOPS},
  {"md", no_argument, NULL, OPTION_MOVE_DATA},
  {NULL, no_argument, NULL, 0}
};

size_t md_longopts_size = sizeof (md_longopts);

void
md_show_usage (FILE * stream)
{
  fprintf (stream,
	   _("MSP430 options:\n"
	     "  -mmcu=<msp430-name>     - select microcontroller type\n"
             "  -mcpu={430|430x|430xv2} - select microcontroller architecture\n"));
  fprintf (stream,
	   _("  -mQ - enable relaxation at assembly time. DANGEROUS!\n"
	     "  -mP - enable polymorph instructions\n"));
  fprintf (stream,
	   _("  -ml - enable large code model\n"));
  fprintf (stream,
	   _("  -mN - disable generation of NOP after changing interrupts\n"));
  fprintf (stream,
	   _("  -md - Force copying of data from ROM to RAM at startup\n"));

  show_mcu_list (stream);
}

symbolS *
md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
{
  return NULL;
}

static char *
extract_cmd (char * from, char * to, int limit)
{
  int size = 0;

  while (*from && ! ISSPACE (*from) && *from != '.' && limit > size)
    {
      *(to + size) = *from;
      from++;
      size++;
    }

  *(to + size) = 0;

  return from;
}

char *
md_atof (int type, char * litP, int * sizeP)
{
  return ieee_md_atof (type, litP, sizeP, FALSE);
}

void
md_begin (void)
{
  struct msp430_opcode_s * opcode;
  msp430_hash = hash_new ();

  for (opcode = msp430_opcodes; opcode->name; opcode++)
    hash_insert (msp430_hash, opcode->name, (char *) opcode);

  bfd_set_arch_mach (stdoutput, TARGET_ARCH,
		     target_is_430x () ? bfd_mach_msp430x : bfd_mach_msp11);
}

/* Returns the register number equivalent to the string T.
   Returns -1 if there is no such register.
   Skips a leading 'r' or 'R' character if there is one.
   Handles the register aliases PC and SP.  */

static signed int
check_reg (char * t)
{
  signed int val;

  if (t == NULL)
    return -1;

  if (*t == 'r' || *t == 'R')
    ++t;

  if (strncasecmp (t, "pc", 2) == 0)
    return 0;

  if (strncasecmp (t, "sp", 2) == 0)
    return 1;

  if (strncasecmp (t, "sr", 2) == 0)
    return 2;

  if (*t == '0')
    return 0;

  val = atoi (t);

  if (val < 1 || val > 15)
    return -1;

  return val;
}

static int
msp430_srcoperand (struct msp430_operand_s * op,
		   char * l,
		   int bin,
		   int * imm_op,
		   bfd_boolean allow_20bit_values,
		   bfd_boolean constants_allowed)
{
  char *__tl = l;

  /* Check if an immediate #VALUE.  The hash sign should be only at the beginning!  */
  if (*l == '#')
    {
      char *h = l;
      int vshift = -1;
      int rval = 0;

      /* Check if there is:
	 llo(x) - least significant 16 bits, x &= 0xffff
	 lhi(x) - x = (x >> 16) & 0xffff,
	 hlo(x) - x = (x >> 32) & 0xffff,
	 hhi(x) - x = (x >> 48) & 0xffff
	 The value _MUST_ be constant expression: #hlo(1231231231).  */

      *imm_op = 1;

      if (strncasecmp (h, "#llo(", 5) == 0)
	{
	  vshift = 0;
	  rval = 3;
	}
      else if (strncasecmp (h, "#lhi(", 5) == 0)
	{
	  vshift = 1;
	  rval = 3;
	}
      else if (strncasecmp (h, "#hlo(", 5) == 0)
	{
	  vshift = 2;
	  rval = 3;
	}
      else if (strncasecmp (h, "#hhi(", 5) == 0)
	{
	  vshift = 3;
	  rval = 3;
	}
      else if (strncasecmp (h, "#lo(", 4) == 0)
	{
	  vshift = 0;
	  rval = 2;
	}
      else if (strncasecmp (h, "#hi(", 4) == 0)
	{
	  vshift = 1;
	  rval = 2;
	}

      op->reg = 0;		/* Reg PC.  */
      op->am = 3;
      op->ol = 1;		/* Immediate  will follow an instruction.  */
      __tl = h + 1 + rval;
      op->mode = OP_EXP;

      parse_exp (__tl, &(op->exp));
      if (op->exp.X_op == O_constant)
	{
	  int x = op->exp.X_add_number;

	  if (vshift == 0)
	    {
	      x = x & 0xffff;
	      op->exp.X_add_number = x;
	    }
	  else if (vshift == 1)
	    {
	      x = (x >> 16) & 0xffff;
	      op->exp.X_add_number = x;
	    }
	  else if (vshift > 1)
	    {
	      if (x < 0)
		op->exp.X_add_number = -1;
	      else
		op->exp.X_add_number = 0;	/* Nothing left.  */
	      x = op->exp.X_add_number;
	    }

	  if (allow_20bit_values)
	    {
	      if (op->exp.X_add_number > 0xfffff || op->exp.X_add_number < - (0x7ffff))
		{
		  as_bad (_("value 0x%x out of extended range."), x);
		  return 1;
		}
	    }
	  else if (op->exp.X_add_number > 65535 || op->exp.X_add_number < -32768)
	    {
	      as_bad (_("value %d out of range. Use #lo() or #hi()"), x);
	      return 1;
	    }

	  /* Now check constants.  */
	  /* Substitute register mode with a constant generator if applicable.  */

	  if (!allow_20bit_values)
	    x = (short) x;	/* Extend sign.  */

	  if (! constants_allowed)
	    ;
	  else if (x == 0)
	    {
	      op->reg = 3;
	      op->am = 0;
	      op->ol = 0;
	      op->mode = OP_REG;
	    }
	  else if (x == 1)
	    {
	      op->reg = 3;
	      op->am = 1;
	      op->ol = 0;
	      op->mode = OP_REG;
	    }
	  else if (x == 2)
	    {
	      op->reg = 3;
	      op->am = 2;
	      op->ol = 0;
	      op->mode = OP_REG;
	    }
	  else if (x == -1)
	    {
	      op->reg = 3;
	      op->am = 3;
	      op->ol = 0;
	      op->mode = OP_REG;
	    }
	  else if (x == 4)
	    {
#ifdef PUSH_1X_WORKAROUND
	      if (bin == 0x1200)
		{
		  /* Remove warning as confusing.
		     as_warn (_("Hardware push bug workaround")); */
		}
	      else
#endif
		{
		  op->reg = 2;
		  op->am = 2;
		  op->ol = 0;
		  op->mode = OP_REG;
		}
	    }
	  else if (x == 8)
	    {
#ifdef PUSH_1X_WORKAROUND
	      if (bin == 0x1200)
		{
		  /* Remove warning as confusing.
		     as_warn (_("Hardware push bug workaround")); */
		}
	      else
#endif
		{
		  op->reg = 2;
		  op->am = 3;
		  op->ol = 0;
		  op->mode = OP_REG;
		}
	    }
	}
      else if (op->exp.X_op == O_symbol)
	{
	  op->mode = OP_EXP;
	}
      else if (op->exp.X_op == O_big)
	{
	  short x;
	  if (vshift != -1)
	    {
	      op->exp.X_op = O_constant;
	      op->exp.X_add_number = 0xffff & generic_bignum[vshift];
	      x = op->exp.X_add_number;
	    }
	  else
	    {
	      as_bad (_
		      ("unknown expression in operand %s. use #llo() #lhi() #hlo() #hhi() "),
		      l);
	      return 1;
	    }

	  if (x == 0)
	    {
	      op->reg = 3;
	      op->am = 0;
	      op->ol = 0;
	      op->mode = OP_REG;
	    }
	  else if (x == 1)
	    {
	      op->reg = 3;
	      op->am = 1;
	      op->ol = 0;
	      op->mode = OP_REG;
	    }
	  else if (x == 2)
	    {
	      op->reg = 3;
	      op->am = 2;
	      op->ol = 0;
	      op->mode = OP_REG;
	    }
	  else if (x == -1)
	    {
	      op->reg = 3;
	      op->am = 3;
	      op->ol = 0;
	      op->mode = OP_REG;
	    }
	  else if (x == 4)
	    {
	      op->reg = 2;
	      op->am = 2;
	      op->ol = 0;
	      op->mode = OP_REG;
	    }
	  else if (x == 8)
	    {
	      op->reg = 2;
	      op->am = 3;
	      op->ol = 0;
	      op->mode = OP_REG;
	    }
	}
      /* Redundant (yet) check.  */
      else if (op->exp.X_op == O_register)
	as_bad
	  (_("Registers cannot be used within immediate expression [%s]"), l);
      else
	as_bad (_("unknown operand %s"), l);

      return 0;
    }

  /* Check if absolute &VALUE (assume that we can construct something like ((a&b)<<7 + 25).  */
  if (*l == '&')
    {
      char *h = l;

      op->reg = 2;		/* reg 2 in absolute addr mode.  */
      op->am = 1;		/* mode As == 01 bin.  */
      op->ol = 1;		/* Immediate value followed by instruction.  */
      __tl = h + 1;
      parse_exp (__tl, &(op->exp));
      op->mode = OP_EXP;
      if (op->exp.X_op == O_constant)
	{
	  int x = op->exp.X_add_number;

	  if (allow_20bit_values)
	    {
	      if (x > 0xfffff || x < -(0x7ffff))
		{
		  as_bad (_("value 0x%x out of extended range."), x);
		  return 1;
		}
	    }
	  else if (x > 65535 || x < -32768)
	    {
	      as_bad (_("value out of range: 0x%x"), x);
	      return 1;
	    }
	}
      else if (op->exp.X_op == O_symbol)
	;
      else
	{
	  /* Redundant (yet) check.  */
	  if (op->exp.X_op == O_register)
	    as_bad
	      (_("Registers cannot be used within absolute expression [%s]"), l);
	  else
	    as_bad (_("unknown expression in operand %s"), l);
	  return 1;
	}
      return 0;
    }

  /* Check if indirect register mode @Rn / postincrement @Rn+.  */
  if (*l == '@')
    {
      char *t = l;
      char *m = strchr (l, '+');

      if (t != l)
	{
	  as_bad (_("unknown addressing mode %s"), l);
	  return 1;
	}

      t++;

      if ((op->reg = check_reg (t)) == -1)
	{
	  as_bad (_("Bad register name %s"), t);
	  return 1;
	}

      op->mode = OP_REG;
      op->am = m ? 3 : 2;
      op->ol = 0;

      /* PC cannot be used in indirect addressing.  */
      if (target_is_430xv2 () && op->reg == 0)
	{
	  as_bad (_("cannot use indirect addressing with the PC"));
	  return 1;
	}
      return 0;
    }

  /* Check if register indexed X(Rn).  */
  do
    {
      char *h = strrchr (l, '(');
      char *m = strrchr (l, ')');
      char *t;

      *imm_op = 1;

      if (!h)
	break;
      if (!m)
	{
	  as_bad (_("')' required"));
	  return 1;
	}

      t = h;
      op->am = 1;
      op->ol = 1;

      /* Extract a register.  */
      if ((op->reg = check_reg (t + 1)) == -1)
	{
	  as_bad (_
		  ("unknown operator %s. Did you mean X(Rn) or #[hl][hl][oi](CONST) ?"),
		  l);
	  return 1;
	}

      if (op->reg == 2)
	{
	  as_bad (_("r2 should not be used in indexed addressing mode"));
	  return 1;
	}

      /* Extract constant.  */
      __tl = l;
      *h = 0;
      op->mode = OP_EXP;
      parse_exp (__tl, &(op->exp));
      if (op->exp.X_op == O_constant)
	{
	  int x = op->exp.X_add_number;

	  if (allow_20bit_values)
	    {
	      if (x > 0xfffff || x < - (0x7ffff))
		{
		  as_bad (_("value 0x%x out of extended range."), x);
		  return 1;
		}
	    }
	  else if (x > 65535 || x < -32768)
	    {
	      as_bad (_("value out of range: 0x%x"), x);
	      return 1;
	    }

	  if (x == 0)
	    {
	      op->mode = OP_REG;
	      op->am = 2;
	      op->ol = 0;
	      return 0;
	    }
	}
      else if (op->exp.X_op == O_symbol)
	;
      else
	{
	  /* Redundant (yet) check.  */
	  if (op->exp.X_op == O_register)
	    as_bad
	      (_("Registers cannot be used as a prefix of indexed expression [%s]"), l);
	  else
	    as_bad (_("unknown expression in operand %s"), l);
	  return 1;
	}

      return 0;
    }
  while (0);

  /* Possibly register mode 'mov r1,r2'.  */
  if ((op->reg = check_reg (l)) != -1)
    {
      op->mode = OP_REG;
      op->am = 0;
      op->ol = 0;
      return 0;
    }

  /* Symbolic mode 'mov a, b' == 'mov x(pc), y(pc)'.  */
  do
    {
      op->mode = OP_EXP;
      op->reg = 0;		/* PC relative... be careful.  */
      /* An expression starting with a minus sign is a constant, not an address.  */
      op->am = (*l == '-' ? 3 : 1);
      op->ol = 1;
      __tl = l;
      parse_exp (__tl, &(op->exp));
      return 0;
    }
  while (0);

  /* Unreachable.  */
  as_bad (_("unknown addressing mode for operand %s"), l);
  return 1;
}


static int
msp430_dstoperand (struct msp430_operand_s * op,
		   char * l,
		   int bin,
		   bfd_boolean allow_20bit_values,
		   bfd_boolean constants_allowed)
{
  int dummy;
  int ret = msp430_srcoperand (op, l, bin, & dummy,
			       allow_20bit_values,
			       constants_allowed);

  if (ret)
    return ret;

  if (op->am == 2)
    {
      char *__tl = "0";

      op->mode = OP_EXP;
      op->am = 1;
      op->ol = 1;
      parse_exp (__tl, &(op->exp));

      if (op->exp.X_op != O_constant || op->exp.X_add_number != 0)
	{
	  as_bad (_("Internal bug. Try to use 0(r%d) instead of @r%d"),
		  op->reg, op->reg);
	  return 1;
	}
      return 0;
    }

  if (op->am > 1)
    {
      as_bad (_
	      ("this addressing mode is not applicable for destination operand"));
      return 1;
    }
  return 0;
}


/* Attempt to encode a MOVA instruction with the given operands.
   Returns the length of the encoded instruction if successful
   or 0 upon failure.  If the encoding fails, an error message
   will be returned if a pointer is provided.  */

static int
try_encode_mova (bfd_boolean imm_op,
		 int bin,
		 struct msp430_operand_s * op1,
		 struct msp430_operand_s * op2,
		 const char ** error_message_return)
{
  short ZEROS = 0;
  char *frag;
  int where;

  /* Only a restricted subset of the normal MSP430 addressing modes
     are supported here, so check for the ones that are allowed.  */
  if (imm_op)
    {
      if (op1->mode == OP_EXP)
	{
	  if (op2->mode != OP_REG)
	    {
	      if (error_message_return != NULL)
		* error_message_return = _("expected register as second argument of %s");
	      return 0;
	    }

	  if (op1->am == 3)
	    {
	      /* MOVA #imm20, Rdst.  */
	      bin |= 0x80 | op2->reg;
	      frag = frag_more (4);
	      where = frag - frag_now->fr_literal;
	      if (op1->exp.X_op == O_constant)
		{
		  bin |= ((op1->exp.X_add_number >> 16) & 0xf) << 8;
		  bfd_putl16 ((bfd_vma) bin, frag);
		  bfd_putl16 (op1->exp.X_add_number & 0xffff, frag + 2);
		}
	      else
		{
		  bfd_putl16 ((bfd_vma) bin, frag);
		  fix_new_exp (frag_now, where, 4, &(op1->exp), FALSE,
			       BFD_RELOC_MSP430X_ABS20_ADR_SRC);
		  bfd_putl16 ((bfd_vma) ZEROS, frag + 2);
		}

	      return 4;
	    }
	  else if (op1->am == 1)
	    {
	      /* MOVA z16(Rsrc), Rdst.  */
	      bin |= 0x30 | (op1->reg << 8) | op2->reg;
	      frag = frag_more (4);
	      where = frag - frag_now->fr_literal;
	      bfd_putl16 ((bfd_vma) bin, frag);
	      if (op1->exp.X_op == O_constant)
		{
		  if (op1->exp.X_add_number > 0xffff
		      || op1->exp.X_add_number < -(0x7fff))
		    {
		      if (error_message_return != NULL)
			* error_message_return = _("index value too big for %s");
		      return 0;
		    }
		  bfd_putl16 (op1->exp.X_add_number & 0xffff, frag + 2);
		}
	      else
		{
		  bfd_putl16 ((bfd_vma) ZEROS, frag + 2);
		  fix_new_exp (frag_now, where + 2, 2, &(op1->exp), FALSE,
			       op1->reg == 0 ?
			       BFD_RELOC_MSP430X_PCR16 :
			       BFD_RELOC_MSP430X_ABS16);
		}
	      return 4;
	    }

	  if (error_message_return != NULL)
	    * error_message_return = _("unexpected addressing mode for %s");
	  return 0;
	}
      else if (op1->am == 0)
	{
	  /* MOVA Rsrc, ... */
	  if (op2->mode == OP_REG)
	    {
	      bin |= 0xc0 | (op1->reg << 8) | op2->reg;
	      frag = frag_more (2);
	      where = frag - frag_now->fr_literal;
	      bfd_putl16 ((bfd_vma) bin, frag);
	      return 2;
	    }
	  else if (op2->am == 1)
	    {
	      if (op2->reg == 2)
		{
		  /* MOVA Rsrc, &abs20.  */
		  bin |= 0x60 | (op1->reg << 8);
		  frag = frag_more (4);
		  where = frag - frag_now->fr_literal;
		  if (op2->exp.X_op == O_constant)
		    {
		      bin |= (op2->exp.X_add_number >> 16) & 0xf;
		      bfd_putl16 ((bfd_vma) bin, frag);
		      bfd_putl16 (op2->exp.X_add_number & 0xffff, frag + 2);
		    }
		  else
		    {
		      bfd_putl16 ((bfd_vma) bin, frag);
		      bfd_putl16 ((bfd_vma) ZEROS, frag + 2);
		      fix_new_exp (frag_now, where, 4, &(op2->exp), FALSE,
				   BFD_RELOC_MSP430X_ABS20_ADR_DST);
		    }
		  return 4;
		}

	      /* MOVA Rsrc, z16(Rdst).  */
	      bin |= 0x70 | (op1->reg << 8) | op2->reg;
	      frag = frag_more (4);
	      where = frag - frag_now->fr_literal;
	      bfd_putl16 ((bfd_vma) bin, frag);
	      if (op2->exp.X_op == O_constant)
		{
		  if (op2->exp.X_add_number > 0xffff
		      || op2->exp.X_add_number < -(0x7fff))
		    {
		      if (error_message_return != NULL)
			* error_message_return = _("index value too big for %s");
		      return 0;
		    }
		  bfd_putl16 (op2->exp.X_add_number & 0xffff, frag + 2);
		}
	      else
		{
		  bfd_putl16 ((bfd_vma) ZEROS, frag + 2);
		  fix_new_exp (frag_now, where + 2, 2, &(op2->exp), FALSE,
			       op2->reg == 0 ?
			       BFD_RELOC_MSP430X_PCR16 :
			       BFD_RELOC_MSP430X_ABS16);
		}
	      return 4;
	    }

	  if (error_message_return != NULL)
	    * error_message_return = _("unexpected addressing mode for %s");
	  return 0;
	}
    }

  /* imm_op == FALSE.  */

  if (op1->reg == 2 && op1->am == 1 && op1->mode == OP_EXP)
    {
      /* MOVA &abs20, Rdst.  */
      if (op2->mode != OP_REG)
	{
	  if (error_message_return != NULL)
	    * error_message_return = _("expected register as second argument of %s");
	  return 0;
	}

      if (op2->reg == 2 || op2->reg == 3)
	{
	  if (error_message_return != NULL)
	    * error_message_return = _("constant generator destination register found in %s");
	  return 0;
	}

      bin |= 0x20 | op2->reg;
      frag = frag_more (4);
      where = frag - frag_now->fr_literal;
      if (op1->exp.X_op == O_constant)
	{
	  bin |= ((op1->exp.X_add_number >> 16) & 0xf) << 8;
	  bfd_putl16 ((bfd_vma) bin, frag);
	  bfd_putl16 (op1->exp.X_add_number & 0xffff, frag + 2);
	}
      else
	{
	  bfd_putl16 ((bfd_vma) bin, frag);
	  bfd_putl16 ((bfd_vma) ZEROS, frag + 2);
	  fix_new_exp (frag_now, where, 4, &(op1->exp), FALSE,
		       BFD_RELOC_MSP430X_ABS20_ADR_SRC);
	}
      return 4;
    }
  else if (op1->mode == OP_REG)
    {
      if (op1->am == 3)
	{
	  /* MOVA @Rsrc+, Rdst.  */
	  if (op2->mode != OP_REG)
	    {
	      if (error_message_return != NULL)
		* error_message_return = _("expected register as second argument of %s");
	      return 0;
	    }

	  if (op2->reg == 2 || op2->reg == 3)
	    {
	      if (error_message_return != NULL)
		* error_message_return = _("constant generator destination register found in %s");
	      return 0;
	    }

	  if (op1->reg == 2 || op1->reg == 3)
	    {
	      if (error_message_return != NULL)
		* error_message_return = _("constant generator source register found in %s");
	      return 0;
	    }

	  bin |= 0x10 | (op1->reg << 8) | op2->reg;
	  frag = frag_more (2);
	  where = frag - frag_now->fr_literal;
	  bfd_putl16 ((bfd_vma) bin, frag);
	  return 2;
	}
      else if (op1->am == 2)
	{
	  /* MOVA @Rsrc,Rdst */
	  if (op2->mode != OP_REG)
	    {
	      if (error_message_return != NULL)
		* error_message_return = _("expected register as second argument of %s");
	      return 0;
	    }

	  if (op2->reg == 2 || op2->reg == 3)
	    {
	      if (error_message_return != NULL)
		* error_message_return = _("constant generator destination register found in %s");
	      return 0;
	    }

	  if (op1->reg == 2 || op1->reg == 3)
	    {
	      if (error_message_return != NULL)
		* error_message_return = _("constant generator source register found in %s");
	      return 0;
	    }

	  bin |= (op1->reg << 8) | op2->reg;
	  frag = frag_more (2);
	  where = frag - frag_now->fr_literal;
	  bfd_putl16 ((bfd_vma) bin, frag);
	  return 2;
	}
    }

  if (error_message_return != NULL)
    * error_message_return = _("unexpected addressing mode for %s");

  return 0;
}

#define is_opcode(NAME) (strcmp (opcode->name, NAME) == 0)

/* Parse instruction operands.
   Return binary opcode.  */

static unsigned int
msp430_operands (struct msp430_opcode_s * opcode, char * line)
{
  int bin = opcode->bin_opcode;	/* Opcode mask.  */
  int insn_length = 0;
  char l1[MAX_OP_LEN], l2[MAX_OP_LEN];
  char *frag;
  int where;
  struct msp430_operand_s op1, op2;
  int res = 0;
  static short ZEROS = 0;
  int byte_op, imm_op;
  int op_length = 0;
  int fmt;
  int extended = 0x1800;
  bfd_boolean extended_op = FALSE;
  bfd_boolean addr_op;
  const char * error_message;
  static signed int repeat_count = 0;
  bfd_boolean fix_emitted;

  /* Opcode is the one from opcodes table
     line contains something like
     [.w] @r2+, 5(R1)
     or
     .b @r2+, 5(R1).  */

  /* Check if byte or word operation.  */
  if (*line == '.' && TOLOWER (*(line + 1)) == 'b')
    {
      bin |= BYTE_OPERATION;
      byte_op = 1;
    }
  else
    byte_op = 0;

  /* "Address" ops work on 20-bit values.  */
  if (*line == '.' && TOLOWER (*(line + 1)) == 'a')
    {
      addr_op = TRUE;
      bin |= BYTE_OPERATION;
    }
  else
    addr_op = FALSE;

  /* skip .[aAbwBW].  */
  while (! ISSPACE (*line) && *line)
    line++;

  if (opcode->fmt != -1
      && opcode->insn_opnumb
      && (!*line || *line == '\n'))
    {
      as_bad (_("instruction %s requires %d operand(s)"),
	      opcode->name, opcode->insn_opnumb);
      return 0;
    }

  memset (l1, 0, sizeof (l1));
  memset (l2, 0, sizeof (l2));
  memset (&op1, 0, sizeof (op1));
  memset (&op2, 0, sizeof (op2));

  imm_op = 0;

  if ((fmt = opcode->fmt) < 0)
    {
      if (! target_is_430x ())
	{
	  as_bad (_("instruction %s requires MSP430X mcu"),
		  opcode->name);
	  return 0;
	}
	
      fmt = (-fmt) - 1;
      extended_op = TRUE;
    }

  if (repeat_count)
    {
      /* If requested set the extended instruction repeat count.  */
      if (extended_op)
	{
	  if (repeat_count > 0)
	    extended |= (repeat_count - 1);
	  else
	    extended |= (1 << 7) | (- repeat_count);
	}
      else
	as_bad (_("unable to repeat %s insn"), opcode->name);

      repeat_count = 0;
    }

  switch (fmt)
    {
    case 0:			/* Emulated.  */
      switch (opcode->insn_opnumb)
	{
	case 0:
	  /* Set/clear bits instructions.  */
	  if (extended_op)
	    {
	      if (!addr_op)
		extended |= BYTE_OPERATION;

	      /* Emit the extension word.  */
	      insn_length += 2;
	      frag = frag_more (insn_length);
	      bfd_putl16 (extended, frag);
	    }

	  insn_length += 2;
	  frag = frag_more (insn_length);
	  bfd_putl16 ((bfd_vma) bin, frag);

	  if (gen_interrupt_nops
	      && target_is_430xv2 ()
	      && (is_opcode ("eint") || is_opcode ("dint")))
	    {
	      /* Emit a NOP following interrupt enable/disable.
		 See 1.3.4.1 of the MSP430x5xx User Guide.  */
	      insn_length += 2;
	      frag = frag_more (2);
	      as_warn (_("a NOP instruction has been inserted after %s"),
		       opcode->name);
	      bfd_putl16 ((bfd_vma) 0x4303 /* NOP */, frag);
	    }
	  dwarf2_emit_insn (insn_length);
	  break;

	case 1:
	  /* Something which works with destination operand.  */
	  line = extract_operand (line, l1, sizeof (l1));
	  res = msp430_dstoperand (&op1, l1, opcode->bin_opcode, extended_op, TRUE);
	  if (res)
	    break;

	  /* Compute the entire instruction length, in bytes.  */
	  insn_length = (extended_op ? 2 : 0) + 2 + (op1.ol * 2);
	  frag = frag_more (insn_length);
	  where = frag - frag_now->fr_literal;
	  
	  if (extended_op)
	    {
	      if (!addr_op)
		extended |= BYTE_OPERATION;

	      if (op1.ol != 0 && ((extended & 0xf) != 0))
		{
		  as_bad (_("repeat instruction used with non-register mode instruction"));
		  extended &= ~ 0xf;
		}
	      
	      if (op1.mode == OP_EXP)
		{
		  if (op1.exp.X_op == O_constant)
		    extended |= ((op1.exp.X_add_number >> 16) & 0xf) << 7;

		  else if (op1.reg || (op1.reg == 0 && op1.am == 3))	/* Not PC relative.  */
		    fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE,
				 BFD_RELOC_MSP430X_ABS20_EXT_SRC);
		  else
		    fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE,
				 BFD_RELOC_MSP430X_PCR20_EXT_SRC);
		}
	  
	      /* Emit the extension word.  */
	      bfd_putl16 (extended, frag);
	      frag += 2;
	      where += 2;
	    }

	  bin |= (op1.reg | (op1.am << 7));
	  bfd_putl16 ((bfd_vma) bin, frag);
	  frag += 2;
	  where += 2;

	  if (op1.mode == OP_EXP)
	    {
	      if (op1.exp.X_op == O_constant)
		{
		  bfd_putl16 (op1.exp.X_add_number & 0xffff, frag);
		}
	      else
		{
		  bfd_putl16 ((bfd_vma) ZEROS, frag);

		  if (!extended_op)
		    {
		      if (op1.reg)
			fix_new_exp (frag_now, where, 2,
				     &(op1.exp), FALSE, CHECK_RELOC_MSP430);
		      else
			fix_new_exp (frag_now, where, 2,
				     &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
		    }
		}
	    }

	  if (gen_interrupt_nops
	      && target_is_430xv2 ()
	      && is_opcode ("clr")
	      && bin == 0x4302 /* CLR R2*/)
	    {
	      /* Emit a NOP following interrupt enable/disable.
		 See 1.3.4.1 of the MSP430x5xx User Guide.  */
	      insn_length += 2;
	      frag = frag_more (2);
	      bfd_putl16 ((bfd_vma) 0x4303 /* NOP */, frag);
	      as_warn (_("a NOP instruction has been inserted after %s"),
		       opcode->name);
	    }

	  dwarf2_emit_insn (insn_length);
	  break;

	case 2:
	  /* Shift instruction.  */
	  line = extract_operand (line, l1, sizeof (l1));
	  strncpy (l2, l1, sizeof (l2));
	  l2[sizeof (l2) - 1] = '\0';
	  res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, &imm_op, extended_op, TRUE);
	  res += msp430_dstoperand (&op2, l2, opcode->bin_opcode, extended_op, TRUE);

	  if (res)
	    break;	/* An error occurred.  All warnings were done before.  */

	  insn_length = (extended_op ? 2 : 0) + 2 + (op1.ol * 2) + (op2.ol * 2);
	  frag = frag_more (insn_length);
	  where = frag - frag_now->fr_literal;

	  if (target_is_430xv2 ()
	      && op1.mode == OP_REG
	      && op1.reg == 0 
	      && (is_opcode ("rlax")
		  || is_opcode ("rlcx")
		  || is_opcode ("rla")
		  || is_opcode ("rlc")))
	    {
	      as_bad (_("%s: attempt to rotate the PC register"), opcode->name);
	      return 0;
	    }
	  
	  if (extended_op)
	    {
	      if (!addr_op)
		extended |= BYTE_OPERATION;

	      if ((op1.ol != 0 || op2.ol != 0) && ((extended & 0xf) != 0))
		{
		  as_bad (_("repeat instruction used with non-register mode instruction"));
		  extended &= ~ 0xf;
		}
	      
	      if (op1.mode == OP_EXP)
		{
		  if (op1.exp.X_op == O_constant)
		    extended |= ((op1.exp.X_add_number >> 16) & 0xf) << 7;

		  else if (op1.reg || (op1.reg == 0 && op1.am == 3))	/* Not PC relative.  */
		    fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE,
				 BFD_RELOC_MSP430X_ABS20_EXT_SRC);
		  else
		    fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE,
				 BFD_RELOC_MSP430X_PCR20_EXT_SRC);
		}

	      if (op2.mode == OP_EXP)
		{
		  if (op2.exp.X_op == O_constant)
		    extended |= (op2.exp.X_add_number >> 16) & 0xf;

		  else if (op1.mode == OP_EXP)
		    fix_new_exp (frag_now, where, 8, &(op2.exp), FALSE,
				 op2.reg ? BFD_RELOC_MSP430X_ABS20_EXT_ODST
				 : BFD_RELOC_MSP430X_PCR20_EXT_ODST);
		  else
		    fix_new_exp (frag_now, where, 6, &(op2.exp), FALSE,
				 op2.reg ? BFD_RELOC_MSP430X_ABS20_EXT_DST
				 : BFD_RELOC_MSP430X_PCR20_EXT_DST);
		}

	      /* Emit the extension word.  */
	      bfd_putl16 (extended, frag);
	      frag += 2;
	      where += 2;
	    }

	  bin |= (op2.reg | (op1.reg << 8) | (op1.am << 4) | (op2.am << 7));
	  bfd_putl16 ((bfd_vma) bin, frag);
	  frag += 2;
	  where += 2;

	  if (op1.mode == OP_EXP)
	    {
	      if (op1.exp.X_op == O_constant)
		{
		  bfd_putl16 (op1.exp.X_add_number & 0xffff, frag);
		}
	      else
		{
		  bfd_putl16 ((bfd_vma) ZEROS, frag);

		  if (!extended_op)
		    {
		      if (op1.reg || (op1.reg == 0 && op1.am == 3))	/* Not PC relative.  */
			fix_new_exp (frag_now, where, 2,
				     &(op1.exp), FALSE, CHECK_RELOC_MSP430);
		      else
			fix_new_exp (frag_now, where, 2,
				     &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
		    }
		}
	      frag += 2;
	      where += 2;
	    }

	  if (op2.mode == OP_EXP)
	    {
	      if (op2.exp.X_op == O_constant)
		{
		  bfd_putl16 (op2.exp.X_add_number & 0xffff, frag);
		}
	      else
		{
		  bfd_putl16 ((bfd_vma) ZEROS, frag);

		  if (!extended_op)
		    {
		      if (op2.reg)	/* Not PC relative.  */
			fix_new_exp (frag_now, where, 2,
				     &(op2.exp), FALSE, CHECK_RELOC_MSP430);
		      else
			fix_new_exp (frag_now, where, 2,
				     &(op2.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
		    }
		}
	    }

	  dwarf2_emit_insn (insn_length);
	  break;

	case 3:
	  /* Branch instruction => mov dst, r0.  */
	  if (extended_op)
	    {
	      as_bad ("Internal error: state 0/3 not coded for extended instructions");
	      return 0;
	    }

	  line = extract_operand (line, l1, sizeof (l1));
	  res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, &imm_op, extended_op, FALSE);
	  if (res)
	    break;

	  byte_op = 0;
	  imm_op = 0;
	  bin |= ((op1.reg << 8) | (op1.am << 4));
	  op_length = 2 + 2 * op1.ol;
	  frag = frag_more (op_length);
	  where = frag - frag_now->fr_literal;
	  bfd_putl16 ((bfd_vma) bin, frag);

	  if (op1.mode == OP_EXP)
	    {
	      if (op1.exp.X_op == O_constant)
		{
		  bfd_putl16 (op1.exp.X_add_number & 0xffff, frag + 2);
		}
	      else
		{
		  where += 2;
	      
		  bfd_putl16 ((bfd_vma) ZEROS, frag + 2);

		  if (op1.reg || (op1.reg == 0 && op1.am == 3))
		    fix_new_exp (frag_now, where, 2,
				 &(op1.exp), FALSE, CHECK_RELOC_MSP430);
		  else
		    fix_new_exp (frag_now, where, 2,
				 &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
		}
	    }

	  dwarf2_emit_insn (insn_length + op_length);
	  break;

	case 4:
	  /* CALLA instructions.  */
	  fix_emitted = FALSE;

	  line = extract_operand (line, l1, sizeof (l1));
	  imm_op = 0;

	  res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, &imm_op,
				   extended_op, FALSE);
	  if (res)
	    break;

	  byte_op = 0;

	  op_length = 2 + 2 * op1.ol;
	  frag = frag_more (op_length);
	  where = frag - frag_now->fr_literal;

	  if (imm_op)
	    {
	      if (op1.am == 3)
		{
		  bin |= 0xb0;

		  fix_new_exp (frag_now, where, 4, &(op1.exp), FALSE,
			       BFD_RELOC_MSP430X_ABS20_ADR_DST);
		  fix_emitted = TRUE;
		}
	      else if (op1.am == 1)
		{
		  if (op1.reg == 0)
		    {
		      bin |=  0x90;

		      fix_new_exp (frag_now, where, 4, &(op1.exp), FALSE,
				   BFD_RELOC_MSP430X_PCR20_CALL);
		      fix_emitted = TRUE;
		    }
		  else
		    bin |=  0x50 | op1.reg;
		}
	      else if (op1.am == 0)
		bin |= 0x40 | op1.reg;
	    }
	  else if (op1.am == 1)
	    {
	      bin |= 0x80;

	      fix_new_exp (frag_now, where, 4, &(op1.exp), FALSE,
			   BFD_RELOC_MSP430X_ABS20_ADR_DST);
	      fix_emitted = TRUE;
	    }
	  else if (op1.am == 2)
	    bin |= 0x60 | op1.reg;
	  else if (op1.am == 3)
	    bin |= 0x70 | op1.reg;
	  
	  bfd_putl16 ((bfd_vma) bin, frag);

	  if (op1.mode == OP_EXP)
	    {
	      if (op1.ol != 1)
		{
		  as_bad ("Internal error: unexpected CALLA instruction length: %d\n", op1.ol);
		  return 0;
		}

	      bfd_putl16 ((bfd_vma) ZEROS, frag + 2);

	      if (! fix_emitted)
		fix_new_exp (frag_now, where + 2, 2,
			     &(op1.exp), FALSE, BFD_RELOC_16);
	    }

	  dwarf2_emit_insn (insn_length + op_length);
	  break;

	case 5:
	  {
	    int n;
	    int reg;

	    /* [POP|PUSH]M[.A] #N, Rd */
	    line = extract_operand (line, l1, sizeof (l1));
	    line = extract_operand (line, l2, sizeof (l2));

	    if (*l1 != '#')
	      {
		as_bad (_("expected #n as first argument of %s"), opcode->name);
		return 0;
	      }
	    parse_exp (l1 + 1, &(op1.exp));
	    if (op1.exp.X_op != O_constant)
	      {
		as_bad (_("expected constant expression for first argument of %s"),
			opcode->name);
		return 0;
	      }

	    if ((reg = check_reg (l2)) == -1)
	      {
		as_bad (_("expected register as second argument of %s"),
			opcode->name);
		return 0;
	      }

	    op_length = 2;
	    frag = frag_more (op_length);
	    where = frag - frag_now->fr_literal;
	    bin = opcode->bin_opcode;
	    if (! addr_op)
	      bin |= 0x100;
	    n = op1.exp.X_add_number;
	    bin |= (n - 1) << 4;
	    if (is_opcode ("pushm"))
	      bin |= reg;
	    else
	      {
		if (reg - n + 1 < 0)
		  {
		    as_bad (_("Too many registers popped"));
		    return 0;
		  }

		/* CPU21 parts cannot use POPM to restore the SR register.  */
		if (target_is_430xv2 ()
		    && (reg - n + 1 < 3)
		    && reg >= 2
		    && is_opcode ("popm"))
		  {
		    as_bad (_("Cannot use POPM to restore the SR register"));
		    return 0;
		  }

		bin |= (reg - n + 1);
	      }

	    bfd_putl16 ((bfd_vma) bin, frag);
	    dwarf2_emit_insn (op_length);
	    break;
	  }

	case 6:
	  {
	    int n;
	    int reg;

	    /* Bit rotation instructions. RRCM, RRAM, RRUM, RLAM.  */
	    if (extended & 0xff)
	      {
		as_bad (_("repeat count cannot be used with %s"), opcode->name);
		return 0;
	      }

	    line = extract_operand (line, l1, sizeof (l1));
	    line = extract_operand (line, l2, sizeof (l2));

	    if (*l1 != '#')
	      {
		as_bad (_("expected #n as first argument of %s"), opcode->name);
		return 0;
	      }
	    parse_exp (l1 + 1, &(op1.exp));
	    if (op1.exp.X_op != O_constant)
	      {
		as_bad (_("expected constant expression for first argument of %s"),
			opcode->name);
		return 0;
	      }
	    n = op1.exp.X_add_number;
	    if (n > 4 || n < 1)
	      {
		as_bad (_("expected first argument of %s to be in the range 1-4"),
			opcode->name);
		return 0;
	      }

	    if ((reg = check_reg (l2)) == -1)
	      {
		as_bad (_("expected register as second argument of %s"),
			opcode->name);
		return 0;
	      }

	    if (target_is_430xv2 () && reg == 0)
	      {
		as_bad (_("%s: attempt to rotate the PC register"), opcode->name);
		return 0;
	      }

	    op_length = 2;
	    frag = frag_more (op_length);
	    where = frag - frag_now->fr_literal;

	    bin = opcode->bin_opcode;
	    if (! addr_op)
	      bin |= 0x10;
	    bin |= (n - 1) << 10;
	    bin |= reg;

	    bfd_putl16 ((bfd_vma) bin, frag);
	    dwarf2_emit_insn (op_length);
	    break;
	  }

	case 7:
	  {
	    int reg;

	    /* RRUX: Synthetic unsigned right shift of a register by one bit.  */
	    if (extended & 0xff)
	      {
		as_bad (_("repeat count cannot be used with %s"), opcode->name);
		return 0;
	      }

	    line = extract_operand (line, l1, sizeof (l1));
	    if ((reg = check_reg (l1)) == -1)
	      {
		as_bad (_("expected register as argument of %s"),
			opcode->name);
		return 0;
	      }

	    if (target_is_430xv2 () && reg == 0)
	      {
		as_bad (_("%s: attempt to rotate the PC register"), opcode->name);
		return 0;
	      }

	    if (byte_op)
	      {
		/* Tricky - there is no single instruction that will do this.
		   Encode as: RRA.B rN { BIC.B #0x80, rN  */
		op_length = 6;
		frag = frag_more (op_length);
		where = frag - frag_now->fr_literal;
		bin = 0x1140 | reg;
		bfd_putl16 ((bfd_vma) bin, frag);
		dwarf2_emit_insn (2);
		bin = 0xc070 | reg;
		bfd_putl16 ((bfd_vma) bin, frag + 2);
		bin = 0x0080;
		bfd_putl16 ((bfd_vma) bin, frag + 4);
		dwarf2_emit_insn (4);
	      }
	    else
	      {
		/* Encode as RRUM[.A] rN.  */
		bin = opcode->bin_opcode;
		if (! addr_op)
		  bin |= 0x10;
		bin |= reg;
		op_length = 2;
		frag = frag_more (op_length);
		where = frag - frag_now->fr_literal;
		bfd_putl16 ((bfd_vma) bin, frag);
		dwarf2_emit_insn (op_length);
	      }	    
	    break;
	  }

	case 8:
	  {
	    bfd_boolean need_reloc = FALSE;
	    int n;
	    int reg;

	    /* ADDA, CMPA and SUBA address instructions.  */
	    if (extended & 0xff)
	      {
		as_bad (_("repeat count cannot be used with %s"), opcode->name);
		return 0;
	      }

	    line = extract_operand (line, l1, sizeof (l1));
	    line = extract_operand (line, l2, sizeof (l2));

	    bin = opcode->bin_opcode;

	    if (*l1 == '#')
	      {
		parse_exp (l1 + 1, &(op1.exp));

		if (op1.exp.X_op == O_constant)
		  {
		    n = op1.exp.X_add_number;
		    if (n > 0xfffff || n < - (0x7ffff))
		      {
			as_bad (_("expected value of first argument of %s to fit into 20-bits"),
				opcode->name);
			return 0;
		      }

		    bin |= ((n >> 16) & 0xf) << 8;
		  }
		else
		  {
		    n = 0;
		    need_reloc = TRUE;
		  }

		op_length = 4;
	      }
	    else
	      {
		if ((n = check_reg (l1)) == -1)
		  {
		    as_bad (_("expected register name or constant as first argument of %s"),
			    opcode->name);
		    return 0;
		  }

		bin |= (n << 8) | (1 << 6);
		op_length = 2;
	      }

	    if ((reg = check_reg (l2)) == -1)
	      {
		as_bad (_("expected register as second argument of %s"),
			opcode->name);
		return 0;
	      }

	    frag = frag_more (op_length);
	    where = frag - frag_now->fr_literal;
	    bin |= reg;
	    if (need_reloc)
	      fix_new_exp (frag_now, where, 4, &(op1.exp), FALSE,
			   BFD_RELOC_MSP430X_ABS20_ADR_SRC);

	    bfd_putl16 ((bfd_vma) bin, frag);
	    if (op_length == 4)
	      bfd_putl16 ((bfd_vma) (n & 0xffff), frag + 2);
	    dwarf2_emit_insn (op_length);
	    break;
	  }
	  
	case 9: /* MOVA, BRA, RETA.  */
	  imm_op = 0;
	  bin = opcode->bin_opcode;

	  if (is_opcode ("reta"))
	    {
	      /* The RETA instruction does not take any arguments.
		 The implicit first argument is @SP+.
		 The implicit second argument is PC.  */
	      op1.mode = OP_REG;
	      op1.am = 3;
	      op1.reg = 1;

	      op2.mode = OP_REG;
	      op2.reg = 0;
	    }
	  else
	    {
	      line = extract_operand (line, l1, sizeof (l1));
	      res = msp430_srcoperand (&op1, l1, opcode->bin_opcode,
				       &imm_op, extended_op, FALSE);

	      if (is_opcode ("bra"))
		{
		  /* This is the BRA synthetic instruction.
		     The second argument is always PC.  */
		  op2.mode = OP_REG;
		  op2.reg = 0;
		}
	      else
		{
		  line = extract_operand (line, l2, sizeof (l2));
		  res += msp430_dstoperand (&op2, l2, opcode->bin_opcode,
					    extended_op, TRUE);
		}
	  
	      if (res)
		break;	/* Error occurred.  All warnings were done before.  */
	    }

	  /* Only a restricted subset of the normal MSP430 addressing modes
	     are supported here, so check for the ones that are allowed.  */
	  if ((op_length = try_encode_mova (imm_op, bin, & op1, & op2,
					    & error_message)) == 0)
	    {
	      as_bad (error_message, opcode->name);
	      return 0;
	    }
	  dwarf2_emit_insn (op_length);
	  break;

	case 10: /* RPT */
	  line = extract_operand (line, l1, sizeof l1);
	  /* The RPT instruction only accepted immediates and registers.  */
	  if (*l1 == '#')
	    {
	      parse_exp (l1 + 1, &(op1.exp));
	      if (op1.exp.X_op != O_constant)
		{
		  as_bad (_("expected constant value as argument to RPT"));
		  return 0;
		}
	      if (op1.exp.X_add_number < 1
		  || op1.exp.X_add_number > (1 << 4))
		{
		  as_bad (_("expected constant in the range 2..16"));
		  return 0;
		}

	      /* We silently accept and ignore a repeat count of 1.  */
	      if (op1.exp.X_add_number > 1)
		repeat_count = op1.exp.X_add_number;
	    }
	  else
	    {
	      int reg;

	      if ((reg = check_reg (l1)) != -1)
		{
		  if (reg == 0)
		    as_warn (_("PC used as an argument to RPT"));
		  else
		    repeat_count = - reg;
		}
	      else
		{
		  as_bad (_("expected constant or register name as argument to RPT insn"));
		  return 0;
		}
	    }
	  break;

	default:
	  as_bad (_("Illegal emulated instruction "));
	  break;
	}
      break;

    case 1:			/* Format 1, double operand.  */
      line = extract_operand (line, l1, sizeof (l1));
      line = extract_operand (line, l2, sizeof (l2));
      res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, &imm_op, extended_op, TRUE);
      res += msp430_dstoperand (&op2, l2, opcode->bin_opcode, extended_op, TRUE);

      if (res)
	break;			/* Error occurred.  All warnings were done before.  */

      if (extended_op
	  && is_opcode ("movx")
	  && addr_op
	  && msp430_enable_relax)
	{
	  /* This is the MOVX.A instruction.  See if we can convert
	     it into the MOVA instruction instead.  This saves 2 bytes.  */
	  if ((op_length = try_encode_mova (imm_op, 0x0000, & op1, & op2,
					    NULL)) != 0)
	    {
	      dwarf2_emit_insn (op_length);
	      break;
	    }
	}

      /* Compute the entire length of the instruction in bytes.  */
      insn_length =
	(extended_op ? 2 : 0)	/* The extension word.  */
	+ 2 			/* The opcode */
	+ (2 * op1.ol)		/* The first operand. */
	+ (2 * op2.ol);		/* The second operand.  */

      frag = frag_more (insn_length);
      where = frag - frag_now->fr_literal;
      
      if (extended_op)
	{
	  if (!addr_op)
	    extended |= BYTE_OPERATION;

	  if ((op1.ol != 0 || op2.ol != 0) && ((extended & 0xf) != 0))
	    {
	      as_bad (_("repeat instruction used with non-register mode instruction"));
	      extended &= ~ 0xf;
	    }

	  /* If necessary, emit a reloc to update the extension word.  */
	  if (op1.mode == OP_EXP)
	    {
	      if (op1.exp.X_op == O_constant)
		extended |= ((op1.exp.X_add_number >> 16) & 0xf) << 7;

	      else  if (op1.reg || (op1.reg == 0 && op1.am == 3))	/* Not PC relative.  */
		fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE,
			     BFD_RELOC_MSP430X_ABS20_EXT_SRC);
	      else
		fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE,
			     BFD_RELOC_MSP430X_PCR20_EXT_SRC);
	    }
	  
	  if (op2.mode == OP_EXP)
	    {
	      if (op2.exp.X_op == O_constant)
		extended |= (op2.exp.X_add_number >> 16) & 0xf;

	      else if (op1.mode == OP_EXP)
		fix_new_exp (frag_now, where, 8, &(op2.exp), FALSE,
			     op2.reg ? BFD_RELOC_MSP430X_ABS20_EXT_ODST
			     : BFD_RELOC_MSP430X_PCR20_EXT_ODST);
	    
	      else
		fix_new_exp (frag_now, where, 6, &(op2.exp), FALSE,
			     op2.reg ? BFD_RELOC_MSP430X_ABS20_EXT_DST
			     : BFD_RELOC_MSP430X_PCR20_EXT_DST);
	    }

	  /* Emit the extension word.  */
	  bfd_putl16 (extended, frag);
	  where += 2;
	  frag += 2;
	}

      bin |= (op2.reg | (op1.reg << 8) | (op1.am << 4) | (op2.am << 7));
      bfd_putl16 ((bfd_vma) bin, frag);
      where += 2;
      frag += 2;

      if (op1.mode == OP_EXP)
	{
	  if (op1.exp.X_op == O_constant)
	    {
	      bfd_putl16 (op1.exp.X_add_number & 0xffff, frag);
	    }
	  else
	    {
	      bfd_putl16 ((bfd_vma) ZEROS, frag);

	      if (!extended_op)
		{
		  if (op1.reg || (op1.reg == 0 && op1.am == 3))	/* Not PC relative.  */
		    fix_new_exp (frag_now, where, 2,
				 &(op1.exp), FALSE, CHECK_RELOC_MSP430);
		  else
		    fix_new_exp (frag_now, where, 2,
				 &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
		}
	    }

	  where += 2;
	  frag += 2;
	}

      if (op2.mode == OP_EXP)
	{
	  if (op2.exp.X_op == O_constant)
	    {
	      bfd_putl16 (op2.exp.X_add_number & 0xffff, frag);
	    }
	  else
	    {
	      bfd_putl16 ((bfd_vma) ZEROS, frag);

	      if (!extended_op)
		{
		  if (op2.reg)		/* Not PC relative.  */
		    fix_new_exp (frag_now, where, 2,
			     &(op2.exp), FALSE, CHECK_RELOC_MSP430);
		  else
		    fix_new_exp (frag_now, where, 2,
				 &(op2.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
		}
	    }
	}

      if (gen_interrupt_nops
	  && target_is_430xv2 ()
	  && (   (is_opcode ("bic") && bin == 0xc232)
	      || (is_opcode ("bis") && bin == 0xd232)
	      || (is_opcode ("mov") && op2.mode == OP_REG && op2.reg == 2)))
	{
	  /* Emit a NOP following interrupt enable/disable.
	     See 1.3.4.1 of the MSP430x5xx User Guide.  */
	  insn_length += 2;
	  frag = frag_more (2);
	  bfd_putl16 ((bfd_vma) 0x4303 /* NOP */, frag);
	  as_warn (_("a NOP instruction has been inserted after %s"),
		   opcode->name);
	}

      dwarf2_emit_insn (insn_length);
      break;

    case 2:			/* Single-operand mostly instr.  */
      if (opcode->insn_opnumb == 0)
	{
	  /* reti instruction.  */
	  insn_length += 2;
	  frag = frag_more (2);
	  bfd_putl16 ((bfd_vma) bin, frag);
	  dwarf2_emit_insn (insn_length);
	  break;
	}

      line = extract_operand (line, l1, sizeof (l1));
      res = msp430_srcoperand (&op1, l1, opcode->bin_opcode,
			       &imm_op, extended_op, TRUE);
      if (res)
	break;		/* Error in operand.  */

      if (target_is_430xv2 ()
	  && op1.mode == OP_REG
	  && op1.reg == 0 
	  && (is_opcode ("rrax")
	      || is_opcode ("rrcx")
	      || is_opcode ("rra")
	      || is_opcode ("rrc")))
	{
	  as_bad (_("%s: attempt to rotate the PC register"), opcode->name);
	  return 0;
	}
      
      insn_length = (extended_op ? 2 : 0) + 2 + (op1.ol * 2);
      frag = frag_more (insn_length);
      where = frag - frag_now->fr_literal;
      
      if (extended_op)
	{
	  if (is_opcode ("swpbx") || is_opcode ("sxtx"))
	    {
	      /* These two instructions use a special
		 encoding of the A/L and B/W bits.  */
	      bin &= ~ BYTE_OPERATION;

	      if (byte_op)
		{
		  as_bad (_("%s instruction does not accept a .b suffix"),
			  opcode->name);
		  return 0;
		}
	      else if (! addr_op)
		extended |= BYTE_OPERATION;
	    }
	  else if (! addr_op)
	    extended |= BYTE_OPERATION;

	  if (op1.ol != 0 && ((extended & 0xf) != 0))
	    {
	      as_bad (_("repeat instruction used with non-register mode instruction"));
	      extended &= ~ 0xf;
	    }

	  if (op1.mode == OP_EXP)
	    {
	      if (op1.exp.X_op == O_constant)
		extended |= ((op1.exp.X_add_number >> 16) & 0xf) << 7;
	  
	      else if (op1.reg || (op1.reg == 0 && op1.am == 3))	/* Not PC relative.  */
		fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE,
			     BFD_RELOC_MSP430X_ABS20_EXT_SRC);
	      else
		fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE,
			     BFD_RELOC_MSP430X_PCR20_EXT_SRC);
	    }
	  
	  /* Emit the extension word.  */
	  bfd_putl16 (extended, frag);
	  frag += 2;
	  where += 2;
	}

      bin |= op1.reg | (op1.am << 4);
      bfd_putl16 ((bfd_vma) bin, frag);
      frag += 2;
      where += 2;

      if (op1.mode == OP_EXP)
	{
	  if (op1.exp.X_op == O_constant)
	    {
	      bfd_putl16 (op1.exp.X_add_number & 0xffff, frag);
	    }
	  else
	    {
	      bfd_putl16 ((bfd_vma) ZEROS, frag);

	      if (!extended_op)
		{
		  if (op1.reg || (op1.reg == 0 && op1.am == 3))	/* Not PC relative.  */
		    fix_new_exp (frag_now, where, 2,
				 &(op1.exp), FALSE, CHECK_RELOC_MSP430);
		  else
		    fix_new_exp (frag_now, where, 2,
				 &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
		}
	    }
	}

      dwarf2_emit_insn (insn_length);
      break;

    case 3:			/* Conditional jumps instructions.  */
      line = extract_operand (line, l1, sizeof (l1));
      /* l1 is a label.  */
      if (l1[0])
	{
	  char *m = l1;
	  expressionS exp;

	  if (*m == '$')
	    m++;

	  parse_exp (m, &exp);

	  /* In order to handle something like:

	     and #0x8000, r5
	     tst r5
	     jz   4     ;       skip next 4 bytes
	     inv r5
	     inc r5
	     nop        ;       will jump here if r5 positive or zero

	     jCOND      -n      ;assumes jump n bytes backward:

	     mov r5,r6
	     jmp -2

	     is equal to:
	     lab:
	     mov r5,r6
	     jmp lab

	     jCOND      $n      ; jump from PC in either direction.  */

	  if (exp.X_op == O_constant)
	    {
	      int x = exp.X_add_number;

	      if (x & 1)
		{
		  as_warn (_("Even number required. Rounded to %d"), x + 1);
		  x++;
		}

	      if ((*l1 == '$' && x > 0) || x < 0)
		x -= 2;

	      x >>= 1;

	      if (x > 512 || x < -511)
		{
		  as_bad (_("Wrong displacement  %d"), x << 1);
		  break;
		}

	      insn_length += 2;
	      frag = frag_more (2);	/* Instr size is 1 word.  */

	      bin |= x & 0x3ff;
	      bfd_putl16 ((bfd_vma) bin, frag);
	    }
	  else if (exp.X_op == O_symbol && *l1 != '$')
	    {
	      insn_length += 2;
	      frag = frag_more (2);	/* Instr size is 1 word.  */
	      where = frag - frag_now->fr_literal;
	      fix_new_exp (frag_now, where, 2,
			   &exp, TRUE, BFD_RELOC_MSP430_10_PCREL);

	      bfd_putl16 ((bfd_vma) bin, frag);
	    }
	  else if (*l1 == '$')
	    {
	      as_bad (_("instruction requires label sans '$'"));
	    }
	  else
	    as_bad (_
		    ("instruction requires label or value in range -511:512"));
	  dwarf2_emit_insn (insn_length);
	  break;
	}
      else
	{
	  as_bad (_("instruction requires label"));
	  break;
	}
      break;

    case 4:	/* Extended jumps.  */
      if (!msp430_enable_polys)
	{
	  as_bad (_("polymorphs are not enabled. Use -mP option to enable."));
	  break;
	}
	
      line = extract_operand (line, l1, sizeof (l1));
      if (l1[0])
	{
	  char *m = l1;
	  expressionS exp;

	  /* Ignore absolute addressing. make it PC relative anyway.  */
	  if (*m == '#' || *m == '$')
	    m++;

	  parse_exp (m, & exp);
	  if (exp.X_op == O_symbol)
	    {
	      /* Relaxation required.  */
	      struct rcodes_s rc = msp430_rcodes[opcode->insn_opnumb];

	      if (target_is_430x ())
		rc = msp430x_rcodes[opcode->insn_opnumb];

	      /* The parameter to dwarf2_emit_insn is actually the offset to
		 the start of the insn from the fix piece of instruction that
		 was emitted.  Since next fragments may have variable size we
		 tie debug info to the beginning of the instruction.  */
	      insn_length += 8;
	      frag = frag_more (8);
	      dwarf2_emit_insn (0);
	      bfd_putl16 ((bfd_vma) rc.sop, frag);
	      frag = frag_variant (rs_machine_dependent, 8, 2,
				    /* Wild guess.  */
				   ENCODE_RELAX (rc.lpos, STATE_BITS10),
				   exp.X_add_symbol,
				   0,	/* Offset is zero if jump dist less than 1K.  */
				   (char *) frag);
	      break;
	    }
	}

      as_bad (_("instruction requires label"));
      break;

    case 5:	/* Emulated extended branches.  */
      if (!msp430_enable_polys)
	{
	  as_bad (_("polymorphs are not enabled. Use -mP option to enable."));
	  break;
	}
      line = extract_operand (line, l1, sizeof (l1));
      if (l1[0])
	{
	  char * m = l1;
	  expressionS exp;

	  /* Ignore absolute addressing. make it PC relative anyway.  */
	  if (*m == '#' || *m == '$')
	    m++;

	  parse_exp (m, & exp);
	  if (exp.X_op == O_symbol)
	    {
	      /* Relaxation required.  */
	      struct hcodes_s hc = msp430_hcodes[opcode->insn_opnumb];

	      if (target_is_430x ())
		hc = msp430x_hcodes[opcode->insn_opnumb];

	      insn_length += 8;
	      frag = frag_more (8);
	      dwarf2_emit_insn (0);
	      bfd_putl16 ((bfd_vma) hc.op0, frag);
	      bfd_putl16 ((bfd_vma) hc.op1, frag+2);

	      frag = frag_variant (rs_machine_dependent, 8, 2,
				   ENCODE_RELAX (STATE_EMUL_BRANCH, STATE_BITS10), /* Wild guess.  */
				   exp.X_add_symbol,
				   0,	/* Offset is zero if jump dist less than 1K.  */
				   (char *) frag);
	      break;
	    }
	}

      as_bad (_("instruction requires label"));
      break;

    default:
      as_bad (_("Illegal instruction or not implemented opcode."));
    }

  input_line_pointer = line;
  return 0;
}

void
md_assemble (char * str)
{
  struct msp430_opcode_s * opcode;
  char cmd[32];
  unsigned int i = 0;

  str = skip_space (str);	/* Skip leading spaces.  */
  str = extract_cmd (str, cmd, sizeof (cmd));

  while (cmd[i] && i < sizeof (cmd))
    {
      char a = TOLOWER (cmd[i]);
      cmd[i] = a;
      i++;
    }

  if (!cmd[0])
    {
      as_bad (_("can't find opcode "));
      return;
    }

  opcode = (struct msp430_opcode_s *) hash_find (msp430_hash, cmd);

  if (opcode == NULL)
    {
      as_bad (_("unknown opcode `%s'"), cmd);
      return;
    }

  {
    char *__t = input_line_pointer;

    msp430_operands (opcode, str);
    input_line_pointer = __t;
  }
}

/* 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;
}

/* Replaces standard TC_FORCE_RELOCATION_LOCAL.
   Now it handles the situation when relocations
   have to be passed to linker.  */
int
msp430_force_relocation_local (fixS *fixp)
{
  if (fixp->fx_r_type == BFD_RELOC_MSP430_10_PCREL)
    return 1;
  if (fixp->fx_pcrel)
    return 1;
  if (msp430_enable_polys
        && !msp430_enable_relax)
    return 1;

  return (!fixp->fx_pcrel
	  || generic_force_reloc (fixp));
}


/* GAS will call this for each fixup.  It should store the correct
   value in the object file.  */
void
md_apply_fix (fixS * fixp, valueT * valuep, segT seg)
{
  unsigned char * where;
  unsigned long insn;
  long value;

  if (fixp->fx_addsy == (symbolS *) NULL)
    {
      value = *valuep;
      fixp->fx_done = 1;
    }
  else if (fixp->fx_pcrel)
    {
      segT s = S_GET_SEGMENT (fixp->fx_addsy);

      if (fixp->fx_addsy && (s == seg || s == absolute_section))
	{
	  /* FIXME: We can appear here only in case if we perform a pc
	     relative jump to the label which is i) global, ii) locally
	     defined or this is a jump to an absolute symbol.
	     If this is an absolute symbol -- everything is OK.
	     If this is a global label, we've got a symbol value defined
	     twice:
               1. S_GET_VALUE (fixp->fx_addsy) will contain a symbol offset
	          from this section start
               2. *valuep will contain the real offset from jump insn to the
	          label
	     So, the result of S_GET_VALUE (fixp->fx_addsy) + (* valuep);
	     will be incorrect. Therefore remove s_get_value.  */
	  value = /* S_GET_VALUE (fixp->fx_addsy) + */ * valuep;
	  fixp->fx_done = 1;
	}
      else
	value = *valuep;
    }
  else
    {
      value = fixp->fx_offset;

      if (fixp->fx_subsy != (symbolS *) NULL)
	{
	  if (S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
	    {
	      value -= S_GET_VALUE (fixp->fx_subsy);
	      fixp->fx_done = 1;
	    }
	}
    }

  fixp->fx_no_overflow = 1;

  /* If polymorphs are enabled and relax disabled. 
     do not kill any relocs and pass them to linker.  */
  if (msp430_enable_polys 
      && !msp430_enable_relax)
    {
      if (!fixp->fx_addsy || (fixp->fx_addsy 
	  && S_GET_SEGMENT (fixp->fx_addsy) == absolute_section))
	fixp->fx_done = 1;	/* It is ok to kill 'abs' reloc.  */
      else
      	fixp->fx_done = 0;
    }

  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_MSP430_10_PCREL:
	  if (value & 1)
	    as_bad_where (fixp->fx_file, fixp->fx_line,
			  _("odd address operand: %ld"), value);

	  /* Jumps are in words.  */
	  value >>= 1;
	  --value;		/* Correct PC.  */

	  if (value < -512 || value > 511)
	    as_bad_where (fixp->fx_file, fixp->fx_line,
			  _("operand out of range: %ld"), value);

	  value &= 0x3ff;	/* get rid of extended sign */
	  bfd_putl16 ((bfd_vma) (value | insn), where);
	  break;

	case BFD_RELOC_MSP430X_PCR16:
	case BFD_RELOC_MSP430_RL_PCREL:
	case BFD_RELOC_MSP430_16_PCREL:
	  if (value & 1)
	    as_bad_where (fixp->fx_file, fixp->fx_line,
			  _("odd address operand: %ld"), value);
	  /* Fall through.  */

	case BFD_RELOC_MSP430_16_PCREL_BYTE:
	  /* Nothing to be corrected here.  */
	  if (value < -32768 || value > 65536)
	    as_bad_where (fixp->fx_file, fixp->fx_line,
			  _("operand out of range: %ld"), value);
	  /* Fall through.  */

	case BFD_RELOC_MSP430X_ABS16:
	case BFD_RELOC_MSP430_16:
	case BFD_RELOC_16:
	case BFD_RELOC_MSP430_16_BYTE:
	  value &= 0xffff;	/* Get rid of extended sign.  */
	  bfd_putl16 ((bfd_vma) value, where);
	  break;

	case BFD_RELOC_32:
	  bfd_putl16 ((bfd_vma) value, where);
	  break;

	case BFD_RELOC_MSP430_ABS8:
	case BFD_RELOC_8:
	  bfd_put_8 (NULL, (bfd_vma) value, where);
	  break;

	case BFD_RELOC_MSP430X_ABS20_EXT_SRC:
	case BFD_RELOC_MSP430X_PCR20_EXT_SRC:
	  bfd_putl16 ((bfd_vma) (value & 0xffff), where + 4);
	  value >>= 16;	  
	  bfd_putl16 ((bfd_vma) (((value & 0xf) << 7) | insn), where);
	  break;

	case BFD_RELOC_MSP430X_ABS20_ADR_SRC:
	  bfd_putl16 ((bfd_vma) (value & 0xffff), where + 2);
	  value >>= 16;	  
	  bfd_putl16 ((bfd_vma) (((value & 0xf) << 8) | insn), where);
	  break;

	case BFD_RELOC_MSP430X_ABS20_EXT_ODST:
	  bfd_putl16 ((bfd_vma) (value & 0xffff), where + 6);
	  value >>= 16;
	  bfd_putl16 ((bfd_vma) ((value & 0xf) | insn), where);
	  break;

	case BFD_RELOC_MSP430X_PCR20_CALL:
	  bfd_putl16 ((bfd_vma) (value & 0xffff), where + 2);
	  value >>= 16;
	  bfd_putl16 ((bfd_vma) ((value & 0xf) | insn), where);
	  break;

	case BFD_RELOC_MSP430X_ABS20_EXT_DST:
	case BFD_RELOC_MSP430X_PCR20_EXT_DST:
	  bfd_putl16 ((bfd_vma) (value & 0xffff), where + 4);
	  value >>= 16;
	  bfd_putl16 ((bfd_vma) ((value & 0xf) | insn), where);
	  break;

	case BFD_RELOC_MSP430X_PCR20_EXT_ODST:
	  bfd_putl16 ((bfd_vma) (value & 0xffff), where + 6);
	  value >>= 16;
	  bfd_putl16 ((bfd_vma) ((value & 0xf) | insn), where);
	  break;

	case BFD_RELOC_MSP430X_ABS20_ADR_DST:
	  bfd_putl16 ((bfd_vma) (value & 0xffff), where + 2);
	  value >>= 16;
	  bfd_putl16 ((bfd_vma) ((value & 0xf) | insn), where);
	  break;
	  
	default:
	  as_fatal (_("line %d: unknown relocation type: 0x%x"),
		    fixp->fx_line, fixp->fx_r_type);
	  break;
	}
    }
  else
    {
      fixp->fx_addnumber = value;
    }
}

static bfd_boolean
S_IS_GAS_LOCAL (symbolS * s)
{
  const char * name;
  unsigned int len;

  if (s == NULL)
    return FALSE;
  name = S_GET_NAME (s);
  len = strlen (name) - 1;
  
  return name[len] == 1 || name[len] == 2;
}

/* 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)
{
  static arelent * no_relocs = NULL;
  static arelent * relocs[MAX_RELOC_EXPANSION + 1];
  arelent *reloc;

  reloc = xmalloc (sizeof (arelent));
  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);
      free (reloc);
      return & no_relocs;
    }

  relocs[0] = reloc;
  relocs[1] = NULL;

  if (fixp->fx_subsy
      && S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
    {
      fixp->fx_offset -= S_GET_VALUE (fixp->fx_subsy);
      fixp->fx_subsy = NULL;
    }

  if (fixp->fx_addsy && fixp->fx_subsy)
    {
      asection *asec, *ssec;

      asec = S_GET_SEGMENT (fixp->fx_addsy);
      ssec = S_GET_SEGMENT (fixp->fx_subsy);

      /* If we have a difference between two different, non-absolute symbols
	 we must generate two relocs (one for each symbol) and allow the
	 linker to resolve them - relaxation may change the distances between
	 symbols, even local symbols defined in the same section.

	 Unfortunately we cannot do this with assembler generated local labels
	 because there can be multiple incarnations of the same label, with
	 exactly the same name, in any given section and the linker will have
	 no way to identify the correct one.  Instead we just have to hope
	 that no relaxtion will occur between the local label and the other
	 symbol in the expression.

	 Similarly we have to compute differences between symbols in the .eh_frame
	 section as the linker is not smart enough to apply relocations there
	 before attempting to process it.  */
      if ((ssec != absolute_section || asec != absolute_section)
	  && (fixp->fx_addsy != fixp->fx_subsy)
	  && strcmp (ssec->name, ".eh_frame") != 0
	  && ! S_IS_GAS_LOCAL (fixp->fx_addsy)
	  && ! S_IS_GAS_LOCAL (fixp->fx_subsy))
	{
	  arelent * reloc2 = xmalloc (sizeof * reloc);

	  relocs[0] = reloc2;
	  relocs[1] = reloc;

	  reloc2->address = reloc->address;
	  reloc2->howto = bfd_reloc_type_lookup (stdoutput,
						 BFD_RELOC_MSP430_SYM_DIFF);
	  reloc2->addend = - S_GET_VALUE (fixp->fx_subsy);

	  if (ssec == absolute_section)
	    reloc2->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
	  else
	    {
	      reloc2->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
	      *reloc2->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
	    }

	  reloc->addend = fixp->fx_offset; 
	  if (asec == absolute_section)
	    {
	      reloc->addend += S_GET_VALUE (fixp->fx_addsy);
	      reloc->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
	    }
	  else
	    {
	      reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
	      *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
	    }

	  fixp->fx_pcrel = 0;
	  fixp->fx_done = 1;
	  return relocs;
	}
      else
	{
	  char *fixpos = fixp->fx_where + fixp->fx_frag->fr_literal;

	  reloc->addend = (S_GET_VALUE (fixp->fx_addsy)
			   - S_GET_VALUE (fixp->fx_subsy) + fixp->fx_offset);

	  switch (fixp->fx_r_type)
	    {
	    case BFD_RELOC_8:
	      md_number_to_chars (fixpos, reloc->addend, 1);
	      break;

	    case BFD_RELOC_16:
	      md_number_to_chars (fixpos, reloc->addend, 2);
	      break;

	    case BFD_RELOC_24:
	      md_number_to_chars (fixpos, reloc->addend, 3);
	      break;

	    case BFD_RELOC_32:
	      md_number_to_chars (fixpos, reloc->addend, 4);
	      break;

	    default:
	      reloc->sym_ptr_ptr
		= (asymbol **) bfd_abs_section_ptr->symbol_ptr_ptr;
	      return relocs;
	    }

	  free (reloc);
	  return & no_relocs;
	}
    }
  else
    {
#if 0
      if (fixp->fx_r_type == BFD_RELOC_MSP430X_ABS16
	  && S_GET_SEGMENT (fixp->fx_addsy) == absolute_section)
	{
	  bfd_vma amount = S_GET_VALUE (fixp->fx_addsy);
	  char *fixpos = fixp->fx_where + fixp->fx_frag->fr_literal;

	  md_number_to_chars (fixpos, amount, 2);
	  free (reloc);
	  return & no_relocs;
	}
#endif
      reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
      *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
      reloc->addend = fixp->fx_offset;

      if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
	  || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
	reloc->address = fixp->fx_offset;
    }

  return relocs;
}

int
md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED,
			       asection * segment_type ATTRIBUTE_UNUSED)
{
  if (fragP->fr_symbol && S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
    {
      /* This is a jump -> pcrel mode. Nothing to do much here.
         Return value == 2.  */
      fragP->fr_subtype =
	  ENCODE_RELAX (RELAX_LEN (fragP->fr_subtype), STATE_BITS10);
    }
  else if (fragP->fr_symbol)
    {
      /* Its got a segment, but its not ours.   Even if fr_symbol is in
	 an absolute segment, we don't know a displacement until we link
	 object files. So it will always be long. This also applies to
	 labels in a subsegment of current. Liker may relax it to short
	 jump later. Return value == 8.  */
      fragP->fr_subtype =
	  ENCODE_RELAX (RELAX_LEN (fragP->fr_subtype), STATE_WORD);
    }
  else
    {
      /* We know the abs value. may be it is a jump to fixed address.
         Impossible in our case, cause all constants already handled. */
      fragP->fr_subtype =
	  ENCODE_RELAX (RELAX_LEN (fragP->fr_subtype), STATE_UNDEF);
    }

  return md_relax_table[fragP->fr_subtype].rlx_length;
}

void
md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
		 asection * sec ATTRIBUTE_UNUSED,
		 fragS * fragP)
{
  char * where = 0;
  int rela = -1;
  int i;
  struct rcodes_s * cc = NULL;
  struct hcodes_s * hc = NULL;

  switch (fragP->fr_subtype)
    {
    case ENCODE_RELAX (STATE_UNCOND_BRANCH, STATE_BITS10):
    case ENCODE_RELAX (STATE_SIMPLE_BRANCH, STATE_BITS10):
    case ENCODE_RELAX (STATE_NOOV_BRANCH, STATE_BITS10):
      /* We do not have to convert anything here.
         Just apply a fix.  */
      rela = BFD_RELOC_MSP430_10_PCREL;
      break;

    case ENCODE_RELAX (STATE_UNCOND_BRANCH, STATE_WORD):
    case ENCODE_RELAX (STATE_UNCOND_BRANCH, STATE_UNDEF):
      /* Convert uncond branch jmp lab -> br lab.  */
      if (target_is_430x ())
	cc = msp430x_rcodes + 7;
      else
	cc = msp430_rcodes + 7;
      where = fragP->fr_literal + fragP->fr_fix;
      bfd_putl16 (cc->lop0, where);
      rela = BFD_RELOC_MSP430_RL_PCREL;
      fragP->fr_fix += 2;
      break;

    case ENCODE_RELAX (STATE_SIMPLE_BRANCH, STATE_WORD):
    case ENCODE_RELAX (STATE_SIMPLE_BRANCH, STATE_UNDEF):
      {
	/* Other simple branches.  */
	int insn = bfd_getl16 (fragP->fr_opcode);

	insn &= 0xffff;
	/* Find actual instruction.  */
	if (target_is_430x ())
	  {
	    for (i = 0; i < 7 && !cc; i++)
	      if (msp430x_rcodes[i].sop == insn)
		cc = msp430x_rcodes + i;
	  }
	else
	  {
	    for (i = 0; i < 7 && !cc; i++)
	      if (msp430_rcodes[i].sop == insn)
		cc = & msp430_rcodes[i];
	  }

	if (!cc || !cc->name)
	  as_fatal (_("internal inconsistency problem in %s: insn %04lx"),
		    __FUNCTION__, (long) insn);
	where = fragP->fr_literal + fragP->fr_fix;
	bfd_putl16 (cc->lop0, where);
	bfd_putl16 (cc->lop1, where + 2);
	rela = BFD_RELOC_MSP430_RL_PCREL;
	fragP->fr_fix += 4;
      }
      break;

    case ENCODE_RELAX (STATE_NOOV_BRANCH, STATE_WORD):
    case ENCODE_RELAX (STATE_NOOV_BRANCH, STATE_UNDEF):
      if (target_is_430x ())
	cc = msp430x_rcodes + 6;
      else
	cc = msp430_rcodes + 6;
      where = fragP->fr_literal + fragP->fr_fix;
      bfd_putl16 (cc->lop0, where);
      bfd_putl16 (cc->lop1, where + 2);
      bfd_putl16 (cc->lop2, where + 4);
      rela = BFD_RELOC_MSP430_RL_PCREL;
      fragP->fr_fix += 6;
      break;

    case ENCODE_RELAX (STATE_EMUL_BRANCH, STATE_BITS10):
      {
	int insn = bfd_getl16 (fragP->fr_opcode + 2);

	insn &= 0xffff;
	if (target_is_430x ())
	  {
	    for (i = 0; i < 4 && !hc; i++)
	      if (msp430x_hcodes[i].op1 == insn)
		hc = msp430x_hcodes + i;
	  }
	else
	  {
	    for (i = 0; i < 4 && !hc; i++)
	      if (msp430_hcodes[i].op1 == insn)
		hc = &msp430_hcodes[i];
	  }
	if (!hc || !hc->name)
	  as_fatal (_("internal inconsistency problem in %s: ext. insn %04lx"),
	      __FUNCTION__, (long) insn);
	rela = BFD_RELOC_MSP430_10_PCREL;
	/* Apply a fix for a first label if necessary.
	   another fix will be applied to the next word of insn anyway.  */
	if (hc->tlab == 2)
	  fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
		   fragP->fr_offset, TRUE, rela);
	fragP->fr_fix += 2;
      }

      break;

    case ENCODE_RELAX (STATE_EMUL_BRANCH, STATE_WORD):
    case ENCODE_RELAX (STATE_EMUL_BRANCH, STATE_UNDEF):
      {
	int insn = bfd_getl16 (fragP->fr_opcode + 2);

	insn &= 0xffff;
	if (target_is_430x ())
	  {
	    for (i = 0; i < 4 && !hc; i++)
	      if (msp430x_hcodes[i].op1 == insn)
		hc = msp430x_hcodes + i;
	  }
	else
	  {
	    for (i = 0; i < 4 && !hc; i++)
	      if (msp430_hcodes[i].op1 == insn)
		hc = & msp430_hcodes[i];
	  }
	if (!hc || !hc->name)
	  as_fatal (_("internal inconsistency problem in %s: ext. insn %04lx"),
	      __FUNCTION__, (long) insn);
	rela = BFD_RELOC_MSP430_RL_PCREL;
	where = fragP->fr_literal + fragP->fr_fix;
	bfd_putl16 (hc->lop0, where);
	bfd_putl16 (hc->lop1, where + 2);
	bfd_putl16 (hc->lop2, where + 4);
	fragP->fr_fix += 6;
      }
      break;

    default:
      as_fatal (_("internal inconsistency problem in %s:  %lx"),
		__FUNCTION__, (long) fragP->fr_subtype);
      break;
    }

  /* Now apply fix.  */
  fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
	   fragP->fr_offset, TRUE, rela);
  /* Just fixed 2 bytes.  */
  fragP->fr_fix += 2;
}

/* Relax fragment. Mostly stolen from hc11 and mcore
   which arches I think I know.  */

long
msp430_relax_frag (segT seg ATTRIBUTE_UNUSED, fragS * fragP,
		   long stretch ATTRIBUTE_UNUSED)
{
  long growth;
  offsetT aim = 0;
  symbolS *symbolP;
  const relax_typeS *this_type;
  const relax_typeS *start_type;
  relax_substateT next_state;
  relax_substateT this_state;
  const relax_typeS *table = md_relax_table;

  /* Nothing to be done if the frag has already max size.  */
  if (RELAX_STATE (fragP->fr_subtype) == STATE_UNDEF
      || RELAX_STATE (fragP->fr_subtype) == STATE_WORD)
    return 0;

  if (RELAX_STATE (fragP->fr_subtype) == STATE_BITS10)
    {
      symbolP = fragP->fr_symbol;
      if (symbol_resolved_p (symbolP))
	as_fatal (_("internal inconsistency problem in %s: resolved symbol"),
		  __FUNCTION__);
      /* We know the offset. calculate a distance.  */
      aim = S_GET_VALUE (symbolP) - fragP->fr_address - fragP->fr_fix;
    }

  if (!msp430_enable_relax)
    {
      /* Relaxation is not enabled. So, make all jump as long ones
         by setting 'aim' to quite high value.  */
      aim = 0x7fff;
    }
  
  this_state = fragP->fr_subtype;
  start_type = this_type = table + this_state;

  if (aim < 0)
    {
      /* Look backwards.  */
      for (next_state = this_type->rlx_more; next_state;)
	if (aim >= this_type->rlx_backward || !this_type->rlx_backward)
	  next_state = 0;
	else
	  {
	    /* Grow to next state.  */
	    this_state = next_state;
	    this_type = table + this_state;
	    next_state = this_type->rlx_more;
	  }
    }
  else
    {
      /* Look forwards.  */
      for (next_state = this_type->rlx_more; next_state;)
	if (aim <= this_type->rlx_forward || !this_type->rlx_forward)
	  next_state = 0;
	else
	  {
	    /* Grow to next state.  */
	    this_state = next_state;
	    this_type = table + this_state;
	    next_state = this_type->rlx_more;
	  }
    }

  growth = this_type->rlx_length - start_type->rlx_length;
  if (growth != 0)
    fragP->fr_subtype = this_state;
  return growth;
}

/* Return FALSE if the fixup in fixp should be left alone and not
   adjusted.   We return FALSE here so that linker relaxation will
   work.  */

bfd_boolean
msp430_fix_adjustable (struct fix *fixp ATTRIBUTE_UNUSED)
{
  /* If the symbol is in a non-code section then it should be OK.  */
  if (fixp->fx_addsy
      && ((S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_CODE) == 0))
    return TRUE;
	   
  return FALSE;
}

/* Set the contents of the .MSP430.attributes section.  */

void
msp430_md_end (void)
{
  bfd_elf_add_proc_attr_int (stdoutput, OFBA_MSPABI_Tag_ISA,
			     target_is_430x () ? 2 : 1);

  bfd_elf_add_proc_attr_int (stdoutput, OFBA_MSPABI_Tag_Code_Model,
			     large_model ? 2 : 1);

  bfd_elf_add_proc_attr_int (stdoutput, OFBA_MSPABI_Tag_Data_Model,
			     large_model ? 2 : 1);
}

/* Returns FALSE if there is a msp430 specific reason why the
   subtraction of two same-section symbols cannot be computed by
   the assembler.  */

bfd_boolean
msp430_allow_local_subtract (expressionS * left,
			     expressionS * right,
			     segT section)
{
  /* If the symbols are not in a code section then they are OK.  */
  if ((section->flags & SEC_CODE) == 0)
    return TRUE;

  if (S_IS_GAS_LOCAL (left->X_add_symbol) || S_IS_GAS_LOCAL (right->X_add_symbol))
    return TRUE;

  if (left->X_add_symbol == right->X_add_symbol)
    return TRUE;

  /* We have to assume that there may be instructions between the
     two symbols and that relaxation may increase the distance between
     them.  */
  return FALSE;
}
