* read.c (potable): Add spc, ttl, xcom, xref.
[external/binutils.git] / gas / read.c
index 1e41a4e..4ae32ec 100644 (file)
@@ -1,5 +1,6 @@
 /* read.c - read a source file -
-   Copyright (C) 1986, 1987, 1990, 1991, 1993 Free Software Foundation, Inc.
+   Copyright (C) 1986, 1987, 1990, 1991, 1993, 1994
+   Free Software Foundation, Inc.
 
 This file is part of GAS, the GNU Assembler.
 
@@ -15,7 +16,7 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GAS; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
 
 #if 0
 #define MASK_CHAR (0xFF)       /* If your chars aren't 8 bits, you will
@@ -41,24 +42,10 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
 
 #include "as.h"
 #include "subsegs.h"
-
+#include "libiberty.h"
 #include "obstack.h"
 #include "listing.h"
 
-/* We need this, despite the apparent object format dependency, since
-   it defines stab types, which all object formats can use now. */
-
-#include "aout/stab_gnu.h"
-
-/* Allow backends to override the names used for the stab sections.  */
-#ifndef STAB_SECTION_NAME
-#define STAB_SECTION_NAME ".stab"
-#endif
-
-#ifndef STAB_STRING_SECTION_NAME
-#define STAB_STRING_SECTION_NAME ".stabstr"
-#endif
-
 #ifndef TC_START_LABEL
 #define TC_START_LABEL(x,y) (x==':')
 #endif
@@ -73,6 +60,8 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
 
 char *input_line_pointer;      /*->next char of source file to parse. */
 
+int generate_asm_lineno = 0;   /* flag to generate line stab for .s file */
+
 #if BITS_PER_CHAR != 8
 /*  The following table is indexed by[(char)] and will break if
     a char does not have exactly 256 states (hopefully 0:255!)!  */
@@ -84,17 +73,27 @@ die horribly;
 #define LEX_AT 0
 #endif
 
+#ifndef LEX_BR
+/* The RS/6000 assembler uses {,},[,] as parts of symbol names.  */
+#define LEX_BR 0
+#endif
+
+#ifndef LEX_PCT
+/* The Delta 68k assembler permits % inside label names.  */
+#define LEX_PCT 0
+#endif
+
 /* used by is_... macros. our ctype[] */
-const char lex_type[256] =
+char lex_type[256] =
 {
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,      /* @ABCDEFGHIJKLMNO */
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,      /* PQRSTUVWXYZ[\]^_ */
-  0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0,      /* _!"#$%&'()*+,-./ */
+  0, 0, 0, 0, 3, LEX_PCT, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0,        /* _!"#$%&'()*+,-./ */
   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,      /* 0123456789:;<=>? */
   LEX_AT, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* @ABCDEFGHIJKLMNO */
-  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3,      /* PQRSTUVWXYZ[\]^_ */
+  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, LEX_BR, 0, LEX_BR, 0, 3, /* PQRSTUVWXYZ[\]^_ */
   0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,      /* `abcdefghijklmno */
-  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0,      /* pqrstuvwxyz{|}~. */
+  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, LEX_BR, 0, LEX_BR, 0, 0, /* pqrstuvwxyz{|}~. */
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -142,7 +141,17 @@ char is_end_of_line[256] =
 static char *buffer;   /* 1st char of each buffer of lines is here. */
 static char *buffer_limit;     /*->1 + last char in buffer. */
 
-int target_big_endian;
+#ifdef TARGET_BYTES_BIG_ENDIAN
+/* Hack to deal with tc-*.h defining TARGET_BYTES_BIG_ENDIAN to empty
+   instead of to 0 or 1.  */
+#if 5 - TARGET_BYTES_BIG_ENDIAN - 5 == 10
+#undef  TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 1
+#endif
+int target_big_endian = TARGET_BYTES_BIG_ENDIAN;
+#else
+int target_big_endian /* = 0 */;
+#endif
 
 static char *old_buffer;       /* JF a hack */
 static char *old_input;
@@ -160,7 +169,30 @@ struct broken_word *broken_words;
 int new_broken_words;
 #endif
 
-static char *demand_copy_string PARAMS ((int *lenP));
+/* The current offset into the absolute section.  We don't try to
+   build frags in the absolute section, since no data can be stored
+   there.  We just keep track of the current offset.  */
+addressT abs_section_offset;
+
+/* If this line had an MRI style label, it is stored in this variable.
+   This is used by some of the MRI pseudo-ops.  */
+symbolS *mri_line_label;
+
+/* This global variable is used to support MRI common sections.  We
+   translate such sections into a common symbol.  This variable is
+   non-NULL when we are in an MRI common section.  */
+symbolS *mri_common_symbol;
+
+/* In MRI mode, after a dc.b pseudo-op with an odd number of bytes, we
+   need to align to an even byte boundary unless the next pseudo-op is
+   dc.b, ds.b, or dcb.b.  This variable is set to 1 if an alignment
+   may be needed.  */
+static int mri_pending_align;
+
+static void do_align PARAMS ((int, char *));
+static int hex_float PARAMS ((int, char *));
+static void do_org PARAMS ((segT, expressionS *, int));
+char *demand_copy_string PARAMS ((int *lenP));
 int is_it_end_of_statement PARAMS ((void));
 static segT get_segmented_expression PARAMS ((expressionS *expP));
 static segT get_known_segmented_expression PARAMS ((expressionS * expP));
@@ -184,11 +216,14 @@ read_begin ()
   for (p = line_separator_chars; *p; p++)
     is_end_of_line[(unsigned char) *p] = 1;
   /* Use more.  FIXME-SOMEDAY. */
+
+  if (flag_mri)
+    lex_type['?'] = 3;
 }
 \f
 /* set up pseudo-op tables */
 
-struct hash_control *po_hash;
+static struct hash_control *po_hash;
 
 static const pseudo_typeS potable[] =
 {
@@ -196,10 +231,35 @@ static const pseudo_typeS potable[] =
   {"align", s_align_ptwo, 0},
   {"ascii", stringer, 0},
   {"asciz", stringer, 1},
+  {"balign", s_align_bytes, 0},
 /* block */
   {"byte", cons, 1},
   {"comm", s_comm, 0},
+  {"common", s_mri_common, 0},
+  {"common.s", s_mri_common, 1},
   {"data", s_data, 0},
+  {"dc", cons, 2},
+  {"dc.b", cons, 1},
+  {"dc.d", float_cons, 'd'},
+  {"dc.l", cons, 4},
+  {"dc.s", float_cons, 'f'},
+  {"dc.w", cons, 2},
+  {"dc.x", float_cons, 'x'},
+  {"dcb", s_space, 2},
+  {"dcb.b", s_space, 1},
+  {"dcb.d", s_float_space, 'd'},
+  {"dcb.l", s_space, 4},
+  {"dcb.s", s_float_space, 'f'},
+  {"dcb.w", s_space, 2},
+  {"dcb.x", s_float_space, 'x'},
+  {"ds", s_space, 2},
+  {"ds.b", s_space, 1},
+  {"ds.d", s_space, 8},
+  {"ds.l", s_space, 4},
+  {"ds.p", s_space, 12},
+  {"ds.s", s_space, 4},
+  {"ds.w", s_space, 2},
+  {"ds.x", s_space, 12},
 #ifdef S_SET_DESC
   {"desc", s_desc, 0},
 #endif
@@ -208,7 +268,9 @@ static const pseudo_typeS potable[] =
 /* dsect */
   {"eject", listing_eject, 0}, /* Formfeed listing */
   {"else", s_else, 0},
+  {"elsec", s_else, 0},
   {"end", s_end, 0},
+  {"endc", s_endif, 0},
   {"endif", s_endif, 0},
 /* endef */
   {"equ", s_set, 0},
@@ -217,16 +279,24 @@ static const pseudo_typeS potable[] =
   {"extern", s_ignore, 0},     /* We treat all undef as ext */
   {"appfile", s_app_file, 1},
   {"appline", s_app_line, 0},
+  {"fail", s_fail, 0},
   {"file", s_app_file, 0},
   {"fill", s_fill, 0},
   {"float", float_cons, 'f'},
+  {"format", s_ignore, 0},
   {"global", s_globl, 0},
   {"globl", s_globl, 0},
   {"hword", cons, 2},
-  {"if", s_if, 0},
+  {"if", s_if, (int) O_ne},
   {"ifdef", s_ifdef, 0},
+  {"ifeq", s_if, (int) O_eq},
   {"ifeqs", s_ifeqs, 0},
+  {"ifge", s_if, (int) O_ge},
+  {"ifgt", s_if, (int) O_gt},
+  {"ifle", s_if, (int) O_le},
+  {"iflt", s_if, (int) O_lt},
   {"ifndef", s_ifdef, 1},
+  {"ifne", s_if, (int) O_ne},
   {"ifnes", s_ifeqs, 1},
   {"ifnotdef", s_ifdef, 1},
   {"include", s_include, 0},
@@ -234,11 +304,18 @@ static const pseudo_typeS potable[] =
   {"lcomm", s_lcomm, 0},
   {"lflags", listing_flags, 0},        /* Listing flags */
   {"list", listing_list, 1},   /* Turn listing on */
+  {"llen", listing_psize, 1},
   {"long", cons, 4},
   {"lsym", s_lsym, 0},
+  {"noformat", s_ignore, 0},
   {"nolist", listing_list, 0}, /* Turn listing off */
+  {"nopage", listing_nopage, 0},
   {"octa", cons, 16},
+  {"offset", s_struct, 0},
   {"org", s_org, 0},
+  {"p2align", s_align_ptwo, 0},
+  {"page", listing_eject, 0},
+  {"plen", listing_psize, 0},
   {"psize", listing_psize, 0}, /* set paper size */
 /* print */
   {"quad", cons, 8},
@@ -250,81 +327,82 @@ static const pseudo_typeS potable[] =
   {"single", float_cons, 'f'},
 /* size */
   {"space", s_space, 0},
+  {"spc", s_ignore, 0},
   {"stabd", s_stab, 'd'},
   {"stabn", s_stab, 'n'},
   {"stabs", s_stab, 's'},
   {"string", stringer, 1},
+  {"struct", s_struct, 0},
 /* tag */
   {"text", s_text, 0},
+
+  /* This is for gcc to use.  It's only just been added (2/94), so gcc
+     won't be able to use it for a while -- probably a year or more.
+     But once this has been released, check with gcc maintainers
+     before deleting it or even changing the spelling.  */
+  {"this_GCC_requires_the_GNU_assembler", s_ignore, 0},
+  /* If we're folding case -- done for some targets, not necessarily
+     all -- the above string in an input file will be converted to
+     this one.  Match it either way...  */
+  {"this_gcc_requires_the_gnu_assembler", s_ignore, 0},
+
   {"title", listing_title, 0}, /* Listing title */
+  {"ttl", listing_title, 0},
 /* type */
 /* use */
 /* val */
+  {"xcom", s_comm, 0},
+  {"xdef", s_globl, 0},
+  {"xref", s_ignore, 0},
   {"xstabs", s_xstab, 's'},
   {"word", cons, 2},
+  {"zero", s_space, 0},
   {NULL}                       /* end sentinel */
 };
 
