* read.c (read_a_source_file): Only declare inescape if
[deliverable/binutils-gdb.git] / gas / config / tc-sparc.c
index a0e07aaa9fe3c3c894d5251fefc8fa51c151273d..6894ca1a824f73208a6d9d40a2002fcf2237b5cc 100644 (file)
@@ -1,6 +1,5 @@
 /* tc-sparc.c -- Assemble for the SPARC
-   Copyright (C) 1989, 90-95, 1996 Free Software Foundation, Inc.
-
+   Copyright (C) 1989, 90-96, 97, 98, 1999 Free Software Foundation, Inc.
    This file is part of GAS, the GNU Assembler.
 
    GAS is free software; you can redistribute it and/or modify
 #include "as.h"
 #include "subsegs.h"
 
-/* careful, this file includes data *declarations* */
 #include "opcode/sparc.h"
 
-static void sparc_ip PARAMS ((char *, const struct sparc_opcode **));
+#ifdef OBJ_ELF
+#include "elf/sparc.h"
+#endif
+
+static struct sparc_arch *lookup_arch PARAMS ((char *));
+static void init_default_arch PARAMS ((void));
+static int sparc_ip PARAMS ((char *, const struct sparc_opcode **));
+static int in_signed_range PARAMS ((bfd_signed_vma, bfd_signed_vma));
+static int in_unsigned_range PARAMS ((bfd_vma, bfd_vma));
+static int in_bitfield_range PARAMS ((bfd_signed_vma, bfd_signed_vma));
+static int sparc_ffs PARAMS ((unsigned int));
+static void synthetize_setuw PARAMS ((const struct sparc_opcode *));
+static void synthetize_setsw PARAMS ((const struct sparc_opcode *));
+static void synthetize_setx PARAMS ((const struct sparc_opcode *));
+static bfd_vma BSR PARAMS ((bfd_vma, int));
+static int cmp_reg_entry PARAMS ((const PTR, const PTR));
+static int parse_keyword_arg PARAMS ((int (*) (const char *), char **, int *));
+static int parse_const_expr_arg PARAMS ((char **, int *));
+static int get_expression PARAMS ((char *str));
+
+/* Default architecture.  */
+/* ??? The default value should be V8, but sparclite support was added
+   by making it the default.  GCC now passes -Asparclite, so maybe sometime in
+   the future we can set this to V8.  */
+#ifndef DEFAULT_ARCH
+#define DEFAULT_ARCH "sparclite"
+#endif
+static char *default_arch = DEFAULT_ARCH;
+
+/* Non-zero if the initial values of `max_architecture' and `sparc_arch_size'
+   have been set.  */
+static int default_init_p;
 
 /* Current architecture.  We don't bump up unless necessary.  */
 static enum sparc_opcode_arch_val current_architecture = SPARC_OPCODE_ARCH_V6;
 
 /* The maximum architecture level we can bump up to.
    In a 32 bit environment, don't allow bumping up to v9 by default.
-   The native assembler works this way. The user is required to pass
+   The native assembler works this way.  The user is required to pass
    an explicit argument before we'll create v9 object files.  However, if
-   we don't see any v9 insns, a v9 object file is not created.  */
-#ifdef SPARC_ARCH64
-static enum sparc_opcode_arch_val max_architecture = SPARC_OPCODE_ARCH_V9;
-#else
-/* ??? This should be V8, but sparclite support was added by making it the
-   default.  GCC now passes -Asparclite, so maybe sometime in the future
-   we can set this to V8.  */
-static enum sparc_opcode_arch_val max_architecture = SPARC_OPCODE_ARCH_SPARCLITE;
+   we don't see any v9 insns, a v8plus object file is not created.  */
+static enum sparc_opcode_arch_val max_architecture;
+
+/* Either 32 or 64, selects file format.  */
+static int sparc_arch_size;
+/* Initial (default) value, recorded separately in case a user option
+   changes the value before md_show_usage is called.  */
+static int default_arch_size;
+
+#ifdef OBJ_ELF
+/* The currently selected v9 memory model.  Currently only used for
+   ELF.  */
+static enum { MM_TSO, MM_PSO, MM_RMO } sparc_memory_model = MM_RMO;
 #endif
 
 static int architecture_requested;
@@ -56,11 +90,27 @@ static enum sparc_opcode_arch_val warn_after_architecture;
 /* Non-zero if we are generating PIC code.  */
 int sparc_pic_code;
 
+/* Non-zero if we should give an error when misaligned data is seen.  */
+static int enforce_aligned_data;
+
 extern int target_big_endian;
 
+static int target_little_endian_data;
+
+/* V9 and 86x have big and little endian data, but instructions are always big
+   endian.  The sparclet has bi-endian support but both data and insns have
+   the same endianness.  Global `target_big_endian' is used for data.
+   The following macro is used for instructions.  */
+#ifndef INSN_BIG_ENDIAN
+#define INSN_BIG_ENDIAN (target_big_endian \
+                        || default_arch_type == sparc86x \
+                        || SPARC_OPCODE_ARCH_V9_P (max_architecture))
+#endif
+
 /* handle of the OPCODE hash table */
 static struct hash_control *op_hash;
 
+static int log2 PARAMS ((int));
 static void s_data1 PARAMS ((void));
 static void s_seg PARAMS ((int));
 static void s_proc PARAMS ((int));
@@ -68,6 +118,7 @@ static void s_reserve PARAMS ((int));
 static void s_common PARAMS ((int));
 static void s_empty PARAMS ((int));
 static void s_uacons PARAMS ((int));
+static void s_ncons PARAMS ((int));
 
 const pseudo_typeS md_pseudo_table[] =
 {
@@ -76,6 +127,7 @@ const pseudo_typeS md_pseudo_table[] =
   {"empty", s_empty, 0},
   {"global", s_globl, 0},
   {"half", cons, 2},
+  {"nword", s_ncons, 0},
   {"optim", s_ignore, 0},
   {"proc", s_proc, 0},
   {"reserve", s_reserve, 0},
@@ -83,13 +135,16 @@ const pseudo_typeS md_pseudo_table[] =
   {"skip", s_space, 0},
   {"word", cons, 4},
   {"xword", cons, 8},
+  {"uahalf", s_uacons, 2},
+  {"uaword", s_uacons, 4},
+  {"uaxword", s_uacons, 8},
 #ifdef OBJ_ELF
   /* these are specific to sparc/svr4 */
   {"pushsection", obj_elf_section, 0},
   {"popsection", obj_elf_previous, 0},
-  {"uahalf", s_uacons, 2},
-  {"uaword", s_uacons, 4},
-  {"uaxword", s_uacons, 8},
+  {"2byte", s_uacons, 2},
+  {"4byte", s_uacons, 4},
+  {"8byte", s_uacons, 8},
 #endif
   {NULL, 0, 0},
 };
@@ -124,9 +179,7 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
    changed in read.c.  Ideally it shouldn't have to know about it at all,
    but nothing is ideal around here.  */
 
-static unsigned char octal[256];
-#define isoctal(c)  octal[(unsigned char) (c)]
-static unsigned char toHex[256];
+#define isoctal(c)  ((unsigned)((c) - '0') < '8')
 
 struct sparc_it
   {
@@ -134,666 +187,1041 @@ struct sparc_it
     unsigned long opcode;
     struct nlist *nlistp;
     expressionS exp;
+    expressionS exp2;
     int pcrel;
     bfd_reloc_code_real_type reloc;
   };
 
 struct sparc_it the_insn, set_insn;
 
-/* Return non-zero if VAL is in the range -(MAX+1) to MAX.  */
+static void output_insn
+  PARAMS ((const struct sparc_opcode *, struct sparc_it *));
+\f
+/* Table of arguments to -A.
+   The sparc_opcode_arch table in sparc-opc.c is insufficient and incorrect
+   for this use.  That table is for opcodes only.  This table is for opcodes
+   and file formats.  */
 
-static INLINE int
-in_signed_range (val, max)
-     bfd_signed_vma val, max;
-{
-  if (max <= 0)
-    abort ();
-  if (val > max)
-    return 0;
-  if (val < ~max)
-    return 0;
-  return 1;
-}
+enum sparc_arch_types {v6, v7, v8, sparclet, sparclite, sparc86x, v8plus,
+                      v8plusa, v9, v9a, v9_64};
 
-/* Return non-zero if VAL is in the range -(MAX/2+1) to MAX.
-   (e.g. -15 to +31).  */
+static struct sparc_arch {
+  char *name;
+  char *opcode_arch;
+  enum sparc_arch_types arch_type;
+  /* Default word size, as specified during configuration.
+     A value of zero means can't be used to specify default architecture.  */
+  int default_arch_size;
+  /* Allowable arg to -A?  */
+  int user_option_p;
+} sparc_arch_table[] = {
+  { "v6", "v6", v6, 0, 1 },
+  { "v7", "v7", v7, 0, 1 },
+  { "v8", "v8", v8, 32, 1 },
+  { "sparclet", "sparclet", sparclet, 32, 1 },
+  { "sparclite", "sparclite", sparclite, 32, 1 },
+  { "sparc86x", "sparclite", sparc86x, 32, 1 },
+  { "v8plus", "v9", v9, 0, 1 },
+  { "v8plusa", "v9a", v9, 0, 1 },
+  { "v9", "v9", v9, 0, 1 },
+  { "v9a", "v9a", v9, 0, 1 },
+  /* This exists to allow configure.in/Makefile.in to pass one
+     value to specify both the default machine and default word size.  */
+  { "v9-64", "v9", v9, 64, 0 },
+  { NULL, NULL, v8, 0, 0 }
+};
 
-static INLINE int
-in_bitfield_range (val, max)
-     bfd_signed_vma val, max;
+/* Variant of default_arch */
+static enum sparc_arch_types default_arch_type;
+
+static struct sparc_arch *
+lookup_arch (name)
+     char *name;
 {
-  if (max <= 0)
-    abort ();
-  if (val > max)
-    return 0;
-  if (val < ~(max >> 1))
-    return 0;
-  return 1;
+  struct sparc_arch *sa;
+
+  for (sa = &sparc_arch_table[0]; sa->name != NULL; sa++)
+    if (strcmp (sa->name, name) == 0)
+      break;
+  if (sa->name == NULL)
+    return NULL;
+  return sa;
 }
 
-static int
-sparc_ffs (mask)
-     unsigned int mask;
+/* Initialize the default opcode arch and word size from the default
+   architecture name.  */
+
+static void
+init_default_arch ()
 {
-  int i;
+  struct sparc_arch *sa = lookup_arch (default_arch);
+
+  if (sa == NULL
+      || sa->default_arch_size == 0)
+    as_fatal (_("Invalid default architecture, broken assembler."));
+
+  max_architecture = sparc_opcode_lookup_arch (sa->opcode_arch);
+  if (max_architecture == SPARC_OPCODE_ARCH_BAD)
+    as_fatal (_("Bad opcode table, broken assembler."));
+  default_arch_size = sparc_arch_size = sa->default_arch_size;
+  default_init_p = 1;
+  default_arch_type = sa->arch_type;
+}
 
-  if (mask == 0)
-    return -1;
+/* Called by TARGET_FORMAT.  */
 
-  for (i = 0; (mask & 1) == 0; ++i)
-    mask >>= 1;
-  return i;
-}
+const char *
+sparc_target_format ()
+{
+  /* We don't get a chance to initialize anything before we're called,
+     so handle that now.  */
+  if (! default_init_p)
+    init_default_arch ();
 
-#if 0
-static void print_insn PARAMS ((struct sparc_it *insn));
+#ifdef OBJ_AOUT
+#ifdef TE_NetBSD
+  return "a.out-sparc-netbsd";
+#else
+#ifdef TE_SPARCAOUT
+  if (target_big_endian)
+    return "a.out-sunos-big";
+  else if (default_arch_type == sparc86x && target_little_endian_data)
+    return "a.out-sunos-big";
+  else return "a.out-sparc-little";
+#else
+  return "a.out-sunos-big";
+#endif
+#endif
 #endif
-static int getExpression PARAMS ((char *str));
 
-static char *expr_end;
-static int special_case;
+#ifdef OBJ_BOUT
+  return "b.out.big";
+#endif
 
-/*
- * Instructions that require wierd handling because they're longer than
- * 4 bytes.
- */
-#define        SPECIAL_CASE_SET        1
-#define        SPECIAL_CASE_FDIV       2
+#ifdef OBJ_COFF
+#ifdef TE_LYNX
+  return "coff-sparc-lynx";
+#else
+  return "coff-sparc";
+#endif
+#endif
 
-/* The last instruction to be assembled.  */
-static const struct sparc_opcode *last_insn;
+#ifdef OBJ_ELF
+  return sparc_arch_size == 64 ? "elf64-sparc" : "elf32-sparc";
+#endif
 
+  abort ();
+}
+\f
 /*
- * sort of like s_lcomm
+ * md_parse_option
+ *     Invocation line includes a switch not recognized by the base assembler.
+ *     See if it's a processor-specific option.  These are:
+ *
+ *     -bump
+ *             Warn on architecture bumps.  See also -A.
+ *
+ *     -Av6, -Av7, -Av8, -Asparclite, -Asparclet
+ *             Standard 32 bit architectures.
+ *     -Av8plus, -Av8plusa
+ *             Sparc64 in a 32 bit world.
+ *     -Av9, -Av9a
+ *             Sparc64 in either a 32 or 64 bit world (-32/-64 says which).
+ *             This used to only mean 64 bits, but properly specifying it
+ *             complicated gcc's ASM_SPECs, so now opcode selection is
+ *             specified orthogonally to word size (except when specifying
+ *             the default, but that is an internal implementation detail).
+ *     -xarch=v8plus, -xarch=v8plusa
+ *             Same as -Av8plus{,a}, for compatibility with Sun's assembler.
+ *
+ *             Select the architecture and possibly the file format.
+ *             Instructions or features not supported by the selected
+ *             architecture cause fatal errors.
+ *
+ *             The default is to start at v6, and bump the architecture up
+ *             whenever an instruction is seen at a higher level.  In 32 bit
+ *             environments, v9 is not bumped up to, the user must pass
+ *             -Av8plus{,a}.
+ *
+ *             If -bump is specified, a warning is printing when bumping to
+ *             higher levels.
+ *
+ *             If an architecture is specified, all instructions must match
+ *             that architecture.  Any higher level instructions are flagged
+ *             as errors.  Note that in the 32 bit environment specifying
+ *             -Av8plus does not automatically create a v8plus object file, a
+ *             v9 insn must be seen.
  *
+ *             If both an architecture and -bump are specified, the
+ *             architecture starts at the specified level, but bumps are
+ *             warnings.  Note that we can't set `current_architecture' to
+ *             the requested level in this case: in the 32 bit environment,
+ *             we still must avoid creating v8plus object files unless v9
+ *             insns are seen.
+ *
+ * Note:
+ *             Bumping between incompatible architectures is always an
+ *             error.  For example, from sparclite to v9.
  */
