* config/tc-sh.c (md_assemble): For branches, check & update
[deliverable/binutils-gdb.git] / gas / config / tc-mn10300.c
index b73073eca0ca42d2ba01b4137eaa048de27bc920..832464617d2bb1adea6a97d03823ecef1a9b0e01 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-mn10300.c -- Assembler code for the Matsushita 10300
-   Copyright 1996, 1997, 1998, 1999, 2000, 2001
+   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
    Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
@@ -78,6 +78,11 @@ const relax_typeS md_relax_table[] = {
   {0x7fff, -0x8000, 3, 12},
   {0x7fffffff, -0x80000000, 5, 0},
 
+  /* fbCC relaxing  */
+  {0x7f, -0x80, 3, 14},
+  {0x7fff, -0x8000, 6, 15},
+  {0x7fffffff, -0x80000000, 8, 0},
+
 };
 
 /* Local functions.  */
@@ -89,10 +94,12 @@ static unsigned long check_operand PARAMS ((unsigned long,
                                            const struct mn10300_operand *,
                                            offsetT));
 static int reg_name_search PARAMS ((const struct reg_name *, int, const char *));
-static boolean data_register_name PARAMS ((expressionS *expressionP));
-static boolean address_register_name PARAMS ((expressionS *expressionP));
-static boolean other_register_name PARAMS ((expressionS *expressionP));
-static void set_arch_mach PARAMS ((unsigned int));
+static bfd_boolean data_register_name PARAMS ((expressionS *expressionP));
+static bfd_boolean address_register_name PARAMS ((expressionS *expressionP));
+static bfd_boolean other_register_name PARAMS ((expressionS *expressionP));
+static bfd_boolean r_register_name PARAMS ((expressionS *expressionP));
+static bfd_boolean xr_register_name PARAMS ((expressionS *expressionP));
+static void set_arch_mach PARAMS ((int));
 
 /*  Set linkrelax here to avoid fixups in most sections.  */
 int linkrelax = 1;
@@ -123,15 +130,15 @@ size_t md_longopts_size = sizeof (md_longopts);
 /* The target specific pseudo-ops which we support.  */
 const pseudo_typeS md_pseudo_table[] =
 {
-  { "file",     dwarf2_directive_file,  0 },
-  { "loc",      dwarf2_directive_loc,   0 },
   { "am30",    set_arch_mach,          AM30 },
   { "am33",    set_arch_mach,          AM33 },
+  { "am33_2",  (void (*) PARAMS ((int))) set_arch_mach, AM33_2 },
   { "mn10300", set_arch_mach,          MN103 },
   {NULL, 0, 0}
 };
 
-#define HAVE_AM33 (current_machine == AM33)
+#define HAVE_AM33_2 (current_machine == AM33_2)
+#define HAVE_AM33 (current_machine == AM33 || HAVE_AM33_2)
 #define HAVE_AM30 (current_machine == AM30)
 
 /* Opcode hash table.  */
@@ -212,7 +219,6 @@ static const struct reg_name xr_registers[] =
   { "mcrl", 3 },
   { "mcvf", 4 },
   { "mdrq", 1 },
-  { "pc", 0 },
   { "sp", 0 },
   { "xr0", 0 },
   { "xr1", 1 },
@@ -235,9 +241,16 @@ static const struct reg_name xr_registers[] =
 #define XR_REG_NAME_CNT                                        \
   (sizeof (xr_registers) / sizeof (struct reg_name))
 
+/* We abuse the `value' field, that would be otherwise unused, to
+   encode the architecture on which (access to) the register was
+   introduced.  FIXME: we should probably warn when we encounter a
+   register name when assembling for an architecture that doesn't
+   support it, before parsing it as a symbol name.  */
 static const struct reg_name other_registers[] =
 {
+  { "epsw", AM33 },
   { "mdr", 0 },
+  { "pc", AM33 },
   { "psw", 0 },
   { "sp", 0 },
 };
@@ -245,6 +258,69 @@ static const struct reg_name other_registers[] =
 #define OTHER_REG_NAME_CNT                             \
   (sizeof (other_registers) / sizeof (struct reg_name))
 
