+/* 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, 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 = "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}
+};
+
+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 - 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 *
+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,
+ bfd_boolean * 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 = TRUE;
+
+ 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;
+ op->vshift = vshift;
+
+ 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;
+ op->vshift = 0;
+ }
+ 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;
+ op->vshift = 0;
+ }
+
+ if (allow_20bit_values)
+ {
+ 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;
+ }
+ }
+ 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)
+ {
+ 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
+ {
+ 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;
+ op->vshift = 0;
+ 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 = TRUE;
+
+ 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;
+ op->vshift = 0;
+ 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;
+ op->vshift = 0;
+ __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;
+ op->vshift = 0;
+ 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;
+}
+
+static bfd_boolean check_for_nop = FALSE;
+
+#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;
+ bfd_boolean 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;
+ bfd_boolean nop_check_needed = FALSE;
+
+ /* Opcode is the one from opcodes table
+ line contains something like
+ [.w] @r2+, 5(R1)
+ or
+ .b @r2+, 5(R1). */
+
+ byte_op = FALSE;
+ addr_op = FALSE;
+ if (*line == '.')
+ {
+ 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;
+
+ }
+ }
+
+ if (*line && ! ISSPACE (*line))
+ {
+ as_bad (_("junk found after instruction: %s.%s"),
+ opcode->name, line);
+ return 0;
+ }
+
+ /* 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
+ && (!*line || *line == '\n'))
+ {
+ as_bad (_("instruction %s requires %d operand(s)"),