-#ifndef OBJ_ELF
-static int max_alignment = 15;
+
+#ifdef OBJ_ELF
+CONST char *md_shortopts = "A:K:VQ:sq";
+#else
+#ifdef OBJ_AOUT
+CONST char *md_shortopts = "A:k";
+#else
+CONST char *md_shortopts = "A:";
+#endif
+#endif
+struct option md_longopts[] = {
+#define OPTION_BUMP (OPTION_MD_BASE)
+  {"bump", no_argument, NULL, OPTION_BUMP},
+#define OPTION_SPARC (OPTION_MD_BASE + 1)
+  {"sparc", no_argument, NULL, OPTION_SPARC},
+#define OPTION_XARCH (OPTION_MD_BASE + 2)
+  {"xarch", required_argument, NULL, OPTION_XARCH},
+#ifdef OBJ_ELF
+#define OPTION_32 (OPTION_MD_BASE + 3)
+  {"32", no_argument, NULL, OPTION_32},
+#define OPTION_64 (OPTION_MD_BASE + 4)
+  {"64", no_argument, NULL, OPTION_64},
+#define OPTION_TSO (OPTION_MD_BASE + 5)
+  {"TSO", no_argument, NULL, OPTION_TSO},
+#define OPTION_PSO (OPTION_MD_BASE + 6)
+  {"PSO", no_argument, NULL, OPTION_PSO},
+#define OPTION_RMO (OPTION_MD_BASE + 7)
+  {"RMO", no_argument, NULL, OPTION_RMO},
 #endif
+#ifdef SPARC_BIENDIAN
+#define OPTION_LITTLE_ENDIAN (OPTION_MD_BASE + 8)
+  {"EL", no_argument, NULL, OPTION_LITTLE_ENDIAN},
+#define OPTION_BIG_ENDIAN (OPTION_MD_BASE + 9)
+  {"EB", no_argument, NULL, OPTION_BIG_ENDIAN},
+#endif
+#define OPTION_ENFORCE_ALIGNED_DATA (OPTION_MD_BASE + 10)
+  {"enforce-aligned-data", no_argument, NULL, OPTION_ENFORCE_ALIGNED_DATA},
+#define OPTION_LITTLE_ENDIAN_DATA (OPTION_MD_BASE + 11)
+  {"little-endian-data", no_argument, NULL, OPTION_LITTLE_ENDIAN_DATA},
+  {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
 
-static void
-s_reserve (ignore)
-     int ignore;
+int
+md_parse_option (c, arg)
+     int c;
+     char *arg;
 {
-  char *name;
-  char *p;
-  char c;
-  int align;
-  int size;
-  int temp;
-  symbolS *symbolP;
-
-  name = input_line_pointer;
-  c = get_symbol_end ();
-  p = input_line_pointer;
-  *p = c;
-  SKIP_WHITESPACE ();
-
-  if (*input_line_pointer != ',')
-    {
-      as_bad ("Expected comma after name");
-      ignore_rest_of_line ();
-      return;
-    }
-
-  ++input_line_pointer;
+  /* We don't get a chance to initialize anything before we're called,
+     so handle that now.  */
+  if (! default_init_p)
+    init_default_arch ();
 
-  if ((size = get_absolute_expression ()) < 0)
+  switch (c)
     {
-      as_bad ("BSS length (%d.) <0! Ignored.", size);
-      ignore_rest_of_line ();
-      return;
-    }                          /* bad length */
-
-  *p = 0;
-  symbolP = symbol_find_or_make (name);
-  *p = c;
+    case OPTION_BUMP:
+      warn_on_bump = 1;
+      warn_after_architecture = SPARC_OPCODE_ARCH_V6;
+      break;
 
-  if (strncmp (input_line_pointer, ",\"bss\"", 6) != 0
-      && strncmp (input_line_pointer, ",\".bss\"", 7) != 0)
-    {
-      as_bad ("bad .reserve segment -- expected BSS segment");
-      return;
-    }
+    case OPTION_XARCH:
+      /* This is for compatibility with Sun's assembler.  */
+      if (strcmp (arg, "v8plus") != 0
+         && strcmp (arg, "v8plusa") != 0)
+       {
+         as_bad (_("invalid architecture -xarch=%s"), arg);
+         return 0;
+       }
 
-  if (input_line_pointer[2] == '.')
-    input_line_pointer += 7;
-  else
-    input_line_pointer += 6;
-  SKIP_WHITESPACE ();
+      /* fall through */
 
-  if (*input_line_pointer == ',')
-    {
-      ++input_line_pointer;
+    case 'A':
+      {
+       struct sparc_arch *sa;
+       enum sparc_opcode_arch_val opcode_arch;
 
-      SKIP_WHITESPACE ();
-      if (*input_line_pointer == '\n')
-       {
-         as_bad ("Missing alignment");
-         return;
-       }
+       sa = lookup_arch (arg);
+       if (sa == NULL
+           || ! sa->user_option_p)
+         {
+           as_bad (_("invalid architecture -A%s"), arg);
+           return 0;
+         }
 
-      align = get_absolute_expression ();
-#ifndef OBJ_ELF
-      if (align > max_alignment)
-       {
-         align = max_alignment;
-         as_warn ("Alignment too large: %d. assumed.", align);
-       }
-#endif
-      if (align < 0)
-       {
-         align = 0;
-         as_warn ("Alignment negative. 0 assumed.");
-       }
+       opcode_arch = sparc_opcode_lookup_arch (sa->opcode_arch);
+       if (opcode_arch == SPARC_OPCODE_ARCH_BAD)
+         as_fatal (_("Bad opcode table, broken assembler."));
 
-      record_alignment (bss_section, align);
+       max_architecture = opcode_arch;
+       architecture_requested = 1;
+      }
+      break;
 
-      /* convert to a power of 2 alignment */
-      for (temp = 0; (align & 1) == 0; align >>= 1, ++temp);;
+    case OPTION_SPARC:
+      /* Ignore -sparc, used by SunOS make default .s.o rule.  */
+      break;
 
-      if (align != 1)
-       {
-         as_bad ("Alignment not a power of 2");
-         ignore_rest_of_line ();
-         return;
-       }                       /* not a power of two */
+    case OPTION_ENFORCE_ALIGNED_DATA:
+      enforce_aligned_data = 1;
+      break;
 
-      align = temp;
-    }                          /* if has optional alignment */
-  else
-    align = 0;
+#ifdef SPARC_BIENDIAN
+    case OPTION_LITTLE_ENDIAN:
+      target_big_endian = 0;
+      if (default_arch_type != sparclet)
+       as_fatal ("This target does not support -EL");
+      break;
+    case OPTION_LITTLE_ENDIAN_DATA:
+      target_little_endian_data = 1;
+      target_big_endian = 0;
+      if (default_arch_type != sparc86x
+         && default_arch_type != v9)
+       as_fatal ("This target does not support --little-endian-data");
+      break;
+    case OPTION_BIG_ENDIAN:
+      target_big_endian = 1;
+      break;
+#endif
 
-  if (!S_IS_DEFINED (symbolP)
 #ifdef OBJ_AOUT
-      && S_GET_OTHER (symbolP) == 0
-      && S_GET_DESC (symbolP) == 0
+    case 'k':
+      sparc_pic_code = 1;
+      break;
 #endif
-      )
-    {
-      if (! need_pass_2)
-       {
-         char *pfrag;
-         segT current_seg = now_seg;
-         subsegT current_subseg = now_subseg;
 
-         subseg_set (bss_section, 1); /* switch to bss */
+#ifdef OBJ_ELF
+    case OPTION_32:
+    case OPTION_64:
+      {
+       const char **list, **l;
 
-         if (align)
-           frag_align (align, 0); /* do alignment */
+       sparc_arch_size = c == OPTION_32 ? 32 : 64;
+       list = bfd_target_list ();
+       for (l = list; *l != NULL; l++)
+         {
+           if (sparc_arch_size == 32)
+             {
+               if (strcmp (*l, "elf32-sparc") == 0)
+                 break;
+             }
+           else
+             {
+               if (strcmp (*l, "elf64-sparc") == 0)
+                 break;
+             }
+         }
+       if (*l == NULL)
+         as_fatal (_("No compiled in support for %d bit object file format"),
+                   sparc_arch_size);
+       free (list);
+      }
+      break;
 
-         /* detach from old frag */
-         if (S_GET_SEGMENT(symbolP) == bss_section)
-           symbolP->sy_frag->fr_symbol = NULL;
+    case OPTION_TSO:
+      sparc_memory_model = MM_TSO;
+      break;
 
-         symbolP->sy_frag = frag_now;
-         pfrag = frag_var (rs_org, 1, 1, (relax_substateT)0, symbolP,
-                           size, (char *)0);
-         *pfrag = 0;
+    case OPTION_PSO:
+      sparc_memory_model = MM_PSO;
+      break;
 
-         S_SET_SEGMENT (symbolP, bss_section);
+    case OPTION_RMO:
+      sparc_memory_model = MM_RMO;
+      break;
 
-         subseg_set (current_seg, current_subseg);
-       }
+    case 'V':
+      print_version_id ();
+      break;
+
+    case 'Q':
+      /* Qy - do emit .comment
+        Qn - do not emit .comment */
+      break;
+
+    case 's':
+      /* use .stab instead of .stab.excl */
+      break;
+
+    case 'q':
+      /* quick -- native assembler does fewer checks */
+      break;
+
+    case 'K':
+      if (strcmp (arg, "PIC") != 0)
+       as_warn (_("Unrecognized option following -K"));
+      else
+       sparc_pic_code = 1;
+      break;
+#endif
+
+    default:
+      return 0;
     }
-  else
-    {
-      as_warn("Ignoring attempt to re-define symbol %s",
-             S_GET_NAME (symbolP));
-    }                          /* if not redefining */
 
-  demand_empty_rest_of_line ();
+  return 1;
 }
 
-static void
-s_common (ignore)
-     int ignore;
+void
+md_show_usage (stream)
+     FILE *stream;
 {
-  char *name;
-  char c;
-  char *p;
-  int temp, size;
-  symbolS *symbolP;
+  const struct sparc_arch *arch;
 
-  name = input_line_pointer;
-  c = get_symbol_end ();
-  /* just after name is now '\0' */
-  p = input_line_pointer;
-  *p = c;
-  SKIP_WHITESPACE ();
-  if (*input_line_pointer != ',')
-    {
-      as_bad ("Expected comma after symbol-name");
-      ignore_rest_of_line ();
-      return;
-    }
-  input_line_pointer++;                /* skip ',' */
-  if ((temp = get_absolute_expression ()) < 0)
+  /* We don't get a chance to initialize anything before we're called,
+     so handle that now.  */
+  if (! default_init_p)
+    init_default_arch ();
+
+  fprintf(stream, _("SPARC options:\n"));
+  for (arch = &sparc_arch_table[0]; arch->name; arch++)
     {
-      as_bad (".COMMon length (%d.) <0! Ignored.", temp);
-      ignore_rest_of_line ();
-      return;
+      if (arch != &sparc_arch_table[0])
+       fprintf (stream, " | ");
+      if (arch->user_option_p)
+       fprintf (stream, "-A%s", arch->name);
     }
-  size = temp;
-  *p = 0;
-  symbolP = symbol_find_or_make (name);
-  *p = c;
-  if (S_IS_DEFINED (symbolP))
+  fprintf (stream, _("\n-xarch=v8plus | -xarch=v8plusa\n"));
+  fprintf (stream, _("\
+                       specify variant of SPARC architecture\n\
+-bump                  warn when assembler switches architectures\n\
+-sparc                 ignored\n\
+--enforce-aligned-data force .long, etc., to be aligned correctly\n"));
+#ifdef OBJ_AOUT
+  fprintf (stream, _("\
+-k                     generate PIC\n"));
+#endif
+#ifdef OBJ_ELF
+  fprintf (stream, _("\
+-32                    create 32 bit object file\n\
+-64                    create 64 bit object file\n"));
+  fprintf (stream, _("\
+                       [default is %d]\n"), default_arch_size);
+  fprintf (stream, _("\
+-TSO                   use Total Store Ordering\n\
+-PSO                   use Partial Store Ordering\n\
+-RMO                   use Relaxed Memory Ordering\n"));
+  fprintf (stream, _("\
+                       [default is %s]\n"), (default_arch_size == 64) ? "RMO" : "TSO");
+  fprintf (stream, _("\
+-KPIC                  generate PIC\n\
+-V                     print assembler version number\n\
+-q                     ignored\n\
+-Qy, -Qn               ignored\n\
+-s                     ignored\n"));
+#endif
+#ifdef SPARC_BIENDIAN
+  fprintf (stream, _("\
+-EL                    generate code for a little endian machine\n\
+-EB                    generate code for a big endian machine\n\
+--little-endian-data   generate code for a machine having big endian\n\
+                        instructions and little endian data."));
+#endif
+}
+\f
+/* native operand size opcode translation */
+struct
+  {
+    char *name;
+    char *name32;
+    char *name64;
+  } native_op_table[] =
+{
+  {"ldn", "ld", "ldx"},
+  {"ldna", "lda", "ldxa"},
+  {"stn", "st", "stx"},
+  {"stna", "sta", "stxa"},
+  {"slln", "sll", "sllx"},
+  {"srln", "srl", "srlx"},
+  {"sran", "sra", "srax"},
+  {"casn", "cas", "casx"},
+  {"casna", "casa", "casxa"},
+  {"clrn", "clr", "clrx"},
+  {NULL, NULL, NULL},
+};
+\f
+/* sparc64 priviledged registers */
+
+struct priv_reg_entry
+  {
+    char *name;
+    int regnum;
+  };
+
+struct priv_reg_entry priv_reg_table[] =
+{
+  {"tpc", 0},
+  {"tnpc", 1},
+  {"tstate", 2},
+  {"tt", 3},
+  {"tick", 4},
+  {"tba", 5},
+  {"pstate", 6},
+  {"tl", 7},
+  {"pil", 8},
+  {"cwp", 9},
+  {"cansave", 10},
+  {"canrestore", 11},
+  {"cleanwin", 12},
+  {"otherwin", 13},
+  {"wstate", 14},
+  {"fq", 15},
+  {"ver", 31},
+  {"", -1},                    /* end marker */
+};
+
+/* v9a specific asrs */
+
+struct priv_reg_entry v9a_asr_table[] =
+{
+  {"tick_cmpr", 23},
+  {"softint", 22},
+  {"set_softint", 20},
+  {"pic", 17},
+  {"pcr", 16},
+  {"gsr", 19},
+  {"dcr", 18},
+  {"clear_softint", 21},
+  {"", -1},                    /* end marker */
+};
+
+static int
+cmp_reg_entry (parg, qarg)
+     const PTR parg;
+     const PTR qarg;
+{
+  const struct priv_reg_entry *p = (const struct priv_reg_entry *) parg;
+  const struct priv_reg_entry *q = (const struct priv_reg_entry *) qarg;
+
+  return strcmp (q->name, p->name);
+}
+\f
+/* 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 ()
+{
+  register const char *retval = NULL;
+  int lose = 0;
+  register unsigned int i = 0;
+
+  /* We don't get a chance to initialize anything before md_parse_option
+     is called, and it may not be called, so handle default initialization
+     now if not already done.  */
+  if (! default_init_p)
+    init_default_arch ();
+
+  op_hash = hash_new ();
+
+  while (i < (unsigned int) sparc_num_opcodes)
     {
-      as_bad ("Ignoring attempt to re-define symbol");
-      ignore_rest_of_line ();
-      return;
+      const char *name = sparc_opcodes[i].name;
+      retval = hash_insert (op_hash, name, (PTR) &sparc_opcodes[i]);
+      if (retval != NULL)
+       {
+         as_bad (_("Internal error: can't hash `%s': %s\n"),
+                 sparc_opcodes[i].name, retval);
+         lose = 1;
+       }
+      do
+       {
+         if (sparc_opcodes[i].match & sparc_opcodes[i].lose)
+           {
+             as_bad (_("Internal error: losing opcode: `%s' \"%s\"\n"),
+                     sparc_opcodes[i].name, sparc_opcodes[i].args);
+             lose = 1;
+           }
+         ++i;
+       }
+      while (i < (unsigned int) sparc_num_opcodes
+            && !strcmp (sparc_opcodes[i].name, name));
     }
-  if (S_GET_VALUE (symbolP) != 0)
+
+  for (i = 0; native_op_table[i].name; i++)
     {
-      if (S_GET_VALUE (symbolP) != size)
+      const struct sparc_opcode *insn;
+      char *name = sparc_arch_size == 32 ? native_op_table[i].name32 :
+                       native_op_table[i].name64;
+      insn = (struct sparc_opcode *)hash_find (op_hash, name);
+      if (insn == NULL)
+        {
+          as_bad (_("Internal error: can't find opcode `%s' for `%s'\n"),
+                 name, native_op_table[i].name);
+          lose = 1;
+        }
+      else
        {
-         as_warn ("Length of .comm \"%s\" is already %ld. Not changed to %d.",
-                  S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), size);
+         retval = hash_insert (op_hash, native_op_table[i].name, (PTR) insn);
+         if (retval != NULL)
+           {
+             as_bad (_("Internal error: can't hash `%s': %s\n"),
+                     sparc_opcodes[i].name, retval);
+             lose = 1;
+           }
        }
     }
-  else
+
+  if (lose)
+    as_fatal (_("Broken assembler.  No assembly attempted."));
+
+  qsort (priv_reg_table, sizeof (priv_reg_table) / sizeof (priv_reg_table[0]),
+        sizeof (priv_reg_table[0]), cmp_reg_entry);
+
+  /* If -bump, record the architecture level at which we start issuing
+     warnings.  The behaviour is different depending upon whether an
+     architecture was explicitly specified.  If it wasn't, we issue warnings
+     for all upwards bumps.  If it was, we don't start issuing warnings until
+     we need to bump beyond the requested architecture or when we bump between
+     conflicting architectures.  */
+
+  if (warn_on_bump
+      && architecture_requested)
     {
-#ifndef OBJ_ELF
-      S_SET_VALUE (symbolP, (valueT) size);
-      S_SET_EXTERNAL (symbolP);
-#endif
+      /* `max_architecture' records the requested architecture.
+        Issue warnings if we go above it.  */
+      warn_after_architecture = max_architecture;
+
+      /* Find the highest architecture level that doesn't conflict with
+        the requested one.  */
+      for (max_architecture = SPARC_OPCODE_ARCH_MAX;
+          max_architecture > warn_after_architecture;
+          --max_architecture)
+       if (! SPARC_OPCODE_CONFLICT_P (max_architecture,
+                                      warn_after_architecture))
+         break;
     }
-  know (symbolP->sy_frag == &zero_address_frag);
-  if (*input_line_pointer != ',')
+}
+
+/* Called after all assembly has been done.  */
+
+void
+sparc_md_end ()
+{
+  if (sparc_arch_size == 64)
     {
-      as_bad ("Expected comma after common length");
-      ignore_rest_of_line ();
-      return;
+      if (current_architecture == SPARC_OPCODE_ARCH_V9A)
+       bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_v9a);
+      else
+       bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_v9);
     }
-  input_line_pointer++;
-  SKIP_WHITESPACE ();
-  if (*input_line_pointer != '"')
+  else
     {
-      temp = get_absolute_expression ();
-#ifndef OBJ_ELF
-      if (temp > max_alignment)
-       {
-         temp = max_alignment;
-         as_warn ("Common alignment too large: %d. assumed", temp);
-       }
-#endif
-      if (temp < 0)
-       {
-         temp = 0;
-         as_warn ("Common alignment negative; 0 assumed");
-       }
-#ifdef OBJ_ELF
-      if (symbolP->local)
-       {
-         segT old_sec;
-         int old_subsec;
-         char *p;
-         int align;
-
-         old_sec = now_seg;
-         old_subsec = now_subseg;
-         align = temp;
-         record_alignment (bss_section, align);
-         subseg_set (bss_section, 0);
-         if (align)
-           frag_align (align, 0);
-         if (S_GET_SEGMENT (symbolP) == bss_section)
-           symbolP->sy_frag->fr_symbol = 0;
-         symbolP->sy_frag = frag_now;
-         p = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, size,
-                       (char *) 0);
-         *p = 0;
-         S_SET_SEGMENT (symbolP, bss_section);
-         S_CLEAR_EXTERNAL (symbolP);
-         subseg_set (old_sec, old_subsec);
-       }
+      if (current_architecture == SPARC_OPCODE_ARCH_V9)
+       bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_v8plus);
+      else if (current_architecture == SPARC_OPCODE_ARCH_V9A)
+       bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_v8plusa);
+      else if (current_architecture == SPARC_OPCODE_ARCH_SPARCLET)
+       bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_sparclet);
+      else if (default_arch_type == sparc86x && target_little_endian_data)
+       bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_sparclite_le);
       else
-#endif
        {
-       allocate_common:
-         S_SET_VALUE (symbolP, (valueT) size);
-#ifdef OBJ_ELF
-         S_SET_ALIGN (symbolP, temp);
-#endif
-         S_SET_EXTERNAL (symbolP);
-         /* should be common, but this is how gas does it for now */
-         S_SET_SEGMENT (symbolP, bfd_und_section_ptr);
+         /* The sparclite is treated like a normal sparc.  Perhaps it shouldn't
+            be but for now it is (since that's the way it's always been
+            treated).  */
+         bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc);
        }
     }
-  else
-    {
-      input_line_pointer++;
-      /* @@ Some use the dot, some don't.  Can we get some consistency??  */
-      if (*input_line_pointer == '.')
-       input_line_pointer++;
-      /* @@ Some say data, some say bss.  */
-      if (strncmp (input_line_pointer, "bss\"", 4)
-         && strncmp (input_line_pointer, "data\"", 5))
-       {
-         while (*--input_line_pointer != '"')
-           ;
-         input_line_pointer--;
-         goto bad_common_segment;
-       }
-      while (*input_line_pointer++ != '"')
-       ;
-      goto allocate_common;
-    }
-  demand_empty_rest_of_line ();
-  return;
-
-  {
-  bad_common_segment:
-    p = input_line_pointer;
-    while (*p && *p != '\n')
-      p++;
-    c = *p;
-    *p = '\0';
-    as_bad ("bad .common segment %s", input_line_pointer + 1);
-    *p = c;
-    input_line_pointer = p;
-    ignore_rest_of_line ();
-    return;
-  }
-}
-
-/* Handle the .empty pseudo-op.  This supresses the warnings about
-   invalid delay slot usage.  */
-
-static void
-s_empty (ignore)
-     int ignore;
-{
-  /* The easy way to implement is to just forget about the last
-     instruction.  */
-  last_insn = NULL;
 }
+\f
+/* Return non-zero if VAL is in the range -(MAX+1) to MAX.  */
 
-static void
-s_seg (ignore)
-     int ignore;
+static INLINE int
+in_signed_range (val, max)
+     bfd_signed_vma val, max;
 {
-
-  if (strncmp (input_line_pointer, "\"text\"", 6) == 0)
-    {
-      input_line_pointer += 6;
-      s_text (0);
-      return;
-    }
-  if (strncmp (input_line_pointer, "\"data\"", 6) == 0)
-    {
-      input_line_pointer += 6;
-      s_data (0);
-      return;
-    }
-  if (strncmp (input_line_pointer, "\"data1\"", 7) == 0)
-    {
-      input_line_pointer += 7;
-      s_data1 ();
-      return;
-    }
-  if (strncmp (input_line_pointer, "\"bss\"", 5) == 0)
+  if (max <= 0)
+    abort ();
+  /* Sign-extend the value from the architecture word size, so that
+     0xffffffff is always considered -1 on sparc32.  */
+  if (sparc_arch_size == 32)
     {
-      input_line_pointer += 5;
-      /* We only support 2 segments -- text and data -- for now, so
-        things in the "bss segment" will have to go into data for now.
-        You can still allocate SEG_BSS stuff with .lcomm or .reserve. */
-      subseg_set (data_section, 255);  /* FIXME-SOMEDAY */
-      return;
+      bfd_signed_vma sign = (bfd_signed_vma)1 << 31;
+      val = ((val & 0xffffffff) ^ sign) - sign;
     }
-  as_bad ("Unknown segment type");
-  demand_empty_rest_of_line ();
+  if (val > max)
+    return 0;
+  if (val < ~max)
+    return 0;
+  return 1;
 }
 
-static void
-s_data1 ()
+/* Return non-zero if VAL is in the range 0 to MAX.  */
+
+static INLINE int
+in_unsigned_range (val, max)
+     bfd_vma val, max;
 {
-  subseg_set (data_section, 1);
-  demand_empty_rest_of_line ();
+  if (val > max)
+    return 0;
+  return 1;
 }
 
