Replace <sys/dir.h> (and <dirent.h>) with "gdb_dirent.h".
[deliverable/binutils-gdb.git] / gas / config / tc-tic80.c
index b638d8c47d0e625281cf9422172077fa0063b344..f31dba32f93138a8a0c5478116c7c76aac541425 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-tic80.c -- Assemble for the TI TMS320C80 (MV)
-   Copyright (C) 1996 Free Software Foundation, Inc.
+   Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
 #include "as.h"
 #include "opcode/tic80.h"
 
+#define internal_error(what) \
+  as_fatal(_("internal error:%s:%d: %s\n"),__FILE__,__LINE__,what)
+#define internal_error_a(what,arg) \
+  as_fatal(_("internal error:%s:%d: %s %d\n"),__FILE__,__LINE__,what,arg)
+
 \f
 /* Generic assembler global variables which must be defined by all targets. */
 
 const char comment_chars[] = ";";
 
 /* Characters which start a comment at the beginning of a line.  */
-const char line_comment_chars[] = ";*";
+const char line_comment_chars[] = ";*#";
 
-/* Characters which may be used to separate multiple commands on a 
-   single line.  */
-const char line_separator_chars[] = ";";
+/* Characters which may be used to separate multiple commands on a single
+   line. The semicolon is such a character by default and should not be
+   explicitly listed. */
+const char line_separator_chars[] = "";
 
 /* Characters which are used to indicate an exponent in a floating 
    point number.  */
 const char EXP_CHARS[] = "eE";
 
 /* Characters which mean that a number is a floating point constant, 
-   as in 0d1.0.  */
-const char FLT_CHARS[] = "dD";
+   as in 0f1.0.  */
+const char FLT_CHARS[] = "fF";
 
 /* This table describes all the machine specific pseudo-ops the assembler
    has to support.  The fields are:
+
    pseudo-op name without dot
    function to call to execute this pseudo-op
-   Integer arg to pass to the function
-   */
+   integer arg to pass to the function */
+
+extern void obj_coff_section ();
 
 const pseudo_typeS md_pseudo_table[] =
 {
-  {"word", cons, 4},
-  { NULL,       NULL,           0 }
+  { "align",   s_align_bytes,          4 },    /* Do byte alignment, default is a 4 byte boundary */
+  { "word",    cons,                   4 },    /* FIXME: Should this be machine independent? */
+  { "bss",     s_lcomm_bytes,          1 },
+  { "sect",    obj_coff_section,       0},     /* For compatibility with TI tools */
+  { "section", obj_coff_section,       0},     /* Standard COFF .section pseudo-op */
+  { NULL,      NULL,                   0 }
 };
 
 /* Opcode hash table.  */
 static struct hash_control *tic80_hash;
 
 static struct tic80_opcode * find_opcode PARAMS ((struct tic80_opcode *, expressionS []));
-static unsigned long build_insn PARAMS ((struct tic80_opcode *, expressionS *, unsigned long insn));
+static void build_insn PARAMS ((struct tic80_opcode *, expressionS *));
 static int get_operands PARAMS ((expressionS exp[]));
-static int reg_name_search PARAMS ((char *name));
-static int register_name PARAMS ((expressionS *expressionP));
+static int const_overflow PARAMS ((unsigned long num, int bits, int flags));
+
+/* Replace short PC relative instructions with long form when necessary.  Currently
+   this is off by default or when given the -no-relax option.  Turning it on by using
+   the -relax option forces all PC relative instructions to use the long form, which
+   is why it is currently not the default. */
+static int tic80_relax = 0;
 
 \f
 int
@@ -70,7 +87,7 @@ md_estimate_size_before_relax (fragP, segment_type)
      fragS *fragP;
      segT segment_type;
 {
-  as_fatal ("Relaxation is a luxury we can't afford\n");
+  internal_error (_("Relaxation is a luxury we can't afford"));
   return (-1);
 }
 
@@ -121,7 +138,7 @@ md_atof (type, litP, sizeP)
 
     default:
       *sizeP = 0;
-      return "bad call to md_atof ()";
+      return _("bad call to md_atof ()");
     }
 
   t = atof_ieee (input_line_pointer, type, words);
@@ -140,78 +157,33 @@ md_atof (type, litP, sizeP)
   return (NULL);
 }
 
