(tc_gen_reloc): Use bfd_get_reloc_code_name in error message.
[binutils-gdb.git] / gas / config / tc-alpha.c
index 8aa087fc48c2485e06a609244ebd67ab5398d289..2b82dd7e3d151e0e19bd21150814c986e5839e79 100644 (file)
@@ -54,6 +54,8 @@
  *    Date:    Jan 1993
  */
 
+#include <ctype.h>
+
 #include "as.h"
 #include "alpha-opcode.h"
 #include "subsegs.h"
 /* @@ Will a simple 0x8000 work here?  If not, why not?  */
 #define GP_ADJUSTMENT  (0x8000 - 0x10)
 
+/* Which machine type is this?  Currently stores an integer for the
+   model, one of: 21064, 21066, 21164.  */
+static unsigned long machine;
+
 /* 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];
@@ -83,7 +89,7 @@ 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;
+static const reloc_howto_type *gpdisp_hi16_howto;
 
 /* These are exported to ECOFF code.  */
 unsigned long alpha_gprmask, alpha_fprmask;
@@ -104,10 +110,28 @@ extern void s_globl (), s_long (), s_short (), s_space (), cons (), s_text (),
   s_data (), float_cons ();
 
 /* Static functions, needing forward declarations.  */
-static void s_mask (), s_base (), s_proc (), s_alpha_set ();
+static void s_base (), s_proc (), s_alpha_set ();
 static void s_gprel32 (), s_rdata (), s_sdata (), s_alpha_comm ();
 static int alpha_ip ();
 
+static void emit_unaligned_io PARAMS ((char *, int, valueT, int));
+static void emit_load_unal PARAMS ((int, valueT, int));
+static void emit_store_unal PARAMS ((int, valueT, int));
+static void emit_byte_manip_r PARAMS ((char *, int, int, int, int, int));
+static void emit_extract_r PARAMS ((int, int, int, int, int));
+static void emit_insert_r PARAMS ((int, int, int, int, int));
+static void emit_mask_r PARAMS ((int, int, int, int, int));
+static void emit_sign_extend PARAMS ((int, int));
+static void emit_bis_r PARAMS ((int, int, int));
+static int build_mem PARAMS ((int, int, int, bfd_signed_vma));
+static int build_operate_n PARAMS ((int, int, int, int, int));
+static void emit_sll_n PARAMS ((int, int, int));
+static void emit_ldah_num PARAMS ((int, bfd_vma, int));
+static void emit_addq_r PARAMS ((int, int, int));
+static void emit_lda_n PARAMS ((int, bfd_vma, int));
+static void emit_add64 PARAMS ((int, int, bfd_vma));
+static int in_range PARAMS ((bfd_vma, int, int));
+
 const pseudo_typeS md_pseudo_table[] =
 {
   {"common", s_comm, 0},       /* is this used? */
@@ -148,7 +172,8 @@ const pseudo_typeS md_pseudo_table[] =
 #define        T9      23
 #define        T10     24
 #define        T11     25
-#define RA     26
+#define T12    26
+#define RA     26              /* note: same as T12 */
 #define        PV      27
 #define        AT      28
 #define        GP      29
@@ -178,7 +203,7 @@ const char comment_chars[] = "#";
 /* 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 */
+/* Also note that C style comments are always recognized.  */
 const char line_comment_chars[] = "#!";
 
 /* Chars that can be used to separate mant from exp in floating point nums */
@@ -188,6 +213,7 @@ const char line_separator_chars[1];
 
 /* Chars that mean this number is a floating point constant, as in
    "0f12.456" or "0d1.2345e12".  */
+/* @@ Do all of these really get used on the alpha??  */
 char FLT_CHARS[] = "rRsSfFdDxXpP";
 
 /* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
@@ -220,7 +246,6 @@ int
 tc_get_register (frame)
      int frame;
 {
-  int reg;
   int framereg = SP;
 
   SKIP_WHITESPACE ();
@@ -344,7 +369,7 @@ tc_gen_reloc (sec, fixp)
   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)
+  if (fixp->fx_r_type > BFD_RELOC_UNUSED)
     abort ();
 
   if (fixp->fx_r_type == BFD_RELOC_ALPHA_GPDISP_HI16)
@@ -359,8 +384,8 @@ tc_gen_reloc (sec, fixp)
   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 ();
+      as_fatal ("internal error? cannot generate `%s' relocation",
+               bfd_get_reloc_code_name (fixp->fx_r_type));
     }
   assert (!fixp->fx_pcrel == !reloc->howto->pc_relative);
 
@@ -400,11 +425,47 @@ s_base ()
   if (base_register < 0 || base_register > 31)
     {
       base_register = GP;
-      as_warn ("Bad base register, using $r.", base_register);
+      as_warn ("Bad base register, using $%d.", base_register);
     }
   demand_empty_rest_of_line ();
 }
 
+static int in_range (val, nbits, unsignedness)
+     bfd_vma val;
+     int nbits, unsignedness;
+{
+  /* Look at top bit of value that would be stored, figure out how it
+     would be extended by the hardware, and see if that matches the
+     original supplied value.  */
+  bfd_vma mask;
+  bfd_vma one = 1;
+  bfd_vma top_bit, stored_value, missing_bits;
+
+  mask = (one << nbits) - 1;
+  stored_value = val & mask;
+  top_bit = stored_value & (one << nbits - 1);
+  missing_bits = val & ~mask;
+  if (unsignedness)
+    {
+      return missing_bits == 0;
+    }
+  else
+    {
+      /* will sign-extend */
+      if (top_bit)
+       {
+         /* all remaining bits beyond mask should be one */
+         missing_bits |= mask;
+         return missing_bits + 1 == 0;
+       }
+      else
+       {
+         /* all other bits should be zero */
+         return missing_bits == 0;
+       }
+    }
+}
+
 static void
 s_gprel32 ()
 {
@@ -482,74 +543,101 @@ get_lit4_offset (val)
   return retval;
 }
 