-static void
-s_proc (ignore)
-     int ignore;
+/* Return non-zero if VAL is in the range -(MAX/2+1) to MAX.
+   (e.g. -15 to +31).  */
+
+static INLINE int
+in_bitfield_range (val, max)
+     bfd_signed_vma val, max;
 {
-  while (!is_end_of_line[(unsigned char) *input_line_pointer])
-    {
-      ++input_line_pointer;
-    }
-  ++input_line_pointer;
+  if (max <= 0)
+    abort ();
+  if (val > max)
+    return 0;
+  if (val < ~(max >> 1))
+    return 0;
+  return 1;
 }
 
-/* This static variable is set by s_uacons to tell sparc_cons_align
-   that the expession does not need to be aligned.  */
+static int
+sparc_ffs (mask)
+     unsigned int mask;
+{
+  int i;
 
-static int sparc_no_align_cons = 0;
+  if (mask == 0)
+    return -1;
 
-/* This handles the unaligned space allocation pseudo-ops, such as
-   .uaword.  .uaword is just like .word, but the value does not need
-   to be aligned.  */
+  for (i = 0; (mask & 1) == 0; ++i)
+    mask >>= 1;
+  return i;
+}
 
-static void
-s_uacons (bytes)
-     int bytes;
+/* Implement big shift right.  */
+static bfd_vma
+BSR (val, amount)
+     bfd_vma val;
+     int amount;
 {
-  /* Tell sparc_cons_align not to align this value.  */
-  sparc_no_align_cons = 1;
-  cons (bytes);
+  if (sizeof (bfd_vma) <= 4 && amount >= 32)
+    as_fatal (_("Support for 64-bit arithmetic not compiled in."));
+  return val >> amount;
 }
+\f
+/* For communication between sparc_ip and get_expression.  */
+static char *expr_end;
 
-/* We require .word, et. al., to be aligned correctly.  We do it by
-   setting up an rs_align_code frag, and checking in HANDLE_ALIGN to
-   make sure that no unexpected alignment was introduced.  */
+/* Values for `special_case'.
+   Instructions that require wierd handling because they're longer than
+   4 bytes.  */
+#define SPECIAL_CASE_NONE      0
+#define        SPECIAL_CASE_SET        1
+#define SPECIAL_CASE_SETSW     2
+#define SPECIAL_CASE_SETX      3
+/* FIXME: sparc-opc.c doesn't have necessary "S" trigger to enable this.  */
+#define        SPECIAL_CASE_FDIV       4
+
+/* Bit masks of various insns.  */
+#define NOP_INSN 0x01000000
+#define OR_INSN 0x80100000
+#define XOR_INSN 0x80180000
+#define FMOVS_INSN 0x81A00020
+#define SETHI_INSN 0x01000000
+#define SLLX_INSN 0x81281000
+#define SRA_INSN 0x81380000
 
-void
-sparc_cons_align (nbytes)
-     int nbytes;
+/* The last instruction to be assembled.  */
+static const struct sparc_opcode *last_insn;
+/* The assembled opcode of `last_insn'.  */
+static unsigned long last_opcode;
+\f
+/* Handle the set and setuw synthetic instructions.  */
+static void
+synthetize_setuw (insn)
+     const struct sparc_opcode *insn;
 {
-  int nalign;
-  char *p;
+  int need_hi22_p = 0;
+  int rd = (the_insn.opcode & RD (~0)) >> 25;
 
-  if (sparc_no_align_cons)
+  if (the_insn.exp.X_op == O_constant)
     {
-      /* This is an unaligned pseudo-op.  */
-      sparc_no_align_cons = 0;
-      return;
+      if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
+       {
+         if (sizeof(offsetT) > 4
+             && (the_insn.exp.X_add_number < 0
+                 || the_insn.exp.X_add_number > (offsetT) 0xffffffff))
+           as_warn (_("set: number not in 0..4294967295 range"));
+       }
+      else
+       {
+         if (sizeof(offsetT) > 4
+             && (the_insn.exp.X_add_number < -(offsetT) 0x80000000
+                 || the_insn.exp.X_add_number > (offsetT) 0xffffffff))
+           as_warn (_("set: number not in -2147483648..4294967295 range"));
+         the_insn.exp.X_add_number = (int)the_insn.exp.X_add_number;
+       }
     }
 
-  nalign = 0;
-  while ((nbytes & 1) == 0)
+  /* See if operand is absolute and small; skip sethi if so.  */
+  if (the_insn.exp.X_op != O_constant
+      || the_insn.exp.X_add_number >= (1 << 12)
+      || the_insn.exp.X_add_number < -(1 << 12))
     {
-      ++nalign;
-      nbytes >>= 1;
+      the_insn.opcode = (SETHI_INSN | RD (rd)
+                        | ((the_insn.exp.X_add_number >> 10)
+                           & (the_insn.exp.X_op == O_constant ? 0x3fffff : 0)));
+      the_insn.reloc = (the_insn.exp.X_op != O_constant
+                       ? BFD_RELOC_HI22
+                       : BFD_RELOC_NONE);
+      output_insn (insn, &the_insn);
+      need_hi22_p = 1;
     }
 
-  if (nalign == 0)
-    return;
-
-  if (now_seg == absolute_section)
+  /* See if operand has no low-order bits; skip OR if so.  */
+  if (the_insn.exp.X_op != O_constant
+      || (need_hi22_p && (the_insn.exp.X_add_number & 0x3FF) != 0)
+      || ! need_hi22_p)
     {
-      if ((abs_section_offset & ((1 << nalign) - 1)) != 0)
-       as_bad ("misaligned data");
-      return;
+      the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (rd) : 0)
+                        | RD (rd) | IMMED
+                        | (the_insn.exp.X_add_number
+                           & (the_insn.exp.X_op != O_constant ? 0 :
+                              need_hi22_p ? 0x3ff : 0x1fff)));
+      the_insn.reloc = (the_insn.exp.X_op != O_constant
+                       ? BFD_RELOC_LO10
+                       : BFD_RELOC_NONE);
+      output_insn (insn, &the_insn);
     }
-
-  p = frag_var (rs_align_code, 1, 1, (relax_substateT) 0,
-               (symbolS *) NULL, (long) nalign, (char *) NULL);
-
-  record_alignment (now_seg, nalign);
 }
-
-/* This is where we do the unexpected alignment check.  */
-
-void
-sparc_handle_align (fragp)
-     fragS *fragp;
+    
+/* Handle the setsw synthetic instruction.  */
+static void
+synthetize_setsw (insn)
+     const struct sparc_opcode *insn;
 {
-  if (fragp->fr_type == rs_align_code
-      && fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix != 0)
-    as_bad_where (fragp->fr_file, fragp->fr_line, "misaligned data");
-}
+  int low32, rd, opc;
 
-/* sparc64 priviledged registers */
+  rd = (the_insn.opcode & RD (~0)) >> 25;
 
-struct priv_reg_entry
-  {
-    char *name;
-    int regnum;
-  };
+  if (the_insn.exp.X_op != O_constant)
+    {
+      synthetize_setuw (insn);
 
-struct priv_reg_entry priv_reg_table[] =
-{
-  {"tpc", 0},
-  {"tnpc", 1},
-  {"tstate", 2},
-  {"tt", 3},
-  {"tick", 4},
-  {"tba", 5},
-  {"pstate", 6},
-  {"tl", 7},
-  {"pil", 8},
-  {"cwp", 9},
-  {"cansave", 10},
-  {"canrestore", 11},
-  {"cleanwin", 12},
-  {"otherwin", 13},
-  {"wstate", 14},
-  {"fq", 15},
-  {"ver", 31},
-  {"", -1},                    /* end marker */
-};
+      /* Need to sign extend it.  */
+      the_insn.opcode = (SRA_INSN | RS1 (rd) | RD (rd));
+      the_insn.reloc = BFD_RELOC_NONE;
+      output_insn (insn, &the_insn);
+      return;
+    }
 
-static int
-cmp_reg_entry (p, q)
-     struct priv_reg_entry *p, *q;
-{
-  return strcmp (q->name, p->name);
-}
+  if (sizeof(offsetT) > 4
+      && (the_insn.exp.X_add_number < -(offsetT) 0x80000000
+         || the_insn.exp.X_add_number > (offsetT) 0xffffffff))
+    as_warn (_("setsw: number not in -2147483648..4294967295 range"));
 
-/* 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. */
+  low32 = the_insn.exp.X_add_number;   
+             
+  if (low32 >= 0)
+    {
+      synthetize_setuw (insn);
+      return;
+    }
 
-void
-md_begin ()
-{
-  register const char *retval = NULL;
-  int lose = 0;
-  register unsigned int i = 0;
+  opc = OR_INSN;
+    
+  the_insn.reloc = BFD_RELOC_NONE;
+  /* See if operand is absolute and small; skip sethi if so.  */
+  if (low32 < -(1 << 12))
+    {
+      the_insn.opcode = (SETHI_INSN | RD (rd)
+                        | (((~the_insn.exp.X_add_number) >> 10) & 0x3fffff));
+      output_insn (insn, &the_insn);
+      low32 = 0x1c00 | (low32 & 0x3ff);
+      opc = RS1 (rd) | XOR_INSN;
+    }
 
-  op_hash = hash_new ();
+  the_insn.opcode = (opc | RD (rd) | IMMED
+                    | (low32 & 0x1fff));
+  output_insn (insn, &the_insn);
+}
 
-  while (i < sparc_num_opcodes)
+/* Handle the setsw synthetic instruction.  */
+static void
+synthetize_setx (insn)
+     const struct sparc_opcode *insn;
+{
+  int upper32, lower32;
+  int tmpreg = (the_insn.opcode & RS1 (~0)) >> 14;
+  int dstreg = (the_insn.opcode & RD (~0)) >> 25;
+  int upper_dstreg;
+  int need_hh22_p = 0, need_hm10_p = 0, need_hi22_p = 0, need_lo10_p = 0;
+  int need_xor10_p = 0;
+    
+#define SIGNEXT32(x) ((((x) & 0xffffffff) ^ 0x80000000) - 0x80000000)
+  lower32 = SIGNEXT32 (the_insn.exp.X_add_number);
+  upper32 = SIGNEXT32 (BSR (the_insn.exp.X_add_number, 32));
+#undef SIGNEXT32
+
+  upper_dstreg = tmpreg;
+  /* The tmp reg should not be the dst reg.  */
+  if (tmpreg == dstreg)
+    as_warn (_("setx: temporary register same as destination register"));
+
+  /* ??? Obviously there are other optimizations we can do
+     (e.g. sethi+shift for 0x1f0000000) and perhaps we shouldn't be
+     doing some of these.  Later.  If you do change things, try to
+     change all of this to be table driven as well.  */
+  /* What to output depends on the number if it's constant.
+     Compute that first, then output what we've decided upon.  */
+  if (the_insn.exp.X_op != O_constant)
     {
-      const char *name = sparc_opcodes[i].name;
-      retval = hash_insert (op_hash, name, &sparc_opcodes[i]);
-      if (retval != NULL)
+      if (sparc_arch_size == 32)
        {
-         fprintf (stderr, "internal error: can't hash `%s': %s\n",
-                  sparc_opcodes[i].name, retval);
-         lose = 1;
+         /* When arch size is 32, we want setx to be equivalent
+            to setuw for anything but constants.  */
+         the_insn.exp.X_add_number &= 0xffffffff;
+         synthetize_setuw (insn);
+         return;
        }
-      do
+      need_hh22_p = need_hm10_p = need_hi22_p = need_lo10_p = 1;
+      lower32 = 0; upper32 = 0;
+    }
+  else
+    {
+      /* Reset X_add_number, we've extracted it as upper32/lower32.
+        Otherwise fixup_segment will complain about not being able to
+        write an 8 byte number in a 4 byte field.  */
+      the_insn.exp.X_add_number = 0;
+    
+      /* Only need hh22 if `or' insn can't handle constant.  */
+      if (upper32 < -(1 << 12) || upper32 >= (1 << 12))
+       need_hh22_p = 1;
+    
+      /* Does bottom part (after sethi) have bits?  */
+      if ((need_hh22_p && (upper32 & 0x3ff) != 0)
+         /* No hh22, but does upper32 still have bits we can't set
+            from lower32?  */
+         || (! need_hh22_p && upper32 != 0 && upper32 != -1))
+       need_hm10_p = 1;
+    
+      /* If the lower half is all zero, we build the upper half directly
+        into the dst reg.  */
+      if (lower32 != 0
+         /* Need lower half if number is zero or 0xffffffff00000000.  */
+         || (! need_hh22_p && ! need_hm10_p))
        {
-         if (sparc_opcodes[i].match & sparc_opcodes[i].lose)
-           {
-             fprintf (stderr, "internal error: losing opcode: `%s' \"%s\"\n",
-                      sparc_opcodes[i].name, sparc_opcodes[i].args);
-             lose = 1;
-           }
-         ++i;
+         /* No need for sethi if `or' insn can handle constant.  */
+         if (lower32 < -(1 << 12) || lower32 >= (1 << 12)
+             /* Note that we can't use a negative constant in the `or'
+                insn unless the upper 32 bits are all ones.  */
+             || (lower32 < 0 && upper32 != -1)
+             || (lower32 >= 0 && upper32 == -1))
+           need_hi22_p = 1;
+                     
+         if (need_hi22_p && upper32 == -1)
+           need_xor10_p = 1;
+
+         /* Does bottom part (after sethi) have bits?  */
+         else if ((need_hi22_p && (lower32 & 0x3ff) != 0)
+                  /* No sethi.  */
+                  || (! need_hi22_p && (lower32 & 0x1fff) != 0)
+                  /* Need `or' if we didn't set anything else.  */
+                  || (! need_hi22_p && ! need_hh22_p && ! need_hm10_p))
+           need_lo10_p = 1;
        }
-      while (i < sparc_num_opcodes
-            && !strcmp (sparc_opcodes[i].name, name));
+      else
+       /* Output directly to dst reg if lower 32 bits are all zero.  */
+       upper_dstreg = dstreg;
     }
+    
+  if (!upper_dstreg && dstreg)
+    as_warn (_("setx: illegal temporary register g0"));
 
-  if (lose)
-    as_fatal ("Broken assembler.  No assembly attempted.");
+  if (need_hh22_p)
+    {
+      the_insn.opcode = (SETHI_INSN | RD (upper_dstreg)
+                        | ((upper32 >> 10) & 0x3fffff));
+      the_insn.reloc = (the_insn.exp.X_op != O_constant
+                       ? BFD_RELOC_SPARC_HH22 : BFD_RELOC_NONE);
+      output_insn (insn, &the_insn);
+    }
+    
+  if (need_hi22_p)
+    {
+      the_insn.opcode = (SETHI_INSN | RD (dstreg)
+                        | (((need_xor10_p ? ~lower32 : lower32)
+                           >> 10) & 0x3fffff));
+      the_insn.reloc = (the_insn.exp.X_op != O_constant
+                       ? BFD_RELOC_SPARC_LM22 : BFD_RELOC_NONE);
+      output_insn (insn, &the_insn);
+    }
 
-  for (i = '0'; i < '8'; ++i)
-    octal[i] = 1;
-  for (i = '0'; i <= '9'; ++i)
-    toHex[i] = i - '0';
-  for (i = 'a'; i <= 'f'; ++i)
-    toHex[i] = i + 10 - 'a';
-  for (i = 'A'; i <= 'F'; ++i)
-    toHex[i] = i + 10 - 'A';
-
-  qsort (priv_reg_table, sizeof (priv_reg_table) / sizeof (priv_reg_table[0]),
-        sizeof (priv_reg_table[0]), cmp_reg_entry);
-
-  target_big_endian = 1;
-
-  /* If -bump, record the architecture level at which we start issuing
-     warnings.  The behaviour is different depending upon whether an
-     architecture was explicitly specified.  If it wasn't, we issue warnings
-     for all upwards bumps.  If it was, we don't start issuing warnings until
-     we need to bump beyond the requested architecture or when we bump between
-     conflicting architectures.  */
-
-  if (warn_on_bump
-      && architecture_requested)
+  if (need_hm10_p)
     {
-      /* `max_architecture' records the requested architecture.
-        Issue warnings if we go above it.  */
-      warn_after_architecture = max_architecture;
-
-      /* Find the highest architecture level that doesn't conflict with
-        the requested one.  */
-      for (max_architecture = SPARC_OPCODE_ARCH_MAX;
-          max_architecture > warn_after_architecture;
-          --max_architecture)
-       if (! SPARC_OPCODE_CONFLICT_P (max_architecture,
-                                      warn_after_architecture))
-         break;
+      the_insn.opcode = (OR_INSN
+                        | (need_hh22_p ? RS1 (upper_dstreg) : 0)
+                        | RD (upper_dstreg)
+                        | IMMED
+                        | (upper32 & (need_hh22_p ? 0x3ff : 0x1fff)));
+      the_insn.reloc = (the_insn.exp.X_op != O_constant
+                       ? BFD_RELOC_SPARC_HM10 : BFD_RELOC_NONE);
+      output_insn (insn, &the_insn);
+    }
+    
+  if (need_lo10_p)
+    {
+      /* FIXME: One nice optimization to do here is to OR the low part
+        with the highpart if hi22 isn't needed and the low part is
+        positive.  */
+      the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (dstreg) : 0)
+                        | RD (dstreg)
+                        | IMMED
+                        | (lower32 & (need_hi22_p ? 0x3ff : 0x1fff)));
+      the_insn.reloc = (the_insn.exp.X_op != O_constant
+                       ? BFD_RELOC_LO10 : BFD_RELOC_NONE);
+      output_insn (insn, &the_insn);
+    }
+    
+  /* If we needed to build the upper part, shift it into place.  */
+  if (need_hh22_p || need_hm10_p)
+    {
+      the_insn.opcode = (SLLX_INSN | RS1 (upper_dstreg) | RD (upper_dstreg)
+                        | IMMED | 32);
+      the_insn.reloc = BFD_RELOC_NONE;
+      output_insn (insn, &the_insn);
+    }
+    
+  /* To get -1 in upper32, we do sethi %hi(~x), r; xor r, -0x400 | x, r.  */
+  if (need_xor10_p)
+    {
+      the_insn.opcode = (XOR_INSN | RS1 (dstreg) | RD (dstreg) | IMMED
+                        | 0x1c00 | (lower32 & 0x3ff));
+      the_insn.reloc = BFD_RELOC_NONE;
+      output_insn (insn, &the_insn);
     }
-}
-
-/* Called after all assembly has been done.  */
 
-void
-sparc_md_end ()
-{
-#ifdef SPARC_ARCH64
-  if (current_architecture == SPARC_OPCODE_ARCH_V9A)
-    bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_v9a);
-  else
-    bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_v9);
-#else
-  if (current_architecture == SPARC_OPCODE_ARCH_V9)
-    bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_v8plus);
-  else if (current_architecture == SPARC_OPCODE_ARCH_V9A)
-    bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_v8plusa);
-  else if (current_architecture == SPARC_OPCODE_ARCH_SPARCLET)
-    bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_sparclet);
-  else
+  /* If we needed to build both upper and lower parts, OR them together.  */
+  else if ((need_hh22_p || need_hm10_p) && (need_hi22_p || need_lo10_p))
     {
-      /* The sparclite is treated like a normal sparc.  Perhaps it shouldn't
-        be but for now it is (since that's the way it's always been
-        treated).  */
-      bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc);
+      the_insn.opcode = (OR_INSN | RS1 (dstreg) | RS2 (upper_dstreg)
+                        | RD (dstreg));
+      the_insn.reloc = BFD_RELOC_NONE;
+      output_insn (insn, &the_insn);
     }
-#endif
 }