+static const struct reg_name float_registers[] =
+{
+  { "fs0", 0 },
+  { "fs1", 1 },
+  { "fs10", 10 },
+  { "fs11", 11 },
+  { "fs12", 12 },
+  { "fs13", 13 },
+  { "fs14", 14 },
+  { "fs15", 15 },
+  { "fs16", 16 },
+  { "fs17", 17 },
+  { "fs18", 18 },
+  { "fs19", 19 },
+  { "fs2",   2 },
+  { "fs20", 20 },
+  { "fs21", 21 },
+  { "fs22", 22 },
+  { "fs23", 23 },
+  { "fs24", 24 },
+  { "fs25", 25 },
+  { "fs26", 26 },
+  { "fs27", 27 },
+  { "fs28", 28 },
+  { "fs29", 29 },
+  { "fs3",   3 },
+  { "fs30", 30 },
+  { "fs31", 31 },
+  { "fs4",   4 },
+  { "fs5",   5 },
+  { "fs6",   6 },
+  { "fs7",   7 },
+  { "fs8",   8 },
+  { "fs9",   9 },
+};
+
+#define FLOAT_REG_NAME_CNT \
+  (sizeof (float_registers) / sizeof (struct reg_name))
+
+static const struct reg_name double_registers[] =
+{
+  { "fd0",   0 },
+  { "fd10", 10 },
+  { "fd12", 12 },
+  { "fd14", 14 },
+  { "fd16", 16 },
+  { "fd18", 18 },
+  { "fd2",   2 },
+  { "fd20", 20 },
+  { "fd22", 22 },
+  { "fd24", 24 },
+  { "fd26", 26 },
+  { "fd28", 28 },
+  { "fd30", 30 },
+  { "fd4",   4 },
+  { "fd6",   6 },
+  { "fd8",   8 },
+};
+
+#define DOUBLE_REG_NAME_CNT \
+  (sizeof (double_registers) / sizeof (struct reg_name))
+
+
 /* reg_name_search does a binary search of the given register table
    to see if "name" is a valid regiter name.  Returns the register
    number from the array on success, or -1 on failure.  */
@@ -287,7 +363,7 @@ reg_name_search (regs, regcount, name)
  *     its original state.
  */
 
-static boolean
+static bfd_boolean
 r_register_name (expressionP)
      expressionS *expressionP;
 {
@@ -315,12 +391,12 @@ r_register_name (expressionP)
       expressionP->X_add_symbol = NULL;
       expressionP->X_op_symbol = NULL;
 
-      return true;
+      return TRUE;
     }
 
   /* Reset the line as if we had not done anything.  */
   input_line_pointer = start;
-  return false;
+  return FALSE;
 }
 
 /* Summary of register_name().
@@ -334,7 +410,7 @@ r_register_name (expressionP)
  *     its original state.
  */
 
-static boolean
+static bfd_boolean
 xr_register_name (expressionP)
      expressionS *expressionP;
 {
@@ -362,12 +438,12 @@ xr_register_name (expressionP)
       expressionP->X_add_symbol = NULL;
       expressionP->X_op_symbol = NULL;
 
-      return true;
+      return TRUE;
     }
 
   /* Reset the line as if we had not done anything.  */
   input_line_pointer = start;
-  return false;
+  return FALSE;
 }
 
 /* Summary of register_name().
@@ -381,7 +457,7 @@ xr_register_name (expressionP)
  *     its original state.
  */
 
-static boolean
+static bfd_boolean
 data_register_name (expressionP)
      expressionS *expressionP;
 {
@@ -409,12 +485,12 @@ data_register_name (expressionP)
       expressionP->X_add_symbol = NULL;
       expressionP->X_op_symbol = NULL;
 
-      return true;
+      return TRUE;
     }
 
   /* Reset the line as if we had not done anything.  */
   input_line_pointer = start;
-  return false;
+  return FALSE;
 }
 
 /* Summary of register_name().
@@ -428,7 +504,7 @@ data_register_name (expressionP)
  *     its original state.
  */
 
-static boolean
+static bfd_boolean
 address_register_name (expressionP)
      expressionS *expressionP;
 {
@@ -456,12 +532,12 @@ address_register_name (expressionP)
       expressionP->X_add_symbol = NULL;
       expressionP->X_op_symbol = NULL;
 
-      return true;
+      return TRUE;
     }
 
   /* Reset the line as if we had not done anything.  */
   input_line_pointer = start;
-  return false;
+  return FALSE;
 }
 
 /* Summary of register_name().
@@ -475,7 +551,7 @@ address_register_name (expressionP)
  *     its original state.
  */
 
