]> oss.titaniummirror.com Git - msp430-binutils.git/blobdiff - gas/config/tc-cr16.c
Imported binutils-2.20
[msp430-binutils.git] / gas / config / tc-cr16.c
index 256feed3e336e34f0085db00a61664e397529922..1ed74cda44238606f4d08b95b14874fec65038ba 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-cr16.c -- Assembler code for the CR16 CPU core.
-   Copyright 2007 Free Software Foundation, Inc.
+   Copyright 2007, 2008, 2009 Free Software Foundation, Inc.
 
    Contributed by M R Swami Reddy <MR.Swami.Reddy@nsc.com>
 
@@ -99,6 +99,11 @@ const char EXP_CHARS[] = "eE";
 /* Chars that mean this number is a floating point constant as in 0f12.456  */
 const char FLT_CHARS[] = "f'";
 
+#ifdef OBJ_ELF
+/* Pre-defined "_GLOBAL_OFFSET_TABLE_"  */
+symbolS * GOT_symbol;
+#endif
+
 /* Target-specific multicharacter options, not const-declared at usage.  */
 const char *md_shortopts = "";
 struct option md_longopts[] =
@@ -141,92 +146,92 @@ l_cons (int nbytes)
       expression (&exp);
 
       if (*input_line_pointer == ':')
-       {
-         /* Bitfields.  */
-         long value = 0;
+        {
+          /* Bitfields.  */
+          long value = 0;
 
-         for (;;)
-           {
-             unsigned long width;
-
-             if (*input_line_pointer != ':')
-               {
-                 input_line_pointer = hold;
-                 break;
-               }
-             if (exp.X_op == O_absent)
-               {
-                 as_warn (_("using a bit field width of zero"));
-                 exp.X_add_number = 0;
-                 exp.X_op = O_constant;
-               }
-
-             if (exp.X_op != O_constant)
-               {
-                 *input_line_pointer = '\0';
-                 as_bad (_("field width \"%s\" too complex for a bitfield"), hold);
-                 *input_line_pointer = ':';
-                 demand_empty_rest_of_line ();
-                 return;
-               }
-
-             if ((width = exp.X_add_number) >
-                 (unsigned int)(BITS_PER_CHAR * nbytes))
-               {
-                 as_warn (_("field width %lu too big to fit in %d bytes: truncated to %d bits"), width, nbytes, (BITS_PER_CHAR * nbytes));
-                 width = BITS_PER_CHAR * nbytes;
-               }                   /* Too big.  */
-
-
-             if (width > bits_available)
-               {
-                 /* FIXME-SOMEDAY: backing up and reparsing is wasteful.  */
-                 input_line_pointer = hold;
-                 exp.X_add_number = value;
-                 break;
-               }
-
-             /* Skip ':'.  */
-             hold = ++input_line_pointer;
-
-             expression (&exp);
-             if (exp.X_op != O_constant)
-               {
-                 char cache = *input_line_pointer;
-
-                 *input_line_pointer = '\0';
-                 as_bad (_("field value \"%s\" too complex for a bitfield"), hold);
-                 *input_line_pointer = cache;
-                 demand_empty_rest_of_line ();
-                 return;
-               }
-
-             value |= ((~(-1 << width) & exp.X_add_number)
-                       << ((BITS_PER_CHAR * nbytes) - bits_available));
-
-             if ((bits_available -= width) == 0
-                 || is_it_end_of_statement ()
-                 || *input_line_pointer != ',')
-               break;
-
-             hold = ++input_line_pointer;
-             expression (&exp);
-           }
+          for (;;)
+            {
+              unsigned long width;
 
-         exp.X_add_number = value;
-         exp.X_op = O_constant;
-         exp.X_unsigned = 1;
-       }
+              if (*input_line_pointer != ':')
+                {
+                  input_line_pointer = hold;
+                  break;
+                }
+              if (exp.X_op == O_absent)
+                {
+                  as_warn (_("using a bit field width of zero"));
+                  exp.X_add_number = 0;
+                  exp.X_op = O_constant;
+                }
+
+              if (exp.X_op != O_constant)
+                {
+                  *input_line_pointer = '\0';
+                  as_bad (_("field width \"%s\" too complex for a bitfield"), hold);
+                  *input_line_pointer = ':';
+                  demand_empty_rest_of_line ();
+                  return;
+                }
+
+              if ((width = exp.X_add_number) >
+                  (unsigned int)(BITS_PER_CHAR * nbytes))
+                {
+                  as_warn (_("field width %lu too big to fit in %d bytes: truncated to %d bits"), width, nbytes, (BITS_PER_CHAR * nbytes));
+                  width = BITS_PER_CHAR * nbytes;
+                }                   /* Too big.  */
+
+
+              if (width > bits_available)
+                {
+                  /* FIXME-SOMEDAY: backing up and reparsing is wasteful.  */
+                  input_line_pointer = hold;
+                  exp.X_add_number = value;
+                  break;
+                }
+
+              /* Skip ':'.  */
+              hold = ++input_line_pointer;
+
+              expression (&exp);
+              if (exp.X_op != O_constant)
+                {
+                  char cache = *input_line_pointer;
+
+                  *input_line_pointer = '\0';
+                  as_bad (_("field value \"%s\" too complex for a bitfield"), hold);
+                  *input_line_pointer = cache;
+                  demand_empty_rest_of_line ();
+                  return;
+                }
+
+              value |= ((~(-1 << width) & exp.X_add_number)
+                        << ((BITS_PER_CHAR * nbytes) - bits_available));
+
+              if ((bits_available -= width) == 0
+                  || is_it_end_of_statement ()
+                  || *input_line_pointer != ',')
+                break;
+
+              hold = ++input_line_pointer;
+              expression (&exp);
+            }
+
+          exp.X_add_number = value;
+          exp.X_op = O_constant;
+          exp.X_unsigned = 1;
+        }
 
       if ((*(input_line_pointer) == '@') && (*(input_line_pointer +1) == 'c'))
-       code_label = 1;
+        code_label = 1;
       emit_expr (&exp, (unsigned int) nbytes);
       ++c;
       if ((*(input_line_pointer) == '@') && (*(input_line_pointer +1) == 'c'))
-       {
-         input_line_pointer +=3;
-         break;
-       }
+        {
+          input_line_pointer +=3;
+          break;
+        }
     }
   while ((*input_line_pointer++ == ','));
 
@@ -236,7 +241,6 @@ l_cons (int nbytes)
   demand_empty_rest_of_line ();
 }
 
-
 /* This table describes all the machine specific pseudo-ops
    the assembler has to support.  The fields are:
    *** Pseudo-op name without dot.
@@ -248,6 +252,7 @@ const pseudo_typeS md_pseudo_table[] =
   /* In CR16 machine, align is in bytes (not a ptwo boundary).  */
   {"align", s_align_bytes, 0},
   {"long", l_cons,  4 },