+\f
+/* Main entry point to assemble one instruction.  */
 
 void
 md_assemble (str)
      char *str;
 {
   const struct sparc_opcode *insn;
-  char *toP;
-  int rsd;
+  int special_case;
 
   know (str);
-  sparc_ip (str, &insn);
+  special_case = sparc_ip (str, &insn);
 
-  /* We warn about attempts to put a floating point branch in a delay
-     slot.  */
+  /* We warn about attempts to put a floating point branch in a delay slot,
+     unless the delay slot has been annulled.  */
   if (insn != NULL
       && last_insn != NULL
       && (insn->flags & F_FBR) != 0
-      && (last_insn->flags & F_DELAYED) != 0)
-    as_warn ("FP branch in delay slot");
+      && (last_insn->flags & F_DELAYED) != 0
+      /* ??? This test isn't completely accurate.  We assume anything with
+        F_{UNBR,CONDBR,FBR} set is annullable.  */
+      && ((last_insn->flags & (F_UNBR | F_CONDBR | F_FBR)) == 0
+         || (last_opcode & ANNUL) == 0))
+    as_warn (_("FP branch in delay slot"));
 
   /* SPARC before v9 requires a nop instruction between a floating
      point instruction and a floating point branch.  We insert one
@@ -804,159 +1232,58 @@ md_assemble (str)
       && (insn->flags & F_FBR) != 0
       && (last_insn->flags & F_FLOAT) != 0)
     {
-      as_warn ("FP branch preceded by FP instruction; NOP inserted");
-      toP = frag_more (4);
-      md_number_to_chars (toP, (valueT) 0x01000000, 4);
-    }
-
-  /* See if "set" operand is absolute and small; skip sethi if so. */
-  if (special_case == SPECIAL_CASE_SET
-      && the_insn.exp.X_op == O_constant)
-    {
-      if (the_insn.exp.X_add_number >= -(1 << 12)
-         && the_insn.exp.X_add_number < (1 << 12))
-       {
-         the_insn.opcode = 0x80102000  /* or %g0,imm,... */
-           | (the_insn.opcode & 0x3E000000)    /* dest reg */
-           | (the_insn.exp.X_add_number & 0x1FFF);     /* imm */
-         special_case = 0;     /* No longer special */
-         the_insn.reloc = BFD_RELOC_NONE;      /* No longer relocated */
-       }
-    }
-
-  toP = frag_more (4);
-  /* put out the opcode */
-  md_number_to_chars (toP, (valueT) the_insn.opcode, 4);
+      struct sparc_it nop_insn;
 
-  /* put out the symbol-dependent stuff */
-  if (the_insn.reloc != BFD_RELOC_NONE)
-    {
-      fix_new_exp (frag_now,   /* which frag */
-                  (toP - frag_now->fr_literal),        /* where */
-                  4,           /* size */
-                  &the_insn.exp,
-                  the_insn.pcrel,
-                  the_insn.reloc);
+      nop_insn.opcode = NOP_INSN;
+      nop_insn.reloc = BFD_RELOC_NONE;
+      output_insn (insn, &nop_insn);
+      as_warn (_("FP branch preceded by FP instruction; NOP inserted"));
     }
 
-  last_insn = insn;
-
   switch (special_case)
     {
+    case SPECIAL_CASE_NONE:
+      /* normal insn */
+      output_insn (insn, &the_insn);
+      break;
+
+    case SPECIAL_CASE_SETSW:
+      synthetize_setsw (insn);
+      break;
+         
     case SPECIAL_CASE_SET:
-      special_case = 0;
-      assert (the_insn.reloc == BFD_RELOC_HI22);
-      /* See if "set" operand has no low-order bits; skip OR if so. */
-      if (the_insn.exp.X_op == O_constant
-         && ((the_insn.exp.X_add_number & 0x3FF) == 0))
-       return;
-      toP = frag_more (4);
-      rsd = (the_insn.opcode >> 25) & 0x1f;
-      the_insn.opcode = 0x80102000 | (rsd << 25) | (rsd << 14);
-      md_number_to_chars (toP, (valueT) the_insn.opcode, 4);
-      fix_new_exp (frag_now,   /* which frag */
-                  (toP - frag_now->fr_literal),        /* where */
-                  4,           /* size */
-                  &the_insn.exp,
-                  the_insn.pcrel,
-                  BFD_RELOC_LO10);
-      return;
+      synthetize_setuw (insn);
+      break;
 
+    case SPECIAL_CASE_SETX:
+      synthetize_setx (insn);
+      break;
+      
     case SPECIAL_CASE_FDIV:
-      /* According to information leaked from Sun, the "fdiv" instructions
-        on early SPARC machines would produce incorrect results sometimes.
-        The workaround is to add an fmovs of the destination register to
-        itself just after the instruction.  This was true on machines
-        with Weitek 1165 float chips, such as the Sun-4/260 and /280. */
-      special_case = 0;
-      assert (the_insn.reloc == BFD_RELOC_NONE);
-      toP = frag_more (4);
-      rsd = (the_insn.opcode >> 25) & 0x1f;
-      the_insn.opcode = 0x81A00020 | (rsd << 25) | rsd;        /* fmovs dest,dest */
-      md_number_to_chars (toP, (valueT) the_insn.opcode, 4);
-      return;
-
-    case 0:
-      return;
-
+      {
+       int rd = (the_insn.opcode >> 25) & 0x1f;
+    
+       output_insn (insn, &the_insn);
+    
+       /* According to information leaked from Sun, the "fdiv" instructions
+          on early SPARC machines would produce incorrect results sometimes.
+          The workaround is to add an fmovs of the destination register to
+          itself just after the instruction.  This was true on machines
+          with Weitek 1165 float chips, such as the Sun-4/260 and /280. */
+       assert (the_insn.reloc == BFD_RELOC_NONE);
+       the_insn.opcode = FMOVS_INSN | rd | RD (rd);
+       output_insn (insn, &the_insn);
+       return;
+      }
+    
     default:
-      as_fatal ("failed sanity check.");
+      as_fatal (_("failed special case insn sanity check"));
     }
 }
 
-/* Implement big shift right.  */
-static bfd_vma
-BSR (val, amount)
-     bfd_vma val;
-     int amount;
-{
-  if (sizeof (bfd_vma) <= 4 && amount >= 32)
-    as_fatal ("Support for 64-bit arithmetic not compiled in.");
-  return val >> amount;
-}
-
-/* Parse an argument that can be expressed as a keyword.
-   (eg: #StoreStore or %ccfr).
-   The result is a boolean indicating success.
-   If successful, INPUT_POINTER is updated.  */
-
-static int
-parse_keyword_arg (lookup_fn, input_pointerP, valueP)
-     int (*lookup_fn) ();
-     char **input_pointerP;
-     int *valueP;
-{
-  int value;
-  char c, *p, *q;
-
-  p = *input_pointerP;
-  for (q = p + (*p == '#' || *p == '%'); isalpha (*q) || *q == '_'; ++q)
-    continue;
-  c = *q;
-  *q = 0;
-  value = (*lookup_fn) (p);
-  *q = c;
-  if (value == -1)
-    return 0;
-  *valueP = value;
-  *input_pointerP = q;
-  return 1;
-}
-
-/* Parse an argument that is a constant expression.
-   The result is a boolean indicating success.  */
+/* Subroutine of md_assemble to do the actual parsing.  */
 
 static int
-parse_const_expr_arg (input_pointerP, valueP)
-     char **input_pointerP;
-     int *valueP;
-{
-  char *save = input_line_pointer;
-  expressionS exp;
-
-  input_line_pointer = *input_pointerP;
-  /* The next expression may be something other than a constant
-     (say if we're not processing the right variant of the insn).
-     Don't call expression unless we're sure it will succeed as it will
-     signal an error (which we want to defer until later).  */
-  /* FIXME: It might be better to define md_operand and have it recognize
-     things like %asi, etc. but continuing that route through to the end
-     is a lot of work.  */
-  if (*input_line_pointer == '%')
-    {
-      input_line_pointer = save;
-      return 0;
-    }
-  expression (&exp);
-  *input_pointerP = input_line_pointer;
-  input_line_pointer = save;
-  if (exp.X_op != O_constant)
-    return 0;
-  *valueP = exp.X_add_number;
-  return 1;
-}
-
-static void
 sparc_ip (str, pinsn)
      char *str;
      const struct sparc_opcode **pinsn;
@@ -971,11 +1298,16 @@ sparc_ip (str, pinsn)
   unsigned int mask = 0;
   int match = 0;
   int comma = 0;
-  long immediate_max = 0;
   int v9_arg_p;
+  int special_case = SPECIAL_CASE_NONE;
 
-  for (s = str; islower (*s) || (*s >= '0' && *s <= '3'); ++s)
-    ;
+  s = str;
+  if (islower ((unsigned char) *s))
+    {
+      do
+       ++s;
+      while (islower ((unsigned char) *s) || isdigit ((unsigned char) *s));
+    }
 
   switch (*s)
     {
@@ -992,14 +1324,14 @@ sparc_ip (str, pinsn)
       break;
 
     default:
-      as_fatal ("Unknown opcode: `%s'", str);
+      as_fatal (_("Unknown opcode: `%s'"), str);
     }
   insn = (struct sparc_opcode *) hash_find (op_hash, str);
   *pinsn = insn;
   if (insn == NULL)
     {
-      as_bad ("Unknown opcode: `%s'", str);
-      return;
+      as_bad (_("Unknown opcode: `%s'"), str);
+      return special_case;
     }
   if (comma)
     {
@@ -1036,7 +1368,7 @@ sparc_ip (str, pinsn)
                        if (! parse_keyword_arg (sparc_encode_membar, &s,
                                                 &mask))
                          {
-                           error_message = ": invalid membar mask name";
+                           error_message = _(": invalid membar mask name");
                            goto error;
                          }
                        kmask |= mask;
@@ -1050,12 +1382,12 @@ sparc_ip (str, pinsn)
                  {
                    if (! parse_const_expr_arg (&s, &kmask))
                      {
-                       error_message = ": invalid membar mask expression";
+                       error_message = _(": invalid membar mask expression");
                        goto error;
                      }
                    if (kmask < 0 || kmask > 127)
                      {
-                       error_message = ": invalid membar mask number";
+                       error_message = _(": invalid membar mask number");
                        goto error;
                      }
                  }
@@ -1073,7 +1405,7 @@ sparc_ip (str, pinsn)
                  {
                    if (! parse_keyword_arg (sparc_encode_prefetch, &s, &fcn))
                      {
-                       error_message = ": invalid prefetch function name";
+                       error_message = _(": invalid prefetch function name");
                        goto error;
                      }
                  }
@@ -1081,12 +1413,12 @@ sparc_ip (str, pinsn)
                  {
                    if (! parse_const_expr_arg (&s, &fcn))
                      {
-                       error_message = ": invalid prefetch function expression";
+                       error_message = _(": invalid prefetch function expression");
                        goto error;
                      }
                    if (fcn < 0 || fcn > 31)
                      {
-                       error_message = ": invalid prefetch function number";
+                       error_message = _(": invalid prefetch function number");
                        goto error;
                      }
                  }
@@ -1114,7 +1446,7 @@ sparc_ip (str, pinsn)
                    }
                  if (p->name[0] != s[0])
                    {
-                     error_message = ": unrecognizable privileged register";
+                     error_message = _(": unrecognizable privileged register");
                      goto error;
                    }
                  if (*args == '?')
@@ -1126,7 +1458,48 @@ sparc_ip (str, pinsn)
                }
              else
                {
-                 error_message = ": unrecognizable privileged register";
+                 error_message = _(": unrecognizable privileged register");
+                 goto error;
+               }
+
+           case '_':
+           case '/':
+             /* Parse a v9a ancillary state register.  */
+             if (*s == '%')
+               {
+                 struct priv_reg_entry *p = v9a_asr_table;
+                 unsigned int len = 9999999; /* init to make gcc happy */
+
+                 s += 1;
+                 while (p->name[0] > s[0])
+                   p++;
+                 while (p->name[0] == s[0])
+                   {
+                     len = strlen (p->name);
+                     if (strncmp (p->name, s, len) == 0)
+                       break;
+                     p++;
+                   }
+                 if (p->name[0] != s[0])
+                   {
+                     error_message = _(": unrecognizable v9a ancillary state register");
+                     goto error;
+                   }
+                 if (*args == '/' && (p->regnum == 20 || p->regnum == 21))
+                   {
+                     error_message = _(": rd on write only ancillary state register");
+                     goto error;
+                   }                 
+                 if (*args == '/')
+                   opcode |= (p->regnum << 14);
+                 else
+                   opcode |= (p->regnum << 25);
+                 s += len;
+                 continue;
+               }
+             else
+               {
+                 error_message = _(": unrecognizable v9a ancillary state register");
                  goto error;
                }
 