-static boolean
+static bfd_boolean
 other_register_name (expressionP)
      expressionS *expressionP;
 {
@@ -493,6 +569,102 @@ other_register_name (expressionP)
   /* Put back the delimiting char.  */
   *input_line_pointer = c;
 
+  /* Look to see if it's in the register table.  */
+  if (reg_number == 0
+      || (reg_number == AM33 && HAVE_AM33))
+    {
+      expressionP->X_op = O_register;
+      expressionP->X_add_number = 0;
+
+      /* Make the rest nice.  */
+      expressionP->X_add_symbol = NULL;
+      expressionP->X_op_symbol = NULL;
+
+      return TRUE;
+    }
+
+  /* Reset the line as if we had not done anything.  */
+  input_line_pointer = start;
+  return FALSE;
+}
+
+static bfd_boolean double_register_name PARAMS ((expressionS *));
+static bfd_boolean float_register_name  PARAMS ((expressionS *));
+
+/* Summary of float_register_name:
+
+   in: Input_line_pointer points to 1st char of operand.
+
+   out: A expressionS.
+       The operand may have been a register: in this case, X_op == O_register,
+       X_add_number is set to the register number, and truth is returned.
+       Input_line_pointer->(next non-blank) char after operand, or is in
+       its original state.  */
+
+static bfd_boolean
+float_register_name (expressionP)
+     expressionS *expressionP;
+{
+  int reg_number;
+  char *name;
+  char *start;
+  char c;
+
+  /* Find the spelling of the operand.  */
+  start = name = input_line_pointer;
+
+  c = get_symbol_end ();
+  reg_number = reg_name_search (float_registers, FLOAT_REG_NAME_CNT, name);
+
+  /* Put back the delimiting char.  */
+  * input_line_pointer = c;
+
+  /* Look to see if it's in the register table.  */
+  if (reg_number >= 0)
+    {
+      expressionP->X_op = O_register;
+      expressionP->X_add_number = reg_number;
+
+      /* Make the rest nice.  */
+      expressionP->X_add_symbol = NULL;
+      expressionP->X_op_symbol = NULL;
+
+      return TRUE;
+    }
+
+  /* Reset the line as if we had not done anything.  */
+  input_line_pointer = start;
+  return FALSE;
+}
+
+/* Summary of double_register_name:
+
+   in: Input_line_pointer points to 1st char of operand.
+
+   out: A expressionS.
+       The operand may have been a register: in this case, X_op == O_register,
+       X_add_number is set to the register number, and truth is returned.
+       Input_line_pointer->(next non-blank) char after operand, or is in
+       its original state.  */
+
+static bfd_boolean
+double_register_name (expressionP)
+     expressionS *expressionP;
+{
+  int reg_number;
+  char *name;
+  char *start;
+  char c;
+
+  /* Find the spelling of the operand.  */
+  start = name = input_line_pointer;
+
+  c = get_symbol_end ();
+  reg_number = reg_name_search (double_registers, DOUBLE_REG_NAME_CNT, name);
+
+  /* Put back the delimiting char.  */
+  * input_line_pointer = c;
+
   /* Look to see if it's in the register table.  */
   if (reg_number >= 0)
     {
@@ -503,12 +675,12 @@ other_register_name (expressionP)
       expressionP->X_add_symbol = NULL;
       expressionP->X_op_symbol = NULL;
 
-      return true;
+      return TRUE;
     }
 
   /* Reset the line as if we had not done anything.  */
   input_line_pointer = start;
-  return false;
+  return FALSE;
 }
 
 void
@@ -858,6 +1030,151 @@ md_convert_frag (abfd, sec, fragP)
       fragP->fr_var = 0;
       fragP->fr_fix += 5;
     }