+  {"4byte", l_cons, 4 },
   {0, 0, 0}
 };
 
@@ -255,7 +260,7 @@ const pseudo_typeS md_pseudo_table[] =
 const relax_typeS md_relax_table[] =
 {
   /* bCC  */
-  {0xfa, -0x100, 2, 1},                 /*  8 */
+  {0x7f, -0x80, 2, 1},                  /*  8 */
   {0xfffe, -0x10000, 4, 2},             /* 16 */
   {0xfffffe, -0x1000000, 6, 0},         /* 24 */
 };
@@ -373,8 +378,8 @@ get_index_register_pair (char *reg_name)
   if (reg != NULL)
     {
       if ((reg->value.reg_val != 1) || (reg->value.reg_val != 7)
-         || (reg->value.reg_val != 9) || (reg->value.reg_val > 10))
-       return reg->value.reg_val;
+          || (reg->value.reg_val != 9) || (reg->value.reg_val > 10))
+        return reg->value.reg_val;
 
       as_bad (_("Unknown register pair - index relative mode: `%d'"), reg->value.reg_val);
     }
@@ -478,9 +483,7 @@ reset_vars (char *op)
 int
 cr16_force_relocation (fixS *fix)
 {
-  /* REVISIT: Check if the "SWITCH_TABLE (fix)" should be added
-     if (generic_force_reloc (fix) || SWITCH_TABLE (fix))  */
-  if (generic_force_reloc (fix))
+  if (generic_force_reloc (fix) || SWITCH_TABLE (fix))
     return 1;
 
   return 0;
@@ -491,7 +494,8 @@ cr16_force_relocation (fixS *fix)
 void
 cr16_cons_fix_new (fragS *frag, int offset, int len, expressionS *exp)
 {
-  int rtype;
+  int rtype = BFD_RELOC_UNUSED;
+
   switch (len)
     {
     default: rtype = BFD_RELOC_NONE; break;
@@ -499,12 +503,12 @@ cr16_cons_fix_new (fragS *frag, int offset, int len, expressionS *exp)
     case 2: rtype = BFD_RELOC_CR16_NUM16; break;
     case 4:
       if (code_label)
-       {
-         rtype = BFD_RELOC_CR16_NUM32a;
-         code_label = 0;
-       }
+        {
+          rtype = BFD_RELOC_CR16_NUM32a;
+          code_label = 0;
+        }
       else
-       rtype = BFD_RELOC_CR16_NUM32;
+        rtype = BFD_RELOC_CR16_NUM32;
       break;
     }
 
@@ -517,6 +521,14 @@ arelent *
 tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS * fixP)
 {
   arelent * reloc;
+  bfd_reloc_code_real_type code;
+
+  /* If symbols are local and resolved, then no relocation needed.  */
+  if ( ((fixP->fx_addsy) 
+        && (S_GET_SEGMENT (fixP->fx_addsy) == absolute_section))
+       || ((fixP->fx_subsy) 
+          && (S_GET_SEGMENT (fixP->fx_subsy) == absolute_section)))
+     return NULL;
 
   reloc = xmalloc (sizeof (arelent));
   reloc->sym_ptr_ptr  = xmalloc (sizeof (asymbol *));
@@ -534,21 +546,21 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS * fixP)
 
           switch (fixP->fx_r_type)
             {
-           case BFD_RELOC_CR16_NUM8:
-             fixP->fx_r_type = BFD_RELOC_CR16_NUM8;
-             break;
-           case BFD_RELOC_CR16_NUM16:
-             fixP->fx_r_type = BFD_RELOC_CR16_NUM16;
-             break;
-           case BFD_RELOC_CR16_NUM32:
-             fixP->fx_r_type = BFD_RELOC_CR16_NUM32;
-             break;
-           case BFD_RELOC_CR16_NUM32a:
-             fixP->fx_r_type = BFD_RELOC_CR16_NUM32a;
-             break;
-           default:
-             abort ();
-             break;
+            case BFD_RELOC_CR16_NUM8:
+              fixP->fx_r_type = BFD_RELOC_CR16_SWITCH8;
+              break;
+            case BFD_RELOC_CR16_NUM16:
+              fixP->fx_r_type = BFD_RELOC_CR16_SWITCH16;
+              break;
+            case BFD_RELOC_CR16_NUM32:
+              fixP->fx_r_type = BFD_RELOC_CR16_SWITCH32;
+              break;
+            case BFD_RELOC_CR16_NUM32a:
+              fixP->fx_r_type = BFD_RELOC_CR16_NUM32a;
+              break;
+            default:
+              abort ();
+              break;
             }
         }
       else
@@ -564,8 +576,24 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS * fixP)
                         segment_name (S_GET_SEGMENT (fixP->fx_addsy)));
         }
     }
+#ifdef OBJ_ELF
+      if ((fixP->fx_r_type == BFD_RELOC_CR16_GOT_REGREL20)
+           && GOT_symbol
+          && fixP->fx_addsy == GOT_symbol)
+       {
+           code = BFD_RELOC_CR16_GOT_REGREL20;
+           reloc->addend = fixP->fx_offset = reloc->address;
+       }
+      else if ((fixP->fx_r_type == BFD_RELOC_CR16_GOTC_REGREL20)
+           && GOT_symbol
+          && fixP->fx_addsy == GOT_symbol)
+       {
+           code = BFD_RELOC_CR16_GOTC_REGREL20;
+           reloc->addend = fixP->fx_offset = reloc->address;
+       }
+#endif
 
-  assert ((int) fixP->fx_r_type > 0);
+  gas_assert ((int) fixP->fx_r_type > 0);
   reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
 
   if (reloc->howto == NULL)
@@ -576,7 +604,7 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS * fixP)
                     bfd_get_reloc_code_name (fixP->fx_r_type));
       return NULL;
     }
-  assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
+  gas_assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
 
   return reloc;
 }
@@ -613,10 +641,41 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, fragS *fragP)
 {
   /* 'opcode' points to the start of the instruction, whether
      we need to change the instruction's fixed encoding.  */
-  bfd_reloc_code_real_type reloc = BFD_RELOC_NONE;
+  char *opcode = fragP->fr_literal + fragP->fr_fix;
+  bfd_reloc_code_real_type reloc;
 
   subseg_change (sec, 0);
 
+  switch (fragP->fr_subtype)
+    {
+    case 0:
+      reloc = BFD_RELOC_CR16_DISP8;
+      break;
+    case 1:
+      /* If the subtype is not changed due to :m operand qualifier,
+         then no need to update the opcode value.  */
+      if ((int)opcode[1] != 0x18)
+        {
+          opcode[0] = (opcode[0] & 0xf0);
+          opcode[1] = 0x18;
+        }
+      reloc = BFD_RELOC_CR16_DISP16;
+      break;
+    case 2:
+      /* If the subtype is not changed due to :l operand qualifier,
+         then no need to update the opcode value.  */
+      if ((int)opcode[1] != 0)
+        {
+          opcode[2] = opcode[0];
+          opcode[0] = opcode[1];
+          opcode[1] = 0x0;
+        }
+      reloc = BFD_RELOC_CR16_DISP24;
+      break;
+    default:
+      abort();
+    }
+
   fix_new (fragP, fragP->fr_fix,
            bfd_get_reloc_size (bfd_reloc_type_lookup (stdoutput, reloc)),
            fragP->fr_symbol, fragP->fr_offset, 1, reloc);
@@ -624,6 +683,24 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, fragS *fragP)
   fragP->fr_fix += md_relax_table[fragP->fr_subtype].rlx_length;
 }
 
