X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-i860.c;h=858d81e9983e8cdddb8c93cbb53a1b5518d55100;hb=4512dafa508c4b148996f2d67222a82dd018cdd3;hp=9ffd4ee6800c0884a284d731ec9e344083721887;hpb=4a4f25cfdeaaf1d1f6922302e262bd84bb7b6f4c;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/config/tc-i860.c b/gas/config/tc-i860.c index 9ffd4ee680..858d81e998 100644 --- a/gas/config/tc-i860.c +++ b/gas/config/tc-i860.c @@ -1,6 +1,5 @@ /* tc-i860.c -- Assembler for the Intel i860 architecture. - Copyright (C) 1989, 1992, 1993, 1994, 1995, 1998, 1999, 2000 - Free Software Foundation, Inc. + Copyright (C) 1989-2016 Free Software Foundation, Inc. Brought back from the dead and completely reworked by Jason Eckhardt . @@ -9,7 +8,7 @@ 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, @@ -19,21 +18,14 @@ 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. */ + Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -#include -#include -#include #include "as.h" +#include "safe-ctype.h" #include "subsegs.h" #include "opcode/i860.h" #include "elf/i860.h" -/* Defined by default since this is primarily a SVR4/860 assembler. - However, I'm trying to leave the door open for Intel syntax. Of course, - if full support for anything other than SVR4 is done, then we should - select this based on a command-line flag. */ -#define SYNTAX_SVR4 /* The opcode hash table. */ static struct hash_control *op_hash = NULL; @@ -54,24 +46,28 @@ const char EXP_CHARS[] = "eE"; As in 0f12.456 or 0d1.2345e12. */ const char FLT_CHARS[] = "rRsSfFdDxXpP"; -/* Register prefix. */ -#ifdef SYNTAX_SVR4 -static const char reg_prefix = '%'; -#else -static const char reg_prefix = 0; -#endif +/* Register prefix (depends on syntax). */ +static char reg_prefix; + +#define MAX_FIXUPS 2 struct i860_it { - char *error; + const char *error; unsigned long opcode; - expressionS exp; enum expand_type expand; - bfd_reloc_code_real_type reloc; - int pcrel; - valueT fup; + struct i860_fi + { + expressionS exp; + bfd_reloc_code_real_type reloc; + int pcrel; + valueT fup; + } fi[MAX_FIXUPS]; } the_insn; +/* The current fixup count. */ +static int fc; + static char *expr_end; /* Indicates error if a pseudo operation was expanded after a branch. */ @@ -80,27 +76,32 @@ static char last_expand; /* If true, then warn if any pseudo operations were expanded. */ static int target_warn_expand = 0; +/* If true, then XP support is enabled. */ +static int target_xp = 0; + +/* If true, then Intel syntax is enabled (default to AT&T/SVR4 syntax). */ +static int target_intel_syntax = 0; + + /* Prototypes. */ -static void i860_process_insn PARAMS ((char *)); -static void s_dual PARAMS ((int)); -static void s_enddual PARAMS ((int)); -static void s_atmp PARAMS ((int)); -static int i860_get_expression PARAMS ((char *)); -static bfd_reloc_code_real_type obtain_reloc_for_imm16 - PARAMS ((fixS *, long *)); +static void i860_process_insn (char *); +static void s_dual (int); +static void s_enddual (int); +static void s_atmp (int); +static void s_align_wrapper (int); +static int i860_get_expression (char *); +static bfd_reloc_code_real_type obtain_reloc_for_imm16 (fixS *, long *); #ifdef DEBUG_I860 -static void print_insn PARAMS ((struct i860_it *)); +static void print_insn (struct i860_it *); #endif const pseudo_typeS md_pseudo_table[] = { -#ifdef OBJ_ELF - {"align", s_align_bytes, 0}, -#endif - {"dual", s_dual, 0}, - {"enddual", s_enddual, 0}, - {"atmp", s_atmp, 0}, - {NULL, 0, 0}, + {"align", s_align_wrapper, 0}, + {"dual", s_dual, 0}, + {"enddual", s_enddual, 0}, + {"atmp", s_atmp, 0}, + {NULL, 0, 0}, }; /* Dual-instruction mode handling. */ @@ -112,28 +113,39 @@ static enum dual dual_mode = DUAL_OFF; /* Handle ".dual" directive. */ static void -s_dual (ignore) - int ignore ATTRIBUTE_UNUSED; +s_dual (int ignore ATTRIBUTE_UNUSED) { - dual_mode = DUAL_ON; + if (target_intel_syntax) + dual_mode = DUAL_ON; + else + as_bad (_("Directive .dual available only with -mintel-syntax option")); } /* Handle ".enddual" directive. */ static void -s_enddual (ignore) - int ignore ATTRIBUTE_UNUSED; +s_enddual (int ignore ATTRIBUTE_UNUSED) { - dual_mode = DUAL_OFF; + if (target_intel_syntax) + dual_mode = DUAL_OFF; + else + as_bad (_("Directive .enddual available only with -mintel-syntax option")); } /* Temporary register used when expanding assembler pseudo operations. */ static int atmp = 31; static void -s_atmp (ignore) - int ignore ATTRIBUTE_UNUSED; +s_atmp (int ignore ATTRIBUTE_UNUSED) { - register int temp; + int temp; + + if (! target_intel_syntax) + { + as_bad (_("Directive .atmp available only with -mintel-syntax option")); + demand_empty_rest_of_line (); + return; + } + if (strncmp (input_line_pointer, "sp", 2) == 0) { input_line_pointer += 2; @@ -160,11 +172,44 @@ s_atmp (ignore) demand_empty_rest_of_line (); } +/* Handle ".align" directive depending on syntax mode. + AT&T/SVR4 syntax uses the standard align directive. However, + the Intel syntax additionally allows keywords for the alignment + parameter: ".align type", where type is one of {.short, .long, + .quad, .single, .double} representing alignments of 2, 4, + 16, 4, and 8, respectively. */ +static void +s_align_wrapper (int arg) +{ + char *parm = input_line_pointer; + + if (target_intel_syntax) + { + /* Replace a keyword with the equivalent integer so the + standard align routine can parse the directive. */ + if (strncmp (parm, ".short", 6) == 0) + strncpy (parm, " 2", 6); + else if (strncmp (parm, ".long", 5) == 0) + strncpy (parm, " 4", 5); + else if (strncmp (parm, ".quad", 5) == 0) + strncpy (parm, " 16", 5); + else if (strncmp (parm, ".single", 7) == 0) + strncpy (parm, " 4", 7); + else if (strncmp (parm, ".double", 7) == 0) + strncpy (parm, " 8", 7); + + while (*input_line_pointer == ' ') + ++input_line_pointer; + } + + s_align_bytes (arg); +} + /* This function is called once, at assembler startup time. It should set up all the tables and data structures that the MD part of the assembler will need. */ void -md_begin () +md_begin (void) { const char *retval = NULL; int lose = 0; @@ -175,7 +220,7 @@ md_begin () while (i860_opcodes[i].name != NULL) { const char *name = i860_opcodes[i].name; - retval = hash_insert (op_hash, name, (PTR)&i860_opcodes[i]); + retval = hash_insert (op_hash, name, (void *) &i860_opcodes[i]); if (retval != NULL) { fprintf (stderr, _("internal error: can't hash `%s': %s\n"), @@ -199,34 +244,38 @@ md_begin () if (lose) as_fatal (_("Defective assembler. No assembly attempted.")); + + /* Set the register prefix for either Intel or AT&T/SVR4 syntax. */ + reg_prefix = target_intel_syntax ? 0 : '%'; } /* This is the core of the machine-dependent assembler. STR points to a machine dependent instruction. This function emits the frags/bytes it assembles to. */ void -md_assemble (str) - char *str; +md_assemble (char *str) { char *destp; int num_opcodes = 1; int i; struct i860_it pseudo[3]; - assert (str); + gas_assert (str); + fc = 0; /* Assemble the instruction. */ i860_process_insn (str); /* Check for expandable flag to produce pseudo-instructions. This is an undesirable feature that should be avoided. */ - if (the_insn.expand != 0 - && ! (the_insn.fup & (OP_SEL_HA | OP_SEL_H | OP_SEL_L | OP_SEL_GOT + if (the_insn.expand != 0 && the_insn.expand != XP_ONLY + && ! (the_insn.fi[0].fup & (OP_SEL_HA | OP_SEL_H | OP_SEL_L | OP_SEL_GOT | OP_SEL_GOTOFF | OP_SEL_PLT))) { for (i = 0; i < 3; i++) pseudo[i] = the_insn; + fc = 1; switch (the_insn.expand) { @@ -235,103 +284,106 @@ md_assemble (str) break; case E_MOV: - if (the_insn.exp.X_add_symbol == NULL - && the_insn.exp.X_op_symbol == NULL - && (the_insn.exp.X_add_number < (1 << 15) - && the_insn.exp.X_add_number >= -(1 << 15))) + if (the_insn.fi[0].exp.X_add_symbol == NULL + && the_insn.fi[0].exp.X_op_symbol == NULL + && (the_insn.fi[0].exp.X_add_number < (1 << 15) + && the_insn.fi[0].exp.X_add_number >= -(1 << 15))) break; /* Emit "or l%const,r0,ireg_dest". */ pseudo[0].opcode = (the_insn.opcode & 0x001f0000) | 0xe4000000; - pseudo[0].fup = (OP_IMM_S16 | OP_SEL_L); + pseudo[0].fi[0].fup = (OP_IMM_S16 | OP_SEL_L); /* Emit "orh h%const,ireg_dest,ireg_dest". */ pseudo[1].opcode = (the_insn.opcode & 0x03ffffff) | 0xec000000 | ((the_insn.opcode & 0x001f0000) << 5); - pseudo[1].fup = (OP_IMM_S16 | OP_SEL_H); + pseudo[1].fi[0].fup = (OP_IMM_S16 | OP_SEL_H); num_opcodes = 2; break; case E_ADDR: - if (the_insn.exp.X_add_symbol == NULL - && the_insn.exp.X_op_symbol == NULL - && (the_insn.exp.X_add_number < (1 << 15) - && the_insn.exp.X_add_number >= -(1 << 15))) + if (the_insn.fi[0].exp.X_add_symbol == NULL + && the_insn.fi[0].exp.X_op_symbol == NULL + && (the_insn.fi[0].exp.X_add_number < (1 << 15) + && the_insn.fi[0].exp.X_add_number >= -(1 << 15))) break; - /* Emit "orh ha%addr_expr,r0,r31". */ - pseudo[0].opcode = 0xec000000 | (atmp << 16); - pseudo[0].fup = (OP_IMM_S16 | OP_SEL_HA); + /* Emit "orh ha%addr_expr,ireg_src2,r31". */ + pseudo[0].opcode = 0xec000000 | (the_insn.opcode & 0x03e00000) + | (atmp << 16); + pseudo[0].fi[0].fup = (OP_IMM_S16 | OP_SEL_HA); /* Emit "l%addr_expr(r31),ireg_dest". We pick up the fixup information from the original instruction. */ pseudo[1].opcode = (the_insn.opcode & ~0x03e00000) | (atmp << 21); - pseudo[1].fup = the_insn.fup | OP_SEL_L; + pseudo[1].fi[0].fup = the_insn.fi[0].fup | OP_SEL_L; num_opcodes = 2; break; case E_U32: - if (the_insn.exp.X_add_symbol == NULL - && the_insn.exp.X_op_symbol == NULL - && (the_insn.exp.X_add_number < (1 << 16) - && the_insn.exp.X_add_number >= 0)) + if (the_insn.fi[0].exp.X_add_symbol == NULL + && the_insn.fi[0].exp.X_op_symbol == NULL + && (the_insn.fi[0].exp.X_add_number < (1 << 16) + && the_insn.fi[0].exp.X_add_number >= 0)) break; /* Emit "$(opcode)h h%const,ireg_src2,r31". */ pseudo[0].opcode = (the_insn.opcode & 0xf3e0ffff) | 0x0c000000 | (atmp << 16); - pseudo[0].fup = (OP_IMM_S16 | OP_SEL_H); + pseudo[0].fi[0].fup = (OP_IMM_S16 | OP_SEL_H); /* Emit "$(opcode) l%const,r31,ireg_dest". */ pseudo[1].opcode = (the_insn.opcode & 0xf01f0000) | 0x04000000 | (atmp << 21); - pseudo[1].fup = (OP_IMM_S16 | OP_SEL_L); + pseudo[1].fi[0].fup = (OP_IMM_S16 | OP_SEL_L); num_opcodes = 2; break; case E_AND: - if (the_insn.exp.X_add_symbol == NULL - && the_insn.exp.X_op_symbol == NULL - && (the_insn.exp.X_add_number < (1 << 16) - && the_insn.exp.X_add_number >= 0)) + if (the_insn.fi[0].exp.X_add_symbol == NULL + && the_insn.fi[0].exp.X_op_symbol == NULL + && (the_insn.fi[0].exp.X_add_number < (1 << 16) + && the_insn.fi[0].exp.X_add_number >= 0)) break; /* Emit "andnot h%const,ireg_src2,r31". */ pseudo[0].opcode = (the_insn.opcode & 0x03e0ffff) | 0xd4000000 | (atmp << 16); - pseudo[0].fup = (OP_IMM_S16 | OP_SEL_H); - pseudo[0].exp.X_add_number = -1 - the_insn.exp.X_add_number; + pseudo[0].fi[0].fup = (OP_IMM_S16 | OP_SEL_H); + pseudo[0].fi[0].exp.X_add_number = + -1 - the_insn.fi[0].exp.X_add_number; /* Emit "andnot l%const,r31,ireg_dest". */ pseudo[1].opcode = (the_insn.opcode & 0x001f0000) | 0xd4000000 | (atmp << 21); - pseudo[1].fup = (OP_IMM_S16 | OP_SEL_L); - pseudo[1].exp.X_add_number = -1 - the_insn.exp.X_add_number; + pseudo[1].fi[0].fup = (OP_IMM_S16 | OP_SEL_L); + pseudo[1].fi[0].exp.X_add_number = + -1 - the_insn.fi[0].exp.X_add_number; num_opcodes = 2; break; case E_S32: - if (the_insn.exp.X_add_symbol == NULL - && the_insn.exp.X_op_symbol == NULL - && (the_insn.exp.X_add_number < (1 << 15) - && the_insn.exp.X_add_number >= -(1 << 15))) + if (the_insn.fi[0].exp.X_add_symbol == NULL + && the_insn.fi[0].exp.X_op_symbol == NULL + && (the_insn.fi[0].exp.X_add_number < (1 << 15) + && the_insn.fi[0].exp.X_add_number >= -(1 << 15))) break; /* Emit "orh h%const,r0,r31". */ pseudo[0].opcode = 0xec000000 | (atmp << 16); - pseudo[0].fup = (OP_IMM_S16 | OP_SEL_H); + pseudo[0].fi[0].fup = (OP_IMM_S16 | OP_SEL_H); /* Emit "or l%const,r31,r31". */ pseudo[1].opcode = 0xe4000000 | (atmp << 21) | (atmp << 16); - pseudo[1].fup = (OP_IMM_S16 | OP_SEL_L); + pseudo[1].fi[0].fup = (OP_IMM_S16 | OP_SEL_L); /* Emit "r31,ireg_src2,ireg_dest". */ pseudo[2].opcode = (the_insn.opcode & ~0x0400ffff) | (atmp << 11); - pseudo[2].fup = OP_IMM_S16; + pseudo[2].fi[0].fup = OP_IMM_S16; num_opcodes = 3; break; @@ -355,32 +407,40 @@ md_assemble (str) as_warn (_("An instruction was expanded (%s)"), str); } + dwarf2_emit_insn (0); i = 0; do { + int tmp; + /* Output the opcode. Note that the i860 always reads instructions as little-endian data. */ destp = frag_more (4); number_to_chars_littleendian (destp, the_insn.opcode, 4); /* Check for expanded opcode after branch or in dual mode. */ - last_expand = the_insn.pcrel; - - /* Output the symbol-dependent stuff. */ - if (the_insn.fup != OP_NONE) - { - fixS *fix; - fix = fix_new_exp (frag_now, - destp - frag_now->fr_literal, - 4, - &the_insn.exp, - the_insn.pcrel, - the_insn.reloc); - - /* Despite the odd name, this is a scratch field. We use - it to encode operand type information. */ - fix->fx_addnumber = the_insn.fup; - } + last_expand = the_insn.fi[0].pcrel; + + /* Output the symbol-dependent stuff. Only btne and bte will ever + loop more than once here, since only they (possibly) have more + than one fixup. */ + for (tmp = 0; tmp < fc; tmp++) + { + if (the_insn.fi[tmp].fup != OP_NONE) + { + fixS *fix; + fix = fix_new_exp (frag_now, + destp - frag_now->fr_literal, + 4, + &the_insn.fi[tmp].exp, + the_insn.fi[tmp].pcrel, + the_insn.fi[tmp].reloc); + + /* Despite the odd name, this is a scratch field. We use + it to encode operand type information. */ + fix->fx_addnumber = the_insn.fi[tmp].fup; + } + } the_insn = pseudo[++i]; } while (--num_opcodes > 0); @@ -389,8 +449,7 @@ md_assemble (str) /* Assemble the instruction pointed to by STR. */ static void -i860_process_insn (str) - char *str; +i860_process_insn (char *str) { char *s; const char *args; @@ -409,7 +468,7 @@ i860_process_insn (str) opcode = 0; #endif - for (s = str; islower (*s) || *s == '.' || *s == '3' + for (s = str; ISLOWER (*s) || *s == '.' || *s == '3' || *s == '2' || *s == '1'; ++s) ; @@ -455,15 +514,23 @@ i860_process_insn (str) args_start = s; for (;;) { + int t; opcode = insn->match; memset (&the_insn, '\0', sizeof (the_insn)); - the_insn.reloc = BFD_RELOC_NONE; - the_insn.pcrel = 0; - the_insn.fup = OP_NONE; + fc = 0; + for (t = 0; t < MAX_FIXUPS; t++) + { + the_insn.fi[t].reloc = BFD_RELOC_NONE; + the_insn.fi[t].pcrel = 0; + the_insn.fi[t].fup = OP_NONE; + } /* Build the opcode, checking as we go that the operands match. */ for (args = insn->args; ; ++args) { + if (fc > MAX_FIXUPS) + abort (); + switch (*args) { @@ -485,9 +552,9 @@ i860_process_insn (str) /* Must be at least one digit. */ case '#': - if (isdigit (*s++)) + if (ISDIGIT (*s++)) { - while (isdigit (*s)) + while (ISDIGIT (*s)) ++s; continue; } @@ -500,7 +567,7 @@ i860_process_insn (str) /* Check for register prefix if necessary. */ if (reg_prefix && *s != reg_prefix) goto error; - else + else if (reg_prefix) s++; switch (*s) @@ -528,11 +595,11 @@ i860_process_insn (str) /* Any register r0..r31. */ case 'r': s++; - if (!isdigit (c = *s++)) + if (!ISDIGIT (c = *s++)) { goto error; } - if (isdigit (*s)) + if (ISDIGIT (*s)) { if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32) goto error; @@ -572,13 +639,13 @@ i860_process_insn (str) /* Check for register prefix if necessary. */ if (reg_prefix && *s != reg_prefix) goto error; - else + else if (reg_prefix) s++; - if (*s++ == 'f' && isdigit (*s)) + if (*s++ == 'f' && ISDIGIT (*s)) { mask = *s++; - if (isdigit (*s)) + if (ISDIGIT (*s)) { mask = 10 * (mask - '0') + (*s++ - '0'); if (mask >= 32) @@ -602,12 +669,6 @@ i860_process_insn (str) case 'g': opcode |= mask << 16; - if (dual_mode != DUAL_OFF) - opcode |= (1 << 9); - if (dual_mode == DUAL_DDOT) - dual_mode = DUAL_OFF; - if (dual_mode == DUAL_ONDDOT) - dual_mode = DUAL_ON; if ((opcode & (1 << 10)) && mask != 0 && (mask == ((opcode >> 11) & 0x1f))) as_warn (_("Pipelined instruction: fsrc1 = fdest")); @@ -621,7 +682,7 @@ i860_process_insn (str) /* Check for register prefix if necessary. */ if (reg_prefix && *s != reg_prefix) goto error; - else + else if (reg_prefix) s++; if (strncmp (s, "fir", 3) == 0) @@ -660,6 +721,43 @@ i860_process_insn (str) s += 4; continue; } + /* The remaining control registers are XP only. */ + if (target_xp && strncmp (s, "bear", 4) == 0) + { + opcode |= 0x6 << 21; + s += 4; + continue; + } + if (target_xp && strncmp (s, "ccr", 3) == 0) + { + opcode |= 0x7 << 21; + s += 3; + continue; + } + if (target_xp && strncmp (s, "p0", 2) == 0) + { + opcode |= 0x8 << 21; + s += 2; + continue; + } + if (target_xp && strncmp (s, "p1", 2) == 0) + { + opcode |= 0x9 << 21; + s += 2; + continue; + } + if (target_xp && strncmp (s, "p2", 2) == 0) + { + opcode |= 0xa << 21; + s += 2; + continue; + } + if (target_xp && strncmp (s, "p3", 2) == 0) + { + opcode |= 0xb << 21; + s += 2; + continue; + } break; /* 5-bit immediate in src1. */ @@ -667,74 +765,75 @@ i860_process_insn (str) if (! i860_get_expression (s)) { s = expr_end; - the_insn.fup |= OP_IMM_U5; + the_insn.fi[fc].fup |= OP_IMM_U5; + fc++; continue; } break; /* 26-bit immediate, relative branch (lbroff). */ case 'l': - the_insn.pcrel = 1; - the_insn.fup |= OP_IMM_BR26; + the_insn.fi[fc].pcrel = 1; + the_insn.fi[fc].fup |= OP_IMM_BR26; goto immediate; /* 16-bit split immediate, relative branch (sbroff). */ case 'r': - the_insn.pcrel = 1; - the_insn.fup |= OP_IMM_BR16; + the_insn.fi[fc].pcrel = 1; + the_insn.fi[fc].fup |= OP_IMM_BR16; goto immediate; /* 16-bit split immediate. */ case 's': - the_insn.fup |= OP_IMM_SPLIT16; + the_insn.fi[fc].fup |= OP_IMM_SPLIT16; goto immediate; /* 16-bit split immediate, byte aligned (st.b). */ case 'S': - the_insn.fup |= OP_IMM_SPLIT16; + the_insn.fi[fc].fup |= OP_IMM_SPLIT16; goto immediate; /* 16-bit split immediate, half-word aligned (st.s). */ case 'T': - the_insn.fup |= (OP_IMM_SPLIT16 | OP_ENCODE1 | OP_ALIGN2); + the_insn.fi[fc].fup |= (OP_IMM_SPLIT16 | OP_ENCODE1 | OP_ALIGN2); goto immediate; /* 16-bit split immediate, word aligned (st.l). */ case 'U': - the_insn.fup |= (OP_IMM_SPLIT16 | OP_ENCODE1 | OP_ALIGN4); + the_insn.fi[fc].fup |= (OP_IMM_SPLIT16 | OP_ENCODE1 | OP_ALIGN4); goto immediate; /* 16-bit immediate. */ case 'i': - the_insn.fup |= OP_IMM_S16; + the_insn.fi[fc].fup |= OP_IMM_S16; goto immediate; /* 16-bit immediate, byte aligned (ld.b). */ case 'I': - the_insn.fup |= OP_IMM_S16; + the_insn.fi[fc].fup |= OP_IMM_S16; goto immediate; /* 16-bit immediate, half-word aligned (ld.s). */ case 'J': - the_insn.fup |= (OP_IMM_S16 | OP_ENCODE1 | OP_ALIGN2); + the_insn.fi[fc].fup |= (OP_IMM_S16 | OP_ENCODE1 | OP_ALIGN2); goto immediate; /* 16-bit immediate, word aligned (ld.l, {p}fld.l, fst.l). */ case 'K': if (insn->name[0] == 'l') - the_insn.fup |= (OP_IMM_S16 | OP_ENCODE1 | OP_ALIGN4); + the_insn.fi[fc].fup |= (OP_IMM_S16 | OP_ENCODE1 | OP_ALIGN4); else - the_insn.fup |= (OP_IMM_S16 | OP_ENCODE2 | OP_ALIGN4); + the_insn.fi[fc].fup |= (OP_IMM_S16 | OP_ENCODE2 | OP_ALIGN4); goto immediate; /* 16-bit immediate, double-word aligned ({p}fld.d, fst.d). */ case 'L': - the_insn.fup |= (OP_IMM_S16 | OP_ENCODE3 | OP_ALIGN8); + the_insn.fi[fc].fup |= (OP_IMM_S16 | OP_ENCODE3 | OP_ALIGN8); goto immediate; /* 16-bit immediate, quad-word aligned (fld.q, fst.q). */ case 'M': - the_insn.fup |= (OP_IMM_S16 | OP_ENCODE3 | OP_ALIGN16); + the_insn.fi[fc].fup |= (OP_IMM_S16 | OP_ENCODE3 | OP_ALIGN16); /*FALLTHROUGH*/ @@ -742,90 +841,97 @@ i860_process_insn (str) SVR4 syntax. The Intel syntax is "ha%immediate" whereas SVR4 syntax is "[immediate]@ha". */ immediate: -#ifdef SYNTAX_SVR4 - if (*s == ' ') - s++; - - /* Note that if i860_get_expression() fails, we will still - have created U entries in the symbol table for the - 'symbols' in the input string. Try not to create U - symbols for registers, etc. */ - if (! i860_get_expression (s)) - s = expr_end; - else - goto error; - - if (strncmp (s, "@ha", 3) == 0) - { - the_insn.fup |= OP_SEL_HA; - s += 3; - } - else if (strncmp (s, "@h", 2) == 0) - { - the_insn.fup |= OP_SEL_H; - s += 2; - } - else if (strncmp (s, "@l", 2) == 0) - { - the_insn.fup |= OP_SEL_L; - s += 2; - } - else if (strncmp (s, "@gotoff", 7) == 0 - || strncmp (s, "@GOTOFF", 7) == 0) - { - as_bad (_("Assembler does not yet support PIC")); - the_insn.fup |= OP_SEL_GOTOFF; - s += 7; - } - else if (strncmp (s, "@got", 4) == 0 - || strncmp (s, "@GOT", 4) == 0) + if (target_intel_syntax == 0) { - as_bad (_("Assembler does not yet support PIC")); - the_insn.fup |= OP_SEL_GOT; - s += 4; - } - else if (strncmp (s, "@plt", 4) == 0 - || strncmp (s, "@PLT", 4) == 0) - { - as_bad (_("Assembler does not yet support PIC")); - the_insn.fup |= OP_SEL_PLT; - s += 4; - } + /* AT&T/SVR4 syntax. */ + if (*s == ' ') + s++; + + /* Note that if i860_get_expression() fails, we will still + have created U entries in the symbol table for the + 'symbols' in the input string. Try not to create U + symbols for registers, etc. */ + if (! i860_get_expression (s)) + s = expr_end; + else + goto error; + + if (strncmp (s, "@ha", 3) == 0) + { + the_insn.fi[fc].fup |= OP_SEL_HA; + s += 3; + } + else if (strncmp (s, "@h", 2) == 0) + { + the_insn.fi[fc].fup |= OP_SEL_H; + s += 2; + } + else if (strncmp (s, "@l", 2) == 0) + { + the_insn.fi[fc].fup |= OP_SEL_L; + s += 2; + } + else if (strncmp (s, "@gotoff", 7) == 0 + || strncmp (s, "@GOTOFF", 7) == 0) + { + as_bad (_("Assembler does not yet support PIC")); + the_insn.fi[fc].fup |= OP_SEL_GOTOFF; + s += 7; + } + else if (strncmp (s, "@got", 4) == 0 + || strncmp (s, "@GOT", 4) == 0) + { + as_bad (_("Assembler does not yet support PIC")); + the_insn.fi[fc].fup |= OP_SEL_GOT; + s += 4; + } + else if (strncmp (s, "@plt", 4) == 0 + || strncmp (s, "@PLT", 4) == 0) + { + as_bad (_("Assembler does not yet support PIC")); + the_insn.fi[fc].fup |= OP_SEL_PLT; + s += 4; + } - the_insn.expand = insn->expand; + the_insn.expand = insn->expand; + fc++; - continue; -#else /* ! SYNTAX_SVR4 */ - if (*s == ' ') - s++; - if (strncmp (s, "ha%", 3) == 0) - { - the_insn.fup |= OP_SEL_HA; - s += 3; - } - else if (strncmp (s, "h%", 2) == 0) - { - the_insn.fup |= OP_SEL_H; - s += 2; + continue; } - else if (strncmp (s, "l%", 2) == 0) + else { - the_insn.fup |= OP_SEL_L; - s += 2; + /* Intel syntax. */ + if (*s == ' ') + s++; + if (strncmp (s, "ha%", 3) == 0) + { + the_insn.fi[fc].fup |= OP_SEL_HA; + s += 3; + } + else if (strncmp (s, "h%", 2) == 0) + { + the_insn.fi[fc].fup |= OP_SEL_H; + s += 2; + } + else if (strncmp (s, "l%", 2) == 0) + { + the_insn.fi[fc].fup |= OP_SEL_L; + s += 2; + } + the_insn.expand = insn->expand; + + /* Note that if i860_get_expression() fails, we will still + have created U entries in the symbol table for the + 'symbols' in the input string. Try not to create U + symbols for registers, etc. */ + if (! i860_get_expression (s)) + s = expr_end; + else + goto error; + + fc++; + continue; } - the_insn.expand = insn->expand; - - /* Note that if i860_get_expression() fails, we will still - have created U entries in the symbol table for the - 'symbols' in the input string. Try not to create U - symbols for registers, etc. */ - if (! i860_get_expression (s)) - s = expr_end; - else - goto error; - - continue; -#endif /* SYNTAX_SVR4 */ break; default: @@ -853,19 +959,43 @@ i860_process_insn (str) break; } + /* Set the dual bit on this instruction if necessary. */ + if (dual_mode != DUAL_OFF) + { + if ((opcode & 0xfc000000) == 0x48000000 || opcode == 0xb0000000) + { + /* The instruction is a flop or a fnop, so set its dual bit + (but check that it is 8-byte aligned). */ + if (((frag_now->fr_address + frag_now_fix_octets ()) & 7) == 0) + opcode |= (1 << 9); + else + as_bad (_("'d.%s' must be 8-byte aligned"), insn->name); + + if (dual_mode == DUAL_DDOT) + dual_mode = DUAL_OFF; + else if (dual_mode == DUAL_ONDDOT) + dual_mode = DUAL_ON; + } + else if (dual_mode == DUAL_DDOT || dual_mode == DUAL_ONDDOT) + as_bad (_("Prefix 'd.' invalid for instruction `%s'"), insn->name); + } + the_insn.opcode = opcode; + + /* Only recognize XP instructions when the user has requested it. */ + if (insn->expand == XP_ONLY && ! target_xp) + as_bad (_("Unknown opcode: `%s'"), insn->name); } static int -i860_get_expression (str) - char *str; +i860_get_expression (char *str) { char *save_in; segT seg; save_in = input_line_pointer; input_line_pointer = str; - seg = expression (&the_insn.exp); + seg = expression (&the_insn.fi[fc].exp); if (seg != absolute_section && seg != undefined_section && ! SEG_NORMAL (seg)) @@ -880,74 +1010,15 @@ i860_get_expression (str) return 0; } -/* 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. */ - -/* Equal to MAX_PRECISION in atof-ieee.c. */ -#define MAX_LITTLENUMS 6 - -char * -md_atof (type, litP, sizeP) - char type; - char *litP; - int *sizeP; +const char * +md_atof (int type, char *litP, int *sizeP) { - int prec; - LITTLENUM_TYPE words[MAX_LITTLENUMS]; - LITTLENUM_TYPE *wordP; - char *t; - char *atof_ieee (); - - switch (type) - { - case 'f': - case 'F': - case 's': - case 'S': - prec = 2; - break; - - case 'd': - case 'D': - case 'r': - case 'R': - prec = 4; - break; - - case 'x': - case 'X': - prec = 6; - break; - - case 'p': - case 'P': - prec = 6; - 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); - for (wordP = words; prec--;) - { - md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE)); - litP += sizeof (LITTLENUM_TYPE); - } - return 0; + return ieee_md_atof (type, litP, sizeP, TRUE); } /* Write out in current endian mode. */ void -md_number_to_chars (buf, val, n) - char *buf; - valueT val; - int n; +md_number_to_chars (char *buf, valueT val, int n) { if (target_big_endian) number_to_chars_bigendian (buf, val, n); @@ -955,39 +1026,17 @@ md_number_to_chars (buf, val, n) number_to_chars_littleendian (buf, val, n); } -/* This should never be called for i860. */ -void -md_number_to_disp (buf, val, n) - char *buf ATTRIBUTE_UNUSED; - long val ATTRIBUTE_UNUSED; - int n ATTRIBUTE_UNUSED; -{ - as_fatal (_("md_number_to_disp\n")); -} - -/* This should never be called for i860. */ -void -md_number_to_field (buf, val, fix) - char *buf ATTRIBUTE_UNUSED; - long val ATTRIBUTE_UNUSED; - void *fix ATTRIBUTE_UNUSED; -{ - as_fatal (_("i860_number_to_field\n")); -} - /* This should never be called for i860. */ int -md_estimate_size_before_relax (fragP, segtype) - register fragS *fragP ATTRIBUTE_UNUSED; - segT segtype ATTRIBUTE_UNUSED; +md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED, + segT segtype ATTRIBUTE_UNUSED) { - as_fatal (_("i860_estimate_size_before_relax\n")); + as_fatal (_("relaxation not supported\n")); } #ifdef DEBUG_I860 static void -print_insn (insn) - struct i860_it *insn; +print_insn (struct i860_it *insn) { if (insn->error) fprintf (stderr, "ERROR: %s\n", insn->error); @@ -1013,27 +1062,29 @@ print_insn (insn) #ifdef OBJ_ELF -CONST char *md_shortopts = "VQ:"; +const char *md_shortopts = "VQ:"; #else -CONST char *md_shortopts = ""; +const char *md_shortopts = ""; #endif #define OPTION_EB (OPTION_MD_BASE + 0) #define OPTION_EL (OPTION_MD_BASE + 1) #define OPTION_WARN_EXPAND (OPTION_MD_BASE + 2) +#define OPTION_XP (OPTION_MD_BASE + 3) +#define OPTION_INTEL_SYNTAX (OPTION_MD_BASE + 4) struct option md_longopts[] = { { "EB", no_argument, NULL, OPTION_EB }, { "EL", no_argument, NULL, OPTION_EL }, { "mwarn-expand", no_argument, NULL, OPTION_WARN_EXPAND }, + { "mxp", no_argument, NULL, OPTION_XP }, + { "mintel-syntax",no_argument, NULL, OPTION_INTEL_SYNTAX }, { NULL, no_argument, NULL, 0 } }; size_t md_longopts_size = sizeof (md_longopts); int -md_parse_option (c, arg) - int c; - char *arg ATTRIBUTE_UNUSED; +md_parse_option (int c, const char *arg ATTRIBUTE_UNUSED) { switch (c) { @@ -1049,6 +1100,14 @@ md_parse_option (c, arg) target_warn_expand = 1; break; + case OPTION_XP: + target_xp = 1; + break; + + case OPTION_INTEL_SYNTAX: + target_intel_syntax = 1; + break; + #ifdef OBJ_ELF /* SVR4 argument compatibility (-V): print version ID. */ case 'V': @@ -1069,13 +1128,14 @@ md_parse_option (c, arg) } void -md_show_usage (stream) - FILE *stream; +md_show_usage (FILE *stream) { fprintf (stream, _("\ -EL generate code for little endian mode (default)\n\ -EB generate code for big endian mode\n\ - -mwarn-expand warn if pseudo operations are expanded\n")); + -mwarn-expand warn if pseudo operations are expanded\n\ + -mxp enable i860XP support (disabled by default)\n\ + -mintel-syntax enable Intel syntax (default to AT&T/SVR4)\n")); #ifdef OBJ_ELF /* SVR4 compatibility flags. */ fprintf (stream, _("\ @@ -1087,16 +1147,14 @@ md_show_usage (stream) /* We have no need to default values of symbols. */ symbolS * -md_undefined_symbol (name) - char *name ATTRIBUTE_UNUSED; +md_undefined_symbol (char *name ATTRIBUTE_UNUSED) { return 0; } /* The i860 denotes auto-increment with '++'. */ void -md_operand (exp) - expressionS *exp; +md_operand (expressionS *exp) { char *s; @@ -1113,19 +1171,17 @@ md_operand (exp) /* Round up a section size to the appropriate boundary. */ valueT -md_section_align (segment, size) - segT segment ATTRIBUTE_UNUSED; - valueT size ATTRIBUTE_UNUSED; +md_section_align (segT segment ATTRIBUTE_UNUSED, + valueT size ATTRIBUTE_UNUSED) { /* Byte alignment is fine. */ return size; } /* On the i860, a PC-relative offset is relative to the address of the - of the offset plus its size. */ + offset plus its size. */ long -md_pcrel_from (fixP) - fixS *fixP; +md_pcrel_from (fixS *fixP) { return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address; } @@ -1134,9 +1190,7 @@ md_pcrel_from (fixP) Also adjust the given immediate as necessary. Finally, check that all constraints (such as alignment) are satisfied. */ static bfd_reloc_code_real_type -obtain_reloc_for_imm16 (fix, val) - fixS *fix; - long *val; +obtain_reloc_for_imm16 (fixS *fix, long *val) { valueT fup = fix->fx_addnumber; bfd_reloc_code_real_type reloc; @@ -1228,15 +1282,12 @@ obtain_reloc_for_imm16 (fix, val) /* Attempt to simplify or eliminate a fixup. To indicate that a fixup has been eliminated, set fix->fx_done. If fix->fx_addsy is non-NULL, we will have to generate a reloc entry. */ -int -md_apply_fix3 (fix, valuep, seg) - fixS *fix; - valueT *valuep; - segT seg ATTRIBUTE_UNUSED; -{ +void +md_apply_fix (fixS *fix, valueT *valP, segT seg ATTRIBUTE_UNUSED) +{ char *buf; - long val = (long)(*valuep); + long val = *valP; unsigned long insn; valueT fup; @@ -1250,7 +1301,7 @@ md_apply_fix3 (fix, valuep, seg) /* Determine the necessary relocations as well as inserting an immediate into the instruction. */ - if (fup == OP_IMM_U5) + if (fup & OP_IMM_U5) { if (val & ~0x1f) as_bad_where (fix->fx_file, fix->fx_line, @@ -1280,9 +1331,8 @@ md_apply_fix3 (fix, valuep, seg) } } else if (fup & OP_IMM_U16) - { - abort (); - } + abort (); + else if (fup & OP_IMM_SPLIT16) { fix->fx_r_type = obtain_reloc_for_imm16 (fix, &val); @@ -1347,7 +1397,7 @@ md_apply_fix3 (fix, valuep, seg) else if (fup != OP_NONE) { as_bad_where (fix->fx_file, fix->fx_line, - _("Unrecognized fix-up (0x%08x)"), fup); + _("Unrecognized fix-up (0x%08lx)"), (unsigned long) fup); abort (); } else @@ -1367,21 +1417,17 @@ md_apply_fix3 (fix, valuep, seg) fix->fx_done = 1; } } - - /* Return value ignored. */ - return 0; } /* Generate a machine dependent reloc from a fixup. */ arelent* -tc_gen_reloc (section, fixp) - asection *section ATTRIBUTE_UNUSED; - fixS *fixp; +tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, + fixS *fixp) { arelent *reloc; - reloc = xmalloc (sizeof (*reloc)); - reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + reloc = XNEW (arelent); + reloc->sym_ptr_ptr = XNEW (asymbol *); *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; reloc->addend = fixp->fx_offset; @@ -1395,3 +1441,51 @@ tc_gen_reloc (section, fixp) } return reloc; } + +/* This is called from HANDLE_ALIGN in write.c. Fill in the contents + of an rs_align_code fragment. */ + +void +i860_handle_align (fragS *fragp) +{ + /* Instructions are always stored little-endian on the i860. */ + static const unsigned char le_nop[] = { 0x00, 0x00, 0x00, 0xA0 }; + + int bytes; + char *p; + + if (fragp->fr_type != rs_align_code) + return; + + bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix; + p = fragp->fr_literal + fragp->fr_fix; + + /* Make sure we are on a 4-byte boundary, in case someone has been + putting data into a text section. */ + if (bytes & 3) + { + int fix = bytes & 3; + memset (p, 0, fix); + p += fix; + fragp->fr_fix += fix; + } + + memcpy (p, le_nop, 4); + fragp->fr_var = 4; +} + +/* This is called after a user-defined label is seen. We check + if the label has a double colon (valid in Intel syntax mode only), + in which case it should be externalized. */ + +void +i860_check_label (symbolS *labelsym) +{ + /* At this point, the current line pointer is sitting on the character + just after the first colon on the label. */ + if (target_intel_syntax && *input_line_pointer == ':') + { + S_SET_EXTERNAL (labelsym); + input_line_pointer++; + } +}