-static void 
-pobegin ()
+static int pop_override_ok = 0;
+static const char *pop_table_name;
+
+void
+pop_insert (table)
+     const pseudo_typeS *table;
 {
-  const char *errtxt;                  /* error text */
+  const char *errtxt;
   const pseudo_typeS *pop;
+  for (pop = table; pop->poc_name; pop++)
+    {
+      errtxt = hash_insert (po_hash, pop->poc_name, (char *) pop);
+      if (errtxt && (!pop_override_ok || strcmp (errtxt, "exists")))
+       as_fatal ("error constructing %s pseudo-op table", pop_table_name);
+    }
+}
 
+#ifndef md_pop_insert
+#define md_pop_insert()                pop_insert(md_pseudo_table)
+#endif
+
+#ifndef obj_pop_insert
+#define obj_pop_insert()       pop_insert(obj_pseudo_table)
+#endif
+
+static void 
+pobegin ()
+{
   po_hash = hash_new ();
 
   /* Do the target-specific pseudo ops. */
-  for (pop = md_pseudo_table; pop->poc_name; pop++)
-    {
-      errtxt = hash_insert (po_hash, pop->poc_name, (char *) pop);
-      if (errtxt)
-       {
-         as_fatal ("error constructing md pseudo-op table");
-       }                       /* on error */
-    }                          /* for each op */
+  pop_table_name = "md";
+  md_pop_insert ();
 
   /* Now object specific.  Skip any that were in the target table. */
-  for (pop = obj_pseudo_table; pop->poc_name; pop++)
-    {
-      errtxt = hash_insert (po_hash, pop->poc_name, (char *) pop);
-      if (errtxt)
-       {
-         if (!strcmp (errtxt, "exists"))
-           {
-#ifdef DIE_ON_OVERRIDES
-             as_fatal ("pseudo op \".%s\" overridden.\n", pop->poc_name);
-#endif /* DIE_ON_OVERRIDES */
-             continue;         /* OK if target table overrides. */
-           }
-         else
-           {
-             as_fatal ("error constructing obj pseudo-op table");
-           }                   /* if overridden */
-       }                       /* on error */
-    }                          /* for each op */
+  pop_table_name = "obj";
+  pop_override_ok = 1;
+  obj_pop_insert ();
 
   /* Now portable ones.  Skip any that we've seen already. */
-  for (pop = potable; pop->poc_name; pop++)
-    {
-      errtxt = hash_insert (po_hash, pop->poc_name, (char *) pop);
-      if (errtxt)
-       {
-         if (!strcmp (errtxt, "exists"))
-           {
-#ifdef DIE_ON_OVERRIDES
-             as_fatal ("pseudo op \".%s\" overridden.\n", pop->poc_name);
-#endif /* DIE_ON_OVERRIDES */
-             continue;         /* OK if target table overrides. */
-           }
-         else
-           {
-             as_fatal ("error constructing obj pseudo-op table");
-           }                   /* if overridden */
-       }                       /* on error */
-    }                          /* for each op */
-
-  return;
-}                              /* pobegin() */
+  pop_table_name = "standard";
+  pop_insert (potable);
+}
 \f
 #define HANDLE_CONDITIONAL_ASSEMBLY()                                  \
   if (ignore_input ())                                                 \
@@ -370,26 +448,59 @@ read_a_source_file (name)
           * If input_line_pointer [-1] == '\n' then we just
           * scanned another line: so bump line counters.
           */
-         if (input_line_pointer[-1] == '\n')
+         if (is_end_of_line[(unsigned char) input_line_pointer[-1]])
            {
-             bump_line_counters ();
+#ifdef md_start_line_hook
+             md_start_line_hook ();
+#endif
 
-#ifdef MRI
-             /* Text at the start of a line must be a label, we run down and stick a colon in */
-             if (is_name_beginner (*input_line_pointer))
+             if (input_line_pointer[-1] == '\n')
+               bump_line_counters ();
+
+             if (flag_mri
+#ifdef LABELS_WITHOUT_COLONS
+                 || 1
+#endif
+                 )
                {
-                 char *line_start = input_line_pointer;
-                 char c = get_symbol_end ();
-                 colon (line_start);
-                 *input_line_pointer = c;
-                 if (c == ':')
-                   input_line_pointer++;
+                 mri_line_label = NULL;
 
+                 /* Text at the start of a line must be a label, we
+                    run down and stick a colon in.  */
+                 if (is_name_beginner (*input_line_pointer))
+                   {
+                     char *line_start = input_line_pointer;
+                     char c = get_symbol_end ();
+
+                     if (! ignore_input ())
+                       {
+                         /* In MRI mode, the EQU pseudoop must be
+                            handled specially.  */
+                         if (flag_mri)
+                           {
+                             if (((strncasecmp (input_line_pointer + 1,
+                                                "EQU", 3) == 0)
+                                  || (strncasecmp (input_line_pointer + 1,
+                                                   "SET", 3) == 0))
+                                 && (input_line_pointer[4] == ' '
+                                     || input_line_pointer[4] == '\t'))
+                               {
+                                 input_line_pointer += 4;
+                                 equals (line_start);
+                                 continue;
+                               }
+                           }
+
+                         mri_line_label = colon (line_start);
+                       }
+
+                     *input_line_pointer = c;
+                     if (c == ':')
+                       input_line_pointer++;
+                   }
                }
-#endif
            }
 
-
          /*
           * We are at the begining of a line, or similar place.
           * We expect a well-formed assembler statement.
@@ -418,7 +529,8 @@ read_a_source_file (name)
           * Input_line_pointer points after that character.
           */
          if (is_name_beginner (c))