@@ -1136,11 +1509,11 @@ sparc_ip (str, pinsn)
                {
                  s += 4;
 
-                 if (isdigit (*s))
+                 if (isdigit ((unsigned char) *s))
                    {
                      long num = 0;
 
-                     while (isdigit (*s))
+                     while (isdigit ((unsigned char) *s))
                        {
                          num = num * 10 + *s - '0';
                          ++s;
@@ -1150,7 +1523,7 @@ sparc_ip (str, pinsn)
                        {
                          if (num < 16 || 31 < num)
                            {
-                             error_message = ": asr number must be between 16 and 31";
+                             error_message = _(": asr number must be between 16 and 31");
                              goto error;
                            }
                        }
@@ -1158,7 +1531,7 @@ sparc_ip (str, pinsn)
                        {
                          if (num < 0 || 31 < num)
                            {
-                             error_message = ": asr number must be between 0 and 31";
+                             error_message = _(": asr number must be between 0 and 31");
                              goto error;
                            }
                        }
@@ -1168,7 +1541,7 @@ sparc_ip (str, pinsn)
                    }
                  else
                    {
-                     error_message = ": expecting %asrN";
+                     error_message = _(": expecting %asrN");
                      goto error;
                    }
                } /* if %asr */
@@ -1176,12 +1549,10 @@ sparc_ip (str, pinsn)
 
            case 'I':
              the_insn.reloc = BFD_RELOC_SPARC_11;
-             immediate_max = 0x03FF;
              goto immediate;
 
            case 'j':
              the_insn.reloc = BFD_RELOC_SPARC_10;
-             immediate_max = 0x01FF;
              goto immediate;
 
            case 'X':
@@ -1192,7 +1563,6 @@ sparc_ip (str, pinsn)
                the_insn.reloc = BFD_RELOC_SPARC13;
              /* These fields are unsigned, but for upward compatibility,
                 allow negative values as well.  */
-             immediate_max = 0x1f;
              goto immediate;
 
            case 'Y':
@@ -1203,7 +1573,6 @@ sparc_ip (str, pinsn)
                the_insn.reloc = BFD_RELOC_SPARC13;
              /* These fields are unsigned, but for upward compatibility,
                 allow negative values as well.  */
-             immediate_max = 0x3f;
              goto immediate;
 
            case 'k':
@@ -1348,9 +1717,9 @@ sparc_ip (str, pinsn)
              break;
 
            case '#':           /* must be at least one digit */
-             if (isdigit (*s++))
+             if (isdigit ((unsigned char) *s++))
                {
-                 while (isdigit (*s))
+                 while (isdigit ((unsigned char) *s))
                    {
                      ++s;
                    }
@@ -1369,10 +1738,10 @@ sparc_ip (str, pinsn)
            case 'b':           /* next operand is a coprocessor register */
            case 'c':
            case 'D':
-             if (*s++ == '%' && *s++ == 'c' && isdigit (*s))
+             if (*s++ == '%' && *s++ == 'c' && isdigit ((unsigned char) *s))
                {
                  mask = *s++;
-                 if (isdigit (*s))
+                 if (isdigit ((unsigned char) *s))
                    {
                      mask = 10 * (mask - '0') + (*s++ - '0');
                      if (mask >= 32)
@@ -1421,7 +1790,8 @@ sparc_ip (str, pinsn)
                      goto error;
 
                    case 'g':   /* global register */
-                     if (isoctal (c = *s++))
+                     c = *s++;
+                     if (isoctal (c))
                        {
                          mask = c - '0';
                          break;
@@ -1429,7 +1799,8 @@ sparc_ip (str, pinsn)
                      goto error;
 
                    case 'i':   /* in register */
-                     if (isoctal (c = *s++))
+                     c = *s++;
+                     if (isoctal (c))
                        {
                          mask = c - '0' + 24;
                          break;
@@ -1437,7 +1808,8 @@ sparc_ip (str, pinsn)
                      goto error;
 
                    case 'l':   /* local register */
-                     if (isoctal (c = *s++))
+                     c = *s++;
+                     if (isoctal (c))
                        {
                          mask = (c - '0' + 16);
                          break;
@@ -1445,7 +1817,8 @@ sparc_ip (str, pinsn)
                      goto error;
 
                    case 'o':   /* out register */
-                     if (isoctal (c = *s++))
+                     c = *s++;
+                     if (isoctal (c))
                        {
                          mask = (c - '0' + 8);
                          break;
@@ -1461,7 +1834,7 @@ sparc_ip (str, pinsn)
                      goto error;
 
                    case 'r':   /* any register */
-                     if (!isdigit (c = *s++))
+                     if (!isdigit ((unsigned char) (c = *s++)))
                        {
                          goto error;
                        }
@@ -1476,7 +1849,7 @@ sparc_ip (str, pinsn)
                    case '7':
                    case '8':
                    case '9':
-                     if (isdigit (*s))
+                     if (isdigit ((unsigned char) *s))
                        {
                          if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32)
                            {
@@ -1537,9 +1910,9 @@ sparc_ip (str, pinsn)
 
                if (*s++ == '%'
                    && ((format = *s) == 'f')
-                   && isdigit (*++s))
+                   && isdigit ((unsigned char) *++s))
                  {
-                   for (mask = 0; isdigit (*s); ++s)
+                   for (mask = 0; isdigit ((unsigned char) *s); ++s)
                      {
                        mask = 10 * mask + (*s - '0');
                      }         /* read the number */
@@ -1563,9 +1936,9 @@ sparc_ip (str, pinsn)
                    if (mask >= 64)
                      {
                        if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
-                         error_message = ": There are only 64 f registers; [0-63]";
+                         error_message = _(": There are only 64 f registers; [0-63]");
                        else
-                         error_message = ": There are only 32 f registers; [0-31]";
+                         error_message = _(": There are only 32 f registers; [0-31]");
                        goto error;
                      } /* on error */
                    else if (mask >= 32)
@@ -1577,7 +1950,7 @@ sparc_ip (str, pinsn)
                          }
                        else
                          {
-                           error_message = ": There are only 32 f registers; [0-31]";
+                           error_message = _(": There are only 32 f registers; [0-31]");
                            goto error;
                          }
                      }
@@ -1621,8 +1994,8 @@ sparc_ip (str, pinsn)
                }
              break;
 
-           case 'h':           /* high 22 bits */
-             the_insn.reloc = BFD_RELOC_HI22;
+           case '0':           /* 64 bit immediate (set, setsw, setx insn) */
+             the_insn.reloc = BFD_RELOC_NONE; /* reloc handled elsewhere */
              goto immediate;
 
            case 'l':           /* 22 bit PC relative immediate */
@@ -1635,153 +2008,259 @@ sparc_ip (str, pinsn)
              the_insn.pcrel = 1;
              goto immediate;
 
+           case 'h':
            case 'n':           /* 22 bit immediate */
              the_insn.reloc = BFD_RELOC_SPARC22;
              goto immediate;
 
            case 'i':           /* 13 bit immediate */
              the_insn.reloc = BFD_RELOC_SPARC13;
-             immediate_max = 0x0FFF;
 
-             /*FALLTHROUGH */
+             /* fallthrough */
 
            immediate:
              if (*s == ' ')
                s++;
-             if (*s == '%')
-               {
-                 if ((c = s[1]) == 'h' && s[2] == 'i')
-                   {
-                     the_insn.reloc = BFD_RELOC_HI22;
-                     s += 3;
-                   }
-                 else if (c == 'l' && s[2] == 'o')
-                   {
-                     the_insn.reloc = BFD_RELOC_LO10;
-                     s += 3;
-                   }
-                 else if (c == 'u'
-                          && s[2] == 'h'
-                          && s[3] == 'i')
-                   {
-                     the_insn.reloc = BFD_RELOC_SPARC_HH22;
-                     s += 4;
-                     v9_arg_p = 1;
-                   }
-                 else if (c == 'u'
-                          && s[2] == 'l'
-                          && s[3] == 'o')
-                   {
-                     the_insn.reloc = BFD_RELOC_SPARC_HM10;
-                     s += 4;
-                     v9_arg_p = 1;
-                   }
-                 else
-                   break;
-               }
-             /* Note that if the getExpression() fails, we will still
-                have created U entries in the symbol table for the
-                'symbols' in the input string.  Try not to create U
-                symbols for registers, etc.  */
+
              {
+               char *s1;
+               char *op_arg = NULL;
+               expressionS op_exp;
+               bfd_reloc_code_real_type old_reloc = the_insn.reloc;
+
+               /* Check for %hi, etc.  */
+               if (*s == '%')
+                 {
+                   static const struct ops {
+                     /* The name as it appears in assembler.  */
+                     char *name;
+                     /* strlen (name), precomputed for speed */
+                     int len;
+                     /* The reloc this pseudo-op translates to.  */
+                     int reloc;
+                     /* Non-zero if for v9 only.  */
+                     int v9_p;
+                     /* Non-zero if can be used in pc-relative contexts.  */
+                     int pcrel_p;/*FIXME:wip*/
+                   } ops[] = {
+                     /* hix/lox must appear before hi/lo so %hix won't be
+                        mistaken for %hi.  */
+                     { "hix", 3, BFD_RELOC_SPARC_HIX22, 1, 0 },
+                     { "lox", 3, BFD_RELOC_SPARC_LOX10, 1, 0 },
+                     { "hi", 2, BFD_RELOC_HI22, 0, 1 },
+                     { "lo", 2, BFD_RELOC_LO10, 0, 1 },
+                     { "hh", 2, BFD_RELOC_SPARC_HH22, 1, 1 },
+                     { "hm", 2, BFD_RELOC_SPARC_HM10, 1, 1 },
+                     { "lm", 2, BFD_RELOC_SPARC_LM22, 1, 1 },
+                     { "h44", 3, BFD_RELOC_SPARC_H44, 1, 0 },
+                     { "m44", 3, BFD_RELOC_SPARC_M44, 1, 0 },
+                     { "l44", 3, BFD_RELOC_SPARC_L44, 1, 0 },
+                     { "uhi", 3, BFD_RELOC_SPARC_HH22, 1, 0 },
+                     { "ulo", 3, BFD_RELOC_SPARC_HM10, 1, 0 },
+                     { NULL }
+                   };
+                   const struct ops *o;
+  
+                   for (o = ops; o->name; o++)
+                     if (strncmp (s + 1, o->name, o->len) == 0)
+                       break;
+                   if (o->name == NULL)
+                     break;
+                     
+                   if (s[o->len + 1] != '(')
+                     {
+                       as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name);
+                       return special_case;
+                     }
+
+                   op_arg = o->name;
+                   the_insn.reloc = o->reloc;
+                   s += o->len + 2;
+                   v9_arg_p = o->v9_p;
+                 }
+
+               /* Note that if the get_expression() fails, we will still
+                  have created U entries in the symbol table for the
+                  'symbols' in the input string.  Try not to create U
+                  symbols for registers, etc.  */
+
                /* This stuff checks to see if the expression ends in
                   +%reg.  If it does, it removes the register from
                   the expression, and re-sets 's' to point to the
                   right place.  */
 
-               char *s1;
+               if (op_arg)
+                 {
+                   int npar = 0;
+
+                   for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++)
+                     if (*s1 == '(')
+                       npar++;
+                     else if (*s1 == ')')
+                       {
+                         if (!npar)
+                           break;
+                         npar--;
+                       }
+
+                   if (*s1 != ')')
+                     {
+                       as_bad (_("Illegal operands: %%%s requires arguments in ()"), op_arg);
+                       return special_case;
+                     }
+                   
+                   *s1 = '\0';
+                   (void) get_expression (s);
+                   *s1 = ')';
+                   s = s1 + 1;
+                   if (*s == ',' || *s == ']' || !*s)
+                     continue;
+                   if (*s != '+' && *s != '-')
+                     {
+                       as_bad (_("Illegal operands: Can't do arithmetics other than + and - involving %%%s()"), op_arg);
+                       return special_case;
+                     }
+                   *s1 = '0';
+                   s = s1;
+                   op_exp = the_insn.exp;
+                   memset (&the_insn.exp, 0, sizeof(the_insn.exp));
+                 }
 
-               for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++);;
+               for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++) ;
 
-               if (s1 != s && isdigit (s1[-1]))
+               if (s1 != s && isdigit ((unsigned char) s1[-1]))
                  {
                    if (s1[-2] == '%' && s1[-3] == '+')
+                     s1 -= 3;
+                   else if (strchr ("goli0123456789", s1[-2]) && s1[-3] == '%' && s1[-4] == '+')
+                     s1 -= 4;
+                   else
+                     s1 = NULL;
+                   if (s1)
                      {
-                       s1 -= 3;
                        *s1 = '\0';
-                       (void) getExpression (s);
+                       (void) get_expression (s);
                        *s1 = '+';
+                       if (op_arg)
+                         *s = ')';
                        s = s1;
-                       continue;
                      }
-                   else if (strchr ("goli0123456789", s1[-2]) && s1[-3] == '%' && s1[-4] == '+')
+                 }
+               else
+                 s1 = NULL;
+
+               if (!s1)
+                 {
+                   (void) get_expression (s);
+                   if (op_arg)
+                     *s = ')';
+                   s = expr_end;
+                 }
+
+               if (op_arg)
+                 {
+                   the_insn.exp2 = the_insn.exp;
+                   the_insn.exp = op_exp;
+                   if (the_insn.exp2.X_op == O_absent)
+                     the_insn.exp2.X_op = O_illegal;
+                   else if (the_insn.exp.X_op == O_absent)
                      {
-                       s1 -= 4;
-                       *s1 = '\0';
-                       (void) getExpression (s);
-                       *s1 = '+';
-                       s = s1;
-                       continue;
+                       the_insn.exp = the_insn.exp2;
+                       the_insn.exp2.X_op = O_illegal;
+                     }
+                   else if (the_insn.exp.X_op == O_constant)
+                     {
+                       valueT val = the_insn.exp.X_add_number;
+                       switch (the_insn.reloc)
+                         {
+                         case BFD_RELOC_SPARC_HH22:
+                           val = BSR (val, 32);
+                           /* intentional fallthrough */
+
+                         case BFD_RELOC_SPARC_LM22:
+                         case BFD_RELOC_HI22:
+                           val = (val >> 10) & 0x3fffff;
+                           break;
+
+                         case BFD_RELOC_SPARC_HM10:
+                           val = BSR (val, 32);
+                           /* intentional fallthrough */
+
+                         case BFD_RELOC_LO10:
+                           val &= 0x3ff;
+                           break;
+
+                         case BFD_RELOC_SPARC_H44:
+                           val >>= 22;
+                           val &= 0x3fffff;
+                           break;
+
+                         case BFD_RELOC_SPARC_M44:
+                           val >>= 12;
+                           val &= 0x3ff;
+                           break;
+
+                         case BFD_RELOC_SPARC_L44:
+                           val &= 0xfff;
+                           break;
+
+                         case BFD_RELOC_SPARC_HIX22:
+                           val = ~ val;
+                           val = (val >> 10) & 0x3fffff;
+                           break;
+
+                         case BFD_RELOC_SPARC_LOX10:
+                           val = (val & 0x3ff) | 0x1c00;
+                           break;
+                         }
+                       the_insn.exp = the_insn.exp2;
+                       the_insn.exp.X_add_number += val;
+                       the_insn.exp2.X_op = O_illegal;
+                       the_insn.reloc = old_reloc;
+                     }
+                   else if (the_insn.exp2.X_op != O_constant)
+                     {
+                       as_bad (_("Illegal operands: Can't add non-constant expression to %%%s()"), op_arg);
+                       return special_case;
+                     }
+                   else
+                     {
+                       if (1 || old_reloc != BFD_RELOC_SPARC13
+                           || the_insn.reloc != BFD_RELOC_LO10
+                           || sparc_arch_size != 64
+                           || sparc_pic_code)
+                         {
+                           as_bad (_("Illegal operands: Can't do arithmetics involving %%%s() of a relocatable symbol"), op_arg);
+                           return special_case;
+                         }
+                       the_insn.reloc = BFD_RELOC_SPARC_OLO10;
                      }
                  }
              }
-             (void) getExpression (s);
-             s = expr_end;
-
+             /* Check for constants that don't require emitting a reloc.  */
              if (the_insn.exp.X_op == O_constant
                  && the_insn.exp.X_add_symbol == 0
                  && the_insn.exp.X_op_symbol == 0)
                {
-                 /* Handle %uhi/%ulo by moving the upper word to the lower
-                    one and pretending it's %hi/%lo.  We also need to watch
-                    for %hi/%lo: the top word needs to be zeroed otherwise
-                    fixup_segment will complain the value is too big.  */
-                 switch (the_insn.reloc)
-                   {
-                   case BFD_RELOC_SPARC_HH22:
-                     the_insn.reloc = BFD_RELOC_HI22;
-                     the_insn.exp.X_add_number = BSR (the_insn.exp.X_add_number, 32);
-                     break;
-                   case BFD_RELOC_SPARC_HM10:
-                     the_insn.reloc = BFD_RELOC_LO10;
-                     the_insn.exp.X_add_number = BSR (the_insn.exp.X_add_number, 32);
-                     break;
-                   case BFD_RELOC_HI22:
-                   case BFD_RELOC_LO10:
-                     the_insn.exp.X_add_number &= 0xffffffff;
-                     break;
-                   default:
-                     break;
-                   }
-
                  /* For pc-relative call instructions, we reject
                     constants to get better code.  */
                  if (the_insn.pcrel
                      && the_insn.reloc == BFD_RELOC_32_PCREL_S2
-                     && in_signed_range (the_insn.exp.X_add_number, 0x3fff)
-                     )
+                     && in_signed_range (the_insn.exp.X_add_number, 0x3fff))
                    {
-                     error_message = ": PC-relative operand can't be a constant";
+                     error_message = _(": PC-relative operand can't be a constant");
                      goto error;
                    }
-                 /* Check for invalid constant values.  Don't warn if
-                    constant was inside %hi or %lo, since these
-                    truncate the constant to fit.  */
-                 if (immediate_max != 0
-                     && the_insn.reloc != BFD_RELOC_LO10
-                     && the_insn.reloc != BFD_RELOC_HI22
-                     && !in_signed_range (the_insn.exp.X_add_number,
-                                          immediate_max)
-                     )
-                   {
-                     if (the_insn.pcrel)
-                       /* Who knows?  After relocation, we may be within
-                          range.  Let the linker figure it out.  */
-                       {
-                         the_insn.exp.X_op = O_symbol;
-                         the_insn.exp.X_add_symbol = section_symbol (absolute_section);
-                       }
-                     else
-                       /* Immediate value is non-pcrel, and out of
-                           range.  */
-                       as_bad ("constant value %ld out of range (%ld .. %ld)",
-                               the_insn.exp.X_add_number,
-                               ~immediate_max, immediate_max);
-                   }
-               }
 
-             /* Reset to prevent extraneous range check.  */
-             immediate_max = 0;
+                 /* Constants that won't fit are checked in md_apply_fix3
+                    and bfd_install_relocation.
+                    ??? It would be preferable to install the constants
+                    into the insn here and save having to create a fixS
+                    for each one.  There already exists code to handle
+                    all the various cases (e.g. in md_apply_fix3 and
+                    bfd_install_relocation) so duplicating all that code
+                    here isn't right.  */
+               }
 
              continue;
 
@@ -1802,7 +2281,7 @@ sparc_ip (str, pinsn)
                  {
                    if (! parse_keyword_arg (sparc_encode_asi, &s, &asi))
                      {
-                       error_message = ": invalid ASI name";
+                       error_message = _(": invalid ASI name");
                        goto error;
                      }
                  }
@@ -1810,12 +2289,12 @@ sparc_ip (str, pinsn)
                  {
                    if (! parse_const_expr_arg (&s, &asi))
                      {
-                       error_message = ": invalid ASI expression";
+                       error_message = _(": invalid ASI expression");
                        goto error;
                      }
                    if (asi < 0 || asi > 255)
                      {
-                       error_message = ": invalid ASI number";
+                       error_message = _(": invalid ASI number");
                        goto error;
                      }
                  }
@@ -1848,17 +2327,28 @@ sparc_ip (str, pinsn)
              break;
 
            case 'S':
-             if (strcmp (str, "set") == 0)
+             if (strcmp (str, "set") == 0
+                 || strcmp (str, "setuw") == 0)
                {
                  special_case = SPECIAL_CASE_SET;
                  continue;
                }
-             else if (strncmp (str, "fdiv", 4) == 0)
+             else if (strcmp (str, "setsw") == 0)
                {
-                 special_case = SPECIAL_CASE_FDIV;
+                 special_case = SPECIAL_CASE_SETSW;
                  continue;
                }
-             break;
+             else if (strcmp (str, "setx") == 0)
+               {
+                 special_case = SPECIAL_CASE_SETX;
+                 continue;
+               }
+             else if (strncmp (str, "fdiv", 4) == 0)
+               {
+                 special_case = SPECIAL_CASE_FDIV;
+                 continue;
+               }
+             break;
 
            case 'o':
              if (strncmp (s, "%asi", 4) != 0)
@@ -1901,12 +2391,12 @@ sparc_ip (str, pinsn)
                  {
                    int n = e.X_add_number;
                    if (n != e.X_add_number || (n & ~0x1ff) != 0)
-                     as_bad ("OPF immediate operand out of range (0-0x1ff)");
+                     as_bad (_("OPF immediate operand out of range (0-0x1ff)"));
                    else
                      opcode |= e.X_add_number << 5;
                  }
                else
-                 as_bad ("non-immediate OPF operand, ignored");
+                 as_bad (_("non-immediate OPF operand, ignored"));
                s = input_line_pointer;
                input_line_pointer = push;
                continue;
@@ -1925,7 +2415,7 @@ sparc_ip (str, pinsn)
                int cpreg;
                if (! parse_keyword_arg (sparc_encode_sparclet_cpreg, &s, &cpreg))
                  {
-                   error_message = ": invalid cpreg name";
+                   error_message = _(": invalid cpreg name");
                    goto error;
                  }
                opcode |= (*args == 'U' ? RS1 (cpreg) : RD (cpreg));
@@ -1933,7 +2423,7 @@ sparc_ip (str, pinsn)
              }
 
            default:
-             as_fatal ("failed sanity check.");
+             as_fatal (_("failed sanity check."));
            }                   /* switch on arg code */
 
          /* Break out of for() loop.  */
@@ -1944,7 +2434,7 @@ sparc_ip (str, pinsn)
       if (match == 0)
        {
          /* Args don't match. */
-         if (((unsigned) (&insn[1] - sparc_opcodes)) < sparc_num_opcodes
+         if (&insn[1] - sparc_opcodes < sparc_num_opcodes
              && (insn->name == insn[1].name
                  || !strcmp (insn->name, insn[1].name)))
            {
@@ -1954,8 +2444,8 @@ sparc_ip (str, pinsn)
            }
          else
            {
-             as_bad ("Illegal operands%s", error_message);
-             return;
+             as_bad (_("Illegal operands%s"), error_message);
+             return special_case;
            }
        }
       else
@@ -1983,7 +2473,7 @@ sparc_ip (str, pinsn)
              if (warn_on_bump
                  && needed_architecture > warn_after_architecture)
                {
-                 as_warn ("architecture bumped from \"%s\" to \"%s\" on \"%s\"",
+                 as_warn (_("architecture bumped from \"%s\" to \"%s\" on \"%s\""),
                           sparc_opcode_archs[current_architecture].name,
                           sparc_opcode_archs[needed_architecture].name,
                           str);
@@ -2019,11 +2509,11 @@ sparc_ip (str, pinsn)
                  ++arch;
                }
 
-             as_bad ("Architecture mismatch on \"%s\".", str);
-             as_tsktsk (" (Requires %s; requested architecture is %s.)",
+             as_bad (_("Architecture mismatch on \"%s\"."), str);
+             as_tsktsk (_(" (Requires %s; requested architecture is %s.)"),
                         required_archs,
                         sparc_opcode_archs[max_architecture].name);
-             return;
+             return special_case;
            }
        } /* if no match */
 
@@ -2031,10 +2521,76 @@ sparc_ip (str, pinsn)
     } /* forever looking for a match */
 
   the_insn.opcode = opcode;
+  return special_case;
+}
+
+/* Parse an argument that can be expressed as a keyword.
+   (eg: #StoreStore or %ccfr).
+   The result is a boolean indicating success.
+   If successful, INPUT_POINTER is updated.  */
+
+static int
+parse_keyword_arg (lookup_fn, input_pointerP, valueP)
+     int (*lookup_fn) PARAMS ((const char *));
+     char **input_pointerP;
+     int *valueP;
+{
+  int value;
+  char c, *p, *q;
+
+  p = *input_pointerP;
+  for (q = p + (*p == '#' || *p == '%');
+       isalnum ((unsigned char) *q) || *q == '_';
+       ++q)
+    continue;
+  c = *q;
+  *q = 0;
+  value = (*lookup_fn) (p);
+  *q = c;
+  if (value == -1)
+    return 0;
+  *valueP = value;
+  *input_pointerP = q;
+  return 1;
+}
+
+/* Parse an argument that is a constant expression.
+   The result is a boolean indicating success.  */
+
+static int
+parse_const_expr_arg (input_pointerP, valueP)
+     char **input_pointerP;
+     int *valueP;
+{
+  char *save = input_line_pointer;
+  expressionS exp;
+
+  input_line_pointer = *input_pointerP;
+  /* The next expression may be something other than a constant
+     (say if we're not processing the right variant of the insn).
+     Don't call expression unless we're sure it will succeed as it will
+     signal an error (which we want to defer until later).  */
+  /* FIXME: It might be better to define md_operand and have it recognize
+     things like %asi, etc. but continuing that route through to the end
+     is a lot of work.  */
+  if (*input_line_pointer == '%')
+    {
+      input_line_pointer = save;
+      return 0;
+    }
+  expression (&exp);
+  *input_pointerP = input_line_pointer;
+  input_line_pointer = save;
+  if (exp.X_op != O_constant)
+    return 0;
+  *valueP = exp.X_add_number;
+  return 1;
 }
 
+/* Subroutine of sparc_ip to parse an expression.  */
+
 static int
-getExpression (str)
+get_expression (str)
      char *str;
 {
   char *save_in;
@@ -2049,7 +2605,7 @@ getExpression (str)
       && seg != bss_section
       && seg != undefined_section)
     {
-      the_insn.error = "bad segment";
+      the_insn.error = _("bad segment");
       expr_end = input_line_pointer;
       input_line_pointer = save_in;
       return 1;
@@ -2057,9 +2613,43 @@ getExpression (str)
   expr_end = input_line_pointer;
   input_line_pointer = save_in;
   return 0;
-}                              /* getExpression() */
+}
 
+/* Subroutine of md_assemble to output one insn.  */
 