+  else if (fragP->fr_subtype == 13)
+    {
+      fix_new (fragP, fragP->fr_fix + 2, 1, fragP->fr_symbol,
+              fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);
+      fragP->fr_var = 0;
+      fragP->fr_fix += 3;
+    }
+  else if (fragP->fr_subtype == 14)
+    {
+      /* Reverse the condition of the first branch.  */
+      int offset = fragP->fr_fix;
+      int opcode = fragP->fr_literal[offset + 1] & 0xff;
+
+      switch (opcode)
+       {
+       case 0xd0:
+         opcode = 0xd1;
+         break;
+       case 0xd1:
+         opcode = 0xd0;
+         break;
+       case 0xd2:
+         opcode = 0xdc;
+         break;
+       case 0xd3:
+         opcode = 0xdb;
+         break;
+       case 0xd4:
+         opcode = 0xda;
+         break;
+       case 0xd5:
+         opcode = 0xd9;
+         break;
+       case 0xd6:
+         opcode = 0xd8;
+         break;
+       case 0xd7:
+         opcode = 0xdd;
+         break;
+       case 0xd8:
+         opcode = 0xd6;
+         break;
+       case 0xd9:
+         opcode = 0xd5;
+         break;
+       case 0xda:
+         opcode = 0xd4;
+         break;
+       case 0xdb:
+         opcode = 0xd3;
+         break;
+       case 0xdc:
+         opcode = 0xd2;
+         break;
+       case 0xdd:
+         opcode = 0xd7;
+         break;
+       default:
+         abort ();
+       }
+      fragP->fr_literal[offset + 1] = opcode;
+
+      /* Create a fixup for the reversed conditional branch.  */
+      sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
+      fix_new (fragP, fragP->fr_fix + 2, 1,
+              symbol_new (buf, sec, 0, fragP->fr_next),
+              fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);
+
+      /* Now create the unconditional branch + fixup to the
+        final target.  */
+      fragP->fr_literal[offset + 3] = 0xcc;
+      fix_new (fragP, fragP->fr_fix + 4, 2, fragP->fr_symbol,
+              fragP->fr_offset + 1, 1, BFD_RELOC_16_PCREL);
+      fragP->fr_var = 0;
+      fragP->fr_fix += 6;
+    }
+  else if (fragP->fr_subtype == 15)
+    {
+      /* Reverse the condition of the first branch.  */
+      int offset = fragP->fr_fix;
+      int opcode = fragP->fr_literal[offset + 1] & 0xff;
+
+      switch (opcode)
+       {
+       case 0xd0:
+         opcode = 0xd1;
+         break;
+       case 0xd1:
+         opcode = 0xd0;
+         break;
+       case 0xd2:
+         opcode = 0xdc;
+         break;
+       case 0xd3:
+         opcode = 0xdb;
+         break;
+       case 0xd4:
+         opcode = 0xda;
+         break;
+       case 0xd5:
+         opcode = 0xd9;
+         break;
+       case 0xd6:
+         opcode = 0xd8;
+         break;
+       case 0xd7:
+         opcode = 0xdd;
+         break;
+       case 0xd8:
+         opcode = 0xd6;
+         break;
+       case 0xd9:
+         opcode = 0xd5;
+         break;
+       case 0xda:
+         opcode = 0xd4;
+         break;
+       case 0xdb:
+         opcode = 0xd3;
+         break;
+       case 0xdc:
+         opcode = 0xd2;
+         break;
+       case 0xdd:
+         opcode = 0xd7;
+         break;
+       default:
+         abort ();
+       }
+      fragP->fr_literal[offset + 1] = opcode;
+
+      /* Create a fixup for the reversed conditional branch.  */
+      sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
+      fix_new (fragP, fragP->fr_fix + 2, 1,
+              symbol_new (buf, sec, 0, fragP->fr_next),
+              fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);
+
+      /* Now create the unconditional branch + fixup to the
+        final target.  */
+      fragP->fr_literal[offset + 3] = 0xdc;
+      fix_new (fragP, fragP->fr_fix + 4, 4, fragP->fr_symbol,
+              fragP->fr_offset + 1, 1, BFD_RELOC_32_PCREL);
+      fragP->fr_var = 0;
+      fragP->fr_fix += 8;
+    }
   else
     abort ();
 }
@@ -896,10 +1213,151 @@ md_begin ()
     }
 
   /* Set the default machine type.  */
+#ifdef TE_LINUX
+  if (!bfd_set_arch_mach (stdoutput, bfd_arch_mn10300, AM33_2))
+    as_warn (_("could not set architecture and machine"));
+
+  current_machine = AM33_2;
+#else  
   if (!bfd_set_arch_mach (stdoutput, bfd_arch_mn10300, MN103))
     as_warn (_("could not set architecture and machine"));
 
   current_machine = MN103;