-/* reg_name_search does a binary search of the tic80_pre_defined_registers
-   array to see if "name" is a valid regiter name.  Returns the register
-   number from the array on success, or -1 on failure. */
+/* Check to see if the constant value in NUM will fit in a field of
+   width BITS if it has flags FLAGS. */
 
 static int
-reg_name_search (name)
-     char *name;
+const_overflow (num, bits, flags)
+     unsigned long num;
+     int bits;
+     int flags;
 {
-  int middle, low, high;
-  int cmp;
-
-  low = 0;
-  high = tic80_num_regs - 1;
-
-  do
-    {
-      middle = (low + high) / 2;
-      cmp = strcasecmp (name, tic80_pre_defined_registers[middle].name);
-      if (cmp < 0)
-       {
-         high = middle - 1;
-       }
-      else if (cmp > 0)
-       {
-         low = middle + 1;
-       }
-      else 
-       {
-         return (tic80_pre_defined_registers[middle].value);
-       }
-    }
-  while (low <= high);
-  return (-1);
-}
-
-/* register_name() checks the string at input_line_pointer
-   to see if it is a valid register name */
-
-static int
-register_name (expressionP)
-     expressionS *expressionP;
-{
-  int reg_number;
-  char c;
-  char *p = input_line_pointer;
-  
-  while (*p != '\000' && *p != '\n' && *p != '\r' && *p != ',' && *p != ' ' && *p != ')')
-    p++;
-
-  c = *p;
-  if (c)
-    {
-      *p++ = '\000';
-    }
-
-  /* look to see if it's in the register table */
-
-  reg_number = reg_name_search (input_line_pointer);
-  if (reg_number >= 0) 
-    {
-      expressionP -> X_op = O_register;
-      /* temporarily store a pointer to the string here */
-      expressionP -> X_op_symbol = (struct symbol *) input_line_pointer;
-      expressionP -> X_add_number = reg_number;
-      input_line_pointer = p;
-      return (1);
-    }
-  if (c)
-    {
-      *(p - 1) = c;
-    }
-  return (0);
+  long min, max;
+  int retval = 0;
+
+  /* Only need to check fields less than 32 bits wide */
+  if (bits < 32)
+    if (flags & TIC80_OPERAND_SIGNED)
+      {
+       max = (1 << (bits - 1)) - 1; 
+       min = - (1 << (bits - 1));  
+       retval = ((long) num > max) || ((long) num < min);
+      }
+    else
+      {
+       max = (1 << bits) - 1;
+       min = 0;
+       retval = (num > max) || (num < min);
+      }
+  return (retval);
 }
 
 /* get_operands() parses a string of operands and fills in a passed array of
@@ -259,16 +231,18 @@ get_operands (exp)
          if (*p == 'm') 
            {
              p++;
+             /* This is a ":m" modifier */
              exp[numexp].X_add_number = TIC80_OPERAND_M_SI | TIC80_OPERAND_M_LI;
            }
          else if (*p == 's')
            {
              p++;
+             /* This is a ":s" modifier */
              exp[numexp].X_add_number = TIC80_OPERAND_SCALED;
            }
          else
            {
-             as_bad ("':' not followed by 'm' or 's'");
+             as_bad (_("':' not followed by 'm' or 's'"));
            }
          numexp++;
          continue;
@@ -282,7 +256,7 @@ get_operands (exp)
        {
          if (++parens != 1)
            {
-             as_bad ("paren nesting");
+             as_bad (_("paren nesting"));
            }
          p++;
          continue;
@@ -297,7 +271,7 @@ get_operands (exp)
          /* Record that we have left a paren group and continue */
          if (--parens < 0)
            {
-             as_bad ("mismatched parenthesis");
+             as_bad (_("mismatched parenthesis"));
            }
          p++;
          continue;
@@ -306,22 +280,15 @@ get_operands (exp)
       /* Begin operand parsing at the current scan point. */
 
       input_line_pointer = p;
-
-      /* Check to see if it might be a register name */
-
-      if (!register_name (&exp[numexp]))
-       {
-         /* parse as an expression */
-         expression (&exp[numexp]);
-       }
+      expression (&exp[numexp]);
 
       if (exp[numexp].X_op == O_illegal)
        {
-         as_bad ("illegal operand");
+         as_bad (_("illegal operand"));
        }
       else if (exp[numexp].X_op == O_absent)
        {
-         as_bad ("missing operand");
+         as_bad (_("missing operand"));
        }
 
       numexp++;
@@ -334,7 +301,9 @@ get_operands (exp)
       exp[numexp++].X_add_number = TIC80_OPERAND_PARENS;
     }
 