-/* 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;
+#define load_insn(NAME, OP)    (hash_insert (op_hash, (NAME), (PTR) (OP)))
 
-  op_hash = hash_new ();
+static void
+load_insn_table (ops, size)
+     struct alpha_opcode *ops;
+     int size;
+{
+  struct alpha_opcode *end = ops + size;
+  struct alpha_opcode *op;
+  const char *name;
 
-  for (i = 0; i < NUMOPCODES; )
+  for (op = ops; op < end; )
     {
-      const char *name = alpha_opcodes[i].name;
-      retval = hash_insert (op_hash, name, (PTR) & alpha_opcodes[i]);
+      const char *retval;
+
+      name = op->name;
+
+      retval = load_insn (op->name, op);
       if (retval)
-       {
-         as_bad ("internal error: can't hash opcode `%s': %s",
-                 alpha_opcodes[i].name, retval);
-         lose = 1;
-       }
+       as_fatal ("internal error: can't hash opcode `%s': %s",
+                 op->name, retval);
+
       do
-       ++i;
-      while (i < NUMOPCODES
-            && (alpha_opcodes[i].name == name
-                || !strcmp (alpha_opcodes[i].name, name)));
+       op++;
+      while (op < end
+            && (op->name == name
+                || !strcmp (op->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; )
+  for (op = ops; op < end; )
     {
-      const char *name = alpha_opcodes[i].name;
+      name = op->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
-           }
+         char *name2, *p;
+         const char *q;
+
+         name2 = xmalloc (strlen (name));
+         p = name2;
+         q = name;
+
+         while (*q)
+           if (*q == '/')
+             q++;
+           else
+             *p++ = *q++;
+         *p = 0;
+         /* Ignore failures -- the opcode table does duplicate some
+            variants in different forms, like "hw_stq" and "hw_st/q".
+            Maybe the variants can be eliminated, and this error checking
+            restored.  */
+         load_insn (name2, op);
        }
+
       do
-       ++i;
-      while (i < NUMOPCODES
-            && (alpha_opcodes[i].name == name
-                || !strcmp (alpha_opcodes[i].name, name)));
+       op++;
+      while (op < end
+            && (op->name == name
+                || !strcmp (op->name, name)));
     }
+}
+
+static struct alpha_it clear_insn;
+
+/* 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, that can be determined before arguments are parsed.  */
+void
+md_begin ()
+{
+  int i;
+
+  op_hash = hash_new ();
+  load_insn_table (alpha_opcodes, NUMOPCODES);
 
+  /* Default to 21064 PAL instructions.  */
+  if (machine == 0)
+    machine = 21064;
 
-  if (lose)
-    as_fatal ("Broken assembler.  No assembly attempted.");
+  switch (machine)
+    {
+    case 21064:
+    case 21066:
+      load_insn_table (alpha_pal21064_opcodes, NUM21064OPCODES);
+      break;
+    case 21164:
+      load_insn_table (alpha_pal21164_opcodes, NUM21164OPCODES);
+      break;
+    default:
+      as_fatal ("palcode set unknown (internal error)");
+    }
 
   lituse_basereg.X_op = O_constant;
   lituse_basereg.X_add_number = 1;
@@ -563,18 +651,57 @@ md_begin ()
   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);
