/* tc-msp430.c -- Assembler code for the Texas Instruments MSP430
- Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007
+ 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 2, or (at your option)
+ 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,
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, 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
#include <limits.h>
#define PUSH_1X_WORKAROUND
#include "subsegs.h"
#include "opcode/msp430.h"
#include "safe-ctype.h"
+#include "dwarf2dbg.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;
+
+/* 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
+
+
+/* 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}
+};
const char comment_chars[] = ";";
const char line_comment_chars[] = "#";
-const char line_separator_chars[] = "";
+const char line_separator_chars[] = "{";
const char EXP_CHARS[] = "eE";
const char FLT_CHARS[] = "dD";
#define MSP430_ISA_14 14
#define MSP430_ISA_15 15
#define MSP430_ISA_16 16
+#define MSP430_ISA_21 21
#define MSP430_ISA_31 31
#define MSP430_ISA_32 32
#define MSP430_ISA_33 33
{"msp430x1611", MSP430_ISA_16, bfd_mach_msp16},
{"msp430x1612", MSP430_ISA_16, bfd_mach_msp16},
+ {"msp430x2101", MSP430_ISA_21, bfd_mach_msp21},
+ {"msp430x2111", MSP430_ISA_21, bfd_mach_msp21},
+ {"msp430x2121", MSP430_ISA_21, bfd_mach_msp21},
+ {"msp430x2131", MSP430_ISA_21, bfd_mach_msp21},
+
{"msp430x311", MSP430_ISA_31, bfd_mach_msp31},
{"msp430x312", MSP430_ISA_31, bfd_mach_msp31},
{"msp430x313", MSP430_ISA_31, bfd_mach_msp31},
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 farme size
+ ; note, that spare var filled with the frame size
mov r15,r8
....
.profiler cdE,fxx ; check stack
return s;
}
-/* Extract one word from FROM and copy it to TO. Delimeters are ",;\n" */
+/* Extract one word from FROM and copy it to TO. Delimiters are ",;\n" */
static char *
extract_operand (char * from, char * to, int limit)
|| ! pow2value (p_flags & ( MSP430_PROFILER_FLAG_INITSECT
| MSP430_PROFILER_FLAG_FINISECT))))
{
- as_bad (_("ambigious flags combination - '.profiler' directive ignored."));
+ 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) /* Paranoja ? */
+ if (now_seg == absolute_section) /* Paranoia ? */
{
exp1.X_op = O_constant;
exp1.X_add_number = abs_section_offset;
- as_warn (_("profiling in absolute section? Hm..."));
+ as_warn (_("profiling in absolute section?"));
}
else
{
}
#define OPTION_MMCU 'm'
+#define OPTION_RELAX 'Q'
+#define OPTION_POLYMORPHS 'P'
static void
msp430_set_arch (int dummy ATTRIBUTE_UNUSED)
as_fatal (_("redefinition of mcu type %s' to %s'"),
msp430_mcu->name, mcu_types[i].name);
return 1;
+ break;
+
+ case OPTION_RELAX:
+ msp430_enable_relax = 1;
+ return 1;
+ break;
+
+ case OPTION_POLYMORPHS:
+ msp430_enable_polys = 1;
+ return 1;
+ break;
}
return 0;
struct option md_longopts[] =
{
{"mmcu", required_argument, NULL, OPTION_MMCU},
+ {"mP", no_argument, NULL, OPTION_POLYMORPHS},
+ {"mQ", no_argument, NULL, OPTION_RELAX},
{NULL, no_argument, NULL, 0}
};
" msp430xG437 msp430xG438 msp430G439\n"
" msp430x435 msp430x436 msp430x437\n"
" msp430x447 msp430x448 msp430x449\n"));
+ fprintf (stream,
+ _(" -mQ - enable relaxation at assembly time. DANGEROUS!\n"
+ " -mP - enable polymorph instructions\n"));
show_mcu_list (stream);
}
return from;
}
-/* Turn a string in input_line_pointer into a floating point constant
- of type TYPE, and store the appropriate bytes in *LITP. The number
- of LITTLENUMS emitted is stored in *SIZEP. An error message is
- returned, or NULL on OK. */
-
char *
md_atof (int type, char * litP, int * sizeP)
{
- int prec;
- LITTLENUM_TYPE words[4];
- LITTLENUM_TYPE *wordP;
- char *t;
-
- switch (type)
- {
- case 'f':
- prec = 2;
- break;
- case 'd':
- prec = 4;
- break;
- default:
- *sizeP = 0;
- return _("bad call to md_atof");
- }
-
- t = atof_ieee (input_line_pointer, type, words);
- if (t)
- input_line_pointer = t;
-
- *sizeP = prec * sizeof (LITTLENUM_TYPE);
-
- /* This loop outputs the LITTLENUMs in REVERSE order. */
- for (wordP = words + prec - 1; prec--;)
- {
- md_number_to_chars (litP, (valueT) (*wordP--), sizeof (LITTLENUM_TYPE));
- litP += sizeof (LITTLENUM_TYPE);
- }
-
- return NULL;
+ return ieee_md_atof (type, litP, sizeP, FALSE);
}
void
op->mode = OP_REG;
}
}
- /* Redudant (yet) check. */
+ /* Redundant (yet) check. */
else if (op->exp.X_op == O_register)
as_bad
(_("Registers cannot be used within immediate expression [%s]"), l);
;
else
{
- /* Redudant (yet) check. */
+ /* Redundant (yet) check. */
if (op->exp.X_op == O_register)
as_bad
(_("Registers cannot be used within absolute expression [%s]"), l);
op->reg = *t - '0';
if (op->reg > 9 || op->reg < 0)
{
- as_bad (_("unknown operator (r%s substituded as a register name"),
+ as_bad (_("unknown operator (r%s substituted as a register name"),
t);
return 1;
}
;
else
{
- /* Redudant (yet) check. */
+ /* Redundant (yet) check. */
if (op->exp.X_op == O_register)
as_bad
(_("Registers cannot be used as a prefix of indexed expression [%s]"), l);
/* Symbolic mode 'mov a, b' == 'mov x(pc), y(pc)'. */
do
{
-#if 0 /* Allow expression in operand like 'a+123*(1|2)'. */
- char *t = l;
-
- __tl = l;
-
- while (*t)
- {
- /* alpha/number underline dot for labels. */
- if (! ISALNUM (*t) && *t != '_' && *t != '.')
- {
- as_bad (_("unknown operand %s"), l);
- return 1;
- }
- t++;
- }
-#endif
op->mode = OP_EXP;
op->reg = 0; /* PC relative... be careful. */
op->am = 1;
msp430_operands (struct msp430_opcode_s * opcode, char * line)
{
int bin = opcode->bin_opcode; /* Opcode mask. */
- int __is;
+ int __is = 0;
char l1[MAX_OP_LEN], l2[MAX_OP_LEN];
char *frag;
int where;
__is = 2;
frag = frag_more (__is);
bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (__is);
break;
case 1:
/* Something which works with destination operand. */
frag = frag_more (2 * __is);
where = frag - frag_now->fr_literal;
bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (2 * __is);
if (op1.mode == OP_EXP)
{
frag = frag_more (2 * __is);
where = frag - frag_now->fr_literal;
bfd_putl16 ((bfd_vma) bin, frag);
-
+ dwarf2_emit_insn (2 * __is);
+
if (op1.mode == OP_EXP)
{
where += 2; /* Advance 'where' as we do not know _where_. */
frag = frag_more (2 * __is);
where = frag - frag_now->fr_literal;
bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (2 * __is);
if (op1.mode == OP_EXP)
{
frag = frag_more (2 * __is);
where = frag - frag_now->fr_literal;
bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (2 * __is);
if (op1.mode == OP_EXP)
{
/* reti instruction. */
frag = frag_more (2);
bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (2);
break;
}
frag = frag_more (2 * __is);
where = frag - frag_now->fr_literal;
bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (2 * __is);
if (op1.mode == OP_EXP)
{
else if (*l1 == '$')
{
as_bad (_("instruction requires label sans '$'"));
- break;
}
else
{
as_bad (_
("instruction requires label or value in range -511:512"));
- break;
}
+ dwarf2_emit_insn (2 * __is);
+ break;
}
else
{
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])
{
/* Relaxation required. */
struct rcodes_s rc = msp430_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. */
frag = frag_more (8);
+ dwarf2_emit_insn (0);
bfd_putl16 ((bfd_vma) rc.sop, frag);
frag = frag_variant (rs_machine_dependent, 8, 2,
ENCODE_RELAX (rc.lpos, STATE_BITS10), /* Wild guess. */
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])
{
struct hcodes_s hc = msp430_hcodes[opcode->insn_opnumb];
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,
break;
default:
- as_bad (_("Ilegal instruction or not implmented opcode."));
+ as_bad (_("Illegal instruction or not implemented opcode."));
}
input_line_pointer = line;
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 (msp430_enable_polys
+ && !msp430_enable_relax)
+ return 1;
+ else
+ 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_fix3 (fixS * fixp, valueT * valuep, segT seg)
+md_apply_fix (fixS * fixp, valueT * valuep, segT seg)
{
unsigned char * where;
unsigned long insn;
if (fixp->fx_addsy && (s == seg || s == absolute_section))
{
- value = S_GET_VALUE (fixp->fx_addsy) + *valuep;
+ /* 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
}
}
- switch (fixp->fx_r_type)
+ 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)
{
- default:
- fixp->fx_no_overflow = 1;
- break;
- case BFD_RELOC_MSP430_10_PCREL:
- break;
+ 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 = fixp->fx_frag->fr_literal + fixp->fx_where;
+ where = (unsigned char *) fixp->fx_frag->fr_literal + fixp->fx_where;
insn = bfd_getl16 (where);
}
}
-/* A `BFD_ASSEMBLER' GAS will call this to generate a reloc. GAS
- will pass 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'. */
+/* 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. */
else if (fragP->fr_symbol)
{
/* Its got a segment, but its not ours. Even if fr_symbol is in
- an absolute segment, we dont know a displacement until we link
+ 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. */
else
{
/* We know the abs value. may be it is a jump to fixed address.
- Impossible in our case, cause all constants already handeled. */
+ Impossible in our case, cause all constants already handled. */
fragP->fr_subtype =
ENCODE_RELAX (RELAX_LEN (fragP->fr_subtype), STATE_UNDEF);
}
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;
{
/* Look backwards. */
for (next_state = this_type->rlx_more; next_state;)
- if (aim >= this_type->rlx_backward)
+ if (aim >= this_type->rlx_backward || !this_type->rlx_backward)
next_state = 0;
else
{
{
/* Look forwards. */
for (next_state = this_type->rlx_more; next_state;)
- if (aim <= this_type->rlx_forward)
+ if (aim <= this_type->rlx_forward || !this_type->rlx_forward)
next_state = 0;
else
{