-           {                   /* want user-defined label or pseudo/opcode */
+           {
+             /* want user-defined label or pseudo/opcode */
              HANDLE_CONDITIONAL_ASSEMBLY ();
 
              s = --input_line_pointer;
@@ -451,11 +563,6 @@ read_a_source_file (name)
                }
              else
                {               /* expect pseudo-op or machine instruction */
-#ifdef MRI
-                 if (!done_pseudo (s))
-
-#else
-
                  pop = NULL;
 
 #define IGNORE_OPCODE_CASE
@@ -471,12 +578,18 @@ read_a_source_file (name)
                  }
 #endif
 
+                 if (flag_mri
 #ifdef NO_PSEUDO_DOT
-                 /* The m88k uses pseudo-ops without a period.  */
-                 pop = (pseudo_typeS *) hash_find (po_hash, s);
-                 if (pop != NULL && pop->poc_handler == NULL)
-                   pop = NULL;
+                     || 1
 #endif
+                     )
+                   {
+                     /* The MRI assembler and the m88k use pseudo-ops
+                         without a period.  */
+                     pop = (pseudo_typeS *) hash_find (po_hash, s);
+                     if (pop != NULL && pop->poc_handler == NULL)
+                       pop = NULL;
+                   }
 
                  if (pop != NULL || *s == '.')
                    {
@@ -491,6 +604,20 @@ read_a_source_file (name)
                      if (pop == NULL)
                        pop = (pseudo_typeS *) hash_find (po_hash, s + 1);
 
+                     /* In MRI mode, we may need to insert an
+                         automatic alignment directive.  What a hack
+                         this is.  */
+                     if (mri_pending_align
+                         && (pop == NULL
+                             || ! ((pop->poc_handler == cons
+                                    && pop->poc_val == 1)
+                                   || (pop->poc_handler == s_space
+                                       && pop->poc_val == 1))))
+                       {
+                         do_align (1, (char *) NULL);
+                         mri_pending_align = 0;
+                       }
+
                      /* Print the error msg now, while we still can */
                      if (pop == NULL)
                        {
@@ -513,10 +640,19 @@ read_a_source_file (name)
                       * after pseudo-operation.
                       */
                      (*pop->poc_handler) (pop->poc_val);
+
+                     /* If that was .end, just get out now.  */
+                     if (pop->poc_handler == s_end)
+                       goto quit;
                    }
                  else
-#endif
                    {           /* machine instruction */
+                     if (mri_pending_align)
+                       {
+                         do_align (1, (char *) NULL);
+                         mri_pending_align = 0;
+                       }
+
                      /* WARNING: c has char, which may be end-of-line. */
                      /* Also: input_line_pointer->`\0` where c was. */
                      *input_line_pointer = c;
@@ -532,6 +668,22 @@ read_a_source_file (name)
                      c = *input_line_pointer;
                      *input_line_pointer = '\0';
 
+#ifdef OBJ_GENERATE_ASM_LINENO
+                     if (generate_asm_lineno == 0)
+                       {
+                         if (ecoff_no_current_file ())
+                           generate_asm_lineno = 1;
+                       }
+                     if (generate_asm_lineno == 1)
+                       {
+                         unsigned int lineno;
+                         char *s;
+
+                         as_where (&s, &lineno);
+                         OBJ_GENERATE_ASM_LINENO (s, lineno);
+                       }
+#endif
+
                      md_assemble (s);  /* Assemble 1 instruction. */
 
                      *input_line_pointer++ = c;
@@ -548,8 +700,8 @@ read_a_source_file (name)
          if (is_end_of_line[(unsigned char) c])
            continue;
 
-#if defined(LOCAL_LABELS_DOLLAR) || defined(LOCAL_LABELS_FB)
-         if (isdigit (c))
+         if ((LOCAL_LABELS_DOLLAR || LOCAL_LABELS_FB)
+             && isdigit (c))
            {
              /* local label  ("4:") */
              char *backup = input_line_pointer;
@@ -564,8 +716,8 @@ read_a_source_file (name)
                  ++input_line_pointer;
                }               /* read the whole number */
 
-#ifdef LOCAL_LABELS_DOLLAR
-             if (*input_line_pointer == '$'
+             if (LOCAL_LABELS_DOLLAR
+                 && *input_line_pointer == '$'
                  && *(input_line_pointer + 1) == ':')
                {
                  input_line_pointer += 2;
@@ -579,20 +731,17 @@ read_a_source_file (name)
                  colon (dollar_label_name (temp, 0));
                  continue;
                }
-#endif /* LOCAL_LABELS_DOLLAR */
 
-#ifdef LOCAL_LABELS_FB
-             if (*input_line_pointer++ == ':')
+             if (LOCAL_LABELS_FB
+                 && *input_line_pointer++ == ':')
                {
                  fb_label_instance_inc (temp);
                  colon (fb_label_name (temp, 0));
                  continue;
                }
-#endif /* LOCAL_LABELS_FB */
 
              input_line_pointer = backup;
            }                   /* local label  ("4:") */
-#endif /* LOCAL_LABELS_DOLLAR or LOCAL_LABELS_FB */
 
          if (c && strchr (line_comment_chars, c))
            {                   /* Its a comment.  Better say APP or NO_APP */
@@ -638,7 +787,7 @@ read_a_source_file (name)
                        num = buffer_limit - buffer;
 
                      tmp_buf = xrealloc (tmp_buf, tmp_len + num);
-                     memcpy (tmp_buf, buffer + tmp_len, num);
+                     memcpy (tmp_buf + tmp_len, buffer, num);
                      tmp_len += num;
                    }
                  while (!ends);
@@ -692,6 +841,11 @@ read_a_source_file (name)
          input_line_pointer--; /* Report unknown char as ignored. */
          ignore_rest_of_line ();
        }                       /* while (input_line_pointer<buffer_limit) */
+
+#ifdef md_after_pass_hook
+      md_after_pass_hook ();
+#endif
+
       if (old_buffer)
        {
          bump_line_counters ();
@@ -705,16 +859,52 @@ read_a_source_file (name)
            }
        }
     }                          /* while (more buffers to scan) */
-  input_scrub_close ();                /* Close the input file */
 
-}                              /* read_a_source_file() */
+ quit:
+  input_scrub_close ();                /* Close the input file */
+}
 
 void 
 s_abort (ignore)
      int ignore;
 {
   as_fatal (".abort detected.  Abandoning ship.");
-}                              /* s_abort() */
+}
+
+/* Guts of .align directive.  */
+static void 
+do_align (n, fill)
+     int n;
+     char *fill;
+{
+#ifdef md_do_align
+  md_do_align (n, fill, just_record_alignment);
+#endif
+  if (!fill)
+    {
+      /* @@ Fix this right for BFD!  */
+      static char zero;
+      static char nop_opcode = NOP_OPCODE;
+
+      if (now_seg != data_section && now_seg != bss_section)
+       {
+         fill = &nop_opcode;
+       }
+      else
+       {
+         fill = &zero;
+       }
+    }
+  /* Only make a frag if we HAVE to. . . */
+  if (n && !need_pass_2)
+    frag_align (n, *fill);
+
+#ifdef md_do_align
+ just_record_alignment:
+#endif
+
+  record_alignment (now_seg, n);
+}
 
 /* For machines where ".align 4" means align to a 4 byte boundary. */
 void 
@@ -722,7 +912,7 @@ s_align_bytes (arg)
      int arg;
 {
   register unsigned int temp;
-  register long temp_fill;
+  char temp_fill;
   unsigned int i = 0;
   unsigned long max_alignment = 1 << 15;
 
@@ -736,10 +926,8 @@ s_align_bytes (arg)
       as_bad ("Alignment too large: %d. assumed.", temp = max_alignment);
     }
 
-  /*
-     * For the sparc, `.align (1<<n)' actually means `.align n'
-     * so we have to convert it.
-     */
+  /* For the sparc, `.align (1<<n)' actually means `.align n' so we
+     have to convert it.  */
   if (temp != 0)
     {
       for (i = 0; (temp & 1) == 0; temp >>= 1, ++i)
@@ -753,19 +941,13 @@ s_align_bytes (arg)
     {
       input_line_pointer++;
       temp_fill = get_absolute_expression ();
+      do_align (temp, &temp_fill);
     }
-  else if (now_seg != data_section && now_seg != bss_section)
-    temp_fill = NOP_OPCODE;
   else
-    temp_fill = 0;
-  /* Only make a frag if we HAVE to. . . */
-  if (temp && !need_pass_2)
-    frag_align ((int) temp, (int) temp_fill);
-
-  record_alignment (now_seg, (int) temp);
+    do_align (temp, (char *) 0);
 
   demand_empty_rest_of_line ();
-}                              /* s_align_bytes() */
+}
 
 /* For machines where ".align 4" means align to 2**4 boundary. */
 void 
@@ -773,7 +955,7 @@ s_align_ptwo (ignore)
      int ignore;
 {
   register int temp;
-  register long temp_fill;
+  char temp_fill;
   long max_alignment = 15;
 
   temp = get_absolute_expression ();
@@ -788,20 +970,13 @@ s_align_ptwo (ignore)
     {
       input_line_pointer++;
       temp_fill = get_absolute_expression ();
+      do_align (temp, &temp_fill);
     }
-  /* @@ Fix this right for BFD!  */
-  else if (now_seg != data_section && now_seg != bss_section)
-    temp_fill = NOP_OPCODE;
   else
-    temp_fill = 0;
-  /* Only make a frag if we HAVE to. . . */
-  if (temp && !need_pass_2)
-    frag_align (temp, (int) temp_fill);
-
-  record_alignment (now_seg, temp);
+    do_align (temp, (char *) 0);
 
   demand_empty_rest_of_line ();
-}                              /* s_align_ptwo() */
+}
 
 void 
 s_comm (ignore)