+  gp = symbol_create ("<GP value>", lita_sec, 0x8000, &zero_address_frag);
+
+  memset (&clear_insn, 0, sizeof (clear_insn));
+  for (i = 0; i < MAX_RELOCS; i++)
+    clear_insn.reloc[i].code = BFD_RELOC_NONE;
 }
 
 int optnum = 1;
 
+static void
+emit_insn (insn)
+     struct alpha_it *insn;
+{
+  char *toP;
+  int j;
+
+  toP = frag_more (4);
+
+  /* put out the opcode */
+  md_number_to_chars (toP, insn->opcode, 4);
+
+  /* put out the symbol-dependent stuff */
+  for (j = 0; j < MAX_RELOCS; j++)
+    {
+      struct reloc_data *r = &insn->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;
+       }
+    }
+}
+
 void
 md_assemble (str)
      char *str;
 {
-  char *toP;
-  int i, j, count;
+  int i, count;
 #define        MAX_INSNS       5
   struct alpha_it insns[MAX_INSNS];
 
@@ -583,36 +710,7 @@ md_assemble (str)
     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;
-           }
-       }
-    }
+    emit_insn (&insns[i]);
 }
 
 static inline void
@@ -672,6 +770,7 @@ alpha_force_relocation (f)
     case BFD_RELOC_8:
     case BFD_RELOC_23_PCREL_S2:
     case BFD_RELOC_14:
+    case BFD_RELOC_26:
       return 0;
     default:
       abort ();
@@ -692,8 +791,10 @@ alpha_fix_adjustable (f)
       return 0;
     case BFD_RELOC_GPREL32:
       return 1;
+    default:
+      return !alpha_force_relocation (f);
     }
-  return !alpha_force_relocation (f);
+  /*NOTREACHED*/
 }
 
 valueT
@@ -712,13 +813,11 @@ md_section_align (seg, size)
 }
 
 /* Add this thing to the .lita section and produce a LITERAL reloc referring
-   to it.
+   to it.  */
 
-   TODO:
-   Remove duplicates.
-   Set GP value properly, and have values in LITERAL references set
-   accordingly.
-   */
+/* Are we currently eligible to emit a LITUSE reloc for the literal
+   references just generated?  */
+static int lituse_pending;
 
 static void
 load_symbol_address (reg, insn)
@@ -728,11 +827,7 @@ load_symbol_address (reg, insn)
   static symbolS *lita_sym;
 
   int x;
-  addressT reloc_addr;
   valueT retval;
-  char *p;
-  symbolS *sym;
-  valueT addend;
 
   if (!lita_sym)
     {
@@ -749,6 +844,7 @@ load_symbol_address (reg, insn)
   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;
+  lituse_pending = 1;
 
   if (retval == 0x8000)
     /* Overflow? */
@@ -784,7 +880,7 @@ load_expression (reg, insn)
      int reg;
      struct alpha_it *insn;
 {
-  valueT addend;
+  valueT addend, addendhi, addendlo;
   int num_insns = 1;
 
   if (insn->reloc[0].exp.X_add_symbol->bsym->flags & BSF_SECTION_SYM)
@@ -799,22 +895,40 @@ load_expression (reg, insn)
   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;
+      if ((addend & ~0x7fffffff) != 0
+         && (addend & ~0x7fffffff) + 0x80000000 != 0)
+       {
+         as_bad ("assembler not prepared to handle constants >32 bits yet");
+         addend = 0;
+       }
+      addendlo = addend & 0xffff;
+      addend -= addendlo;
+      addendhi = addend >> 16;
+      if (addendlo & 0x8000)
+       addendhi++;
+      /* It appears that the BASEREG LITUSE reloc should not be used on
+        an LDAH instruction.  */
+      if (addendlo)
+       {
+         insn[1].opcode = (0x20000000  /* lda */
+                           | (reg << SA)
+                           | (reg << SB)
+                           | (addendlo & 0xffff));
+         insn[1].reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
+         insn[1].reloc[0].exp = lituse_basereg;
+         num_insns++;
+       }
+      if (addendhi)
+       {
+         insn[num_insns].opcode = (0x24000000
+                                   | (reg << SA)
+                                   | (reg << SB)
+                                   | (addendhi & 0xffff));
+         num_insns++;
+       }
+      if (num_insns == 1)
+       abort ();
+      lituse_pending = 0;
     }
   return num_insns;
 }
@@ -878,6 +992,216 @@ getExpression (str, this_insn)
   input_line_pointer = save_in;
 }
 
