Fixes a snafu checking the size of a 20-bit immediate.
[platform/upstream/binutils.git] / gas / config / tc-msp430.c
index 0fed29a..25ec0ee 100644 (file)
@@ -1,6 +1,6 @@
 /* tc-msp430.c -- Assembler code for the Texas Instruments MSP430
 
-  Copyright (C) 2002-2013 Free Software Foundation, Inc.
+  Copyright (C) 2002-2014 Free Software Foundation, Inc.
   Contributed by Dmitry Diky <diwil@mail.ru>
 
   This file is part of GAS, the GNU Assembler.
@@ -257,48 +257,43 @@ relax_typeS md_relax_table[] =
 
 #define MAX_OP_LEN     256
 
-struct mcu_type_s
+typedef enum msp_isa
 {
-  char * name;
-  int isa;
-  int mach;
-};
+  MSP_ISA_430,
+  MSP_ISA_430X,
+  MSP_ISA_430Xv2
+} msp_isa;
+
+static enum msp_isa selected_isa = MSP_ISA_430Xv2;
+
+static inline bfd_boolean
+target_is_430x (void)
+{
+  return selected_isa >= MSP_ISA_430X;
+}
+
+static inline bfd_boolean
+target_is_430xv2 (void)
+{
+  return selected_isa == MSP_ISA_430Xv2;
+}
 
-#define MSP430_ISA_11   11
-#define MSP430_ISA_110 110
-#define MSP430_ISA_12   12
-#define MSP430_ISA_13   13
-#define MSP430_ISA_14   14
-#define MSP430_ISA_15   15
-#define MSP430_ISA_16   16
-#define MSP430_ISA_20   20
-#define MSP430_ISA_21   21
-#define MSP430_ISA_22   22
-#define MSP430_ISA_23   23
-#define MSP430_ISA_24   24
-#define MSP430_ISA_26   26
-#define MSP430_ISA_31   31
-#define MSP430_ISA_32   32
-#define MSP430_ISA_33   33
-#define MSP430_ISA_41   41
-#define MSP430_ISA_42   42
-#define MSP430_ISA_43   43
-#define MSP430_ISA_44   44
-#define MSP430X_ISA     45
-#define MSP430_ISA_46   46
-#define MSP430_ISA_47   47
-#define MSP430_ISA_54   54
-
-/* 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.
+/* Generate an absolute 16-bit relocation.
+   For the 430X we generate a relocation without linker range checking
+    if the value is being used in an extended (ie 20-bit) instruction,
+    otherwise if have a shifted expression we use a HI reloc.
    For the 430 we generate a relocation without assembler range checking
-   if we are handling an immediate value or a byte-width instruction.  */
+    if we are handling an immediate value or a byte-width instruction.  */
+
 #undef  CHECK_RELOC_MSP430