-  exp[numexp].X_op = 0;
+  /* Mark the end of the valid operands with an illegal expression. */
+  exp[numexp].X_op = O_illegal;
+
   return (numexp);
 }
 
@@ -347,23 +316,166 @@ get_operands (exp)
    previous non-O_absent expression, such as ":m" or ":s" modifiers or
    register numbers enclosed in parens like "(r10)".
 
-   It then looks at all opcodes with the same name and use the operands to
+   It then looks at all opcodes with the same name and uses the operands to
    choose the correct opcode.  */
 
-
 static struct tic80_opcode *
 find_opcode (opcode, myops)
      struct tic80_opcode *opcode;
      expressionS myops[];
 {
-  int i, match, done, numops;
-  struct tic80_opcode *next_opcode;
+  int numexp;                          /* Number of expressions from parsing operands */
+  int expi;                            /* Index of current expression to match */
+  int opi;                             /* Index of current operand to match */
+  int match = 0;                       /* Set to 1 when an operand match is found */
+  struct tic80_opcode *opc = opcode;   /* Pointer to current opcode table entry */
+  const struct tic80_opcode *end;      /* Pointer to end of opcode table */
 
-  match = 0;
+  /* First parse all the operands so we only have to do it once.  There may
+     be more expressions generated than there are operands. */
 
-  /* First parse all the operands so we only have to do it once. */
+  numexp = get_operands (myops);
 
-  get_operands (myops);
+  /* For each opcode with the same name, try to match it against the parsed
+     operands. */
+
+  end = tic80_opcodes + tic80_num_opcodes;
+  while (!match && (opc < end) && (strcmp (opc -> name, opcode -> name) == 0))
+    {
+      /* Start off assuming a match.  If we find a mismatch, then this is
+        reset and the operand/expr matching loop terminates with match
+        equal to zero, which allows us to try the next opcode. */
+
+      match = 1;
+
+      /* For each expression, try to match it against the current operand
+        for the current opcode.  Upon any mismatch, we abandon further
+        matching for the current opcode table entry.  */
+
+      for (expi = 0, opi = -1; (expi < numexp) && match; expi++)
+       {
+         int bits, flags, X_op, num;
+
+         X_op = myops[expi].X_op;
+         num = myops[expi].X_add_number;
+
+         /* The O_absent expressions apply to the same operand as the most
+            recent non O_absent expression.  So only increment the operand
+            index when the current expression is not one of these special
+            expressions. */
+
+         if (X_op != O_absent)
+           {
+             opi++;
+           }
+
+         flags = tic80_operands[opc -> operands[opi]].flags;
+         bits = tic80_operands[opc -> operands[opi]].bits;
+
+         switch (X_op)
+           {
+           case O_register:
+             /* Also check that registers that are supposed to be even actually
+                are even. */
+             if (((flags & TIC80_OPERAND_GPR) != (num & TIC80_OPERAND_GPR)) ||
+                 ((flags & TIC80_OPERAND_FPA) != (num & TIC80_OPERAND_FPA)) ||
+                 ((flags & TIC80_OPERAND_CR) != (num & TIC80_OPERAND_CR)) ||
+                 ((flags & TIC80_OPERAND_EVEN) && (num & 1)) ||
+                 const_overflow (num & ~TIC80_OPERAND_MASK, bits, flags))
+               {
+                 match = 0;
+               }
+             break;
+           case O_constant:
+             if ((flags & TIC80_OPERAND_ENDMASK) && (num == 32))
+               {
+                 /* Endmask values of 0 and 32 give identical results */
+                 num = 0;
+               }
+             if ((flags & (TIC80_OPERAND_FPA | TIC80_OPERAND_GPR)) ||
+                 const_overflow (num, bits, flags))
+               {
+                 match = 0;
+               }
+             break;
+           case O_symbol:
+             if ((bits < 32) && (flags & TIC80_OPERAND_PCREL) && !tic80_relax)
+               {
+                 /* The default is to prefer the short form of PC relative relocations.
+                    This is the only form that the TI assembler supports.
+                    If the -relax option is given, we never use the short forms.
+                    FIXME: Should be able to choose "best-fit". */
+               }
+             else if ((bits == 32) /* && (flags & TIC80_OPERAND_BASEREL) */)
+               {
+                 /* The default is to prefer the long form of base relative relocations.
+                    This is the only form that the TI assembler supports.
+                    If the -no-relax option is given, we always use the long form of
+                    PC relative relocations.
+                    FIXME: Should be able to choose "best-fit". */
+               }
+             else
+               {
+                 /* Symbols that don't match one of the above cases are
+                    rejected as an operand. */
+                 match = 0;
+               }
+             break;
+           case O_absent:
+             /* If this is an O_absent expression, then it may be an expression that
+                supplies additional information about the operand, such as ":m" or
+                ":s" modifiers. Check to see that the operand matches this requirement. */
+             if (!((num & TIC80_OPERAND_M_SI) && (flags & TIC80_OPERAND_M_SI) ||
+                   (num & TIC80_OPERAND_M_LI) && (flags & TIC80_OPERAND_M_LI) ||
+                   (num & TIC80_OPERAND_SCALED) && (flags & TIC80_OPERAND_SCALED)))
+               {
+                 match = 0;
+               }
+             break;
+           case O_big:
+             if ((num > 0) || !(flags & TIC80_OPERAND_FLOAT))
+               {
+                 match = 0;
+               }
+             break;
+           case O_illegal:
+           case O_symbol_rva:
+           case O_uminus:
+           case O_bit_not:
+           case O_logical_not:
+           case O_multiply:
+           case O_divide:
+           case O_modulus:
+           case O_left_shift:
+           case O_right_shift:
+           case O_bit_inclusive_or:
+           case O_bit_or_not:
+           case O_bit_exclusive_or:
+           case O_bit_and:
+           case O_add:
+           case O_subtract:
+           case O_eq:
+           case O_ne:
+           case O_lt:
+           case O_le:
+           case O_ge:
+           case O_gt:
+           case O_logical_and:
+           case O_logical_or:
+           case O_max:
+           default:
+             internal_error_a (_("unhandled expression type"), X_op);
+           }
+       }
+      if (!match)
+       {
+         opc++;
+       }
+    }  
+
+  return (match ? opc : NULL);
+
+#if 0
 
   /* Now search the opcode table table for one with operands that
      matches what we've got. */
@@ -383,23 +495,23 @@ find_opcode (opcode, myops)
              break;
            }
              
