diff options
Diffstat (limited to 'gas/config/tc-i860.c')
-rw-r--r-- | gas/config/tc-i860.c | 1263 |
1 files changed, 1263 insertions, 0 deletions
diff --git a/gas/config/tc-i860.c b/gas/config/tc-i860.c new file mode 100644 index 0000000..f2e2b19 --- /dev/null +++ b/gas/config/tc-i860.c @@ -0,0 +1,1263 @@ +/* tc-i860.c -- Assemble for the I860 + Copyright (C) 1989, 92, 93, 94, 95, 1998 Free Software Foundation, Inc. + + 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) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + 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. */ + +#include "as.h" + +#include "opcode/i860.h" + +void md_begin (); +void md_number_to_chars (); +void md_assemble (); +char *md_atof (); +void md_convert_frag (); +int md_estimate_size_before_relax (); +void md_number_to_imm (); +void md_number_to_disp (); +void md_number_to_field (); +void md_ri_to_chars (); +static void i860_ip (); +/* void emit_machine_reloc(); */ + +const int md_reloc_size = sizeof (struct relocation_info); + +/* void (*md_emit_relocations)() = emit_machine_reloc; */ + +/* handle of the OPCODE hash table */ +static struct hash_control *op_hash = NULL; + +static void s_dual (), s_enddual (); +static void s_atmp (); + +const pseudo_typeS + md_pseudo_table[] = +{ + {"dual", s_dual, 4}, + {"enddual", s_enddual, 4}, + {"atmp", s_atmp, 4}, + {NULL, 0, 0}, +}; + +/* This array holds the chars that always start a comment. If the + pre-processor is disabled, these aren't very useful */ +const char comment_chars[] = "!/"; /* JF removed '|' from comment_chars */ + +/* This array holds the chars that only start a comment at the beginning of + a line. If the line seems to have the form '# 123 filename' + .line and .file directives will appear in the pre-processed output */ +/* Note that input_file.c hand checks for '#' at the beginning of the + first line of the input file. This is because the compiler outputs + #NO_APP at the beginning of its output. */ +/* Also note that comments like this one will always work. */ +const char line_comment_chars[] = "#/"; + +const char line_separator_chars[] = ""; + +/* Chars that can be used to separate mant from exp in floating point nums */ +const char EXP_CHARS[] = "eE"; + +/* Chars that mean this number is a floating point constant */ +/* As in 0f12.456 */ +/* or 0d1.2345e12 */ +const char FLT_CHARS[] = "rRsSfFdDxXpP"; + +/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be + changed in read.c . Ideally it shouldn't have to know about it at all, + but nothing is ideal around here. + */ +int size_reloc_info = sizeof (struct relocation_info); + +static unsigned char octal[256]; +#define isoctal(c) octal[c] +static unsigned char toHex[256]; + +struct i860_it + { + char *error; + unsigned long opcode; + struct nlist *nlistp; + expressionS exp; + int pcrel; + enum expand_type expand; + enum highlow_type highlow; + enum reloc_type reloc; + } the_insn; + +#if __STDC__ == 1 + +static void print_insn (struct i860_it *insn); +static int getExpression (char *str); + +#else /* not __STDC__ */ + +static void print_insn (); +static int getExpression (); + +#endif /* not __STDC__ */ + +static char *expr_end; +static char last_expand; /* error if expansion after branch */ + +enum dual +{ + DUAL_OFF = 0, DUAL_ON, DUAL_DDOT, DUAL_ONDDOT, +}; +static enum dual dual_mode = DUAL_OFF; /* dual-instruction mode */ + +static void +s_dual () /* floating point instructions have dual set */ +{ + dual_mode = DUAL_ON; +} + +static void +s_enddual () /* floating point instructions have dual set */ +{ + dual_mode = DUAL_OFF; +} + +static int atmp = 31; /* temporary register for pseudo's */ + +static void +s_atmp () +{ + register int temp; + if (strncmp (input_line_pointer, "sp", 2) == 0) + { + input_line_pointer += 2; + atmp = 2; + } + else if (strncmp (input_line_pointer, "fp", 2) == 0) + { + input_line_pointer += 2; + atmp = 3; + } + else if (strncmp (input_line_pointer, "r", 1) == 0) + { + input_line_pointer += 1; + temp = get_absolute_expression (); + if (temp >= 0 && temp <= 31) + atmp = temp; + else + as_bad (_("Unknown temporary pseudo register")); + } + else + { + as_bad (_("Unknown temporary pseudo register")); + } + demand_empty_rest_of_line (); +} + +/* This function is called once, at assembler startup time. It should + set up all the tables, etc. that the MD part of the assembler will need. */ +void +md_begin () +{ + register char *retval = NULL; + int lose = 0; + register unsigned int i = 0; + + op_hash = hash_new (); + + while (i < NUMOPCODES) + { + const char *name = i860_opcodes[i].name; + retval = hash_insert (op_hash, name, &i860_opcodes[i]); + if (retval != NULL) + { + fprintf (stderr, _("internal error: can't hash `%s': %s\n"), + i860_opcodes[i].name, retval); + lose = 1; + } + do + { + if (i860_opcodes[i].match & i860_opcodes[i].lose) + { + fprintf (stderr, _("internal error: losing opcode: `%s' \"%s\"\n"), + i860_opcodes[i].name, i860_opcodes[i].args); + lose = 1; + } + ++i; + } + while (i < NUMOPCODES + && !strcmp (i860_opcodes[i].name, name)); + } + + if (lose) + as_fatal (_("Broken assembler. No assembly attempted.")); + + for (i = '0'; i < '8'; ++i) + octal[i] = 1; + for (i = '0'; i <= '9'; ++i) + toHex[i] = i - '0'; + for (i = 'a'; i <= 'f'; ++i) + toHex[i] = i + 10 - 'a'; + for (i = 'A'; i <= 'F'; ++i) + toHex[i] = i + 10 - 'A'; +} + +void +md_assemble (str) + char *str; +{ + char *toP; + int rsd; + int no_opcodes = 1; + int i; + struct i860_it pseudo[3]; + + assert (str); + i860_ip (str); + + /* check for expandable flag to produce pseudo-instructions */ + if (the_insn.expand != 0 && the_insn.highlow == NO_SPEC) + { + for (i = 0; i < 3; i++) + pseudo[i] = the_insn; + + switch (the_insn.expand) + { + + case E_DELAY: + no_opcodes = 1; + 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))) + break; + /* or l%const,r0,ireg_dest */ + pseudo[0].opcode = (the_insn.opcode & 0x001f0000) | 0xe4000000; + pseudo[0].highlow = PAIR; + /* orh h%const,ireg_dest,ireg_dest */ + pseudo[1].opcode = (the_insn.opcode & 0x03ffffff) | 0xec000000 | + ((the_insn.opcode & 0x001f0000) << 5); + pseudo[1].highlow = HIGH; + no_opcodes = 2; + break; + + case E_ADDR: + if (the_insn.exp.X_add_symbol == NULL && + the_insn.exp.X_op_symbol == NULL) + break; + /* orh ha%addr_expr,r0,r31 */ + pseudo[0].opcode = 0xec000000 | (atmp << 16); + pseudo[0].highlow = HIGHADJ; + pseudo[0].reloc = LOW0; /* must overwrite */ + /* l%addr_expr(r31),ireg_dest */ + pseudo[1].opcode = (the_insn.opcode & ~0x003e0000) | (atmp << 21); + pseudo[1].highlow = PAIR; + no_opcodes = 2; + break; + + case E_U32: /* 2nd version emulates Intel as, not doc. */ + 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)) + break; + /* $(opcode)h h%const,ireg_src2,ireg_dest + pseudo[0].opcode = (the_insn.opcode & 0xf3ffffff) | 0x0c000000; */ + /* $(opcode)h h%const,ireg_src2,r31 */ + pseudo[0].opcode = (the_insn.opcode & 0xf3e0ffff) | 0x0c000000 | + (atmp << 16); + pseudo[0].highlow = HIGH; + /* $(opcode) l%const,ireg_dest,ireg_dest + pseudo[1].opcode = (the_insn.opcode & 0xf01f0000) | 0x04000000 | + ((the_insn.opcode & 0x001f0000) << 5); */ + /* $(opcode) l%const,r31,ireg_dest */ + pseudo[1].opcode = (the_insn.opcode & 0xf01f0000) | 0x04000000 | + (atmp << 21); + pseudo[1].highlow = PAIR; + no_opcodes = 2; + break; + + case E_AND: /* 2nd version emulates Intel as, not doc. */ + 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)) + break; + /* andnot h%const,ireg_src2,ireg_dest + pseudo[0].opcode = (the_insn.opcode & 0x03ffffff) | 0xd4000000; */ + /* andnot h%const,ireg_src2,r31 */ + pseudo[0].opcode = (the_insn.opcode & 0x03e0ffff) | 0xd4000000 | + (atmp << 16); + pseudo[0].highlow = HIGH; + pseudo[0].exp.X_add_number = -1 - the_insn.exp.X_add_number; + /* andnot l%const,ireg_dest,ireg_dest + pseudo[1].opcode = (the_insn.opcode & 0x001f0000) | 0xd4000000 | + ((the_insn.opcode & 0x001f0000) << 5); */ + /* andnot l%const,r31,ireg_dest */ + pseudo[1].opcode = (the_insn.opcode & 0x001f0000) | 0xd4000000 | + (atmp << 21); + pseudo[1].highlow = PAIR; + pseudo[1].exp.X_add_number = -1 - the_insn.exp.X_add_number; + no_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))) + break; + /* orh h%const,r0,r31 */ + pseudo[0].opcode = 0xec000000 | (atmp << 16); + pseudo[0].highlow = HIGH; + /* or l%const,r31,r31 */ + pseudo[1].opcode = 0xe4000000 | (atmp << 21) | (atmp << 16); + pseudo[1].highlow = PAIR; + /* r31,ireg_src2,ireg_dest */ + pseudo[2].opcode = (the_insn.opcode & ~0x0400ffff) | (atmp << 11); + pseudo[2].reloc = NO_RELOC; + no_opcodes = 3; + break; + + default: + as_fatal (_("failed sanity check.")); + } + + the_insn = pseudo[0]; + /* check for expanded opcode after branch or in dual */ + if (no_opcodes > 1 && last_expand == 1) + as_warn (_("Expanded opcode after delayed branch: `%s'"), str); + if (no_opcodes > 1 && dual_mode != DUAL_OFF) + as_warn (_("Expanded opcode in dual mode: `%s'"), str); + } + + i = 0; + do + { /* always produce at least one opcode */ + toP = frag_more (4); + /* put out the opcode */ + md_number_to_chars (toP, the_insn.opcode, 4); + + /* check for expanded opcode after branch or in dual */ + last_expand = the_insn.pcrel; + + /* put out the symbol-dependent stuff */ + if (the_insn.reloc != NO_RELOC) + { + fix_new (frag_now, /* which frag */ + (toP - frag_now->fr_literal), /* where */ + 4, /* size */ + &the_insn.exp, + the_insn.pcrel, + /* merge bit fields into one argument */ + (int) (((the_insn.highlow & 0x3) << 4) | (the_insn.reloc & 0xf))); + } + the_insn = pseudo[++i]; + } + while (--no_opcodes > 0); + +} + +static void +i860_ip (str) + char *str; +{ + char *s; + const char *args; + char c; + unsigned long i; + struct i860_opcode *insn; + char *argsStart; + unsigned long opcode; + unsigned int mask; + int match = 0; + int comma = 0; + + + for (s = str; islower (*s) || *s == '.' || *s == '3'; ++s) + ; + switch (*s) + { + + case '\0': + break; + + case ',': + comma = 1; + + /*FALLTHROUGH*/ + + case ' ': + *s++ = '\0'; + break; + + default: + as_fatal (_("Unknown opcode: `%s'"), str); + } + + if (strncmp (str, "d.", 2) == 0) + { /* check for d. opcode prefix */ + if (dual_mode == DUAL_ON) + dual_mode = DUAL_ONDDOT; + else + dual_mode = DUAL_DDOT; + str += 2; + } + + if ((insn = (struct i860_opcode *) hash_find (op_hash, str)) == NULL) + { + if (dual_mode == DUAL_DDOT || dual_mode == DUAL_ONDDOT) + str -= 2; + as_bad (_("Unknown opcode: `%s'"), str); + return; + } + if (comma) + { + *--s = ','; + } + argsStart = s; + for (;;) + { + opcode = insn->match; + memset (&the_insn, '\0', sizeof (the_insn)); + the_insn.reloc = NO_RELOC; + + /* + * Build the opcode, checking as we go to make + * sure that the operands match + */ + for (args = insn->args;; ++args) + { + switch (*args) + { + + case '\0': /* end of args */ + if (*s == '\0') + { + match = 1; + } + break; + + case '+': + case '(': /* these must match exactly */ + case ')': + case ',': + case ' ': + if (*s++ == *args) + continue; + break; + + case '#': /* must be at least one digit */ + if (isdigit (*s++)) + { + while (isdigit (*s)) + { + ++s; + } + continue; + } + break; + + case '1': /* next operand must be a register */ + case '2': + case 'd': + switch (*s) + { + + case 'f': /* frame pointer */ + s++; + if (*s++ == 'p') + { + mask = 0x3; + break; + } + goto error; + + case 's': /* stack pointer */ + s++; + if (*s++ == 'p') + { + mask = 0x2; + break; + } + goto error; + + case 'r': /* any register */ + s++; + if (!isdigit (c = *s++)) + { + goto error; + } + if (isdigit (*s)) + { + if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32) + { + goto error; + } + } + else + { + c -= '0'; + } + mask = c; + break; + + default: /* not this opcode */ + goto error; + } + /* + * Got the register, now figure out where + * it goes in the opcode. + */ + switch (*args) + { + + case '1': + opcode |= mask << 11; + continue; + + case '2': + opcode |= mask << 21; + continue; + + case 'd': + opcode |= mask << 16; + continue; + + } + break; + + case 'e': /* next operand is a floating point register */ + case 'f': + case 'g': + if (*s++ == 'f' && isdigit (*s)) + { + mask = *s++; + if (isdigit (*s)) + { + mask = 10 * (mask - '0') + (*s++ - '0'); + if (mask >= 32) + { + break; + } + } + else + { + mask -= '0'; + } + switch (*args) + { + + case 'e': + opcode |= mask << 11; + continue; + + case 'f': + opcode |= mask << 21; + continue; + + case 'g': + opcode |= mask << 16; + if (dual_mode != DUAL_OFF) + opcode |= (1 << 9); /* dual mode instruction */ + if (dual_mode == DUAL_DDOT) + dual_mode = DUAL_OFF; + if (dual_mode == DUAL_ONDDOT) + dual_mode = DUAL_ON; + if ((opcode & (1 << 10)) && (mask == ((opcode >> 11) & 0x1f))) + as_warn (_("Fsr1 equals fdest with Pipelining")); + continue; + } + } + break; + + case 'c': /* next operand must be a control register */ + if (strncmp (s, "fir", 3) == 0) + { + opcode |= 0x0 << 21; + s += 3; + continue; + } + if (strncmp (s, "psr", 3) == 0) + { + opcode |= 0x1 << 21; + s += 3; + continue; + } + if (strncmp (s, "dirbase", 7) == 0) + { + opcode |= 0x2 << 21; + s += 7; + continue; + } + if (strncmp (s, "db", 2) == 0) + { + opcode |= 0x3 << 21; + s += 2; + continue; + } + if (strncmp (s, "fsr", 3) == 0) + { + opcode |= 0x4 << 21; + s += 3; + continue; + } + if (strncmp (s, "epsr", 4) == 0) + { + opcode |= 0x5 << 21; + s += 4; + continue; + } + break; + + case '5': /* 5 bit immediate in src1 */ + memset (&the_insn, '\0', sizeof (the_insn)); + if (!getExpression (s)) + { + s = expr_end; + if (the_insn.exp.X_add_number & ~0x1f) + as_bad (_("5-bit immediate too large")); + opcode |= (the_insn.exp.X_add_number & 0x1f) << 11; + memset (&the_insn, '\0', sizeof (the_insn)); + the_insn.reloc = NO_RELOC; + continue; + } + break; + + case 'l': /* 26 bit immediate, relative branch */ + the_insn.reloc = BRADDR; + the_insn.pcrel = 1; + goto immediate; + + case 's': /* 16 bit immediate, split relative branch */ + /* upper 5 bits of offset in dest field */ + the_insn.pcrel = 1; + the_insn.reloc = SPLIT0; + goto immediate; + + case 'S': /* 16 bit immediate, split (st), aligned */ + if (opcode & (1 << 28)) + if (opcode & 0x1) + the_insn.reloc = SPLIT2; + else + the_insn.reloc = SPLIT1; + else + the_insn.reloc = SPLIT0; + goto immediate; + + case 'I': /* 16 bit immediate, aligned */ + if (opcode & (1 << 28)) + if (opcode & 0x1) + the_insn.reloc = LOW2; + else + the_insn.reloc = LOW1; + else + the_insn.reloc = LOW0; + goto immediate; + + case 'i': /* 16 bit immediate */ + the_insn.reloc = LOW0; + + /*FALLTHROUGH*/ + + immediate: + if (*s == ' ') + s++; + if (strncmp (s, "ha%", 3) == 0) + { + the_insn.highlow = HIGHADJ; + s += 3; + } + else if (strncmp (s, "h%", 2) == 0) + { + the_insn.highlow = HIGH; + s += 2; + } + else if (strncmp (s, "l%", 2) == 0) + { + the_insn.highlow = PAIR; + s += 2; + } + the_insn.expand = insn->expand; + + /* Note that if the getExpression() 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 (!getExpression (s)) + { + s = expr_end; + continue; + } + break; + + default: + as_fatal (_("failed sanity check.")); + } + break; + } + error: + if (match == 0) + { + /* Args don't match. */ + if (&insn[1] - i860_opcodes < NUMOPCODES + && !strcmp (insn->name, insn[1].name)) + { + ++insn; + s = argsStart; + continue; + } + else + { + as_bad (_("Illegal operands")); + return; + } + } + break; + } + + the_insn.opcode = opcode; +} + +static int +getExpression (str) + char *str; +{ + char *save_in; + segT seg; + + save_in = input_line_pointer; + input_line_pointer = str; + seg = expression (&the_insn.exp); + if (seg != absolute_section + && seg != undefined_section + && ! SEG_NORMAL (seg)) + { + the_insn.error = _("bad segment"); + expr_end = input_line_pointer; + input_line_pointer = save_in; + return 1; + } + expr_end = input_line_pointer; + input_line_pointer = save_in; + return 0; +} + + +/* + This is identical to the md_atof in m68k.c. I think this is right, + but I'm not sure. + + 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; +{ + 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; +} + +/* + * Write out big-endian. + */ +void +md_number_to_chars (buf, val, n) + char *buf; + valueT val; + int n; +{ + number_to_chars_bigendian (buf, val, n); +} + +void +md_number_to_imm (buf, val, n, fixP) + char *buf; + long val; + int n; + fixS *fixP; +{ + enum reloc_type reloc = fixP->fx_r_type & 0xf; + enum highlow_type highlow = (fixP->fx_r_type >> 4) & 0x3; + + assert (buf); + assert (n == 4); /* always on i860 */ + + switch (highlow) + { + + case HIGHADJ: /* adjusts the high-order 16-bits */ + if (val & (1 << 15)) + val += (1 << 16); + + /*FALLTHROUGH*/ + + case HIGH: /* selects the high-order 16-bits */ + val >>= 16; + break; + + case PAIR: /* selects the low-order 16-bits */ + val = val & 0xffff; + break; + + default: + break; + } + + switch (reloc) + { + + case BRADDR: /* br,call,bc,bc.t,bnc,bnc.t w/26-bit immediate */ + if (fixP->fx_pcrel != 1) + as_bad (_("26-bit branch w/o pc relative set: 0x%08x"), val); + val >>= 2; /* align pcrel offset, see manual */ + + if (val >= (1 << 25) || val < -(1 << 25)) /* check for overflow */ + as_bad (_("26-bit branch offset overflow: 0x%08x"), val); + buf[0] = (buf[0] & 0xfc) | ((val >> 24) & 0x3); + buf[1] = val >> 16; + buf[2] = val >> 8; + buf[3] = val; + break; + + case SPLIT2: /* 16 bit immediate, 4-byte aligned */ + if (val & 0x3) + as_bad (_("16-bit immediate 4-byte alignment error: 0x%08x"), val); + val &= ~0x3; /* 4-byte align value */ + /*FALLTHROUGH*/ + case SPLIT1: /* 16 bit immediate, 2-byte aligned */ + if (val & 0x1) + as_bad (_("16-bit immediate 2-byte alignment error: 0x%08x"), val); + val &= ~0x1; /* 2-byte align value */ + /*FALLTHROUGH*/ + case SPLIT0: /* st,bla,bte,btne w/16-bit immediate */ + if (fixP->fx_pcrel == 1) + val >>= 2; /* align pcrel offset, see manual */ + /* check for bounds */ + if (highlow != PAIR && (val >= (1 << 16) || val < -(1 << 15))) + as_bad (_("16-bit branch offset overflow: 0x%08x"), val); + buf[1] = (buf[1] & ~0x1f) | ((val >> 11) & 0x1f); + buf[2] = (buf[2] & ~0x7) | ((val >> 8) & 0x7); + buf[3] |= val; /* perserve bottom opcode bits */ + break; + + case LOW4: /* fld,pfld,pst,flush 16-byte aligned */ + if (val & 0xf) + as_bad (_("16-bit immediate 16-byte alignment error: 0x%08x"), val); + val &= ~0xf; /* 16-byte align value */ + /*FALLTHROUGH*/ + case LOW3: /* fld,pfld,pst,flush 8-byte aligned */ + if (val & 0x7) + as_bad (_("16-bit immediate 8-byte alignment error: 0x%08x"), val); + val &= ~0x7; /* 8-byte align value */ + /*FALLTHROUGH*/ + case LOW2: /* 16 bit immediate, 4-byte aligned */ + if (val & 0x3) + as_bad (_("16-bit immediate 4-byte alignment error: 0x%08x"), val); + val &= ~0x3; /* 4-byte align value */ + /*FALLTHROUGH*/ + case LOW1: /* 16 bit immediate, 2-byte aligned */ + if (val & 0x1) + as_bad (_("16-bit immediate 2-byte alignment error: 0x%08x"), val); + val &= ~0x1; /* 2-byte align value */ + /*FALLTHROUGH*/ + case LOW0: /* 16 bit immediate, byte aligned */ + /* check for bounds */ + if (highlow != PAIR && (val >= (1 << 16) || val < -(1 << 15))) + as_bad (_("16-bit immediate overflow: 0x%08x"), val); + buf[2] = val >> 8; + buf[3] |= val; /* perserve bottom opcode bits */ + break; + + case NO_RELOC: + default: + as_bad (_("bad relocation type: 0x%02x"), reloc); + break; + } +} + +/* should never be called for i860 */ +void +md_number_to_disp (buf, val, n) + char *buf; + long val; +{ + as_fatal (_("md_number_to_disp\n")); +} + +/* should never be called for i860 */ +void +md_number_to_field (buf, val, fix) + char *buf; + long val; + void *fix; +{ + as_fatal (_("i860_number_to_field\n")); +} + +/* the bit-field entries in the relocation_info struct plays hell + with the byte-order problems of cross-assembly. So as a hack, + I added this mach. dependent ri twiddler. Ugly, but it gets + you there. -KWK */ +/* on i860: first 4 bytes are normal unsigned long address, next three + bytes are index, most sig. byte first. Byte 7 is broken up with + bit 7 as pcrel, bit 6 as extern, and the lower six bits as + relocation type (highlow 5-4). Next 4 bytes are long addend. */ +/* Thanx and a tip of the hat to Michael Bloom, mb@ttidca.tti.com */ +void +md_ri_to_chars (ri_p, ri) + struct relocation_info *ri_p, ri; +{ +#if 0 + unsigned char the_bytes[sizeof (*ri_p)]; + + /* this is easy */ + md_number_to_chars (the_bytes, ri.r_address, sizeof (ri.r_address)); + /* now the fun stuff */ + the_bytes[4] = (ri.r_index >> 16) & 0x0ff; + the_bytes[5] = (ri.r_index >> 8) & 0x0ff; + the_bytes[6] = ri.r_index & 0x0ff; + the_bytes[7] = ((ri.r_extern << 7) & 0x80) | (0 & 0x60) | (ri.r_type & 0x1F); + /* Also easy */ + md_number_to_chars (&the_bytes[8], ri.r_addend, sizeof (ri.r_addend)); + /* now put it back where you found it, Junior... */ + memcpy ((char *) ri_p, the_bytes, sizeof (*ri_p)); +#endif +} + +/* should never be called for i860 */ +void +md_convert_frag (headers, seg, fragP) + object_headers *headers; + segT seg; + register fragS *fragP; +{ + as_fatal (_("i860_convert_frag\n")); +} + +/* should never be called for i860 */ +int +md_estimate_size_before_relax (fragP, segtype) + register fragS *fragP; + segT segtype; +{ + as_fatal (_("i860_estimate_size_before_relax\n")); +} + +/* for debugging only, must match enum reloc_type */ +static char *Reloc[] = +{ + "NO_RELOC", + "BRADDR", + "LOW0", + "LOW1", + "LOW2", + "LOW3", + "LOW4", + "SPLIT0", + "SPLIT1", + "SPLIT2", + "RELOC_32", +}; +static char *Highlow[] = +{ + "NO_SPEC", + "PAIR", + "HIGH", + "HIGHADJ", +}; +static void +print_insn (insn) + struct i860_it *insn; +{ + if (insn->error) + { + fprintf (stderr, "ERROR: %s\n"); + } + fprintf (stderr, "opcode=0x%08x\t", insn->opcode); + fprintf (stderr, "expand=0x%08x\t", insn->expand); + fprintf (stderr, "reloc = %s\t", Reloc[insn->reloc]); + fprintf (stderr, "highlow = %s\n", Highlow[insn->highlow]); + fprintf (stderr, "exp = {\n"); + fprintf (stderr, "\t\tX_add_symbol = %s\n", + insn->exp.X_add_symbol ? + (S_GET_NAME (insn->exp.X_add_symbol) ? + S_GET_NAME (insn->exp.X_add_symbol) : "???") : "0"); + fprintf (stderr, "\t\tX_op_symbol = %s\n", + insn->exp.X_op_symbol ? + (S_GET_NAME (insn->exp.X_op_symbol) ? + S_GET_NAME (insn->exp.X_op_symbol) : "???") : "0"); + fprintf (stderr, "\t\tX_add_number = %d\n", + insn->exp.X_add_number); + fprintf (stderr, "}\n"); +} + +CONST char *md_shortopts = ""; +struct option md_longopts[] = { + {NULL, no_argument, NULL, 0} +}; +size_t md_longopts_size = sizeof(md_longopts); + +int +md_parse_option (c, arg) + int c; + char *arg; +{ + return 0; +} + +void +md_show_usage (stream) + FILE *stream; +{ +} + +#ifdef comment +/* + * I860 relocations are completely different, so it needs + * this machine dependent routine to emit them. + */ +void +emit_machine_reloc (fixP, segment_address_in_file) + register fixS *fixP; + relax_addressT segment_address_in_file; +{ + struct reloc_info_i860 ri; + register symbolS *symbolP; + extern char *next_object_file_charP; + long add_number; + + memset ((char *) &ri, '\0', sizeof (ri)); + for (; fixP; fixP = fixP->fx_next) + { + + if (fixP->fx_r_type & ~0x3f) + { + as_fatal ("fixP->fx_r_type = %d\n", fixP->fx_r_type); + } + ri.r_pcrel = fixP->fx_pcrel; + ri.r_type = fixP->fx_r_type; + + if ((symbolP = fixP->fx_addsy) != NULL) + { + ri.r_address = fixP->fx_frag->fr_address + + fixP->fx_where - segment_address_in_file; + if (!S_IS_DEFINED (symbolP)) + { + ri.r_extern = 1; + ri.r_symbolnum = symbolP->sy_number; + } + else + { + ri.r_extern = 0; + ri.r_symbolnum = S_GET_TYPE (symbolP); + } + if (symbolP && symbolP->sy_frag) + { + ri.r_addend = symbolP->sy_frag->fr_address; + } + ri.r_type = fixP->fx_r_type; + if (fixP->fx_pcrel) + { + /* preserve actual offset vs. pc + 4 */ + ri.r_addend -= (ri.r_address + 4); + } + else + { + ri.r_addend = fixP->fx_addnumber; + } + + md_ri_to_chars ((char *) &ri, ri); + append (&next_object_file_charP, (char *) &ri, sizeof (ri)); + } + } +} + +#endif /* comment */ + +#ifdef OBJ_AOUT + +/* on i860: first 4 bytes are normal unsigned long address, next three + bytes are index, most sig. byte first. Byte 7 is broken up with + bit 7 as pcrel, bit 6 as extern, and the lower six bits as + relocation type (highlow 5-4). Next 4 bytes are long addend. */ + +void +tc_aout_fix_to_chars (where, fixP, segment_address_in_file) + char *where; + fixS *fixP; + relax_addressT segment_address_in_file; +{ + long r_index; + long r_extern; + long r_addend = 0; + long r_address; + + know (fixP->fx_addsy); + know (!(fixP->fx_r_type & ~0x3f)); + + if (!S_IS_DEFINED (fixP->fx_addsy)) + { + r_extern = 1; + r_index = fixP->fx_addsy->sy_number; + } + else + { + r_extern = 0; + r_index = S_GET_TYPE (fixP->fx_addsy); + } + + md_number_to_chars (where, + r_address = fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file, + 4); + + where[4] = (r_index >> 16) & 0x0ff; + where[5] = (r_index >> 8) & 0x0ff; + where[6] = r_index & 0x0ff; + where[7] = (((fixP->fx_pcrel << 7) & 0x80) + | ((r_extern << 6) & 0x40) + | (fixP->fx_r_type & 0x3F)); + + if (fixP->fx_addsy->sy_frag) + { + r_addend = fixP->fx_addsy->sy_frag->fr_address; + } + + if (fixP->fx_pcrel) + { + /* preserve actual offset vs. pc + 4 */ + r_addend -= (r_address + 4); + } + else + { + r_addend = fixP->fx_addnumber; + } + + md_number_to_chars (&where[8], r_addend, 4); +} + +#endif /* OBJ_AOUT */ + +/* We have no need to default values of symbols. */ + +/* ARGSUSED */ +symbolS * +md_undefined_symbol (name) + char *name; +{ + return 0; +} + +/* Round up a section size to the appropriate boundary. */ +valueT +md_section_align (segment, size) + segT segment; + valueT size; +{ + return size; /* Byte alignment is fine */ +} + +/* Exactly what point is a PC-relative offset relative TO? + On the i860, they're relative to the address of the offset, plus + its size. (??? Is this right? FIXME-SOON!) */ +long +md_pcrel_from (fixP) + fixS *fixP; +{ + return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address; +} + +void +md_apply_fix (fixP, val) + fixS *fixP; + long val; +{ + char *place = fixP->fx_where + fixP->fx_frag->fr_literal; + + /* looks to me like i860 never has bit fixes. Let's see. xoxorich. */ + know (fixP->fx_bit_fixP == NULL); + if (!fixP->fx_bit_fixP) + { + fixP->fx_addnumber = val; + md_number_to_imm (place, val, fixP->fx_size, fixP); + } + else + { + md_number_to_field (place, val, fixP->fx_bit_fixP); + } +} + +/* + * Local Variables: + * fill-column: 131 + * comment-column: 0 + * End: + */ + +/* end of tc-i860.c */ |