/* tc-msp430.c -- Assembler code for the Texas Instruments MSP430
- Copyright (C) 2002-2018 Free Software Foundation, Inc.
+ Copyright (C) 2002-2019 Free Software Foundation, Inc.
Contributed by Dmitry Diky <diwil@mail.ru>
This file is part of GAS, the GNU Assembler.
#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 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)
expression (op);
if (op->X_op == O_absent)
as_bad (_("missing operand"));
+
/* Our caller is likely to check that the entire expression was parsed.
If we have found a hex constant with an 'h' suffix, ilp will be left
pointing at the 'h', so skip it here. */
#define OPTION_WARN_INTR_NOPS 'y'
#define OPTION_NO_WARN_INTR_NOPS 'Y'
static bfd_boolean warn_interrupt_nops = TRUE;
+#define OPTION_UNKNOWN_INTR_NOPS 'u'
+#define OPTION_NO_UNKNOWN_INTR_NOPS 'U'
+static bfd_boolean do_unknown_interrupt_nops = TRUE;
#define OPTION_MCPU 'c'
#define OPTION_MOVE_DATA 'd'
static bfd_boolean move_data = FALSE;
warn_interrupt_nops = FALSE;
return 1;
+ case OPTION_UNKNOWN_INTR_NOPS:
+ do_unknown_interrupt_nops = TRUE;
+ return 1;
+ case OPTION_NO_UNKNOWN_INTR_NOPS:
+ do_unknown_interrupt_nops = FALSE;
+ return 1;
+
case OPTION_MOVE_DATA:
move_data = TRUE;
return 1;
msp430_make_init_symbols (const char * name)
{
if (strncmp (name, ".bss", 4) == 0
+ || strncmp (name, ".lower.bss", 10) == 0
+ || strncmp (name, ".either.bss", 11) == 0
|| strncmp (name, ".gnu.linkonce.b.", 16) == 0)
(void) symbol_find_or_make ("__crt0_init_bss");
if (strncmp (name, ".data", 5) == 0
+ || strncmp (name, ".lower.data", 11) == 0
+ || strncmp (name, ".either.data", 12) == 0
|| strncmp (name, ".gnu.linkonce.d.", 16) == 0)
(void) symbol_find_or_make ("__crt0_movedata");
-
/* Note - data assigned to the .either.data section may end up being
placed in the .upper.data section if the .lower.data section is
full. Hence the need to define the crt0 symbol.
|| strncmp (name, ".either.bss", 11) == 0
|| upper_data_region_in_use)
(void) symbol_find_or_make ("__crt0_init_highbss");
+
+ /* The following symbols are for the crt0 functions that run through
+ the different .*_array sections and call the functions placed there.
+ - init_array stores global static C++ constructors to run before main.
+ - preinit_array is not expected to ever be used for MSP430.
+ GCC only places initialization functions for runtime "sanitizers"
+ (i.e. {a,l,t,u}san) and "virtual table verification" in preinit_array.
+ - fini_array stores global static C++ destructors to run after calling
+ exit() or returning from main.
+ __crt0_run_array is required to actually call the functions in the above
+ arrays. */
+ if (strncmp (name, ".init_array", 11) == 0)
+ {
+ (void) symbol_find_or_make ("__crt0_run_init_array");
+ (void) symbol_find_or_make ("__crt0_run_array");
+ }
+ else if (strncmp (name, ".preinit_array", 14) == 0)
+ {
+ (void) symbol_find_or_make ("__crt0_run_preinit_array");
+ (void) symbol_find_or_make ("__crt0_run_array");
+ }
+ else if (strncmp (name, ".fini_array", 11) == 0)
+ {
+ (void) symbol_find_or_make ("__crt0_run_fini_array");
+ (void) symbol_find_or_make ("__crt0_run_array");
+ }
}
static void
{NULL, NULL, 0}
};
-const char *md_shortopts = "mm:,mP,mQ,ml,mN,mn,my,mY";
+const char *md_shortopts = "mm:,mP,mQ,ml,mN,mn,my,mY,mu,mU";
struct option md_longopts[] =
{
{"mn", no_argument, NULL, OPTION_INTR_NOPS},
{"mY", no_argument, NULL, OPTION_NO_WARN_INTR_NOPS},
{"my", no_argument, NULL, OPTION_WARN_INTR_NOPS},
+ {"mu", no_argument, NULL, OPTION_UNKNOWN_INTR_NOPS},
+ {"mU", no_argument, NULL, OPTION_NO_UNKNOWN_INTR_NOPS},
{"md", no_argument, NULL, OPTION_MOVE_DATA},
{"mdata-region", required_argument, NULL, OPTION_DATA_REGION},
{NULL, no_argument, NULL, 0}
fprintf (stream,
_(" -my - warn about missing NOPs after changing interrupts (default)\n"));
fprintf (stream,
+ _(" -mU - for an instruction which changes interrupt state, but where it is not\n"
+ " known how the state is changed, do not warn/insert NOPs\n"));
+ fprintf (stream,
+ _(" -mu - for an instruction which changes interrupt state, but where it is not\n"
+ " known how the state is changed, warn/insert NOPs (default)\n"
+ " -mn and/or -my are required for this to have any effect\n"));
+ fprintf (stream,
_(" -md - Force copying of data from ROM to RAM at startup\n"));
fprintf (stream,
_(" -mdata-region={none|lower|upper|either} - select region data will be\n"
{
char *h = l;
- op->reg = 2; /* reg 2 in absolute addr mode. */
- op->am = 1; /* mode As == 01 bin. */
+ 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;
end = parse_exp (__tl, &(op->exp));
}
/* Insert/inform about adding a NOP if this insn enables interrupts. */
+
static void
warn_eint_nop (bfd_boolean prev_insn_is_nop, bfd_boolean prev_insn_is_dint)
{
if (prev_insn_is_nop
- /* Prevent double warning for DINT immediately before EINT. */
+ /* If the last insn was a DINT, we will have already warned that a NOP is
+ required after it. */
|| prev_insn_is_dint
/* 430 ISA does not require a NOP before EINT. */
|| (! target_is_430x ()))
return;
+
if (gen_interrupt_nops)
{
gen_nop ();
/* Use when unsure what effect the insn will have on the interrupt status,
to insert/warn about adding a NOP before the current insn. */
+
static void
-warn_unsure_interrupt (void)
+warn_unsure_interrupt (bfd_boolean prev_insn_is_nop,
+ bfd_boolean prev_insn_is_dint)
{
- /* Since this could enable or disable interrupts, need to add/warn about
- adding a NOP before and after this insn. */
+ if (prev_insn_is_nop
+ /* If the last insn was a DINT, we will have already warned that a NOP is
+ required after it. */
+ || prev_insn_is_dint
+ /* 430 ISA does not require a NOP before EINT or DINT. */
+ || (! target_is_430x ()))
+ return;
+
if (gen_interrupt_nops)
{
gen_nop ();
switch (fmt)
{
- case 0: /* Emulated. */
+ case 0:
+ /* Emulated. */
switch (opcode->insn_opnumb)
{
case 0:
else if (op1.mode == OP_REG
&& (op1.reg == 2 || op1.reg == 3))
this_insn_is_dint = TRUE;
- else
+ else if (do_unknown_interrupt_nops)
{
/* FIXME: Couldn't work out whether the insn is enabling or
disabling interrupts, so for safety need to treat it as both
a DINT and EINT. */
- warn_unsure_interrupt ();
+ warn_unsure_interrupt (prev_insn_is_nop, prev_insn_is_dint);
check_for_nop |= NOP_CHECK_INTERRUPT;
}
}