import insight-2000-02-04 snapshot (2nd try)
[external/binutils.git] / gas / config / tc-sh.c
index 0f5fb0f..9f7a6c1 100644 (file)
@@ -1,6 +1,5 @@
 /* tc-sh.c -- Assemble code for the Hitachi Super-H
-
-   Copyright (C) 1993 Free Software Foundation.
+   Copyright (C) 1993, 94, 95, 96, 97, 98, 1999 Free Software Foundation.
 
    This file is part of GAS, the GNU Assembler.
 
@@ -16,7 +15,8 @@
 
    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.  */
 
 /*
    Written By Steve Chamberlain
 #include "bfd.h"
 #include "subsegs.h"
 #define DEFINE_TABLE
-#include "../opcodes/sh-opc.h"
+#include "opcodes/sh-opc.h"
 #include <ctype.h>
-
 const char comment_chars[] = "!";
 const char line_separator_chars[] = ";";
-const char line_comment_chars[] = "!";
+const char line_comment_chars[] = "!#";
+
+static void s_uses PARAMS ((int));
+
+static void sh_count_relocs PARAMS ((bfd *, segT, PTR));
+static void sh_frob_section PARAMS ((bfd *, segT, PTR));
 
 /* This table describes all the machine specific pseudo-ops the assembler
    has to support.  The fields are:
@@ -44,22 +48,41 @@ const char line_comment_chars[] = "!";
 
 void cons ();
 void s_align_bytes ();
+static void s_uacons PARAMS ((int));
+
+int shl = 0;
+
+static void
+little (ignore)
+     int ignore;
+{
+  shl = 1;
+  target_big_endian = 0;
+}
 
 const pseudo_typeS md_pseudo_table[] =
 {
   {"int", cons, 4},
   {"word", cons, 2},
   {"form", listing_psize, 0},
+  {"little", little, 0},
   {"heading", listing_title, 0},
   {"import", s_ignore, 0},
   {"page", listing_eject, 0},
   {"program", s_ignore, 0},
+  {"uses", s_uses, 0},
+  {"uaword", s_uacons, 2},
+  {"ualong", s_uacons, 4},
   {0, 0, 0}
 };
 
 /*int md_reloc_size; */
 
-static int relax;              /* set if -relax seen */
+int sh_relax;          /* set if -relax seen */
+
+/* Whether -small was seen.  */
+
+int sh_small;
 
 const char EXP_CHARS[] = "eE";
 
@@ -74,9 +97,11 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 #define ENCODE_RELAX(what,length) (((what) << 4) + (length))
 #define GET_WHAT(x) ((x>>4))
 
-/* These are the two types of relaxable instrction */
+/* These are the three types of relaxable instrction */
 #define COND_JUMP 1
-#define UNCOND_JUMP  2
+#define COND_JUMP_DELAY 2
+#define UNCOND_JUMP  3
+#define END 4
 
 #define UNDEF_DISP 0
 #define COND8  1
@@ -85,35 +110,72 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 #define UNCOND12 1
 #define UNCOND32 2
 #define UNDEF_WORD_DISP 4
-#define END 5
 
 #define UNCOND12 1
 #define UNCOND32 2
 
-#define COND8_F 254
-#define COND8_M -256
+/* Branch displacements are from the address of the branch plus
+   four, thus all minimum and maximum values have 4 added to them.  */
+#define COND8_F 258
+#define COND8_M -252
 #define COND8_LENGTH 2
-#define COND12_F (4094 - 4)    /* -4 since there are two extra */
-/* instructions needed */
-#define COND12_M -4096
+
+/* There is one extra instruction before the branch, so we must add
+   two more bytes to account for it.  */
+#define COND12_F 4100
+#define COND12_M -4090
 #define COND12_LENGTH 6
+
+#define COND12_DELAY_LENGTH 4
+
+/* ??? The minimum and maximum values are wrong, but this does not matter
+   since this relocation type is not supported yet.  */
 #define COND32_F (1<<30)
 #define COND32_M -(1<<30)
 #define COND32_LENGTH 14
 
-#define COND8_RANGE(x) ((x) > COND8_M && (x) < COND8_F)
-#define COND12_RANGE(x) ((x) > COND12_M && (x) < COND12_F)
-
-#define UNCOND12_F 4094
-#define UNCOND12_M -4096
+#define UNCOND12_F 4098
+#define UNCOND12_M -4092
 #define UNCOND12_LENGTH 2
 
+/* ??? The minimum and maximum values are wrong, but this does not matter
+   since this relocation type is not supported yet.  */
 #define UNCOND32_F (1<<30)
 #define UNCOND32_M -(1<<30)
 #define UNCOND32_LENGTH 14
 