+static void
+output_insn (insn, the_insn)
+     const struct sparc_opcode *insn;
+     struct sparc_it *the_insn;
+{
+  char *toP = frag_more (4);
+
+  /* put out the opcode */
+  if (INSN_BIG_ENDIAN)
+    number_to_chars_bigendian (toP, (valueT) the_insn->opcode, 4);
+  else
+    number_to_chars_littleendian (toP, (valueT) the_insn->opcode, 4);
+
+  /* put out the symbol-dependent stuff */
+  if (the_insn->reloc != BFD_RELOC_NONE)
+    {
+      fixS *fixP =  fix_new_exp (frag_now,     /* which frag */
+                                (toP - frag_now->fr_literal),  /* where */
+                                4,             /* size */
+                                &the_insn->exp,
+                                the_insn->pcrel,
+                                the_insn->reloc);
+      /* Turn off overflow checking in fixup_segment.  We'll do our
+        own overflow checking in md_apply_fix3.  This is necessary because
+        the insn size is 4 and fixup_segment will signal an overflow for
+        large 8 byte quantities.  */
+      fixP->fx_no_overflow = 1;
+    }
+
+  last_insn = insn;
+  last_opcode = the_insn->opcode;
+}
+\f
 /*
   This is identical to the md_atof in m68k.c.  I think this is right,
   but I'm not sure.
@@ -2078,718 +2668,1167 @@ md_atof (type, litP, sizeP)
      char *litP;
      int *sizeP;
 {
-  int prec;
-  LITTLENUM_TYPE words[MAX_LITTLENUMS];
-  LITTLENUM_TYPE *wordP;
-  char *t;
-  char *atof_ieee ();
-
-  switch (type)
-    {
+  int i,prec;
+  LITTLENUM_TYPE words[MAX_LITTLENUMS];
+  char *t;
+
+  switch (type)
+    {
+    case 'f':
+    case 'F':
+    case 's':
+    case 'S':
+      prec = 2;
+      break;
+
+    case 'd':
+    case 'D':
+    case 'r':
+    case 'R':
+      prec = 4;
+      break;
+
+    case 'x':
+    case 'X':
+      prec = 6;
+      break;
+
+    case 'p':
+    case 'P':
+      prec = 6;
+      break;
+
+    default:
+      *sizeP = 0;
+      return _("Bad call to MD_ATOF()");
+    }
+
+  t = atof_ieee (input_line_pointer, type, words);
+  if (t)
+    input_line_pointer = t;
+  *sizeP = prec * sizeof (LITTLENUM_TYPE);
+
+  if (target_big_endian)
+    {
+      for (i = 0; i < prec; i++)
+       {
+         md_number_to_chars (litP, (valueT) words[i], sizeof (LITTLENUM_TYPE));
+         litP += sizeof (LITTLENUM_TYPE);
+       }
+    }
+  else
+    {
+      for (i = prec - 1; i >= 0; i--)
+       {
+         md_number_to_chars (litP, (valueT) words[i], sizeof (LITTLENUM_TYPE));
+         litP += sizeof (LITTLENUM_TYPE);
+       }
+    }
+     
+  return 0;
+}
+
+/* Write a value out to the object file, using the appropriate
+   endianness.  */
+
+void
+md_number_to_chars (buf, val, n)
+     char *buf;
+     valueT val;
+     int n;
+{
+  if (target_big_endian)
+    number_to_chars_bigendian (buf, val, n);
+  else if (target_little_endian_data
+          && ((n == 4 || n == 2) && ~now_seg->flags & SEC_ALLOC))
+    /* Output debug words, which are not in allocated sections, as big endian */
+    number_to_chars_bigendian (buf, val, n);
+  else if (target_little_endian_data || ! target_big_endian)
+    number_to_chars_littleendian (buf, val, n);
+}
+\f
+/* Apply a fixS to the frags, now that we know the value it ought to
+   hold. */
+
+int
+md_apply_fix3 (fixP, value, segment)
+     fixS *fixP;
+     valueT *value;
+     segT segment;
+{
+  char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+  offsetT val;
+  long insn;
+
+  val = *value;
+
+  assert (fixP->fx_r_type < BFD_RELOC_UNUSED);
+
+  fixP->fx_addnumber = val;    /* Remember value for emit_reloc */
+
+#ifdef OBJ_ELF
+  /* FIXME: SPARC ELF relocations don't use an addend in the data
+     field itself.  This whole approach should be somehow combined
+     with the calls to bfd_install_relocation.  Also, the value passed
+     in by fixup_segment includes the value of a defined symbol.  We
+     don't want to include the value of an externally visible symbol.  */
+  if (fixP->fx_addsy != NULL)
+    {
+      if (symbol_used_in_reloc_p (fixP->fx_addsy)
+         && (S_IS_EXTERNAL (fixP->fx_addsy)
+             || S_IS_WEAK (fixP->fx_addsy)
+             || (sparc_pic_code && ! fixP->fx_pcrel)
+             || (S_GET_SEGMENT (fixP->fx_addsy) != segment
+                 && ((bfd_get_section_flags (stdoutput,
+                                             S_GET_SEGMENT (fixP->fx_addsy))
+                      & SEC_LINK_ONCE) != 0
+                     || strncmp (segment_name (S_GET_SEGMENT (fixP->fx_addsy)),
+                                 ".gnu.linkonce",
+                                 sizeof ".gnu.linkonce" - 1) == 0)))
+         && S_GET_SEGMENT (fixP->fx_addsy) != absolute_section
+         && S_GET_SEGMENT (fixP->fx_addsy) != undefined_section
+         && ! bfd_is_com_section (S_GET_SEGMENT (fixP->fx_addsy)))
+       fixP->fx_addnumber -= S_GET_VALUE (fixP->fx_addsy);
+      return 1;
+    }
+#endif
+
+  /* This is a hack.  There should be a better way to
+     handle this.  Probably in terms of howto fields, once
+     we can look at these fixups in terms of howtos.  */
+  if (fixP->fx_r_type == BFD_RELOC_32_PCREL_S2 && fixP->fx_addsy)
+    val += fixP->fx_where + fixP->fx_frag->fr_address;
+
+#ifdef OBJ_AOUT
+  /* FIXME: More ridiculous gas reloc hacking.  If we are going to
+     generate a reloc, then we just want to let the reloc addend set
+     the value.  We do not want to also stuff the addend into the
+     object file.  Including the addend in the object file works when
+     doing a static link, because the linker will ignore the object
+     file contents.  However, the dynamic linker does not ignore the
+     object file contents.  */
+  if (fixP->fx_addsy != NULL
+      && fixP->fx_r_type != BFD_RELOC_32_PCREL_S2)
+    val = 0;
+
+  /* When generating PIC code, we do not want an addend for a reloc
+     against a local symbol.  We adjust fx_addnumber to cancel out the
+     value already included in val, and to also cancel out the
+     adjustment which bfd_install_relocation will create.  */
+  if (sparc_pic_code
+      && fixP->fx_r_type != BFD_RELOC_32_PCREL_S2
+      && fixP->fx_addsy != NULL
+      && ! S_IS_COMMON (fixP->fx_addsy)
+      && symbol_section_p (fixP->fx_addsy))
+    fixP->fx_addnumber -= 2 * S_GET_VALUE (fixP->fx_addsy);
+
+  /* When generating PIC code, we need to fiddle to get
+     bfd_install_relocation to do the right thing for a PC relative
+     reloc against a local symbol which we are going to keep.  */
+  if (sparc_pic_code
+      && fixP->fx_r_type == BFD_RELOC_32_PCREL_S2
+      && fixP->fx_addsy != NULL
+      && (S_IS_EXTERNAL (fixP->fx_addsy)
+         || S_IS_WEAK (fixP->fx_addsy))
+      && S_IS_DEFINED (fixP->fx_addsy)
+      && ! S_IS_COMMON (fixP->fx_addsy))
+    {
+      val = 0;
+      fixP->fx_addnumber -= 2 * S_GET_VALUE (fixP->fx_addsy);
+    }
+#endif
+
+  /* If this is a data relocation, just output VAL.  */
+
+  if (fixP->fx_r_type == BFD_RELOC_16)
+    {
+      md_number_to_chars (buf, val, 2);
+    }
+  else if (fixP->fx_r_type == BFD_RELOC_32
+          || fixP->fx_r_type == BFD_RELOC_SPARC_REV32)
+    {
+      md_number_to_chars (buf, val, 4);
+    }
+  else if (fixP->fx_r_type == BFD_RELOC_64)
+    {
+      md_number_to_chars (buf, val, 8);
+    }
+  else if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT 
+           || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+    {
+      fixP->fx_done = 0;
+      return 1;
+    }
+  else
+    {
+      /* It's a relocation against an instruction.  */
+
+      if (INSN_BIG_ENDIAN)
+       insn = bfd_getb32 ((unsigned char *) buf);
+      else
+       insn = bfd_getl32 ((unsigned char *) buf);
+    
+      switch (fixP->fx_r_type)
+       {
+       case BFD_RELOC_32_PCREL_S2:
+         val = val >> 2;
+         /* FIXME: This increment-by-one deserves a comment of why it's
+            being done!  */
+         if (! sparc_pic_code
+             || fixP->fx_addsy == NULL
+             || symbol_section_p (fixP->fx_addsy))
+           ++val;
+         insn |= val & 0x3fffffff;
+         break;
+
+       case BFD_RELOC_SPARC_11:
+         if (! in_signed_range (val, 0x7ff))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         insn |= val & 0x7ff;
+         break;
+
+       case BFD_RELOC_SPARC_10:
+         if (! in_signed_range (val, 0x3ff))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         insn |= val & 0x3ff;
+         break;
+
+       case BFD_RELOC_SPARC_7:
+         if (! in_bitfield_range (val, 0x7f))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         insn |= val & 0x7f;
+         break;
+
+       case BFD_RELOC_SPARC_6:
+         if (! in_bitfield_range (val, 0x3f))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         insn |= val & 0x3f;
+         break;
+
+       case BFD_RELOC_SPARC_5:
+         if (! in_bitfield_range (val, 0x1f))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         insn |= val & 0x1f;
+         break;
+
+       case BFD_RELOC_SPARC_WDISP16:
+         /* FIXME: simplify */
+         if (((val > 0) && (val & ~0x3fffc))
+             || ((val < 0) && (~(val - 1) & ~0x3fffc)))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         /* FIXME: The +1 deserves a comment.  */
+         val = (val >> 2) + 1;
+         insn |= ((val & 0xc000) << 6) | (val & 0x3fff);
+         break;
+
+       case BFD_RELOC_SPARC_WDISP19:
+         /* FIXME: simplify */
+         if (((val > 0) && (val & ~0x1ffffc))
+             || ((val < 0) && (~(val - 1) & ~0x1ffffc)))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         /* FIXME: The +1 deserves a comment.  */
+         val = (val >> 2) + 1;
+         insn |= val & 0x7ffff;
+         break;
+
+       case BFD_RELOC_SPARC_HH22:
+         val = BSR (val, 32);
+         /* intentional fallthrough */
+
+       case BFD_RELOC_SPARC_LM22:
+       case BFD_RELOC_HI22:
+         if (!fixP->fx_addsy)
+           {
+             insn |= (val >> 10) & 0x3fffff;
+           }
+         else
+           {
+             /* FIXME: Need comment explaining why we do this.  */
+             insn &= ~0xffff;
+           }
+         break;
+
+       case BFD_RELOC_SPARC22:
+         if (val & ~0x003fffff)
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         insn |= (val & 0x3fffff);
+         break;
+
+       case BFD_RELOC_SPARC_HM10:
+         val = BSR (val, 32);
+         /* intentional fallthrough */
+
+       case BFD_RELOC_LO10:
+         if (!fixP->fx_addsy)
+           {
+             insn |= val & 0x3ff;
+           }
+         else
+           {
+             /* FIXME: Need comment explaining why we do this.  */
+             insn &= ~0xff;
+           }
+         break;
+
+       case BFD_RELOC_SPARC13:
+         if (! in_signed_range (val, 0x1fff))
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("relocation overflow"));
+         insn |= val & 0x1fff;
+         break;
+
+       case BFD_RELOC_SPARC_WDISP22:
+         val = (val >> 2) + 1;
+         /* FALLTHROUGH */
+       case BFD_RELOC_SPARC_BASE22:
+         insn |= val & 0x3fffff;
+         break;
+
+       case BFD_RELOC_SPARC_H44:
+         if (!fixP->fx_addsy)
+           {
+             bfd_vma tval = val;
+             tval >>= 22;
+             insn |= tval & 0x3fffff;
+           }
+         break;
+
+       case BFD_RELOC_SPARC_M44:
+         if (!fixP->fx_addsy)
+           insn |= (val >> 12) & 0x3ff;
+         break;
+
+       case BFD_RELOC_SPARC_L44:
+         if (!fixP->fx_addsy)
+           insn |= val & 0xfff;
+         break;
+
+       case BFD_RELOC_SPARC_HIX22:
+         if (!fixP->fx_addsy)
+           {
+             val ^= ~ (offsetT) 0;
+             insn |= (val >> 10) & 0x3fffff;
+           }
+         break;
+
+       case BFD_RELOC_SPARC_LOX10:
+         if (!fixP->fx_addsy)
+           insn |= 0x1c00 | (val & 0x3ff);
+         break;
+
+       case BFD_RELOC_NONE:
+       default:
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("bad or unhandled relocation type: 0x%02x"),
+                       fixP->fx_r_type);
+         break;
+       }
+
+      if (INSN_BIG_ENDIAN)
+       bfd_putb32 (insn, (unsigned char *) buf);
+      else
+       bfd_putl32 (insn, (unsigned char *) buf);
+    }
+
+  /* Are we finished with this relocation now?  */
+  if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
+    fixP->fx_done = 1;
+
+  return 1;
+}
+
+/* Translate internal representation of relocation info to BFD target
+   format.  */
+arelent *
+tc_gen_reloc (section, fixp)
+     asection *section;
+     fixS *fixp;
+{
+  arelent *reloc;
+  bfd_reloc_code_real_type code;
+
+  reloc = (arelent *) xmalloc (sizeof (arelent));
+
+  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+  switch (fixp->fx_r_type)
+    {
+    case BFD_RELOC_16:
+    case BFD_RELOC_32:
+    case BFD_RELOC_HI22:
+    case BFD_RELOC_LO10:
+    case BFD_RELOC_32_PCREL_S2:
+    case BFD_RELOC_SPARC13:
+    case BFD_RELOC_SPARC22:
+    case BFD_RELOC_SPARC_BASE13:
+    case BFD_RELOC_SPARC_WDISP16:
+    case BFD_RELOC_SPARC_WDISP19:
+    case BFD_RELOC_SPARC_WDISP22:
+    case BFD_RELOC_64:
+    case BFD_RELOC_SPARC_5:
+    case BFD_RELOC_SPARC_6:
+    case BFD_RELOC_SPARC_7:
+    case BFD_RELOC_SPARC_10:
+    case BFD_RELOC_SPARC_11:
+    case BFD_RELOC_SPARC_HH22:
+    case BFD_RELOC_SPARC_HM10:
+    case BFD_RELOC_SPARC_LM22:
+    case BFD_RELOC_SPARC_PC_HH22:
+    case BFD_RELOC_SPARC_PC_HM10:
+    case BFD_RELOC_SPARC_PC_LM22:
+    case BFD_RELOC_SPARC_H44:
+    case BFD_RELOC_SPARC_M44:
+    case BFD_RELOC_SPARC_L44:
+    case BFD_RELOC_SPARC_HIX22:
+    case BFD_RELOC_SPARC_LOX10:
+    case BFD_RELOC_SPARC_REV32:
+    case BFD_RELOC_VTABLE_ENTRY:
+    case BFD_RELOC_VTABLE_INHERIT:
+      code = fixp->fx_r_type;
+      break;
+    default:
+      abort ();
+      return NULL;
+    }
+
+#if defined (OBJ_ELF) || defined (OBJ_AOUT)
+  /* If we are generating PIC code, we need to generate a different
+     set of relocs.  */
+
+#ifdef OBJ_ELF
+#define GOT_NAME "_GLOBAL_OFFSET_TABLE_"
+#else
+#define GOT_NAME "__GLOBAL_OFFSET_TABLE_"
+#endif
+
+  if (sparc_pic_code)
+    {
+      switch (code)
+       {
+       case BFD_RELOC_32_PCREL_S2:
+         if (! S_IS_DEFINED (fixp->fx_addsy)
+             || S_IS_COMMON (fixp->fx_addsy)
+             || S_IS_EXTERNAL (fixp->fx_addsy)
+             || S_IS_WEAK (fixp->fx_addsy))
+           code = BFD_RELOC_SPARC_WPLT30;
+         break;
+       case BFD_RELOC_HI22:
+         if (fixp->fx_addsy != NULL
+             && strcmp (S_GET_NAME (fixp->fx_addsy), GOT_NAME) == 0)
+           code = BFD_RELOC_SPARC_PC22;
+         else
+           code = BFD_RELOC_SPARC_GOT22;
+         break;
+       case BFD_RELOC_LO10:
+         if (fixp->fx_addsy != NULL
+             && strcmp (S_GET_NAME (fixp->fx_addsy), GOT_NAME) == 0)
+           code = BFD_RELOC_SPARC_PC10;
+         else
+           code = BFD_RELOC_SPARC_GOT10;
+         break;
+       case BFD_RELOC_SPARC13:
+         code = BFD_RELOC_SPARC_GOT13;
+         break;
+       default:
+         break;
+       }
+    }
+#endif /* defined (OBJ_ELF) || defined (OBJ_AOUT) */
+
+  reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+  if (reloc->howto == 0)
+    {
+      as_bad_where (fixp->fx_file, fixp->fx_line,
+                   _("internal error: can't export reloc type %d (`%s')"),
+                   fixp->fx_r_type, bfd_get_reloc_code_name (code));
+      return 0;
+    }
+
+  /* @@ Why fx_addnumber sometimes and fx_offset other times?  */
+#ifdef OBJ_AOUT
+
+  if (reloc->howto->pc_relative == 0
+      || code == BFD_RELOC_SPARC_PC10
+      || code == BFD_RELOC_SPARC_PC22)
+    reloc->addend = fixp->fx_addnumber;
+  else if (sparc_pic_code
+          && fixp->fx_r_type == BFD_RELOC_32_PCREL_S2
+          && fixp->fx_addsy != NULL
+          && (S_IS_EXTERNAL (fixp->fx_addsy)
+              || S_IS_WEAK (fixp->fx_addsy))
+          && S_IS_DEFINED (fixp->fx_addsy)
+          && ! S_IS_COMMON (fixp->fx_addsy))
+    reloc->addend = fixp->fx_addnumber;
+  else
+    reloc->addend = fixp->fx_offset - reloc->address;
+
+#else /* elf or coff */
+
+  if (reloc->howto->pc_relative == 0
+      || code == BFD_RELOC_SPARC_PC10
+      || code == BFD_RELOC_SPARC_PC22)
+    reloc->addend = fixp->fx_addnumber;
+  else if (symbol_section_p (fixp->fx_addsy))
+    reloc->addend = (section->vma
+                    + fixp->fx_addnumber
+                    + md_pcrel_from (fixp));
+  else
+    reloc->addend = fixp->fx_offset;
+#endif
+
+  return reloc;
+}
+\f
+/* We have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *
+md_undefined_symbol (name)
+     char *name;
+{
+  return 0;
+}                              /* md_undefined_symbol() */
+
+/* Round up a section size to the appropriate boundary. */
+valueT
+md_section_align (segment, size)
+     segT segment;
+     valueT size;
+{
+#ifndef OBJ_ELF
+  /* This is not right for ELF; a.out wants it, and COFF will force
+     the alignment anyways.  */
+  valueT align = ((valueT) 1
+                 << (valueT) bfd_get_section_alignment (stdoutput, segment));
+  valueT newsize;
+  /* turn alignment value into a mask */
+  align--;
+  newsize = (size + align) & ~align;
+  return newsize;
+#else
+  return size;
+#endif
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+   On the sparc, they're relative to the address of the offset, plus
+   its size.  This gets us to the following instruction.
+   (??? Is this right?  FIXME-SOON) */
+long 
+md_pcrel_from (fixP)
+     fixS *fixP;
+{
+  long ret;
 
-    case 'f':
-    case 'F':
-    case 's':
-    case 'S':
-      prec = 2;
-      break;
+  ret = fixP->fx_where + fixP->fx_frag->fr_address;
+  if (! sparc_pic_code
+      || fixP->fx_addsy == NULL
+      || symbol_section_p (fixP->fx_addsy))
+    ret += fixP->fx_size;
+  return ret;
+}
+\f
+/* Return log2 (VALUE), or -1 if VALUE is not an exact positive power
+   of two.  */
 
-    case 'd':
-    case 'D':
-    case 'r':
-    case 'R':
-      prec = 4;
-      break;
+static int
+log2 (value)
+     int value;
+{
+  int shift;
 
-    case 'x':
-    case 'X':
-      prec = 6;
-      break;
+  if (value <= 0)
+    return -1;
 
-    case 'p':
-    case 'P':
-      prec = 6;
-      break;
+  for (shift = 0; (value & 1) == 0; value >>= 1)
+    ++shift;
 
-    default:
-      *sizeP = 0;
-      return "Bad call to MD_ATOF()";
-    }
-  t = atof_ieee (input_line_pointer, type, words);
-  if (t)
-    input_line_pointer = t;
-  *sizeP = prec * sizeof (LITTLENUM_TYPE);
-  for (wordP = words; prec--;)
-    {
-      md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE));
-      litP += sizeof (LITTLENUM_TYPE);
-    }
-  return 0;
+  return (value == 1) ? shift : -1;
 }
 
 /*
- * Write out big-endian.
+ * sort of like s_lcomm
  */
