Handle EV5 (21164/66/68) PALcode support.
authorKen Raeburn <raeburn@cygnus>
Thu, 2 Jun 1994 16:06:43 +0000 (16:06 +0000)
committerKen Raeburn <raeburn@cygnus>
Thu, 2 Jun 1994 16:06:43 +0000 (16:06 +0000)
* config/tc-alpha.c (machine): New variable.
(load_insn): New macro.
(load_insn_table): New function.
(md_begin): Call load_insn_table, once for basic instructions and
once for appropriate PAL instruction table.
(md_parse_option): Set `machine' based on -m##### arguments.

* config/alpha-opcode.h (alpha_pal21064_opcodes): Split out from alpha_opcodes.
(alpha_pal21164_opcodes): New table.
(NUM21064OPCODES, NUM21164OPCODES): New macros.

gas/config/alpha-opcode.h
gas/config/tc-alpha.c

index dd35c58d7c1301b81c5c0b1f400e775c42ba3bb3..af1c1d31225d5d5f0cb83addfc69718e3080de65 100644 (file)
@@ -86,7 +86,7 @@ Kinds of operands:
    e    fa floating point register.
    f    fb floating point register.
    g    fc floating point register.
-   I   26 bit immediate
+   I   26 bit immediate (PALcode function #)
    l   16 low bits of immediate
    h   16 high(er) bits of immediate           [Never used. KR]
    L   22 bit PC relative immediate.
@@ -173,6 +173,33 @@ static const struct alpha_opcode alpha_opcodes[] =
 { "stq_c",      0xbc000000, 0, "1,l(2)" },
 { "stq_c",      0xbc000000, 0, "1,P" },                /* regbase macro */
 
+{ "ldb",       0, 0,           "1,l(2)Bd" },
+{ "ldb",       0, 0,           "1,PBd" },
+{ "ldbu",      0, 0,           "1,l(2)Bd" },
+{ "ldbu",      0, 0,           "1,PBd" },
+{ "ldw",       0, 0,           "1,l(2)Bd" },
+{ "ldw",       0, 0,           "1,PBd" },
+{ "ldwu",      0, 0,           "1,l(2)Bd" },
+{ "ldwu",      0, 0,           "1,PBd" },
+{ "stb",       0, 0,           "1,l(2)Bd" },
+{ "stb",       0, 0,           "1,PBd" },
+{ "stw",       0, 0,           "1,l(2)Bd" },
+{ "stw",       0, 0,           "1,PBd" },
+{ "ustw",      0, 0,           "1,l(2)Bd" },
+{ "ustw",      0, 0,           "1,PBd" },
+{ "ustl",      0, 0,           "1,l(2)Bd" },
+{ "ustl",      0, 0,           "1,PBd" },
+{ "ustq",      0, 0,           "1,l(2)Bd" },
+{ "ustq",      0, 0,           "1,PBd" },
+{ "uldw",      0, 0,           "1,l(2)Bd" },
+{ "uldw",      0, 0,           "1,PBd" },
+{ "uldwu",     0, 0,           "1,l(2)Bd" },
+{ "uldwu",     0, 0,           "1,PBd" },
+{ "uldl",      0, 0,           "1,l(2)Bd" },
+{ "uldl",      0, 0,           "1,PBd" },
+{ "uldq",      0, 0,           "1,l(2)Bd" },
+{ "uldq",      0, 0,           "1,PBd" },
+
 { "beq",        0xe4000000, 0, "1,L" },                /* 6o+5a+21d */
 { "bne",        0xf4000000, 0, "1,L" },
 { "blt",        0xe8000000, 0, "1,L" },
@@ -723,7 +750,7 @@ static const struct alpha_opcode alpha_opcodes[] =
 { "mult/suid",  0x5800fc40, 1, "e,f,g" },
 
 /*
- * Miscellaneous
+ * Miscellaneous, including standard PAL instructions.
  */
 { "pal",        0x00000000, 0, "I" },          /* 6o+26f */
 { "call_pal",   0x00000000, 0, "I" },          /* alias */
@@ -741,8 +768,19 @@ static const struct alpha_opcode alpha_opcodes[] =
 { "rs",         0x6000f000, 0, "1" },
 
 /*
- * PAL instructions
+ * More macros
+ */
+{ "nop",         0x47ff041f, 0, "" },          /* or zero,zero,zero */
+{ "mov",         0x47e00400, 0, "2,3" },               /* or zero,r2,r3 */
+};
+
+#define NUMOPCODES ((sizeof alpha_opcodes)/(sizeof alpha_opcodes[0]))
+
+/*
+ * PAL instructions for 21064 (and 21066/68)
  */
+static const struct alpha_opcode alpha_pal21064_opcodes[] =
+{
 { "hw_ld",     0x6c000000, 0, "1,t(2)" },
 { "hw_ld/p",   0x6c008000, 0, "1,t(2)" },
 { "hw_ld/a",   0x6c004000, 0, "1,t(2)" },
@@ -814,11 +852,42 @@ static const struct alpha_opcode alpha_opcodes[] =
 { "hw_mtpr",   0x74000000, 0, "R,8" },
 
 { "hw_rei",    0x7bff8000, 0, "" },
+};
+
+#define NUM21064OPCODES ((sizeof alpha_pal21064_opcodes)/(sizeof alpha_pal21064_opcodes[0]))
+
 /*
- * More macros
+ * 21164 (and 21166/68) specific PAL instructions.
  */
-{ "nop",         0x47ff041f, 0, "" },          /* or zero,zero,zero */
-{ "mov",         0x47e00400, 0, "2,3" },               /* or zero,r2,r3 */
+static const struct alpha_opcode alpha_pal21164_opcodes[] =
+{
+{ "hw_ld",     0x6c000000, 0, "1,l(2)" },      /* RA, 16 bit displacement (RB) */
+{ "hw_st",     0x7c000000, 0, "1,l(2)" },      /* RA, 16 bit displacement (RB) */
+
+{ "hw_ldl/a",  0x6c004000, 0, "1,t(2)" },      /* RA, 12 bit displacement (RB) */
+{ "hw_ldq/a",  0x6c005000, 0, "1,t(2)" },      /* RA, 12 bit displacement (RB) */
+{ "hw_stl/a",  0x7c004000, 0, "1,t(2)" },      /* RA, 12 bit displacement (RB) */
+{ "hw_stq/a",  0x7c005000, 0, "1,t(2)" },      /* RA, 12 bit displacement (RB) */
+
+{ "hw_ldl/p",  0x6c008000, 0, "1,t(2)" },      /* RA, 12 bit displacement (RB) */
+{ "hw_ldq/p",  0x6c009000, 0, "1,t(2)" },      /* RA, 12 bit displacement (RB) */
+{ "hw_stl/p",  0x7c008000, 0, "1,t(2)" },      /* RA, 12 bit displacement (RB) */
+{ "hw_stq/p",  0x7c009000, 0, "1,t(2)" },      /* RA, 12 bit displacement (RB) */
+
+{ "hw_ldq/v",  0x6c001800, 0, "1,t(2)" },      /* RA, 12 bit displacement (RB) */
+
+{ "hw_ldl/l",  0x6c000400, 0, "1,t(2)" },      /* RA, 12 bit displacement (RB) */
+{ "hw_ldq/l",  0x6c001400, 0, "1,t(2)" },      /* RA, 12 bit displacement (RB) */
+
+{ "hw_stl/c",  0x7c000400, 0, "1,t(2)" },      /* RA, 12 bit displacement (RB) */
+{ "hw_stq/c",  0x7c001400, 0, "1,t(2)" },      /* RA, 12 bit displacement (RB) */
+
+{ "hw_mfpr",   0x64000000, 0, "R,l" },         /* RA,RB,16 bits displacement */
+{ "hw_mtpr",   0x74000000, 0, "R,l" },         /* RA,RB,16 bits displacement */
+
+{ "hw_rei",    0x7bff8000, 0, "" },
+
+{ "hw_rei_stall",      0x7bffc000, 0, "" },
 };
 
-#define NUMOPCODES ((sizeof alpha_opcodes)/(sizeof alpha_opcodes[0]))
+#define NUM21164OPCODES ((sizeof alpha_pal21164_opcodes)/(sizeof alpha_pal21164_opcodes[0]))
index 8aa087fc48c2485e06a609244ebd67ab5398d289..c8da0cb4812481d154b7ddc3dfcf1760ff775a05 100644 (file)
 /* @@ 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];
@@ -148,7 +152,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
@@ -188,6 +193,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
@@ -482,74 +488,100 @@ 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
-           }
+         const char *name2, *p, *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)));
     }
+}
 
+/* 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 ()
+{
+  const char *retval;
+  int lose = 0;
+  unsigned int i = 0;
 
-  if (lose)
-    as_fatal ("Broken assembler.  No assembly attempted.");
+  op_hash = hash_new ();
+  load_insn_table (alpha_opcodes, NUMOPCODES);
+
+  /* Default to 21064 PAL instructions.  */
+  if (machine == 0)
+    machine = 21064;
+
+  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;
@@ -569,12 +601,48 @@ md_begin ()
 
 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 +651,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 +711,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 ();
@@ -712,13 +752,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)
@@ -749,6 +787,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? */
@@ -815,6 +854,7 @@ load_expression (reg, insn)
                        | (addend & 0xffff));
       insn[1].reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
       insn[1].reloc[0].exp = lituse_basereg;
+      lituse_pending = 0;
     }
   return num_insns;
 }
@@ -878,6 +918,80 @@ getExpression (str, this_insn)
   input_line_pointer = save_in;
 }
 
+/* All of these should soon be changed to just emit words to the
+   output frag...  */
+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;
+{
+  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)
+{
+  emit_byte_manip_r ("ext", in, mask, out, mode, which);
+}
+
+static void
+emit_insert_r (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)
+{
+  emit_byte_manip_r ("msk", in, mask, out, mode, which);
+}
+
+static void
+emit_sign_extend (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)
+{
+  char buf[90];
+  sprintf (buf, "bis $%d,$%d,$%d", in1, in2, out);
+  md_assemble (buf);
+}
+
 /* 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
@@ -1141,6 +1255,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 +1273,6 @@ alpha_ip (str, insns)
 #else
            case 't':
            case '8':
-           case 'I':
              abort ();
 #endif
              /*FALLTHROUGH*/
@@ -1257,8 +1374,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;
@@ -1342,15 +1463,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 +1551,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 +1598,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 +1615,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.  */
+                     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
@@ -1731,6 +2043,34 @@ md_parse_option (argP, cntP, vecP)
   if (!strcmp (*argP, "nocpp"))
     {
       *argP += 5;
+      return 1;
+    }
+  if (**argP == 'm')
+    {
+      unsigned long mach;
+
+      (*argP)++;
+      if (!strcmp (*argP, "21064"))
+       mach = 21064;
+      else if (!strcmp (*argP, "21066"))
+       mach = 21066;
+      else if (!strcmp (*argP, "21164"))
+       mach = 21164;
+      else
+       {
+         mach = 0;
+         (*argP)--;
+         return 0;
+       }
+      (*argP) += 5;
+
+      if (machine != 0 && machine != mach)
+       {
+         as_warn ("machine type %lu already chosen, overriding with %lu",
+                  machine, mach);
+       }
+      machine = mach;
+
       return 1;
     }
   return 0;
@@ -1921,6 +2261,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)