+static void
+emit_unaligned_io (dir, addr_reg, addr_offset, reg)
+     char *dir;
+     int addr_reg, reg;
+     valueT addr_offset;
+{
+  char buf[90];
+  sprintf (buf, "%sq_u $%d,%ld($%d)", dir, reg, (long) addr_offset, addr_reg);
+  md_assemble (buf);
+}
+
+static void
+emit_load_unal (addr_reg, addr_offset, reg)
+     int addr_reg, reg;
+     valueT addr_offset;
+{
+  emit_unaligned_io ("ld", addr_reg, addr_offset, reg);
+}
+
+static void
+emit_store_unal (addr_reg, addr_offset, reg)
+     int addr_reg, reg;
+     valueT addr_offset;
+{
+  emit_unaligned_io ("st", addr_reg, addr_offset, reg);
+}
+
+static void
+emit_byte_manip_r (op, in, mask, out, mode, which)
+     char *op;
+     int in, mask, out, mode, which;
+{
+  char buf[90];
+  sprintf (buf, "%s%c%c $%d,$%d,$%d", op, mode, which, in, mask, out);
+  md_assemble (buf);
+}
+
+static void
+emit_extract_r (in, mask, out, mode, which)
+     int in, mask, out, mode, which;
+{
+  emit_byte_manip_r ("ext", in, mask, out, mode, which);
+}
+
+static void
+emit_insert_r (in, mask, out, mode, which)
+     int in, mask, out, mode, which;
+{
+  emit_byte_manip_r ("ins", in, mask, out, mode, which);
+}
+
+static void
+emit_mask_r (in, mask, out, mode, which)
+     int in, mask, out, mode, which;
+{
+  emit_byte_manip_r ("msk", in, mask, out, mode, which);
+}
+
+static void
+emit_sign_extend (reg, size)
+     int reg, size;
+{
+  char buf[90];
+  sprintf (buf, "sll $%d,0x%x,$%d", reg, 64 - size, reg);
+  md_assemble (buf);
+  sprintf (buf, "sra $%d,0x%x,$%d", reg, 64 - size, reg);
+  md_assemble (buf);
+}
+
+static void
+emit_bis_r (in1, in2, out)
+     int in1, in2, out;
+{
+  char buf[90];
+  sprintf (buf, "bis $%d,$%d,$%d", in1, in2, out);
+  md_assemble (buf);
+}
+
+static int
+build_mem (opc, ra, rb, disp)
+     int opc, ra, rb;
+     bfd_signed_vma disp;
+{
+  if ((disp >> 15) != 0
+      && (disp >> 15) + 1 != 0)
+    abort ();
+  return ((opc << 26) | (ra << SA) | (rb << SB) | (disp & 0xffff));
+}
+
+static int
+build_operate_n (opc, fn, ra, lit, rc)
+     int opc, fn, ra, rc;
+     int lit;
+{
+  if (lit & ~0xff)
+    abort ();
+  return ((opc << 26) | (fn << 5) | (ra << SA) | (lit << SN) | (1 << 12) | (rc << SC));
+}
+
+static void
+emit_sll_n (dest, disp, src)
+     int dest, disp, src;
+{
+  struct alpha_it insn = clear_insn;
+  insn.opcode = build_operate_n (0x12, 0x39, src, disp, dest);
+  emit_insn (&insn);
+}
+
+static void
+emit_ldah_num (dest, addend, src)
+     int dest, src;
+     bfd_vma addend;
+{
+  struct alpha_it insn = clear_insn;
+  insn.opcode = build_mem (0x09, dest, src, addend);
+  emit_insn (&insn);
+}
+
+static void
+emit_addq_r (in1, in2, out)
+     int in1, in2, out;
+{
+  struct alpha_it insn = clear_insn;
+  insn.opcode = 0x40000400 | (in1 << SA) | (in2 << SB) | (out << SC);
+  emit_insn (&insn);
+}
+
+static void
+emit_lda_n (dest, addend, src)
+     int dest, src;
+     bfd_vma addend;
+{
+  struct alpha_it insn = clear_insn;
+  insn.opcode = build_mem (0x08, dest, src, addend);
+  emit_insn (&insn);
+}
+
+static void
+emit_add64 (in, out, num)
+     int in, out;
+     bfd_vma num;
+{
+  bfd_signed_vma snum = num;
+
+  if (in_range (num, 16, 0))
+    {
+      emit_lda_n (out, num, in);
+      return;
+    }
+  if ((num & 0xffff) == 0
+      && in == ZERO
+      && in_range (snum >> 16, 16, 0))
+    {
+      emit_ldah_num (out, snum >> 16, in);
+      return;
+    }
+  /* I'm not sure this one is getting invoked when it could.  */
+  if ((num & 1) == 0 && in == ZERO)
+    {
+      if (in_range (snum >> 1, 16, 0))
+       {
+         emit_lda_n (out, snum >> 1, in);
+         emit_addq_r (out, out, out);
+         return;
+       }
+      else if (num & 0x1fffe == 0
+              && in_range (snum >> 17, 16, 0))
+       {
+         emit_ldah_num (out, snum >> 17, in);
+         emit_addq_r (out, out, out);
+         return;
+       }
+    }
+  if (in_range (num, 32, 0))
+    {
+      bfd_vma lo = num & 0xffff;
+      if (lo & 0x8000)
+       lo -= 0x10000;
+      num -= lo;
+      emit_ldah_num (out, snum >> 16, in);
+      if (lo)
+       emit_lda_n (out, lo, out);
+      return;
+    }
+
+  if (in != ZERO && in != AT && out != AT && at_ok)
+    {
+      emit_add64 (ZERO, AT, num);
+      emit_addq_r (AT, in, out);
+      return;
+    }
+
+  if (in != ZERO)
+    as_bad ("load expression too complex to expand");
+
+  /* Could check also for loading 16- or 32-bit value and shifting by
+     arbitrary displacement.  */
+
+  {
+    bfd_vma lo = snum & 0xffffffff;
+    if (lo & 0x80000000)
+      lo -= ((bfd_vma)0x10000000 << 4);
+    snum -= lo;
+    emit_add64 (ZERO, out, snum >> 32);
+    emit_sll_n (out, 32, out);
+    if (lo != 0)
+      emit_add64 (out, out, lo);
+  }
+}
+
 /* Note that for now, this function is called recursively (by way of
    calling md_assemble again).  Some of the macros defined as part of
    the assembly language are currently rewritten as sequences of
@@ -936,11 +1260,8 @@ alpha_ip (str, insns)
     {
       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];
+      for (i = 0; i < MAX_INSNS; i++)
+       insns[i] = clear_insn;
 
       /* Build the opcode, checking as we go to make sure that the
         operands match.  */