-void
-md_number_to_chars (buf, val, n)
-     char *buf;
-     valueT val;
-     int n;
-{
-  number_to_chars_bigendian (buf, val, n);
-}
 
-/* Apply a fixS to the frags, now that we know the value it ought to
-   hold. */
+#ifndef OBJ_ELF
+static int max_alignment = 15;
+#endif
 
-int
-md_apply_fix (fixP, value)
-     fixS *fixP;
-     valueT *value;
+static void
+s_reserve (ignore)
+     int ignore;
 {
-  char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
-  offsetT val;
+  char *name;
+  char *p;
+  char c;
+  int align;
+  int size;
+  int temp;
+  symbolS *symbolP;
 
-  val = *value;
+  name = input_line_pointer;
+  c = get_symbol_end ();
+  p = input_line_pointer;
+  *p = c;
+  SKIP_WHITESPACE ();
 
-  assert (fixP->fx_r_type < BFD_RELOC_UNUSED);
+  if (*input_line_pointer != ',')
+    {
+      as_bad (_("Expected comma after name"));
+      ignore_rest_of_line ();
+      return;
+    }
 
-  fixP->fx_addnumber = val;    /* Remember value for emit_reloc */
+  ++input_line_pointer;
 
-#ifdef OBJ_ELF
-  /* FIXME: SPARC ELF relocations don't use an addend in the data
-     field itself.  This whole approach should be somehow combined
-     with the calls to bfd_perform_relocation.  Also, the value passed
-     in by fixup_segment includes the value of a defined symbol.  We
-     don't want to include the value of an externally visible symbol.  */
-  if (fixP->fx_addsy != NULL)
+  if ((size = get_absolute_expression ()) < 0)
     {
-      if ((S_IS_EXTERNAL (fixP->fx_addsy)
-          || S_IS_WEAK (fixP->fx_addsy)
-          || (sparc_pic_code && ! fixP->fx_pcrel))
-         && S_GET_SEGMENT (fixP->fx_addsy) != absolute_section
-         && S_GET_SEGMENT (fixP->fx_addsy) != undefined_section
-         && ! bfd_is_com_section (S_GET_SEGMENT (fixP->fx_addsy)))
-       fixP->fx_addnumber -= S_GET_VALUE (fixP->fx_addsy);
-      return 1;
+      as_bad (_("BSS length (%d.) <0! Ignored."), size);
+      ignore_rest_of_line ();
+      return;
+    }                          /* bad length */
+
+  *p = 0;
+  symbolP = symbol_find_or_make (name);
+  *p = c;
+
+  if (strncmp (input_line_pointer, ",\"bss\"", 6) != 0
+      && strncmp (input_line_pointer, ",\".bss\"", 7) != 0)
+    {
+      as_bad (_("bad .reserve segment -- expected BSS segment"));
+      return;
     }
-#endif
 
-  /* This is a hack.  There should be a better way to
-     handle this.  Probably in terms of howto fields, once
-     we can look at these fixups in terms of howtos.  */
-  if (fixP->fx_r_type == BFD_RELOC_32_PCREL_S2 && fixP->fx_addsy)
-    val += fixP->fx_where + fixP->fx_frag->fr_address;
+  if (input_line_pointer[2] == '.')
+    input_line_pointer += 7;
+  else
+    input_line_pointer += 6;
+  SKIP_WHITESPACE ();
 
-#ifdef OBJ_AOUT
-  /* FIXME: More ridiculous gas reloc hacking.  If we are going to
-     generate a reloc, then we just want to let the reloc addend set
-     the value.  We do not want to also stuff the addend into the
-     object file.  Including the addend in the object file works when
-     doing a static link, because the linker will ignore the object
-     file contents.  However, the dynamic linker does not ignore the
-     object file contents.  */
-  if (fixP->fx_addsy != NULL
-      && fixP->fx_r_type != BFD_RELOC_32_PCREL_S2)
-    val = 0;
+  if (*input_line_pointer == ',')
+    {
+      ++input_line_pointer;
 
-  /* When generating PIC code, we do not want an addend for a reloc
-     against a local symbol.  We adjust fx_addnumber to cancel out the
-     value already included in val, and to also cancel out the
-     adjustment which bfd_install_relocation will create.  */
-  if (sparc_pic_code
-      && fixP->fx_r_type != BFD_RELOC_32_PCREL_S2
-      && fixP->fx_addsy != NULL
-      && ! S_IS_COMMON (fixP->fx_addsy)
-      && (fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) == 0)
-    fixP->fx_addnumber -= 2 * S_GET_VALUE (fixP->fx_addsy);
+      SKIP_WHITESPACE ();
+      if (*input_line_pointer == '\n')
+       {
+         as_bad (_("missing alignment"));
+         ignore_rest_of_line ();
+         return;
+       }
+
+      align = (int) get_absolute_expression ();
+
+#ifndef OBJ_ELF
+      if (align > max_alignment)
+       {
+         align = max_alignment;
+         as_warn (_("alignment too large; assuming %d"), align);
+       }
 #endif
 
-  switch (fixP->fx_r_type)
-    {
-    case BFD_RELOC_16:
-      buf[0] = val >> 8;
-      buf[1] = val;
-      break;
+      if (align < 0)
+       {
+         as_bad (_("negative alignment"));
+         ignore_rest_of_line ();
+         return;
+       }
 
-    case BFD_RELOC_32:
-      buf[0] = val >> 24;
-      buf[1] = val >> 16;
-      buf[2] = val >> 8;
-      buf[3] = val;
-      break;
+      if (align != 0)
+       {
+         temp = log2 (align);
+         if (temp < 0)
+           {
+             as_bad (_("alignment not a power of 2"));
+             ignore_rest_of_line ();
+             return;
+           }
 
-    case BFD_RELOC_32_PCREL_S2:
-      val = val >> 2;
-      /* FIXME: This increment-by-one deserves a comment of why it's
-        being done!  */
-      if (! sparc_pic_code
-         || fixP->fx_addsy == NULL
-         || (fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) != 0)
-       ++val;
-      buf[0] |= (val >> 24) & 0x3f;
-      buf[1] = val >> 16;
-      buf[2] = val >> 8;
-      buf[3] = val;
-      break;
+         align = temp;
+       }
 
-    case BFD_RELOC_64:
-      {
-       bfd_vma valh = BSR (val, 32);
-       buf[0] = valh >> 24;
-       buf[1] = valh >> 16;
-       buf[2] = valh >> 8;
-       buf[3] = valh;
-       buf[4] = val >> 24;
-       buf[5] = val >> 16;
-       buf[6] = val >> 8;
-       buf[7] = val;
-      }
-      break;
+      record_alignment (bss_section, align);
+    }
+  else
+    align = 0;
 
-    case BFD_RELOC_SPARC_11:
-      if (! in_signed_range (val, 0x7ff))
-       as_bad ("relocation overflow.");
+  if (!S_IS_DEFINED (symbolP)
+#ifdef OBJ_AOUT
+      && S_GET_OTHER (symbolP) == 0
+      && S_GET_DESC (symbolP) == 0
+#endif
+      )
+    {
+      if (! need_pass_2)
+       {
+         char *pfrag;
+         segT current_seg = now_seg;
+         subsegT current_subseg = now_subseg;
 
-      buf[2] |= (val >> 8) & 0x7;
-      buf[3] = val;
-      break;
+         subseg_set (bss_section, 1); /* switch to bss */
 
-    case BFD_RELOC_SPARC_10:
-      if (! in_signed_range (val, 0x3ff))
-       as_bad ("relocation overflow.");
+         if (align)
+           frag_align (align, 0, 0); /* do alignment */
 
-      buf[2] |= (val >> 8) & 0x3;
-      buf[3] = val;
-      break;
+         /* detach from old frag */
+         if (S_GET_SEGMENT(symbolP) == bss_section)
+           symbol_get_frag (symbolP)->fr_symbol = NULL;
 
-    case BFD_RELOC_SPARC_6:
-      if (! in_bitfield_range (val, 0x3f))
-       as_bad ("relocation overflow.");
+         symbol_set_frag (symbolP, frag_now);
+         pfrag = frag_var (rs_org, 1, 1, (relax_substateT)0, symbolP,
+                           (offsetT) size, (char *)0);
+         *pfrag = 0;
 
-      buf[3] |= val & 0x3f;
-      break;
+         S_SET_SEGMENT (symbolP, bss_section);
 
-    case BFD_RELOC_SPARC_5:
-      if (! in_bitfield_range (val, 0x1f))
-       as_bad ("relocation overflow.");
+         subseg_set (current_seg, current_subseg);
 
-      buf[3] |= val & 0x1f;
-      break;
+#ifdef OBJ_ELF
+         S_SET_SIZE (symbolP, size);
+#endif
+       }
+    }
+  else
+    {
+      as_warn("Ignoring attempt to re-define symbol %s",
+             S_GET_NAME (symbolP));
+    }                          /* if not redefining */
 
-    case BFD_RELOC_SPARC_WDISP16:
-      /* FIXME: simplify */
-      if (((val > 0) && (val & ~0x3fffc))
-         || ((val < 0) && (~(val - 1) & ~0x3fffc)))
+  demand_empty_rest_of_line ();
+}
+
+static void
+s_common (ignore)
+     int ignore;
+{
+  char *name;
+  char c;
+  char *p;
+  int temp, size;
+  symbolS *symbolP;
+
+  name = input_line_pointer;
+  c = get_symbol_end ();
+  /* just after name is now '\0' */
+  p = input_line_pointer;
+  *p = c;
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer != ',')
+    {
+      as_bad (_("Expected comma after symbol-name"));
+      ignore_rest_of_line ();
+      return;
+    }
+  input_line_pointer++;                /* skip ',' */
+  if ((temp = get_absolute_expression ()) < 0)
+    {
+      as_bad (_(".COMMon length (%d.) <0! Ignored."), temp);
+      ignore_rest_of_line ();
+      return;
+    }
+  size = temp;
+  *p = 0;
+  symbolP = symbol_find_or_make (name);
+  *p = c;
+  if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+    {
+      as_bad (_("Ignoring attempt to re-define symbol"));
+      ignore_rest_of_line ();
+      return;
+    }
+  if (S_GET_VALUE (symbolP) != 0)
+    {
+      if (S_GET_VALUE (symbolP) != (valueT) size)
        {
-         as_bad ("relocation overflow.");
+         as_warn (_("Length of .comm \"%s\" is already %ld. Not changed to %d."),
+                  S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), size);
        }
+    }
+  else
+    {
+#ifndef OBJ_ELF
+      S_SET_VALUE (symbolP, (valueT) size);
+      S_SET_EXTERNAL (symbolP);
+#endif
+    }
+  know (symbolP->sy_frag == &zero_address_frag);
+  if (*input_line_pointer != ',')
+    {
+      as_bad (_("Expected comma after common length"));
+      ignore_rest_of_line ();
+      return;
+    }
+  input_line_pointer++;
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer != '"')
+    {
+      temp = get_absolute_expression ();
 
-      /* FIXME: The +1 deserves a comment.  */
-      val = (val >> 2) + 1;
-      buf[1] |= ((val >> 14) & 0x3) << 4;
-      buf[2] |= (val >> 8) & 0x3f;
-      buf[3] = val;
-      break;
+#ifndef OBJ_ELF
+      if (temp > max_alignment)
+       {
+         temp = max_alignment;
+         as_warn (_("alignment too large; assuming %d"), temp);
+       }
+#endif
 
-    case BFD_RELOC_SPARC_WDISP19:
-      /* FIXME: simplify */
-      if (((val > 0) && (val & ~0x1ffffc))
-         || ((val < 0) && (~(val - 1) & ~0x1ffffc)))
+      if (temp < 0)
        {
-         as_bad ("relocation overflow.");
+         as_bad (_("negative alignment"));
+         ignore_rest_of_line ();
+         return;
        }
 
-      /* FIXME: The +1 deserves a comment.  */
-      val = (val >> 2) + 1;
-      buf[1] |= (val >> 16) & 0x7;
-      buf[2] = (val >> 8) & 0xff;
-      buf[3] = val;
-      break;
+#ifdef OBJ_ELF
+      if (symbol_get_obj (symbolP)->local)
+       {
+         segT old_sec;
+         int old_subsec;
+         char *p;
+         int align;
 
-    case BFD_RELOC_SPARC_HH22:
-      val = BSR (val, 32);
-      /* intentional fallthrough */
+         old_sec = now_seg;
+         old_subsec = now_subseg;
 
-    case BFD_RELOC_SPARC_LM22:
-    case BFD_RELOC_HI22:
-      if (!fixP->fx_addsy)
-       {
-         buf[1] |= (val >> 26) & 0x3f;
-         buf[2] = val >> 18;
-         buf[3] = val >> 10;
+         if (temp == 0)
+           align = 0;
+         else
+           align = log2 (temp);
+
+         if (align < 0)
+           {
+             as_bad (_("alignment not a power of 2"));
+             ignore_rest_of_line ();
+             return;
+           }
+
+         record_alignment (bss_section, align);
+         subseg_set (bss_section, 0);
+         if (align)
+           frag_align (align, 0, 0);
+         if (S_GET_SEGMENT (symbolP) == bss_section)
+           symbol_get_frag (symbolP)->fr_symbol = 0;
+         symbol_set_frag (symbolP, frag_now);
+         p = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
+                       (offsetT) size, (char *) 0);
+         *p = 0;
+         S_SET_SEGMENT (symbolP, bss_section);
+         S_CLEAR_EXTERNAL (symbolP);
+         S_SET_SIZE (symbolP, size);
+         subseg_set (old_sec, old_subsec);
        }
       else
+#endif /* OBJ_ELF */
        {
-         buf[2] = 0;
-         buf[3] = 0;
+       allocate_common:
+         S_SET_VALUE (symbolP, (valueT) size);
+#ifdef OBJ_ELF
+         S_SET_ALIGN (symbolP, temp);
+         S_SET_SIZE (symbolP, size);
+#endif
+         S_SET_EXTERNAL (symbolP);
+         S_SET_SEGMENT (symbolP, bfd_com_section_ptr);
        }
-      break;
-
-    case BFD_RELOC_SPARC22:
-      if (val & ~0x003fffff)
-       {
-         as_bad ("relocation overflow");
-       }                       /* on overflow */
-      buf[1] |= (val >> 16) & 0x3f;
-      buf[2] = val >> 8;
-      buf[3] = val;
-      break;
-
-    case BFD_RELOC_SPARC_HM10:
-      val = BSR (val, 32);
-      /* intentional fallthrough */
-
-    case BFD_RELOC_LO10:
-      if (!fixP->fx_addsy)
+    }
+  else
+    {
+      input_line_pointer++;
+      /* @@ Some use the dot, some don't.  Can we get some consistency??  */
+      if (*input_line_pointer == '.')
+       input_line_pointer++;
+      /* @@ Some say data, some say bss.  */
+      if (strncmp (input_line_pointer, "bss\"", 4)
+         && strncmp (input_line_pointer, "data\"", 5))
        {
-         buf[2] |= (val >> 8) & 0x03;
-         buf[3] = val;
+         while (*--input_line_pointer != '"')
+           ;
+         input_line_pointer--;
+         goto bad_common_segment;
        }
-      else
-       buf[3] = 0;
-      break;
-
-    case BFD_RELOC_SPARC13:
-      if (! in_signed_range (val, 0x1fff))
-       as_bad ("relocation overflow");
+      while (*input_line_pointer++ != '"')
+       ;
+      goto allocate_common;
+    }
 
-      buf[2] |= (val >> 8) & 0x1f;
-      buf[3] = val;
-      break;
+#ifdef BFD_ASSEMBLER
+  symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT;
+#endif
 
-    case BFD_RELOC_SPARC_WDISP22:
-      val = (val >> 2) + 1;
-      /* FALLTHROUGH */
-    case BFD_RELOC_SPARC_BASE22:
-      buf[1] |= (val >> 16) & 0x3f;
-      buf[2] = val >> 8;
-      buf[3] = val;
-      break;
+  demand_empty_rest_of_line ();
+  return;
 
-    case BFD_RELOC_NONE:
-    default:
-      as_bad ("bad or unhandled relocation type: 0x%02x", fixP->fx_r_type);
-      break;
-    }
+  {
+  bad_common_segment:
+    p = input_line_pointer;
+    while (*p && *p != '\n')
+      p++;
+    c = *p;
+    *p = '\0';
+    as_bad (_("bad .common segment %s"), input_line_pointer + 1);
+    *p = c;
+    input_line_pointer = p;
+    ignore_rest_of_line ();
+    return;
+  }
+}
 
-  /* Are we finished with this relocation now?  */
-  if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
-    fixP->fx_done = 1;
+/* Handle the .empty pseudo-op.  This supresses the warnings about
+   invalid delay slot usage.  */
 
-  return 1;
+static void
+s_empty (ignore)
+     int ignore;
+{
+  /* The easy way to implement is to just forget about the last
+     instruction.  */
+  last_insn = NULL;
 }
 