@@ -837,7 +1012,8 @@ s_comm (ignore)
   *p = c;
   if (S_IS_DEFINED (symbolP))
     {
-      as_bad ("Ignoring attempt to re-define symbol");
+      as_bad ("Ignoring attempt to re-define symbol `%s'.",
+             S_GET_NAME (symbolP));
       ignore_rest_of_line ();
       return;
     }
@@ -855,13 +1031,114 @@ s_comm (ignore)
       S_SET_EXTERNAL (symbolP);
     }
 #ifdef OBJ_VMS
-  if ( (!temp) || !flagseen['1'])
-    S_GET_OTHER(symbolP) = const_flag;
+  {
+    extern int flag_one;
+    if ( (!temp) || !flag_one)
+      S_GET_OTHER(symbolP) = const_flag;
+  }
 #endif /* not OBJ_VMS */
   know (symbolP->sy_frag == &zero_address_frag);
   demand_empty_rest_of_line ();
 }                              /* s_comm() */
 
+/* The MRI COMMON pseudo-op.  We handle this by creating a common
+   symbol with the appropriate name.  We make s_space do the right
+   thing by increasing the size.  */
+
+void
+s_mri_common (small)
+     int small;
+{
+  char *name;
+  char c;
+  char *alc = NULL;
+  symbolS *sym;
+  offsetT align;
+
+  if (! flag_mri)
+    {
+      s_comm (0);
+      return;
+    }
+
+  SKIP_WHITESPACE ();
+
+  name = input_line_pointer;
+  if (! isdigit ((unsigned char) *name))
+    c = get_symbol_end ();
+  else
+    {
+      do
+       {
+         ++input_line_pointer;
+       }
+      while (isdigit ((unsigned char) *input_line_pointer));
+      c = *input_line_pointer;
+      *input_line_pointer = '\0';
+
+      if (mri_line_label != NULL)
+       {
+         alc = (char *) xmalloc (strlen (S_GET_NAME (mri_line_label))
+                                 + (input_line_pointer - name)
+                                 + 1);
+         sprintf (alc, "%s%s", name, S_GET_NAME (mri_line_label));
+         name = alc;
+       }
+    }
+
+  sym = symbol_find_or_make (name);
+  *input_line_pointer = c;
+  if (alc != NULL)
+    free (alc);
+
+  if (*input_line_pointer != ',')
+    align = 0;
+  else
+    {
+      ++input_line_pointer;
+      align = get_absolute_expression ();
+    }
+
+  if (S_IS_DEFINED (sym))
+    {
+#if defined (S_IS_COMMON) || defined (BFD_ASSEMBLER)
+      if (! S_IS_COMMON (sym))
+#endif
+       {
+         as_bad ("attempt to re-define symbol `%s'", S_GET_NAME (sym));
+         ignore_rest_of_line ();
+         return;
+       }
+    }
+
+  S_SET_EXTERNAL (sym);
+  mri_common_symbol = sym;
+
+#ifdef S_SET_ALIGN
+  if (align != 0)
+    S_SET_ALIGN (sym, align);
+#endif
+
+  if (mri_line_label != NULL)
+    {
+      mri_line_label->sy_value.X_op = O_symbol;
+      mri_line_label->sy_value.X_add_symbol = sym;
+      mri_line_label->sy_value.X_add_number = S_GET_VALUE (sym);
+      mri_line_label->sy_frag = &zero_address_frag;
+      S_SET_SEGMENT (mri_line_label, expr_section);
+    }
+
+  /* FIXME: We just ignore the small argument, which distinguishes
+     COMMON and COMMON.S.  I don't know what we can do about it.  */
+
+  /* Ignore the type and hptype.  */
+  if (*input_line_pointer == ',')
+    input_line_pointer += 2;
+  if (*input_line_pointer == ',')
+    input_line_pointer += 2;
+  demand_empty_rest_of_line ();
+}
+
 void
 s_data (ignore)
      int ignore;
@@ -870,7 +1147,7 @@ s_data (ignore)
   register int temp;
 
   temp = get_absolute_expression ();
-  if (flagseen['R'])
+  if (flag_readonly_data_in_text)
     {
       section = text_section;
       temp += 1000;
@@ -913,11 +1190,8 @@ s_app_file (appfile)
        listing_source_file (s);
 #endif
     }
-#ifdef OBJ_COFF
-  c_dot_file_symbol (s);
-#endif /* OBJ_COFF */
-#ifdef OBJ_ELF
-  elf_file_symbol (s);
+#ifdef obj_app_file
+  obj_app_file (s);
 #endif
 }
 
@@ -934,11 +1208,51 @@ s_app_line (ignore)
 
   /* The given number is that of the next line.  */
   l = get_absolute_expression () - 1;
-  new_logical_line ((char *) NULL, l);
+  if (l < 0)
+    /* Some of the back ends can't deal with non-positive line numbers.
+       Besides, it's silly.  */
+    as_warn ("Line numbers must be positive; line number %d rejected.", l+1);
+  else
+    {
+      new_logical_line ((char *) NULL, l);
 #ifdef LISTING
-  if (listing)
-    listing_source_line (l);
+      if (listing)
+       listing_source_line (l);
 #endif
+    }
+  demand_empty_rest_of_line ();
+}
+
+/* Handle the .end pseudo-op.  Actually, the real work is done in
+   read_a_source_file.  */
+
+void
+s_end (ignore)
+     int ignore;
+{
+  if (flag_mri)
+    {
+      /* The MRI assembler permits the start symbol to follow .end,
+         but we don't support that.  */
+      SKIP_WHITESPACE ();
+      if (! is_end_of_line[(unsigned char) *input_line_pointer])
+       as_warn ("start address not supported");
+    }
+}
+
+/* Handle the MRI fail pseudo-op.  */
+
+void
+s_fail (ignore)
+     int ignore;
+{
+  offsetT temp;
+
+  temp = get_absolute_expression ();
+  if (temp >= 500)
+    as_warn (".fail %ld encountered", (long) temp);
+  else
+    as_bad (".fail %ld encountered", (long) temp);
   demand_empty_rest_of_line ();
 }
 
@@ -1075,16 +1389,32 @@ s_lcomm (needs_align)
       return;
     }
 
-#ifdef TC_MIPS
-#if defined (OBJ_ECOFF) || defined (OBJ_ELF)
-  /* For MIPS ECOFF or ELF, small objects are put in .sbss.  */
-  if (temp <= bfd_get_gp_size (stdoutput))
+#if defined (TC_MIPS) || defined (TC_ALPHA)
+  if (OUTPUT_FLAVOR == bfd_target_ecoff_flavour
+      || OUTPUT_FLAVOR == bfd_target_elf_flavour)
     {
-      bss_seg = subseg_new (".sbss", 1);
-      seg_info (bss_seg)->bss = 1;
+      /* For MIPS and Alpha ECOFF or ELF, small objects are put in .sbss.  */
+      if (temp <= bfd_get_gp_size (stdoutput))
+       {
+         bss_seg = subseg_new (".sbss", 1);
+         seg_info (bss_seg)->bss = 1;
+       }
     }
 #endif
-#endif
+   if (!needs_align)
+     {
+       /* FIXME. This needs to be machine independent. */
+       if (temp >= 8)
+        align = 3;
+       else if (temp >= 4)
+        align = 2;
+       else if (temp >= 2)
+        align = 1;
+       else
+        align = 0;
+
+       record_alignment(bss_seg, align);
+     }
 
   if (needs_align)
     {
@@ -1116,6 +1446,18 @@ s_lcomm (needs_align)
        }
       record_alignment (bss_seg, align);
     }                          /* if needs align */
+  else
+    {
+      /* Assume some objects may require alignment on some systems.  */
+#ifdef TC_ALPHA
+      if (temp > 1)
+       {
+         align = ffs (temp) - 1;
+         if (temp % (1 << align))
+           abort ();
+       }
+#endif
+    }
 
   *p = 0;
   symbolP = symbol_find_or_make (name);
@@ -1157,9 +1499,8 @@ s_lcomm (needs_align)
 #endif /* OBJ_COFF */
     }
   else
-    {
-      as_bad ("Ignoring attempt to re-define symbol %s.", name);
-    }
+    as_bad ("Ignoring attempt to re-define symbol `%s'.",
+           S_GET_NAME (symbolP));
 
   subseg_set (current_seg, current_subseg);
 
@@ -1228,6 +1569,39 @@ s_lsym (ignore)
   demand_empty_rest_of_line ();
 }                              /* s_lsym() */
 