+symbolS *
+md_undefined_symbol (char *name)
+{
+  if (*name == '_' && *(name + 1) == 'G'
+      && strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0)
+   {
+     if (!GOT_symbol)
+       {
+         if (symbol_find (name))
+             as_bad (_("GOT already in symbol table"));
+          GOT_symbol = symbol_new (name, undefined_section,
+                                   (valueT) 0, &zero_address_frag);
+       }
+     return GOT_symbol;
+   }
+  return 0;
+}
+
 /* Process machine-dependent command line options.  Called once for
    each option on the command line that the machine-independent part of
    GAS does not understand.  */
@@ -642,58 +719,10 @@ md_show_usage (FILE *stream ATTRIBUTE_UNUSED)
   return;
 }
 
-/* Turn a string in input_line_pointer into a floating point constant
-   of type TYPE, and store the appropriate bytes in *LITP.  The number
-   of LITTLENUMS emitted is stored in *SIZEP.  An error message is
-   returned, or NULL on OK.  */
-
 char *
 md_atof (int type, char *litP, int *sizeP)
 {
-  int prec;
-  int i;
-  LITTLENUM_TYPE words[4];
-  char *t;
-
-  switch (type)
-    {
-      case 'f':
-        prec = 2;
-        break;
-
-      case 'd':
-        prec = 4;
-        break;
-
-      default:
-        *sizeP = 0;
-        return _("bad call to md_atof");
-    }
-
-  t = atof_ieee (input_line_pointer, type, words);
-  if (t)
-    input_line_pointer = t;
-
-  *sizeP = prec * 2;
-
-  if (! target_big_endian)
-    {
-      for (i = prec - 1; i >= 0; i--)
-        {
-          md_number_to_chars (litP, (valueT) words[i], 2);
-          litP += 2;
-        }
-    }
-  else
-    {
-      for (i = 0; i < prec; i++)
-        {
-          md_number_to_chars (litP, (valueT) words[i], 2);
-          litP += 2;
-        }
-    }
-
-  return NULL;
+  return ieee_md_atof (type, litP, sizeP, target_big_endian);
 }
 
 /* Apply a fixS (fixup of an instruction or data that we didn't have
@@ -706,39 +735,52 @@ void
 md_apply_fix (fixS *fixP, valueT *valP, segT seg)
 {
   valueT val = * valP;
-  char *buf = fixP->fx_frag->fr_literal + fixP->fx_where;
-  fixP->fx_offset = 0;
-
-  switch (fixP->fx_r_type)
-    {
-      case BFD_RELOC_CR16_NUM8:
-        bfd_put_8 (stdoutput, (unsigned char) val, buf);
-        break;
-      case BFD_RELOC_CR16_NUM16:
-        bfd_put_16 (stdoutput, val, buf);
-        break;
-      case BFD_RELOC_CR16_NUM32:
-        bfd_put_32 (stdoutput, val, buf);
-        break;
-      case BFD_RELOC_CR16_NUM32a:
-        bfd_put_32 (stdoutput, val, buf);
-        break;
-      default:
-        /* We shouldn't ever get here because linkrelax is nonzero.  */
-        abort ();
-        break;
-    }
-
-  fixP->fx_done = 0;
 
   if (fixP->fx_addsy == NULL
       && fixP->fx_pcrel == 0)
     fixP->fx_done = 1;
-
-  if (fixP->fx_pcrel == 1
+  else if (fixP->fx_pcrel == 1
       && fixP->fx_addsy != NULL
       && S_GET_SEGMENT (fixP->fx_addsy) == seg)
     fixP->fx_done = 1;
+  else
+    fixP->fx_done = 0;
+
+  if (fixP->fx_addsy != NULL && !fixP->fx_pcrel)
+    {
+      val = fixP->fx_offset;
+      fixP->fx_done = 1;
+    }
+
+  if (fixP->fx_done)
+    {
+      char *buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+      fixP->fx_offset = 0;
+
+      switch (fixP->fx_r_type)
+       {
+       case BFD_RELOC_CR16_NUM8:
+         bfd_put_8 (stdoutput, (unsigned char) val, buf);
+         break;
+       case BFD_RELOC_CR16_NUM16:
+         bfd_put_16 (stdoutput, val, buf);
+         break;
+       case BFD_RELOC_CR16_NUM32:
+         bfd_put_32 (stdoutput, val, buf);
+         break;
+       case BFD_RELOC_CR16_NUM32a:
+         bfd_put_32 (stdoutput, val, buf);
+         break;
+       default:
+         /* We shouldn't ever get here because linkrelax is nonzero.  */
+         abort ();
+         break;
+       }
+      fixP->fx_done = 0;
+    }
+  else
+    fixP->fx_offset = * valP;
 }
 
 /* The location from which a PC relative jump should be calculated,
@@ -752,8 +794,8 @@ md_pcrel_from (fixS *fixp)
 
 static void
 initialise_reg_hash_table (struct hash_control ** hash_table,
-                          const reg_entry * register_table,
-                          const unsigned int num_entries)
+                           const reg_entry * register_table,
+                           const unsigned int num_entries)
 {
   const reg_entry * reg;
   const char *hashret;
@@ -767,8 +809,8 @@ initialise_reg_hash_table (struct hash_control ** hash_table,
     {
       hashret = hash_insert (* hash_table, reg->name, (char *) reg);
       if (hashret)
-       as_fatal (_("Internal Error:  Can't hash %s: %s"),
-                 reg->name, hashret);
+        as_fatal (_("Internal Error:  Can't hash %s: %s"),
+                  reg->name, hashret);
     }
 }
 
@@ -790,7 +832,7 @@ md_begin (void)
       const char *mnemonic = cr16_instruction[i].mnemonic;
 
       hashret = hash_insert (cr16_inst_hash, mnemonic,
-                            (char *)(cr16_instruction + i));
+                             (char *)(cr16_instruction + i));
 
       if (hashret != NULL && *hashret != '\0')
         as_fatal (_("Can't hash `%s': %s\n"), cr16_instruction[i].mnemonic,
@@ -832,6 +874,8 @@ process_label_constant (char *str, ins * cr16_ins)
   int symbol_with_s = 0;
   int symbol_with_m = 0;
   int symbol_with_l = 0;
+  int symbol_with_at_got = 0;
+  int symbol_with_at_gotc = 0;
   argument *cur_arg = cr16_ins->arg + cur_arg_num;  /* Current argument.  */
 
   saved_input_line_pointer = input_line_pointer;