+#endif
+}
+
+static symbolS *GOT_symbol;
+
+static inline int mn10300_check_fixup PARAMS ((struct mn10300_fixup *));
+static inline int mn10300_PIC_related_p PARAMS ((symbolS *));
+
+static inline int
+mn10300_PIC_related_p (sym)
+     symbolS *sym;
+{
+  expressionS *exp;
+
+  if (! sym)
+    return 0;
+
+  if (sym == GOT_symbol)
+    return 1;
+
+  exp = symbol_get_value_expression (sym);
+
+  return (exp->X_op == O_PIC_reloc
+         || mn10300_PIC_related_p (exp->X_add_symbol)
+         || mn10300_PIC_related_p (exp->X_op_symbol));
+}
+
+static inline int
+mn10300_check_fixup (fixup)
+     struct mn10300_fixup *fixup;
+{
+  expressionS *exp = &fixup->exp;
+
+ repeat:
+  switch (exp->X_op)
+    {
+    case O_add:
+    case O_subtract: /* If we're sufficiently unlucky that the label
+                       and the expression that references it happen
+                       to end up in different frags, the subtract
+                       won't be simplified within expression().  */
+      /* The PIC-related operand must be the first operand of a sum.  */
+      if (exp != &fixup->exp || mn10300_PIC_related_p (exp->X_op_symbol))
+       return 1;
+
+      if (exp->X_add_symbol && exp->X_add_symbol == GOT_symbol)
+       fixup->reloc = BFD_RELOC_32_GOT_PCREL;
+
+      exp = symbol_get_value_expression (exp->X_add_symbol);
+      goto repeat;
+
+    case O_symbol:
+      if (exp->X_add_symbol && exp->X_add_symbol == GOT_symbol)
+       fixup->reloc = BFD_RELOC_32_GOT_PCREL;
+      break;
+
+    case O_PIC_reloc:
+      fixup->reloc = exp->X_md;
+      exp->X_op = O_symbol;
+      if (fixup->reloc == BFD_RELOC_32_PLT_PCREL
+         && fixup->opindex >= 0
+         && (mn10300_operands[fixup->opindex].flags
+             & MN10300_OPERAND_RELAX))
+       return 1;
+      break;
+
+    default:
+      return (mn10300_PIC_related_p (exp->X_add_symbol)
+             || mn10300_PIC_related_p (exp->X_op_symbol));
+    }
+
+  return 0;
+}
+
+void
+mn10300_cons_fix_new (frag, off, size, exp)
+     fragS *frag;
+     int off, size;
+     expressionS *exp;
+{
+  struct mn10300_fixup fixup;
+
+  fixup.opindex = -1;
+  fixup.exp = *exp;
+  fixup.reloc = BFD_RELOC_UNUSED;
+
+  mn10300_check_fixup (&fixup);
+
+  if (fixup.reloc == BFD_RELOC_MN10300_GOT32)
+    switch (size)
+      {
+      case 2:
+       fixup.reloc = BFD_RELOC_MN10300_GOT16;
+       break;
+
+      case 3:
+       fixup.reloc = BFD_RELOC_MN10300_GOT24;
+       break;
+
+      case 4:
+       break;
+
+      default:
+       goto error;
+      }
+  else if (fixup.reloc == BFD_RELOC_UNUSED)
+    switch (size)
+      {
+      case 1:
+       fixup.reloc = BFD_RELOC_8;
+       break;
+
+      case 2:
+       fixup.reloc = BFD_RELOC_16;
+       break;
+
+      case 3:
+       fixup.reloc = BFD_RELOC_24;
+       break;
+
+      case 4:
+       fixup.reloc = BFD_RELOC_32;
+       break;
+
+      default:
+       goto error;
+      }
+  else if (size != 4)
+    {
+    error:
+      as_bad (_("unsupported BFD relocation size %u"), size);
+      fixup.reloc = BFD_RELOC_UNUSED;
+    }
+    
+  fix_new_exp (frag, off, size, &fixup.exp, 0, fixup.reloc);
 }
 
 void
@@ -958,6 +1416,7 @@ md_assemble (str)
       /* If the instruction is not available on the current machine
         then it can not possibly match.  */
       if (opcode->machine
+         && !(opcode->machine == AM33_2 && HAVE_AM33_2)
          && !(opcode->machine == AM33 && HAVE_AM33)
          && !(opcode->machine == AM30 && HAVE_AM30))
        goto error;
@@ -1052,6 +1511,39 @@ md_assemble (str)
                  goto error;
                }
            }
