summaryrefslogtreecommitdiffstats
path: root/binutils-2.25/gas/config/tc-dlx.c
diff options
context:
space:
mode:
Diffstat (limited to 'binutils-2.25/gas/config/tc-dlx.c')
-rw-r--r--binutils-2.25/gas/config/tc-dlx.c1243
1 files changed, 1243 insertions, 0 deletions
diff --git a/binutils-2.25/gas/config/tc-dlx.c b/binutils-2.25/gas/config/tc-dlx.c
new file mode 100644
index 00000000..a629533b
--- /dev/null
+++ b/binutils-2.25/gas/config/tc-dlx.c
@@ -0,0 +1,1243 @@
+/* tc-dlx.c -- Assemble for the DLX
+ Copyright 2002, 2003, 2004, 2005, 2007, 2009, 2010, 2012
+ 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 3, 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, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* Initially created by Kuang Hwa Lin, 3/20/2002. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "tc-dlx.h"
+#include "opcode/dlx.h"
+
+/* Make it easier to clone this machine desc into another one. */
+#define machine_opcode dlx_opcode
+#define machine_opcodes dlx_opcodes
+#define machine_ip dlx_ip
+#define machine_it dlx_it
+
+#define NO_RELOC BFD_RELOC_NONE
+#define RELOC_DLX_REL26 BFD_RELOC_DLX_JMP26
+#define RELOC_DLX_16 BFD_RELOC_16
+#define RELOC_DLX_REL16 BFD_RELOC_16_PCREL_S2
+#define RELOC_DLX_HI16 BFD_RELOC_HI16_S
+#define RELOC_DLX_LO16 BFD_RELOC_LO16
+#define RELOC_DLX_VTINHERIT BFD_RELOC_VTABLE_INHERIT
+#define RELOC_DLX_VTENTRY BFD_RELOC_VTABLE_ENTRY
+
+/* 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 size;
+ int reloc_offset; /* Offset of reloc within insn. */
+ int reloc;
+ int HI;
+ int LO;
+}
+the_insn;
+
+/* 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";
+
+static void
+insert_sreg (char *regname, int regnum)
+{
+ /* Must be large enough to hold the names of the special registers. */
+ char buf[80];
+ int i;
+
+ symbol_table_insert (symbol_new (regname, reg_section, (valueT) regnum,
+ &zero_address_frag));
+ for (i = 0; regname[i]; i++)
+ buf[i] = ISLOWER (regname[i]) ? TOUPPER (regname[i]) : regname[i];
+ buf[i] = '\0';
+
+ symbol_table_insert (symbol_new (buf, reg_section, (valueT) regnum,
+ &zero_address_frag));
+}
+
+/* Install symbol definitions for assorted special registers.
+ See MIPS Assembly Language Programmer's Guide page 1-4 */
+
+static void
+define_some_regs (void)
+{
+ /* Software representation. */
+ insert_sreg ("zero", 0);
+ insert_sreg ("at", 1);
+ insert_sreg ("v0", 2);
+ insert_sreg ("v1", 3);
+ insert_sreg ("a0", 4);
+ insert_sreg ("a1", 5);
+ insert_sreg ("a2", 6);
+ insert_sreg ("a3", 7);
+ insert_sreg ("t0", 8);
+ insert_sreg ("t1", 9);
+ insert_sreg ("t2", 10);
+ insert_sreg ("t3", 11);
+ insert_sreg ("t4", 12);
+ insert_sreg ("t5", 13);
+ insert_sreg ("t6", 14);
+ insert_sreg ("t7", 15);
+ insert_sreg ("s0", 16);
+ insert_sreg ("s1", 17);
+ insert_sreg ("s2", 18);
+ insert_sreg ("s3", 19);
+ insert_sreg ("s4", 20);
+ insert_sreg ("s5", 21);
+ insert_sreg ("s6", 22);
+ insert_sreg ("s7", 23);
+ insert_sreg ("t8", 24);
+ insert_sreg ("t9", 25);
+ insert_sreg ("k0", 26);
+ insert_sreg ("k1", 27);
+ insert_sreg ("gp", 28);
+ insert_sreg ("sp", 29);
+ insert_sreg ("fp", 30);
+ insert_sreg ("ra", 31);
+ /* Special registers. */
+ insert_sreg ("pc", 0);
+ insert_sreg ("npc", 1);
+ insert_sreg ("iad", 2);
+}
+
+/* Subroutine check the string to match an register. */
+
+static int
+match_sft_register (char *name)
+{
+#define MAX_REG_NO 35
+/* Currently we have 35 software registers defined -
+ we borrowed from MIPS. */
+ static char *soft_reg[] =
+ {
+ "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s7", "k0", "k1",
+ "gp", "sp", "fp", "ra", "pc", "npc", "iad",
+ "EndofTab" /* End of the Table indicator */
+ };
+ char low_name[21], *ptr;
+ int idx;
+
+ for (ptr = name,idx = 0; *ptr != '\0'; ptr++)
+ low_name[idx++] = TOLOWER (*ptr);
+
+ low_name[idx] = '\0';
+ idx = 0;
+
+ while (idx < MAX_REG_NO && strcmp (soft_reg[idx], & low_name [0]))
+ idx += 1;
+
+ return idx < MAX_REG_NO;
+}
+
+/* Subroutine check the string to match an register. */
+
+static int
+is_ldst_registers (char *name)
+{
+ char *ptr = name;
+
+ /* The first character of the register name got to be either %, $, r of R. */
+ if ((ptr[0] == '%' || ptr[0] == '$' || ptr[0] == 'r' || ptr[0] == 'R')
+ && ISDIGIT ((unsigned char) ptr[1]))
+ return 1;
+
+ /* Now check the software register representation. */
+ return match_sft_register (ptr);
+}
+
+/* Subroutine of s_proc so targets can choose a different default prefix.
+ If DEFAULT_PREFIX is NULL, use the target's "leading char". */
+
+static void
+s_proc (int end_p)
+{
+ /* Record the current function so that we can issue an error message for
+ misplaced .func,.endfunc, and also so that .endfunc needs no
+ arguments. */
+ static char *current_name;
+ static char *current_label;
+
+ if (end_p)
+ {
+ if (current_name == NULL)
+ {
+ as_bad (_("missing .proc"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ current_name = current_label = NULL;
+ SKIP_WHITESPACE ();
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ input_line_pointer++;
+ }
+ else
+ {
+ char *name, *label;
+ char delim1, delim2;
+
+ if (current_name != NULL)
+ {
+ as_bad (_(".endfunc missing for previous .proc"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ name = input_line_pointer;
+ delim1 = get_symbol_end ();
+ name = xstrdup (name);
+ *input_line_pointer = delim1;
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ char leading_char = 0;
+
+ leading_char = bfd_get_symbol_leading_char (stdoutput);
+ /* Missing entry point, use function's name with the leading
+ char prepended. */
+ if (leading_char)
+ {
+ unsigned len = strlen (name) + 1;
+ label = xmalloc (len + 1);
+ label[0] = leading_char;
+ memcpy (label + 1, name, len);
+ }
+ else
+ label = name;
+ }
+ else
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ label = input_line_pointer;
+ delim2 = get_symbol_end ();
+ label = xstrdup (label);
+ *input_line_pointer = delim2;
+ }
+
+ current_name = name;
+ current_label = label;
+ }
+ 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 (void)
+{
+ const char *retval = NULL;
+ int lose = 0;
+ unsigned int i;
+
+ /* Create a new hash table. */
+ op_hash = hash_new ();
+
+ /* Hash up all the opcodes for fast use later. */
+ for (i = 0; i < num_dlx_opcodes; i++)
+ {
+ const char *name = machine_opcodes[i].name;
+
+ retval = hash_insert (op_hash, name, (void *) &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."));
+
+ define_some_regs ();
+}
+
+/* This function will check the opcode and return 1 if the opcode is one
+ of the load/store instruction, and it will fix the operand string to
+ the standard form so we can use the standard parse_operand routine. */
+
+#define READ_OP 0x100
+#define WRITE_OP 0x200
+static char iBuf[81];
+
+static char *
+dlx_parse_loadop (char * str)
+{
+ char *ptr = str;
+ int idx = 0;
+
+ /* The last pair of ()/[] is the register, all other are the
+ reloc displacement, and if there is a register then it ought
+ to have a pair of ()/[]
+ This is not necessarily true, what if the load instruction come
+ without the register and with %hi/%lo modifier? */
+ for (idx = 0; idx < 72 && ptr[idx] != '\0'; idx++)
+ ;
+
+ if (idx == 72)
+ {
+ badoperand_load:
+ as_bad (_("Bad operand for a load instruction: <%s>"), str);
+ return NULL;
+ }
+ else
+ {
+ int i, pb = 0;
+ int m2 = 0;
+ char rs1[7], rd[7], endm, match = '0';
+ char imm[72];
+
+ idx -= 1;
+ switch (str[idx])
+ {
+ case ')':
+ match = '(';
+ endm = ')';
+ break;
+ case ']':
+ match = '[';
+ endm = ']';
+ break;
+ default:
+ /* No register indicated, fill in zero. */
+ rs1[0] = 'r';
+ rs1[1] = '0';
+ rs1[2] = '\0';
+ match = 0;
+ endm = 0;
+ m2 = 1;
+ }
+
+ if (!m2)
+ {
+ /* Searching for (/[ which will match the ]/). */
+ for (pb = idx - 1; str[pb] != match; pb -= 1)
+ /* Match can only be either '[' or '(', if it is
+ '(' then this can be a normal expression, we'll treat
+ it as an operand. */
+ if (str[pb] == endm || pb < (idx - 5))
+ goto load_no_rs1;
+ pb += 1;
+
+ for (i = 0; (pb + i) < idx; i++)
+ rs1[i] = str[pb+i];
+
+ rs1[i] = '\0';
+
+ if (is_ldst_registers (& rs1[0]))
+ /* Point to the last character of the imm. */
+ pb -= 1;
+ else
+ {
+ load_no_rs1:
+ if (match == '[')
+ goto badoperand_load;
+ /* No register indicated, fill in zero and restore the imm. */
+ rs1[0] = 'r';
+ rs1[1] = '0';
+ rs1[2] = '\0';
+ m2 = 1;
+ }
+ }
+
+ /* Duplicate the first register. */
+ for (i = 0; i < 7 && str[i] != ','; i++)
+ rd[i] = ptr[i];
+
+ if (str[i] != ',')
+ goto badoperand_load;
+ else
+ rd[i] = '\0';
+
+ /* Copy the immd. */
+ if (m2)
+ /* Put the '\0' back in. */
+ pb = idx + 1;
+
+ for (i++, m2 = 0; i < pb; m2++,i++)
+ imm[m2] = ptr[i];
+
+ imm[m2] = '\0';
+
+ /* Assemble the instruction to gas internal format. */
+ for (i = 0; rd[i] != '\0'; i++)
+ iBuf[i] = rd[i];
+
+ iBuf[i++] = ',';
+
+ for (pb = 0 ; rs1[pb] != '\0'; i++, pb++)
+ iBuf[i] = rs1[pb];
+
+ iBuf[i++] = ',';
+
+ for (pb = 0; imm[pb] != '\0'; i++, pb++)
+ iBuf[i] = imm[pb];
+
+ iBuf[i] = '\0';
+ return iBuf;
+ }
+}
+
+static char *
+dlx_parse_storeop (char * str)
+{
+ char *ptr = str;
+ int idx = 0;
+
+ /* Search for the ','. */
+ for (idx = 0; idx < 72 && ptr[idx] != ','; idx++)
+ ;
+
+ if (idx == 72)
+ {
+ badoperand_store:
+ as_bad (_("Bad operand for a store instruction: <%s>"), str);
+ return NULL;
+ }
+ else
+ {
+ /* idx now points to the ','. */
+ int i, pb = 0;
+ int comma = idx;
+ int m2 = 0;
+ char rs1[7], rd[7], endm, match = '0';
+ char imm[72];
+
+ /* Now parse the '(' and ')', and make idx point to ')'. */
+ idx -= 1;
+ switch (str[idx])
+ {
+ case ')':
+ match = '(';
+ endm = ')';
+ break;
+ case ']':
+ match = '[';
+ endm = ']';
+ break;
+ default:
+ /* No register indicated, fill in zero. */
+ rs1[0] = 'r';
+ rs1[1] = '0';
+ rs1[2] = '\0';
+ match = 0;
+ endm = 0;
+ m2 = 1;
+ }
+
+ if (!m2)
+ {
+ /* Searching for (/[ which will match the ]/). */
+ for (pb = idx - 1; str[pb] != match; pb -= 1)
+ if (pb < (idx - 5) || str[pb] == endm)
+ goto store_no_rs1;
+ pb += 1;
+
+ for (i = 0; (pb + i) < idx; i++)
+ rs1[i] = str[pb + i];
+
+ rs1[i] = '\0';
+
+ if (is_ldst_registers (& rs1[0]))
+ /* Point to the last character of the imm. */
+ pb -= 1;
+ else
+ {
+ store_no_rs1:
+ if (match == '[')
+ goto badoperand_store;
+
+ /* No register indicated, fill in zero and restore the imm. */
+ rs1[0] = 'r';
+ rs1[1] = '0';
+ rs1[2] = '\0';
+ pb = comma;
+ }
+ }
+ else
+ /* No register was specified. */
+ pb = comma;
+
+ /* Duplicate the first register. */
+ for (i = comma + 1; (str[i] == ' ' || str[i] == '\t'); i++)
+ ;
+
+ for (m2 = 0; (m2 < 7 && str[i] != '\0'); i++, m2++)
+ {
+ if (str[i] != ' ' && str[i] != '\t')
+ rd[m2] = str[i];
+ else
+ goto badoperand_store;
+ }
+
+ if (str[i] != '\0')
+ goto badoperand_store;
+ else
+ rd[m2] = '\0';
+
+ /* Copy the immd. */
+ for (i = 0; i < pb; i++)
+ imm[i] = ptr[i];
+
+ imm[i] = '\0';
+
+ /* Assemble the instruction to gas internal format. */
+ for (i = 0; rd[i] != '\0'; i++)
+ iBuf[i] = rd[i];
+ iBuf[i++] = ',';
+ for (pb = 0 ; rs1[pb] != '\0'; i++, pb++)
+ iBuf[i] = rs1[pb];
+ iBuf[i++] = ',';
+ for (pb = 0; imm[pb] != '\0'; i++, pb++)
+ iBuf[i] = imm[pb];
+ iBuf[i] = '\0';
+ return iBuf;
+ }
+}
+
+static char *
+fix_ld_st_operand (unsigned long opcode, char* str)
+{
+ /* Check the opcode. */
+ switch ((int) opcode)
+ {
+ case LBOP:
+ case LBUOP:
+ case LSBUOP:
+ case LHOP:
+ case LHUOP:
+ case LSHUOP:
+ case LWOP:
+ case LSWOP:
+ return dlx_parse_loadop (str);
+ case SBOP:
+ case SHOP:
+ case SWOP:
+ return dlx_parse_storeop (str);
+ default:
+ return str;
+ }
+}
+
+static int
+hilo_modifier_ok (char *s)
+{
+ char *ptr = s;
+ int idx, count = 1;
+
+ if (*ptr != '(')
+ return 1;
+
+ for (idx = 1; ptr[idx] != '\0' && ptr[idx] != '[' && idx < 73; idx += 1)
+ {
+ if (count == 0)
+ return count;
+
+ if (ptr[idx] == '(')
+ count += 1;
+
+ if (ptr[idx] == ')')
+ count -= 1;
+ }
+
+ return (count == 0) ? 1:0;
+}
+
+static char *
+parse_operand (char *s, expressionS *operandp)
+{
+ char *save = input_line_pointer;
+ char *new_pos;
+
+ the_insn.HI = the_insn.LO = 0;
+
+ /* Search for %hi and %lo, make a mark and skip it. */
+ if (strncmp (s, "%hi", 3) == 0)
+ {
+ s += 3;
+ the_insn.HI = 1;
+ }
+ else
+ {
+ if (strncmp (s, "%lo", 3) == 0)
+ {
+ s += 3;
+ the_insn.LO = 1;
+ }
+ else
+ the_insn.LO = 0;
+ }
+
+ if (the_insn.HI || the_insn.LO)
+ {
+ if (!hilo_modifier_ok (s))
+ as_bad (_("Expression Error for operand modifier %%hi/%%lo\n"));
+ }
+
+ /* Check for the % and $ register representation */
+ if ((s[0] == '%' || s[0] == '$' || s[0] == 'r' || s[0] == 'R')
+ && ISDIGIT ((unsigned char) s[1]))
+ {
+ /* We have a numeric register expression. No biggy. */
+ s += 1;
+ input_line_pointer = s;
+ (void) expression (operandp);
+ if (operandp->X_op != O_constant
+ || operandp->X_add_number > 31)
+ as_bad (_("Invalid expression after %%%%\n"));
+ operandp->X_op = O_register;
+ }
+ else
+ {
+ /* Normal operand parsing. */
+ input_line_pointer = s;
+ (void) expression (operandp);
+ }
+
+ new_pos = input_line_pointer;
+ input_line_pointer = save;
+ return new_pos;
+}
+
+/* Instruction parsing. Takes a string containing the opcode.
+ Operands are at input_line_pointer. Output is in the_insn.
+ Warnings or errors are generated. */
+
+static void
+machine_ip (char *str)
+{
+ char *s;
+ const char *args;
+ struct machine_opcode *insn;
+ unsigned long opcode;
+ expressionS the_operand;
+ expressionS *operand = &the_operand;
+ unsigned int reg, reg_shift = 0;
+
+ /* Fixup the opcode string to all lower cases, and also
+ allow numerical digits. */
+ s = str;
+
+ if (ISALPHA (*s))
+ for (; ISALNUM (*s); ++s)
+ if (ISUPPER (*s))
+ *s = TOLOWER (*s);
+
+ switch (*s)
+ {
+ case '\0':
+ break;
+
+ /* FIXME-SOMEDAY more whitespace. */
+ case ' ':
+ *s++ = '\0';
+ break;
+
+ default:
+ as_bad (_("Unknown opcode: `%s'"), str);
+ return;
+ }
+
+ /* Hash the opcode, insn will have the string from opcode table.
+ also initialized the_insn struct. */
+ if ((insn = (struct machine_opcode *) hash_find (op_hash, str)) == NULL)
+ {
+ /* Handle the ret and return macro here. */
+ if ((strcmp (str, "ret") == 0) || (strcmp (str, "return") == 0))
+ {
+ memset (&the_insn, '\0', sizeof (the_insn));
+ the_insn.reloc = NO_RELOC;
+ the_insn.pcrel = 0;
+ the_insn.opcode =
+ (unsigned long)(JROP | 0x03e00000); /* 0x03e00000 = r31 << 21 */
+ }
+ else
+ as_bad (_("Unknown opcode `%s'."), str);
+
+ return;
+ }
+
+ opcode = insn->opcode;
+ memset (&the_insn, '\0', sizeof (the_insn));
+ the_insn.reloc = NO_RELOC;
+ the_insn.pcrel = 0;
+
+ /* Set the sip reloc HI16 flag. */
+ if (!set_dlx_skip_hi16_flag (1))
+ as_bad (_("Can not set dlx_skip_hi16_flag"));
+
+ /* Fix the operand string if it is one of load store instructions. */
+ s = fix_ld_st_operand (opcode, s);
+
+ /* 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' && insn->args[0] != 'N')
+ {
+ /* Prime the pump. */
+ if (*s == '\0')
+ {
+ as_bad (_("Missing arguments for opcode <%s>."), str);
+ return;
+ }
+ else
+ s = parse_operand (s, operand);
+ }
+ else if (insn->args[0] == 'N')
+ {
+ /* Clean up the insn and done! */
+ the_insn.opcode = opcode;
+ return;
+ }
+
+ /* Parse through the args (this is from opcode table), *s point to
+ the current character of the instruction stream. */
+ for (args = insn->args;; ++args)
+ {
+ switch (*args)
+ {
+ /* End of Line. */
+ case '\0':
+ /* End of args. */
+ if (*s == '\0')
+ {
+ /* We are truly done. */
+ the_insn.opcode = opcode;
+ /* Clean up the HI and LO mark. */
+ the_insn.HI = 0;
+ the_insn.LO = 0;
+ return;
+ }
+
+ the_insn.HI = 0;
+ the_insn.LO = 0;
+ as_bad (_("Too many operands: %s"), s);
+ break;
+
+ /* ',' Args separator */
+ case ',':
+ /* Must match a comma. */
+ if (*s++ == ',')
+ {
+ /* Parse next operand. */
+ s = parse_operand (s, operand);
+ continue;
+ }
+ break;
+
+ /* It can be a 'a' register or 'i' operand. */
+ case 'P':
+ /* Macro move operand/reg. */
+ if (operand->X_op == O_register)
+ {
+ /* Its a register. */
+ reg_shift = 21;
+ goto general_reg;
+ }
+
+ /* The immediate 16 bits literal, bit 0-15. */
+ case 'i':
+ /* offset, unsigned. */
+ case 'I':
+ /* offset, signed. */
+ if (operand->X_op == O_constant)
+ {
+ if (the_insn.HI)
+ operand->X_add_number >>= 16;
+
+ opcode |= operand->X_add_number & 0xFFFF;
+
+ if (the_insn.HI && the_insn.LO)
+ as_bad (_("Both the_insn.HI and the_insn.LO are set : %s"), s);
+ else
+ {
+ the_insn.HI = 0;
+ the_insn.LO = 0;
+ }
+ continue;
+ }
+
+ the_insn.reloc = (the_insn.HI) ? RELOC_DLX_HI16
+ : (the_insn.LO ? RELOC_DLX_LO16 : RELOC_DLX_16);
+ the_insn.reloc_offset = 2;
+ the_insn.size = 2;
+ the_insn.pcrel = 0;
+ the_insn.exp = * operand;
+ the_insn.HI = 0;
+ the_insn.LO = 0;
+ continue;
+
+ case 'd':
+ /* offset, signed. */
+ if (operand->X_op == O_constant)
+ {
+ opcode |= operand->X_add_number & 0xFFFF;
+ continue;
+ }
+ the_insn.reloc = RELOC_DLX_REL16;
+ the_insn.reloc_offset = 0; /* BIG-ENDIAN Byte 3 of insn. */
+ the_insn.size = 4;
+ the_insn.pcrel = 1;
+ the_insn.exp = *operand;
+ continue;
+
+ /* The immediate 26 bits literal, bit 0-25. */
+ case 'D':
+ /* offset, signed. */
+ if (operand->X_op == O_constant)
+ {
+ opcode |= operand->X_add_number & 0x3FFFFFF;
+ continue;
+ }
+ the_insn.reloc = RELOC_DLX_REL26;
+ the_insn.reloc_offset = 0; /* BIG-ENDIAN Byte 3 of insn. */
+ the_insn.size = 4;
+ the_insn.pcrel = 1;
+ the_insn.exp = *operand;
+ continue;
+
+ /* Type 'a' Register. */
+ case 'a':
+ /* A general register at bits 21-25, rs1. */
+ reg_shift = 21;
+ goto general_reg;
+
+ /* Type 'b' Register. */
+ case 'b':
+ /* A general register at bits 16-20, rs2/rd. */
+ reg_shift = 16;
+ goto general_reg;
+
+ /* Type 'c' Register. */
+ case 'c':
+ /* A general register at bits 11-15, rd. */
+ reg_shift = 11;
+
+ general_reg:
+ know (operand->X_add_symbol == 0);
+ know (operand->X_op_symbol == 0);
+ reg = operand->X_add_number;
+ if (reg & 0xffffffe0)
+ as_fatal (_("failed regnum sanity check."));
+ else
+ /* Got the register, now figure out where it goes in the opcode. */
+ opcode |= reg << reg_shift;
+
+ switch (*args)
+ {
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'P':
+ continue;
+ }
+ as_fatal (_("failed general register sanity check."));
+ break;
+
+ default:
+ BAD_CASE (*args);
+ }
+
+ /* Types or values of args don't match. */
+ as_bad (_("Invalid operands"));
+ return;
+ }
+}
+
+/* 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 (char *str)
+{
+ char *toP;
+ fixS *fixP;
+ bit_fixS *bitP;
+
+ know (str);
+ machine_ip (str);
+ toP = frag_more (4);
+ dwarf2_emit_insn (4);
+
+ /* Put out the opcode. */
+ md_number_to_chars (toP, the_insn.opcode, 4);
+
+ /* Put out the symbol-dependent stuff. */
+ if (the_insn.reloc != NO_RELOC)
+ {
+ fixP = fix_new_exp (frag_now,
+ (toP - frag_now->fr_literal + the_insn.reloc_offset),
+ the_insn.size, & the_insn.exp, the_insn.pcrel,
+ the_insn.reloc);
+
+ /* Turn off complaints that the addend is
+ too large for things like foo+100000@ha. */
+ switch (the_insn.reloc)
+ {
+ case RELOC_DLX_HI16:
+ case RELOC_DLX_LO16:
+ fixP->fx_no_overflow = 1;
+ break;
+ default:
+ break;
+ }
+
+ switch (fixP->fx_r_type)
+ {
+ case RELOC_DLX_REL26:
+ bitP = malloc (sizeof (bit_fixS));
+ bitP->fx_bit_size = 26;
+ bitP->fx_bit_offset = 25;
+ bitP->fx_bit_base = the_insn.opcode & 0xFC000000;
+ bitP->fx_bit_base_adj = 0;
+ bitP->fx_bit_max = 0;
+ bitP->fx_bit_min = 0;
+ bitP->fx_bit_add = 0x03FFFFFF;
+ fixP->fx_bit_fixP = bitP;
+ break;
+ case RELOC_DLX_LO16:
+ case RELOC_DLX_REL16:
+ bitP = malloc (sizeof (bit_fixS));
+ bitP->fx_bit_size = 16;
+ bitP->fx_bit_offset = 15;
+ bitP->fx_bit_base = the_insn.opcode & 0xFFFF0000;
+ bitP->fx_bit_base_adj = 0;
+ bitP->fx_bit_max = 0;
+ bitP->fx_bit_min = 0;
+ bitP->fx_bit_add = 0x0000FFFF;
+ fixP->fx_bit_fixP = bitP;
+ break;
+ case RELOC_DLX_HI16:
+ bitP = malloc (sizeof (bit_fixS));
+ bitP->fx_bit_size = 16;
+ bitP->fx_bit_offset = 15;
+ bitP->fx_bit_base = the_insn.opcode & 0xFFFF0000;
+ bitP->fx_bit_base_adj = 0;
+ bitP->fx_bit_max = 0;
+ bitP->fx_bit_min = 0;
+ bitP->fx_bit_add = 0x0000FFFF;
+ fixP->fx_bit_fixP = bitP;
+ break;
+ default:
+ fixP->fx_bit_fixP = NULL;
+ break;
+ }
+ }
+}
+
+/* This is identical to the md_atof in m68k.c. I think this is right,
+ but I'm not sure. Dlx will not use it anyway, so I just leave it
+ here for now. */
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+/* Write out big-endian. */
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ number_to_chars_bigendian (buf, val, n);
+}
+
+bfd_boolean
+md_dlx_fix_adjustable (fixS *fixP)
+{
+ /* We need the symbol name for the VTABLE entries. */
+ return (fixP->fx_r_type != BFD_RELOC_VTABLE_INHERIT
+ && fixP->fx_r_type != BFD_RELOC_VTABLE_ENTRY);
+}
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ long val = *valP;
+ char *place = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ switch (fixP->fx_r_type)
+ {
+ case RELOC_DLX_LO16:
+ case RELOC_DLX_REL16:
+ if (fixP->fx_bit_fixP != NULL)
+ {
+ val = (val & 0x0000FFFF) | fixP->fx_bit_fixP->fx_bit_base;
+ free (fixP->fx_bit_fixP);
+ fixP->fx_bit_fixP = NULL;
+ }
+#ifdef DEBUG
+ else
+ know ((fixP->fx_bit_fixP != NULL));
+#endif
+ break;
+
+ case RELOC_DLX_HI16:
+ if (fixP->fx_bit_fixP != NULL)
+ {
+ val = (val >> 16) | fixP->fx_bit_fixP->fx_bit_base;
+ free (fixP->fx_bit_fixP);
+ fixP->fx_bit_fixP = NULL;
+ }
+#ifdef DEBUG
+ else
+ know ((fixP->fx_bit_fixP != NULL));
+#endif
+ break;
+
+ case RELOC_DLX_REL26:
+ if (fixP->fx_bit_fixP != NULL)
+ {
+ val = (val & 0x03FFFFFF) | fixP->fx_bit_fixP->fx_bit_base;
+ free (fixP->fx_bit_fixP);
+ fixP->fx_bit_fixP = NULL;
+ }
+#ifdef DEBUG
+ else
+ know ((fixP->fx_bit_fixP != NULL));
+#endif
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ /* This borrowed from tc-ppc.c on a whim. */
+ fixP->fx_done = 0;
+ if (fixP->fx_addsy
+ && !S_IS_DEFINED (fixP->fx_addsy)
+ && !S_IS_WEAK (fixP->fx_addsy))
+ S_SET_WEAK (fixP->fx_addsy);
+ return;
+
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ return;
+
+ default:
+ break;
+ }
+
+ number_to_chars_bigendian (place, val, fixP->fx_size);
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = 1;
+}
+
+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 (int c ATTRIBUTE_UNUSED,
+ char *arg ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+void
+md_show_usage (FILE *stream ATTRIBUTE_UNUSED)
+{
+}
+
+/* This is called when a line is unrecognized. */
+
+int
+dlx_unrecognized_line (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;
+}
+
+/* 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 (char *name ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+/* Parse an operand that is machine-specific, the function was called
+ in expr.c by operand() function, when everything failed before it
+ call a quit. */
+
+void
+md_operand (expressionS* expressionP)
+{
+ /* Check for the #number representation */
+ if (input_line_pointer[0] == '#' &&
+ ISDIGIT ((unsigned char) input_line_pointer[1]))
+ {
+ /* We have a numeric number expression. No biggy. */
+ input_line_pointer += 1; /* Skip # */
+
+ (void) expression (expressionP);
+
+ if (expressionP->X_op != O_constant)
+ as_bad (_("Invalid expression after # number\n"));
+ }
+
+ return;
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED,
+ valueT size)
+{
+ /* Byte alignment is fine. */
+ return size;
+}
+
+/* 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 (fixS* fixP)
+{
+ return 4 + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format.
+ FIXME: To what extent can we get all relevant targets to use this?
+ The above FIXME is from a29k, but I think it is also needed here. */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED,
+ fixS *fixP)
+{
+ arelent * reloc;
+
+ reloc = xmalloc (sizeof (arelent));
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("internal error: can't export reloc type %d (`%s')"),
+ fixP->fx_r_type,
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+ return NULL;
+ }
+
+ gas_assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
+
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
+ reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
+
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ reloc->address = fixP->fx_offset;
+ reloc->addend = 0;
+
+ return reloc;
+}
+
+const pseudo_typeS
+dlx_pseudo_table[] =
+{
+ /* Some additional ops that are used by gcc-dlx. */
+ {"asciiz", stringer, 8 + 1},
+ {"half", cons, 2},
+ {"dword", cons, 8},
+ {"word", cons, 4},
+ {"proc", s_proc, 0},
+ {"endproc", s_proc, 1},
+ {NULL, NULL, 0}
+};
+
+void
+dlx_pop_insert (void)
+{
+ pop_insert (dlx_pseudo_table);
+ return ;
+}