-         if (flags & TIC80_OPERAND_GPR
+         if (flags & (TIC80_OPERAND_GPR | TIC80_OPERAND_FPA | TIC80_OPERAND_CR)
            {
              if ((X_op != O_register) ||
-                 ((flags & OPERAND_ACC) != (num & OPERAND_ACC)) ||
-                 ((flags & OPERAND_FLAG) != (num & OPERAND_FLAG)) ||
-                 ((flags & OPERAND_CONTROL) != (num & OPERAND_CONTROL)))
+                 ((flags & TIC80_OPERAND_GPR) != (num & TIC80_OPERAND_GPR)) ||
+                 ((flags & TIC80_OPERAND_FPA) != (num & TIC80_OPERAND_FPA)) ||
+                 ((flags & TIC80_OPERAND_CR) != (num & TIC80_OPERAND_CR)))
                {
                  match=0;
                  break;
                }         
            }
              
-         if (((flags & OPERAND_MINUS) && ((X_op != O_absent) || (num != OPERAND_MINUS))) ||
-             ((flags & OPERAND_PLUS) && ((X_op != O_absent) || (num != OPERAND_PLUS))) ||
-             ((flags & OPERAND_ATMINUS) && ((X_op != O_absent) || (num != OPERAND_ATMINUS))) ||
-             ((flags & OPERAND_ATPAR) && ((X_op != O_absent) || (num != OPERAND_ATPAR))) ||
-             ((flags & OPERAND_ATSIGN) && ((X_op != O_absent) || (num != OPERAND_ATSIGN)))) 
+         if (((flags & TIC80_OPERAND_MINUS) && ((X_op != O_absent) || (num != TIC80_OPERAND_MINUS))) ||
+             ((flags & TIC80_OPERAND_PLUS) && ((X_op != O_absent) || (num != TIC80_OPERAND_PLUS))) ||
+             ((flags & TIC80_OPERAND_ATMINUS) && ((X_op != O_absent) || (num != TIC80_OPERAND_ATMINUS))) ||
+             ((flags & TIC80_OPERAND_ATPAR) && ((X_op != O_absent) || (num != TIC80_OPERAND_ATPAR))) ||
+             ((flags & TIC80_OPERAND_ATSIGN) && ((X_op != O_absent) || (num != TIC80_OPERAND_ATSIGN)))) 
            {
              match=0;
              break;
@@ -422,7 +534,7 @@ find_opcode (opcode, myops)
 
   if (!match)  
     {
-      as_bad ("bad opcode or operands");
+      as_bad (_("bad opcode or operands"));
       return (0);
     }
 
@@ -431,12 +543,12 @@ find_opcode (opcode, myops)
   /* fix that here. */
   for (i=0; opcode->operands[i]; i++) 
     {
-      if ((tic80_operands[opcode->operands[i]].flags & OPERAND_EVEN) &&
+      if ((tic80_operands[opcode->operands[i]].flags & TIC80_OPERAND_EVEN) &&
          (myops[i].X_add_number & 1)) 
-       as_fatal("Register number must be EVEN");
+       as_fatal (_("Register number must be EVEN"));
       if (myops[i].X_op == O_register)
        {
-         if (!(tic80_operands[opcode->operands[i]].flags & OPERAND_REG)) 
+         if (!(tic80_operands[opcode->operands[i]].flags & TIC80_OPERAND_REG)) 
            {
              myops[i].X_op = O_symbol;
              myops[i].X_add_symbol = symbol_find_or_make ((char *)myops[i].X_op_symbol);
@@ -445,25 +557,212 @@ find_opcode (opcode, myops)
            }
        }
     }
-  return opcode;
+
+#endif
 }
 
 /* build_insn takes a pointer to the opcode entry in the opcode table
-   and the array of operand expressions and returns the instruction */
+   and the array of operand expressions and writes out the instruction.
 
-static unsigned long
-build_insn (opcode, opers, insn) 
+   Note that the opcode word and extended word may be written to different
+   frags, with the opcode at the end of one frag and the extension at the
+   beginning of the next. */
+
+static void
+build_insn (opcode, opers) 
      struct tic80_opcode *opcode;
      expressionS *opers;
-     unsigned long insn;
 {
-  return (0);
+  int expi;                            /* Index of current expression to match */
+  int opi;                             /* Index of current operand to match */
+  unsigned long insn[2];               /* Instruction and long immediate (if any) */
+  char *f;                             /* Pointer to frag location for insn[0] */
+  fragS *ffrag;                                /* Frag containing location f */
+  char *fx = NULL;                     /* Pointer to frag location for insn[1] */
+  fragS *fxfrag;                       /* Frag containing location fx */
+
+  /* Start with the raw opcode bits from the opcode table. */
+  insn[0] = opcode -> opcode;
+
+  /* We are going to insert at least one 32 bit opcode so get the
+     frag now. */
+
+  f = frag_more (4);
+  ffrag = frag_now;
+
+  /* For each operand expression, insert the appropriate bits into the
+     instruction . */
+  for (expi = 0, opi = -1; opers[expi].X_op != O_illegal; expi++)
+    {
+      int bits, shift, flags, X_op, num;
+
+      X_op = opers[expi].X_op;
+      num = opers[expi].X_add_number;
+
+      /* The O_absent expressions apply to the same operand as the most
+        recent non O_absent expression.  So only increment the operand
+        index when the current expression is not one of these special
+        expressions. */
+
+      if (X_op != O_absent)
+       {
+         opi++;
+       }
+
+      flags = tic80_operands[opcode -> operands[opi]].flags;
+      bits = tic80_operands[opcode -> operands[opi]].bits;
+      shift = tic80_operands[opcode -> operands[opi]].shift;
+
+      switch (X_op)
+       {
+       case O_register:
+         num &= ~TIC80_OPERAND_MASK;
+         insn[0] = insn[0] | (num << shift);
+         break;
+       case O_constant:
+         if ((flags & TIC80_OPERAND_ENDMASK) && (num == 32))
+           {
+             /* Endmask values of 0 and 32 give identical results */
+             num = 0;
+           }
+         else if ((flags & TIC80_OPERAND_BITNUM))
+           {
+             /* BITNUM values are stored in one's complement form */
+             num = (~num & 0x1F);
+           }
+         /* Mask off upper bits, just it case it is signed and is negative */
+         if (bits < 32)
+           {
+             num &= (1 << bits) - 1;
+             insn[0] = insn[0] | (num << shift);
+           }
+         else
+           {
+             fx = frag_more (4);
+             fxfrag = frag_now;
+             insn[1] = num;
+           }
+         break;
+       case O_symbol:
+         if (bits == 32)
+           {
+             fx = frag_more (4);
+             fxfrag = frag_now;
+             insn[1] = 0;
+             if (flags & TIC80_OPERAND_PCREL)
+               {
+                 fix_new_exp (fxfrag,
+                              fx - (fxfrag -> fr_literal),
+                              4,
+                              &opers[expi], 
+                              1,
+                              R_MPPCR);
+               }
+             else
+               {
+                 fix_new_exp (fxfrag,
+                              fx - (fxfrag -> fr_literal),
+                              4,
+                              &opers[expi], 
+                              0,
+                              R_RELLONGX);
+               }
+           }
+         else if (flags & TIC80_OPERAND_PCREL)
+           {
+             fix_new_exp (ffrag,
+                          f - (ffrag -> fr_literal),
+                          4,                   /* FIXME! how is this used? */
+                          &opers[expi],
+                          1,
+                          R_MPPCR15W);
+           }
+         else
+           {
+             internal_error (_("symbol reloc that is not PC relative or 32 bits"));
+           }
+         break;
+       case O_absent:
+         /* Each O_absent expression can indicate exactly one possible modifier. */
+         if ((num & TIC80_OPERAND_M_SI) && (flags & TIC80_OPERAND_M_SI))
+           {
+             insn[0] = insn[0] | (1 << 17);
+           }
+         else if ((num & TIC80_OPERAND_M_LI) && (flags & TIC80_OPERAND_M_LI))
+           {
+             insn[0] = insn[0] | (1 << 15);
+           }
+         else if ((num & TIC80_OPERAND_SCALED) && (flags & TIC80_OPERAND_SCALED))
+           {
+             insn[0] = insn[0] | (1 << 11);
+           }
+         else if ((num & TIC80_OPERAND_PARENS) && (flags & TIC80_OPERAND_PARENS))
+           {
+             /* No code to generate, just accept and discard this expression */
+           }
+         else
+           {
+             internal_error_a (_("unhandled operand modifier"), opers[expi].X_add_number);
+           }
+         break;
+       case O_big:
+         fx = frag_more (4);
+         fxfrag = frag_now;
+         {
+           int precision = 2;
+           long exponent_bits = 8L;
+           LITTLENUM_TYPE words[2];
+           /* Value is still in generic_floating_point_number */
+           gen_to_words (words, precision, exponent_bits);
+           insn[1] = (words[0] << 16) | words[1];
+         }
+         break;
+       case O_illegal:
+       case O_symbol_rva:
+       case O_uminus:
+       case O_bit_not:
+       case O_logical_not:
+       case O_multiply:
+       case O_divide:
+       case O_modulus:
+       case O_left_shift:
+       case O_right_shift:
+       case O_bit_inclusive_or:
+       case O_bit_or_not:
+       case O_bit_exclusive_or:
+       case O_bit_and:
+       case O_add:
+       case O_subtract:
+       case O_eq:
+       case O_ne:
+       case O_lt:
+       case O_le:
+       case O_ge:
+       case O_gt:
+       case O_logical_and:
+       case O_logical_or:
+       case O_max:
+       default:
+         internal_error_a (_("unhandled expression"), X_op);
+         break;
+       }
+    }
+
+  /* Write out the instruction, either 4 or 8 bytes.  */
+
+  md_number_to_chars (f, insn[0], 4);
+  if (fx != NULL)
+    {
+      md_number_to_chars (fx, insn[1], 4);
+    }
 }
 
-/* This is the main entry point for the machine-dependent assembler.  STR points to a
-   machine dependent instruction.  This function is supposed to emit the frags/bytes
-   it assembles to.
- */
+/* This is the main entry point for the machine-dependent assembler.  Gas
+   calls this function for each input line which does not contain a
+   pseudoop.
+
+  STR points to a NULL terminated machine dependent instruction.  This
+  function is supposed to emit the frags/bytes it assembles to.  */
 
 void
 md_assemble (str)
@@ -472,7 +771,7 @@ md_assemble (str)
   char *scan;
   unsigned char *input_line_save;
   struct tic80_opcode *opcode;
-  expressionS myops[6];
+  expressionS myops[16];
   unsigned long insn;
 
   /* Ensure there is something there to assemble. */
@@ -495,7 +794,7 @@ md_assemble (str)
   /* Try to find this mnemonic in the hash table */
   if ((opcode = (struct tic80_opcode *) hash_find (tic80_hash, str)) == NULL)
     {
-      as_bad ("Invalid mnemonic: '%s'", str);
+      as_bad (_("Invalid mnemonic: '%s'"), str);
       return;
     }
 
@@ -511,18 +810,19 @@ md_assemble (str)
   opcode = find_opcode (opcode, myops);
   if (opcode == NULL)
     {
-      return;
+      as_bad (_("Invalid operands: '%s'"), input_line_save);
     }
 
   input_line_pointer = input_line_save;
-  insn = build_insn (opcode, myops, 0);
-
-  /* FIXME - finish this */
+  build_insn (opcode, myops);
 }
 
-/* This function is called once, at assembler startup time.  It should
-   set up all the tables, etc., that the MD part of the assembler will
-   need.  */
+/* This function is called once at the start of assembly, after the command
+   line arguments have been parsed and all the machine independent
+   initializations have been completed.
+
+   It should set up all the tables, etc., that the machine dependent part of
+   the assembler will need.  */
 
 void
 md_begin ()
@@ -530,14 +830,20 @@ md_begin ()
   char *prev_name = "";
   register const struct tic80_opcode *op;
   register const struct tic80_opcode *op_end;
+  const struct predefined_symbol *pdsp;
+  extern int coff_flags;                       /* Defined in obj-coff.c */
 
-  tic80_hash = hash_new();
+  /* Set F_AR32WR in coff_flags, which will end up in the file header
+     f_flags field. */
+
+  coff_flags |= F_AR32WR;      /* TIc80 is 32 bit little endian */
 
   /* Insert unique names into hash table.  The TIc80 instruction set
      has many identical opcode names that have different opcodes based
      on the operands.  This hash table then provides a quick index to
      the first opcode with a particular name in the opcode table.  */
 
+  tic80_hash = hash_new ();
   op_end = tic80_opcodes + tic80_num_opcodes;
   for (op = tic80_opcodes; op < op_end; op++)
     {
@@ -547,34 +853,105 @@ md_begin ()
          hash_insert (tic80_hash, op -> name, (char *) op);
        }
     }