@@ -845,7 +889,7 @@ process_label_constant (char *str, ins * cr16_ins)
     case O_absent:
       /* Missing or bad expr becomes absolute 0.  */
       as_bad (_("missing or invalid displacement expression `%s' taken as 0"),
-             str);
+              str);
       cr16_ins->exp.X_op = O_constant;
       cr16_ins->exp.X_add_number = 0;
       cr16_ins->exp.X_add_symbol = NULL;
@@ -861,115 +905,172 @@ process_label_constant (char *str, ins * cr16_ins)
     case O_subtract:
     case O_add:
       cur_arg->X_op = O_symbol;
+      cur_arg->constant = cr16_ins->exp.X_add_number;
+      cr16_ins->exp.X_add_number = 0;
       cr16_ins->rtype = BFD_RELOC_NONE;
       relocatable = 1;
 
       if (strneq (input_line_pointer, "@c", 2))
-       symbol_with_at = 1;
+        symbol_with_at = 1;
 
       if (strneq (input_line_pointer, "@l", 2)
-         || strneq (input_line_pointer, ":l", 2))
-       symbol_with_l = 1;
+          || strneq (input_line_pointer, ":l", 2))
+        symbol_with_l = 1;
 
       if (strneq (input_line_pointer, "@m", 2)
-         || strneq (input_line_pointer, ":m", 2))
-       symbol_with_m = 1;
+          || strneq (input_line_pointer, ":m", 2))
+        symbol_with_m = 1;
 
       if (strneq (input_line_pointer, "@s", 2)
-         || strneq (input_line_pointer, ":s", 2))
-       symbol_with_s = 1;
+          || strneq (input_line_pointer, ":s", 2))
+        symbol_with_s = 1;
 
-      switch (cur_arg->type)
-        {
-       case arg_cr:
-         if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (CSTBIT_INS))
-           {
-             if (cur_arg->size == 20)
-               cr16_ins->rtype = BFD_RELOC_CR16_REGREL20;
-             else
-               cr16_ins->rtype = BFD_RELOC_CR16_REGREL20a;
-           }
-         break;
+      if (strneq (input_line_pointer, "@cGOT", 5)
+          || strneq (input_line_pointer, "@cgot", 5))
+       {
+         if (GOT_symbol == NULL)
+           GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
 
-       case arg_crp:
-         if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (CSTBIT_INS))
-           switch (instruction->size)
-             {
-             case 1:
-               switch (cur_arg->size)
-                 {
-                 case 0:
-                   cr16_ins->rtype = BFD_RELOC_CR16_REGREL0;
-                   break;
-                 case 4:
-                   if (IS_INSN_MNEMONIC ("loadb") || IS_INSN_MNEMONIC ("storb"))
-                     cr16_ins->rtype = BFD_RELOC_CR16_REGREL4;
-                   else
-                     cr16_ins->rtype = BFD_RELOC_CR16_REGREL4a;
-                   break;
-                 default: break;
-                 }
-               break;
-             case 2:
-               cr16_ins->rtype = BFD_RELOC_CR16_REGREL16;
-               break;
-             case 3:
-               if (cur_arg->size == 20)
-                 cr16_ins->rtype = BFD_RELOC_CR16_REGREL20;
-               else
-                 cr16_ins->rtype = BFD_RELOC_CR16_REGREL20a;
-               break;
-             default:
-               break;
-             }
-         break;
+          symbol_with_at_gotc = 1;
+       }
+      else if (strneq (input_line_pointer, "@GOT", 4)
+          || strneq (input_line_pointer, "@got", 4))
+       {
+          if ((strneq (input_line_pointer, "+", 1)) 
+              || (strneq (input_line_pointer, "-", 1)))
+           as_warn (_("GOT bad expression with %s."), input_line_pointer);
 
-       case arg_idxr:
-         if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (CSTBIT_INS))
-           cr16_ins->rtype = BFD_RELOC_CR16_REGREL20;
-         break;
+         if (GOT_symbol == NULL)
+           GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
 
-       case arg_idxrp:
-         if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (CSTBIT_INS))
-           switch (instruction->size)
-             {
-             case 1: cr16_ins->rtype = BFD_RELOC_CR16_REGREL0; break;
-             case 2: cr16_ins->rtype = BFD_RELOC_CR16_REGREL14; break;
-             case 3: cr16_ins->rtype = BFD_RELOC_CR16_REGREL20; break;
-             default: break;
-             }
-         break;
+          symbol_with_at_got = 1;
+       }
+
+      switch (cur_arg->type)
+        {
+        case arg_cr:
+          if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (CSTBIT_INS))
+            {
+             if (symbol_with_at_got)
+                 cr16_ins->rtype = BFD_RELOC_CR16_GOT_REGREL20;
+             else if (symbol_with_at_gotc)
+                 cr16_ins->rtype = BFD_RELOC_CR16_GOTC_REGREL20;
+             else if (cur_arg->size == 20)
+                cr16_ins->rtype = BFD_RELOC_CR16_REGREL20;
+              else
+                cr16_ins->rtype = BFD_RELOC_CR16_REGREL20a;
+            }
+          break;
 
-       case arg_c:
-         if (IS_INSN_MNEMONIC ("bal"))
-           cr16_ins->rtype = BFD_RELOC_CR16_DISP24;
-         else if (IS_INSN_TYPE (BRANCH_INS))
+        case arg_crp:
+          if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (CSTBIT_INS))
+          {
+           if (symbol_with_at_got)
+             cr16_ins->rtype = BFD_RELOC_CR16_GOT_REGREL20;
+           else if (symbol_with_at_gotc)
+             cr16_ins->rtype = BFD_RELOC_CR16_GOTC_REGREL20;
+          } else {
+            switch (instruction->size)
+              {
+              case 1:
+                switch (cur_arg->size)
+                  {
+                  case 0:
+                    cr16_ins->rtype = BFD_RELOC_CR16_REGREL0;
+                    break;
+                  case 4:
+                    if (IS_INSN_MNEMONIC ("loadb") || IS_INSN_MNEMONIC ("storb"))
+                      cr16_ins->rtype = BFD_RELOC_CR16_REGREL4;
+                    else
+                      cr16_ins->rtype = BFD_RELOC_CR16_REGREL4a;
+                    break;
+                  default: break;
+                  }
+                break;
+              case 2:
+                cr16_ins->rtype = BFD_RELOC_CR16_REGREL16;
+                break;
+              case 3:
+                if (cur_arg->size == 20)
+                  cr16_ins->rtype = BFD_RELOC_CR16_REGREL20;
+                else
+                  cr16_ins->rtype = BFD_RELOC_CR16_REGREL20a;
+                break;
+              default:
+                break;
+              }
+           }
+          break;
+
+        case arg_idxr:
+          if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (CSTBIT_INS))
            {
-             if (symbol_with_s)
-               cr16_ins->rtype = BFD_RELOC_CR16_DISP8;
-             else if (symbol_with_m)
-               cr16_ins->rtype = BFD_RELOC_CR16_DISP16;
+             if (symbol_with_at_got)
+               cr16_ins->rtype = BFD_RELOC_CR16_GOT_REGREL20;
+             else if (symbol_with_at_gotc)
+               cr16_ins->rtype = BFD_RELOC_CR16_GOTC_REGREL20;
              else
-               cr16_ins->rtype = BFD_RELOC_CR16_DISP24;
+                cr16_ins->rtype = BFD_RELOC_CR16_REGREL20;
            }