+/* Handle changing the location counter.  */
+
+static void
+do_org (segment, exp, fill)
+     segT segment;
+     expressionS *exp;
+     int fill;
+{
+  if (segment != now_seg && segment != absolute_section)
+    as_bad ("invalid segment \"%s\"; segment \"%s\" assumed",
+           segment_name (segment), segment_name (now_seg));
+
+  if (now_seg == absolute_section)
+    {
+      if (fill != 0)
+       as_warn ("ignoring fill value in absolute section");
+      if (exp->X_op != O_constant)
+       {
+         as_bad ("only constant offsets supported in absolute section");
+         exp->X_add_number = 0;
+       }
+      abs_section_offset = exp->X_add_number;
+    }
+  else
+    {
+      char *p;
+
+      p = frag_var (rs_org, 1, 1, (relax_substateT) 0, exp->X_add_symbol,
+                   exp->X_add_number, (char *) NULL);
+      *p = fill;
+    }
+}
+
 void 
 s_org (ignore)
      int ignore;
@@ -1235,7 +1609,17 @@ s_org (ignore)
   register segT segment;
   expressionS exp;
   register long temp_fill;
-  register char *p;
+
+  /* The MRI assembler has a different meaning for .org.  It means to
+     create an absolute section at a given address.  We can't support
+     that--use a linker script instead.  */
+  if (flag_mri)
+    {
+      as_bad ("MRI style ORG pseudo-op not supported");
+      ignore_rest_of_line ();
+      return;
+    }
+
   /* Don't believe the documentation of BSD 4.2 AS.  There is no such
      thing as a sub-segment-relative origin.  Any absolute origin is
      given a warning, then assumed to be segment-relative.  Any
@@ -1256,18 +1640,101 @@ s_org (ignore)
     }
   else
     temp_fill = 0;
+
   if (!need_pass_2)
-    {
-      if (segment != now_seg && segment != absolute_section)
-       as_bad ("Invalid segment \"%s\". Segment \"%s\" assumed.",
-               segment_name (segment), segment_name (now_seg));
-      p = frag_var (rs_org, 1, 1, (relax_substateT) 0, exp.X_add_symbol,
-                   exp.X_add_number, (char *) 0);
-      *p = temp_fill;
-    }                          /* if (ok to make frag) */
+    do_org (segment, &exp, temp_fill);
+
   demand_empty_rest_of_line ();
 }                              /* s_org() */
 
+/* Handle parsing for the MRI SECT/SECTION pseudo-op.  This should be
+   called by the obj-format routine which handles section changing
+   when in MRI mode.  It will create a new section, and return it.  It
+   will set *TYPE to the section type: one of '\0' (unspecified), 'C'
+   (code), 'D' (data), 'M' (mixed), or 'R' (romable).  If
+   BFD_ASSEMBLER is defined, the flags will be set in the section.  */
+
+void
+s_mri_sect (type)
+     char *type;
+{
+  char *name;
+  char c;
+  segT seg;
+
+  SKIP_WHITESPACE ();
+  
+  name = input_line_pointer;
+  if (! isdigit ((unsigned char) *name))
+    c = get_symbol_end ();
+  else
+    {
+      do
+       {
+         ++input_line_pointer;
+       }
+      while (isdigit ((unsigned char) *input_line_pointer));
+      c = *input_line_pointer;
+      *input_line_pointer = '\0';
+    }
+
+  name = strdup (name);
+  if (name == NULL)
+    as_fatal ("virtual memory exhausted");
+
+  *input_line_pointer = c;
+
+  seg = subseg_new (name, 0);
+
+  if (*input_line_pointer == ',')
+    {
+      int align;
+
+      ++input_line_pointer;
+      align = get_absolute_expression ();
+      record_alignment (seg, align);
+    }
+
+  *type = '\0';
+  if (*input_line_pointer == ',')
+    {
+      c = *++input_line_pointer;
+      c = toupper ((unsigned char) c);
+      if (c == 'C' || c == 'D' || c == 'M' || c == 'R')
+       *type = c;
+      else
+       as_bad ("unrecognized section type");
+      ++input_line_pointer;
+
+#ifdef BFD_ASSEMBLER
+      {
+       flagword flags;
+
+       flags = SEC_NO_FLAGS;
+       if (type == 'C')
+         flags = SEC_CODE;
+       else if (type == 'D')
+         flags = SEC_DATA;
+       else if (type == 'R')
+         flags = SEC_ROM;
+       if (flags != SEC_NO_FLAGS)
+         {
+           if (! bfd_set_section_flags (stdoutput, seg, flags))
+             as_warn ("error setting flags for \"%s\": %s",
+                      bfd_section_name (stdoutput, sec),
+                      bfd_errmsg (bfd_get_error ()));
+         }
+      }
+#endif
+    }
+
+  /* Ignore the HP type.  */
+  if (*input_line_pointer == ',')
+    input_line_pointer += 2;
+
+  demand_empty_rest_of_line ();
+}
+
 void 
 s_set (ignore)
      int ignore;
@@ -1305,20 +1772,11 @@ s_set (ignore)
       /* Turn '. = mumble' into a .org mumble */
       register segT segment;
       expressionS exp;
-      register char *ptr;
 
       segment = get_known_segmented_expression (&exp);
 
       if (!need_pass_2)
-       {
-         if (segment != now_seg && segment != absolute_section)
-           as_bad ("Invalid segment \"%s\". Segment \"%s\" assumed.",
-                   segment_name (segment),
-                   segment_name (now_seg));
-         ptr = frag_var (rs_org, 1, 1, (relax_substateT) 0, exp.X_add_symbol,
-                         exp.X_add_number, (char *) 0);
-         *ptr = 0;
-       }                       /* if (ok to make frag) */
+       do_org (segment, &exp, 0);
 
       *end_name = delim;
       return;
@@ -1346,38 +1804,164 @@ void
 s_space (mult)
      int mult;
 {
-  long temp_repeat;
-  register long temp_fill;
-  register char *p;
+  expressionS exp;
+  long temp_fill;
+  char *p = 0;
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
 
   /* Just like .fill, but temp_size = 1 */
-  if (get_absolute_expression_and_terminator (&temp_repeat) == ',')
+  expression (&exp);
+  if (exp.X_op == O_constant)
+    {
+      long repeat;
+
+      repeat = exp.X_add_number;
+      if (mult)
+       repeat *= mult;
+      if (repeat <= 0)
+       {
+         as_warn (".space repeat count is %s, ignored",
+                  repeat ? "negative" : "zero");
+         ignore_rest_of_line ();
+         return;
+       }
+
+      /* If we are in the absolute section, just bump the offset.  */
+      if (now_seg == absolute_section)
+       {
+         abs_section_offset += repeat;
+         demand_empty_rest_of_line ();
+         return;
+       }
+
+      /* If we are secretly in an MRI common section, then creating
+         space just increases the size of the common symbol.  */
+      if (mri_common_symbol != NULL)
+       {
+         S_SET_VALUE (mri_common_symbol,
+                      S_GET_VALUE (mri_common_symbol) + repeat);
+         demand_empty_rest_of_line ();
+         return;
+       }
+
+      if (!need_pass_2)
+       p = frag_var (rs_fill, 1, 1, (relax_substateT) 0, (symbolS *) 0,
+                     repeat, (char *) 0);
+    }
+  else
+    {
+      if (now_seg == absolute_section)
+       {
+         as_bad ("space allocation too complex in absolute section");
+         subseg_set (text_section, 0);
+       }
+      if (mri_common_symbol != NULL)
+       {
+         as_bad ("space allocation too complex in common section");
+         mri_common_symbol = NULL;
+       }
+      if (!need_pass_2)
+       p = frag_var (rs_space, 1, 1, (relax_substateT) 0,
+                     make_expr_symbol (&exp), 0L, (char *) 0);
+    }
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer == ',')
     {
+      input_line_pointer++;
       temp_fill = get_absolute_expression ();
     }
   else
     {
-      input_line_pointer--;    /* Backup over what was not a ','. */
       temp_fill = 0;
     }
-  if (mult)
+  if (p)
     {
-      temp_repeat *= mult;
+      *p = temp_fill;
     }
-  if (temp_repeat <= 0)
+  demand_empty_rest_of_line ();
+}
+
+/* This is like s_space, but the value is a floating point number with
+   the given precision.  This is for the MRI dcb.s pseudo-op and
+   friends.  */
+
+void
+s_float_space (float_type)
+     int float_type;
+{
+  offsetT count;
+  int flen;
+  char temp[MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT];
+
+  count = get_absolute_expression ();
+
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer != ',')
     {
-      as_warn ("Repeat < 0, .space ignored");
+      as_bad ("missing value");
       ignore_rest_of_line ();
       return;
     }