@@ -1141,6 +1462,10 @@ alpha_ip (str, insns)
              insns[0].reloc[0].code = BFD_RELOC_8;
              goto immediate;
 
+           case 'I':           /* 26 bit immediate, for PALcode */
+             insns[0].reloc[0].code = BFD_RELOC_26;
+             goto immediate;
+
 #if 0
            case 't':           /* 12 bit 0...11 */
              insns[0].reloc = RELOC_0_12;
@@ -1155,7 +1480,6 @@ alpha_ip (str, insns)
 #else
            case 't':
            case '8':
-           case 'I':
              abort ();
 #endif
              /*FALLTHROUGH*/
@@ -1200,21 +1524,52 @@ alpha_ip (str, insns)
                  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);
+                     emit_add64 (ZERO, AT,
+                                 insns[0].reloc[0].exp.X_add_number);
+                     opcode &= ~ 0x1000;
+                     opcode |= (AT << SB);
                      insns[0].reloc[0].code = BFD_RELOC_NONE;
                    }
                  else
                    as_bad ("overflow in 8-bit literal field in `operate' format insn");
                }
+             else if (insns[0].reloc[0].code == BFD_RELOC_16
+                      && insns[0].reloc[0].exp.X_op == O_constant
+                      && !in_range (insns[0].reloc[0].exp.X_add_number,
+                                    16, 0))
+               {
+                 bfd_vma val = insns[0].reloc[0].exp.X_add_number;
+                 if (OPCODE (opcode) == 0x08)
+                   {
+                     emit_add64 (ZERO, AT, val);
+                     opcode &= ~0x1000;
+                     opcode |= (AT << SB);
+                     insns[0].reloc[0].code = BFD_RELOC_NONE;
+                   }
+                 else if (OPCODE (opcode) == 0x09
+                          && in_range (val >> 16, 16, 0))
+                   {
+                     /* ldah with high operand - convert to low */
+                     insns[0].reloc[0].exp.X_add_number >>= 16;
+                   }
+                 else
+                   as_bad ("I don't know how to handle 32+ bit constants here yet, sorry.");
+               }
+             else if (insns[0].reloc[0].code == BFD_RELOC_32
+                      && insns[0].reloc[0].exp.X_op == O_constant)
+               {
+                 bfd_vma val = insns[0].reloc[0].exp.X_add_number;
+                 bfd_signed_vma sval = val;
+                 if (val >> 32 != 0
+                     && sval >> 32 != 0
+                     && sval >> 32 != -1)
+                   as_bad ("I don't know how to handle 64 bit constants here yet, sorry.");
+               }
              continue;
 
            case 'F':
              {
-               int format, length, mode, i, size;
+               int format, length, mode, i;
                char temp[20 /*MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT*/];
                char *err;
                static const char formats[4] = "FGfd";
@@ -1257,8 +1612,12 @@ alpha_ip (str, insns)
                insns[0].reloc[0].exp.X_op = O_symbol;
                offset &= 0xffff;
                num_gen = load_expression (AT, &insns[0]);
-               insns[num_gen].reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
-               insns[num_gen].reloc[0].exp = lituse_basereg;
+               if (lituse_pending)
+                 {
+                   insns[num_gen].reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
+                   insns[num_gen].reloc[0].exp = lituse_basereg;
+                   lituse_pending = 0;
+                 }
                insns[num_gen++].opcode = opcode | (AT << SB) | offset;
                opcode = insns[0].opcode;
                s = input_line_pointer;
@@ -1281,7 +1640,6 @@ alpha_ip (str, insns)
              /* fall through */
 
            case 'G':           /* Addressing macros: GET */
-           get_macro:
              /* All it is missing is the expression, which is what we
                 will get now */
 
@@ -1298,34 +1656,25 @@ alpha_ip (str, insns)
                }
              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;