-#define CHECK_RELOC_MSP430                                  \
-  (msp430_mcu->isa == MSP430X_ISA                           \
-   ? (extended_op ? BFD_RELOC_16 : BFD_RELOC_MSP430X_ABS16)  \
-   : ((imm_op || byte_op)                                   \
+#define CHECK_RELOC_MSP430(OP)                         \
+  (target_is_430x ()                                   \
+  ? (extended_op                                       \
+     ? BFD_RELOC_16                                    \
+     : ((OP).vshift == 1)                              \
+     ? BFD_RELOC_MSP430_ABS_HI16                       \
+     : BFD_RELOC_MSP430X_ABS16)                                \
+   : ((imm_op || byte_op)                              \
       ? BFD_RELOC_MSP430_16_BYTE : BFD_RELOC_MSP430_16))
 
 /* Generate a 16-bit pc-relative relocation.
@@ -307,186 +302,11 @@ struct mcu_type_s
    if we are handling an immediate value or a byte-width instruction.  */
 #undef  CHECK_RELOC_MSP430_PCREL
 #define CHECK_RELOC_MSP430_PCREL                            \
-  (msp430_mcu->isa == MSP430X_ISA                           \
+  (target_is_430x ()                                        \
    ? BFD_RELOC_MSP430X_PCR16                                \
    : (imm_op || byte_op)                                    \
    ? BFD_RELOC_MSP430_16_PCREL_BYTE : BFD_RELOC_MSP430_16_PCREL)
 
-static struct mcu_type_s mcu_types[] =
-{
-  {"msp1",        MSP430_ISA_11, bfd_mach_msp11},
-  {"msp2",        MSP430_ISA_14, bfd_mach_msp14},
-  {"msp3",        MSP430_ISA_20, bfd_mach_msp20},
-  {"msp4",        MSP430_ISA_24, bfd_mach_msp24},
-  {"msp5",        MSP430_ISA_31, bfd_mach_msp31},
-  {"msp6",        MSP430_ISA_42, bfd_mach_msp42},
-
-  {"msp430x110",  MSP430_ISA_11, bfd_mach_msp11},
-  {"msp430x112",  MSP430_ISA_11, bfd_mach_msp11},
-  {"msp430x1101", MSP430_ISA_110, bfd_mach_msp110},
-  {"msp430x1111", MSP430_ISA_110, bfd_mach_msp110},
-  {"msp430x1121", MSP430_ISA_110, bfd_mach_msp110},
-  {"msp430x1122", MSP430_ISA_11, bfd_mach_msp110},
-  {"msp430x1132", MSP430_ISA_11, bfd_mach_msp110},
-
-  {"msp430x122",  MSP430_ISA_12, bfd_mach_msp12},
-  {"msp430x123",  MSP430_ISA_12, bfd_mach_msp12},
-  {"msp430x1222", MSP430_ISA_12, bfd_mach_msp12},
-  {"msp430x1232", MSP430_ISA_12, bfd_mach_msp12},
-
-  {"msp430x133",  MSP430_ISA_13, bfd_mach_msp13},
-  {"msp430x135",  MSP430_ISA_13, bfd_mach_msp13},
-  {"msp430x1331", MSP430_ISA_13, bfd_mach_msp13},
-  {"msp430x1351", MSP430_ISA_13, bfd_mach_msp13},
-  {"msp430x147",  MSP430_ISA_14, bfd_mach_msp14},
-  {"msp430x148",  MSP430_ISA_14, bfd_mach_msp14},
-  {"msp430x149",  MSP430_ISA_14, bfd_mach_msp14},
-  {"msp430x1471", MSP430_ISA_14, bfd_mach_msp14},
-  {"msp430x1481", MSP430_ISA_14, bfd_mach_msp14},
-  {"msp430x1491", MSP430_ISA_14, bfd_mach_msp14},
-
-  {"msp430x155",  MSP430_ISA_15, bfd_mach_msp15},
-  {"msp430x156",  MSP430_ISA_15, bfd_mach_msp15},
-  {"msp430x157",  MSP430_ISA_15, bfd_mach_msp15},
-  {"msp430x167",  MSP430_ISA_16, bfd_mach_msp16},
-  {"msp430x168",  MSP430_ISA_16, bfd_mach_msp16},
-  {"msp430x169",  MSP430_ISA_16, bfd_mach_msp16},
-  {"msp430x1610", MSP430_ISA_16, bfd_mach_msp16},
-  {"msp430x1611", MSP430_ISA_16, bfd_mach_msp16},
-  {"msp430x1612", MSP430_ISA_16, bfd_mach_msp16},
-
-  {"msp430x2001", MSP430_ISA_20, bfd_mach_msp20},
-  {"msp430x2011", MSP430_ISA_20, bfd_mach_msp20},
-  {"msp430x2002", MSP430_ISA_20, bfd_mach_msp20},
-  {"msp430x2012", MSP430_ISA_20, bfd_mach_msp20},
-  {"msp430x2003", MSP430_ISA_20, bfd_mach_msp20},
-  {"msp430x2013", MSP430_ISA_20, bfd_mach_msp20 },
-
-  {"msp430x2101", MSP430_ISA_21, bfd_mach_msp21},
-  {"msp430x2111", MSP430_ISA_21, bfd_mach_msp21},
-  {"msp430x2112", MSP430_ISA_21, bfd_mach_msp21},
-  {"msp430x2121", MSP430_ISA_21, bfd_mach_msp21},
-  {"msp430x2131", MSP430_ISA_21, bfd_mach_msp21},
-  {"msp430x2132", MSP430_ISA_21, bfd_mach_msp21},
-
-  {"msp430x2232", MSP430_ISA_22, bfd_mach_msp22},
-  {"msp430x2234", MSP430_ISA_22, bfd_mach_msp22},
-  {"msp430x2252", MSP430_ISA_22, bfd_mach_msp22},
-  {"msp430x2254", MSP430_ISA_22, bfd_mach_msp22},
-  {"msp430x2272", MSP430_ISA_22, bfd_mach_msp22},
-  {"msp430x2274", MSP430_ISA_22, bfd_mach_msp22},
-
-  {"msp430x233",  MSP430_ISA_23, bfd_mach_msp23},
-  {"msp430x235",  MSP430_ISA_23, bfd_mach_msp23},
-  {"msp430x2330", MSP430_ISA_23, bfd_mach_msp23},
-  {"msp430x2350", MSP430_ISA_23, bfd_mach_msp23},
-  {"msp430x2370", MSP430_ISA_23, bfd_mach_msp23},
-
-  {"msp430x247",  MSP430_ISA_24, bfd_mach_msp24},
-  {"msp430x2471", MSP430_ISA_24, bfd_mach_msp24},
-  {"msp430x248",  MSP430_ISA_24, bfd_mach_msp24},
-  {"msp430x2481", MSP430_ISA_24, bfd_mach_msp24},
-  {"msp430x249",  MSP430_ISA_24, bfd_mach_msp24},
-  {"msp430x2491", MSP430_ISA_24, bfd_mach_msp24},
-  {"msp430x2410", MSP430_ISA_24, bfd_mach_msp24},
-  {"msp430x2416", MSP430_ISA_24, bfd_mach_msp24},
-  {"msp430x2417", MSP430_ISA_24, bfd_mach_msp24},
-  {"msp430x2418", MSP430_ISA_24, bfd_mach_msp24},
-  {"msp430x2419", MSP430_ISA_24, bfd_mach_msp24},
-
-  {"msp430x2616", MSP430_ISA_26, bfd_mach_msp26},
-  {"msp430x2617", MSP430_ISA_26, bfd_mach_msp26},
-  {"msp430x2618", MSP430_ISA_26, bfd_mach_msp26},
-  {"msp430x2619", MSP430_ISA_26, bfd_mach_msp26},
-
-  {"msp430x311",  MSP430_ISA_31, bfd_mach_msp31},
-  {"msp430x312",  MSP430_ISA_31, bfd_mach_msp31},
-  {"msp430x313",  MSP430_ISA_31, bfd_mach_msp31},
-  {"msp430x314",  MSP430_ISA_31, bfd_mach_msp31},
-  {"msp430x315",  MSP430_ISA_31, bfd_mach_msp31},
-  {"msp430x323",  MSP430_ISA_32, bfd_mach_msp32},
-  {"msp430x325",  MSP430_ISA_32, bfd_mach_msp32},
-  {"msp430x336",  MSP430_ISA_33, bfd_mach_msp33},
-  {"msp430x337",  MSP430_ISA_33, bfd_mach_msp33},
-
-  {"msp430x412",  MSP430_ISA_41, bfd_mach_msp41},
-  {"msp430x413",  MSP430_ISA_41, bfd_mach_msp41},
-  {"msp430x415",  MSP430_ISA_41, bfd_mach_msp41},
-  {"msp430x417",  MSP430_ISA_41, bfd_mach_msp41},
-
-  {"msp430x423",  MSP430_ISA_42, bfd_mach_msp42},
-  {"msp430x425",  MSP430_ISA_42, bfd_mach_msp42},
-  {"msp430x427",  MSP430_ISA_42, bfd_mach_msp42},
-  {"msp430x4250", MSP430_ISA_42, bfd_mach_msp42},
-  {"msp430x4260", MSP430_ISA_42, bfd_mach_msp42},
-  {"msp430x4270", MSP430_ISA_42, bfd_mach_msp42},
-  {"msp430xG4250",MSP430_ISA_42, bfd_mach_msp42},
-  {"msp430xG4260",MSP430_ISA_42, bfd_mach_msp42},
-  {"msp430xG4270",MSP430_ISA_42, bfd_mach_msp42},
-
-  {"msp430xE423", MSP430_ISA_42, bfd_mach_msp42},
-  {"msp430xE4232",MSP430_ISA_42, bfd_mach_msp42},
-  {"msp430xE4242",MSP430_ISA_42, bfd_mach_msp42},
-  {"msp430xE4252",MSP430_ISA_42, bfd_mach_msp42},
-  {"msp430xE425", MSP430_ISA_42, bfd_mach_msp42},
-  {"msp430xE427", MSP430_ISA_42, bfd_mach_msp42},
-  {"msp430xE4272",MSP430_ISA_42, bfd_mach_msp42},
-
-  {"msp430xW423", MSP430_ISA_42, bfd_mach_msp42},
-  {"msp430xW425", MSP430_ISA_42, bfd_mach_msp42},
-  {"msp430xW427", MSP430_ISA_42, bfd_mach_msp42},
-
-  {"msp430xG437", MSP430_ISA_43, bfd_mach_msp43},
-  {"msp430xG438", MSP430_ISA_43, bfd_mach_msp43},
-  {"msp430xG439", MSP430_ISA_43, bfd_mach_msp43},
-
-  {"msp430x435",  MSP430_ISA_43, bfd_mach_msp43},
-  {"msp430x4351", MSP430_ISA_43, bfd_mach_msp43},
-  {"msp430x436",  MSP430_ISA_43, bfd_mach_msp43},
-  {"msp430x4361", MSP430_ISA_43, bfd_mach_msp43},
-  {"msp430x437",  MSP430_ISA_43, bfd_mach_msp43},
-  {"msp430x4371", MSP430_ISA_43, bfd_mach_msp43},
-
-  {"msp430x447",  MSP430_ISA_44, bfd_mach_msp44},
-  {"msp430x448",  MSP430_ISA_44, bfd_mach_msp44},
-  {"msp430x449",  MSP430_ISA_44, bfd_mach_msp44},
-
-  {"msp430xG4616",MSP430_ISA_46, bfd_mach_msp46},
-  {"msp430xG4617",MSP430_ISA_46, bfd_mach_msp46},
-  {"msp430xG4618",MSP430_ISA_46, bfd_mach_msp46},
-  {"msp430xG4619",MSP430_ISA_46, bfd_mach_msp46},
-
-  {"msp430x4783", MSP430_ISA_47, bfd_mach_msp47},
-  {"msp430x4784", MSP430_ISA_47, bfd_mach_msp47},
-  {"msp430x4793", MSP430_ISA_47, bfd_mach_msp47},
-  {"msp430x4794", MSP430_ISA_47, bfd_mach_msp47},
-  {"msp430x47166",MSP430_ISA_47, bfd_mach_msp47},
-  {"msp430x47176",MSP430_ISA_47, bfd_mach_msp47},
-  {"msp430x47186",MSP430_ISA_47, bfd_mach_msp47},
-  {"msp430x47196",MSP430_ISA_47, bfd_mach_msp47},
-  {"msp430x47167",MSP430_ISA_47, bfd_mach_msp47},
-  {"msp430x47177",MSP430_ISA_47, bfd_mach_msp47},
-  {"msp430x47187",MSP430_ISA_47, bfd_mach_msp47},
-  {"msp430x47197",MSP430_ISA_47, bfd_mach_msp47},
-
-  {"msp430x5418", MSP430_ISA_54, bfd_mach_msp54},
-  {"msp430x5419", MSP430_ISA_54, bfd_mach_msp54},
-  {"msp430x5435", MSP430_ISA_54, bfd_mach_msp54},
-  {"msp430x5436", MSP430_ISA_54, bfd_mach_msp54},
-  {"msp430x5437", MSP430_ISA_54, bfd_mach_msp54},
-  {"msp430x5438", MSP430_ISA_54, bfd_mach_msp54},
-
-  {"msp430X",     MSP430X_ISA,   bfd_mach_msp430x},
-
-  {NULL, 0, 0}
-};
-
-
-static struct mcu_type_s default_mcu =
-    { "msp430x11", MSP430_ISA_11, bfd_mach_msp11 };
-
-static struct mcu_type_s * msp430_mcu = & default_mcu;
-
 /* 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.
@@ -851,62 +671,129 @@ extract_word (char * from, char * to, int limit)
 #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_INTR_NOPS 'n'
+static bfd_boolean gen_interrupt_nops = FALSE;
+#define OPTION_WARN_INTR_NOPS 'y'
+#define OPTION_NO_WARN_INTR_NOPS 'Y'
+static bfd_boolean warn_interrupt_nops = TRUE;
+#define OPTION_MCPU 'c'
+#define OPTION_MOVE_DATA 'd'
+static bfd_boolean move_data = FALSE;
 
 static void
-msp430_set_arch (int dummy ATTRIBUTE_UNUSED)
+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_MMCU, str);
-  bfd_set_arch_mach (stdoutput, TARGET_ARCH, msp430_mcu->mach);
+  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)
+/* This is the full list of MCU names that are known to only
+   support the 430 ISA.  */
+static const char * msp430_mcu_names [] =
 {
-  int i;
-
-  fprintf (stream, _("Known MCU names:\n"));
-
-  for (i = 0; mcu_types[i].name; i++)
-    {
-      fprintf (stream, "%13.13s", mcu_types[i].name);
-      if ((i % 6) == 5)
-       fprintf (stream, "\n");
-    }
-
-  fprintf (stream, "\n");
-}
+"msp430afe221",        "msp430afe222", "msp430afe223", "msp430afe231", 
+"msp430afe232",        "msp430afe233", "msp430afe251", "msp430afe252", 
+"msp430afe253",        "msp430c091",   "msp430c092",   "msp430c111",   
+"msp430c1111", "msp430c112",   "msp430c1121",  "msp430c1331",  
+"msp430c1351", "msp430c311s",  "msp430c312",   "msp430c313",   
+"msp430c314",  "msp430c315",   "msp430c323",   "msp430c325",   
+"msp430c336",  "msp430c337",   "msp430c412",   "msp430c413",   
+"msp430e112",  "msp430e313",   "msp430e315",   "msp430e325",   
+"msp430e337",  "msp430f110",   "msp430f1101",  "msp430f1101a", 
+"msp430f1111", "msp430f1111a", "msp430f112",   "msp430f1121",  
+"msp430f1121a",        "msp430f1122",  "msp430f1132",  "msp430f122",   
+"msp430f1222", "msp430f123",   "msp430f1232",  "msp430f133",   
+"msp430f135",  "msp430f147",   "msp430f1471",  "msp430f148",   
+"msp430f1481", "msp430f149",   "msp430f1491",  "msp430f155",   
+"msp430f156",  "msp430f157",   "msp430f1610",  "msp430f1611",  
+"msp430f1612", "msp430f167",   "msp430f168",   "msp430f169",   
+"msp430f2001", "msp430f2002",  "msp430f2003",  "msp430f2011",  
+"msp430f2012", "msp430f2013",  "msp430f2101",  "msp430f2111",  
+"msp430f2112", "msp430f2121",  "msp430f2122",  "msp430f2131",  
+"msp430f2132", "msp430f2232",  "msp430f2234",  "msp430f2252",  
+"msp430f2254", "msp430f2272",  "msp430f2274",  "msp430f233",   
+"msp430f2330", "msp430f235",   "msp430f2350",  "msp430f2370",  
+"msp430f2410", "msp430f247",   "msp430f2471",  "msp430f248",   
+"msp430f2481", "msp430f249",   "msp430f2491",  "msp430f412",   
+"msp430f413",  "msp430f4132",  "msp430f415",   "msp430f4152",  
+"msp430f417",  "msp430f423",   "msp430f423a",  "msp430f425",   
+"msp430f4250", "msp430f425a",  "msp430f4260",  "msp430f427",   
+"msp430f4270", "msp430f427a",  "msp430f435",   "msp430f4351",  
+"msp430f436",  "msp430f4361",  "msp430f437",   "msp430f4371",  
+"msp430f438",  "msp430f439",   "msp430f447",   "msp430f448",   
+"msp430f4481", "msp430f449",   "msp430f4491",  "msp430f477",   
+"msp430f478",  "msp430f4783",  "msp430f4784",  "msp430f479",   
+"msp430f4793", "msp430f4794",  "msp430fe423",  "msp430fe4232", 
+"msp430fe423a",        "msp430fe4242", "msp430fe425",  "msp430fe4252", 
+"msp430fe425a",        "msp430fe427",  "msp430fe4272", "msp430fe427a", 
+"msp430fg4250",        "msp430fg4260", "msp430fg4270", "msp430fg437",  
+"msp430fg438", "msp430fg439",  "msp430fg477",  "msp430fg478",  
+"msp430fg479", "msp430fw423",  "msp430fw425",  "msp430fw427",  
+"msp430fw428", "msp430fw429",  "msp430g2001",  "msp430g2101",  
+"msp430g2102", "msp430g2111",  "msp430g2112",  "msp430g2113",  
+"msp430g2121", "msp430g2131",  "msp430g2132",  "msp430g2152",  
+"msp430g2153", "msp430g2201",  "msp430g2202",  "msp430g2203",  
+"msp430g2210", "msp430g2211",  "msp430g2212",  "msp430g2213",  
+"msp430g2221", "msp430g2230",  "msp430g2231",  "msp430g2232",  
+"msp430g2233", "msp430g2252",  "msp430g2253",  "msp430g2302",  
+"msp430g2303", "msp430g2312",  "msp430g2313",  "msp430g2332",  
+"msp430g2333", "msp430g2352",  "msp430g2353",  "msp430g2402",  
+"msp430g2403", "msp430g2412",  "msp430g2413",  "msp430g2432",  
+"msp430g2433", "msp430g2444",  "msp430g2452",  "msp430g2453",  
+"msp430g2513", "msp430g2533",  "msp430g2544",  "msp430g2553",  
+"msp430g2744", "msp430g2755",  "msp430g2855",  "msp430g2955",  
+"msp430i2020", "msp430i2021",  "msp430i2030",  "msp430i2031",  
+"msp430i2040", "msp430i2041",  "msp430l092",   "msp430p112",   
+"msp430p313",  "msp430p315",   "msp430p315s",  "msp430p325",   
+"msp430p337",  "msp430tch5e"
+};
 
 int
 md_parse_option (int c, char * arg)
 {
-  int i;
-
   switch (c)
     {
     case OPTION_MMCU:
-      for (i = 0; mcu_types[i].name; ++i)
-       if (strcmp (mcu_types[i].name, arg) == 0)
-         break;
-
-      if (!mcu_types[i].name)
+      if (arg == NULL)
+       as_fatal (_("MCU option requires a name\n"));
+
+      if (strcasecmp ("msp430", arg) == 0)
+       selected_isa = MSP_ISA_430;
+      else if (strcasecmp ("msp430xv2", arg) == 0)
+       selected_isa = MSP_ISA_430Xv2;
+      else if (strcasecmp ("msp430x", arg) == 0)
+       selected_isa = MSP_ISA_430X;
+      else
        {
-         show_mcu_list (stderr);
-         as_fatal (_("unknown MCU: %s\n"), arg);
+         int i;
+
+         for (i = sizeof msp430_mcu_names / sizeof msp430_mcu_names[0]; i--;)
+           if (strcasecmp (msp430_mcu_names[i], arg) == 0)
+             {
+               selected_isa = MSP_ISA_430;
+               break;
+           }
        }
+      /* It is not an error if we do not match the MCU name.  */
+      return 1;
 
-      if (msp430_mcu == &default_mcu || msp430_mcu->mach == mcu_types[i].mach)
-       msp430_mcu = &mcu_types[i];
-      else if (msp430_mcu->mach == bfd_mach_msp430x)
-       /* Allow switching to a lesser architecture.  */
-       msp430_mcu = mcu_types + i;
+    case OPTION_MCPU:
+      if (strcmp (arg, "430") == 0
+         || strcasecmp (arg, "msp430") == 0)
+       selected_isa = MSP_ISA_430;
+      else if (strcasecmp (arg, "430x") == 0
+              || strcasecmp (arg, "msp430x") == 0)
+       selected_isa = MSP_ISA_430X;
+      else if (strcasecmp (arg, "430xv2") == 0
+              || strcasecmp (arg, "msp430xv2") == 0)
+       selected_isa = MSP_ISA_430Xv2;
       else
-       as_fatal (_("redefinition of mcu type %s' to %s'"),
-                 msp430_mcu->name, mcu_types[i].name);
+       as_fatal (_("unrecognised argument to -mcpu option '%s'"), arg);
       return 1;
 
     case OPTION_RELAX:
@@ -924,28 +811,128 @@ md_parse_option (int c, char * arg)
     case OPTION_NO_INTR_NOPS:
       gen_interrupt_nops = FALSE;
       return 1;
+    case OPTION_INTR_NOPS:
+      gen_interrupt_nops = TRUE;
+      return 1;
+
+    case OPTION_WARN_INTR_NOPS:
+      warn_interrupt_nops = TRUE;
+      return 1;
+    case OPTION_NO_WARN_INTR_NOPS:
+      warn_interrupt_nops = FALSE;
+      return 1;
+
+    case OPTION_MOVE_DATA:
+      move_data = TRUE;
+      return 1;
     }
 
   return 0;
 }
 
+/* The intention here is to have the mere presence of these sections
+   cause the object to have a reference to a well-known symbol.  This
+   reference pulls in the bits of the runtime (crt0) that initialize
+   these sections.  Thus, for example, the startup code to call
+   memset() to initialize .bss will only be linked in when there is a
+   non-empty .bss section.  Otherwise, the call would exist but have a
+   zero length parameter, which is a waste of memory and cycles.
+
+   The code which initializes these sections should have a global
+   label for these symbols, and should be marked with KEEP() in the
+   linker script.
+ */
+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 (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);
+}
+
+void
+msp430_frob_section (asection *sec)
+{
+  const char *name = sec->name;
+
+  if (sec->size == 0)
+    return;
+
+  if (strncmp (name, ".bss", 4) == 0
+      || strncmp (name, ".gnu.linkonce.b.", 16) == 0)
+    (void) symbol_find_or_make ("__crt0_init_bss");
+
+  if (strncmp (name, ".data", 5) == 0
+      || strncmp (name, ".gnu.linkonce.d.", 16) == 0)
+    (void) symbol_find_or_make ("__crt0_movedata");
+}
+
+static void
+msp430_lcomm (int ignore ATTRIBUTE_UNUSED)
+{
+  symbolS *symbolP = s_comm_internal (0, s_lcomm_internal);
+
+  if (symbolP)
+    symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT;
+  (void) symbol_find_or_make ("__crt0_init_bss");
+}
+
+static void
+msp430_comm (int needs_align)
+{
+  s_comm_internal (needs_align, elf_common_parse);
+  (void) symbol_find_or_make ("__crt0_init_bss");
+}
+
+static void
+msp430_refsym (int arg ATTRIBUTE_UNUSED)
+{
+  char sym_name[1024];
+  input_line_pointer = extract_word (input_line_pointer, sym_name, 1024);
+
+  (void) symbol_find_or_make (sym_name);
+}
 
 const pseudo_typeS md_pseudo_table[] =
 {
-  {"arch", msp430_set_arch, 0},
+  {"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},
+  {"refsym", msp430_refsym, 0},
+  {"comm", msp430_comm, 0},
+  {"lcomm", msp430_lcomm, 0},
   {NULL, NULL, 0}
 };
 
-const char *md_shortopts = "m:";
+const char *md_shortopts = "mm:,mP,mQ,ml,mN,mn,my,mY";
 
 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},
+  {"mn", no_argument, NULL, OPTION_INTR_NOPS},
+  {"mY", no_argument, NULL, OPTION_NO_WARN_INTR_NOPS},
+  {"my", no_argument, NULL, OPTION_WARN_INTR_NOPS},
+  {"md", no_argument, NULL, OPTION_MOVE_DATA},
   {NULL, no_argument, NULL, 0}
 };
 