-  if (!need_pass_2)
+
+  ++input_line_pointer;
+
+  SKIP_WHITESPACE ();
+
+  /* Skip any 0{letter} that may be present.  Don't even check if the
+   * letter is legal.  */
+  if (input_line_pointer[0] == '0' && isalpha (input_line_pointer[1]))
+    input_line_pointer += 2;
+
+  /* Accept :xxxx, where the x's are hex digits, for a floating point
+     with the exact digits specified.  */
+  if (input_line_pointer[0] == ':')
     {
-      p = frag_var (rs_fill, 1, 1, (relax_substateT) 0, (symbolS *) 0,
-                   temp_repeat, (char *) 0);
-      *p = temp_fill;
+      flen = hex_float (float_type, temp);
+      if (flen < 0)
+       {
+         ignore_rest_of_line ();
+         return;
+       }
+    }
+  else
+    {
+      char *err;
+
+      err = md_atof (float_type, temp, &flen);
+      know (flen <= MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT);
+      know (flen > 0);
+      if (err)
+       {
+         as_bad ("Bad floating literal: %s", err);
+         ignore_rest_of_line ();
+         return;
+       }
+    }
+
+  while (--count >= 0)
+    {
+      char *p;
+
+      p = frag_more (flen);
+      memcpy (p, temp, (unsigned int) flen);
     }
+
+  demand_empty_rest_of_line ();
+}
+
+/* Handle the .struct pseudo-op, as found in MIPS assemblers.  */
+
+void
+s_struct (ignore)
+     int ignore;
+{
+  abs_section_offset = get_absolute_expression ();
+  subseg_set (absolute_section, 0);
   demand_empty_rest_of_line ();
-}                              /* s_space() */
+}
 
 void
 s_text (ignore)
@@ -1388,6 +1972,9 @@ s_text (ignore)
   temp = get_absolute_expression ();
   subseg_set (text_section, (subsegT) temp);
   demand_empty_rest_of_line ();
+#ifdef OBJ_VMS
+  const_flag &= ~IN_DEFAULT_SECTION;
+#endif
 }                              /* s_text() */
 \f
 
@@ -1442,13 +2029,12 @@ pseudo_set (symbolP)
      symbolS *symbolP;
 {
   expressionS exp;
-#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
+#if (defined (OBJ_AOUT) || defined (OBJ_BOUT)) && ! defined (BFD_ASSEMBLER)
   int ext;
 #endif /* OBJ_AOUT or OBJ_BOUT */
 
   know (symbolP);              /* NULL pointer is logic error. */
-#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
-  /* @@ Fix this right for BFD.  */
+#if (defined (OBJ_AOUT) || defined (OBJ_BOUT)) && ! defined (BFD_ASSEMBLER)
   ext = S_IS_EXTERNAL (symbolP);
 #endif /* OBJ_AOUT or OBJ_BOUT */
 
@@ -1481,8 +2067,7 @@ pseudo_set (symbolP)
       /* Fall through.  */
     case O_constant:
       S_SET_SEGMENT (symbolP, absolute_section);
-#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
-      /* @@ Fix this right for BFD.  */
+#if (defined (OBJ_AOUT) || defined (OBJ_BOUT)) && ! defined (BFD_ASSEMBLER)
       if (ext)
        S_SET_EXTERNAL (symbolP);
       else
@@ -1499,21 +2084,24 @@ pseudo_set (symbolP)
       break;
 
     case O_symbol:
-      if (S_GET_SEGMENT (exp.X_add_symbol) == undefined_section)
+      if (S_GET_SEGMENT (exp.X_add_symbol) == undefined_section
+         || exp.X_add_number != 0)
        symbolP->sy_value = exp;
       else
        {
-         S_SET_SEGMENT (symbolP, S_GET_SEGMENT (exp.X_add_symbol));
-#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
-         /* @@ Fix this right for BFD!  */
+         symbolS *s = exp.X_add_symbol;
+
+         S_SET_SEGMENT (symbolP, S_GET_SEGMENT (s));
+#if (defined (OBJ_AOUT) || defined (OBJ_BOUT)) && ! defined (BFD_ASSEMBLER)
          if (ext)
            S_SET_EXTERNAL (symbolP);
          else
            S_CLEAR_EXTERNAL (symbolP);
 #endif /* OBJ_AOUT or OBJ_BOUT */
          S_SET_VALUE (symbolP,
-                      exp.X_add_number + S_GET_VALUE (exp.X_add_symbol));
-         symbolP->sy_frag = exp.X_add_symbol->sy_frag;
+                      exp.X_add_number + S_GET_VALUE (s));
+         symbolP->sy_frag = s->sy_frag;
+         copy_symbol_attributes (symbolP, s);
        }
       break;
 
@@ -1550,17 +2138,15 @@ pseudo_set (symbolP)
    are defined, which is the normal case, then only simple expressions
    are permitted.  */
 
+static void
+parse_mri_cons PARAMS ((expressionS *exp, unsigned int nbytes));
+
 #ifndef TC_PARSE_CONS_EXPRESSION
 #ifdef BITFIELD_CONS_EXPRESSIONS
 #define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) parse_bitfield_cons (EXP, NBYTES)
 static void 
 parse_bitfield_cons PARAMS ((expressionS *exp, unsigned int nbytes));
 #endif
-#ifdef MRI
-#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) parse_mri_cons (EXP)
-static void
-parse_mri_cons PARAMS ((expressionS *exp));
-#endif
 #ifdef REPEAT_CONS_EXPRESSIONS
 #define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) parse_repeat_cons (EXP, NBYTES)
 static void
@@ -1580,24 +2166,40 @@ void
 cons (nbytes)
      register int nbytes;      /* 1=.byte, 2=.word, 4=.long */
 {
+  int c;
   expressionS exp;
 
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
   if (is_it_end_of_statement ())
     {
       demand_empty_rest_of_line ();
       return;
     }
 
+  c = 0;
   do
     {
-      TC_PARSE_CONS_EXPRESSION (&exp, (unsigned int) nbytes);
+      if (flag_mri)
+       parse_mri_cons (&exp, (unsigned int) nbytes);
+      else
+       TC_PARSE_CONS_EXPRESSION (&exp, (unsigned int) nbytes);
       emit_expr (&exp, (unsigned int) nbytes);
+      ++c;
     }
   while (*input_line_pointer++ == ',');
 
+  /* In MRI mode, after an odd number of bytes, we must align to an
+     even word boundary, unless the next instruction is a dc.b, ds.b
+     or dcb.b.  */
+  if (flag_mri && nbytes == 1 && (c & 1) != 0)
+    mri_pending_align = 1;
+
   input_line_pointer--;                /* Put terminator back into stream. */
   demand_empty_rest_of_line ();
-}                              /* cons() */
+}
 
 /* Put the contents of expression EXP into the object file using
    NBYTES bytes.  If need_pass_2 is 1, this does nothing.  */
@@ -1617,6 +2219,15 @@ emit_expr (exp, nbytes)
 
   op = exp->X_op;
 