+         else if (operand->flags & MN10300_OPERAND_FSREG)
+           {
+             if (!float_register_name (&ex))
+               {
+                 input_line_pointer = hold;
+                 str = hold;
+                 goto error;
+               }
+           }
+         else if (operand->flags & MN10300_OPERAND_FDREG)
+           {
+             if (!double_register_name (&ex))
+               {
+                 input_line_pointer = hold;
+                 str = hold;
+                 goto error;
+               }
+           }
+         else if (operand->flags & MN10300_OPERAND_FPCR)
+           {
+             char *start = input_line_pointer;
+             char c = get_symbol_end ();
+
+             if (strcasecmp (start, "fpcr") != 0)
+               {
+                 *input_line_pointer = c;
+                 input_line_pointer = hold;
+                 str = hold;
+                 goto error;
+               }
+             *input_line_pointer = c;
+             goto keep_going;
+           }
          else if (operand->flags & MN10300_OPERAND_USP)
            {
              char *start = input_line_pointer;
@@ -1291,6 +1783,18 @@ md_assemble (str)
              str = hold;
              goto error;
            }
+         else if (HAVE_AM33_2 && float_register_name (&ex))
+           {
+             input_line_pointer = hold;
+             str = hold;
+             goto error;
+           }
+         else if (HAVE_AM33_2 && double_register_name (&ex))
+           {
+             input_line_pointer = hold;
+             str = hold;
+             goto error;
+           }
          else if (*str == ')' || *str == '(')
            {
              input_line_pointer = hold;
@@ -1317,6 +1821,8 @@ md_assemble (str)
                mask = MN10300_OPERAND_DREG | MN10300_OPERAND_AREG;
                if (HAVE_AM33)
                  mask |= MN10300_OPERAND_RREG | MN10300_OPERAND_XRREG;
+               if (HAVE_AM33_2)
+                 mask |= MN10300_OPERAND_FSREG | MN10300_OPERAND_FDREG;
                if ((operand->flags & mask) == 0)
                  {
                    input_line_pointer = hold;
@@ -1383,6 +1889,8 @@ md_assemble (str)
              fixups[fc].exp = ex;
              fixups[fc].opindex = *opindex_ptr;
              fixups[fc].reloc = BFD_RELOC_UNUSED;
+             if (mn10300_check_fixup (& fixups[fc]))
+               goto error;
              ++fc;
              break;
            }
@@ -1484,6 +1992,9 @@ keep_going:
   if (opcode->format == FMT_D2)
     size = 4;
 
+  if (opcode->format == FMT_D3)
+    size = 5;
+
   if (opcode->format == FMT_D4)
     size = 6;
 
@@ -1522,6 +2033,8 @@ keep_going:
       /* jmp  */
       else if (size == 3 && opcode->opcode == 0xcc0000)
        type = 10;
+      else if (size == 3 && (opcode->opcode & 0xfff000) == 0xf8d000)
+       type = 13;
       /* bCC (uncommon cases)  */
       else
        type = 3;
@@ -1628,6 +2141,12 @@ keep_going:
             is really two 8bit immediates.  */
          number_to_chars_bigendian (f, insn, 4);
        }
