Handle EV5 (21164/66/68) PALcode support.
[deliverable/binutils-gdb.git] / gas / config / tc-alpha.c
index 90bb0bf4a91451f0d3ccd750055285c88df06c5e..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];
@@ -70,10 +74,11 @@ 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;
+/* Sections and symbols we'll want to keep track of.  */
+static segT lita_sec, rdata, sdata, lit8_sec, lit4_sec;
+static symbolS *lit8_sym, *lit4_sym;
 
-/* setting for ".set [no]{at,macro}" */
+/* Setting for ".set [no]{at,macro}".  */
 static int at_ok = 1, macro_ok = 1;
 
 /* Keep track of global pointer.  */
@@ -147,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
@@ -187,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
@@ -431,88 +438,150 @@ s_gprel32 ()
 }
 
 static void
-create_lita_section ()
+create_literal_section (secp, name)
+     segT *secp;
+     const char *name;
 {
   segT current_section = now_seg;
   int current_subsec = now_subseg;
+  segT new_sec;
 
-  lita_sec = subseg_new (".lita", 0);
+  *secp = new_sec = subseg_new (name, 0);
   subseg_set (current_section, current_subsec);
-  bfd_set_section_flags (stdoutput, lita_sec,
+  bfd_set_section_alignment (stdoutput, new_sec, 3);
+  bfd_set_section_flags (stdoutput, new_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 ()
+#define create_lita_section() create_literal_section (&lita_sec, ".lita")
+
+static valueT
+get_lit8_offset (val)
+     bfd_vma val;
 {
-  const char *retval;
-  int lose = 0;
-  unsigned int i = 0;
+  valueT retval;
+  if (lit8_sec == 0)
+    {
+      create_literal_section (&lit8_sec, ".lit8");
+      lit8_sym = section_symbol (lit8_sec);
+    }
+  retval = add_to_literal_pool ((symbolS *) 0, val, lit8_sec, 8);
+  if (retval >= 0xfff0)
+    as_fatal ("overflow in fp literal (.lit8) table");
+  return retval;
+}
 
-  op_hash = hash_new ();
+static valueT
+get_lit4_offset (val)
+     bfd_vma val;
+{
+  valueT retval;
+  if (lit4_sec == 0)
+    {
+      create_literal_section (&lit4_sec, ".lit4");
+      lit4_sym = section_symbol (lit4_sec);
+    }
+  retval = add_to_literal_pool ((symbolS *) 0, val, lit4_sec, 4);
+  if (retval >= 0xfff0)
+    as_fatal ("overflow in fp literal (.lit4) table");
+  return retval;
+}
+
+#define load_insn(NAME, OP)    (hash_insert (op_hash, (NAME), (PTR) (OP)))
 
-  for (i = 0; i < NUMOPCODES; )
+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 (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;
+
+  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;
@@ -532,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];
 
@@ -546,63 +651,37 @@ 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;
+    emit_insn (&insns[i]);
+}
 
-         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;
-           }
-       }
-    }
+static inline void
+maybe_set_gp (sec)
+     asection *sec;
+{
+  bfd_vma vma;
+  if (!sec)
+    return;
+  vma = bfd_get_section_vma (foo, sec);
+  if (vma && vma < alpha_gp_value)
+    alpha_gp_value = vma;
 }
 
 static void
 select_gp_value ()
 {
-  bfd_vma lita_vma, sdata_vma;
-
   if (alpha_gp_value != 0)
     abort ();
 
-  if (lita_sec)
-    lita_vma = bfd_get_section_vma (abfd, lita_sec);
-  else
-    lita_vma = 0;
+  /* Get minus-one in whatever width...  */
+  alpha_gp_value = 0; alpha_gp_value--;
+
+  /* Select the smallest VMA of these existing sections.  */
+  maybe_set_gp (lita_sec);
+/* maybe_set_gp (sdata);   Was disabled before -- should we use it?  */
 #if 0
-  if (sdata)
-    sdata_vma = bfd_get_section_vma (abfd, sdata);
-  else
+  maybe_set_gp (lit8_sec);
+  maybe_set_gp (lit4_sec);
 #endif
-    sdata = 0;
-
-  if (lita_vma == 0
-      /* Who knows which order they'll get laid out in?  */
-      || (sdata_vma != 0 && sdata_vma < lita_vma))
-    alpha_gp_value = sdata_vma;
-  else
-    alpha_gp_value = lita_vma;
 
   alpha_gp_value += GP_ADJUSTMENT;
 
@@ -632,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 ();
@@ -672,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)
@@ -709,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? */
@@ -747,8 +826,15 @@ load_expression (reg, insn)
   valueT addend;
   int num_insns = 1;
 