+
+  /* Insert the predefined symbols into the symbol table.  We use symbol_create
+     rather than symbol_new so that these symbols don't end up in the object
+     files' symbol table.  Note that the values of the predefined symbols include
+     some upper bits that distinguish the type of the symbol (register, bitnum,
+     condition code, etc) and these bits must be masked away before actually
+     inserting the values into the instruction stream.  For registers we put
+     these bits in the symbol table since we use them later and there is no
+     question that they aren't part of the register number.  For constants we
+     can't do that since the constant can be any value, so they are masked off
+     before putting them into the symbol table. */
+
+  pdsp = NULL;
+  while ((pdsp = tic80_next_predefined_symbol (pdsp)) != NULL)
+    {
+      segT segment;
+      valueT valu;
+      int symtype;
+
+      symtype = PDS_VALUE (pdsp) & TIC80_OPERAND_MASK;
+      switch (symtype)
+       {
+       case TIC80_OPERAND_GPR:
+       case TIC80_OPERAND_FPA:
+       case TIC80_OPERAND_CR:
+         segment = reg_section;
+         valu = PDS_VALUE (pdsp);
+         break;
+       case TIC80_OPERAND_CC:
+       case TIC80_OPERAND_BITNUM:
+         segment = absolute_section;
+         valu = PDS_VALUE (pdsp) & ~TIC80_OPERAND_MASK;
+         break;
+       default:
+         internal_error_a (_("unhandled predefined symbol bits"), symtype);
+         break;
+       }
+      symbol_table_insert (symbol_create (PDS_NAME (pdsp), segment, valu,
+                                         &zero_address_frag));
+    }
 }
 
 \f
 