-
-const relax_typeS md_relax_table[C (END, 0)];
+const relax_typeS md_relax_table[C (END, 0)] = {
+  { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
+  { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
+
+  { 0 },
+  /* C (COND_JUMP, COND8) */
+  { COND8_F, COND8_M, COND8_LENGTH, C (COND_JUMP, COND12) },
+  /* C (COND_JUMP, COND12) */
+  { COND12_F, COND12_M, COND12_LENGTH, C (COND_JUMP, COND32), },
+  /* C (COND_JUMP, COND32) */
+  { COND32_F, COND32_M, COND32_LENGTH, 0, },
+  { 0 }, { 0 }, { 0 }, { 0 },
+  { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
+
+  { 0 },
+  /* C (COND_JUMP_DELAY, COND8) */
+  { COND8_F, COND8_M, COND8_LENGTH, C (COND_JUMP_DELAY, COND12) },
+  /* C (COND_JUMP_DELAY, COND12) */
+  { COND12_F, COND12_M, COND12_DELAY_LENGTH, C (COND_JUMP_DELAY, COND32), },
+  /* C (COND_JUMP_DELAY, COND32) */
+  { COND32_F, COND32_M, COND32_LENGTH, 0, },
+  { 0 }, { 0 }, { 0 }, { 0 },
+  { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
+
+  { 0 },
+  /* C (UNCOND_JUMP, UNCOND12) */
+  { UNCOND12_F, UNCOND12_M, UNCOND12_LENGTH, C (UNCOND_JUMP, UNCOND32), },
+  /* C (UNCOND_JUMP, UNCOND32) */
+  { UNCOND32_F, UNCOND32_M, UNCOND32_LENGTH, 0, },
+  { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
+  { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
+};
 
 static struct hash_control *opcode_hash_control;       /* Opcode mnemonics */
 
@@ -128,6 +190,9 @@ md_begin ()
   sh_opcode_info *opcode;
   char *prev_name = "";
 
+  if (! shl)
+    target_big_endian = 1;
+
   opcode_hash_control = hash_new ();
 
   /* Insert unique names into hash table */
@@ -145,39 +210,12 @@ md_begin ()
          opcode->name = prev_name;
        }
     }
-
-  /* Initialize the relax table */
-  md_relax_table[C (COND_JUMP, COND8)].rlx_forward = COND8_F;
-  md_relax_table[C (COND_JUMP, COND8)].rlx_backward = COND8_M;
-  md_relax_table[C (COND_JUMP, COND8)].rlx_length = COND8_LENGTH;
-  md_relax_table[C (COND_JUMP, COND8)].rlx_more = C (COND_JUMP, COND12);
-
-  md_relax_table[C (COND_JUMP, COND12)].rlx_forward = COND12_F;
-  md_relax_table[C (COND_JUMP, COND12)].rlx_backward = COND12_M;
-  md_relax_table[C (COND_JUMP, COND12)].rlx_length = COND12_LENGTH;
-  md_relax_table[C (COND_JUMP, COND12)].rlx_more = C (COND_JUMP, COND32);
-
-  md_relax_table[C (COND_JUMP, COND32)].rlx_forward = COND32_F;
-  md_relax_table[C (COND_JUMP, COND32)].rlx_backward = COND32_M;
-  md_relax_table[C (COND_JUMP, COND32)].rlx_length = COND32_LENGTH;
-  md_relax_table[C (COND_JUMP, COND32)].rlx_more = 0;
-
-
-  md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_forward = UNCOND12_F;
-  md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_backward = UNCOND12_M;
-  md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length = UNCOND12_LENGTH;
-  md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_more = C (UNCOND_JUMP, UNCOND32);
-
-  md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_forward = UNCOND32_F;
-  md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_backward = UNCOND32_M;
-  md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length = UNCOND32_LENGTH;
-  md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_more = 0;
-
-
 }
 
 static int reg_m;
 static int reg_n;
+static int reg_b;
+
 static expressionS immediate;  /* absolute expression */
 
 typedef struct
@@ -195,18 +233,35 @@ parse_reg (src, mode, reg)
      int *mode;
      int *reg;
 {
+  /* We use !isalnum for the next character after the register name, to
+     make sure that we won't accidentally recognize a symbol name such as
+     'sram' as being a reference to the register 'sr'.  */
+
+  if (src[0] == 'r')
+    {
+      if (src[1] >= '0' && src[1] <= '7' && strncmp(&src[2], "_bank", 5) == 0
+         && ! isalnum ((unsigned char) src[7]))
+       {
+         *mode = A_REG_B;
+         *reg  = (src[1] - '0');
+         return 7;
+       }
+    }
+
   if (src[0] == 'r')
     {
       if (src[1] == '1')
        {
-         if (src[2] >= '0' && src[2] <= '5')
+         if (src[2] >= '0' && src[2] <= '5'
+             && ! isalnum ((unsigned char) src[3]))
            {
              *mode = A_REG_N;
              *reg = 10 + src[2] - '0';
              return 3;
            }
        }
-      if (src[1] >= '0' && src[1] <= '9')
+      if (src[1] >= '0' && src[1] <= '9'
+         && ! isalnum ((unsigned char) src[2]))
        {
          *mode = A_REG_N;
          *reg = (src[1] - '0');
@@ -214,41 +269,73 @@ parse_reg (src, mode, reg)
        }
     }
 
-  if (src[0] == 's' && src[1] == 'r')
+  if (src[0] == 's'
+      && src[1] == 's'
+      && src[2] == 'r' && ! isalnum ((unsigned char) src[3]))
+    {
+      *mode = A_SSR;
+      return 3;
+    }
+
+  if (src[0] == 's' && src[1] == 'p' && src[2] == 'c'
+      && ! isalnum ((unsigned char) src[3]))
+    {
+      *mode = A_SPC;
+      return 3;
+    }
+
+  if (src[0] == 's' && src[1] == 'g' && src[2] == 'r'
+      && ! isalnum ((unsigned char) src[3]))
+    {
+      *mode = A_SGR;
+      return 3;
+    }
+
+  if (src[0] == 'd' && src[1] == 'b' && src[2] == 'r'
+      && ! isalnum ((unsigned char) src[3]))
+    {
+      *mode = A_DBR;
+      return 3;
+    }
+
+  if (src[0] == 's' && src[1] == 'r' && ! isalnum ((unsigned char) src[2]))
     {
       *mode = A_SR;
       return 2;
     }
 
-  if (src[0] == 's' && src[1] == 'p')
+  if (src[0] == 's' && src[1] == 'p' && ! isalnum ((unsigned char) src[2]))
     {
       *mode = A_REG_N;
       *reg = 15;
       return 2;
     }
 
-  if (src[0] == 'p' && src[1] == 'r')
+  if (src[0] == 'p' && src[1] == 'r' && ! isalnum ((unsigned char) src[2]))
     {
       *mode = A_PR;
       return 2;
     }
-  if (src[0] == 'p' && src[1] == 'c')
+  if (src[0] == 'p' && src[1] == 'c' && ! isalnum ((unsigned char) src[2]))
     {
       *mode = A_DISP_PC;
       return 2;
     }
-  if (src[0] == 'g' && src[1] == 'b' && src[2] == 'r')
+  if (src[0] == 'g' && src[1] == 'b' && src[2] == 'r'
+      && ! isalnum ((unsigned char) src[3]))
     {
       *mode = A_GBR;
       return 3;
     }
-  if (src[0] == 'v' && src[1] == 'b' && src[2] == 'r')
+  if (src[0] == 'v' && src[1] == 'b' && src[2] == 'r'
+      && ! isalnum ((unsigned char) src[3]))
     {
       *mode = A_VBR;
       return 3;
     }
 
-  if (src[0] == 'm' && src[1] == 'a' && src[2] == 'c')
+  if (src[0] == 'm' && src[1] == 'a' && src[2] == 'c'
+      && ! isalnum ((unsigned char) src[4]))
     {
       if (src[3] == 'l')
        {
@@ -261,6 +348,102 @@ parse_reg (src, mode, reg)
          return 4;
        }
     }
+  if (src[0] == 'f' && src[1] == 'r')
+    {
+      if (src[2] == '1')
+       {
+         if (src[3] >= '0' && src[3] <= '5'
+             && ! isalnum ((unsigned char) src[4]))
+           {
+             *mode = F_REG_N;
+             *reg = 10 + src[3] - '0';
+             return 4;
+           }
+       }
+      if (src[2] >= '0' && src[2] <= '9'
+         && ! isalnum ((unsigned char) src[3]))
+       {
+         *mode = F_REG_N;
+         *reg = (src[2] - '0');
+         return 3;
+       }
+    }
+  if (src[0] == 'd' && src[1] == 'r')
+    {
+      if (src[2] == '1')
+       {
+         if (src[3] >= '0' && src[3] <= '4' && ! ((src[3] - '0') & 1)
+             && ! isalnum ((unsigned char) src[4]))
+           {
+             *mode = D_REG_N;
+             *reg = 10 + src[3] - '0';
+             return 4;
+           }
+       }
+      if (src[2] >= '0' && src[2] <= '8' && ! ((src[2] - '0') & 1)
+         && ! isalnum ((unsigned char) src[3]))
+       {
+         *mode = D_REG_N;
+         *reg = (src[2] - '0');
+         return 3;
+       }
+    }
+  if (src[0] == 'x' && src[1] == 'd')
+    {
+      if (src[2] == '1')
+       {
+         if (src[3] >= '0' && src[3] <= '4' && ! ((src[3] - '0') & 1)
+             && ! isalnum ((unsigned char) src[4]))
+           {
+             *mode = X_REG_N;
+             *reg = 11 + src[3] - '0';
+             return 4;
+           }
+       }
+      if (src[2] >= '0' && src[2] <= '8' && ! ((src[2] - '0') & 1)
+         && ! isalnum ((unsigned char) src[3]))
+       {
+         *mode = X_REG_N;
+         *reg = (src[2] - '0') + 1;
+         return 3;
+       }
+    }
+  if (src[0] == 'f' && src[1] == 'v')
+    {
+      if (src[2] == '1'&& src[3] == '2' && ! isalnum ((unsigned char) src[4]))
+       {
+         *mode = V_REG_N;
+         *reg = 12;
+         return 4;
+       }
+      if ((src[2] == '0' || src[2] == '4' || src[2] == '8')
+         && ! isalnum ((unsigned char) src[3]))
+       {
+         *mode = V_REG_N;
+         *reg = (src[2] - '0');
+         return 3;
+       }
+    }
+  if (src[0] == 'f' && src[1] == 'p' && src[2] == 'u' && src[3] == 'l'
+      && ! isalnum ((unsigned char) src[4]))
+    {
+      *mode = FPUL_N;
+      return 4;
+    }
+
+  if (src[0] == 'f' && src[1] == 'p' && src[2] == 's' && src[3] == 'c'
+      && src[4] == 'r' && ! isalnum ((unsigned char) src[5]))
+    {
+      *mode = FPSCR_N;
+      return 5;
+    }
+
+  if (src[0] == 'x' && src[1] == 'm' && src[2] == 't' && src[3] == 'r'
+      && src[4] == 'x' && ! isalnum ((unsigned char) src[5]))
+    {
+      *mode = XMTRX_M4;
+      return 5;
+    }
 
   return 0;
 }
@@ -292,7 +475,7 @@ parse_exp (s)
   input_line_pointer = s;
   expression (&immediate);
   if (immediate.X_op == O_absent)
-    as_bad ("missing operand");
+    as_bad (_("missing operand"));
   new = input_line_pointer;
   input_line_pointer = save;
   return new;
@@ -335,7 +518,7 @@ parse_at (src, op)
 
       len = parse_reg (src, &mode, &(op->reg));
       if (mode != A_REG_N)
-       as_bad ("illegal register after @-");
+       as_bad (_("illegal register after @-"));
 
       op->type = A_DEC_N;
       src += len;
@@ -351,7 +534,7 @@ parse_at (src, op)
          src += len;
          if (op->reg != 0)
            {
-             as_bad ("must be @(r0,...)");
+             as_bad (_("must be @(r0,...)"));
            }
          if (src[0] == ',')
            src++;
@@ -367,7 +550,7 @@ parse_at (src, op)
            }
          else
            {
-             as_bad ("syntax error in @(r0,...)");
+             as_bad (_("syntax error in @(r0,...)"));
            }
        }
       else
@@ -399,17 +582,17 @@ parse_at (src, op)
                }
              else
                {
-                 as_bad ("syntax error in @(disp,[Rn, gbr, pc])");
+                 as_bad (_("syntax error in @(disp,[Rn, gbr, pc])"));
                }
            }
          else
            {
-             as_bad ("syntax error in @(disp,[Rn, gbr, pc])");
+             as_bad (_("syntax error in @(disp,[Rn, gbr, pc])"));
            }
        }
       src += len;
       if (src[0] != ')')
-       as_bad ("expecting )");
+       as_bad (_("expecting )"));
       else
        src++;
     }
@@ -418,7 +601,7 @@ parse_at (src, op)
       src += parse_reg (src, &mode, &(op->reg));
       if (mode != A_REG_N)
        {
-         as_bad ("illegal register after @");
+         as_bad (_("illegal register after @"));
        }
       if (src[0] == '+')
        {
@@ -492,16 +675,30 @@ get_operands (info, args, operand)
              ptr++;
            }
          get_operand (&ptr, operand + 1);
+         if (info->arg[2])
+           {
+             if (*ptr == ',')
+               {
+                 ptr++;
+               }
+             get_operand (&ptr, operand + 2);
+           }
+         else
+           {
+             operand[2].type = 0;
+           }
        }
       else
        {
          operand[1].type = 0;
+         operand[2].type = 0;
        }
     }
   else
     {
       operand[0].type = 0;
       operand[1].type = 0;
+      operand[2].type = 0;
     }
   return ptr;
 }
@@ -519,7 +716,6 @@ get_specific (opcode, operands)
 {
   sh_opcode_info *this_try = opcode;
   char *name = opcode->name;
-  int arg_to_test = 0;
   int n = 0;
   while (opcode->name)
     {
@@ -537,8 +733,8 @@ get_specific (opcode, operands)
 
       for (n = 0; this_try->arg[n]; n++)
        {
-         sh_operand_info *user = operands + arg_to_test;
-         sh_arg_type arg = this_try->arg[arg_to_test];
+         sh_operand_info *user = operands + n;
+         sh_arg_type arg = this_try->arg[n];
          switch (arg)
            {
            case A_IMM:
@@ -561,6 +757,10 @@ get_specific (opcode, operands)
              if (user->type != A_R0_GBR || user->reg != 0)
                goto fail;
              break;
+           case F_FR0:
+             if (user->type != F_REG_N || user->reg != 0)
+               goto fail;
+             break;
 
            case A_REG_N:
            case A_INC_N:
@@ -568,16 +768,42 @@ get_specific (opcode, operands)
            case A_IND_N:
            case A_IND_R0_REG_N:
            case A_DISP_REG_N:
+           case F_REG_N:
+           case D_REG_N:
+           case X_REG_N:
+           case V_REG_N:
+           case FPUL_N:
+           case FPSCR_N:
              /* Opcode needs rn */
              if (user->type != arg)
                goto fail;
              reg_n = user->reg;
              break;
+           case FD_REG_N:
+             if (user->type != F_REG_N && user->type != D_REG_N)
+               goto fail;
+             reg_n = user->reg;
+             break;
+           case DX_REG_N:
+             if (user->type != D_REG_N && user->type != X_REG_N)
+               goto fail;
+             reg_n = user->reg;
+             break;
            case A_GBR:
            case A_SR:
            case A_VBR:
+           case A_SSR:
+           case A_SPC:
+           case A_SGR:
+           case A_DBR:
+             if (user->type != arg)
+               goto fail;
+             break;
+
+            case A_REG_B:
              if (user->type != arg)
                goto fail;
+             reg_b = user->reg;
              break;
 
            case A_REG_M:
@@ -591,12 +817,33 @@ get_specific (opcode, operands)
                goto fail;
              reg_m = user->reg;
              break;
+
+           case F_REG_M:
+           case D_REG_M:
+           case X_REG_M:
+           case V_REG_M:
+           case FPUL_M:
+           case FPSCR_M:
+             /* Opcode needs rn */
+             if (user->type != arg - F_REG_M + F_REG_N)
+               goto fail;
+             reg_m = user->reg;
+             break;
+           case DX_REG_M:
+             if (user->type != D_REG_N && user->type != X_REG_N)
+               goto fail;
+             reg_m = user->reg;
+             break;
+           case XMTRX_M4:
+             if (user->type != XMTRX_M4)
+               goto fail;
+             reg_m = 4;
+             break;
+       
            default:
-             printf ("unhandled %d\n", arg);
+             printf (_("unhandled %d\n"), arg);
              goto fail;
            }
-         /* If we did 0, test 1 next, else 0 */
-         arg_to_test = 1 - arg_to_test;
        }
       return this_try;
     fail:;
@@ -615,7 +862,7 @@ check (operand, low, high)
       || operand->X_add_number < low
       || operand->X_add_number > high)
     {
-      as_bad ("operand must be absolute in range %d..%d", low, high);
+      as_bad (_("operand must be absolute in range %d..%d"), low, high);
     }
   return operand->X_add_number;
 }