+      else if (opcode->format == FMT_D3)
+       {
+         number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
+         number_to_chars_littleendian (f + 2, insn & 0xffff, 2);
+         number_to_chars_bigendian (f + 4, extension & 0xff, 1);
+       }
       else if (opcode->format == FMT_D4)
        {
          unsigned long temp = ((insn & 0xffff) << 16) | (extension & 0xffff);
@@ -1666,7 +2185,11 @@ keep_going:
          const struct mn10300_operand *operand;
 
          operand = &mn10300_operands[fixups[i].opindex];
-         if (fixups[i].reloc != BFD_RELOC_UNUSED)
+         if (fixups[i].reloc != BFD_RELOC_UNUSED
+             && fixups[i].reloc != BFD_RELOC_32_GOT_PCREL
+             && fixups[i].reloc != BFD_RELOC_32_GOTOFF
+             && fixups[i].reloc != BFD_RELOC_32_PLT_PCREL
+             && fixups[i].reloc != BFD_RELOC_MN10300_GOT32)
            {
              reloc_howto_type *reloc_howto;
              int size;
@@ -1696,6 +2219,8 @@ keep_going:
              fixS *fixP;
 
              reloc = BFD_RELOC_NONE;
+             if (fixups[i].reloc != BFD_RELOC_UNUSED)
+               reloc = fixups[i].reloc;
              /* How big is the reloc?  Remember SPLIT relocs are
                 implicitly 32bits.  */
              if ((operand->flags & MN10300_OPERAND_SPLIT) != 0)
@@ -1707,11 +2232,15 @@ keep_going:
 
              /* Is the reloc pc-relative?  */
              pcrel = (operand->flags & MN10300_OPERAND_PCREL) != 0;
+             if (reloc != BFD_RELOC_NONE)
+               pcrel = bfd_reloc_type_lookup (stdoutput, reloc)->pc_relative;
 
              offset = size - (reloc_size + operand->shift) / 8;
 
              /* Choose a proper BFD relocation type.  */
-             if (pcrel)
+             if (reloc != BFD_RELOC_NONE)
+               ;
+             else if (pcrel)
                {
                  if (reloc_size == 32)
                    reloc = BFD_RELOC_32_PCREL;
@@ -1777,6 +2306,13 @@ tc_gen_reloc (seg, fixp)
     }
   reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
 
+  if (fixp->fx_subsy
+      && S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
+    {
+      fixp->fx_offset -= S_GET_VALUE (fixp->fx_subsy);
+      fixp->fx_subsy = 0;
+    }
+
   if (fixp->fx_addsy && fixp->fx_subsy)
     {
       reloc->sym_ptr_ptr = NULL;
@@ -1799,7 +2335,7 @@ tc_gen_reloc (seg, fixp)
              reloc->howto = bfd_reloc_type_lookup (stdoutput,
                                                    BFD_RELOC_8_PCREL);
              return reloc;
-             
+
            case BFD_RELOC_16:
              reloc->howto = bfd_reloc_type_lookup (stdoutput,
                                                    BFD_RELOC_16_PCREL);
@@ -1839,7 +2375,7 @@ tc_gen_reloc (seg, fixp)
            case BFD_RELOC_8:
              md_number_to_chars (fixpos, reloc->addend, 1);
              break;
-             
+
            case BFD_RELOC_16:
              md_number_to_chars (fixpos, reloc->addend, 2);
              break;
@@ -1890,6 +2426,8 @@ md_estimate_size_before_relax (fragp, seg)
                || seg != S_GET_SEGMENT (fragp->fr_symbol)))
     fragp->fr_subtype = 12;
 
+  if (fragp->fr_subtype == 13)
+    return 3;
   if (fragp->fr_subtype >= sizeof (md_relax_table) / sizeof (md_relax_table[0]))
     abort ();
 
@@ -1983,39 +2521,14 @@ md_apply_fix3 (fixP, valP, seg)
     fixP->fx_done = 1;
 }
 
-/* Return nonzero if the fixup in FIXP will require a relocation,
-   even it if appears that the fixup could be completely handled
-   within GAS.  */
-
-int
-mn10300_force_relocation (fixp)
-     struct fix *fixp;
-{
-  if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
-      || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
-    return 1;
-
-  /* Do not adjust relocations involving symbols in code sections,
-     because it breaks linker relaxations.  This could be fixed in the
-     linker, but this fix is simpler, and it pretty much only affects
-     object size a little bit.  */
-  if ((S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_CODE)
-      && fixp->fx_subsy
-      && S_GET_SEGMENT (fixp->fx_addsy) == S_GET_SEGMENT (fixp->fx_subsy))
-    return 1;
-
-  return 0;
-}
-
 /* Return zero if the fixup in fixp should be left alone and not
    adjusted.  */
 
-boolean
+bfd_boolean
 mn10300_fix_adjustable (fixp)
      struct fix *fixp;
 {
-  /* Prevent all adjustments to global symbols.  */
-  if (S_IS_EXTERN (fixp->fx_addsy) || S_IS_WEAK (fixp->fx_addsy))
+  if (! TC_RELOC_RTSYM_LOC_FIXUP (fixp))
     return 0;
 
   if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
@@ -2096,6 +2609,51 @@ mn10300_insert_operand (insnp, extensionp, operand, val, file, line, shift)
       *extensionp |= ((val & ((1 << (24 - operand->bits)) - 1))
                      << operand->shift);
     }
+  else if ((operand->flags & (MN10300_OPERAND_FSREG | MN10300_OPERAND_FDREG)))
+    {
+      /* See devo/opcodes/m10300-opc.c just before #define FSM0 for an
+         explanation of these variables.  Note that FMT-implied shifts
+        are not taken into account for FP registers.  */
+      unsigned long mask_low, mask_high;
+      int shl_low, shr_high, shl_high;
+
+      switch (operand->bits)
+       {
+       case 5:
+         /* Handle regular FP registers.  */
+         if (operand->shift >= 0)
+           {
+             /* This is an `m' register.  */
+             shl_low = operand->shift;
+             shl_high = 8 + (8 & shl_low) + (shl_low & 4) / 4;
+           }
+         else
+           {
+             /* This is an `n' register.  */
+             shl_low = -operand->shift;
+             shl_high = shl_low / 4;
+           }
+
+         mask_low = 0x0f;
+         mask_high = 0x10;
+         shr_high = 4;
+         break;
+
+       case 3:
+         /* Handle accumulators.  */
+         shl_low = -operand->shift;
+         shl_high = 0;
+         mask_low = 0x03;
+         mask_high = 0x04;
+         shr_high = 2;
+         break;
+
+       default:
+         abort ();
+       }
+      *insnp |= ((((val & mask_high) >> shr_high) << shl_high)
+                | ((val & mask_low) << shl_low));
+    }
   else if ((operand->flags & MN10300_OPERAND_EXTENDED) == 0)
     {
       *insnp |= (((long) val & ((1 << operand->bits) - 1))
@@ -2158,10 +2716,94 @@ check_operand (insn, operand, val)
 
 static void
 set_arch_mach (mach)
-     unsigned int mach;
+     int mach;
 {
   if (!bfd_set_arch_mach (stdoutput, bfd_arch_mn10300, mach))
     as_warn (_("could not set architecture and machine"));
 
   current_machine = mach;
 }
+
+static inline char * mn10300_end_of_match PARAMS ((char *, char *));
+
+static inline char *
+mn10300_end_of_match (cont, what)
+     char *cont, *what;
+{
+  int len = strlen (what);
+
+  if (strncmp (cont, what, strlen (what)) == 0
+      && ! is_part_of_name (cont[len]))
+    return cont + len;
+
+  return NULL;
+}  
+
+int
+mn10300_parse_name (name, exprP, nextcharP)
+     char const *name;
+     expressionS *exprP;
+     char *nextcharP;
+{
+  char *next = input_line_pointer;
+  char *next_end;
+  int reloc_type;
+  segT segment;
+
+  exprP->X_op_symbol = NULL;
+
+  if (strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0)
+    {
+      if (! GOT_symbol)
+       GOT_symbol = symbol_find_or_make (name);
+
+      exprP->X_add_symbol = GOT_symbol;
+    no_suffix:
+      /* If we have an absolute symbol or a reg,
+        then we know its value now.  */
+      segment = S_GET_SEGMENT (exprP->X_add_symbol);
+      if (segment == absolute_section)
+       {
+         exprP->X_op = O_constant;
+         exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+         exprP->X_add_symbol = NULL;
+       }
+      else if (segment == reg_section)
+       {
+         exprP->X_op = O_register;
+         exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+         exprP->X_add_symbol = NULL;
+       }
+      else
+       {
+         exprP->X_op = O_symbol;
+         exprP->X_add_number = 0;
+       }
+
+      return 1;
+    }
+
+  exprP->X_add_symbol = symbol_find_or_make (name);
+  
+  if (*nextcharP != '@')
+    goto no_suffix;
+  else if ((next_end = mn10300_end_of_match (next + 1, "GOTOFF")))
+    reloc_type = BFD_RELOC_32_GOTOFF;
+  else if ((next_end = mn10300_end_of_match (next + 1, "GOT")))
+    reloc_type = BFD_RELOC_MN10300_GOT32;
+  else if ((next_end = mn10300_end_of_match (next + 1, "PLT")))
+    reloc_type = BFD_RELOC_32_PLT_PCREL;
+  else
+    goto no_suffix;
+
+  *input_line_pointer = *nextcharP;
+  input_line_pointer = next_end;
+  *nextcharP = *input_line_pointer;
+  *input_line_pointer = '\0';
+
+  exprP->X_op = O_PIC_reloc;
+  exprP->X_add_number = 0;
+  exprP->X_md = reloc_type;
+
+  return 1;
+}
This page took 0.035389 seconds and 4 git commands to generate.