+/* The assembler adds md_shortopts to the string passed to getopt. */
+
 CONST char *md_shortopts = "";
 
+/* The assembler adds md_longopts to the machine independent long options
+   that are passed to getopt. */
+
 struct option md_longopts[] = {
+
+#define OPTION_RELAX   (OPTION_MD_BASE)
+  {"relax", no_argument, NULL, OPTION_RELAX},
+
+#define OPTION_NO_RELAX        (OPTION_RELAX + 1)
+  {"no-relax", no_argument, NULL, OPTION_NO_RELAX},
+
   {NULL, no_argument, NULL, 0}
 };
 
 size_t md_longopts_size = sizeof(md_longopts);
 
-/* Take care of the target-specific command-line options.  */
+/* The md_parse_option function will be called whenever getopt returns an
+   unrecognized code, presumably indicating a special code value which
+   appears in md_longopts for machine specific command line options. */
 
 int
 md_parse_option (c, arg)
      int c;
      char *arg;
 {
-  return (0);
+  switch (c)
+    {
+    case OPTION_RELAX:
+      tic80_relax = 1;
+      break;
+    case OPTION_NO_RELAX:
+      tic80_relax = 0;
+      break;
+    default:
+      return (0);
+    }
+  return (1);
 }
 
-/* Print a description of the command-line options that we accept.  */
+/* The md_show_usage function will be called whenever a usage message is
+   printed.  It should print a description of the machine specific options
+   found in md_longopts. */
 
 void
 md_show_usage (stream)
      FILE *stream;
 {
+  fprintf (stream, "\
+TIc80 options:\n\
+-relax                 alter PC relative branch instructions to use long form when needed\n\
+-no-relax              always use short PC relative branch instructions, error on overflow\n");
 }
 
 \f