@@ -629,40 +876,41 @@ insert (where, how, pcrel)
 {
   fix_new_exp (frag_now,
               where - frag_now->fr_literal,
-              4,
+              2,
               &immediate,
               pcrel,
               how);
-
 }
 
 static void
 build_relax (opcode)
      sh_opcode_info *opcode;
 {
-  int len;
+  int high_byte = target_big_endian ? 0 : 1;
   char *p;
+
   if (opcode->arg[0] == A_BDISP8)
     {
+      int what = (opcode->nibbles[1] & 4) ? COND_JUMP_DELAY : COND_JUMP;
       p = frag_var (rs_machine_dependent,
-                   md_relax_table[C (COND_JUMP, COND32)].rlx_length,
-                   len = md_relax_table[C (COND_JUMP, COND8)].rlx_length,
-                   C (COND_JUMP, 0),
+                   md_relax_table[C (what, COND32)].rlx_length,
+                   md_relax_table[C (what, COND8)].rlx_length,
+                   C (what, 0),
                    immediate.X_add_symbol,
                    immediate.X_add_number,
                    0);
-      p[0] = (opcode->nibbles[0] << 4) | (opcode->nibbles[1]);
+      p[high_byte] = (opcode->nibbles[0] << 4) | (opcode->nibbles[1]);
     }
   else if (opcode->arg[0] == A_BDISP12)
     {
       p = frag_var (rs_machine_dependent,
                    md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length,
-                len = md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length,
+                   md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length,
                    C (UNCOND_JUMP, 0),
                    immediate.X_add_symbol,
                    immediate.X_add_number,
                    0);
-      p[0] = (opcode->nibbles[0] << 4);
+      p[high_byte] = (opcode->nibbles[0] << 4);
     }
 
 }
@@ -678,7 +926,7 @@ build_Mytes (opcode, operand)
   int index;
   char nbuf[4];
   char *output = frag_more (2);
-
+  int low_byte = target_big_endian ? 1 : 0;
   nbuf[0] = 0;
   nbuf[1] = 0;
   nbuf[2] = 0;
@@ -701,40 +949,52 @@ build_Mytes (opcode, operand)
            case REG_M:
              nbuf[index] = reg_m;
              break;
+           case REG_NM:
+             nbuf[index] = reg_n | (reg_m >> 2);
+             break;
+            case REG_B:
+             nbuf[index] = reg_b | 0x08;
+             break;
            case DISP_4:
-             insert (output + 1, R_SH_IMM4, 0);
+             insert (output + low_byte, BFD_RELOC_SH_IMM4, 0);
              break;
            case IMM_4BY4:
-             insert (output + 1, R_SH_IMM4BY4, 0);
+             insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0);
              break;
            case IMM_4BY2:
-             insert (output + 1, R_SH_IMM4BY2, 0);
+             insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0);
              break;
            case IMM_4:
-             insert (output + 1, R_SH_IMM4, 0);
+             insert (output + low_byte, BFD_RELOC_SH_IMM4, 0);
              break;
            case IMM_8BY4:
-             insert (output + 1, R_SH_IMM8BY4, 0);
+             insert (output + low_byte, BFD_RELOC_SH_IMM8BY4, 0);
              break;
            case IMM_8BY2:
-             insert (output + 1, R_SH_IMM8BY2, 0);
+             insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0);
              break;
            case IMM_8:
-             insert (output + 1, R_SH_IMM8, 0);
+             insert (output + low_byte, BFD_RELOC_SH_IMM8, 0);
              break;
            case PCRELIMM_8BY4:
-             insert (output + 1, R_SH_PCRELIMM8BY4, 1);
+             insert (output, BFD_RELOC_SH_PCRELIMM8BY4, 1);
              break;
            case PCRELIMM_8BY2:
-             insert (output + 1, R_SH_PCRELIMM8BY2, 1);
+             insert (output, BFD_RELOC_SH_PCRELIMM8BY2, 1);
              break;
            default:
-             printf ("failed for %d\n", i);
+             printf (_("failed for %d\n"), i);
            }
        }
     }
-  output[0] = (nbuf[0] << 4) | (nbuf[1]);
-  output[1] = (nbuf[2] << 4) | (nbuf[3]);
+  if (! target_big_endian) {
+    output[1] = (nbuf[0] << 4) | (nbuf[1]);
+    output[0] = (nbuf[2] << 4) | (nbuf[3]);
+  }
+  else {
+    output[0] = (nbuf[0] << 4) | (nbuf[1]);
+    output[1] = (nbuf[2] << 4) | (nbuf[3]);
+  }
 }
 
 /* This is the guts of the machine-dependent assembler.  STR points to a
@@ -748,11 +1008,10 @@ md_assemble (str)
 {
   unsigned char *op_start;
   unsigned char *op_end;
-  sh_operand_info operand[2];
+  sh_operand_info operand[3];
   sh_opcode_info *opcode;
   char name[20];
   int nlen = 0;
-  char *p;
   /* Drop leading whitespace */
   while (*str == ' ')
     str++;
@@ -764,24 +1023,41 @@ md_assemble (str)
        && !is_end_of_line[*op_end] && *op_end != ' ';
        op_end++)
     {
-      name[nlen] = op_start[nlen];
+      unsigned char c = op_start[nlen];
+
+      /* The machine independent code will convert CMP/EQ into cmp/EQ
+        because it thinks the '/' is the end of the symbol.  Instead of
+        hacking up the machine independent code, we just deal with it
+        here.  */
+      c = isupper (c) ? tolower (c) : c;
+      name[nlen] = c;
       nlen++;
     }
   name[nlen] = 0;
 
   if (nlen == 0)
     {
-      as_bad ("can't find opcode ");
+      as_bad (_("can't find opcode "));
     }
 
   opcode = (sh_opcode_info *) hash_find (opcode_hash_control, name);
 
   if (opcode == NULL)
     {
-      as_bad ("unknown opcode");
+      as_bad (_("unknown opcode"));
       return;
     }
 
+  if (sh_relax
+      && ! seg_info (now_seg)->tc_segment_info_data.in_code)
+    {
+      /* Output a CODE reloc to tell the linker that the following
+         bytes are instructions, not data.  */
+      fix_new (frag_now, frag_now_fix (), 2, &abs_symbol, 0, 0,
+              BFD_RELOC_SH_CODE);
+      seg_info (now_seg)->tc_segment_info_data.in_code = 1;
+    }
+
   if (opcode->arg[0] == A_BDISP12
       || opcode->arg[0] == A_BDISP8)
     {
@@ -790,9 +1066,16 @@ md_assemble (str)
     }
   else
     {
-      if (opcode->arg[0] != A_END)
+      if (opcode->arg[0] == A_END)
        {
-         get_operands (opcode, op_end, operand);
+         /* Ignore trailing whitespace.  If there is any, it has already
+            been compressed to a single space.  */
+         if (*op_end == ' ')
+           op_end++;
+       }
+      else
+       {
+         op_end = get_operands (opcode, op_end, operand);
        }
       opcode = get_specific (opcode, operand);
 
@@ -803,20 +1086,56 @@ md_assemble (str)
 
          where[0] = 0x0;
          where[1] = 0x0;
-         as_bad ("invalid operands for opcode");
+         as_bad (_("invalid operands for opcode"));
          return;
        }
 
+      if (*op_end)
+       as_bad (_("excess operands: '%s'"), op_end);
+
       build_Mytes (opcode, operand);
     }
 
 }
 
+/* This routine is called each time a label definition is seen.  It
+   emits a BFD_RELOC_SH_LABEL reloc if necessary.  */
+
 void
-DEFUN (tc_crawl_symbol_chain, (headers),
-       object_headers * headers)
+sh_frob_label ()
 {
-  printf ("call to tc_crawl_symbol_chain \n");
+  static fragS *last_label_frag;
+  static int last_label_offset;
+
+  if (sh_relax
+      && seg_info (now_seg)->tc_segment_info_data.in_code)
+    {
+      int offset;
+
+      offset = frag_now_fix ();
+      if (frag_now != last_label_frag
+         || offset != last_label_offset)
+       {       
+         fix_new (frag_now, offset, 2, &abs_symbol, 0, 0, BFD_RELOC_SH_LABEL);
+         last_label_frag = frag_now;
+         last_label_offset = offset;
+       }
+    }
+}
+
+/* This routine is called when the assembler is about to output some
+   data.  It emits a BFD_RELOC_SH_DATA reloc if necessary.  */
+
+void
+sh_flush_pending_output ()
+{
+  if (sh_relax
+      && seg_info (now_seg)->tc_segment_info_data.in_code)
+    {
+      fix_new (frag_now, frag_now_fix (), 2, &abs_symbol, 0, 0,
+              BFD_RELOC_SH_DATA);
+      seg_info (now_seg)->tc_segment_info_data.in_code = 0;
+    }
 }
 
 symbolS *
@@ -826,13 +1145,24 @@ DEFUN (md_undefined_symbol, (name),
   return 0;
 }
 
+#ifdef OBJ_COFF
+
+void
+DEFUN (tc_crawl_symbol_chain, (headers),
+       object_headers * headers)
+{
+  printf (_("call to tc_crawl_symbol_chain \n"));
+}
+
 void
 DEFUN (tc_headers_hook, (headers),
        object_headers * headers)
 {
-  printf ("call to tc_headers_hook \n");
+  printf (_("call to tc_headers_hook \n"));
 }
 