-         else if (IS_INSN_TYPE (STOR_IMM_INS) || IS_INSN_TYPE (LD_STOR_INS)
-                  || IS_INSN_TYPE (CSTBIT_INS))
+          break;
+
+        case arg_idxrp:
+          if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (CSTBIT_INS))
            {
-             if (symbol_with_s)
-               as_bad (_("operand %d: illegal use expression: `%s`"), cur_arg_num + 1, str);
-             if (symbol_with_m)
-               cr16_ins->rtype = BFD_RELOC_CR16_ABS20;
-             else /* Default to (symbol_with_l) */
-               cr16_ins->rtype = BFD_RELOC_CR16_ABS24;
+           if (symbol_with_at_got)
+             cr16_ins->rtype = BFD_RELOC_CR16_GOT_REGREL20;
+           else if (symbol_with_at_gotc)
+             cr16_ins->rtype = BFD_RELOC_CR16_GOTC_REGREL20;
+           else {
+            switch (instruction->size)
+              {
+              case 1: cr16_ins->rtype = BFD_RELOC_CR16_REGREL0; break;
+              case 2: cr16_ins->rtype = BFD_RELOC_CR16_REGREL14; break;
+              case 3: cr16_ins->rtype = BFD_RELOC_CR16_REGREL20; break;
+              default: break;
+              }
            }
-         else if (IS_INSN_TYPE (BRANCH_NEQ_INS))
-           cr16_ins->rtype = BFD_RELOC_CR16_DISP4;
+          }
+          break;
+
+        case arg_c:
+          if (IS_INSN_MNEMONIC ("bal"))
+            cr16_ins->rtype = BFD_RELOC_CR16_DISP24;
+          else if (IS_INSN_TYPE (BRANCH_INS))
+            {
+              if (symbol_with_l)
+                cr16_ins->rtype = BFD_RELOC_CR16_DISP24;
+              else if (symbol_with_m)
+                cr16_ins->rtype = BFD_RELOC_CR16_DISP16;
+              else
+                cr16_ins->rtype = BFD_RELOC_CR16_DISP8;
+            }
+          else if (IS_INSN_TYPE (STOR_IMM_INS) || IS_INSN_TYPE (LD_STOR_INS)
+                   || IS_INSN_TYPE (CSTBIT_INS))
+            {
+             if (symbol_with_s)
+                as_bad (_("operand %d: illegal use expression: `%s`"), cur_arg_num + 1, str);
+             if (symbol_with_at_got)
+               cr16_ins->rtype = BFD_RELOC_CR16_GOT_REGREL20;
+             else if (symbol_with_at_gotc)
+               cr16_ins->rtype = BFD_RELOC_CR16_GOTC_REGREL20;
+             else if (symbol_with_m)
+                cr16_ins->rtype = BFD_RELOC_CR16_ABS20;
+              else /* Default to (symbol_with_l) */
+                cr16_ins->rtype = BFD_RELOC_CR16_ABS24;
+            }
+          else if (IS_INSN_TYPE (BRANCH_NEQ_INS))
+            cr16_ins->rtype = BFD_RELOC_CR16_DISP4;
           break;
 
         case arg_ic:
           if (IS_INSN_TYPE (ARITH_INS))
             {
-              if (symbol_with_s)
+             if (symbol_with_at_got)
+               cr16_ins->rtype = BFD_RELOC_CR16_GOT_REGREL20;
+             else if (symbol_with_at_gotc)
+               cr16_ins->rtype = BFD_RELOC_CR16_GOTC_REGREL20;
+             else if (symbol_with_s)
                 cr16_ins->rtype = BFD_RELOC_CR16_IMM4;
               else if (symbol_with_m)
                 cr16_ins->rtype = BFD_RELOC_CR16_IMM20;
@@ -979,13 +1080,13 @@ process_label_constant (char *str, ins * cr16_ins)
                 cr16_ins->rtype = BFD_RELOC_CR16_IMM32;
             }
           else if (IS_INSN_TYPE (ARITH_BYTE_INS))
-           {
-             cr16_ins->rtype = BFD_RELOC_CR16_IMM16;
-           }
+            {
+              cr16_ins->rtype = BFD_RELOC_CR16_IMM16;
+            }
           break;
         default:
           break;
-       }
+        }
       break;
 
     default:
@@ -1029,9 +1130,9 @@ getreg_image (reg r)
     {
     case CR16_R_REGTYPE:
       if (! is_procreg)
-       return reg->image;
+        return reg->image;
       else
-       IMAGE_ERR;
+        IMAGE_ERR;
 
     case CR16_P_REGTYPE:
       return reg->image;
@@ -1097,10 +1198,10 @@ set_operand (char *operand, ins * cr16_ins)
 
       /* set the arg->rp, if reg is "r12" or "r13" or "14" or "15" */
       if ((cur_arg->type != arg_rbase)
-         && ((getreg_image (cur_arg->r) == 12)
-             || (getreg_image (cur_arg->r) == 13)
-             || (getreg_image (cur_arg->r) == 14)
-             || (getreg_image (cur_arg->r) == 15)))
+          && ((getreg_image (cur_arg->r) == 12)
+              || (getreg_image (cur_arg->r) == 13)
+              || (getreg_image (cur_arg->r) == 14)
+              || (getreg_image (cur_arg->r) == 15)))
          {
            cur_arg->type = arg_crp;
            cur_arg->rp = cur_arg->r;
@@ -1137,7 +1238,7 @@ set_operand (char *operand, ins * cr16_ins)
          cur_arg->type = arg_idxrp;
         }
       else
-       cur_arg->rp = -1;
+        cur_arg->rp = -1;
 
        operandE = operandS;
       /* Set displacement constant.  */