+                 bfd_vma val = insns[0].reloc[0].exp.X_add_number;
+                 bfd_vma top, low;
 
                  insns[0].reloc[0].code = BFD_RELOC_NONE;
                  insns[1].reloc[0].code = BFD_RELOC_NONE;
 
-                 sval = val;
-                 if ((sval != val) && (val & 0x8000))
+                 low = val & 0xffff;
+                 if (low & 0x8000)
+                   low -= 0x10000;
+                 top = val - low;
+                 if (top)
                    {
-                     val += 0x10000;
-                     sval = val;
-                   }
-
-                 if (optnum && (sval == val))
-                   {
-                     /* optimize away the ldah */
-                     num_gen = 1;
-                     opcode |= (ZERO << SB) | (val & 0xffff);
+                     emit_add64 (ZERO, AT, top);
+                     opcode |= AT << SB;
                    }
                  else
-                   {
-                     num_gen = 2;
-                     insns[1].opcode = opcode | (mask << SB) | (val & 0xffff);
-                     opcode = 0x24000000 /*ldah*/  |
-                       mask << SA | (ZERO << SB) |
-                       ((val >> 16) & 0xffff);
-                   }
+                   opcode |= ZERO << SB;
+                 opcode &= ~0x1000;
+                 opcode |= low & 0xffff;
                }
              else if (insns[0].reloc[0].exp.X_op == O_symbol)
                {
@@ -1342,15 +1691,23 @@ alpha_ip (str, insns)
                    tmp_reg = AT;
                  num_gen = load_expression (tmp_reg, insns);
                  opcode = insns[0].opcode;
-                 /* lda is opcode 8, 0x20000000 */
-                 if (OPCODE (old_opcode) != 0x08)
+                 /* lda is opcode 8, 0x20000000, and the macros that use
+                    this code have an opcode field of 0.  The latter
+                    require further processing, and we don't have the
+                    true opcode here.  */
+                 if (OPCODE (old_opcode) != 0
+                     && OPCODE (old_opcode) != 0x08)
                    {
                      struct alpha_it *i;
                      i = &insns[num_gen++];
                      i->opcode = old_opcode | (tmp_reg << SB);
 
-                     i->reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
-                     i->reloc[0].exp = lituse_basereg;
+                     if (lituse_pending)
+                       {
+                         i->reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
+                         i->reloc[0].exp = lituse_basereg;
+                         lituse_pending = 0;
+                       }
                    }
                }
              else
@@ -1422,7 +1779,8 @@ alpha_ip (str, insns)
                        insns[1].opcode |= addend & 0xffff;
                        insns[0].opcode |= ((addend >> 16)
                                            + (addend & 0x8000 ? 1 : 0));
-                       ecoff_set_gp_prolog_size (0);
+                       if (r2 == PV)
+                         ecoff_set_gp_prolog_size (0);
                      }
                      break;
                    default:
@@ -1468,12 +1826,13 @@ alpha_ip (str, insns)
                                   | (mask << SA)
                                   | (PV << SB)
                                   | 0);
-                   if (num_gen == 2)
+                   if (lituse_pending)
                      {
                        /* LITUSE wasn't emitted yet */
                        jsr->reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
                        jsr->reloc[0].exp = lituse_jsr;
                        r = &jsr->reloc[1];
+                       lituse_pending = 0;
                      }
                    else
                      r = &jsr->reloc[0];
@@ -1484,6 +1843,187 @@ alpha_ip (str, insns)
                  }
                  continue;
 
