aboutsummaryrefslogtreecommitdiff
path: root/gas/config/tc-alpha.c
diff options
context:
space:
mode:
authorKen Raeburn <raeburn@cygnus>1994-01-28 01:37:02 +0000
committerKen Raeburn <raeburn@cygnus>1994-01-28 01:37:02 +0000
commit5749c4970051e6f4149c9a52e11b530c70d50f19 (patch)
treeb32e6e59d9d15533790fb680aaa71fa5d149162b /gas/config/tc-alpha.c
parent804c860185bcf1f840f346c25a8c5299a55a18be (diff)
downloadgdb-5749c4970051e6f4149c9a52e11b530c70d50f19.zip
gdb-5749c4970051e6f4149c9a52e11b530c70d50f19.tar.gz
gdb-5749c4970051e6f4149c9a52e11b530c70d50f19.tar.bz2
New Alpha support files, based on files from CMU.
Still to do: - fix floating-point handling - figure out if we can adapt to using ../opcodes/alpha-opc.h - gcc bootstrap testing - 32-bit mode support? - test cross-assembly
Diffstat (limited to 'gas/config/tc-alpha.c')
-rw-r--r--gas/config/tc-alpha.c1918
1 files changed, 1918 insertions, 0 deletions
diff --git a/gas/config/tc-alpha.c b/gas/config/tc-alpha.c
new file mode 100644
index 0000000..f14e5e9
--- /dev/null
+++ b/gas/config/tc-alpha.c
@@ -0,0 +1,1918 @@
+/* tc-alpha.c - Processor-specific code for the DEC Alpha CPU.
+ Copyright (C) 1989, 1993, 1994 Free Software Foundation, Inc.
+ Contributed by Carnegie Mellon University, 1993.
+ Written by Alessandro Forin, based on earlier gas-1.38 target CPU files.
+ Modified by Ken Raeburn for gas-2.x and ECOFF support.
+
+ 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/*
+ * Mach Operating System
+ * Copyright (c) 1993 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+/*
+ * HISTORY
+ * 5-Oct-93 Alessandro Forin (af) at Carnegie-Mellon University
+ * First Checkin
+ *
+ * $Log$
+ * Revision 1.1 1994/01/28 01:36:53 raeburn
+ * New Alpha support files, based on files from CMU.
+ * Still to do:
+ * - fix floating-point handling
+ * - figure out if we can adapt to using ../opcodes/alpha-opc.h
+ * - gcc bootstrap testing
+ * - 32-bit mode support?
+ * - test cross-assembly
+ *
+ *
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: Jan 1993
+ */
+
+#include "as.h"
+#include "alpha-opcode.h"
+#include "subsegs.h"
+
+/* These are exported to relaxing code, even though we don't do any
+ relaxing on this processor currently. */
+const relax_typeS md_relax_table[1];
+int md_short_jump_size = 4;
+int md_long_jump_size = 4;
+
+/* handle of the OPCODE hash table */
+static struct hash_control *op_hash;
+
+/* sections we'll want to keep track of */
+static segT lita_sec, rdata, sdata;
+
+/* setting for ".set [no]{at,macro}" */
+static int at_ok = 1, macro_ok = 1;
+
+/* Keep track of global pointer. */
+static valueT gp_value;
+static symbolS *gp;
+
+/* We'll probably be using this relocation frequently, and we
+ will want to compare for it. */
+static reloc_howto_type *gpdisp_hi16_howto;
+
+/* These are exported to ECOFF code. */
+unsigned long alpha_gprmask, alpha_fprmask;
+
+/* Used for LITUSE relocations. */
+static expressionS lituse_basereg, lituse_byteoff, lituse_jsr;
+
+/* Imported functions -- they should be defined in header files somewhere. */
+extern segT subseg_get ();
+extern PTR bfd_alloc_by_size_t ();
+extern void s_globl (), s_long (), s_short (), s_space (), cons (), s_text (),
+ s_data (), float_cons ();
+
+static void s_mask (), s_base (), s_proc (), s_alpha_set ();
+static void s_gprel32 (), s_rdata (), s_sdata (), s_alpha_comm ();
+static int alpha_ip ();
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"common", s_comm, 0}, /* is this used? */
+ {"comm", s_alpha_comm, 0}, /* osf1 compiler does this */
+ {"rdata", s_rdata, 0},
+ {"sdata", s_sdata, 0},
+ {"gprel32", s_gprel32, 0},
+ {"t_floating", float_cons, 'd'},
+ {"s_floating", float_cons, 'f'},
+ {"f_floating", float_cons, 'F'},
+ {"g_floating", float_cons, 'G'},
+ {"d_floating", float_cons, 'D'},
+
+ {"proc", s_proc, 0},
+ {"aproc", s_proc, 1},
+ {"set", s_alpha_set, 0},
+ {"reguse", s_ignore, 0},
+ {"livereg", s_ignore, 0},
+ {"extern", s_ignore, 0}, /*??*/
+ {"base", s_base, 0}, /*??*/
+ {"option", s_ignore, 0},
+ {"prologue", s_ignore, 0},
+ {"aent", s_ignore, 0},
+ {"ugen", s_ignore, 0},
+
+/* We don't do any optimizing, so we can safely ignore these. */
+ {"noalias", s_ignore, 0},
+ {"alias", s_ignore, 0},
+
+ {NULL, 0, 0},
+};
+
+#define SA 21 /* shift for register Ra */
+#define SB 16 /* shift for register Rb */
+#define SC 0 /* shift for register Rc */
+#define SN 13 /* shift for 8 bit immediate # */
+
+#define T9 23
+#define T10 24
+#define T11 25
+#define RA 26
+#define PV 27
+#define AT 28
+#define GP 29
+#define SP 30
+#define ZERO 31
+
+#define OPCODE(X) (((X) >> 26) & 0x3f)
+#define OP_FCN(X) (((X) >> 5) & 0x7f)
+
+#ifndef FIRST_32BIT_QUADRANT
+#define FIRST_32BIT_QUADRANT 0
+#endif
+
+int first_32bit_quadrant = FIRST_32BIT_QUADRANT;
+int base_register = FIRST_32BIT_QUADRANT ? ZERO : GP;
+
+int no_mixed_code = 0;
+int nofloats = 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[] = "#";
+
+/* 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 '/*' will always start a comment */
+const char line_comment_chars[] = "#";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+const char line_separator_chars[1];
+
+/* Chars that mean this number is a floating point constant, as in
+ "0f12.456" or "0d1.2345e12". */
+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. */
+
+struct reloc_data {
+ expressionS exp;
+ int pcrel;
+ bfd_reloc_code_real_type code;
+};
+
+/* Occasionally, two relocations will be desired for one address.
+ Mainly only in cases like "jsr $r,foo" where we want both a LITUSE
+ and a HINT reloc. */
+#define MAX_RELOCS 2
+
+struct alpha_it {
+ unsigned long opcode; /* need at least 32 bits */
+ struct reloc_data reloc[MAX_RELOCS];
+};
+
+static int getExpression (char *str, struct alpha_it *insn);
+static char *expr_end;
+
+#define note_gpreg(R) (alpha_gprmask |= (1 << (R)))
+#define note_fpreg(R) (alpha_fprmask |= (1 << (R)))
+
+int
+tc_get_register (frame)
+ int frame;
+{
+ int reg;
+ int framereg = SP;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '$')
+ {
+ input_line_pointer++;
+ if (input_line_pointer[0] == 's'
+ && input_line_pointer[1] == 'p')
+ {
+ input_line_pointer += 2;
+ framereg = SP;
+ }
+ else
+ framereg = get_absolute_expression ();
+ framereg &= 31; /* ? */
+ }
+ else
+ as_warn ("frame reg expected, using $%d.", framereg);
+
+ note_gpreg (framereg);
+ return framereg;
+}
+
+static void
+s_rdata (ignore)
+ int ignore;
+{
+ int temp;
+
+ temp = get_absolute_expression ();
+#if 0
+ if (!rdata)
+ rdata = subseg_get (".rdata", 0);
+ subseg_set (rdata, (subsegT) temp);
+#else
+ rdata = subseg_new (".rdata", 0);
+#endif
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_sdata (ignore)
+ int ignore;
+{
+ int temp;
+
+ temp = get_absolute_expression ();
+#if 0
+ if (!sdata)
+ sdata = subseg_get (".sdata", 0);
+ subseg_set (sdata, (subsegT) temp);
+#else
+ sdata = subseg_new (".sdata", 0);
+#endif
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_alpha_comm (ignore)
+ int ignore;
+{
+ register char *name;
+ register char c;
+ register char *p;
+ offsetT temp;
+ register symbolS *symbolP;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ /* just after name is now '\0' */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ /* Alpha OSF/1 compiler doesn't provide the comma, gcc does. */
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ }
+ if ((temp = get_absolute_expression ()) < 0)
+ {
+ as_warn (".COMMon length (%ld.) <0! Ignored.", (long) temp);
+ ignore_rest_of_line ();
+ return;
+ }
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+ if (S_IS_DEFINED (symbolP))
+ {
+ as_bad ("Ignoring attempt to re-define symbol");
+ ignore_rest_of_line ();
+ return;
+ }
+ if (S_GET_VALUE (symbolP))
+ {
+ if (S_GET_VALUE (symbolP) != (valueT) temp)
+ as_bad ("Length of .comm \"%s\" is already %ld. Not changed to %ld.",
+ S_GET_NAME (symbolP),
+ (long) S_GET_VALUE (symbolP),
+ (long) temp);
+ }
+ else
+ {
+ S_SET_VALUE (symbolP, (valueT) temp);
+ S_SET_EXTERNAL (symbolP);
+ }
+
+ know (symbolP->sy_frag == &zero_address_frag);
+ demand_empty_rest_of_line ();
+}
+
+int
+alpha_local_label (name)
+ const char *name;
+{
+ if (name[0] == 'L' /* || name[0] == '$' */)
+ return 1;
+ return 0;
+}
+
+arelent *
+tc_gen_reloc (sec, fixp)
+ asection *sec;
+ fixS *fixp;
+{
+ arelent *reloc;
+ bfd_reloc_code_real_type code;
+
+ reloc = (arelent *) bfd_alloc_by_size_t (stdoutput, sizeof (arelent));
+ reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ if (fixp->fx_r_type > BFD_RELOC_UNUSED || fixp->fx_r_type < 0)
+ abort ();
+
+ if (fixp->fx_r_type == BFD_RELOC_ALPHA_GPDISP_HI16)
+ {
+ if (!gpdisp_hi16_howto)
+ gpdisp_hi16_howto = bfd_reloc_type_lookup (stdoutput,
+ fixp->fx_r_type);
+ reloc->howto = gpdisp_hi16_howto;
+ }
+ else
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ assert (reloc->howto != 0);
+ if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
+ {
+ as_fatal ("bug in handling type-%d relocs", fixp->fx_r_type);
+ abort ();
+ }
+ assert (!fixp->fx_pcrel == !reloc->howto->pc_relative);
+
+ if (reloc->howto->pc_relative
+ && reloc->howto->pcrel_offset
+#if 1
+ && code != BFD_RELOC_ALPHA_GPDISP_HI16
+ && code != BFD_RELOC_ALPHA_GPDISP_LO16
+#endif
+ )
+ {
+ reloc->addend = fixp->fx_offset - reloc->address;
+ }
+ else
+ reloc->addend = fixp->fx_offset;
+ return reloc;
+}
+
+static void
+s_base ()
+{
+ if (first_32bit_quadrant)
+ {
+ /* not fatal, but it might not work in the end */
+ as_warn ("File overrides no-base-register option.");
+ first_32bit_quadrant = 0;
+ }
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '$')
+ { /* $rNN form */
+ input_line_pointer++;
+ if (*input_line_pointer == 'r')
+ input_line_pointer++;
+ }
+
+ base_register = get_absolute_expression ();
+ if (base_register < 0 || base_register > 31)
+ {
+ base_register = GP;
+ as_warn ("Bad base register, using $r.", base_register);
+ }
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_gprel32 ()
+{
+ expressionS e;
+ char *p;
+
+ SKIP_WHITESPACE ();
+ expression (&e);
+ switch (e.X_op)
+ {
+ case O_constant:
+ e.X_add_symbol = section_symbol (absolute_section);
+ /* fall through */
+ case O_symbol:
+ e.X_op = O_subtract;
+ e.X_op_symbol = gp;
+ break;
+ default:
+ abort ();
+ }
+ p = frag_more (4);
+ memset (p, 0, 4);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &e, 0,
+ BFD_RELOC_GPREL32);
+}
+
+static void
+create_lita_section ()
+{
+ segT current_section = now_seg;
+ int current_subsec = now_subseg;
+
+ lita_sec = subseg_new (".lita", 0);
+ subseg_set (current_section, current_subsec);
+ bfd_set_section_flags (stdoutput, lita_sec,
+ SEC_RELOC | SEC_ALLOC | SEC_LOAD | SEC_READONLY
+ | SEC_DATA);
+ bfd_set_section_alignment (stdoutput, lita_sec, 3);
+}
+
+/* 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;
+ int lose = 0;
+ unsigned int i = 0;
+
+ op_hash = hash_new ();
+
+ for (i = 0; i < NUMOPCODES; )
+ {
+ const char *name = alpha_opcodes[i].name;
+ retval = hash_insert (op_hash, name, (PTR) & alpha_opcodes[i]);
+ if (retval)
+ {
+ as_bad ("internal error: can't hash opcode `%s': %s",
+ alpha_opcodes[i].name, retval);
+ lose = 1;
+ }
+ do
+ ++i;
+ while (i < NUMOPCODES
+ && (alpha_opcodes[i].name == name
+ || !strcmp (alpha_opcodes[i].name, name)));
+ }
+ /* Some opcodes include modifiers of various sorts with a "/mod"
+ syntax, like the architecture documentation suggests. However,
+ for use with gcc at least, we also need to access those same
+ opcodes without the "/". */
+ for (i = 0; i < NUMOPCODES; )
+ {
+ const char *name = alpha_opcodes[i].name;
+ if (strchr (name, '/'))
+ {
+ char *p = xmalloc (strlen (name));
+ const char *q = name;
+ char *q2 = p;
+
+ for (; *q; q++)
+ if (*q != '/')
+ *q2++ = *q;
+
+ *q2++ = 0;
+ retval = hash_insert (op_hash, p, (PTR) & alpha_opcodes[i]);
+ if (retval)
+ {
+ /* Ignore failures -- the opcode table does duplicate
+ some variants in different forms, like "hw_st/q" and
+ "hw_stq". */
+#if 0
+ as_bad ("internal error: can't hash opcode variant `%s': %s",
+ p, retval);
+ lose = 1;
+#endif
+ }
+ }
+ do
+ ++i;
+ while (i < NUMOPCODES
+ && (alpha_opcodes[i].name == name
+ || !strcmp (alpha_opcodes[i].name, name)));
+ }
+
+
+
+ if (lose)
+ as_fatal ("Broken assembler. No assembly attempted.");
+
+ lituse_basereg.X_op = O_constant;
+ lituse_basereg.X_add_number = 1;
+ lituse_byteoff.X_op = O_constant;
+ lituse_byteoff.X_add_number = 2;
+ lituse_jsr.X_op = O_constant;
+ lituse_jsr.X_add_number = 3;
+
+ /* So .sbss will get used for tiny objects. */
+ bfd_set_gp_size (stdoutput, 8);
+ create_lita_section ();
+ /* For handling the GP, create a symbol that won't be output in the
+ symbol table. We'll edit it out of relocs later. */
+ gp = symbol_new ("<GP value>", lita_sec, 0x8000, &zero_address_frag);
+ symbol_remove (gp, &symbol_rootP, &symbol_lastP);
+}
+
+int optnum = 1;
+
+void
+md_assemble (str)
+ char *str;
+{
+ char *toP;
+ int i, j, count;
+#define MAX_INSNS 5
+ struct alpha_it insns[MAX_INSNS];
+
+ count = alpha_ip (str, insns);
+ if (count <= 0)
+ return;
+
+ for (i = 0; i < count; i++)
+ {
+ toP = frag_more (4);
+
+ /* put out the opcode */
+ md_number_to_chars (toP, insns[i].opcode, 4);
+
+ /* put out the symbol-dependent stuff */
+ for (j = 0; j < MAX_RELOCS; j++)
+ {
+ struct reloc_data *r = &insns[i].reloc[j];
+ fixS *f;
+
+ if (r->code != BFD_RELOC_NONE)
+ {
+ if (r->exp.X_op == O_constant)
+ {
+ r->exp.X_add_symbol = section_symbol (absolute_section);
+ r->exp.X_op = O_symbol;
+ }
+ f = fix_new_exp (frag_now, (toP - frag_now->fr_literal), 4,
+ &r->exp, r->pcrel, r->code);
+ }
+ if (r->code == BFD_RELOC_ALPHA_GPDISP_LO16)
+ {
+ static bit_fixS cookie;
+ /* This'll make the range checking in write.c shut up. */
+ f->fx_bit_fixP = &cookie;
+ }
+ }
+ }
+}
+
+/* @@ Will a simple 0x8000 work here? If not, why not? */
+#define GP_ADJUSTMENT (0x8000 - 0x10)
+
+static void
+select_gp_value ()
+{
+ if (gp_value == 0)
+ /* Must be first time through -- pick a GP to use for this file. */
+ {
+ bfd_vma lita_vma, sdata_vma;
+ if (lita_sec)
+ lita_vma = bfd_get_section_vma (abfd, lita_sec);
+ else
+ lita_vma = 0;
+#if 0
+ if (sdata)
+ sdata_vma = bfd_get_section_vma (abfd, sdata);
+ else
+#endif
+ sdata = 0;
+
+ if (lita_vma == 0
+ /* Who knows which order they'll get laid out in? */
+ || (sdata_vma != 0 && sdata_vma < lita_vma))
+ gp_value = sdata_vma;
+ else
+ gp_value = lita_vma;
+
+ gp_value += GP_ADJUSTMENT;
+
+ S_SET_VALUE (gp, gp_value);
+
+#ifdef DEBUG1
+ printf ("Chose GP value of %lx\n", gp_value);
+#endif
+ bfd_set_gp_value (stdoutput, gp_value);
+ }
+}
+
+int
+alpha_force_relocation (f)
+ fixS *f;
+{
+ switch (f->fx_r_type)
+ {
+ case BFD_RELOC_ALPHA_GPDISP_HI16:
+ case BFD_RELOC_ALPHA_GPDISP_LO16:
+ case BFD_RELOC_ALPHA_LITERAL:
+ case BFD_RELOC_ALPHA_LITUSE:
+ case BFD_RELOC_GPREL32:
+ return 1;
+ case BFD_RELOC_ALPHA_HINT:
+ case BFD_RELOC_64:
+ case BFD_RELOC_32:
+ case BFD_RELOC_16:
+ case BFD_RELOC_8:
+ case BFD_RELOC_23_PCREL_S2:
+ case BFD_RELOC_14:
+ return 0;
+ default:
+ abort ();
+ return 0;
+ }
+}
+
+int
+alpha_fix_adjustable (f)
+ fixS *f;
+{
+ /* Are there any relocation types for which we must generate a reloc
+ but we can adjust the values contained within it? */
+ switch (f->fx_r_type)
+ {
+ case BFD_RELOC_ALPHA_GPDISP_HI16:
+ case BFD_RELOC_ALPHA_GPDISP_LO16:
+ return 0;
+ case BFD_RELOC_GPREL32:
+ return 1;
+ }
+ return !alpha_force_relocation (f);
+}
+
+int
+alpha_validate_fix (fixp, seg)
+ fixS *fixp;
+ segT seg;
+{
+ /* We must make sure we've got a good GP value if any relocations might
+ use it... */
+ if (gp_value == 0)
+ select_gp_value ();
+ return 0;
+}
+
+int
+alpha_frob_symbol (s)
+ symbolS *s;
+{
+ return 0;
+}
+
+unsigned long
+md_section_align (seg, size)
+ segT seg;
+ unsigned long size;
+{
+#ifdef OBJ_ECOFF
+ /* This should probably be handled within BFD, or by pulling the
+ number from BFD at least. */
+#define MIN 15
+ size += MIN;
+ size &= ~MIN;
+#endif
+ return size;
+}
+
+/* Add this thing to the .lita section and produce a LITERAL reloc referring
+ to it.
+
+ TODO:
+ Remove duplicates.
+ Set GP value properly, and have values in LITERAL references set
+ accordingly.
+ */
+
+static void
+load_symbol_address (reg, insn)
+ int reg;
+ struct alpha_it *insn;
+{
+ static symbolS *lita_sym;
+
+ int x;
+ addressT reloc_addr;
+ valueT retval;
+ char *p;
+ symbolS *sym;
+ valueT addend;
+
+ if (!lita_sym)
+ {
+ lita_sym = section_symbol (lita_sec);
+ S_CLEAR_EXTERNAL (lita_sym);
+ }
+
+ retval = add_to_literal_pool (insn->reloc[0].exp.X_add_symbol,
+ insn->reloc[0].exp.X_add_number,
+ lita_sec, 8);
+
+ /* @@ Get these numbers from GP setting. */
+ retval -= GP_ADJUSTMENT;
+
+ /* Now emit a LITERAL relocation for the original section. */
+ insn->reloc[0].exp.X_op = O_symbol;
+ insn->reloc[0].exp.X_add_symbol = lita_sym;
+ insn->reloc[0].exp.X_add_number = retval;
+ insn->reloc[0].code = BFD_RELOC_ALPHA_LITERAL;
+
+ if (retval == 0x8000)
+ /* Overflow? */
+ as_fatal ("overflow in literal (.lita) table");
+ x = retval;
+ insn->opcode = (0xa4000000 /* ldq */
+ | (reg << SA)
+ | (base_register << SB)
+ | (x & 0xffff));
+ note_gpreg (base_register);
+}
+
+/* To load an address with a single instruction,
+ emit a LITERAL reloc in this section, and a REFQUAD
+ for the .lita section, so that we'll be able to access
+ it via $gp:
+ lda REG, xx -> ldq REG, -32752(gp)
+ lda REG, xx+4 -> ldq REG, -32752(gp)
+ lda REG, 4(REG)
+
+ The offsets need to start near -0x8000, and the generated LITERAL
+ relocations should negate the offset. I don't completely grok the
+ scheme yet. */
+
+static int
+load_expression (reg, insn)
+ int reg;
+ struct alpha_it *insn;
+{
+ valueT addend;
+ int num_insns = 1;
+
+ addend = insn->reloc[0].exp.X_add_number;
+ insn->reloc[0].exp.X_add_number = 0;
+ load_symbol_address (reg, insn);
+ if (addend)
+ {
+ num_insns++;
+ {
+ valueT x = addend;
+ if (x & ~0x7fff != 0
+ && (x & ~0x7fff) + 0x8000 != 0)
+ {
+ as_bad ("assembler not prepared to handle constants >16 bits yet");
+ addend = 0;
+ }
+ }
+ insn[1].opcode = (0x20000000 /* lda */
+ | (reg << SA)
+ | (reg << SB)
+ | (addend & 0xffff));
+ insn[1].reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
+ insn[1].reloc[0].exp = lituse_basereg;
+ }
+ return num_insns;
+}
+
+static inline int
+getExpression (str, this_insn)
+ char *str;
+ struct alpha_it *this_insn;
+{
+ char *save_in;
+ segT seg;
+
+#if 0 /* Not converted to bfd yet, and I don't think we need them
+ for ECOFF. Re-adding a.out support will probably require
+ them though. */
+ static const struct am {
+ char *name;
+ bfd_reloc_code_real_type reloc;
+ } macro[] = {
+ { "hi", RELOC_48_63 },
+ { "lo", RELOC_0_15 },
+ { "ml", RELOC_16_31 },
+ { "mh", RELOC_32_47 },
+ { "uhi", RELOC_U_48_63 },
+ { "uml", RELOC_U_16_31 },
+ { "umh", RELOC_U_32_47 },
+ { 0, }
+ };
+
+ /* Handle macros: "%macroname(expr)" */
+ if (*str == '%')
+ {
+ struct am *m;
+ char *p, *q;
+
+ str++;
+ m = &macro[0];
+ while (q = m->name)
+ {
+ p = str;
+ while (*q && *p == *q)
+ p++, q++;
+ if (*q == 0)
+ break;
+ m++;
+ }
+ if (q)
+ {
+ str = p; /* keep the '(' */
+ this_insn->reloc = m->reloc;
+ }
+ }
+#endif
+
+ save_in = input_line_pointer;
+ input_line_pointer = str;
+
+ seg = expression (&this_insn->reloc[0].exp);
+ /* XXX validate seg and exp, make sure they're reasonable */
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+
+ return 0;
+}
+
+/* Note that for now, this function is called recursively. Some of the
+ macros defined as part of the assembly language are currently
+ rewritten as sequences of strings to be assembled. See, for example,
+ the handling of "divq".
+
+ For efficiency, this should be fixed someday. */
+static int
+alpha_ip (str, insns)
+ char *str;
+ struct alpha_it insns[];
+{
+ char *s;
+ const char *args;
+ char c;
+ unsigned long i;
+ struct alpha_opcode *pattern;
+ char *argsStart;
+ unsigned int opcode;
+ unsigned int mask;
+ int match = 0, num_gen = 1;
+ int comma = 0;
+
+ for (s = str;
+ islower (*s) || *s == '_' || *s == '/' || *s == '4' || *s == '8';
+ ++s)
+ ;
+ switch (*s)
+ {
+
+ case '\0':
+ break;
+
+ case ',':
+ comma = 1;
+
+ /*FALLTHROUGH*/
+
+ case ' ':
+ *s++ = '\0';
+ break;
+
+ default:
+ as_warn ("Unknown opcode: `%s'", str);
+ exit (1);
+ }
+ if ((pattern = (struct alpha_opcode *) hash_find (op_hash, str)) == NULL)
+ {
+ as_warn ("Unknown opcode: `%s'", str);
+ return -1;
+ }
+ if (comma)
+ *--s = ',';
+
+ argsStart = s;
+ for (;;)
+ {
+ opcode = pattern->match;
+ num_gen = 1;
+ memset (insns, 0, sizeof (*insns));
+ for (i = 0; i < MAX_RELOCS; i++)
+ insns[0].reloc[i].code = BFD_RELOC_NONE;
+ for (i = 1; i < MAX_INSNS; i++)
+ insns[i] = insns[0];
+
+ /* Build the opcode, checking as we go to make sure that the
+ operands match. */
+ for (args = pattern->args;; ++args)
+ {
+ switch (*args)
+ {
+
+ case '\0': /* end of args */
+ if (*s == '\0')
+ {
+ match = 1;
+ }
+ break;
+
+ case '+':
+ if (*s == '+')
+ {
+ ++s;
+ continue;
+ }
+ if (*s == '-')
+ {
+ continue;
+ }
+ break;
+
+ case '(': /* these must match exactly */
+ case ')':
+ case ',':
+ case ' ':
+ case '0':
+ if (*s++ == *args)
+ continue;
+ break;
+
+ case '1': /* next operand must be a register */
+ case '2':
+ case '3':
+ case 'r':
+ case 'R':
+ if (*s++ == '$')
+ {
+ switch (c = *s++)
+ {
+
+ case 'a': /* $at: as temporary */
+ if (*s++ != 't')
+ goto error;
+ mask = AT;
+ break;
+
+ case 'g': /* $gp: base register */
+ if (*s++ != 'p')
+ goto error;
+ mask = base_register;
+ break;
+
+ case 's': /* $sp: stack pointer */
+ if (*s++ != 'p')
+ goto error;
+ mask = SP;
+ break;
+
+
+ case 'r': /* any register */
+ if (!isdigit (c = *s++))
+ {
+ goto error;
+ }
+ /* FALLTHROUGH */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (isdigit (*s))
+ {
+ if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32)
+ {
+ goto error;
+ }
+ }
+ else
+ {
+ c -= '0';
+ }
+ if ((c == GP) && first_32bit_quadrant)
+ c = ZERO;
+
+ mask = c;
+ break;
+
+ default:
+ goto error;
+ }
+ note_gpreg (mask);
+ /* Got the register, now figure out where it goes in
+ the opcode. */
+ doregister:
+ switch (*args)
+ {
+
+ case '1':
+ case 'e':
+ opcode |= mask << SA;
+ continue;
+
+ case '2':
+ case 'f':
+ opcode |= mask << SB;
+ continue;
+
+ case '3':
+ case 'g':
+ opcode |= mask;
+ continue;
+
+ case 'r':
+ opcode |= (mask << SA) | mask;
+ continue;
+
+ case 'R': /* ra and rb are the same */
+ opcode |= (mask << SA) | (mask << SB);
+ continue;
+
+ case 'E':
+ opcode |= (mask << SA) | (mask << SB) | (mask);
+ continue;
+ }
+ }
+ break;
+
+ case 'e': /* next operand is a floating point register */
+ case 'f':
+ case 'g':
+ case 'E':
+ if (*s++ == '$' && *s++ == 'f' && isdigit (*s))
+ {
+ mask = *s++;
+ if (isdigit (*s))
+ {
+ mask = 10 * (mask - '0') + (*s++ - '0');
+ if (mask >= 32)
+ {
+ break;
+ }
+ }
+ else
+ {
+ mask -= '0';
+ }
+ note_fpreg (mask);
+ /* same encoding as gp registers */
+ goto doregister;
+ }
+ break;
+
+#if 0
+ case 'h': /* bits 16..31 */
+ insns[0].reloc = RELOC_16_31;
+ goto immediate;
+#endif
+
+ case 'l': /* bits 0..15 */
+ insns[0].reloc[0].code = BFD_RELOC_16;
+ goto immediate;
+
+ case 'L': /* 21 bit PC relative immediate */
+ insns[0].reloc[0].code = BFD_RELOC_23_PCREL_S2;
+ insns[0].reloc[0].pcrel = 1;
+ goto immediate;
+
+ case 'i': /* 14 bit immediate */
+ if (OPCODE (opcode) != 0x1a)
+ /* Not a jmp variant?? */
+ abort ();
+ else if (opcode & 0x8000)
+ /* ret or jsr_coroutine */
+ {
+ insns[0].reloc[0].code = BFD_RELOC_14;
+ insns[0].reloc[0].pcrel = 0;
+ }
+ else
+ /* jmp or jsr */
+ {
+ insns[0].reloc[0].code = BFD_RELOC_ALPHA_HINT;
+ insns[0].reloc[0].pcrel = 1;
+ }
+ goto immediate;
+
+ case 'b': /* 8 bit immediate */
+ insns[0].reloc[0].code = BFD_RELOC_8;
+ goto immediate;
+
+#if 0
+ case 't': /* 12 bit 0...11 */
+ insns[0].reloc = RELOC_0_12;
+ goto immediate;
+
+ case '8': /* 8 bit 0...7 */
+ insns[0].reloc = RELOC_0_8;
+ goto immediate;
+
+ case 'I': /* 26 bit immediate */
+ insns[0].reloc = RELOC_0_25;
+#else
+ case 't':
+ case '8':
+ case 'I':
+ abort ();
+#endif
+ /*FALLTHROUGH*/
+
+ immediate:
+ if (*s == ' ')
+ s++;
+ (void) getExpression (s, &insns[0]);
+ s = expr_end;
+ /* Handle overflow in certain instructions by converting
+ to other instructions. */
+ if (insns[0].reloc[0].code == BFD_RELOC_8
+ && insns[0].reloc[0].exp.X_op == O_constant
+ && (insns[0].reloc[0].exp.X_add_number < 0
+ || insns[0].reloc[0].exp.X_add_number > 0xff))
+ {
+ if (OPCODE (opcode) == 0x10
+ && (OP_FCN (opcode) == 0x00 /* addl */
+ || OP_FCN (opcode) == 0x40 /* addl/v */
+ || OP_FCN (opcode) == 0x20 /* addq */
+ || OP_FCN (opcode) == 0x60 /* addq/v */
+ || OP_FCN (opcode) == 0x09 /* subl */
+ || OP_FCN (opcode) == 0x49 /* subl/v */
+ || OP_FCN (opcode) == 0x29 /* subq */
+ || OP_FCN (opcode) == 0x69 /* subq/v */
+ || OP_FCN (opcode) == 0x02 /* s4addl */
+ || OP_FCN (opcode) == 0x22 /* s4addq */
+ || OP_FCN (opcode) == 0x0b /* s4subl */
+ || OP_FCN (opcode) == 0x2b /* s4subq */
+ || OP_FCN (opcode) == 0x12 /* s8addl */
+ || OP_FCN (opcode) == 0x32 /* s8addq */
+ || OP_FCN (opcode) == 0x1b /* s8subl */
+ || OP_FCN (opcode) == 0x3b /* s8subq */
+ )
+ /* Can we make it fit by negating? */
+ && -insns[0].reloc[0].exp.X_add_number < 0xff
+ && -insns[0].reloc[0].exp.X_add_number > 0)
+ {
+ opcode ^= 0x120; /* convert add<=>sub */
+ insns[0].reloc[0].exp.X_add_number *= -1;
+ }
+ else if (at_ok && macro_ok)
+ {
+ /* Constant value supplied, but it's too large. */
+ char expansion[64];
+ sprintf (expansion, "lda $%d,%d($%d)", AT,
+ insns[0].reloc[0].exp.X_add_number, ZERO);
+ md_assemble (expansion);
+ opcode |= 0x1000 /* use reg */ | (AT << SB);
+ insns[0].reloc[0].code = BFD_RELOC_NONE;
+ }
+ else
+ as_bad ("overflow in 8-bit literal field in `operate' format insn");
+ }
+ continue;
+
+ /* The following two.. take advantage of the fact that
+ opcode already contains most of what we need to know.
+ We just prepend to the instr an "ldah
+ $r,%ml(expr)($base)" and turn this one (done later
+ after we return) into something like "stq
+ $r,%lo(expr)(at)" or "ldq $r,%lo(expr)($r)".
+
+ NOTE: This can fail later on at link time if the
+ offset from $base actually turns out to be more than
+ 2**31 or 2**47 if use_large_offsets is set. */
+ case 'P': /* Addressing macros: PUT */
+ mask = AT; /* register 'at' */
+ /* fall through */
+
+ case 'G': /* Addressing macros: GET */
+ get_macro:
+ /* All it is missing is the expression, which is what we
+ will get now */
+
+ if (*s == ' ')
+ s++;
+ (void) getExpression (s, &insns[0]);
+ s = expr_end;
+
+ /* Must check for "lda ..,number" too */
+ if (insns[0].reloc[0].exp.X_op == O_big)
+ {
+ as_warn ("Sorry, not yet. Put bignums in .data section yourself.");
+ return -1;
+ }
+ if (insns[0].reloc[0].exp.X_op == O_constant)
+ {
+ /* This only handles 32bit numbers */
+ register int val = insns[0].reloc[0].exp.X_add_number;
+ register short sval;
+
+ insns[0].reloc[0].code = BFD_RELOC_NONE;
+ insns[1].reloc[0].code = BFD_RELOC_NONE;
+
+ sval = val;
+ if (0)
+ fprintf (stderr, "val %lx sval %lx\n", val, sval);
+ if ((sval != val) && (val & 0x8000))
+ {
+ val += 0x10000;
+ sval = val;
+ }
+
+ if (optnum && (sval == val))
+ {
+ /* optimize away the ldah */
+ num_gen = 1;
+ opcode |= (ZERO << SB) | (val & 0xffff);
+ }
+ else
+ {
+ num_gen = 2;
+ insns[1].opcode = opcode | (mask << SB) | (val & 0xffff);
+ opcode = 0x24000000 /*ldah*/ |
+ mask << SA | (ZERO << SB) |
+ ((val >> 16) & 0xffff);
+ }
+ }
+ else if (insns[0].reloc[0].exp.X_op == O_symbol)
+ {
+ unsigned long old_opcode = opcode;
+ int tmp_reg;
+
+ if (!macro_ok)
+ as_bad ("insn requires expansion but `nomacro' specified");
+ else if (*args == 'G')
+ tmp_reg = mask;
+ else if (!at_ok)
+ as_bad ("insn expansion requires AT use, but `noat' specified");
+ else
+ tmp_reg = AT;
+ num_gen = load_expression (tmp_reg, insns);
+ opcode = insns[0].opcode;
+ /* lda is opcode 8, 0x20000000 */
+ if (OPCODE (old_opcode) != 0x08)
+ {
+ struct alpha_it *i;
+ i = &insns[num_gen++];
+ i->reloc[0].code = BFD_RELOC_NONE;
+ i->opcode = old_opcode | (tmp_reg << SB);
+ }
+ }
+ else
+ {
+ /* Not a number */
+ num_gen = 2;
+ insns[1].reloc[0].exp = insns[0].reloc[0].exp;
+
+ /* Generate: ldah REG,x1(GP); OP ?,x0(REG) */
+
+ abort (); /* relocs need fixing */
+#if 0
+ insns[1].reloc = RELOC_0_15;
+ insns[1].opcode = opcode | mask << SB;
+
+ insns[0].reloc = RELOC_16_31;
+ opcode = 0x24000000 /*ldah*/ | mask << SA | (base_register << SB);
+#endif
+ }
+
+ continue;
+
+ /* Same failure modes as above, actually most of the
+ same code shared. */
+ case 'B': /* Builtins */
+ args++;
+ switch (*args)
+ {
+
+ case 'a': /* ldgp */
+
+ if (first_32bit_quadrant || no_mixed_code)
+ return -1;
+ switch (OUTPUT_FLAVOR)
+ {
+ case bfd_target_aout_flavour:
+ /* this is cmu's a.out version */
+ insns[0].reloc[0].code = BFD_RELOC_NONE;
+ /* generate "zap %r,0xf,%r" to take high 32 bits */
+ opcode |= 0x48001600 /* zap ?,#,?*/ | (0xf << SN);
+ break;
+ case bfd_target_ecoff_flavour:
+ /* Given "ldgp R1,N(R2)", turn it into something
+ like "ldah R1,###(R2) ; lda R1,###(R1)" with
+ appropriate constants and relocations. */
+ {
+ unsigned long r1, r2;
+ unsigned long addend = 0;
+
+ num_gen = 2;
+ r2 = mask;
+ r1 = opcode & 0x3f;
+ insns[0].reloc[0].code = BFD_RELOC_ALPHA_GPDISP_HI16;
+ insns[0].reloc[0].pcrel = 1;
+ insns[0].reloc[0].exp.X_op = O_symbol;
+ insns[0].reloc[0].exp.X_add_symbol = gp;
+ insns[0].reloc[0].exp.X_add_number = 0;
+ insns[0].opcode = (0x24000000 /* ldah */
+ | (r1 << SA)
+ | (r2 << SB));
+ insns[1].reloc[0].code = BFD_RELOC_ALPHA_GPDISP_LO16;
+ insns[1].reloc[0].exp.X_op = O_symbol;
+ insns[1].reloc[0].exp.X_add_symbol = gp;
+ insns[1].reloc[0].exp.X_add_number = 4;
+ insns[1].reloc[0].pcrel = 1;
+ insns[1].opcode = 0x20000000 | (r1 << SA) | (r1 << SB);
+ opcode = insns[0].opcode;
+ /* merge in addend */
+ insns[1].opcode |= addend & 0xffff;
+ insns[0].opcode |= ((addend >> 16)
+ + (addend & 0x8000 ? 1 : 0));
+ ecoff_set_gp_prolog_size (0);
+ }
+ break;
+ default:
+ abort ();
+ }
+ continue;
+
+
+ case 'b': /* setgp */
+ switch (OUTPUT_FLAVOR)
+ {
+ case bfd_target_aout_flavour:
+ /* generate "zap %r,0xf,$gp" to take high 32 bits */
+ opcode |= 0x48001600 /* zap ?,#,?*/
+ | (0xf << SN) | (base_register);
+ break;
+ default:
+ abort ();
+ }
+ continue;
+
+ case 'c': /* jsr $r,foo becomes
+ lda $27,foo
+ jsr $r,($27),foo
+ Register 27, t12, is used by convention
+ here. */
+ {
+ struct alpha_it *jsr;
+ expressionS etmp;
+ struct reloc_data *r;
+
+ /* We still have to parse the function name */
+ if (*s == ' ')
+ s++;
+ (void) getExpression (s, &insns[0]);
+ etmp = insns[0].reloc[0].exp;
+ s = expr_end;
+ num_gen = load_expression (PV, &insns[0]);
+ note_gpreg (PV);
+
+ jsr = &insns[num_gen++];
+ jsr->opcode = (0x68004000 /* jsr */
+ | (mask << SA)
+ | (PV << SB)
+ | 0);
+ if (num_gen == 2)
+ {
+ /* LITUSE wasn't emitted yet */
+ jsr->reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
+ jsr->reloc[0].exp = lituse_jsr;
+ r = &jsr->reloc[1];
+ }
+ else
+ r = &jsr->reloc[0];
+ r->exp = etmp;
+ r->code = BFD_RELOC_ALPHA_HINT;
+ r->pcrel = 1;
+ opcode = insns[0].opcode;
+ }
+ continue;
+
+ /* DIVISION and MODULUS. Yech.
+ Convert OP x,y,result
+ to mov x,t10
+ mov y,t11
+ jsr t9, __OP
+ mov t12,result
+
+ with appropriate optimizations if t10,t11,t12
+ are the registers specified by the compiler.
+ We are missing an obvious optimization
+ opportunity here; if the ldq generated by the
+ jsr assembly requires a cycle or two to make
+ the value available, initiating it before one
+ or two of the mov instructions would result in
+ faster execution. */
+ case '0': /* reml */
+ case '1': /* divl */
+ case '2': /* remq */
+ case '3': /* divq */
+ case '4': /* remlu */
+ case '5': /* divlu */
+ case '6': /* remqu */
+ case '7': /* divqu */
+ {
+ static char func[8][6] = {
+ "reml", "divl", "remq", "divq",
+ "remlu", "divlu", "remqu", "divqu"
+ };
+ char expansion[64];
+ int reg;
+
+ /* All regs parsed, in opcode */
+
+ /* Do the expansions, one instr at a time */
+
+ reg = (opcode >> SA) & 31;
+ if (reg != T10)
+ {
+ /* x->t10 */
+ sprintf (expansion, "mov $%d,$%d", reg, T10);
+ md_assemble (expansion);
+ }
+ reg = (opcode >> SB) & 31;
+ if (reg == T10)
+ /* we already overwrote it! */
+ abort ();
+ else if (reg != T11)
+ {
+ /* y->t11 */
+ sprintf (expansion, "mov $%d,$%d", reg, T11);
+ md_assemble (expansion);
+ }
+ sprintf (expansion, "lda $%d,__%s", PV, func[*args - '0']);
+ md_assemble (expansion);
+ sprintf (expansion, "jsr $%d,($%d),__%s", T9, PV,
+ func[*args - '0']);
+ md_assemble (expansion);
+#if 0 /* huh? */
+ if (!first_32bit_quadrant)
+ {
+ sprintf (expansion,
+ "zap $%d,0xf,$%d",
+ T9, base_register);
+ md_assemble (expansion);
+ }
+#endif
+ sprintf (expansion, "ldgp $%d,0($%d)",
+ base_register, T9);
+ md_assemble (expansion);
+
+ /* Use insns[0] to get at the result */
+ if ((reg = (opcode & 31)) != PV)
+ opcode = (0x47e00400 /* or zero,zero,zero */
+ | (PV << SB)
+ | reg /* Rc */ ); /* pv->z */
+ else
+ num_gen = 0;
+ }
+ continue;
+ }
+ /* fall through */
+
+ default:
+ abort ();
+ }
+ break;
+ }
+ error:
+ if (match == 0)
+ {
+ /* Args don't match. */
+ if (&pattern[1] - alpha_opcodes < NUMOPCODES
+ && !strcmp (pattern->name, pattern[1].name))
+ {
+ ++pattern;
+ s = argsStart;
+ continue;
+ }
+ else
+ {
+ as_warn ("Illegal operands");
+ return -1;
+ }
+ }
+ else
+ {
+ /* Args match, see if a float instructions and -nofloats */
+ if (nofloats && pattern->isa_float)
+ return -1;
+ }
+ break;
+ }
+
+ insns[0].opcode = opcode;
+ return num_gen;
+}
+
+/* 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 (), *vax_md_atof ();
+
+ switch (type)
+ {
+ /* VAX floats */
+ case 'G':
+ type = 'g';
+ case 'F':
+ case 'D':
+ return vax_md_atof (type, litP, sizeP);
+
+ /* IEEE floats */
+ case 'f':
+ prec = 2;
+ break;
+
+ case 'd':
+ 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 - 1; prec--;)
+ {
+ md_number_to_chars (litP, (long) (*wordP--), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+
+ return 0;
+}
+
+void
+md_bignum_to_chars (buf, bignum, nchars)
+ char *buf;
+ LITTLENUM_TYPE *bignum;
+ int nchars;
+{
+ while (nchars)
+ {
+ LITTLENUM_TYPE work = *bignum++;
+ int nb = CHARS_PER_LITTLENUM;
+
+ do
+ {
+ *buf++ = work & ((1 << BITS_PER_CHAR) - 1);
+ if (--nchars == 0)
+ return;
+ work >>= BITS_PER_CHAR;
+ }
+ while (--nb);
+ }
+}
+
+int
+md_parse_option (argP, cntP, vecP)
+ char **argP;
+ int *cntP;
+ char ***vecP;
+{
+ if (**argP == 'F')
+ {
+ nofloats = 1;
+ return 1;
+ }
+#if 0 /* I have no idea if this stuff would work any more. And it's
+ probably not right for ECOFF anyways. */
+ /* Use base-register addressing, e.g. PIC code */
+ if (**argP == 'B')
+ {
+ if (first_32bit_quadrant)
+ {
+ first_32bit_quadrant = 0;
+ base_register = GP;
+ }
+ else
+ {
+ first_32bit_quadrant = 1;
+ base_register = ZERO;
+ }
+ if (argP[0][1] == 'k')
+ no_mixed_code = 1;
+ argP[0][1] = 0;
+ return 1;
+ }
+#endif
+ if (!strcmp (*argP, "nocpp"))
+ {
+ *argP += 5;
+ return 1;
+ }
+ return 0;
+}
+
+static void
+s_proc (is_static)
+{
+ /* XXXX Align to cache linesize XXXXX */
+ char *name;
+ char c;
+ char *p;
+ symbolS *symbolP;
+ int temp;
+
+ /* Takes ".proc name,nargs" */
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = input_line_pointer;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ *p = 0;
+ as_warn ("Expected comma after name \"%s\"", name);
+ *p = c;
+ temp = 0;
+ ignore_rest_of_line ();
+ }
+ else
+ {
+ input_line_pointer++;
+ temp = get_absolute_expression ();
+ }
+ /* symbolP->sy_other = (signed char) temp; */
+ as_warn ("unhandled: .proc %s,%d", name, temp);
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_alpha_set (x)
+ int x;
+{
+ char *name = input_line_pointer, ch, *s;
+ int yesno = 1;
+
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ input_line_pointer++;
+ ch = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ s = name;
+ if (s[0] == 'n' && s[1] == 'o')
+ {
+ yesno = 0;
+ s += 2;
+ }
+ if (!strcmp ("reorder", s))
+ /* ignore */ ;
+ else if (!strcmp ("at", s))
+ at_ok = yesno;
+ else if (!strcmp ("macro", s))
+ macro_ok = yesno;
+ else
+ as_warn ("Tried to set unrecognized symbol: %s", name);
+ *input_line_pointer = ch;
+ demand_empty_rest_of_line ();
+}
+
+/* @@ Is this right?? */
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_ALPHA_GPDISP_HI16:
+ case BFD_RELOC_ALPHA_GPDISP_LO16:
+ return addr;
+ default:
+ return fixP->fx_size + addr;
+ }
+}
+
+int
+alpha_do_align (n, fill)
+ int n;
+ char *fill;
+{
+ if (!fill
+ && (now_seg == text_section
+ || !strcmp (now_seg->name, ".init")
+ || !strcmp (now_seg->name, ".fini")))
+ {
+ static const char nop_pattern[] = { 0x1f, 0x04, 0xff, 0x47 };
+ frag_align_pattern (n, nop_pattern, sizeof (nop_pattern));
+ return 1;
+ }
+ return 0;
+}
+
+int
+md_apply_fix (fixP, valueP)
+ fixS *fixP;
+ valueT *valueP;
+{
+ valueT value;
+ int size;
+ valueT addend;
+ char *p = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ value = *valueP;
+
+ switch (fixP->fx_r_type)
+ {
+ /* The GPDISP relocations are processed internally with a symbol
+ referring to the current function; we need to drop in a value
+ which, when added to the address of the start of the function,
+ gives the desired GP. */
+ case BFD_RELOC_ALPHA_GPDISP_HI16:
+ case BFD_RELOC_ALPHA_GPDISP_LO16:
+ addend = value;
+ if (fixP->fx_r_type == BFD_RELOC_ALPHA_GPDISP_HI16)
+ {
+ assert (fixP->fx_next->fx_r_type == BFD_RELOC_ALPHA_GPDISP_LO16);
+#ifdef DEBUG1
+ printf ("hi16: ");
+ fprintf_vma (stdout, addend);
+ printf ("\n");
+#endif
+ if (addend & 0x8000)
+ addend += 0x10000;
+ addend >>= 16;
+ fixP->fx_offset = 4; /* @@ Compute this using fx_next. */
+ }
+ else
+ {
+#ifdef DEBUG1
+ printf ("lo16: ");
+ fprintf_vma (stdout, addend);
+ printf ("\n");
+#endif
+ addend &= 0xffff;
+ fixP->fx_offset = 0;
+ }
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ addend, 2);
+ fixP->fx_addsy = section_symbol (absolute_section);
+ fixP->fx_offset += fixP->fx_frag->fr_address + fixP->fx_where;
+ break;
+
+ case BFD_RELOC_8:
+ /* Write 8 bits, shifted left 13 bit positions. */
+ value &= 0xff;
+ p++;
+ *p &= 0x1f;
+ *p |= (value << 5) & 0xe0;
+ value >>= 3;
+ p++;
+ *p &= 0xe0;
+ *p |= value;
+ value >>= 5;
+ fixP->fx_done = 1;
+ check_zov:
+ if (value != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ "overflow in type-%d reloc", (int) fixP->fx_r_type);
+ return 3;
+
+ case BFD_RELOC_32:
+ case BFD_RELOC_64:
+ return 42;
+ case BFD_RELOC_16:
+ /* Don't want overflow checking. */
+ size = 2;
+ do_it:
+ if (fixP->fx_pcrel == 0
+ && fixP->fx_addsy == 0)
+ {
+ md_number_to_chars (p, value, size);
+ /* @@ Overflow checks?? */
+ goto done;
+ }
+ break;
+
+ case BFD_RELOC_14:
+ if (fixP->fx_addsy != 0
+ && fixP->fx_addsy->bsym->section != absolute_section)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ "ret/jsr_coroutine requires constant in displacement field");
+ else if (value >> 14 != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ "overflow in 14-bit operand field of ret or jsr_coroutine");
+ *p++ = value & 0xff;
+ value >>= 8;
+ *p = (*p & 0xc0) | (value & 0x3f);
+ goto done;
+
+ case BFD_RELOC_23_PCREL_S2:
+ /* Write 21 bits only. */
+ value >>= 2;
+ *p++ = value & 0xff;
+ value >>= 8;
+ *p++ = value & 0xff;
+ value >>= 8;
+ *p &= 0xe0;
+ *p |= (value & 0x1f);
+ goto done;
+
+ case BFD_RELOC_ALPHA_LITERAL:
+ case BFD_RELOC_ALPHA_LITUSE:
+ return 2;
+
+ case BFD_RELOC_GPREL32:
+ assert (fixP->fx_subsy == gp);
+ value = - gp_value; /* huh? this works... */
+ fixP->fx_subsy = 0;
+ md_number_to_chars (p, value, 4);
+ break;
+
+ case BFD_RELOC_ALPHA_HINT:
+ if (fixP->fx_addsy == 0 && fixP->fx_pcrel == 0)
+ {
+ size = 2;
+ goto do_it;
+ }
+ return 2;
+
+ default:
+ as_fatal ("unknown relocation type %d?", fixP->fx_r_type);
+ return 9;
+ }
+
+ if (fixP->fx_addsy == 0 && fixP->fx_pcrel == 0)
+ {
+ printf ("type %d reloc done?\n", fixP->fx_r_type);
+ done:
+ fixP->fx_done = 1;
+ return 42;
+ }
+
+ return 0x12345678;
+}
+
+void
+alpha_end ()
+{
+ /* $zero and $f31 are read-only */
+ alpha_gprmask &= ~(1L << 31);
+ alpha_fprmask &= ~(1L << 31);
+}
+
+/* The Alpha has support for some VAX floating point types, as well as for
+ IEEE floating point. We consider IEEE to be the primary floating point
+ format, and sneak in the VAX floating point support here. */
+#define md_atof vax_md_atof
+#include "config/atof-vax.c"