@@ -1245,9 +1346,9 @@ parse_operand (char *operand, ins * cr16_ins)
     {
     case '$':
       if (strchr (operand, '(') != NULL)
-       cur_arg->type = arg_icr;
+        cur_arg->type = arg_icr;
       else
-       cur_arg->type = arg_ic;
+        cur_arg->type = arg_ic;
       goto set_params;
       break;
 
@@ -1439,7 +1540,7 @@ static int
 is_bcc_insn (char * op)
 {
   if (!(streq (op, "bal") || streq (op, "beq0b") || streq (op, "bnq0b")
-       || streq (op, "beq0w") || streq (op, "bnq0w")))
+        || streq (op, "beq0w") || streq (op, "bnq0w")))
     if ((op[0] == 'b') && (get_b_cc (op) != NULL))
       return 1;
   return 0;
@@ -1540,18 +1641,18 @@ getidxregp_image (reg r)
   if (reg->type == CR16_RP_REGTYPE)
     {
       switch (reg->image)
-       {
-       case 0:  return 0; break;
-       case 2:  return 1; break;
-       case 4:  return 2; break;
-       case 6:  return 3; break;
-       case 8:  return 4; break;
-       case 10: return 5; break;
-       case 3:  return 6; break;
-       case 5:  return 7; break;
-       default:
-         break;
-       }
+        {
+        case 0:  return 0; break;
+        case 2:  return 1; break;
+        case 4:  return 2; break;
+        case 6:  return 3; break;
+        case 8:  return 4; break;
+        case 10: return 5; break;
+        case 3:  return 6; break;
+        case 5:  return 7; break;
+        default:
+          break;
+        }
     }
 
   IDX_RPAIR_IMAGE_ERR;
@@ -1612,17 +1713,17 @@ getprocregp_image (reg r)
       r = r - MAX_REG;
       switch (r)
         {
-       case 4: pregptab_disp = 1;  break;
-       case 6: pregptab_disp = 2;  break;
-       case 8:
-       case 9:
-       case 10:
-         pregptab_disp = 3;  break;
-       case 12:
-         pregptab_disp = 4;  break;
-       case 14:
-         pregptab_disp = 5;  break;
-       default: break;
+        case 4: pregptab_disp = 1;  break;
+        case 6: pregptab_disp = 2;  break;
+        case 8:
+        case 9:
+        case 10:
+          pregptab_disp = 3;  break;
+        case 12:
+          pregptab_disp = 4;  break;
+        case 14:
+          pregptab_disp = 5;  break;
+        default: break;
         }
       reg = &cr16_pregptab[r - pregptab_disp];
     }
@@ -1679,16 +1780,16 @@ print_constant (int nbits, int shift, argument *arg)
     case 32:
     case 28:
       /* mask the upper part of the constant, that is, the bits
-        going to the lowest byte of output_opcode[0].
-        The upper part of output_opcode[1] is always filled,
-        therefore it is always masked with 0xFFFF.  */
+         going to the lowest byte of output_opcode[0].
+         The upper part of output_opcode[1] is always filled,
+         therefore it is always masked with 0xFFFF.  */
       mask = (1 << (nbits - 16)) - 1;
       /* Divide the constant between two consecutive words :
-        0        1         2         3
-        +---------+---------+---------+---------+
-        |         | X X X X | x X x X |         |
-        +---------+---------+---------+---------+
-        output_opcode[0]    output_opcode[1]     */
+         0        1         2         3
+         +---------+---------+---------+---------+
+         |         | X X X X | x X x X |         |
+         +---------+---------+---------+---------+
+         output_opcode[0]    output_opcode[1]     */
 
       CR16_PRINT (0, (constant >> WORD_SHIFT) & mask, 0);
       CR16_PRINT (1, (constant & 0xFFFF), WORD_SHIFT);
@@ -1700,70 +1801,70 @@ print_constant (int nbits, int shift, argument *arg)
     case 22:
     case 20:
       /* mask the upper part of the constant, that is, the bits
-        going to the lowest byte of output_opcode[0].
-        The upper part of output_opcode[1] is always filled,
-        therefore it is always masked with 0xFFFF.  */
+         going to the lowest byte of output_opcode[0].
+         The upper part of output_opcode[1] is always filled,
+         therefore it is always masked with 0xFFFF.  */
       mask = (1 << (nbits - 16)) - 1;
       /* Divide the constant between two consecutive words :
-        0        1         2          3
-        +---------+---------+---------+---------+
-        |         | X X X X | - X - X |         |
-        +---------+---------+---------+---------+
-        output_opcode[0]    output_opcode[1]     */
+         0        1         2          3
+         +---------+---------+---------+---------+
+         |         | X X X X | - X - X |         |
+         +---------+---------+---------+---------+
+         output_opcode[0]    output_opcode[1]     */
 
       if ((instruction->size > 2) && (shift == WORD_SHIFT))
-       {
-         if (arg->type == arg_idxrp)
-           {
-             CR16_PRINT (0, ((constant >> WORD_SHIFT) & mask) << 8, 0);
-             CR16_PRINT (1, (constant & 0xFFFF), WORD_SHIFT);
-           }
-         else
-           {
-             CR16_PRINT (0, (((((constant >> WORD_SHIFT) & mask) << 8) & 0x0f00) | ((((constant >> WORD_SHIFT) & mask) >> 4) & 0xf)),0);
-             CR16_PRINT (1, (constant & 0xFFFF), WORD_SHIFT);
-           }
-       }
+        {
+          if (arg->type == arg_idxrp)
+            {
+              CR16_PRINT (0, ((constant >> WORD_SHIFT) & mask) << 8, 0);
+              CR16_PRINT (1, (constant & 0xFFFF), WORD_SHIFT);
+            }
+          else
+            {
+              CR16_PRINT (0, (((((constant >> WORD_SHIFT) & mask) << 8) & 0x0f00) | ((((constant >> WORD_SHIFT) & mask) >> 4) & 0xf)),0);
+              CR16_PRINT (1, (constant & 0xFFFF), WORD_SHIFT);
+            }
+        }
       else
-       CR16_PRINT (0, constant, shift);
+        CR16_PRINT (0, constant, shift);
       break;
 
     case 14:
       if (arg->type == arg_idxrp)