-  addend = insn->reloc[0].exp.X_add_number;
-  insn->reloc[0].exp.X_add_number = 0;
+  if (insn->reloc[0].exp.X_add_symbol->bsym->flags & BSF_SECTION_SYM)
+    {
+      addend = 0;
+    }
+  else
+    {
+      addend = insn->reloc[0].exp.X_add_number;
+      insn->reloc[0].exp.X_add_number = 0;
+    }
   load_symbol_address (reg, insn);
   if (addend)
     {
@@ -768,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;
 }
@@ -831,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
@@ -1094,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;
@@ -1108,7 +1273,6 @@ alpha_ip (str, insns)
 #else
            case 't':
            case '8':
-           case 'I':
              abort ();
 #endif
              /*FALLTHROUGH*/
@@ -1165,6 +1329,64 @@ alpha_ip (str, insns)
                }
              continue;
 
+           case 'F':
+             {
+               int format, length, mode, i, size;
+               char temp[20 /*MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT*/];
+               char *err;
+               static const char formats[4] = "FGfd";
+               bfd_vma bits, offset;
+               char *old_input_line_pointer = input_line_pointer;
+
+               input_line_pointer = s;
+               SKIP_WHITESPACE ();
+               memset (temp, 0, sizeof (temp));
+               mode = (opcode >> 26) & 3;
+               format = formats[mode];
+               err = md_atof (format, temp, &length);
+               if (err)
+                 {
+                   as_bad ("Bad floating literal: %s", err);
+                   bits = 0;
+                 }
+               else
+                 {
+                   /* Generate little-endian number from byte sequence.  */
+                   bits = 0;
+                   for (i = length - 1; i >= 0; i--)
+                     bits += ((bfd_vma)(temp[i] & 0xff)) << (i * 8);
+                 }
+               switch (length)
+                 {
+                 case 8:
+                   offset = get_lit8_offset (bits) - 0x8000;
+                   insns[0].reloc[0].exp.X_add_symbol = lit8_sym;
+                   insns[0].reloc[0].exp.X_add_number = 0x8000;
+                   break;
+                 case 4:
+                   offset = get_lit4_offset (bits) - 0x8000;
+                   insns[0].reloc[0].exp.X_add_symbol = lit4_sym;
+                   insns[0].reloc[0].exp.X_add_number = 0x8000;
+                   break;
+                 default:
+                   abort ();
+                 }
+               insns[0].reloc[0].exp.X_op = O_symbol;
+               offset &= 0xffff;
+               num_gen = load_expression (AT, &insns[0]);
+               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;
+               input_line_pointer = old_input_line_pointer;
+             }
+             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
@@ -1241,13 +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->reloc[0].code = BFD_RELOC_NONE;
                      i->opcode = old_opcode | (tmp_reg << SB);
+
+                     if (lituse_pending)
+                       {
+                         i->reloc[0].code = BFD_RELOC_ALPHA_LITUSE;
+                         i->reloc[0].exp = lituse_basereg;
+                         lituse_pending = 0;
+                       }
                    }
                }
              else
@@ -1271,7 +1503,7 @@ alpha_ip (str, insns)
              continue;
 
              /* Same failure modes as above, actually most of the
-                same code shared.  */
+                same code shared.  */
            case 'B':           /* Builtins */
              args++;
              switch (*args)
@@ -1319,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:
@@ -1365,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];
@@ -1381,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
@@ -1628,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;
@@ -1818,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)
This page took 0.041579 seconds and 4 git commands to generate.