+               case 'd':
+                 /* Sub-word loads and stores.  We load the address into
+                    $at, which might involve using the `P' parameter
+                    processing too, then emit a sequence to get the job
+                    done, using unaligned memory accesses and byte
+                    manipulation, with t9 and t10 as temporaries.  */
+                 {
+                   /* Characteristics of access.  */
+                   int is_load, is_unsigned = 0, is_unaligned = 0;
+                   int mode_size, mode;
+                   /* Register operand.  */
+                   int reg;
+                   /* Addend for loads and stores.  */
+                   valueT addend;
+                   /* Which register do we use for the address?  */
+                   int addr;
+
+                   {
+                     /* Pick apart name and set flags.  */
+                     const char *s = pattern->name;
+
+                     if (*s == 'u')
+                       {
+                         is_unaligned = 1;
+                         s++;
+                       }
+
+                     if (s[0] == 'l' && s[1] == 'd')
+                       is_load = 1;
+                     else if (s[0] == 's' && s[1] == 't')
+                       is_load = 0;
+                     else
+                       as_fatal ("unrecognized sub-word access insn `%s'",
+                                 str);
+                     s += 2;
+
+                     mode = *s++;
+                     if (mode == 'b') mode_size = 1;
+                     else if (mode == 'w') mode_size = 2;
+                     else if (mode == 'l') mode_size = 4;
+                     else if (mode == 'q') mode_size = 8;
+                     else abort ();
+
+                     if (*s == 'u')
+                       {
+                         is_unsigned = 1;
+                         s++;
+                       }
+
+                     assert (*s == 0);
+
+                     /* Longwords are always kept sign-extended.  */
+                     if (mode == 'l' && is_unsigned)
+                       abort ();
+                     /* There's no special unaligned byte handling.  */
+                     if (mode == 'b' && is_unaligned)
+                       abort ();
+                     /* Stores don't care about signedness.  */
+                     if (!is_load && is_unsigned)
+                       abort ();
+                   }
+
+                   if (args[-2] == 'P')
+                     {
+                       addr = AT;
+                       addend = 0;
+                     }
+                   else
+                     {
+                       /* foo r1,num(r2)
+                          r2 -> mask
+                          r1 -> (opcode >> SA) & 31
+                          num -> insns->reloc[0].*
+
+                          We want to emit "lda at,num(r2)", since these
+                          operations require the use of a single register
+                          with the starting address of the memory operand
+                          we want to access.
+
+                          We could probably get away without doing this
+                          (and use r2 below, with the addend for the
+                          actual reads and writes) in cases where the
+                          addend is known to be a multiple of 8.  */
+                       int r2 = mask;
+                       int r1 = (opcode >> SA) & 31;
+
+                       if (insns[0].reloc[0].code == BFD_RELOC_NONE)
+                         addend = 0;
+                       else if (insns[0].reloc[0].code == BFD_RELOC_16)
+                         {
+                           if (insns[0].reloc[0].exp.X_op != O_constant)
+                             abort ();
+                           addend = insns[0].reloc[0].exp.X_add_number;
+                         }
+                       else
+                         abort ();
+
+                       if (addend + mode_size - 1 < 0x7fff
+                           && (addend % 8) == 0
+                           && (r2 < T9 || r2 > T12))
+                         {
+                           addr = r2;
+                           num_gen = 0;
+                         }
+                       else
+                         {
+                           /* Let later relocation processing deal
+                              with the addend field.  */
+                           insns[num_gen-1].opcode = (0x20000000 /* lda */
+                                                      | (AT << SA)
+                                                      | (r2 << SB));
+                           addr = AT;
+                           addend = 0;
+                         }
+                       reg = r1;
+                     }
+
+                   /* Because the emit_* routines append directly to
+                      the current frag, we now need to flush any
+                      pending insns.  */
+                   {
+                     int i;
+                     for (i = 0; i < num_gen; i++)
+                       emit_insn (&insns[i]);
+                     num_gen = 0;
+                   }
+
+                   if (is_load)
+                     {
+                       int reg2, reg3;
+
+                       if (is_unaligned)
+                         reg2 = T9, reg3 = T10;
+                       else
+                         reg2 = reg;
+
+                       emit_load_unal (addr, addend, T9);
+                       if (is_unaligned)
+                         emit_load_unal (addr, addend + mode_size - 1, T10);
+                       emit_extract_r (T9, addr, reg2, mode, 'l');
+                       if (is_unaligned)
+                         {
+                           emit_extract_r (T10, addr, reg3, mode, 'h');
+                           emit_bis_r (T9, T10, reg);
+                         }
+                       if (!is_unsigned)
+                         emit_sign_extend (reg, mode_size * 8);
+                     }
+                   else
+                     {
+                       /* The second word gets processed first
+                          because if the address does turn out to be
+                          aligned, the processing for the second word
+                          will be pushing around all-zeros, and the
+                          entire value will be handled as the `first'
+                          word.  So we want to store the `first' word
+                          last.  */
+                       /* Pair these up so that the memory loads get
+                          separated from each other, as well as being
+                          well in advance of the uses of the values
+                          loaded.  */
+                       if (is_unaligned)
+                         {
+                           emit_load_unal (addr, addend + mode_size - 1, T11);
+                           emit_insert_r (reg, addr, T12, mode, 'h');
+                         }
+                       emit_load_unal (addr, addend, T9);
+                       emit_insert_r (reg, addr, T10, mode, 'l');
+                       if (is_unaligned)
+                         emit_mask_r (T12, addr, T12, mode, 'h');
+                       emit_mask_r (T10, addr, T10, mode, 'l');
+                       if (is_unaligned)
+                         emit_bis_r (T11, T12, T11);
+                       emit_bis_r (T9, T10, T9);
+                       if (is_unaligned)
+                         emit_store_unal (addr, addend + mode_size - 1, T11);
+                       emit_store_unal (addr, addend, T9);
+                     }
+                 }
+                 return 0;
+
                  /* DIVISION and MODULUS. Yech.
                       Convert  OP x,y,result
                       to       mov x,t10
@@ -1689,55 +2229,77 @@ md_bignum_to_chars (buf, bignum, nchars)
       while (--nb);
     }
 }
+\f
+CONST char *md_shortopts = "Fm:";
+struct option md_longopts[] = {
+#define OPTION_32ADDR (OPTION_MD_BASE)
+  {"32addr", no_argument, NULL, OPTION_32ADDR},
+  {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
 
 int
-md_parse_option (argP, cntP, vecP)
-     char **argP;
-     int *cntP;
-     char ***vecP;
+md_parse_option (c, arg)
+     int c;
+     char *arg;
 {
-  if (**argP == 'F')
+  switch (c)
     {
+    case '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, "32addr"))
-    {
+      break;
+
+    case OPTION_32ADDR:
       addr32 = 1;
-      *argP += 6;
-      return 1;
-    }
-  if (!strcmp (*argP, "nocpp"))
-    {
-      *argP += 5;
-      return 1;
+      break;
+
+    case 'm':
+      {
+       unsigned long mach;
+
+       if (!strcmp (arg, "21064"))
+         mach = 21064;
+       else if (!strcmp (arg, "21066"))
+         mach = 21066;
+       else if (!strcmp (arg, "21164"))
+         mach = 21164;
+       else
+         {
+           as_bad ("invalid architecture %s", arg);
+           return 0;
+         }
+
+       if (machine != 0 && machine != mach)
+         {
+           as_warn ("machine type %lu already chosen, overriding with %lu",
+                    machine, mach);
+         }
+       machine = mach;
+      }
+      break;
+
+    default:
+      return 0;
     }
-  return 0;
+
+  return 1;
 }
 
+void
+md_show_usage (stream)
+     FILE *stream;
+{
+  fprintf(stream, "\
+Alpha options:\n\
+-32addr                        treat addresses as 32-bit values\n\
+-F                     lack floating point instructions support\n\
+-m21064 | -m21066 | -m21164\n\
+                       specify variant of Alpha architecture\n");
+}
+\f
 static void
 s_proc (is_static)
+     int is_static;
 {
   /* XXXX Align to cache linesize XXXXX */
   char *name;
@@ -1820,7 +2382,7 @@ md_pcrel_from (fixP)
 int
 alpha_do_align (n, fill)
      int n;
-     char *fill;
+     const char *fill;
 {
   if (!fill
       && (now_seg == text_section
@@ -1896,7 +2458,6 @@ md_apply_fix (fixP, valueP)
       *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);
@@ -1921,6 +2482,28 @@ md_apply_fix (fixP, valueP)
        }
       break;
 
+    case BFD_RELOC_26:
+      if (fixP->fx_addsy != 0
+         && fixP->fx_addsy->bsym->section != absolute_section)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     "PALcode instructions require immediate constant function code");
+      else if (value >> 26 != 0)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     "overflow in 26-bit PALcode function field");
+      *p++ = value & 0xff;
+      value >>= 8;
+      *p++ = value & 0xff;
+      value >>= 8;
+      *p++ = value & 0xff;
+      value >>= 8;
+      {
+       char x = *p;
+       x &= ~3;
+       x |= (value & 3);
+       *p++ = x;
+      }
+      goto done;
+
     case BFD_RELOC_14:
       if (fixP->fx_addsy != 0
          && fixP->fx_addsy->bsym->section != absolute_section)