-       {
-         if (instruction->size == 2)
-           {
-             CR16_PRINT (0, ((constant)&0xf), shift);         // 0-3 bits
-             CR16_PRINT (0, ((constant>>4)&0x3), (shift+20)); // 4-5 bits
-             CR16_PRINT (0, ((constant>>6)&0x3), (shift+14)); // 6-7 bits
-             CR16_PRINT (0, ((constant>>8)&0x3f), (shift+8)); // 8-13 bits
-           }
-         else
-           CR16_PRINT (0, constant, shift);
-       }
+        {
+          if (instruction->size == 2)
+            {
+              CR16_PRINT (0, ((constant)      & 0xf), shift);        /* 0-3 bits.  */
+              CR16_PRINT (0, ((constant >> 4) & 0x3), (shift + 20)); /* 4-5 bits.  */
+              CR16_PRINT (0, ((constant >> 6) & 0x3), (shift + 14)); /* 6-7 bits.  */
+              CR16_PRINT (0, ((constant >> 8) & 0x3f), (shift + 8)); /* 8-13 bits.  */
+            }
+          else
+            CR16_PRINT (0, constant, shift);
+        }
       break;
 
     case 16:
     case 12:
       /* When instruction size is 3 and 'shift' is 16, a 16-bit constant is
-        always filling the upper part of output_opcode[1]. If we mistakenly
-        write it to output_opcode[0], the constant prefix (that is, 'match')
-        will be overriden.
-        0        1         2         3
-        +---------+---------+---------+---------+
-        | 'match' |         | X X X X |         |
-        +---------+---------+---------+---------+
-        output_opcode[0]    output_opcode[1]     */
+         always filling the upper part of output_opcode[1]. If we mistakenly
+         write it to output_opcode[0], the constant prefix (that is, 'match')
+         will be overriden.
+         0        1         2         3
+         +---------+---------+---------+---------+
+         | 'match' |         | X X X X |         |
+         +---------+---------+---------+---------+
+         output_opcode[0]    output_opcode[1]     */
 
       if ((instruction->size > 2) && (shift == WORD_SHIFT))
-       CR16_PRINT (1, constant, WORD_SHIFT);
+        CR16_PRINT (1, constant, WORD_SHIFT);
       else
-       CR16_PRINT (0, constant, shift);
+        CR16_PRINT (0, constant, shift);
       break;
 
     case 8:
-      CR16_PRINT (0, ((constant/2)&0xf), shift);
-      CR16_PRINT (0, ((constant/2)>>4),  (shift+8));
+      CR16_PRINT (0, ((constant / 2) & 0xf), shift);
+      CR16_PRINT (0, ((constant / 2) >> 4), (shift + 8));
       break;
 
     default:
@@ -1810,35 +1911,35 @@ print_operand (int nbits, int shift, argument *arg)
             +-----------------------------+          */
 
       if (instruction->size == 3)
-       {
-         CR16_PRINT (0, getidxregp_image (arg->rp), 0);
-         if (getreg_image (arg->i_r) == 12)
-           CR16_PRINT (0, 0, 3);
-         else
-           CR16_PRINT (0, 1, 3);
-       }
+        {
+          CR16_PRINT (0, getidxregp_image (arg->rp), 0);
+          if (getreg_image (arg->i_r) == 12)
+            CR16_PRINT (0, 0, 3);
+          else
+            CR16_PRINT (0, 1, 3);
+        }
       else
-       {
-         CR16_PRINT (0, getidxregp_image (arg->rp), 16);
-         if (getreg_image (arg->i_r) == 12)
-           CR16_PRINT (0, 0, 19);
-         else
-           CR16_PRINT (0, 1, 19);
-       }
+        {
+          CR16_PRINT (0, getidxregp_image (arg->rp), 16);
+          if (getreg_image (arg->i_r) == 12)
+            CR16_PRINT (0, 0, 19);
+          else
+            CR16_PRINT (0, 1, 19);
+        }
       print_constant (nbits, shift, arg);
       break;
 
     case arg_idxr:
       if (getreg_image (arg->i_r) == 12)
-       if (IS_INSN_MNEMONIC ("cbitb") || IS_INSN_MNEMONIC ("sbitb")
-           || IS_INSN_MNEMONIC ("tbitb"))
-         CR16_PRINT (0, 0, 23);
-       else CR16_PRINT (0, 0, 24);
+        if (IS_INSN_MNEMONIC ("cbitb") || IS_INSN_MNEMONIC ("sbitb")
+            || IS_INSN_MNEMONIC ("tbitb"))
+          CR16_PRINT (0, 0, 23);
+        else CR16_PRINT (0, 0, 24);
       else
-       if (IS_INSN_MNEMONIC ("cbitb") || IS_INSN_MNEMONIC ("sbitb")
-           || IS_INSN_MNEMONIC ("tbitb"))
-         CR16_PRINT (0, 1, 23);
-       else CR16_PRINT (0, 1, 24);
+        if (IS_INSN_MNEMONIC ("cbitb") || IS_INSN_MNEMONIC ("sbitb")
+            || IS_INSN_MNEMONIC ("tbitb"))
+          CR16_PRINT (0, 1, 23);
+        else CR16_PRINT (0, 1, 24);
 
       print_constant (nbits, shift, arg);
       break;
@@ -1861,16 +1962,16 @@ print_operand (int nbits, int shift, argument *arg)
     case arg_crp:
       print_constant (nbits, shift , arg);
       if (instruction->size > 1)
-       CR16_PRINT (0, getregp_image (arg->rp), (shift + 16));
+        CR16_PRINT (0, getregp_image (arg->rp), (shift + 16));
       else if (IS_INSN_TYPE (LD_STOR_INS) || (IS_INSN_TYPE (CSTBIT_INS)))
-       {
-         if (instruction->size == 2)
-           CR16_PRINT (0, getregp_image (arg->rp), (shift - 8));
-         else if (instruction->size == 1)
-           CR16_PRINT (0, getregp_image (arg->rp), 16);
-       }
+        {
+          if (instruction->size == 2)
+            CR16_PRINT (0, getregp_image (arg->rp), (shift - 8));
+          else if (instruction->size == 1)
+            CR16_PRINT (0, getregp_image (arg->rp), 16);
+        }
       else
-       CR16_PRINT (0, getregp_image (arg->rp), shift);
+        CR16_PRINT (0, getregp_image (arg->rp), shift);
       break;
 
     default:
@@ -1949,11 +2050,11 @@ check_range (long *num, int bits, int unsigned flags, int update)
       if (value == 0xB || value == 0x9)
         return OP_OUT_OF_RANGE;
       else if (value == -1)
-       {
-         if (update)
-           *num = 9;
-         return retval;
-       }
+        {
+          if (update)
+            *num = 9;
+          return retval;
+        }
     }
 
   if (flags & OP_ESC1)
@@ -1980,7 +2081,7 @@ check_range (long *num, int bits, int unsigned flags, int update)
    else if (flags & OP_NEG)
      {
        max = - 1;
-       min = - ((1 << (bits - 1))-1);
+       min = - ((1 << (bits - 1)) - 1);
        if ((value > max) || (value < min))
          retval = OP_OUT_OF_RANGE;
      }