+  /* Allow `.word 0' in the absolute section.  */
+  if (now_seg == absolute_section)
+    {
+      if (op != O_constant || exp->X_add_number != 0)
+       as_bad ("attempt to store value in absolute section");
+      abs_section_offset += nbytes;
+      return;
+    }
+
   /* Handle a negative bignum.  */
   if (op == O_uminus
       && exp->X_add_number == 0
@@ -1714,17 +2325,17 @@ emit_expr (exp, nbytes)
 
   if (op == O_constant)
     {
-      register long get;
-      register long use;
-      register long mask;
-      register long unmask;
+      register valueT get;
+      register valueT use;
+      register valueT mask;
+      register valueT unmask;
 
       /* JF << of >= number of bits in the object is undefined.  In
         particular SPARC (Sun 4) has problems */
-      if (nbytes >= sizeof (long))
+      if (nbytes >= sizeof (valueT))
        mask = 0;
       else
-       mask = ~0 << (BITS_PER_CHAR * nbytes);  /* Don't store these bits. */
+       mask = ~(valueT) 0 << (BITS_PER_CHAR * nbytes); /* Don't store these bits. */
 
       unmask = ~mask;          /* Do store these bits. */
 
@@ -1740,7 +2351,7 @@ emit_expr (exp, nbytes)
          as_warn ("Value 0x%lx truncated to 0x%lx.", get, use);
        }
       /* put bytes in right order. */
-      md_number_to_chars (p, (valueT) use, (int) nbytes);
+      md_number_to_chars (p, use, (int) nbytes);
     }
   else if (op == O_big)
     {
@@ -1796,7 +2407,7 @@ emit_expr (exp, nbytes)
     }
   else
     {
-      md_number_to_chars (p, (valueT) 0, (int) nbytes);
+      memset (p, 0, nbytes);
 
       /* Now we need to generate a fixS to record the symbol value.
         This is easy for BFD.  For other targets it can be more
@@ -1886,7 +2497,7 @@ parse_bitfield_cons (exp, nbytes)
             widths, positions, and masks which most
             of our current object formats don't
             support.
-            
+
             In the specific case where a symbol
             *is* defined in this assembly, we
             *could* build fixups and track it, but
@@ -1963,20 +2574,34 @@ parse_bitfield_cons (exp, nbytes)
 
 #endif /* BITFIELD_CONS_EXPRESSIONS */
 \f
-#ifdef MRI
+/* Handle an MRI style string expression.  */
 
 static void
 parse_mri_cons (exp, nbytes)
      expressionS *exp;
      unsigned int nbytes;
 {
-  if (*input_line_pointer == '\'')
+  if (*input_line_pointer != '\''
+      && (input_line_pointer[1] != '\''
+         || (*input_line_pointer != 'A'
+             && *input_line_pointer != 'E')))
+    TC_PARSE_CONS_EXPRESSION (exp, nbytes);
+  else
     {
-      /* An MRI style string, cut into as many bytes as will fit into
-        a nbyte chunk, left justify if necessary, and separate with
-        commas so we can try again later */
       int scan = 0;
       unsigned int result = 0;
+
+      /* An MRI style string.  Cut into as many bytes as will fit into
+        a nbyte chunk, left justify if necessary, and separate with
+        commas so we can try again later.  */
+      if (*input_line_pointer == 'A')
+       ++input_line_pointer;
+      else if (*input_line_pointer == 'E')
+       {
+         as_bad ("EBCDIC constants are not supported");
+         ++input_line_pointer;
+       }
+
       input_line_pointer++;
       for (scan = 0; scan < nbytes; scan++)
        {
@@ -2012,11 +2637,7 @@ parse_mri_cons (exp, nbytes)
       else
        input_line_pointer++;
     }
-  else
-    expression (&exp);
 }
-
-#endif /* MRI */
 \f
 #ifdef REPEAT_CONS_EXPRESSIONS
 
@@ -2059,6 +2680,97 @@ parse_repeat_cons (exp, nbytes)
 
 #endif /* REPEAT_CONS_EXPRESSIONS */
 \f
+/* Parse a floating point number represented as a hex constant.  This
+   permits users to specify the exact bits they want in the floating
+   point number.  */
+
+static int
+hex_float (float_type, bytes)
+     int float_type;
+     char *bytes;
+{
+  int length;
+  int i;
+
+  switch (float_type)
+    {
+    case 'f':
+    case 'F':
+    case 's':
+    case 'S':
+      length = 4;
+      break;
+
+    case 'd':
+    case 'D':
+    case 'r':
+    case 'R':
+      length = 8;
+      break;
+
+    case 'x':
+    case 'X':
+      length = 12;
+      break;
+
+    case 'p':
+    case 'P':
+      length = 12;
+      break;
+
+    default:
+      as_bad ("Unknown floating type type '%c'", float_type);
+      return -1;
+    }
+
+  /* It would be nice if we could go through expression to parse the
+     hex constant, but if we get a bignum it's a pain to sort it into
+     the buffer correctly.  */
+  i = 0;
+  while (hex_p (*input_line_pointer) || *input_line_pointer == '_')
+    {
+      int d;
+
+      /* The MRI assembler accepts arbitrary underscores strewn about
+        through the hex constant, so we ignore them as well. */
+      if (*input_line_pointer == '_')
+       {
+         ++input_line_pointer;
+         continue;
+       }
+
+      if (i >= length)
+       {
+         as_warn ("Floating point constant too large");
+         return -1;
+       }
+      d = hex_value (*input_line_pointer) << 4;
+      ++input_line_pointer;
+      while (*input_line_pointer == '_')
+       ++input_line_pointer;
+      if (hex_p (*input_line_pointer))
+       {
+         d += hex_value (*input_line_pointer);
+         ++input_line_pointer;
+       }
+      if (target_big_endian)
+       bytes[i] = d;
+      else
+       bytes[length - i - 1] = d;
+      ++i;
+    }
+
+  if (i < length)
+    {
+      if (target_big_endian)
+       memset (bytes + i, 0, length - i);
+      else
+       memset (bytes, 0, length - i);
+    }
+
+  return length;
+}
+
 /*
  *                     float_cons()
  *
@@ -2108,14 +2820,29 @@ float_cons (float_type)
       if (input_line_pointer[0] == '0' && isalpha (input_line_pointer[1]))
        input_line_pointer += 2;
 
-      err = md_atof (float_type, temp, &length);
-      know (length <= MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT);
-      know (length > 0);
-      if (err)
+      /* Accept :xxxx, where the x's are hex digits, for a floating
+         point with the exact digits specified.  */
+      if (input_line_pointer[0] == ':')
        {
-         as_bad ("Bad floating literal: %s", err);
-         ignore_rest_of_line ();
-         return;
+         ++input_line_pointer;
+         length = hex_float (float_type, temp);
+         if (length < 0)
+           {
+             ignore_rest_of_line ();
+             return;
+           }
+       }
+      else
+       {
+         err = md_atof (float_type, temp, &length);
+         know (length <= MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT);
+         know (length > 0);
+         if (err)
+           {
+             as_bad ("Bad floating literal: %s", err);
+             ignore_rest_of_line ();
+             return;
+           }
        }
 
       if (!need_pass_2)
@@ -2171,6 +2898,10 @@ stringer (append_zero)           /* Worker to do .ascii etc statements. */
 {
   register unsigned int c;
 
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
   /*
    * The following awkward logic is to parse ZERO or more strings,
    * comma seperated. Recall a string expression includes spaces
@@ -2243,6 +2974,7 @@ next_char_of_string ()
       c = NOT_A_CHAR;
       break;
 
+#ifndef NO_STRING_ESCAPES
     case '\\':
       switch (c = *input_line_pointer++)
        {
@@ -2266,11 +2998,9 @@ next_char_of_string ()
          c = '\t';
          break;
 
-#ifdef BACKSLASH_V
        case 'v':
          c = '\013';
          break;
-#endif
 
        case '\\':
        case '"':
@@ -2337,6 +3067,7 @@ next_char_of_string ()
          break;
        }                       /* switch on escaped char */
       break;
+#endif /* ! defined (NO_STRING_ESCAPES) */
 
     default:
       break;
@@ -2396,7 +3127,7 @@ get_absolute_expression ()
   if (exp.X_op != O_constant)
     {
       if (exp.X_op != O_absent)
-       as_bad ("bad absolute expression; zero assumed");
+       as_bad ("bad or irreducible absolute expression; zero assumed");
       exp.X_add_number = 0;
     }
   return exp.X_add_number;
@@ -2449,7 +3180,7 @@ demand_copy_C_string (len_pointer)
  * Demand string, but return a safe (=private) copy of the string.
  * Return NULL if we can't read a string here.
  */
-static char *
+char *
 demand_copy_string (lenP)
      int *lenP;
 {
@@ -2517,19 +3248,10 @@ equals (sym_name)
       /* Turn '. = mumble' into a .org mumble */
       register segT segment;
       expressionS exp;
-      register char *p;
 
       segment = get_known_segmented_expression (&exp);
       if (!need_pass_2)
-       {
-         if (segment != now_seg && segment != absolute_section)
-           as_warn ("Illegal segment \"%s\". Segment \"%s\" assumed.",
-                    segment_name (segment),
-                    segment_name (now_seg));
-         p = frag_var (rs_org, 1, 1, (relax_substateT) 0, exp.X_add_symbol,
-                       exp.X_add_number, (char *) 0);
-         *p = 0;
-       }                       /* if (ok to make frag) */
+       do_org (segment, &exp, 0);
     }
   else
     {
@@ -2608,355 +3330,7 @@ s_ignore (arg)
       ++input_line_pointer;
     }
   ++input_line_pointer;
-
-  return;
-}                              /* s_ignore() */
-\f
-/*
- * Handle .stabX directives, which used to be open-coded.
- * So much creeping featurism overloaded the semantics that we decided
- * to put all .stabX thinking in one place. Here.
- *
- * We try to make any .stabX directive legal. Other people's AS will often
- * do assembly-time consistency checks: eg assigning meaning to n_type bits
- * and "protecting" you from setting them to certain values. (They also zero
- * certain bits before emitting symbols. Tut tut.)
- *
- * If an expression is not absolute we either gripe or use the relocation
- * information. Other people's assemblers silently forget information they
- * don't need and invent information they need that you didn't supply.
- */
-
-/*
- * Build a string dictionary entry for a .stabX symbol.
- * The symbol is added to the .<secname>str section.
- */
-
-#ifdef SEPARATE_STAB_SECTIONS
-
-unsigned int
-get_stab_string_offset (string, stabstr_secname)
-     const char *string;
-     const char *stabstr_secname;
-{
-  unsigned int length;
-  unsigned int retval;
-
-  retval = 0;
-  length = strlen (string);
-  if (length > 0)
-    {                          /* Ordinary case. */
-      segT save_seg;
-      subsegT save_subseg;
-      char *newsecname;
-      segT seg;
-      char *p;
-
-      save_seg = now_seg;
-      save_subseg = now_subseg;
-
-      /* Create the stab string section.  */
-      newsecname = xmalloc ((unsigned long) (strlen (stabstr_secname) + 1));
-      strcpy (newsecname, stabstr_secname);
-
-      seg = subseg_new (newsecname, 0);
-
-      retval = seg_info (seg)->stabu.stab_string_size;
-      if (retval > 0)
-       free (newsecname);
-      else
-       {
-         /* Make sure the first string is empty.  */
-         p = frag_more (1);
-         *p = 0;
-         retval = seg_info (seg)->stabu.stab_string_size = 1;
-#ifdef BFD_ASSEMBLER
-         bfd_set_section_flags (stdoutput, seg, SEC_READONLY | SEC_DEBUGGING);
-#else
-         free (newsecname);
-#endif
-       }
-
-      p = frag_more (length + 1);
-      strcpy (p, string);
-
-      seg_info (seg)->stabu.stab_string_size += length + 1;
-
-      subseg_set (save_seg, save_subseg);
-    }
-
-  return retval;
-}
-
-#endif /* SEPARATE_STAB_SECTIONS */
-
-/* This can handle different kinds of stabs (s,n,d) and different
-   kinds of stab sections. */
-
-static void 
-s_stab_generic (what, stab_secname, stabstr_secname)
-     int what;
-     char *stab_secname;
-     char *stabstr_secname;
-{
-  long longint;
-  char *string;
-  int type;
-  int other;
-  int desc;
-
-  /* The general format is:
-     .stabs "STRING",TYPE,OTHER,DESC,VALUE
-     .stabn TYPE,OTHER,DESC,VALUE
-     .stabd TYPE,OTHER,DESC
-     At this point input_line_pointer points after the pseudo-op and
-     any trailing whitespace.  The argument what is one of 's', 'n' or
-     'd' indicating which type of .stab this is.  */
-
-  if (what != 's')
-    string = "";
-  else
-    {
-      int length;
-
-      string = demand_copy_C_string (&length);
-      SKIP_WHITESPACE ();
-      if (*input_line_pointer == ',')
-       input_line_pointer++;
-      else
-       {
-         as_warn (".stabs: Missing comma");
-         ignore_rest_of_line ();
-         return;
-       }
-    }
-
-  if (get_absolute_expression_and_terminator (&longint) != ',')
-    {
-      as_warn (".stab%c: Missing comma", what);
-      ignore_rest_of_line ();
-      return;
-    }
-  type = longint;
-
-  if (get_absolute_expression_and_terminator (&longint) != ',')
-    {
-      as_warn (".stab%c: Missing comma", what);
-      ignore_rest_of_line ();
-      return;
-    }
-  other = longint;
-
-  desc = get_absolute_expression ();
-  if (what == 's' || what == 'n')
-    {
-      if (*input_line_pointer != ',')
-       {
-         as_warn (".stab%c: Missing comma", what);
-         ignore_rest_of_line ();
-         return;
-       }
-      input_line_pointer++;
-      SKIP_WHITESPACE ();
-    }
-
-  /* We have now gathered the type, other, and desc information.  For
-     .stabs or .stabn, input_line_pointer is now pointing at the
-     value.  */
-
-#ifdef SEPARATE_STAB_SECTIONS
-  /* Output the stab information in a separate section.  This is used
-     at least for COFF and ELF.  */
-  {
-    segT saved_seg = now_seg;
-    subsegT saved_subseg = now_subseg;
-    fragS *saved_frag = frag_now;
-    valueT dot;
-    segT seg;
-    unsigned int stroff;
-    char *p;
-    
-    dot = frag_now_fix ();
-
-    seg = subseg_new (stab_secname, 0);
-
-    if (! seg_info (seg)->hadone)
-      {
-#ifdef BFD_ASSEMBLER
-       bfd_set_section_flags (stdoutput, seg,
-                              SEC_READONLY | SEC_RELOC | SEC_DEBUGGING);
-#endif
-#ifdef INIT_STAB_SECTION
-       INIT_STAB_SECTION (seg);
-#endif
-       seg_info (seg)->hadone = 1;
-      }
-
-    stroff = get_stab_string_offset (string, stabstr_secname);
-
-    /* At least for now, stabs in a special stab section are always
-       output as 12 byte blocks of information.  */
-    p = frag_more (8);
-    md_number_to_chars (p, (valueT) stroff, 4);
-    md_number_to_chars (p + 4, (valueT) type, 1);
-    md_number_to_chars (p + 5, (valueT) other, 1);
-    md_number_to_chars (p + 6, (valueT) desc, 2);
-
-    if (what == 's' || what == 'n')
-      {
-       /* Pick up the value from the input line.  */
-       cons (4);
-       input_line_pointer--;
-      }
-    else
-      {
-       const char *fake;
-       symbolS *symbol;
-       expressionS exp;
-
-       /* Arrange for a value representing the current location.  */
-       fake = FAKE_LABEL_NAME;
-       symbol = symbol_new (fake, saved_seg, dot, saved_frag);
-
-       exp.X_op = O_symbol;
-       exp.X_add_symbol = symbol;
-       exp.X_add_number = 0;
-
-       emit_expr (&exp, 4);
-      }
-
-#ifdef OBJ_PROCESS_STAB
-    OBJ_PROCESS_STAB (seg, string, stroff, type, other, desc);
-#endif
-
-    subseg_set (saved_seg, saved_subseg);
-  }
-#else /* ! SEPARATE_STAB_SECTIONS */
-#ifdef OBJ_PROCESS_STAB
-  OBJ_PROCESS_STAB (what, string, type, other, desc);
-#else
-  /* Put the stab information in the symbol table.  */
-  {
-    symbolS *symbol;
-
-    symbol = symbol_new (string, undefined_section, 0,
-                        (struct frag *) NULL);
-    if (what == 's' || what == 'n')
-      {
-       /* Pick up the value from the input line.  */
-       symbol->sy_frag = &zero_address_frag;
-       pseudo_set (symbol);
-      }
-    else
-      {
-       /* .stabd sets the name to NULL.  Why?  */
-       S_SET_NAME (symbol, NULL);
-       symbol->sy_frag = frag_now;
-       S_SET_VALUE (symbol, (valueT) frag_now_fix ());
-      }
-
-    S_SET_TYPE (symbol, type);
-    S_SET_OTHER (symbol, other);
-    S_SET_DESC (symbol, desc);
-  }
-#endif /* ! OBJ_PROCESS_STAB */
-#endif /* ! SEPARATE_STAB_SECTIONS */
-
-#ifndef NO_LISTING
-  if (listing)
-    {
-      switch (type)
-       {
-       case N_SLINE:
-         listing_source_line ((unsigned int) desc);
-         break;
-       case N_SO:
-       case N_SOL:
-         listing_source_file (string);
-         break;
-       }
-    }
-#endif /* ! NO_LISTING */
-
-  demand_empty_rest_of_line ();
-}
-
-/* Regular stab directive. */
-
-void
-s_stab (what)
-     int what;
-{
-  s_stab_generic (what, STAB_SECTION_NAME, STAB_STRING_SECTION_NAME);
-}
-
-/* "Extended stabs", used in Solaris only now. */
-
-void
-s_xstab (what)
-     int what;
-{
-  int length;
-  char *stab_secname, *stabstr_secname;
-
-  stab_secname = demand_copy_C_string (&length);
-  SKIP_WHITESPACE ();
-  if (*input_line_pointer == ',')
-    input_line_pointer++;
-  else
-    {
-      as_bad ("comma missing in .xstabs");
-      ignore_rest_of_line ();
-      return;
-    }
-
-  /* To get the name of the stab string section, simply .str to
-     the stab section name.  */
-  stabstr_secname = (char *) xmalloc (strlen (stab_secname) + 4);
-  strcpy (stabstr_secname, stab_secname);
-  strcat (stabstr_secname, "str");
-  s_stab_generic (what, stab_secname, stabstr_secname);
-  free (stabstr_secname);
 }
 
-#ifdef S_SET_DESC
-
-/* Frob invented at RMS' request. Set the n_desc of a symbol.  */
-
-void 
-s_desc (ignore)
-     int ignore;
-{
-  char *name;
-  char c;
-  char *p;
-  symbolS *symbolP;
-  int temp;
-
-  name = input_line_pointer;
-  c = get_symbol_end ();
-  p = input_line_pointer;
-  *p = c;
-  SKIP_WHITESPACE ();
-  if (*input_line_pointer != ',')
-    {
-      *p = 0;
-      as_bad ("Expected comma after name \"%s\"", name);
-      *p = c;
-      ignore_rest_of_line ();
-    }
-  else
-    {
-      input_line_pointer++;
-      temp = get_absolute_expression ();
-      *p = 0;
-      symbolP = symbol_find_or_make (name);
-      *p = c;
-      S_SET_DESC (symbolP, temp);
-    }
-  demand_empty_rest_of_line ();
-}                              /* s_desc() */
-
-#endif /* defined (S_SET_DESC) */
 
 /* end of read.c */