diff options
author | Nick Clifton <nickc@redhat.com> | 2002-01-31 17:33:08 +0000 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2002-01-31 17:33:08 +0000 |
commit | 3b16e843f2a75ccf8e7ecc5102e1217a122a05ad (patch) | |
tree | 683e5fc887a3f4f43c06e85a8e1f6c68c0a63f92 /gas/config/tc-or32.c | |
parent | 6d9c411afd0301f0262ff63d6dc59dac38f58e63 (diff) | |
download | ppe42-binutils-3b16e843f2a75ccf8e7ecc5102e1217a122a05ad.tar.gz ppe42-binutils-3b16e843f2a75ccf8e7ecc5102e1217a122a05ad.zip |
Add support for OpenRISC 32-bit embedded processor
Diffstat (limited to 'gas/config/tc-or32.c')
-rw-r--r-- | gas/config/tc-or32.c | 1636 |
1 files changed, 1636 insertions, 0 deletions
diff --git a/gas/config/tc-or32.c b/gas/config/tc-or32.c new file mode 100644 index 0000000000..4f0c2ecdb3 --- /dev/null +++ b/gas/config/tc-or32.c @@ -0,0 +1,1636 @@ +/* Assembly backend for the OpenRISC 1000. + Copyright (C) 2002 Free Software Foundation, Inc. + Contributed by Damjan Lampret <lampret@opencores.org>. + Modified bu Johan Rydberg, <johan.rydberg@netinsight.se>. + Based upon a29k port. + + 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. */ + +/* tc-a29k.c used as a template. */ + +#include "safe-ctype.h" +#include "as.h" +#include "opcode/or32.h" + +#ifdef BFD_ASSEMBLER +#include "elf/or32.h" +#endif + +#define DEBUG 0 + +#ifndef REGISTER_PREFIX +#define REGISTER_PREFIX '%' +#endif + +/* Make it easier to clone this machine desc into another one. */ +#define machine_opcode or32_opcode +#define machine_opcodes or32_opcodes +#define machine_ip or32_ip +#define machine_it or32_it + +/* Handle of the OPCODE hash table. */ +static struct hash_control *op_hash = NULL; + +struct machine_it + { + char * error; + unsigned long opcode; + struct nlist * nlistp; + expressionS exp; + int pcrel; + int reloc_offset; /* Offset of reloc within insn. */ + int reloc; + } +the_insn; + +static void machine_ip PARAMS ((char *)); + +const pseudo_typeS md_pseudo_table[] = + { + {"align", s_align_bytes, 4 }, + {"space", s_space, 0 }, + {"cputype", s_ignore, 0 }, + {"reg", s_lsym, 0 }, /* Register equate, same as equ. */ + {"sect", s_ignore, 0 }, /* Creation of coff sections. */ + {"proc", s_ignore, 0 }, /* Start of a function. */ + {"endproc", s_ignore, 0 }, /* Function end. */ + {"word", cons, 4 }, + {NULL, 0, 0 }, + }; + +int md_short_jump_size = 4; +int md_long_jump_size = 4; + +#if defined(BFD_HEADERS) +#ifdef RELSZ +const int md_reloc_size = RELSZ; /* Coff headers. */ +#else +const int md_reloc_size = 12; /* Something else headers. */ +#endif +#else +const int md_reloc_size = 12; /* Not bfdized. */ +#endif + +/* 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[] = "#"; + +/* 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[] = "#"; + +/* We needed an unused char for line separation to work around the + lack of macros, using sed and such. */ +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"; + +/* "l.jalr r9" precalculated opcode. */ +static unsigned long jalr_r9_opcode; + + +static int check_invalid_opcode PARAMS ((unsigned long)); +static void encode PARAMS ((const struct machine_opcode *, unsigned long *, signed long, char)); +#ifdef BFD_ASSEMBLER +static char * parse_operand PARAMS ((char *, expressionS *, int)); +#endif + +/* Set bits in machine opcode according to insn->encoding + description and passed operand. */ + +static void +encode (insn, opcode, param_val, param_ch) + const struct machine_opcode *insn; + unsigned long *opcode; + signed long param_val; + char param_ch; +{ + int opc_pos = 0; + int param_pos = 0; + char *enc; + +#if DEBUG + printf (" encode: opcode=%.8lx param_val=%.8lx abs=%.8lx param_ch=%c\n", + *opcode, param_val, abs (param_val), param_ch); +#endif + for (enc = insn->encoding; *enc != '\0'; enc++) + if (*enc == param_ch) + { + if (enc - 2 >= insn->encoding && (*(enc - 2) == '0') && (*(enc - 1) == 'x')) + continue; + else + param_pos ++; + } + + opc_pos = 32; + + for (enc = insn->encoding; *enc != '\0';) + { + if ((*enc == '0') && (*(enc + 1) == 'x')) + { + int tmp = strtol (enc, NULL, 16); + + opc_pos -= 4; + *opcode |= tmp << opc_pos; + enc += 3; + } + else if ((*enc == '0') || (*enc == '-')) + { + opc_pos--; + enc++; + } + else if (*enc == '1') + { + opc_pos--; + *opcode |= 1 << opc_pos; + enc++; + } + else if (*enc == param_ch) + { + opc_pos--; + param_pos--; + *opcode |= ((param_val >> param_pos) & 0x1) << opc_pos; + enc++; + } + else if (ISALPHA (*enc)) + { + opc_pos--; + enc++; + } + else + enc++; + } + +#if DEBUG + printf (" opcode=%.8lx\n", *opcode); +#endif +} + +/* 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 () +{ + const char *retval = NULL; + int lose = 0; + int skipnext = 0; + unsigned int i; + + /* Hash up all the opcodes for fast use later. */ + op_hash = hash_new (); + + for (i = 0; i < or32_num_opcodes; i++) + { + const char *name = machine_opcodes[i].name; + + if (skipnext) + { + skipnext = 0; + continue; + } + + retval = hash_insert (op_hash, name, (PTR) &machine_opcodes[i]); + if (retval != NULL) + { + fprintf (stderr, "internal error: can't hash `%s': %s\n", + machine_opcodes[i].name, retval); + lose = 1; + } + } + + if (lose) + as_fatal (_("Broken assembler. No assembly attempted.")); + + encode (&machine_opcodes[insn_index ("l.jalr")], &jalr_r9_opcode, 9, 'B'); +} + +/* Returs non zero if instruction is to be used. */ + +static int +check_invalid_opcode (opcode) + unsigned long opcode; +{ + return opcode == jalr_r9_opcode; +} + +/* Assemble a single instruction. Its label has already been handled + by the generic front end. We just parse opcode and operands, and + produce the bytes of data and relocation. */ + +void +md_assemble (str) + char *str; +{ + char *toP; + +#if DEBUG + printf ("NEW INSTRUCTION\n"); +#endif + + know (str); + machine_ip (str); + toP = frag_more (4); + + /* Put out the opcode. */ + md_number_to_chars (toP, the_insn.opcode, 4); + + /* Put out the symbol-dependent stuff. */ +#ifdef BFD_ASSEMBLER + if (the_insn.reloc != BFD_RELOC_NONE) +#else + if (the_insn.reloc != NO_RELOC) +#endif + { + fix_new_exp (frag_now, + (toP - frag_now->fr_literal + the_insn.reloc_offset), + 4, /* size */ + &the_insn.exp, + the_insn.pcrel, + the_insn.reloc); + } +} + +/* This is true of the we have issued a "lo(" or "hi"(. */ +static int waiting_for_shift = 0; + +static int mask_or_shift = 0; + +#ifdef BFD_ASSEMBLER +static char * +parse_operand (s, operandp, opt) + char *s; + expressionS *operandp; + int opt; +{ + char *save = input_line_pointer; + char *new; + +#if DEBUG + printf (" PROCESS NEW OPERAND(%s) == %c (%d)\n", s, opt ? opt : '!', opt); +#endif + + input_line_pointer = s; + + if (strncasecmp (s, "HI(", 3) == 0) + { + waiting_for_shift = 1; + mask_or_shift = BFD_RELOC_HI16; + + input_line_pointer += 3; + } + else if (strncasecmp (s, "LO(", 3) == 0) + { + mask_or_shift = BFD_RELOC_LO16; + + input_line_pointer += 3; + } + else + mask_or_shift = 0; + + if ((*s == '(') && (*(s+1) == 'r')) + s++; + + if ((*s == 'r') && ISDIGIT (*(s + 1))) + { + operandp->X_add_number = strtol (s + 1, NULL, 10); + operandp->X_op = O_register; + for (; (*s != ',') && (*s != '\0');) + s++; + input_line_pointer = save; + return s; + } + + expression (operandp); + + if (operandp->X_op == O_absent) + { + if (! opt) + as_bad (_("missing operand")); + else + { + operandp->X_add_number = 0; + operandp->X_op = O_constant; + } + } + + new = input_line_pointer; + input_line_pointer = save; + +#if DEBUG + printf (" %s=parse_operand(%s): operandp->X_op = %u\n", new, s, operandp->X_op); +#endif + + return new; +} +#else + +char * +parse_operand (s, operandp, opt) + char *s; + expressionS *operandp; + int opt; +{ + char *save = input_line_pointer; + char *new; + +#if DEBUG + printf (" PROCESS NEW OPERAND(%s) == %c (%d)\n", s, opt ? opt : '!', opt); +#endif + + input_line_pointer = s; + + if (strncasecmp (s, "HI(", 3) == 0) + { + waiting_for_shift = 1; + mask_or_shift = RELOC_CONSTH; + + input_line_pointer += 3; + } + else if (strncasecmp (s, "LO(", 3) == 0) + { + mask_or_shift = RELOC_CONST; + + input_line_pointer += 3; + } + else + mask_or_shift = 0; + + + expression (operandp); + + if (operandp->X_op == O_absent) + { + if (! opt) + as_bad (_("missing operand")); + else + { + operandp->X_add_number = 0; + operandp->X_op = O_constant; + } + } + + new = input_line_pointer; + input_line_pointer = save; + + if ((operandp->X_op == O_symbol) && (*s != '_')) + { +#if DEBUG + printf ("symbol: '%s'\n", save); +#endif + + for (save = s; s < new; s++) + if ((*s == REGISTER_PREFIX) && (*(s + 1) == 'r')) /* Register prefix. */ + s++; + + if ((*s == 'r') && ISDIGIT (*(s + 1))) + { + operandp->X_add_number = strtol (s + 1, NULL, 10); + operandp->X_op = O_register; + } + s = save; + } + +#if DEBUG + printf (" %s=parse_operand(%s): operandp->X_op = %u\n", new, s, operandp->X_op); +#endif + + return new; +} +#endif + +/* Instruction parsing. Takes a string containing the opcode. + Operands are at input_line_pointer. Output is in the_insn. + Warnings or errors are generated. */ + +#ifdef BFD_ASSEMBLER +static void +machine_ip (str) + char *str; +{ + char *s; + const char *args; + const struct machine_opcode *insn; + char *argsStart; + unsigned long opcode; + expressionS the_operand; + expressionS *operand = &the_operand; + unsigned int regno; + int reloc = BFD_RELOC_NONE; + +#if DEBUG + printf ("machine_ip(%s)\n", str); +#endif + + s = str; + for (; ISALNUM (*s) || *s == '.'; ++s) + if (ISUPPER (*s)) + *s = TOLOWER (*s); + + switch (*s) + { + case '\0': + break; + + case ' ': /* FIXME-SOMEDAY more whitespace. */ + *s++ = '\0'; + break; + + default: + as_bad (_("unknown opcode1: `%s'"), str); + return; + } + + if ((insn = (struct machine_opcode *) hash_find (op_hash, str)) == NULL) + { + as_bad (_("unknown opcode2 `%s'."), str); + return; + } + + argsStart = s; + opcode = 0; + memset (&the_insn, '\0', sizeof (the_insn)); + the_insn.reloc = BFD_RELOC_NONE; + + reloc = BFD_RELOC_NONE; + + /* Build the opcode, checking as we go to make sure that the + operands match. + + If an operand matches, we modify the_insn or opcode appropriately, + and do a "continue". If an operand fails to match, we "break". */ + if (insn->args[0] != '\0') + { + /* Prime the pump. */ + s = parse_operand (s, operand, insn->args[0] == 'I'); + } + + for (args = insn->args;; ++args) + { +#if DEBUG + printf (" args = %s\n", args); +#endif + switch (*args) + { + case '\0': /* End of args. */ + /* We have have 0 args, do the bazoooka! */ + if (args == insn->args) + encode (insn, &opcode, 0, 0); + + if (*s == '\0') + { + /* We are truly done. */ + the_insn.opcode = opcode; + if (check_invalid_opcode (opcode)) + as_bad (_("instruction not allowed: %s"), str); + return; + } + as_bad (_("too many operands: %s"), s); + break; + + case ',': /* Must match a comma. */ + if (*s++ == ',') + { + reloc = BFD_RELOC_NONE; + + /* Parse next operand. */ + s = parse_operand (s, operand, args[1] == 'I'); +#if DEBUG + printf (" ',' case: operand->X_add_number = %d, *args = %s, *s = %s\n", + operand->X_add_number, args, s); +#endif + continue; + } + break; + + case '(': /* Must match a (. */ + s = parse_operand (s, operand, args[1] == 'I'); + continue; + + case ')': /* Must match a ). */ + continue; + + case 'r': /* A general register. */ + args++; + + if (operand->X_op != O_register) + break; /* Only registers. */ + + know (operand->X_add_symbol == 0); + know (operand->X_op_symbol == 0); + regno = operand->X_add_number; + encode (insn, &opcode, regno, *args); +#if DEBUG + printf (" r: operand->X_op = %d\n", operand->X_op); +#endif + continue; + + default: + /* if (! ISALPHA (*args)) + break; */ /* Only immediate values. */ + + if (mask_or_shift) + { +#if DEBUG + printf ("mask_or_shift = %d\n", mask_or_shift); +#endif + reloc = mask_or_shift; + } + mask_or_shift = 0; + + if (strncasecmp (args, "LO(", 3) == 0) + { +#if DEBUG + printf ("reloc_const\n"); +#endif + reloc = BFD_RELOC_LO16; + } + else if (strncasecmp (args, "HI(", 3) == 0) + { +#if DEBUG + printf ("reloc_consth\n"); +#endif + reloc = BFD_RELOC_HI16; + } + + if (*s == '(') + { + operand->X_op = O_constant; +#if 0 + operand->X_add_number = 0; /* ??? if enabled load/store offsets + are zero. */ +#endif + } + else if (*s == ')') + s += 1; +#if DEBUG + printf (" default case: operand->X_add_number = %d, *args = %s, *s = %s\n", operand->X_add_number, args, s); +#endif + if (operand->X_op == O_constant) + { + if (reloc == BFD_RELOC_NONE) + { + bfd_vma v, mask; + + mask = 0x3ffffff; + v = abs (operand->X_add_number) & ~ mask; + if (v) + as_bad (_("call/jmp target out of range (1)")); + } + + if (reloc == BFD_RELOC_HI16) + operand->X_add_number = ((operand->X_add_number >> 16) & 0xffff); + + the_insn.pcrel = 0; + encode (insn, &opcode, operand->X_add_number, *args); + /* the_insn.reloc = BFD_RELOC_NONE; */ + continue; + } + + if (reloc == BFD_RELOC_NONE) + the_insn.reloc = BFD_RELOC_32_GOT_PCREL; + else + the_insn.reloc = reloc; + + /* the_insn.reloc = insn->reloc; */ +#if DEBUG + printf (" reloc sym=%d\n", the_insn.reloc); + printf (" BFD_RELOC_NONE=%d\n", BFD_RELOC_NONE); +#endif + the_insn.exp = *operand; + + /* the_insn.reloc_offset = 1; */ + the_insn.pcrel = 1; /* Assume PC-relative jump. */ + + /* FIXME-SOON, Do we figure out whether abs later, after + know sym val? */ + if (reloc == BFD_RELOC_LO16 || reloc == BFD_RELOC_HI16) + the_insn.pcrel = 0; + + encode (insn, &opcode, operand->X_add_number, *args); + continue; + } + + /* Types or values of args don't match. */ + as_bad (_("invalid operands")); + return; + } +} + +#else + +static void +machine_ip (str) + char *str; +{ + char *s; + const char *args; + const struct machine_opcode *insn; + char *argsStart; + unsigned long opcode; + expressionS the_operand; + expressionS *operand = &the_operand; + unsigned int regno; + int reloc = NO_RELOC; + +#if DEBUG + printf ("machine_ip(%s)\n", str); +#endif + + s = str; + for (; ISALNUM (*s) || *s == '.'; ++s) + if (ISUPPER (*s)) + *s = TOLOWER (*s); + + switch (*s) + { + case '\0': + break; + + case ' ': /* FIXME-SOMEDAY more whitespace. */ + *s++ = '\0'; + break; + + default: + as_bad (_("unknown opcode1: `%s'"), str); + return; + } + + if ((insn = (struct machine_opcode *) hash_find (op_hash, str)) == NULL) + { + as_bad (_("unknown opcode2 `%s'."), str); + return; + } + + argsStart = s; + opcode = 0; + memset (&the_insn, '\0', sizeof (the_insn)); + the_insn.reloc = NO_RELOC; + + reloc = NO_RELOC; + + /* Build the opcode, checking as we go to make sure that the + operands match. + + If an operand matches, we modify the_insn or opcode appropriately, + and do a "continue". If an operand fails to match, we "break". */ + if (insn->args[0] != '\0') + /* Prime the pump. */ + s = parse_operand (s, operand, + insn->args[0] == 'I' + || strcmp (insn->name, "l.nop") == 0); + + for (args = insn->args;; ++args) + { +#if DEBUG + printf (" args = %s\n", args); +#endif + switch (*args) + { + case '\0': /* End of args. */ + /* We have have 0 args, do the bazoooka! */ + if (args == insn->args) + encode (insn, &opcode, 0, 0); + + if (*s == '\0') + { + /* We are truly done. */ + the_insn.opcode = opcode; + if (check_invalid_opcode (opcode)) + as_bad (_("instruction not allowed: %s"), str); + return; + } + as_bad (_("too many operands: %s"), s); + break; + + case ',': /* Must match a comma. */ + if (*s++ == ',') + { + reloc = NO_RELOC; + + /* Parse next operand. */ + s = parse_operand (s, operand, args[1] == 'I'); +#if DEBUG + printf (" ',' case: operand->X_add_number = %d, *args = %s, *s = %s\n", + operand->X_add_number, args, s); +#endif + continue; + } + break; + + case '(': /* Must match a (. */ + s = parse_operand (s, operand, args[1] == 'I'); + continue; + + case ')': /* Must match a ). */ + continue; + + case 'r': /* A general register. */ + args++; + + if (operand->X_op != O_register) + break; /* Only registers. */ + + know (operand->X_add_symbol == 0); + know (operand->X_op_symbol == 0); + regno = operand->X_add_number; + encode (insn, &opcode, regno, *args); +#if DEBUG + printf (" r: operand->X_op = %d\n", operand->X_op); +#endif + continue; + + default: + /* if (! ISALPHA (*args)) + break; */ /* Only immediate values. */ + + if (mask_or_shift) + { +#if DEBUG + printf ("mask_or_shift = %d\n", mask_or_shift); +#endif + reloc = mask_or_shift; + } + mask_or_shift = 0; + + if (strncasecmp (args, "LO(", 3) == 0) + { +#if DEBUG + printf ("reloc_const\n"); +#endif + reloc = RELOC_CONST; + } + else if (strncasecmp (args, "HI(", 3) == 0) + { +#if DEBUG + printf ("reloc_consth\n"); +#endif + reloc = RELOC_CONSTH; + } + + if (*s == '(') + { + operand->X_op = O_constant; +#if 0 + operand->X_add_number = 0; /* ??? if enabled load/store offsets + are zero. */ +#endif + } + else if (*s == ')') + s += 1; +#if DEBUG + printf (" default case: operand->X_add_number = %d, *args = %s, *s = %s\n", + operand->X_add_number, args, s); +#endif + if (operand->X_op == O_constant) + { + if (reloc == NO_RELOC) + { + unsigned long v, mask; + + mask = 0x3ffffff; + v = abs (operand->X_add_number) & ~ mask; + if (v) + as_bad (_("call/jmp target out of range (1)")); + } + + if (reloc == RELOC_CONSTH) + operand->X_add_number = ((operand->X_add_number>>16) & 0xffff); + + the_insn.pcrel = 0; + encode (insn, &opcode, operand->X_add_number, *args); + /* the_insn.reloc = NO_RELOC; */ + continue; + } + + if (reloc == NO_RELOC) + the_insn.reloc = RELOC_JUMPTARG; + else + the_insn.reloc = reloc; +#if DEBUG + printf (" reloc sym=%d\n", the_insn.reloc); + printf (" NO_RELOC=%d\n", NO_RELOC); +#endif + the_insn.exp = *operand; + + /* the_insn.reloc_offset = 1; */ + the_insn.pcrel = 1; /* Assume PC-relative jump. */ + + /* FIXME-SOON, Do we figure out whether abs later, after + know sym val? */ + if (reloc == RELOC_CONST || reloc == RELOC_CONSTH) + the_insn.pcrel = 0; + + encode (insn, &opcode, operand->X_add_number, *args); + continue; + } + + /* Types or values of args don't match. */ + as_bad (_("invalid operands")); + return; + } +} +#endif + +/* 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; + + 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, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE)); + litP += sizeof (LITTLENUM_TYPE); + } + + return NULL; +} + +/* 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); +} + +#ifdef BFD_ASSEMBLER +void +md_apply_fix3 (fixP, val, seg) + fixS * fixP; + valueT * val; + segT seg ATTRIBUTE_UNUSED; +{ + char *buf = fixP->fx_where + fixP->fx_frag->fr_literal; + long t_val; + + t_val = (long) *val; + +#if DEBUG + printf ("md_apply_fix val:%x\n", t_val); +#endif + + fixP->fx_addnumber = t_val; /* Remember value for emit_reloc. */ + + know (fixP->fx_size == 4); + know (fixP->fx_r_type < BFD_RELOC_NONE); + + switch (fixP->fx_r_type) + { + case BFD_RELOC_32: /* XXXXXXXX pattern in a word. */ +#if DEBUG + printf ("reloc_const: val=%x\n", t_val); +#endif + buf[0] = t_val >> 24; + buf[1] = t_val >> 16; + buf[2] = t_val >> 8; + buf[3] = t_val; + break; + + case BFD_RELOC_16: /* XXXX0000 pattern in a word. */ +#if DEBUG + printf ("reloc_const: val=%x\n", t_val); +#endif + buf[0] = t_val >> 8; + buf[1] = t_val; + break; + + case BFD_RELOC_8: /* XX000000 pattern in a word. */ +#if DEBUG + printf ("reloc_const: val=%x\n", t_val); +#endif + buf[0] = t_val; + break; + + case BFD_RELOC_LO16: /* 0000XXXX pattern in a word. */ +#if DEBUG + printf ("reloc_const: val=%x\n", t_val); +#endif + buf[2] = t_val >> 8; /* Holds bits 0000XXXX. */ + buf[3] = t_val; + break; + + case BFD_RELOC_HI16: /* 0000XXXX pattern in a word. */ +#if DEBUG + printf ("reloc_consth: val=%x\n", t_val); +#endif + buf[2] = t_val >> 24; /* Holds bits XXXX0000. */ + buf[3] = t_val >> 16; + break; + + case BFD_RELOC_32_GOT_PCREL: /* 0000XXXX pattern in a word. */ + if (!fixP->fx_done) + { + /* The linker tries to support both AMD and old GNU style + R_IREL relocs. That means that if the addend is exactly + the negative of the address within the section, the + linker will not handle it correctly. */ +#if 0 + if (fixP->fx_pcrel + && t_val != 0 + && t_val == - (fixP->fx_frag->fr_address + fixP->fx_where)) + as_bad_where + (fixP->fx_file, fixP->fx_line, + _("the linker will not handle this relocation correctly (1)")); +#endif + } + else if (fixP->fx_pcrel) + { + long v = t_val >> 28; + + if (v != 0 && v != -1) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("call/jmp target out of range (2)")); + } + else + /* This case was supposed to be handled in machine_ip. */ + abort (); + + buf[0] |= (t_val >> 26) & 0x03; /* Holds bits 0FFFFFFC of address. */ + buf[1] = t_val >> 18; + buf[2] = t_val >> 10; + buf[3] = t_val >> 2; + break; + + case BFD_RELOC_VTABLE_INHERIT: + case BFD_RELOC_VTABLE_ENTRY: + fixP->fx_done = 0; + break; + + case BFD_RELOC_NONE: + default: + as_bad (_("bad relocation type: 0x%02x"), fixP->fx_r_type); + break; + } + + if (fixP->fx_addsy == (symbolS *) NULL) + fixP->fx_done = 1; +} +#else +void +md_apply_fix3 (fixP, valP, seg) + fixS *fixP; + valueT *valP; + segT seg ATTRIBUTE_UNUSED; +{ + long val = *(long*)valP; + char *buf = fixP->fx_where + fixP->fx_frag->fr_literal; + +#if DEBUG + printf ("md_apply_fix val:%x\n", val); +#endif + + fixP->fx_addnumber = val; /* Remember value for emit_reloc. */ + + know (fixP->fx_size == 4); + know (fixP->fx_r_type < NO_RELOC); + + /* This is a hack. There should be a better way to handle this. */ + if (fixP->fx_r_type == RELOC_WDISP30 && fixP->fx_addsy) + val += fixP->fx_where + fixP->fx_frag->fr_address; + + switch (fixP->fx_r_type) + { + case RELOC_32: + buf[0] = val >> 24; + buf[1] = val >> 16; + buf[2] = val >> 8; + buf[3] = val; + break; + + case RELOC_8: + buf[0] = val; + break; + + case RELOC_WDISP30: + val = (val >>= 2) + 1; + buf[0] |= (val >> 24) & 0x3f; + buf[1] = (val >> 16); + buf[2] = val >> 8; + buf[3] = val; + break; + + case RELOC_HI22: + buf[1] |= (val >> 26) & 0x3f; + buf[2] = val >> 18; + buf[3] = val >> 10; + break; + + case RELOC_LO10: + buf[2] |= (val >> 8) & 0x03; + buf[3] = val; + break; + + case RELOC_BASE13: + buf[2] |= (val >> 8) & 0x1f; + buf[3] = val; + break; + + case RELOC_WDISP22: + val = (val >>= 2) + 1; + /* FALLTHROUGH */ + case RELOC_BASE22: + buf[1] |= (val >> 16) & 0x3f; + buf[2] = val >> 8; + buf[3] = val; + break; + + case RELOC_JUMPTARG: /* 0000XXXX pattern in a word. */ + if (!fixP->fx_done) + { + /* The linker tries to support both AMD and old GNU style + R_IREL relocs. That means that if the addend is exactly + the negative of the address within the section, the + linker will not handle it correctly. */ +#if 0 + if (fixP->fx_pcrel + && val != 0 + && val == - (fixP->fx_frag->fr_address + fixP->fx_where)) + as_bad_where + (fixP->fx_file, fixP->fx_line, + _("the linker will not handle this relocation correctly (1)")); +#endif + } + else if (fixP->fx_pcrel) + { + long v = val >> 28; +#if 1 + if (v != 0 && v != -1) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("call/jmp target out of range (2)")); +#endif + } + else + /* This case was supposed to be handled in machine_ip. */ + abort (); + + buf[0] |= (val >> 26) & 0x03; /* Holds bits 0FFFFFFC of address. */ + buf[1] = val >> 18; + buf[2] = val >> 10; + buf[3] = val >> 2; + break; + + case RELOC_CONST: /* 0000XXXX pattern in a word. */ +#if DEBUG + printf ("reloc_const: val=%x\n", val); +#endif + buf[2] = val >> 8; /* Holds bits 0000XXXX. */ + buf[3] = val; + break; + + case RELOC_CONSTH: /* 0000XXXX pattern in a word. */ +#if DEBUG + printf ("reloc_consth: val=%x\n", val); +#endif + buf[2] = val >> 24; /* Holds bits XXXX0000. */ + buf[3] = val >> 16; + break; + + case BFD_RELOC_VTABLE_INHERIT: + case BFD_RELOC_VTABLE_ENTRY: + fixP->fx_done = 0; + break; + + case NO_RELOC: + default: + as_bad (_("bad relocation type: 0x%02x"), fixP->fx_r_type); + break; + } + + if (fixP->fx_addsy == (symbolS *) NULL) + fixP->fx_done = 1; +} +#endif + +#ifdef OBJ_COFF +short +tc_coff_fix2rtype (fixP) + fixS *fixP; +{ +#if DEBUG + printf ("tc_coff_fix2rtype\n"); +#endif + + switch (fixP->fx_r_type) + { + case RELOC_32: + return (R_WORD); + case RELOC_8: + return (R_BYTE); + case RELOC_CONST: + return (R_ILOHALF); + case RELOC_CONSTH: + return (R_IHIHALF); + case RELOC_JUMPTARG: + return (R_IREL); + default: + printf ("need %d\n", fixP->fx_r_type); + abort (); + } + + return 0; +} + +#endif /* OBJ_COFF */ + +/* Should never be called for or32. */ + +void +md_create_short_jump (ptr, from_addr, to_addr, frag, to_symbol) + char * ptr ATTRIBUTE_UNUSED; + addressT from_addr ATTRIBUTE_UNUSED; + addressT to_addr ATTRIBUTE_UNUSED; + fragS * frag ATTRIBUTE_UNUSED; + symbolS * to_symbol ATTRIBUTE_UNUSED; +{ + as_fatal ("or32_create_short_jmp\n"); +} + +/* Should never be called for or32. */ + +#ifndef BFD_ASSEMBLER +void +md_convert_frag (headers, seg, fragP) + object_headers * headers ATTRIBUTE_UNUSED; + segT seg ATTRIBUTE_UNUSED; + register fragS * fragP ATTRIBUTE_UNUSED; +{ + as_fatal ("or32_convert_frag\n"); +} + +#else +void +md_convert_frag (headers, seg, fragP) + bfd * headers ATTRIBUTE_UNUSED; + segT seg ATTRIBUTE_UNUSED; + fragS * fragP ATTRIBUTE_UNUSED; +{ + as_fatal ("or32_convert_frag\n"); +} +#endif + +/* Should never be called for or32. */ + +void +md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol) + char * ptr ATTRIBUTE_UNUSED; + addressT from_addr ATTRIBUTE_UNUSED; + addressT to_addr ATTRIBUTE_UNUSED; + fragS * frag ATTRIBUTE_UNUSED; + symbolS * to_symbol ATTRIBUTE_UNUSED; +{ + as_fatal ("or32_create_long_jump\n"); +} + +/* Should never be called for or32. */ + +int +md_estimate_size_before_relax (fragP, segtype) + fragS * fragP ATTRIBUTE_UNUSED; + segT segtype ATTRIBUTE_UNUSED; +{ + as_fatal ("or32_estimate_size_before_relax\n"); + return 0; +} + +/* Translate internal representation of relocation info to target format. + + On sparc/29k: 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 external, bits 6 & 5 unused, and the lower + five bits as relocation type. Next 4 bytes are long addend. */ +/* Thanx and a tip of the hat to Michael Bloom, mb@ttidca.tti.com. */ + +#ifdef OBJ_AOUT +void +tc_aout_fix_to_chars (where, fixP, segment_address_in_file) + char *where; + fixS *fixP; + relax_addressT segment_address_in_file; +{ + long r_symbolnum; + +#if DEBUG + printf ("tc_aout_fix_to_chars\n"); +#endif + + know (fixP->fx_r_type < BFD_RELOC_NONE); + know (fixP->fx_addsy != NULL); + + md_number_to_chars + (where, + fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file, + 4); + + r_symbolnum = (S_IS_DEFINED (fixP->fx_addsy) + ? S_GET_TYPE (fixP->fx_addsy) + : fixP->fx_addsy->sy_number); + + where[4] = (r_symbolnum >> 16) & 0x0ff; + where[5] = (r_symbolnum >> 8) & 0x0ff; + where[6] = r_symbolnum & 0x0ff; + where[7] = (((!S_IS_DEFINED (fixP->fx_addsy)) << 7) & 0x80) | (0 & 0x60) | (fixP->fx_r_type & 0x1F); + + /* Also easy. */ + md_number_to_chars (&where[8], fixP->fx_addnumber, 4); +} + +#endif /* OBJ_AOUT */ + +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 ATTRIBUTE_UNUSED; + char * arg ATTRIBUTE_UNUSED; +{ + return 0; +} + +void +md_show_usage (stream) + FILE * stream ATTRIBUTE_UNUSED; +{ +} + +/* This is called when a line is unrecognized. This is used to handle + definitions of or32 style local labels. */ + +int +or32_unrecognized_line (c) + int c; +{ + int lab; + char *s; + + if (c != '$' + || ! ISDIGIT ((unsigned char) input_line_pointer[0])) + return 0; + + s = input_line_pointer; + + lab = 0; + while (ISDIGIT ((unsigned char) *s)) + { + lab = lab * 10 + *s - '0'; + ++s; + } + + if (*s != ':') + /* Not a label definition. */ + return 0; + + if (dollar_label_defined (lab)) + { + as_bad (_("label \"$%d\" redefined"), lab); + return 0; + } + + define_dollar_label (lab); + colon (dollar_label_name (lab, 0)); + input_line_pointer = s + 1; + + return 1; +} + +#ifndef BFD_ASSEMBLER +/* Record a fixup for a cons expression. */ +/* + void +or32_cons_fix_new (frag, where, nbytes, exp) + fragS *frag; + int where; + int nbytes; + expressionS *exp; +{ + fix_new_exp (frag, where, nbytes, exp, 0, + nbytes == 5 ? RELOC_32 + : nbytes == 2 ? RELOC_16 + : RELOC_8); +} +void +tc_aout_pre_write_hook () +{ +#if DEBUG + printf ("In tc_aout_pre_write_hook()\n"); +#endif +} +*/ +#endif + +/* Default the values of symbols known that should be "predefined". We + don't bother to predefine them unless you actually use one, since there + are a lot of them. */ + +symbolS * +md_undefined_symbol (name) + char *name ATTRIBUTE_UNUSED; +{ +#ifndef BFD_ASSEMBLER + long regnum; + char testbuf[5 + /*SLOP*/ 5]; + +#if DEBUG + printf ("md_undefined_symbol(%s)\n", name); +#endif + + /* Register name. */ + if (name[0] == 'r' || name[0] == 'R' || name[0] == 'a' || name[0] == 'b') + { + long maxreg; + + /* Parse the number, make sure it has no extra zeroes or + trailing chars. */ + regnum = atol (& name[1]); + + if (regnum > 31) + as_fatal (_("register out of range")); + + sprintf (testbuf, "%ld", regnum); + + if (strcmp (testbuf, &name[1]) != 0) + return NULL; /* gr007 or lr7foo or whatever. */ + + /* We have a wiener! Define and return a new symbol for it. */ + return (symbol_new (name, SEG_REGISTER, (valueT) regnum, + &zero_address_frag)); + } +#endif + return NULL; +} + +/* Parse an operand that is machine-specific. */ + +void +md_operand (expressionP) + expressionS *expressionP; +{ +#if DEBUG + printf (" md_operand(input_line_pointer = %s)\n", input_line_pointer); +#endif + + if (input_line_pointer[0] == REGISTER_PREFIX && input_line_pointer[1] == 'r') + { + /* We have a numeric register expression. No biggy. */ + input_line_pointer += 2; /* Skip %r */ + (void) expression (expressionP); + + if (expressionP->X_op != O_constant + || expressionP->X_add_number > 255) + as_bad (_("Invalid expression after %%%%\n")); + expressionP->X_op = O_register; + } + else if (input_line_pointer[0] == '&') + { + /* We are taking the 'address' of a register...this one is not + in the manual, but it *is* in traps/fpsymbol.h! What they + seem to want is the register number, as an absolute number. */ + input_line_pointer++; /* Skip & */ + (void) expression (expressionP); + + if (expressionP->X_op != O_register) + as_bad (_("invalid register in & expression")); + else + expressionP->X_op = O_constant; + } + else if (input_line_pointer[0] == '$' + && ISDIGIT ((unsigned char) input_line_pointer[1])) + { + long lab; + char *name; + symbolS *sym; + + /* This is a local label. */ + ++input_line_pointer; + lab = (long) get_absolute_expression (); + + if (dollar_label_defined (lab)) + { + name = dollar_label_name (lab, 0); + sym = symbol_find (name); + } + else + { + name = dollar_label_name (lab, 1); + sym = symbol_find_or_make (name); + } + + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = sym; + expressionP->X_add_number = 0; + } + else if (input_line_pointer[0] == '$') + { + char *s; + char type; + int fieldnum, fieldlimit; + LITTLENUM_TYPE floatbuf[8]; + + /* $float(), $doubleN(), or $extendN() convert floating values + to integers. */ + s = input_line_pointer; + + ++s; + + fieldnum = 0; + if (strncmp (s, "double", sizeof "double" - 1) == 0) + { + s += sizeof "double" - 1; + type = 'd'; + fieldlimit = 2; + } + else if (strncmp (s, "float", sizeof "float" - 1) == 0) + { + s += sizeof "float" - 1; + type = 'f'; + fieldlimit = 1; + } + else if (strncmp (s, "extend", sizeof "extend" - 1) == 0) + { + s += sizeof "extend" - 1; + type = 'x'; + fieldlimit = 4; + } + else + return; + + if (ISDIGIT (*s)) + { + fieldnum = *s - '0'; + ++s; + } + if (fieldnum >= fieldlimit) + return; + + SKIP_WHITESPACE (); + if (*s != '(') + return; + ++s; + SKIP_WHITESPACE (); + + s = atof_ieee (s, type, floatbuf); + if (s == NULL) + return; + s = s; + + SKIP_WHITESPACE (); + if (*s != ')') + return; + ++s; + SKIP_WHITESPACE (); + + input_line_pointer = s; + expressionP->X_op = O_constant; + expressionP->X_unsigned = 1; + expressionP->X_add_number = ((floatbuf[fieldnum * 2] + << LITTLENUM_NUMBER_OF_BITS) + + floatbuf[fieldnum * 2 + 1]); + } +} + +/* Round up a section size to the appropriate boundary. */ + +valueT +md_section_align (segment, size) + segT segment ATTRIBUTE_UNUSED; + valueT size ATTRIBUTE_UNUSED; +{ + return size; /* Byte alignment is fine. */ +} + +/* Exactly what point is a PC-relative offset relative TO? + On the 29000, they're relative to the address of the instruction, + which we have set up as the address of the fixup too. */ + +long +md_pcrel_from (fixP) + fixS *fixP; +{ + return fixP->fx_where + fixP->fx_frag->fr_address; +} + +/* Generate a reloc for a fixup. */ + +#ifdef BFD_ASSEMBLER +arelent * +tc_gen_reloc (seg, fixp) + asection *seg ATTRIBUTE_UNUSED; + fixS *fixp; +{ + arelent *reloc; + + reloc = (arelent *) xmalloc (sizeof (arelent)); + reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; + /* reloc->address = fixp->fx_frag->fr_address + fixp->fx_where + fixp->fx_addnumber;*/ + reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type); + + if (reloc->howto == (reloc_howto_type *) NULL) + { + as_bad_where (fixp->fx_file, fixp->fx_line, + _("reloc %d not supported by object file format"), + (int) fixp->fx_r_type); + return NULL; + } + + if ( fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY + || fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT) + reloc->addend = fixp->fx_offset; + else + reloc->addend = fixp->fx_addnumber; + + return reloc; +} +#endif + |