@@ -2011,17 +2112,17 @@ warn_if_needed (ins *insn)
       unsigned int count = insn->arg[0].constant, reg_val;
 
       /* Check if count operand caused to save/retrive the RA twice
-        to generate warning message.  */
+         to generate warning message.  */
      if (insn->nargs > 2)
        {
          reg_val = getreg_image (insn->arg[1].r);
 
          if (   ((reg_val == 9) &&  (count > 7))
-            || ((reg_val == 10) && (count > 6))
-            || ((reg_val == 11) && (count > 5))
-            || ((reg_val == 12) && (count > 4))
-            || ((reg_val == 13) && (count > 2))
-            || ((reg_val == 14) && (count > 0)))
+             || ((reg_val == 10) && (count > 6))
+             || ((reg_val == 11) && (count > 5))
+             || ((reg_val == 12) && (count > 4))
+             || ((reg_val == 13) && (count > 2))
+             || ((reg_val == 14) && (count > 0)))
            as_warn (_("RA register is saved twice."));
 
          /* Check if the third operand is "RA" or "ra" */
@@ -2036,10 +2137,10 @@ warn_if_needed (ins *insn)
          /* If register is a register pair ie r12/r13/r14 in operand1, then
             the count constant should be validated.  */
          if (((reg_val == 11) && (count > 7))
-            || ((reg_val == 12) && (count > 6))
-            || ((reg_val == 13) && (count > 4))
-            || ((reg_val == 14) && (count > 2))
-            || ((reg_val == 15) && (count > 0)))
+             || ((reg_val == 12) && (count > 6))
+             || ((reg_val == 13) && (count > 4))
+             || ((reg_val == 14) && (count > 2))
+             || ((reg_val == 15) && (count > 0)))
            as_bad (_("`%s' Illegal count-register combination."), ins_parse);
        }
      else
@@ -2187,14 +2288,14 @@ assemble_insn (char *mnemonic, ins *insn)
           /* If 'bal' instruction size is '2' and reg operand is not 'ra'
              then goto next instruction.  */
           if (IS_INSN_MNEMONIC ("bal") && (i == 0)
-             && (instruction->size == 2) && (insn->arg[i].rp != 14))
+              && (instruction->size == 2) && (insn->arg[i].rp != 14))
             goto next_insn;
 
           /* If 'storb' instruction with 'sp' reg and 16-bit disp of
            * reg-pair, leads to undifined trap, so this should use
            * 20-bit disp of reg-pair.  */
           if (IS_INSN_MNEMONIC ("storb") && (instruction->size == 2)
-             && (insn->arg[i].r == 15) && (insn->arg[i + 1].type == arg_crp))
+              && (insn->arg[i].r == 15) && (insn->arg[i + 1].type == arg_crp))
             goto next_insn;
 
           /* Only check range - don't update the constant's value, since the
@@ -2216,7 +2317,7 @@ assemble_insn (char *mnemonic, ins *insn)
              determined) is sufficient.  */
           else if ((insn->arg[i].X_op == O_symbol)
                    && ((bfd_reloc_type_lookup (stdoutput, insn->rtype))->bitsize
-                      > cur_size[i]))
+                       > cur_size[i]))
                   goto next_insn;
         }
       found_const_within_range = 1;
@@ -2241,15 +2342,15 @@ next_insn:
         {
           switch (const_err)
             {
-           case OP_OUT_OF_RANGE:
-             as_bad (_("Operand out of range (arg %d)"), invalid_const);
-             break;
-           case OP_NOT_EVEN:
-             as_bad (_("Operand has odd displacement (arg %d)"), invalid_const);
-             break;
-           default:
-             as_bad (_("Illegal operand (arg %d)"), invalid_const);
-             break;
+            case OP_OUT_OF_RANGE:
+              as_bad (_("Operand out of range (arg %d)"), invalid_const);
+              break;
+            case OP_NOT_EVEN:
+              as_bad (_("Operand has odd displacement (arg %d)"), invalid_const);
+              break;
+            default:
+              as_bad (_("Illegal operand (arg %d)"), invalid_const);
+              break;
             }
         }
 
@@ -2321,28 +2422,61 @@ print_insn (ins *insn)
       words[j++] = output_opcode[i] & 0xFFFF;
     }
 
-    insn_size = instruction->size;
-    this_frag = frag_more (insn_size * 2);
-
     /* Handle relocation.  */
-    if ((relocatable) && (insn->rtype != BFD_RELOC_NONE))
+    if ((instruction->flags & RELAXABLE) && relocatable)
+      {
+        int relax_subtype;
+        /* Write the maximal instruction size supported.  */
+        insn_size = INSN_MAX_SIZE;
+
+        if (IS_INSN_TYPE (BRANCH_INS))
+          {
+            switch (insn->rtype)
+              {
+              case BFD_RELOC_CR16_DISP24:
+                relax_subtype = 2;
+                break;
+              case BFD_RELOC_CR16_DISP16:
+                relax_subtype = 1;
+                break;
+              default:
+                relax_subtype = 0;
+                break;
+              }
+          }
+        else
+          abort ();
+
+        this_frag = frag_var (rs_machine_dependent, insn_size *2,
+                              4, relax_subtype,
+                              insn->exp.X_add_symbol,
+                              0,
+                              0);
+      }
+    else
       {
-         reloc_howto_type *reloc_howto;
-         int size;
+        insn_size = instruction->size;
+        this_frag = frag_more (insn_size * 2);
 
-         reloc_howto = bfd_reloc_type_lookup (stdoutput, insn->rtype);
+        if ((relocatable) && (insn->rtype != BFD_RELOC_NONE))
+          {
+             reloc_howto_type *reloc_howto;
+             int size;
 
-         if (!reloc_howto)
-           abort ();
+             reloc_howto = bfd_reloc_type_lookup (stdoutput, insn->rtype);
+  
+             if (!reloc_howto)
+               abort ();
 
-         size = bfd_get_reloc_size (reloc_howto);
+             size = bfd_get_reloc_size (reloc_howto);
 
-         if (size < 1 || size > 4)
-           abort ();
+             if (size < 1 || size > 4)
+               abort ();
 
-         fix_new_exp (frag_now, this_frag - frag_now->fr_literal,
-                      size, &insn->exp, reloc_howto->pc_relative,
-                      insn->rtype);
+             fix_new_exp (frag_now, this_frag - frag_now->fr_literal,
+                          size, &insn->exp, reloc_howto->pc_relative,
+                          insn->rtype);
+          }
       }
 
   /* Verify a 2-byte code alignment.  */
@@ -2410,14 +2544,14 @@ md_assemble (char *op)
       instruction = (const inst *) hash_find (cr16_inst_hash, op);
        parse_operands (&cr16_ins, param1);
       if (((&cr16_ins)->arg[0].type == arg_ic)
-         && ((&cr16_ins)->arg[0].constant >= 0))
+          && ((&cr16_ins)->arg[0].constant >= 0))
         {
            if (streq ("lshb", op))
-            op = "ashub";
+             op = "ashub";
            else if (streq ("lshd", op))
-            op = "ashud";
-          else
-            op = "ashuw";
+             op = "ashud";
+           else
+             op = "ashuw";
         }
     }