+#endif
+
 /* Various routines to kill one day */
 /* Equal to MAX_PRECISION in atof-ieee.c */
 #define MAX_LITTLENUMS 6
@@ -843,63 +1173,94 @@ DEFUN (tc_headers_hook, (headers),
  */
 char *
 md_atof (type, litP, sizeP)
-     char type;
+     int type;
      char *litP;
      int *sizeP;
 {
   int prec;
-  LITTLENUM_TYPE words[MAX_LITTLENUMS];
-  LITTLENUM_TYPE *wordP;
+  LITTLENUM_TYPE words[4];
   char *t;
-  char *atof_ieee ();
+  int i;
 
   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_NTOF()";
+      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--;)
+  *sizeP = prec * 2;
+
+  if (! target_big_endian)
     {
-      md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE));
-      litP += sizeof (LITTLENUM_TYPE);
+      for (i = prec - 1; i >= 0; i--)
+       {
+         md_number_to_chars (litP, (valueT) words[i], 2);
+         litP += 2;
+       }
     }
-  return 0;
+  else
+    {
+      for (i = 0; i < prec; i++)
+       {
+         md_number_to_chars (litP, (valueT) words[i], 2);
+         litP += 2;
+       }
+    }
+     
+  return NULL;
+}
+
+/* Handle the .uses pseudo-op.  This pseudo-op is used just before a
+   call instruction.  It refers to a label of the instruction which
+   loads the register which the call uses.  We use it to generate a
+   special reloc for the linker.  */
+
+static void
+s_uses (ignore)
+     int ignore;
+{
+  expressionS ex;
+
+  if (! sh_relax)
+    as_warn (_(".uses pseudo-op seen when not relaxing"));
+
+  expression (&ex);
+
+  if (ex.X_op != O_symbol || ex.X_add_number != 0)
+    {
+      as_bad (_("bad .uses format"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  fix_new_exp (frag_now, frag_now_fix (), 2, &ex, 1, BFD_RELOC_SH_USES);
+
+  demand_empty_rest_of_line ();
 }
 \f
 CONST char *md_shortopts = "";
 struct option md_longopts[] = {
-#define OPTION_RELAX (OPTION_MD_BASE)
+
+#define OPTION_RELAX  (OPTION_MD_BASE)
+#define OPTION_LITTLE (OPTION_MD_BASE + 1)
+#define OPTION_SMALL (OPTION_LITTLE + 1)
+
   {"relax", no_argument, NULL, OPTION_RELAX},
+  {"little", no_argument, NULL, OPTION_LITTLE},
+  {"small", no_argument, NULL, OPTION_SMALL},
   {NULL, no_argument, NULL, 0}
 };
 size_t md_longopts_size = sizeof(md_longopts);
@@ -912,7 +1273,16 @@ md_parse_option (c, arg)
   switch (c)
     {
     case OPTION_RELAX:
-      relax = 1;
+      sh_relax = 1;
+      break;
+
+    case OPTION_LITTLE:
+      shl = 1;
+      target_big_endian = 0;
+      break;
+
+    case OPTION_SMALL:
+      sh_small = 1;
       break;
 
     default:
@@ -926,215 +1296,428 @@ void
 md_show_usage (stream)
      FILE *stream;
 {
-  fprintf(stream, "\
+  fprintf(stream, _("\
 SH options:\n\
--relax                 alter jump instructions for long displacements\n");
+-little                        generate little endian code\n\
+-relax                 alter jump instructions for long displacements\n\
+-small                 align sections to 4 byte boundaries, not 16\n"));
 }
 \f
-int md_short_jump_size;
-
 void
 tc_Nout_fix_to_chars ()
 {
-  printf ("call to tc_Nout_fix_to_chars \n");
+  printf (_("call to tc_Nout_fix_to_chars \n"));
   abort ();
 }
 
-void
-md_create_short_jump (ptr, from_Nddr, to_Nddr, frag, to_symbol)
-     char *ptr;
-     addressT from_Nddr;
-     addressT to_Nddr;
-     fragS *frag;
-     symbolS *to_symbol;
+/* This struct is used to pass arguments to sh_count_relocs through
+   bfd_map_over_sections.  */
+
+struct sh_count_relocs
+{
+  /* Symbol we are looking for.  */
+  symbolS *sym;
+  /* Count of relocs found.  */
+  int count;
+};
+
+/* Count the number of fixups in a section which refer to a particular
+   symbol.  When using BFD_ASSEMBLER, this is called via
+   bfd_map_over_sections.  */
+
+/*ARGSUSED*/
+static void
+sh_count_relocs (abfd, sec, data)
+     bfd *abfd;
+     segT sec;
+     PTR data;
+{
+  struct sh_count_relocs *info = (struct sh_count_relocs *) data;
+  segment_info_type *seginfo;
+  symbolS *sym;
+  fixS *fix;
+
+  seginfo = seg_info (sec);
+  if (seginfo == NULL)
+    return;
+
+  sym = info->sym;
+  for (fix = seginfo->fix_root; fix != NULL; fix = fix->fx_next)
+    {
+      if (fix->fx_addsy == sym)
+       {
+         ++info->count;
+         fix->fx_tcbit = 1;
+       }
+    }
+}
+
+/* Handle the count relocs for a particular section.  When using
+   BFD_ASSEMBLER, this is called via bfd_map_over_sections.  */
+
+/*ARGSUSED*/
+static void
+sh_frob_section (abfd, sec, ignore)
+     bfd *abfd;
+     segT sec;
+     PTR ignore;
 {
-  as_fatal ("failed sanity check.");
+  segment_info_type *seginfo;
+  fixS *fix;
+
+  seginfo = seg_info (sec);
+  if (seginfo == NULL)
+    return;
+
+  for (fix = seginfo->fix_root; fix != NULL; fix = fix->fx_next)
+    {
+      symbolS *sym;
+      bfd_vma val;
+      fixS *fscan;
+      struct sh_count_relocs info;
+
+      if (fix->fx_r_type != BFD_RELOC_SH_USES)
+       continue;
+
+      /* The BFD_RELOC_SH_USES reloc should refer to a defined local
+        symbol in the same section.  */
+      sym = fix->fx_addsy;
+      if (sym == NULL
+         || fix->fx_subsy != NULL
+         || fix->fx_addnumber != 0
+         || S_GET_SEGMENT (sym) != sec
+#if ! defined (BFD_ASSEMBLER) && defined (OBJ_COFF)
+         || S_GET_STORAGE_CLASS (sym) == C_EXT
+#endif
+         || S_IS_EXTERNAL (sym))
+       {
+         as_warn_where (fix->fx_file, fix->fx_line,
+                        _(".uses does not refer to a local symbol in the same section"));
+         continue;
+       }
+
+      /* Look through the fixups again, this time looking for one
+        at the same location as sym.  */
+      val = S_GET_VALUE (sym);
+      for (fscan = seginfo->fix_root;
+          fscan != NULL;
+          fscan = fscan->fx_next)
+       if (val == fscan->fx_frag->fr_address + fscan->fx_where
+           && fscan->fx_r_type != BFD_RELOC_SH_ALIGN
+           && fscan->fx_r_type != BFD_RELOC_SH_CODE
+           && fscan->fx_r_type != BFD_RELOC_SH_DATA
+           && fscan->fx_r_type != BFD_RELOC_SH_LABEL)
+         break;
+      if (fscan == NULL)
+       {
+         as_warn_where (fix->fx_file, fix->fx_line,
+                        _("can't find fixup pointed to by .uses"));
+         continue;
+       }
+
+      if (fscan->fx_tcbit)
+       {
+         /* We've already done this one.  */
+         continue;
+       }
+
+      /* fscan should also be a fixup to a local symbol in the same
+        section.  */
+      sym = fscan->fx_addsy;
+      if (sym == NULL
+         || fscan->fx_subsy != NULL
+         || fscan->fx_addnumber != 0
+         || S_GET_SEGMENT (sym) != sec
+#if ! defined (BFD_ASSEMBLER) && defined (OBJ_COFF)
+         || S_GET_STORAGE_CLASS (sym) == C_EXT
+#endif
+         || S_IS_EXTERNAL (sym))
+       {
+         as_warn_where (fix->fx_file, fix->fx_line,
+                        _(".uses target does not refer to a local symbol in the same section"));
+         continue;
+       }
+
+      /* Now we look through all the fixups of all the sections,
+        counting the number of times we find a reference to sym.  */
+      info.sym = sym;
+      info.count = 0;
+#ifdef BFD_ASSEMBLER
+      bfd_map_over_sections (stdoutput, sh_count_relocs, (PTR) &info);
+#else
+      {
+       int iscan;
+
+       for (iscan = SEG_E0; iscan < SEG_UNKNOWN; iscan++)
+         sh_count_relocs ((bfd *) NULL, iscan, (PTR) &info);
+      }
+#endif
+
+      if (info.count < 1)
+       abort ();
+
+      /* Generate a BFD_RELOC_SH_COUNT fixup at the location of sym.
+        We have already adjusted the value of sym to include the
+        fragment address, so we undo that adjustment here.  */
+      subseg_change (sec, 0);
+      fix_new (symbol_get_frag (sym),
+              S_GET_VALUE (sym) - symbol_get_frag (sym)->fr_address,
+              4, &abs_symbol, info.count, 0, BFD_RELOC_SH_COUNT);
+    }
 }
 
+/* This function is called after the symbol table has been completed,
+   but before the relocs or section contents have been written out.
+   If we have seen any .uses pseudo-ops, they point to an instruction
+   which loads a register with the address of a function.  We look
+   through the fixups to find where the function address is being
+   loaded from.  We then generate a COUNT reloc giving the number of
+   times that function address is referred to.  The linker uses this
+   information when doing relaxing, to decide when it can eliminate
+   the stored function address entirely.  */
+
 void
-md_create_long_jump (ptr, from_Nddr, to_Nddr, frag, to_symbol)
-     char *ptr;
-     addressT from_Nddr, to_Nddr;
-     fragS *frag;
-     symbolS *to_symbol;
+sh_frob_file ()
 {
-  as_fatal ("failed sanity check.");
+  if (! sh_relax)
+    return;
+
+#ifdef BFD_ASSEMBLER
+  bfd_map_over_sections (stdoutput, sh_frob_section, (PTR) NULL);
+#else
+  {
+    int iseg;
+
+    for (iseg = SEG_E0; iseg < SEG_UNKNOWN; iseg++)
+      sh_frob_section ((bfd *) NULL, iseg, (PTR) NULL);
+  }
+#endif
 }
 
-/*
-   called after relaxing, change the frags so they know how big they are
- */
+/* Called after relaxing.  Set the correct sizes of the fragments, and
+   create relocs so that md_apply_fix will fill in the correct values.  */
+
 void
-md_convert_frag (headers, fragP)
+md_convert_frag (headers, seg, fragP)
+#ifdef BFD_ASSEMBLER
+     bfd *headers;
+#else
      object_headers *headers;
+#endif
+     segT seg;
      fragS *fragP;
-
 {
-  unsigned char *buffer = (unsigned char *) (fragP->fr_fix + fragP->fr_literal);
   int donerelax = 0;
-  int targ_addr = ((fragP->fr_symbol ? S_GET_VALUE (fragP->fr_symbol) : 0) + fragP->fr_offset);
+
   switch (fragP->fr_subtype)
     {
     case C (COND_JUMP, COND8):
-      {
-       /* Get the address of the end of the instruction */
-       int next_inst = fragP->fr_fix + fragP->fr_address + 2;
-
-       int disp = targ_addr - next_inst - 2;
-       disp /= 2;
-       
-       md_number_to_chars (buffer + 1, disp & 0xff, 1);
-       fragP->fr_fix += 2;
-       fragP->fr_var = 0;
-      }
+    case C (COND_JUMP_DELAY, COND8):
+      subseg_change (seg, 0);
+      fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol, fragP->fr_offset,
+              1, BFD_RELOC_SH_PCDISP8BY2);
+      fragP->fr_fix += 2;
+      fragP->fr_var = 0;
       break;
 
     case C (UNCOND_JUMP, UNCOND12):
-      {
-       /* Get the address of the end of the instruction */
-       int next_inst = fragP->fr_fix + fragP->fr_address + 2;
-
-       int t;
-       int disp = targ_addr - next_inst - 2;
-
-       disp /= 2;
-       t = buffer[0] & 0xf0;
-       md_number_to_chars (buffer, disp & 0xfff, 2);
-       buffer[0] = (buffer[0] & 0xf) | t;
-       fragP->fr_fix += 2;
-       fragP->fr_var = 0;
-      }
+      subseg_change (seg, 0);
+      fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol, fragP->fr_offset,
+              1, BFD_RELOC_SH_PCDISP12BY2);
+      fragP->fr_fix += 2;
+      fragP->fr_var = 0;
       break;
 
     case C (UNCOND_JUMP, UNCOND32):
     case C (UNCOND_JUMP, UNDEF_WORD_DISP):
-      {
-       /* A jump wont fit in 12 bits, make code which looks like
-          bra foo
-          mov.w @(0, PC), r14
-          .long disp
-          foo: bra @r14
+      if (fragP->fr_symbol == NULL)
+       as_bad (_("at 0x%lx, displacement overflows 12-bit field"),
+               (unsigned long) fragP->fr_address);
+      else if (S_IS_DEFINED (fragP->fr_symbol))
+       as_bad (_("at 0x%lx, displacement to defined symbol %s overflows 12-bit field"),
+               (unsigned long) fragP->fr_address,              
+               S_GET_NAME (fragP->fr_symbol));
+      else
+       as_bad (_("at 0x%lx, displacement to undefined symbol %s overflows 12-bit field"),
+               (unsigned long) fragP->fr_address,              
+               S_GET_NAME (fragP->fr_symbol));
+
+#if 0                          /* This code works, but generates poor code and the compiler
+                                  should never produce a sequence that requires it to be used.  */
+
+      /* A jump wont fit in 12 bits, make code which looks like
+        bra foo
+        mov.w @(0, PC), r14
+        .long disp
+        foo: bra @r14
         */
+      int t = buffer[0] & 0x10;
 
-       int next_inst =
-       fragP->fr_fix + fragP->fr_address + UNCOND32_LENGTH;
-
-       int disp = targ_addr - next_inst;
-       int t = buffer[0] & 0x10;
-
-       disp /= 2;
-
-       buffer[0] = 0xa0;       /* branch over move and disp */
-       buffer[1] = 3;
-       buffer[2] = 0xd0 | JREG;        /* Build mov insn */
-       buffer[3] = 0x00;
+      buffer[highbyte] = 0xa0; /* branch over move and disp */
+      buffer[lowbyte] = 3;
+      buffer[highbyte+2] = 0xd0 | JREG;        /* Build mov insn */
+      buffer[lowbyte+2] = 0x00;
 
-       buffer[4] = 0;          /* space for 32 bit jump disp */
-       buffer[5] = 0;
-       buffer[6] = 0;
-       buffer[7] = 0;
+      buffer[highbyte+4] = 0;  /* space for 32 bit jump disp */
+      buffer[lowbyte+4] = 0;
+      buffer[highbyte+6] = 0;
+      buffer[lowbyte+6] = 0;
 
-       buffer[10] = 0x40 | JREG;       /* Build jmp @JREG */
-       buffer[11] = t ? 0xb : 0x2b;
+      buffer[highbyte+8] = 0x40 | JREG;        /* Build jmp @JREG */
+      buffer[lowbyte+8] = t ? 0xb : 0x2b;
 
-       buffer[12] = 0x20;      /* build nop */
-       buffer[13] = 0x0b;
+      buffer[highbyte+10] = 0x20; /* build nop */
+      buffer[lowbyte+10] = 0x0b;
 
-       /* Make reloc for the long disp */
-       fix_new (fragP,
-                fragP->fr_fix + 4,
-                4,
-                fragP->fr_symbol,
-                fragP->fr_offset,
-                0,
-                R_SH_IMM32);
-       fragP->fr_fix += UNCOND32_LENGTH;
-       fragP->fr_var = 0;
-       donerelax = 1;
+      /* Make reloc for the long disp */
+      fix_new (fragP,
+              fragP->fr_fix + 4,
+              4,
+              fragP->fr_symbol,
+              fragP->fr_offset,
+              0,
+              BFD_RELOC_32);
+      fragP->fr_fix += UNCOND32_LENGTH;
+      fragP->fr_var = 0;
+      donerelax = 1;
+#endif
 
-      }
       break;
 
     case C (COND_JUMP, COND12):
+    case C (COND_JUMP_DELAY, COND12):
+      /* A bcond won't fit, so turn it into a b!cond; bra disp; nop */
+      /* I found that a relax failure for gcc.c-torture/execute/930628-1.c
+        was due to gas incorrectly relaxing an out-of-range conditional
+        branch with delay slot.  It turned:
+                     bf.s    L6              (slot mov.l   r12,@(44,r0))
+         into:
+   
+2c:  8f 01 a0 8b     bf.s    32 <_main+32>   (slot bra       L6)
+30:  00 09           nop
+32:  10 cb           mov.l   r12,@(44,r0)
+         Therefore, branches with delay slots have to be handled
+        differently from ones without delay slots.  */
       {
-       /* A bcond won't fit, so turn it into a b!cond; bra disp; nop */
-       int next_inst =
-       fragP->fr_fix + fragP->fr_address + 6;
-
-       int disp = targ_addr - next_inst;
-       disp /= 2;
-       md_number_to_chars (buffer + 2, disp & 0xfff, 2);
-       buffer[0] ^= 0x2;       /* Toggle T/F bit */
-       buffer[1] = 1;          /* branch over jump and nop */
-       buffer[2] = (buffer[2] & 0xf) | 0xa0;   /* Build jump insn */
-       buffer[4] = 0x20;       /* Build nop */
-       buffer[5] = 0x0b;
-       fragP->fr_fix += 6;
+       unsigned char *buffer =
+         (unsigned char *) (fragP->fr_fix + fragP->fr_literal);
+       int highbyte = target_big_endian ? 0 : 1;
+       int lowbyte = target_big_endian ? 1 : 0;
+       int delay = fragP->fr_subtype == C (COND_JUMP_DELAY, COND12);
+
+       /* Toggle the true/false bit of the bcond.  */
+       buffer[highbyte] ^= 0x2;
+
+       /* If this is a dalayed branch, we may not put the the bra in the
+          slot.  So we change it to a non-delayed branch, like that:
+          b! cond slot_label; bra disp; slot_label: slot_insn
+          ??? We should try if swapping the conditional branch and
+          its delay-slot insn already makes the branch reach.  */
+
+       /* Build a relocation to six / four bytes farther on.  */
+       subseg_change (seg, 0);
+       fix_new (fragP, fragP->fr_fix, 2,
+#ifdef BFD_ASSEMBLER
+                section_symbol (seg),
+#else
+                seg_info (seg)->dot,
+#endif
+                fragP->fr_address + fragP->fr_fix + (delay ? 4 : 6),
+                1, BFD_RELOC_SH_PCDISP8BY2);
+
+       /* Set up a jump instruction.  */
+       buffer[highbyte + 2] = 0xa0;
+       buffer[lowbyte + 2] = 0;
+       fix_new (fragP, fragP->fr_fix + 2, 2, fragP->fr_symbol,
+                fragP->fr_offset, 1, BFD_RELOC_SH_PCDISP12BY2);
+
+       if (delay)
+         {
+           buffer[highbyte] &= ~0x4; /* Removes delay slot from branch.  */
+           fragP->fr_fix += 4;
+         }
+       else
+         {
+           /* Fill in a NOP instruction.  */
+           buffer[highbyte + 4] = 0x0;
+           buffer[lowbyte + 4] = 0x9;
+
+           fragP->fr_fix += 6;
+         }
        fragP->fr_var = 0;
        donerelax = 1;
       }
       break;
 
     case C (COND_JUMP, COND32):
+    case C (COND_JUMP_DELAY, COND32):
     case C (COND_JUMP, UNDEF_WORD_DISP):
-      {
-       /* A bcond won't fit and it won't go into a 12 bit
-          displacement either, the code sequence looks like:
-          b!cond foop
-          mov.w @(n, PC), r14
-          jmp  @r14
-          nop
-          .long where
-          foop:
+    case C (COND_JUMP_DELAY, UNDEF_WORD_DISP):
+      if (fragP->fr_symbol == NULL)
+       as_bad (_("at 0x%lx, displacement overflows 8-bit field"), 
+               (unsigned long) fragP->fr_address);
+      else if (S_IS_DEFINED (fragP->fr_symbol))
+       as_bad (_("at 0x%lx, displacement to defined symbol %s overflows 8-bit field "),
+               (unsigned long) fragP->fr_address,              
+               S_GET_NAME (fragP->fr_symbol));
+      else
+       as_bad (_("at 0x%lx, displacement to undefined symbol %s overflows 8-bit field "),
+               (unsigned long) fragP->fr_address,              
+               S_GET_NAME (fragP->fr_symbol));
+
+#if 0                          /* This code works, but generates poor code, and the compiler
+                                  should never produce a sequence that requires it to be used.  */
+
+      /* A bcond won't fit and it won't go into a 12 bit
+        displacement either, the code sequence looks like:
+        b!cond foop
+        mov.w @(n, PC), r14
+        jmp  @r14
+        nop
+        .long where
+        foop:
         */
 
-       int next_inst =
-       fragP->fr_fix + fragP->fr_address + COND32_LENGTH;
-
-       int disp = targ_addr - next_inst;
-       disp /= 2;
-
-       buffer[0] ^= 0x2;       /* Toggle T/F bit */
+      buffer[0] ^= 0x2;                /* Toggle T/F bit */
 #define JREG 14
-       buffer[1] = 5;          /* branch over mov, jump, nop and ptr */
-       buffer[2] = 0xd0 | JREG;        /* Build mov insn */
-       buffer[3] = 0x2;
-       buffer[4] = 0x40 | JREG;        /* Build jmp @JREG */
-       buffer[5] = 0x0b;
-       buffer[6] = 0x20;       /* build nop */
-       buffer[7] = 0x0b;
-       buffer[8] = 0;          /* space for 32 bit jump disp */
-       buffer[9] = 0;
-       buffer[10] = 0;
-       buffer[11] = 0;
-       buffer[12] = 0;
-       buffer[13] = 0;
-       /* Make reloc for the long disp */
-       fix_new (fragP,
-                fragP->fr_fix + 8,
-                4,
-                fragP->fr_symbol,
-                fragP->fr_offset,
-                0,
-                R_SH_IMM32);
-       fragP->fr_fix += COND32_LENGTH;
-       fragP->fr_var = 0;
-       donerelax = 1;
-      }
+      buffer[1] = 5;           /* branch over mov, jump, nop and ptr */
+      buffer[2] = 0xd0 | JREG; /* Build mov insn */
+      buffer[3] = 0x2;
+      buffer[4] = 0x40 | JREG; /* Build jmp @JREG */
+      buffer[5] = 0x0b;
+      buffer[6] = 0x20;                /* build nop */
+      buffer[7] = 0x0b;
+      buffer[8] = 0;           /* space for 32 bit jump disp */
+      buffer[9] = 0;
+      buffer[10] = 0;
+      buffer[11] = 0;
+      buffer[12] = 0;
+      buffer[13] = 0;
+      /* Make reloc for the long disp */
+      fix_new (fragP,
+              fragP->fr_fix + 8,
+              4,
+              fragP->fr_symbol,
+              fragP->fr_offset,
+              0,
+              BFD_RELOC_32);
+      fragP->fr_fix += COND32_LENGTH;
+      fragP->fr_var = 0;
+      donerelax = 1;
+#endif
+
       break;
 
     default:
       abort ();
     }
 
-  if (donerelax && !relax)
-    {
-      as_warn ("Offset doesn't fit at 0x%x, trying to get to %s+0x%x",
-              fragP->fr_address,
-              fragP->fr_symbol  ?    S_GET_NAME(fragP->fr_symbol): "",
-              targ_addr);
-    }
-
+  if (donerelax && !sh_relax)
+    as_warn_where (fragP->fr_file, fragP->fr_line,
+                  _("overflow in branch to %s; converted into longer instruction sequence"),
+                  (fragP->fr_symbol != NULL
+                   ? S_GET_NAME (fragP->fr_symbol)
+                   : ""));
 }
 
 valueT
@@ -1142,108 +1725,379 @@ DEFUN (md_section_align, (seg, size),
        segT seg AND
        valueT size)
 {
+#ifdef BFD_ASSEMBLER
+#ifdef OBJ_ELF
+  return size;
+#else /* ! OBJ_ELF */
+  return ((size + (1 << bfd_get_section_alignment (stdoutput, seg)) - 1)
+         & (-1 << bfd_get_section_alignment (stdoutput, seg)));
+#endif /* ! OBJ_ELF */
+#else /* ! BFD_ASSEMBLER */
   return ((size + (1 << section_alignment[(int) seg]) - 1)
          & (-1 << section_alignment[(int) seg]));
+#endif /* ! BFD_ASSEMBLER */
+}
+
+/* This static variable is set by s_uacons to tell sh_cons_align that
+   the expession does not need to be aligned.  */
 
+static int sh_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.  */
+
+static void
+s_uacons (bytes)
+     int bytes;
+{
+  /* Tell sh_cons_align not to align this value.  */
+  sh_no_align_cons = 1;
+  cons (bytes);
 }
 
+/* If a .word, et. al., pseud-op is seen, warn if the value is not
+   aligned correctly.  Note that this can cause warnings to be issued
+   when assembling initialized structured which were declared with the
+   packed attribute.  FIXME: Perhaps we should require an option to
+   enable this warning?  */
+
+void
+sh_cons_align (nbytes)
+     int nbytes;
+{
+  int nalign;
+  char *p;
+
+  if (sh_no_align_cons)
+    {
+      /* This is an unaligned pseudo-op.  */
+      sh_no_align_cons = 0;
+      return;
+    }
+
+  nalign = 0;
+  while ((nbytes & 1) == 0)
+    {
+      ++nalign;
+      nbytes >>= 1;
+    }
+
+  if (nalign == 0)
+    return;
+
+  if (now_seg == absolute_section)
+    {
+      if ((abs_section_offset & ((1 << nalign) - 1)) != 0)
+       as_warn (_("misaligned data"));
+      return;
+    }
+
+  p = frag_var (rs_align_code, 1, 1, (relax_substateT) 0,
+               (symbolS *) NULL, (offsetT) nalign, (char *) NULL);
+
+  record_alignment (now_seg, nalign);
+}
+
+/* When relaxing, we need to output a reloc for any .align directive
+   that requests alignment to a four byte boundary or larger.  This is
+   also where we check for misaligned data.  */
+
+void
+sh_handle_align (frag)
+     fragS *frag;
+{
+  if (sh_relax
+      && frag->fr_type == rs_align
+      && frag->fr_address + frag->fr_fix > 0
+      && frag->fr_offset > 1
+      && now_seg != bss_section)
+    fix_new (frag, frag->fr_fix, 2, &abs_symbol, frag->fr_offset, 0,
+            BFD_RELOC_SH_ALIGN);
+
+  if (frag->fr_type == rs_align_code
+      && frag->fr_next->fr_address - frag->fr_address - frag->fr_fix != 0)
+    as_warn_where (frag->fr_file, frag->fr_line, _("misaligned data"));
+}
+
+/* This macro decides whether a particular reloc is an entry in a
+   switch table.  It is used when relaxing, because the linker needs
+   to know about all such entries so that it can adjust them if
+   necessary.  */
+
+#ifdef BFD_ASSEMBLER
+#define SWITCH_TABLE_CONS(fix) (0)
+#else
+#define SWITCH_TABLE_CONS(fix)                         \
+  ((fix)->fx_r_type == 0                               \
+   && ((fix)->fx_size == 2                             \
+       || (fix)->fx_size == 1                          \
+       || (fix)->fx_size == 4))
+#endif
+
+#define SWITCH_TABLE(fix)                              \
+  ((fix)->fx_addsy != NULL                             \
+   && (fix)->fx_subsy != NULL                          \
+   && S_GET_SEGMENT ((fix)->fx_addsy) == text_section  \
+   && S_GET_SEGMENT ((fix)->fx_subsy) == text_section  \
+   && ((fix)->fx_r_type == BFD_RELOC_32                        \
+       || (fix)->fx_r_type == BFD_RELOC_16             \
+       || (fix)->fx_r_type == BFD_RELOC_8              \
+       || SWITCH_TABLE_CONS (fix)))
+
+/* See whether we need to force a relocation into the output file.
+   This is used to force out switch and PC relative relocations when
+   relaxing.  */
+
+int
+sh_force_relocation (fix)
+     fixS *fix;
+{
+
+  if (fix->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+      || fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+    return 1;
+
+  if (! sh_relax)
+    return 0;
+
+  return (fix->fx_pcrel
+         || SWITCH_TABLE (fix)
+         || fix->fx_r_type == BFD_RELOC_SH_COUNT
+         || fix->fx_r_type == BFD_RELOC_SH_ALIGN
+         || fix->fx_r_type == BFD_RELOC_SH_CODE
+         || fix->fx_r_type == BFD_RELOC_SH_DATA
+         || fix->fx_r_type == BFD_RELOC_SH_LABEL);
+}
+
+#ifdef OBJ_ELF
+boolean
+sh_fix_adjustable (fixP)
+   fixS *fixP;
+{
+
+  if (fixP->fx_addsy == NULL)
+    return 1;
+  
+  /* We need the symbol name for the VTABLE entries */
+  if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+      || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+    return 0;
+
+  return 1;
+}
+#endif
+
+/* Apply a fixup to the object file.  */
+
+#ifdef BFD_ASSEMBLER
+int
+md_apply_fix (fixP, valp)
+     fixS *fixP;
+     valueT *valp;
+#else
 void
 md_apply_fix (fixP, val)
      fixS *fixP;
      long val;
+#endif
 {
   char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
-  int addr = fixP->fx_frag->fr_address + fixP->fx_where;
+  int lowbyte = target_big_endian ? 1 : 0;
+  int highbyte = target_big_endian ? 0 : 1;
+#ifdef BFD_ASSEMBLER
+  long val = *valp;
+#endif
+  long max, min;
+  int shift;
+
+#ifdef BFD_ASSEMBLER
+  /* adjust_reloc_syms won't convert a reloc against a weak symbol
+     into a reloc against a section, but bfd_install_relocation will
+     screw up if the symbol is defined, so we have to adjust val here
+     to avoid the screw up later.  */
+  if (fixP->fx_addsy != NULL
+      && S_IS_WEAK (fixP->fx_addsy))
+    val -= S_GET_VALUE  (fixP->fx_addsy);
+#endif
+
+#ifndef BFD_ASSEMBLER
   if (fixP->fx_r_type == 0)
     {
       if (fixP->fx_size == 2)
-       fixP->fx_r_type = R_SH_IMM16;
+       fixP->fx_r_type = BFD_RELOC_16;
+      else if (fixP->fx_size == 4)
+       fixP->fx_r_type = BFD_RELOC_32;
+      else if (fixP->fx_size == 1)
+       fixP->fx_r_type = BFD_RELOC_8;
       else
-       fixP->fx_r_type = R_SH_IMM32;
+       abort ();
     }
+#endif
 
+  max = min = 0;
+  shift = 0;
   switch (fixP->fx_r_type)
     {
-
-    case R_SH_IMM4:
+    case BFD_RELOC_SH_IMM4:
+      max = 0xf;
       *buf = (*buf & 0xf0) | (val & 0xf);
       break;
 
-    case R_SH_IMM4BY2:
+    case BFD_RELOC_SH_IMM4BY2:
+      max = 0xf;
+      shift = 1;
       *buf = (*buf & 0xf0) | ((val >> 1) & 0xf);
       break;
 
-    case R_SH_IMM4BY4:
+    case BFD_RELOC_SH_IMM4BY4:
+      max = 0xf;
+      shift = 2;
       *buf = (*buf & 0xf0) | ((val >> 2) & 0xf);
       break;
 
-    case R_SH_IMM8BY2:
+    case BFD_RELOC_SH_IMM8BY2:
+      max = 0xff;
+      shift = 1;
       *buf = val >> 1;
       break;
 
-    case R_SH_IMM8BY4:
+    case BFD_RELOC_SH_IMM8BY4:
+      max = 0xff;
+      shift = 2;
       *buf = val >> 2;
       break;
 
-    case R_SH_IMM8:
+    case BFD_RELOC_8:
+    case BFD_RELOC_SH_IMM8:
+      /* Sometimes the 8 bit value is sign extended (e.g., add) and
+         sometimes it is not (e.g., and).  We permit any 8 bit value.
+         Note that adding further restrictions may invalidate
+         reasonable looking assembly code, such as ``and -0x1,r0''.  */
+      max = 0xff;
+      min = - 0xff;
       *buf++ = val;
       break;
 
-    case R_SH_PCRELIMM8BY4:
-      addr &= ~1;
-#if 0
-      if (val & 0x3)
-       as_warn ("non aligned displacement at %x\n", addr);
-#endif
-      /*      val -= (addr + 4); */
-      val += 3;
-      val /= 4;
+    case BFD_RELOC_SH_PCRELIMM8BY4:
+      /* The lower two bits of the PC are cleared before the
+         displacement is added in.  We can assume that the destination
+         is on a 4 byte bounday.  If this instruction is also on a 4
+         byte boundary, then we want
+          (target - here) / 4
+        and target - here is a multiple of 4.
+        Otherwise, we are on a 2 byte boundary, and we want
+          (target - (here - 2)) / 4
+        and target - here is not a multiple of 4.  Computing
+          (target - (here - 2)) / 4 == (target - here + 2) / 4
+        works for both cases, since in the first case the addition of
+        2 will be removed by the division.  target - here is in the
+        variable val.  */
+      val = (val + 2) / 4;
       if (val & ~0xff)
-       as_warn ("pcrel too far at %x\n", addr);
-
-      *buf = val;
+       as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
+      buf[lowbyte] = val;
       break;
 
-    case R_SH_PCRELIMM8BY2:
-      addr &= ~1;
-      if (val & 0x1)
-       as_bad ("odd displacement at %x\n", addr);
-      /*      val -= (addr + 4); */
-      val++;
+    case BFD_RELOC_SH_PCRELIMM8BY2:
       val /= 2;
       if (val & ~0xff)
-       as_warn ("pcrel too far at %x\n", addr);
-      *buf = val;
+       as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
+      buf[lowbyte] = val;
       break;
 
-    case R_SH_IMM32:
-      *buf++ = val >> 24;
-      *buf++ = val >> 16;
-      *buf++ = val >> 8;
-      *buf++ = val >> 0;
+    case BFD_RELOC_SH_PCDISP8BY2:
+      val /= 2;
+      if (val < -0x80 || val > 0x7f)
+       as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
+      buf[lowbyte] = val;
       break;
 
-    case R_SH_IMM16:
-      *buf++ = val >> 8;
-      *buf++ = val >> 0;
+    case BFD_RELOC_SH_PCDISP12BY2:
+      val /= 2;
+      if (val < -0x800 || val >= 0x7ff)
+       as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
+      buf[lowbyte] = val & 0xff;
+      buf[highbyte] |= (val >> 8) & 0xf;
+      break;
+
+    case BFD_RELOC_32:
+      if (! target_big_endian) 
+       {
+         *buf++ = val >> 0;
+         *buf++ = val >> 8;
+         *buf++ = val >> 16;
+         *buf++ = val >> 24;
+       }
+      else 
+       {
+         *buf++ = val >> 24;
+         *buf++ = val >> 16;
+         *buf++ = val >> 8;
+         *buf++ = val >> 0;
+       }
       break;
 
+    case BFD_RELOC_16:
+      if (! target_big_endian)
+       {
+         *buf++ = val >> 0;
+         *buf++ = val >> 8;
+       } 
+      else 
+       {
+         *buf++ = val >> 8;
+         *buf++ = val >> 0;
+       }
+      break;
+
+    case BFD_RELOC_SH_USES:
+      /* Pass the value into sh_coff_reloc_mangle.  */
+      fixP->fx_addnumber = val;
+      break;
+
+    case BFD_RELOC_SH_COUNT:
+    case BFD_RELOC_SH_ALIGN:
+    case BFD_RELOC_SH_CODE:
+    case BFD_RELOC_SH_DATA:
+    case BFD_RELOC_SH_LABEL:
+      /* Nothing to do here.  */
+      break;
+
+    case BFD_RELOC_VTABLE_INHERIT:
+    case BFD_RELOC_VTABLE_ENTRY:
+      fixP->fx_done = 0;
+#ifdef BFD_ASSEMBLER
+      return 0;
+#else
+      return;
+#endif
+
     default:
       abort ();
     }
-}
 
-void
-DEFUN (md_operand, (expressionP), expressionS * expressionP)
-{
+  if (shift != 0)
+    {
+      if ((val & ((1 << shift) - 1)) != 0)
+       as_bad_where (fixP->fx_file, fixP->fx_line, _("misaligned offset"));
+      if (val >= 0)
+       val >>= shift;
+      else
+       val = ((val >> shift)
+              | ((long) -1 & ~ ((long) -1 >> shift)));
+    }
+  if (max != 0 && (val < min || val > max))
+    as_bad_where (fixP->fx_file, fixP->fx_line, _("offset out of range"));
+
+#ifdef BFD_ASSEMBLER
+  return 0;
+#endif
 }
 
-int md_long_jump_size;
+/* Called just before address relaxation.  Return the length
+   by which a fragment must grow to reach it's destination.  */
 
-/*
-   called just before address relaxation, return the length
-   by which a fragment must grow to reach it's destination
- */
 int
 md_estimate_size_before_relax (fragP, segment_type)
      register fragS *fragP;
@@ -1274,27 +2128,31 @@ md_estimate_size_before_relax (fragP, segment_type)
     default:
       abort ();
     case C (COND_JUMP, UNDEF_DISP):
+    case C (COND_JUMP_DELAY, UNDEF_DISP):
       /* used to be a branch to somewhere which was unknown */
       if (fragP->fr_symbol
          && S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
        {
+         int what = GET_WHAT (fragP->fr_subtype);
          /* Got a symbol and it's defined in this segment, become byte
             sized - maybe it will fix up */
-         fragP->fr_subtype = C (COND_JUMP, COND8);
-         fragP->fr_var = md_relax_table[C (COND_JUMP, COND8)].rlx_length;
+         fragP->fr_subtype = C (what, COND8);
+         fragP->fr_var = md_relax_table[C (what, COND8)].rlx_length;
        }
       else if (fragP->fr_symbol)
        {
+         int what = GET_WHAT (fragP->fr_subtype);
          /* Its got a segment, but its not ours, so it will always be long */
-         fragP->fr_subtype = C (COND_JUMP, UNDEF_WORD_DISP);
-         fragP->fr_var = md_relax_table[C (COND_JUMP, COND32)].rlx_length;
-         return md_relax_table[C (COND_JUMP, COND32)].rlx_length;
+         fragP->fr_subtype = C (what, UNDEF_WORD_DISP);
+         fragP->fr_var = md_relax_table[C (what, COND32)].rlx_length;
+         return md_relax_table[C (what, COND32)].rlx_length;
        }
       else
        {
+         int what = GET_WHAT (fragP->fr_subtype);
          /* We know the abs value */
-         fragP->fr_subtype = C (COND_JUMP, COND8);
-         fragP->fr_var = md_relax_table[C (COND_JUMP, COND8)].rlx_length;
+         fragP->fr_subtype = C (what, COND8);
+         fragP->fr_var = md_relax_table[C (what, COND8)].rlx_length;
        }
 
       break;
@@ -1310,86 +2168,269 @@ md_number_to_chars (ptr, use, nbytes)
      valueT use;
      int nbytes;
 {
-  number_to_chars_bigendian (ptr, use, nbytes);
+  if (! target_big_endian)
+    number_to_chars_littleendian (ptr, use, nbytes);
+  else
+    number_to_chars_bigendian (ptr, use, nbytes);
 }
 
 long
 md_pcrel_from (fixP)
      fixS *fixP;
 {
-  int gap = fixP->fx_size + fixP->fx_where +  fixP->fx_frag->fr_address - 1 ;
-  return gap;
+  return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address + 2;
+}
+
+#ifdef OBJ_COFF
+
+int
+tc_coff_sizemachdep (frag)
+     fragS *frag;
+{
+  return md_relax_table[frag->fr_subtype].rlx_length;
 }
 
-short
-tc_coff_fix2rtype (fix_ptr)
-     fixS *fix_ptr;
+#endif /* OBJ_COFF */
+
+/* When we align the .text section, insert the correct NOP pattern.  */
+
+int
+sh_do_align (n, fill, len, max)
+     int n;
+     const char *fill;
+     int len;
+     int max;
 {
-  return fix_ptr->fx_r_type;
+  if (fill == NULL
+      && subseg_text_p (now_seg)
+      && n > 1)
+    {
+      static const unsigned char big_nop_pattern[] = { 0x00, 0x09 };
+      static const unsigned char little_nop_pattern[] = { 0x09, 0x00 };
+
+      /* First align to a 2 byte boundary, in case there is an odd
+         .byte.  */
+      frag_align (1, 0, 0);
+      if (target_big_endian)
+       frag_align_pattern (n, big_nop_pattern, sizeof big_nop_pattern, max);
+      else
+       frag_align_pattern (n, little_nop_pattern, sizeof little_nop_pattern,
+                           max);
+      return 1;
+    }
+
+  return 0;
 }
 
+#ifndef BFD_ASSEMBLER
+#ifdef OBJ_COFF
+
+/* Map BFD relocs to SH COFF relocs.  */
+
+struct reloc_map
+{
+  bfd_reloc_code_real_type bfd_reloc;
+  int sh_reloc;
+};
+
+static const struct reloc_map coff_reloc_map[] =
+{
+  { BFD_RELOC_32, R_SH_IMM32 },
+  { BFD_RELOC_16, R_SH_IMM16 },
+  { BFD_RELOC_8, R_SH_IMM8 },
+  { BFD_RELOC_SH_PCDISP8BY2, R_SH_PCDISP8BY2 },
+  { BFD_RELOC_SH_PCDISP12BY2, R_SH_PCDISP },
+  { BFD_RELOC_SH_IMM4, R_SH_IMM4 },
+  { BFD_RELOC_SH_IMM4BY2, R_SH_IMM4BY2 },
+  { BFD_RELOC_SH_IMM4BY4, R_SH_IMM4BY4 },
+  { BFD_RELOC_SH_IMM8, R_SH_IMM8 },
+  { BFD_RELOC_SH_IMM8BY2, R_SH_IMM8BY2 },
+  { BFD_RELOC_SH_IMM8BY4, R_SH_IMM8BY4 },
+  { BFD_RELOC_SH_PCRELIMM8BY2, R_SH_PCRELIMM8BY2 },
+  { BFD_RELOC_SH_PCRELIMM8BY4, R_SH_PCRELIMM8BY4 },
+  { BFD_RELOC_8_PCREL, R_SH_SWITCH8 },
+  { BFD_RELOC_SH_SWITCH16, R_SH_SWITCH16 },
+  { BFD_RELOC_SH_SWITCH32, R_SH_SWITCH32 },
+  { BFD_RELOC_SH_USES, R_SH_USES },
+  { BFD_RELOC_SH_COUNT, R_SH_COUNT },
+  { BFD_RELOC_SH_ALIGN, R_SH_ALIGN },
+  { BFD_RELOC_SH_CODE, R_SH_CODE },
+  { BFD_RELOC_SH_DATA, R_SH_DATA },
+  { BFD_RELOC_SH_LABEL, R_SH_LABEL },
+  { BFD_RELOC_UNUSED, 0 }
+};
+
+/* Adjust a reloc for the SH.  This is similar to the generic code,
+   but does some minor tweaking.  */
+
 void
-tc_reloc_mangle (fix_ptr, intr, base)
-     fixS *fix_ptr;
+sh_coff_reloc_mangle (seg, fix, intr, paddr)
+     segment_info_type *seg;
+     fixS *fix;
      struct internal_reloc *intr;
-     bfd_vma base;
-
+     unsigned int paddr;
 {
-  symbolS *symbol_ptr;
+  symbolS *symbol_ptr = fix->fx_addsy;
+  symbolS *dot;
 
-  symbol_ptr = fix_ptr->fx_addsy;
+  intr->r_vaddr = paddr + fix->fx_frag->fr_address + fix->fx_where;
 
-  /* If this relocation is attached to a symbol then it's ok
-     to output it */
-  if (fix_ptr->fx_r_type == RELOC_32)
+  if (! SWITCH_TABLE (fix))
     {
-      /* cons likes to create reloc32's whatever the size of the reloc..
-       */
-      switch (fix_ptr->fx_size)
-       {
-       case 2:
-         intr->r_type = R_IMM16;
+      const struct reloc_map *rm;
+
+      for (rm = coff_reloc_map; rm->bfd_reloc != BFD_RELOC_UNUSED; rm++)
+       if (rm->bfd_reloc == (bfd_reloc_code_real_type) fix->fx_r_type)
          break;
-       case 1:
-         intr->r_type = R_IMM8;
+      if (rm->bfd_reloc == BFD_RELOC_UNUSED)
+       as_bad_where (fix->fx_file, fix->fx_line,
+                     _("Can not represent %s relocation in this object file format"),
+                     bfd_get_reloc_code_name (fix->fx_r_type));
+      intr->r_type = rm->sh_reloc;
+      intr->r_offset = 0;
+    }
+  else
+    {
+      know (sh_relax);
+
+      if (fix->fx_r_type == BFD_RELOC_16)
+       intr->r_type = R_SH_SWITCH16;
+      else if (fix->fx_r_type == BFD_RELOC_8)
+       intr->r_type = R_SH_SWITCH8;
+      else if (fix->fx_r_type == BFD_RELOC_32)
+       intr->r_type = R_SH_SWITCH32;
+      else
+       abort ();
+
+      /* For a switch reloc, we set r_offset to the difference between
+         the reloc address and the subtrahend.  When the linker is
+         doing relaxing, it can use the determine the starting and
+         ending points of the switch difference expression.  */
+      intr->r_offset = intr->r_vaddr - S_GET_VALUE (fix->fx_subsy);
+    }
+
+  /* PC relative relocs are always against the current section.  */
+  if (symbol_ptr == NULL)
+    {
+      switch (fix->fx_r_type)
+       {
+       case BFD_RELOC_SH_PCRELIMM8BY2:
+       case BFD_RELOC_SH_PCRELIMM8BY4:
+       case BFD_RELOC_SH_PCDISP8BY2:
+       case BFD_RELOC_SH_PCDISP12BY2:
+       case BFD_RELOC_SH_USES:
+         symbol_ptr = seg->dot;
          break;
        default:
-         abort ();
+         break;
        }
     }
-  else
+
+  if (fix->fx_r_type == BFD_RELOC_SH_USES)
     {
-      intr->r_type = fix_ptr->fx_r_type;
+      /* We can't store the offset in the object file, since this
+        reloc does not take up any space, so we store it in r_offset.
+        The fx_addnumber field was set in md_apply_fix.  */
+      intr->r_offset = fix->fx_addnumber;
+    }
+  else if (fix->fx_r_type == BFD_RELOC_SH_COUNT)
+    {
+      /* We can't store the count in the object file, since this reloc
+         does not take up any space, so we store it in r_offset.  The
+         fx_offset field was set when the fixup was created in
+         sh_coff_frob_file.  */
+      intr->r_offset = fix->fx_offset;
+      /* This reloc is always absolute.  */
+      symbol_ptr = NULL;
+    }
+  else if (fix->fx_r_type == BFD_RELOC_SH_ALIGN)
+    {
+      /* Store the alignment in the r_offset field.  */
+      intr->r_offset = fix->fx_offset;
+      /* This reloc is always absolute.  */
+      symbol_ptr = NULL;
+    }
+  else if (fix->fx_r_type == BFD_RELOC_SH_CODE
+          || fix->fx_r_type == BFD_RELOC_SH_DATA
+          || fix->fx_r_type == BFD_RELOC_SH_LABEL)
+    {
+      /* These relocs are always absolute.  */
+      symbol_ptr = NULL;
     }
-
-  intr->r_vaddr = fix_ptr->fx_frag->fr_address + fix_ptr->fx_where + base;
-  intr->r_offset = fix_ptr->fx_offset;
 
   /* Turn the segment of the symbol into an offset.  */
-  if (symbol_ptr)
+  if (symbol_ptr != NULL)
     {
-      symbolS *dot;
-
       dot = segment_info[S_GET_SEGMENT (symbol_ptr)].dot;
-      if (dot)
-       {
-         intr->r_offset += S_GET_VALUE (symbol_ptr);
-         intr->r_symndx = dot->sy_number;
-       }
+      if (dot != NULL)
+       intr->r_symndx = dot->sy_number;
       else
-       {
-         intr->r_symndx = symbol_ptr->sy_number;
-       }
+       intr->r_symndx = symbol_ptr->sy_number;
     }
   else
-    {
-      intr->r_symndx = -1;
-    }
+    intr->r_symndx = -1;
 }
 
-int
-tc_coff_sizemachdep (frag)
-     fragS *frag;
+#endif /* OBJ_COFF */
+#endif /* ! BFD_ASSEMBLER */
+
+#ifdef BFD_ASSEMBLER
+
+/* Create a reloc.  */
+
+arelent *
+tc_gen_reloc (section, fixp)
+     asection *section;
+     fixS *fixp;
 {
-  return md_relax_table[frag->fr_subtype].rlx_length;
+  arelent *rel;
+  bfd_reloc_code_real_type r_type;
+
+  rel = (arelent *) xmalloc (sizeof (arelent));
+  rel->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+  *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+  rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+  r_type = fixp->fx_r_type;
+
+  if (SWITCH_TABLE (fixp))
+    {
+      rel->addend = rel->address - S_GET_VALUE (fixp->fx_subsy);
+      if (r_type == BFD_RELOC_16)
+       r_type = BFD_RELOC_SH_SWITCH16;
+      else if (r_type == BFD_RELOC_8)
+       r_type = BFD_RELOC_8_PCREL;
+      else if (r_type == BFD_RELOC_32)
+       r_type = BFD_RELOC_SH_SWITCH32;
+      else
+       abort ();
+    }
+  else if (r_type == BFD_RELOC_SH_USES)
+    rel->addend = fixp->fx_addnumber;
+  else if (r_type == BFD_RELOC_SH_COUNT)
+    rel->addend = fixp->fx_offset;
+  else if (r_type == BFD_RELOC_SH_ALIGN)
+    rel->addend = fixp->fx_offset;
+  else if (r_type == BFD_RELOC_VTABLE_INHERIT
+           || r_type == BFD_RELOC_VTABLE_ENTRY)
+    rel->addend = fixp->fx_offset;
+  else if (fixp->fx_pcrel)
+    rel->addend = fixp->fx_addnumber;
+  else
+    rel->addend = 0;
+
+  rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
+  if (rel->howto == NULL)
+    {
+      as_bad_where (fixp->fx_file, fixp->fx_line,
+                   _("Cannot represent relocation type %s"),
+                   bfd_get_reloc_code_name (r_type));
+      /* Set howto to a garbage value so that we can keep going.  */
+      rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
+      assert (rel->howto != NULL);
+    }
+
+  return rel;
 }
+
+#endif /* BFD_ASSEMBLER */