-/* Translate internal representation of relocation info to BFD target
-   format.  */
-arelent *
-tc_gen_reloc (section, fixp)
-     asection *section;
-     fixS *fixp;
+static void
+s_seg (ignore)
+     int ignore;
 {
-  arelent *reloc;
-  bfd_reloc_code_real_type code;
 
-  reloc = (arelent *) bfd_alloc_by_size_t (stdoutput, sizeof (arelent));
-  assert (reloc != 0);
-
-  reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
-  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
-
-  switch (fixp->fx_r_type)
+  if (strncmp (input_line_pointer, "\"text\"", 6) == 0)
     {
-    case BFD_RELOC_16:
-    case BFD_RELOC_32:
-    case BFD_RELOC_HI22:
-    case BFD_RELOC_LO10:
-    case BFD_RELOC_32_PCREL_S2:
-    case BFD_RELOC_SPARC13:
-    case BFD_RELOC_SPARC_BASE13:
-    case BFD_RELOC_SPARC_WDISP16:
-    case BFD_RELOC_SPARC_WDISP19:
-    case BFD_RELOC_SPARC_WDISP22:
-    case BFD_RELOC_64:
-    case BFD_RELOC_SPARC_5:
-    case BFD_RELOC_SPARC_6:
-    case BFD_RELOC_SPARC_10:
-    case BFD_RELOC_SPARC_11:
-    case BFD_RELOC_SPARC_HH22:
-    case BFD_RELOC_SPARC_HM10:
-    case BFD_RELOC_SPARC_LM22:
-    case BFD_RELOC_SPARC_PC_HH22:
-    case BFD_RELOC_SPARC_PC_HM10:
-    case BFD_RELOC_SPARC_PC_LM22:
-      code = fixp->fx_r_type;
-      break;
-    default:
-      abort ();
+      input_line_pointer += 6;
+      s_text (0);
+      return;
     }
-
-#if defined (OBJ_ELF) || defined (OBJ_AOUT)
-  /* If we are generating PIC code, we need to generate a different
-     set of relocs.  */
-
-#ifdef OBJ_ELF
-#define GOT_NAME "_GLOBAL_OFFSET_TABLE_"
-#else
-#define GOT_NAME "__GLOBAL_OFFSET_TABLE_"
-#endif
-
-  if (sparc_pic_code)
+  if (strncmp (input_line_pointer, "\"data\"", 6) == 0)
     {
-      switch (code)
-       {
-       case BFD_RELOC_32_PCREL_S2:
-         if (! S_IS_DEFINED (fixp->fx_addsy)
-             || S_IS_EXTERNAL (fixp->fx_addsy)
-             || S_IS_WEAK (fixp->fx_addsy))
-           code = BFD_RELOC_SPARC_WPLT30;
-         break;
-       case BFD_RELOC_HI22:
-         if (fixp->fx_addsy != NULL
-             && strcmp (S_GET_NAME (fixp->fx_addsy), GOT_NAME) == 0)
-           code = BFD_RELOC_SPARC_PC22;
-         else
-           code = BFD_RELOC_SPARC_GOT22;
-         break;
-       case BFD_RELOC_LO10:
-         if (fixp->fx_addsy != NULL
-             && strcmp (S_GET_NAME (fixp->fx_addsy), GOT_NAME) == 0)
-           code = BFD_RELOC_SPARC_PC10;
-         else
-           code = BFD_RELOC_SPARC_GOT10;
-         break;
-       case BFD_RELOC_SPARC13:
-         code = BFD_RELOC_SPARC_GOT13;
-         break;
-       default:
-         break;
-       }
+      input_line_pointer += 6;
+      s_data (0);
+      return;
     }
-#endif /* defined (OBJ_ELF) || defined (OBJ_AOUT) */
+  if (strncmp (input_line_pointer, "\"data1\"", 7) == 0)
+    {
+      input_line_pointer += 7;
+      s_data1 ();
+      return;
+    }
+  if (strncmp (input_line_pointer, "\"bss\"", 5) == 0)
+    {
+      input_line_pointer += 5;
+      /* We only support 2 segments -- text and data -- for now, so
+        things in the "bss segment" will have to go into data for now.
+        You can still allocate SEG_BSS stuff with .lcomm or .reserve. */
+      subseg_set (data_section, 255);  /* FIXME-SOMEDAY */
+      return;
+    }
+  as_bad (_("Unknown segment type"));
+  demand_empty_rest_of_line ();
+}
 
-  reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
-  if (reloc->howto == 0)
+static void
+s_data1 ()
+{
+  subseg_set (data_section, 1);
+  demand_empty_rest_of_line ();
+}
+
+static void
+s_proc (ignore)
+     int ignore;
+{
+  while (!is_end_of_line[(unsigned char) *input_line_pointer])
     {
-      as_bad_where (fixp->fx_file, fixp->fx_line,
-                   "internal error: can't export reloc type %d (`%s')",
-                   fixp->fx_r_type, bfd_get_reloc_code_name (code));
-      return 0;
+      ++input_line_pointer;
     }
+  ++input_line_pointer;
+}
 
-  /* @@ Why fx_addnumber sometimes and fx_offset other times?  */
-#ifdef OBJ_AOUT
-
-  if (reloc->howto->pc_relative == 0
-      || code == BFD_RELOC_SPARC_PC10
-      || code == BFD_RELOC_SPARC_PC22)
-    reloc->addend = fixp->fx_addnumber;
-  else
-    reloc->addend = fixp->fx_offset - reloc->address;
-
-#else /* elf or coff */
-
-  if (reloc->howto->pc_relative == 0
-      || code == BFD_RELOC_SPARC_PC10
-      || code == BFD_RELOC_SPARC_PC22)
-    reloc->addend = fixp->fx_addnumber;
-  else if ((fixp->fx_addsy->bsym->flags & BSF_SECTION_SYM) != 0)
-    reloc->addend = (section->vma
-                    + fixp->fx_addnumber
-                    + md_pcrel_from (fixp));
-  else
-    reloc->addend = fixp->fx_offset;
-#endif
+/* This static variable is set by s_uacons to tell sparc_cons_align
+   that the expession does not need to be aligned.  */
 
-  return reloc;
-}
+static int sparc_no_align_cons = 0;
 
+/* This handles the unaligned space allocation pseudo-ops, such as
+   .uaword.  .uaword is just like .word, but the value does not need
+   to be aligned.  */
 
-#if 0
-/* for debugging only */
 static void
-print_insn (insn)
-     struct sparc_it *insn;
+s_uacons (bytes)
+     int bytes;
 {
-  const char *const Reloc[] = {
-    "RELOC_8",
-    "RELOC_16",
-    "RELOC_32",
-    "RELOC_DISP8",
-    "RELOC_DISP16",
-    "RELOC_DISP32",
-    "RELOC_WDISP30",
-    "RELOC_WDISP22",
-    "RELOC_HI22",
-    "RELOC_22",
-    "RELOC_13",
-    "RELOC_LO10",
-    "RELOC_SFA_BASE",
-    "RELOC_SFA_OFF13",
-    "RELOC_BASE10",
-    "RELOC_BASE13",
-    "RELOC_BASE22",
-    "RELOC_PC10",
-    "RELOC_PC22",
-    "RELOC_JMP_TBL",
-    "RELOC_SEGOFF16",
-    "RELOC_GLOB_DAT",
-    "RELOC_JMP_SLOT",
-    "RELOC_RELATIVE",
-    "NO_RELOC"
-  };
-
-  if (insn->error)
-    fprintf (stderr, "ERROR: %s\n");
-  fprintf (stderr, "opcode=0x%08x\n", insn->opcode);
-  fprintf (stderr, "reloc = %s\n", Reloc[insn->reloc]);
-  fprintf (stderr, "exp = {\n");
-  fprintf (stderr, "\t\tX_add_symbol = %s\n",
-          ((insn->exp.X_add_symbol != NULL)
-           ? ((S_GET_NAME (insn->exp.X_add_symbol) != NULL)
-              ? S_GET_NAME (insn->exp.X_add_symbol)
-              : "???")
-           : "0"));
-  fprintf (stderr, "\t\tX_sub_symbol = %s\n",
-          ((insn->exp.X_op_symbol != NULL)
-           ? (S_GET_NAME (insn->exp.X_op_symbol)
-              ? S_GET_NAME (insn->exp.X_op_symbol)
-              : "???")
-           : "0"));
-  fprintf (stderr, "\t\tX_add_number = %d\n",
-          insn->exp.X_add_number);
-  fprintf (stderr, "}\n");
+  /* Tell sparc_cons_align not to align this value.  */
+  sparc_no_align_cons = 1;
+  cons (bytes);
 }
-#endif
-\f
-/*
- * md_parse_option
- *     Invocation line includes a switch not recognized by the base assembler.
- *     See if it's a processor-specific option.  These are:
- *
- *     -bump
- *             Warn on architecture bumps.  See also -A.
- *
- *     -Av6, -Av7, -Av8, -Av9, -Av9a, -Asparclite
- *     -xarch=v8plus, -xarch=v8plusa
- *             Select the architecture.  Instructions or features not
- *             supported by the selected architecture cause fatal errors.
- *
- *             The default is to start at v6, and bump the architecture up
- *             whenever an instruction is seen at a higher level.  If 32 bit
- *             environments, v9 is not bumped up to, the user must pass -Av9.
- *
- *             -xarch=v8plus{,a} is for compatibility with the Sun assembler.
- *
- *             If -bump is specified, a warning is printing when bumping to
- *             higher levels.
- *
- *             If an architecture is specified, all instructions must match
- *             that architecture.  Any higher level instructions are flagged
- *             as errors.  Note that in the 32 bit environment specifying
- *             -Av9 does not automatically create a v9 object file, a v9
- *             insn must be seen.
- *
- *             If both an architecture and -bump are specified, the
- *             architecture starts at the specified level, but bumps are
- *             warnings.  Note that we can't set `current_architecture' to
- *             the requested level in this case: in the 32 bit environment,
- *             we still must avoid creating v9 object files unless v9 insns
- *             are seen.
- *
- * Note:
- *             Bumping between incompatible architectures is always an
- *             error.  For example, from sparclite to v9.
- */
 
-#ifdef OBJ_ELF
-CONST char *md_shortopts = "A:K:VQ:sq";
-#else
-#ifdef OBJ_AOUT
-CONST char *md_shortopts = "A:k";
-#else
-CONST char *md_shortopts = "A:";
-#endif
-#endif
-struct option md_longopts[] = {
-#define OPTION_BUMP (OPTION_MD_BASE)
-  {"bump", no_argument, NULL, OPTION_BUMP},
-#define OPTION_SPARC (OPTION_MD_BASE + 1)
-  {"sparc", no_argument, NULL, OPTION_SPARC},
-#define OPTION_XARCH (OPTION_MD_BASE + 2)
-  {"xarch", required_argument, NULL, OPTION_XARCH},
-  {NULL, no_argument, NULL, 0}
-};
-size_t md_longopts_size = sizeof(md_longopts);
+/* This handles the native word allocation pseudo-op .nword.
+   For sparc_arch_size 32 it is equivalent to .word,  for
+   sparc_arch_size 64 it is equivalent to .xword.  */
 
-int
-md_parse_option (c, arg)
-     int c;
-     char *arg;
+static void
+s_ncons (bytes)
+     int bytes;
 {
-  switch (c)
-    {
-    case OPTION_BUMP:
-      warn_on_bump = 1;
-      warn_after_architecture = SPARC_OPCODE_ARCH_V6;
-      break;
+  cons (sparc_arch_size == 32 ? 4 : 8);
+}
 
-    case OPTION_XARCH:
-      /* ??? We could add v8plus and v8plusa to sparc_opcode_archs.
-        But we might want v8plus to mean something different than v9
-        someday, and we'd recognize more -xarch options than Sun's
-        assembler does (which may lead to a conflict someday).  */
-      if (strcmp (arg, "v8plus") == 0)
-       arg = "v9";
-      else if (strcmp (arg, "v8plusa") == 0)
-       arg = "v9a";
-      else
-       {
-         as_bad ("invalid architecture -xarch=%s", arg);
-         return 0;
-       }
+/* If the --enforce-aligned-data option is used, we require .word,
+   et. al., to be aligned correctly.  We do it by setting up an
+   rs_align_code frag, and checking in HANDLE_ALIGN to make sure that
+   no unexpected alignment was introduced.
 
-      /* fall through */
+   The SunOS and Solaris native assemblers enforce aligned data by
+   default.  We don't want to do that, because gcc can deliberately
+   generate misaligned data if the packed attribute is used.  Instead,
+   we permit misaligned data by default, and permit the user to set an
+   option to check for it.  */
 
-    case 'A':
-      {
-       enum sparc_opcode_arch_val new_arch = sparc_opcode_lookup_arch (arg);
+void
+sparc_cons_align (nbytes)
+     int nbytes;
+{
+  int nalign;
+  char *p;
 
-       if (new_arch == SPARC_OPCODE_ARCH_BAD)
-         {
-           as_bad ("invalid architecture -A%s", arg);
-           return 0;
-         }
-       else
-         {
-           max_architecture = new_arch;
-           architecture_requested = 1;
-         }
-      }
-      break;
+  /* Only do this if we are enforcing aligned data.  */
+  if (! enforce_aligned_data)
+    return;
 
-    case OPTION_SPARC:
-      /* Ignore -sparc, used by SunOS make default .s.o rule.  */
-      break;
+  if (sparc_no_align_cons)
+    {
+      /* This is an unaligned pseudo-op.  */
+      sparc_no_align_cons = 0;
+      return;
+    }
 
-#ifdef OBJ_AOUT
-    case 'k':
-      sparc_pic_code = 1;
-      break;
-#endif
+  nalign = log2 (nbytes);
+  if (nalign == 0)
+    return;
 
-#ifdef OBJ_ELF
-    case 'V':
-      print_version_id ();
-      break;
+  assert (nalign > 0);
 
-    case 'Q':
-      /* Qy - do emit .comment
-        Qn - do not emit .comment */
-      break;
+  if (now_seg == absolute_section)
+    {
+      if ((abs_section_offset & ((1 << nalign) - 1)) != 0)
+       as_bad (_("misaligned data"));
+      return;
+    }
 
-    case 's':
-      /* use .stab instead of .stab.excl */
-      break;
+  p = frag_var (rs_align_code, 1, 1, (relax_substateT) 0,
+               (symbolS *) NULL, (offsetT) nalign, (char *) NULL);
 
-    case 'q':
-      /* quick -- native assembler does fewer checks */
-      break;
+  record_alignment (now_seg, nalign);
+}
 
-    case 'K':
-      if (strcmp (arg, "PIC") != 0)
-       as_warn ("Unrecognized option following -K");
-      else
-       sparc_pic_code = 1;
-      break;
-#endif
+/* This is where we do the unexpected alignment check.
+   This is called from HANDLE_ALIGN in tc-sparc.h.  */
 
-    default:
-      return 0;
+void
+sparc_handle_align (fragp)
+     fragS *fragp;
+{
+  if (fragp->fr_type == rs_align_code && !fragp->fr_subtype
+      && fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix != 0)
+    as_bad_where (fragp->fr_file, fragp->fr_line, _("misaligned data"));
+  if (fragp->fr_type == rs_align_code && fragp->fr_subtype == 1024)
+    {
+      int count = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+      
+      if (count >= 4
+         && !(count & 3)
+         && count <= 1024
+         && !((long)(fragp->fr_literal + fragp->fr_fix) & 3))
+        {
+          unsigned *p = (unsigned *)(fragp->fr_literal + fragp->fr_fix);
+          int i;
+          
+          for (i = 0; i < count; i += 4, p++)
+            if (INSN_BIG_ENDIAN)
+              number_to_chars_bigendian ((char *)p, 0x01000000, 4); /* emit nops */
+            else
+              number_to_chars_littleendian ((char *)p, 0x10000000, 4);
+
+          if (SPARC_OPCODE_ARCH_V9_P (max_architecture) && count > 8)
+            {
+             char *waddr = &fragp->fr_literal[fragp->fr_fix];
+             unsigned wval = (0x30680000 | count >> 2); /* ba,a,pt %xcc, 1f */
+             if (INSN_BIG_ENDIAN)
+               number_to_chars_bigendian (waddr, wval, 4);
+             else
+               number_to_chars_littleendian (waddr, wval, 4);
+            }
+          fragp->fr_var = count;
+        }
     }
-
-  return 1;
 }
 
+#ifdef OBJ_ELF
+/* Some special processing for a Sparc ELF file.  */
+
 void
-md_show_usage (stream)
-     FILE *stream;
+sparc_elf_final_processing ()
 {
-  const struct sparc_opcode_arch *arch;
-
-  fprintf(stream, "SPARC options:\n");
-  for (arch = &sparc_opcode_archs[0]; arch->name; arch++)
+  /* Set the Sparc ELF flag bits.  FIXME: There should probably be some
+     sort of BFD interface for this.  */
+  if (sparc_arch_size == 64)
     {
-      if (arch != &sparc_opcode_archs[0])
-       fprintf (stream, " | ");
-      fprintf (stream, "-A%s", arch->name);
+      switch (sparc_memory_model)
+       {
+       case MM_RMO:
+         elf_elfheader (stdoutput)->e_flags |= EF_SPARCV9_RMO;
+         break;
+       case MM_PSO:
+         elf_elfheader (stdoutput)->e_flags |= EF_SPARCV9_PSO;
+         break;
+       default:
+         break;
+       }
     }
-  fprintf (stream, "\n-xarch=v8plus | -xarch=v8plusa\n");
-  fprintf (stream, "\
-                       specify variant of SPARC architecture\n\
--bump                  warn when assembler switches architectures\n\
--sparc                 ignored\n");
-#ifdef OBJ_AOUT
-  fprintf (stream, "\
--k                     generate PIC\n");
-#endif
-#ifdef OBJ_ELF
-  fprintf (stream, "\
--KPIC                  generate PIC\n\
--V                     print assembler version number\n\
--q                     ignored\n\
--Qy, -Qn               ignored\n\
--s                     ignored\n");
-#endif
+  else if (current_architecture >= SPARC_OPCODE_ARCH_V9)
+    elf_elfheader (stdoutput)->e_flags |= EF_SPARC_32PLUS;
+  if (current_architecture == SPARC_OPCODE_ARCH_V9A)
+    elf_elfheader (stdoutput)->e_flags |= EF_SPARC_SUN_US1;
 }
-\f
-/* We have no need to default values of symbols. */
+#endif
 
-/* ARGSUSED */
-symbolS *
-md_undefined_symbol (name)
-     char *name;
-{
-  return 0;
-}                              /* md_undefined_symbol() */
+/* This is called by emit_expr via TC_CONS_FIX_NEW when creating a
+   reloc for a cons.  We could use the definition there, except that
+   we want to handle little endian relocs specially.  */
 
-/* Round up a section size to the appropriate boundary. */
-valueT
-md_section_align (segment, size)
-     segT segment;
-     valueT size;
+void
+cons_fix_new_sparc (frag, where, nbytes, exp)
+     fragS *frag;
+     int where;
+     unsigned int nbytes;
+     expressionS *exp;
 {
-#ifndef OBJ_ELF
-  /* This is not right for ELF; a.out wants it, and COFF will force
-     the alignment anyways.  */
-  valueT align = ((valueT) 1
-                 << (valueT) bfd_get_section_alignment (stdoutput, segment));
-  valueT newsize;
-  /* turn alignment value into a mask */
-  align--;
-  newsize = (size + align) & ~align;
-  return newsize;
-#else
-  return size;
-#endif
+  bfd_reloc_code_real_type r;
+
+  r = (nbytes == 1 ? BFD_RELOC_8 :
+       (nbytes == 2 ? BFD_RELOC_16 :
+       (nbytes == 4 ? BFD_RELOC_32 : BFD_RELOC_64)));
+
+  if (target_little_endian_data && nbytes == 4
+      && now_seg->flags & SEC_ALLOC)  
+    r = BFD_RELOC_SPARC_REV32;
+  fix_new_exp (frag, where, (int) nbytes, exp, 0, r);
 }
 
-/* Exactly what point is a PC-relative offset relative TO?
-   On the sparc, they're relative to the address of the offset, plus
-   its size.  This gets us to the following instruction.
-   (??? Is this right?  FIXME-SOON) */
-long 
-md_pcrel_from (fixP)
-     fixS *fixP;
+#ifdef OBJ_ELF
+int
+elf32_sparc_force_relocation (fixp)
+      struct fix *fixp;
 {
-  long ret;
-
-  ret = fixP->fx_where + fixP->fx_frag->fr_address;
-  if (! sparc_pic_code
-      || fixP->fx_addsy == NULL
-      || (fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) != 0)
-    ret += fixP->fx_size;
-  return ret;
+  if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+      || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+    return 1;
+  return 0;
 }
+#endif
 
-/* end of tc-sparc.c */
This page took 0.07135 seconds and 4 git commands to generate.