@@ -588,22 +965,58 @@ md_apply_fix (fixP, val)
      fixS *fixP;
      long val;
 {
-  as_fatal ("md_apply_fix() not implemented yet\n");
-  abort ();
+  char *dest =  fixP -> fx_frag -> fr_literal + fixP -> fx_where;
+  int overflow;
+
+  switch (fixP -> fx_r_type)
+    {
+    case R_RELLONGX:
+      md_number_to_chars (dest, (valueT) val, 4);
+      break;
+    case R_MPPCR:
+      val >>= 2;
+      val += 1;          /* Target address computed from inst start */
+      md_number_to_chars (dest, (valueT) val, 4);
+      break;
+    case R_MPPCR15W:
+      overflow = (val < -65536L) || (val > 65532L);
+      if (overflow)
+       {
+         as_bad_where (fixP -> fx_file, fixP -> fx_line,
+                       _("PC offset 0x%lx outside range 0x%lx-0x%lx"),
+                       val, -65536L, 65532L);
+       }
+      else
+       {
+         val >>= 2;
+         *dest++ = val & 0xFF;
+         val >>= 8;
+         *dest = (*dest & 0x80) | (val & 0x7F);
+       }
+      break;
+    case R_ABS:
+      md_number_to_chars (dest, (valueT) val, fixP -> fx_size);
+      break;
+    default:
+      internal_error_a (_("unhandled relocation type in fixup"), fixP -> fx_r_type);
+      break;
+    }
 }
 
 \f
 /* Functions concerning relocs.  */
 
 /* The location from which a PC relative jump should be calculated,
-   given a PC relative reloc.  */
+   given a PC relative reloc.
+
+   For the TIc80, this is the address of the 32 bit opcode containing
+   the PC relative field. */
 
 long
 md_pcrel_from (fixP)
      fixS *fixP;
 {
-  as_fatal ("md_pcrel_from() not implemented yet\n");
-  abort ();
+  return (fixP -> fx_frag -> fr_address + fixP -> fx_where) ;
 }
 
 /*
@@ -622,7 +1035,7 @@ md_convert_frag (headers, seg, fragP)
      segT seg;
      fragS *fragP;
 {
-  as_fatal ("md_convert_frag() not implemented yet\n");
+  internal_error (_("md_convert_frag() not implemented yet"));
   abort ();
 }
 
@@ -640,8 +1053,7 @@ short
 tc_coff_fix2rtype (fixP)
      fixS *fixP;
 {
-  as_fatal ("tc_coff_fix2rtype() not implemented yet\n");
-  abort ();
+  return (fixP -> fx_r_type);
 }
 
 #endif /* OBJ_COFF */
This page took 0.034261 seconds and 4 git commands to generate.