@@ -956,16 +943,23 @@ md_show_usage (FILE * stream)
 {
   fprintf (stream,
           _("MSP430 options:\n"
-            "  -mmcu=<msp430-name> select microcontroller type\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"));
-
-  show_mcu_list (stream);
+          _("  -mN - do not insert NOPs after changing interrupts (default)\n"));
+  fprintf (stream,
+          _("  -mn - insert a NOP after changing interrupts\n"));
+  fprintf (stream,
+          _("  -mY - do not warn about missing NOPs after changing interrupts\n"));
+  fprintf (stream,
+          _("  -my - warn about missing NOPs after changing interrupts (default)\n"));
+  fprintf (stream,
+          _("  -md - Force copying of data from ROM to RAM at startup\n"));
 }
 
 symbolS *
@@ -1006,7 +1000,8 @@ md_begin (void)
   for (opcode = msp430_opcodes; opcode->name; opcode++)
     hash_insert (msp430_hash, opcode->name, (char *) opcode);
 
-  bfd_set_arch_mach (stdoutput, TARGET_ARCH, msp430_mcu->mach);
+  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.
@@ -1049,7 +1044,7 @@ static int
 msp430_srcoperand (struct msp430_operand_s * op,
                   char * l,
                   int bin,
-                  int * imm_op,
+                  bfd_boolean * imm_op,
                   bfd_boolean allow_20bit_values,
                   bfd_boolean constants_allowed)
 {
@@ -1069,7 +1064,7 @@ msp430_srcoperand (struct msp430_operand_s * op,
         hhi(x) - x = (x >> 48) & 0xffff
         The value _MUST_ be constant expression: #hlo(1231231231).  */
 
-      *imm_op = 1;
+      *imm_op = TRUE;
 
       if (strncasecmp (h, "#llo(", 5) == 0)
        {
@@ -1104,9 +1099,10 @@ msp430_srcoperand (struct msp430_operand_s * op,
 
       op->reg = 0;             /* Reg PC.  */
       op->am = 3;
-      op->ol = 1;              /* Immediate  will follow an instruction.  */
+      op->ol = 1;              /* Immediate will follow an instruction.  */
       __tl = h + 1 + rval;
       op->mode = OP_EXP;
+      op->vshift = vshift;
 
       parse_exp (__tl, &(op->exp));
       if (op->exp.X_op == O_constant)
@@ -1122,6 +1118,7 @@ msp430_srcoperand (struct msp430_operand_s * op,
            {
              x = (x >> 16) & 0xffff;
              op->exp.X_add_number = x;
+             op->vshift = 0;
            }
          else if (vshift > 1)
            {
@@ -1130,11 +1127,12 @@ msp430_srcoperand (struct msp430_operand_s * op,
              else
                op->exp.X_add_number = 0;       /* Nothing left.  */
              x = op->exp.X_add_number;
+             op->vshift = 0;
            }
 
          if (allow_20bit_values)
            {
-             if (op->exp.X_add_number > 0xfffff || op->exp.X_add_number < - (0x7ffff))
+             if (op->exp.X_add_number > 0xfffff || op->exp.X_add_number < -524288)
                {
                  as_bad (_("value 0x%x out of extended range."), x);
                  return 1;
@@ -1219,16 +1217,20 @@ msp430_srcoperand (struct msp430_operand_s * op,
        }
       else if (op->exp.X_op == O_symbol)
        {
+         if (vshift > 1)
+           as_bad (_("error: unsupported #foo() directive used on 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;
+             op->vshift = 0;
            }
          else
            {
@@ -1302,6 +1304,7 @@ msp430_srcoperand (struct msp430_operand_s * op,
       __tl = h + 1;
       parse_exp (__tl, &(op->exp));
       op->mode = OP_EXP;
+      op->vshift = 0;
       if (op->exp.X_op == O_constant)
        {
          int x = op->exp.X_add_number;
@@ -1359,6 +1362,13 @@ msp430_srcoperand (struct msp430_operand_s * op,
       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;
     }
 
@@ -1369,7 +1379,7 @@ msp430_srcoperand (struct msp430_operand_s * op,
       char *m = strrchr (l, ')');
       char *t;
 
-      *imm_op = 1;
+      *imm_op = TRUE;
 
       if (!h)
        break;
@@ -1402,6 +1412,7 @@ msp430_srcoperand (struct msp430_operand_s * op,
       __tl = l;
       *h = 0;
       op->mode = OP_EXP;
+      op->vshift = 0;
       parse_exp (__tl, &(op->exp));
       if (op->exp.X_op == O_constant)
        {
@@ -1463,6 +1474,7 @@ msp430_srcoperand (struct msp430_operand_s * op,
       /* An expression starting with a minus sign is a constant, not an address.  */
       op->am = (*l == '-' ? 3 : 1);
       op->ol = 1;
+      op->vshift = 0;
       __tl = l;
       parse_exp (__tl, &(op->exp));
       return 0;
@@ -1497,6 +1509,7 @@ msp430_dstoperand (struct msp430_operand_s * op,
       op->mode = OP_EXP;
       op->am = 1;
       op->ol = 1;
+      op->vshift = 0;
       parse_exp (__tl, &(op->exp));
 
       if (op->exp.X_op != O_constant || op->exp.X_add_number != 0)
@@ -1517,7 +1530,6 @@ msp430_dstoperand (struct msp430_operand_s * op,
   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
@@ -1777,6 +1789,10 @@ try_encode_mova (bfd_boolean imm_op,
   return 0;
 }
 
+static bfd_boolean check_for_nop = FALSE;
+
+#define is_opcode(NAME) (strcmp (opcode->name, NAME) == 0)
+
 /* Parse instruction operands.
    Return binary opcode.  */
 
@@ -1791,7 +1807,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
   struct msp430_operand_s op1, op2;
   int res = 0;
   static short ZEROS = 0;
-  int byte_op, imm_op;
+  bfd_boolean byte_op, imm_op;
   int op_length = 0;
   int fmt;
   int extended = 0x1800;
@@ -1800,6 +1816,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
   const char * error_message;
   static signed int repeat_count = 0;
   bfd_boolean fix_emitted;
+  bfd_boolean nop_check_needed = FALSE;
 
   /* Opcode is the one from opcodes table
      line contains something like
@@ -1807,27 +1824,85 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
      or
      .b @r2+, 5(R1).  */
 
-  /* Check if byte or word operation.  */
-  if (*line == '.' && TOLOWER (*(line + 1)) == 'b')
+  byte_op = FALSE;
+  addr_op = FALSE;
+  if (*line == '.')
     {
-      bin |= BYTE_OPERATION;
-      byte_op = 1;
+      bfd_boolean check = FALSE;
+      ++ line;
+
+      switch (TOLOWER (* line))
+       {
+       case 'b':
+         /* Byte operation.  */
+         bin |= BYTE_OPERATION;
+         byte_op = TRUE;
+         check = TRUE;
+         break;
+
+       case 'a':
+         /* "Address" ops work on 20-bit values.  */
+         addr_op = TRUE;
+         bin |= BYTE_OPERATION;
+         check = TRUE;
+         break;
+
+       case 'w':
+         /* Word operation - this is the default.  */
+         check = TRUE;
+         break;
+
+       case 0:
+       case ' ':
+       case '\n':
+       case '\r':
+         as_warn (_("no size modifier after period, .w assumed"));
+         break;
+
+       default:
+         as_bad (_("unrecognised instruction size modifier .%c"),
+                  * line);
+         return 0;
+       }
+
+      if (check)
+       {
+         ++ line;
+
+       }
     }
-  else
-    byte_op = 0;
 
-  /* "Address" ops work on 20-bit values.  */
-  if (*line == '.' && TOLOWER (*(line + 1)) == 'a')
+  if (*line && ! ISSPACE (*line))
     {
-      addr_op = TRUE;
-      bin |= BYTE_OPERATION;
+      as_bad (_("junk found after instruction: %s.%s"),
+             opcode->name, line);
+      return 0;
     }
-  else
-    addr_op = FALSE;
 
-  /* skip .[aAbwBW].  */
-  while (! ISSPACE (*line) && *line)
-    line++;
+  /* Catch the case where the programmer has used a ".a" size modifier on an
+     instruction that does not support it.  Look for an alternative extended
+     instruction that has the same name without the period.  Eg: "add.a"
+     becomes "adda".  Although this not an officially supported way of
+     specifing instruction aliases other MSP430 assemblers allow it.  So we
+     support it for compatibility purposes.  */
+  if (addr_op && opcode->fmt >= 0)
+    {
+      char * old_name = opcode->name;
+      char real_name[32];
+
+      sprintf (real_name, "%sa", old_name);
+      opcode = hash_find (msp430_hash, real_name);
+      if (opcode == NULL)
+       {
+         as_bad (_("instruction %s.a does not exist"), old_name);
+         return 0;
+       }
+#if 0 /* Enable for debugging.  */
+      as_warn ("treating %s.a as %s", old_name, real_name);
+#endif
+      addr_op = FALSE;
+      bin = opcode->bin_opcode;
+    }
 
   if (opcode->fmt != -1
       && opcode->insn_opnumb
@@ -1843,17 +1918,17 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
   memset (&op1, 0, sizeof (op1));
   memset (&op2, 0, sizeof (op2));
 
-  imm_op = 0;
+  imm_op = FALSE;
 
   if ((fmt = opcode->fmt) < 0)
     {
-      if (msp430_mcu->isa != MSP430X_ISA)
+      if (! target_is_430x ())
        {
          as_bad (_("instruction %s requires MSP430X mcu"),
                  opcode->name);
          return 0;
        }
-
+       
       fmt = (-fmt) - 1;
       extended_op = TRUE;
     }
@@ -1874,12 +1949,40 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
       repeat_count = 0;
     }
 
+  if (check_for_nop && is_opcode ("nop"))
+    check_for_nop = FALSE;
+
   switch (fmt)
     {
     case 0:                    /* Emulated.  */
       switch (opcode->insn_opnumb)
        {
        case 0:
+         if (is_opcode ("eint") || is_opcode ("dint"))
+           {
+             if (check_for_nop)
+               {
+                 if (warn_interrupt_nops)
+                   {
+                     if (gen_interrupt_nops)
+                       as_warn (_("NOP inserted between two instructions that change interrupt state"));
+                     else
+                       as_warn (_("a NOP might be needed here because of successive changes in interrupt state"));
+                   }
+
+                 if (gen_interrupt_nops)
+                   {
+                     /* Emit a NOP between 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);
+                   }
+               }
+
+             nop_check_needed = TRUE;
+           }
+
          /* Set/clear bits instructions.  */
          if (extended_op)
            {
@@ -1888,25 +1991,13 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
 
              /* Emit the extension word.  */
              insn_length += 2;
-             frag = frag_more (insn_length);
+             frag = frag_more (2);
              bfd_putl16 (extended, frag);
            }
+
          insn_length += 2;
-         frag = frag_more (insn_length);
+         frag = frag_more (2);
          bfd_putl16 ((bfd_vma) bin, frag);
-
-         if (gen_interrupt_nops
-             && msp430_mcu->isa == MSP430_ISA_54
-             && (strcmp (opcode->name, "eint") == 0
-                 || strcmp (opcode->name, "dint") == 0))
-           {
-             /* 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);
-           }
-
          dwarf2_emit_insn (insn_length);
          break;
 
@@ -1917,9 +2008,37 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
          if (res)
            break;
 
+         bin |= (op1.reg | (op1.am << 7));
+
+         if (is_opcode ("clr") && bin == 0x4302 /* CLR R2*/)
+           {
+             if (check_for_nop)
+               {
+                 if (warn_interrupt_nops)
+                   {
+                     if (gen_interrupt_nops)
+                       as_warn (_("NOP inserted between two instructions that change interrupt state"));
+                     else
+                       as_warn (_("a NOP might be needed here because of successive changes in interrupt state"));
+                   }
+
+                 if (gen_interrupt_nops)
+                   {
+                     /* Emit a NOP between 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);
+                   }
+               }
+
+             nop_check_needed = TRUE;
+           }
+
          /* Compute the entire instruction length, in bytes.  */
-         insn_length = (extended_op ? 2 : 0) + 2 + (op1.ol * 2);
-         frag = frag_more (insn_length);
+         op_length = (extended_op ? 2 : 0) + 2 + (op1.ol * 2);
+         insn_length += op_length;
+         frag = frag_more (op_length);
          where = frag - frag_now->fr_literal;
 
          if (extended_op)
@@ -1952,7 +2071,6 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
              where += 2;
            }
 
-         bin |= (op1.reg | (op1.am << 7));
          bfd_putl16 ((bfd_vma) bin, frag);
          frag += 2;
          where += 2;
@@ -1971,7 +2089,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
                    {
                      if (op1.reg)
                        fix_new_exp (frag_now, where, 2,
-                                    &(op1.exp), FALSE, CHECK_RELOC_MSP430);
+                                    &(op1.exp), FALSE, CHECK_RELOC_MSP430 (op1));
                      else
                        fix_new_exp (frag_now, where, 2,
                                     &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
@@ -1979,18 +2097,6 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
                }
            }
 
-         if (gen_interrupt_nops
-             && msp430_mcu->isa == MSP430_ISA_54
-             && strcmp (opcode->name, "clr") == 0
-             && 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);
-           }
-
          dwarf2_emit_insn (insn_length);
          break;
 
@@ -2009,6 +2115,18 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
          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);
+             break;
+           }
+
          if (extended_op)
            {
              if (!addr_op)
@@ -2073,7 +2191,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
                    {
                      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);
+                                    &(op1.exp), FALSE, CHECK_RELOC_MSP430 (op1));
                      else
                        fix_new_exp (frag_now, where, 2,
                                     &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
@@ -2097,7 +2215,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
                    {
                      if (op2.reg)      /* Not PC relative.  */
                        fix_new_exp (frag_now, where, 2,
-                                    &(op2.exp), FALSE, CHECK_RELOC_MSP430);
+                                    &(op2.exp), FALSE, CHECK_RELOC_MSP430 (op2));
                      else
                        fix_new_exp (frag_now, where, 2,
                                     &(op2.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
@@ -2113,7 +2231,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
          if (extended_op)
            {
              as_bad ("Internal error: state 0/3 not coded for extended instructions");
-             return 0;
+             break;
            }
 
          line = extract_operand (line, l1, sizeof (l1));
@@ -2121,8 +2239,8 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
          if (res)
            break;
 
-         byte_op = 0;
-         imm_op = 0;
+         byte_op = FALSE;
+         imm_op = FALSE;
          bin |= ((op1.reg << 8) | (op1.am << 4));
          op_length = 2 + 2 * op1.ol;
          frag = frag_more (op_length);
@@ -2143,7 +2261,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
 
                  if (op1.reg || (op1.reg == 0 && op1.am == 3))
                    fix_new_exp (frag_now, where, 2,
-                                &(op1.exp), FALSE, CHECK_RELOC_MSP430);
+                                &(op1.exp), FALSE, CHECK_RELOC_MSP430 (op1));
                  else
                    fix_new_exp (frag_now, where, 2,
                                 &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
@@ -2158,14 +2276,14 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
          fix_emitted = FALSE;
 
          line = extract_operand (line, l1, sizeof (l1));
-         imm_op = 0;
+         imm_op = FALSE;
 
          res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, &imm_op,
                                   extended_op, FALSE);
          if (res)
            break;
 
-         byte_op = 0;
+         byte_op = FALSE;
 
          op_length = 2 + 2 * op1.ol;
          frag = frag_more (op_length);
@@ -2217,7 +2335,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
              if (op1.ol != 1)
                {
                  as_bad ("Internal error: unexpected CALLA instruction length: %d\n", op1.ol);
-                 return 0;
+                 break;
                }
 
              bfd_putl16 ((bfd_vma) ZEROS, frag + 2);
@@ -2241,22 +2359,22 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
 
            if (*l1 != '#')
              {
-               as_bad (_("expected #n as first argument of POPM"));
-               return 0;
+               as_bad (_("expected #n as first argument of %s"), opcode->name);
+               break;
              }
            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;
+               break;
              }
 
            if ((reg = check_reg (l2)) == -1)
              {
                as_bad (_("expected register as second argument of %s"),
                        opcode->name);
-               return 0;
+               break;
              }
 
            op_length = 2;
@@ -2267,15 +2385,26 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
              bin |= 0x100;
            n = op1.exp.X_add_number;
            bin |= (n - 1) << 4;
-           if (strcmp (opcode->name, "pushm") == 0)
+           if (is_opcode ("pushm"))
              bin |= reg;
            else
              {
                if (reg - n + 1 < 0)
                  {
                    as_bad (_("Too many registers popped"));
-                   return 0;
+                   break;
                  }
+
+               /* CPU21 errata: 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"));
+                   break;
+                 }
+
                bin |= (reg - n + 1);
              }
 
@@ -2293,7 +2422,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
            if (extended & 0xff)
              {
                as_bad (_("repeat count cannot be used with %s"), opcode->name);
-               return 0;
+               break;
              }
 
            line = extract_operand (line, l1, sizeof (l1));
@@ -2302,28 +2431,34 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
            if (*l1 != '#')
              {
                as_bad (_("expected #n as first argument of %s"), opcode->name);
-               return 0;
+               break;
              }
            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;
+               break;
              }
            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;
+               break;
              }
 
            if ((reg = check_reg (l2)) == -1)
              {
                as_bad (_("expected register as second argument of %s"),
                        opcode->name);
-               return 0;
+               break;
+             }
+
+           if (target_is_430xv2 () && reg == 0)
+             {
+               as_bad (_("%s: attempt to rotate the PC register"), opcode->name);
+               break;
              }
 
            op_length = 2;
@@ -2349,7 +2484,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
            if (extended & 0xff)
              {
                as_bad (_("repeat count cannot be used with %s"), opcode->name);
-               return 0;
+               break;
              }
 
            line = extract_operand (line, l1, sizeof (l1));
@@ -2357,7 +2492,13 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
              {
                as_bad (_("expected register as argument of %s"),
                        opcode->name);
-               return 0;
+               break;
+             }
+
+           if (target_is_430xv2 () && reg == 0)
+             {
+               as_bad (_("%s: attempt to rotate the PC register"), opcode->name);
+               break;
              }
 
            if (byte_op)
@@ -2402,7 +2543,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
            if (extended & 0xff)
              {
                as_bad (_("repeat count cannot be used with %s"), opcode->name);
-               return 0;
+               break;
              }
 
            line = extract_operand (line, l1, sizeof (l1));
@@ -2421,7 +2562,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
                      {
                        as_bad (_("expected value of first argument of %s to fit into 20-bits"),
                                opcode->name);
-                       return 0;
+                       break;
                      }
 
                    bin |= ((n >> 16) & 0xf) << 8;
@@ -2440,7 +2581,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
                  {
                    as_bad (_("expected register name or constant as first argument of %s"),
                            opcode->name);
-                   return 0;
+                   break;
                  }
 
                bin |= (n << 8) | (1 << 6);
@@ -2451,7 +2592,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
              {
                as_bad (_("expected register as second argument of %s"),
                        opcode->name);
-               return 0;
+               break;
              }
 
            frag = frag_more (op_length);
@@ -2469,10 +2610,10 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
          }
 
        case 9: /* MOVA, BRA, RETA.  */
-         imm_op = 0;
+         imm_op = FALSE;
          bin = opcode->bin_opcode;
 
-         if (strcmp (opcode->name, "reta") == 0)
+         if (is_opcode ("reta"))
            {
              /* The RETA instruction does not take any arguments.
                 The implicit first argument is @SP+.
@@ -2490,7 +2631,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
              res = msp430_srcoperand (&op1, l1, opcode->bin_opcode,
                                       &imm_op, extended_op, FALSE);
 
-             if (strcmp (opcode->name, "bra") == 0)
+             if (is_opcode ("bra"))
                {
                  /* This is the BRA synthetic instruction.
                     The second argument is always PC.  */
@@ -2514,7 +2655,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
                                            & error_message)) == 0)
            {
              as_bad (error_message, opcode->name);
-             return 0;
+             break;
            }
          dwarf2_emit_insn (op_length);
          break;
@@ -2528,13 +2669,13 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
              if (op1.exp.X_op != O_constant)
                {
                  as_bad (_("expected constant value as argument to RPT"));
-                 return 0;
+                 break;
                }
              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;
+                 break;
                }
 
              /* We silently accept and ignore a repeat count of 1.  */
@@ -2555,7 +2696,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
              else
                {
                  as_bad (_("expected constant or register name as argument to RPT insn"));
-                 return 0;
+                 break;
                }
            }
          break;
@@ -2576,7 +2717,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
        break;                  /* Error occurred.  All warnings were done before.  */
 
       if (extended_op
-         && strcmp (opcode->name, "movx") == 0
+         && is_opcode ("movx")
          && addr_op
          && msp430_enable_relax)
        {
@@ -2590,14 +2731,43 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
            }
        }
 
+      bin |= (op2.reg | (op1.reg << 8) | (op1.am << 4) | (op2.am << 7));
+
+      if (   (is_opcode ("bic") && bin == 0xc232)
+         || (is_opcode ("bis") && bin == 0xd232)
+         || (is_opcode ("mov") && op2.mode == OP_REG && op2.reg == 2))
+       {
+         if (check_for_nop)
+           {
+             if (warn_interrupt_nops)
+               {
+                 if (gen_interrupt_nops)
+                   as_warn (_("NOP inserted between two instructions that change interrupt state"));
+                 else
+                   as_warn (_("a NOP might be needed here because of successive changes in interrupt state"));
+               }
+
+             if (gen_interrupt_nops)
+               {
+                 /* Emit a NOP between 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);
+               }
+           }
+
+         nop_check_needed = TRUE;
+       }
+
       /* Compute the entire length of the instruction in bytes.  */
-      insn_length =
-       (extended_op ? 2 : 0)   /* The extension word.  */
+      op_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);
+      insn_length += op_length;
+      frag = frag_more (op_length);
       where = frag - frag_now->fr_literal;
 
       if (extended_op)
@@ -2647,7 +2817,6 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
          frag += 2;
        }
 
-      bin |= (op2.reg | (op1.reg << 8) | (op1.am << 4) | (op2.am << 7));
       bfd_putl16 ((bfd_vma) bin, frag);
       where += 2;
       frag += 2;
@@ -2666,7 +2835,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
                {
                  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);
+                                &(op1.exp), FALSE, CHECK_RELOC_MSP430 (op1));
                  else
                    fix_new_exp (frag_now, where, 2,
                                 &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
@@ -2691,7 +2860,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
                {
                  if (op2.reg)          /* Not PC relative.  */
                    fix_new_exp (frag_now, where, 2,
-                            &(op2.exp), FALSE, CHECK_RELOC_MSP430);
+                                &(op2.exp), FALSE, CHECK_RELOC_MSP430 (op2));
                  else
                    fix_new_exp (frag_now, where, 2,
                                 &(op2.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
@@ -2699,19 +2868,6 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
            }
        }
 
-      if (gen_interrupt_nops
-         && msp430_mcu->isa == MSP430_ISA_54
-         && (   (strcmp (opcode->name, "bic") == 0 && bin == 0xc232)
-             || (strcmp (opcode->name, "bis") == 0 && bin == 0xd232)
-             || (strcmp (opcode->name, "mov") == 0 && 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);
-       }
-
       dwarf2_emit_insn (insn_length);
       break;
 
@@ -2732,14 +2888,25 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
       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);
+         break;
+       }
+
       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 (strcmp (opcode->name, "swpbx") == 0
-             || strcmp (opcode->name, "sxtx") == 0)
+         if (is_opcode ("swpbx") || is_opcode ("sxtx"))
            {
              /* These two instructions use a special
                 encoding of the A/L and B/W bits.  */
@@ -2749,7 +2916,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
                {
                  as_bad (_("%s instruction does not accept a .b suffix"),
                          opcode->name);
-                 return 0;
+                 break;
                }
              else if (! addr_op)
                extended |= BYTE_OPERATION;
@@ -2801,7 +2968,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
                {
                  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);
+                                &(op1.exp), FALSE, CHECK_RELOC_MSP430 (op1));
                  else
                    fix_new_exp (frag_now, where, 2,
                                 &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
@@ -2906,7 +3073,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
          as_bad (_("polymorphs are not enabled. Use -mP option to enable."));
          break;
        }
-
+       
       line = extract_operand (line, l1, sizeof (l1));
       if (l1[0])
        {
@@ -2923,7 +3090,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
              /* Relaxation required.  */
              struct rcodes_s rc = msp430_rcodes[opcode->insn_opnumb];
 
-             if (msp430_mcu->isa == MSP430X_ISA)
+             if (target_is_430x ())
                rc = msp430x_rcodes[opcode->insn_opnumb];
 
              /* The parameter to dwarf2_emit_insn is actually the offset to
@@ -2969,7 +3136,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
              /* Relaxation required.  */
              struct hcodes_s hc = msp430_hcodes[opcode->insn_opnumb];
 
-             if (msp430_mcu->isa == MSP430X_ISA)
+             if (target_is_430x ())
                hc = msp430x_hcodes[opcode->insn_opnumb];
 
              insn_length += 8;
@@ -2995,6 +3162,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line)
     }
 
   input_line_pointer = line;
+  check_for_nop = nop_check_needed;
   return 0;
 }
 
@@ -3073,10 +3241,8 @@ 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;
@@ -3202,6 +3368,12 @@ md_apply_fix (fixS * fixp, valueT * valuep, segT seg)
          bfd_putl16 ((bfd_vma) value, where);
          break;
 
+       case BFD_RELOC_MSP430_ABS_HI16:
+         value >>= 16;
+         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;
@@ -3499,9 +3671,10 @@ md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
     case ENCODE_RELAX (STATE_UNCOND_BRANCH, STATE_WORD):
     case ENCODE_RELAX (STATE_UNCOND_BRANCH, STATE_UNDEF):
       /* Convert uncond branch jmp lab -> br lab.  */
-      cc = & msp430_rcodes[7];
-      if (msp430_mcu->isa == MSP430X_ISA)
+      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;
@@ -3516,7 +3689,7 @@ md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
 
        insn &= 0xffff;
        /* Find actual instruction.  */
-       if (msp430_mcu->isa == MSP430X_ISA)
+       if (target_is_430x ())
          {
            for (i = 0; i < 7 && !cc; i++)
              if (msp430x_rcodes[i].sop == insn)
@@ -3542,11 +3715,10 @@ md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
 
     case ENCODE_RELAX (STATE_NOOV_BRANCH, STATE_WORD):
     case ENCODE_RELAX (STATE_NOOV_BRANCH, STATE_UNDEF):
-      cc = & msp430_rcodes[6];
-
-      if (msp430_mcu->isa == MSP430X_ISA)
+      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);
@@ -3560,7 +3732,7 @@ md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
        int insn = bfd_getl16 (fragP->fr_opcode + 2);
 
        insn &= 0xffff;
-       if (msp430_mcu->isa == MSP430X_ISA)
+       if (target_is_430x ())
          {
            for (i = 0; i < 4 && !hc; i++)
              if (msp430x_hcodes[i].op1 == insn)
@@ -3572,7 +3744,6 @@ md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
              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);
@@ -3593,7 +3764,7 @@ md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
        int insn = bfd_getl16 (fragP->fr_opcode + 2);
 
        insn &= 0xffff;
-       if (msp430_mcu->isa == MSP430X_ISA)
+       if (target_is_430x ())
          {
            for (i = 0; i < 4 && !hc; i++)
              if (msp430x_hcodes[i].op1 == insn)
@@ -3726,8 +3897,11 @@ msp430_fix_adjustable (struct fix *fixp ATTRIBUTE_UNUSED)
 void
 msp430_md_end (void)
 {
+  if (check_for_nop == TRUE && warn_interrupt_nops)
+    as_warn ("assembly finished with the last instruction changing interrupt state - a NOP might be needed");
+
   bfd_elf_add_proc_attr_int (stdoutput, OFBA_MSPABI_Tag_ISA,
-                            msp430_mcu->isa == MSP430X_ISA ? 2 : 1);
+                            target_is_430x () ? 2 : 1);
 
   bfd_elf_add_proc_attr_int (stdoutput, OFBA_MSPABI_Tag_Code_Model,
                             large_model ? 2 : 1);