Thu Jul 25 12:03:33 1996 Martin M. Hunt <hunt@pizza.cygnus.com>
authorMartin Hunt <hunt@redhat.com>
Thu, 25 Jul 1996 19:15:14 +0000 (19:15 +0000)
committerMartin Hunt <hunt@redhat.com>
Thu, 25 Jul 1996 19:15:14 +0000 (19:15 +0000)
* tc-d10v.c: Fix packaging bug. Added range checking.
Added kludge for divs instruction.  Fixed minor problem with
multiple text sections.
* tc-d10v.h (d10v_cleanup): Change prototype.

gas/config/tc-d10v.c

index 9316165..b10bed6 100644 (file)
@@ -58,7 +58,7 @@ static int register_name PARAMS ((expressionS *expressionP));
 static int postfix PARAMS ((char *p));
 static bfd_reloc_code_real_type get_reloc PARAMS ((struct d10v_operand *op));
 static int get_operands PARAMS ((expressionS exp[]));
-static unsigned long build_insn PARAMS ((struct d10v_opcode *opcode, expressionS *opers));
+static unsigned long build_insn PARAMS ((struct d10v_opcode *opcode, expressionS *opers, unsigned long insn));
 static void write_long PARAMS ((struct d10v_opcode *opcode, unsigned long insn, Fixups *fx));
 static void write_1_short PARAMS ((struct d10v_opcode *opcode, unsigned long insn, Fixups *fx));
 static int write_2_short PARAMS ((struct d10v_opcode *opcode1, unsigned long insn1, 
@@ -144,6 +144,35 @@ register_name (expressionP)
   return 0;
 }
 
+
+static int
+check_range (num, bits, sign)
+     unsigned long num;
+     int bits;
+     int sign;
+{
+  long min, max;
+  int retval=0;
+
+  if (sign)
+    {
+      max = (1 << (bits - 1)) - 1; 
+      min = - (1 << (bits - 1));  
+      if (((long)num > max) || ((long)num < min))
+       retval = 1;
+    }
+  else
+    {
+      max = (1 << bits) - 1;
+      min = 0;
+      if ((num > max) || (num < min))
+       retval = 1;
+    }
+
+  return retval;
+}
+
+
 void
 md_show_usage (stream)
   FILE *stream;
@@ -372,6 +401,10 @@ d10v_insert_operand (insn, op_type, value, left)
 
   /* truncate to the proper number of bits */
   /* FIXME: overflow checking here? */
+
+  if (check_range (value, bits, d10v_operands[op_type].flags & OPERAND_SIGNED))
+    as_bad("operand out of range: %d",value);
+
   value &= 0x7FFFFFFF >> (31 - bits);
   insn |= (value << shift);
 
@@ -383,15 +416,23 @@ d10v_insert_operand (insn, op_type, value, left)
    and the array of operand expressions and returns the instruction */
 
 static unsigned long
-build_insn (opcode, opers) 
+build_insn (opcode, opers, insn
      struct d10v_opcode *opcode;
      expressionS *opers;
+     unsigned long insn;
 {
-  int i, bits, shift, flags;
-  unsigned long insn;
+  int i, bits, shift, flags, format;
   unsigned int number;
-  insn = opcode->opcode;
-
+  
+  /* the insn argument is only used for the DIVS kludge */
+  if (insn)
+    format = LONG_R;
+  else
+    {
+      insn = opcode->opcode;
+      format = opcode->format;
+    }
+  
   for (i=0;opcode->operands[i];i++) 
     {
       flags = d10v_operands[opcode->operands[i]].flags;
@@ -402,7 +443,7 @@ build_insn (opcode, opers)
       if (flags & OPERAND_REG) 
        {
          number &= REGISTER_MASK;
-         if (opcode->format == LONG_L)
+         if (format == LONG_L)
            shift += 15;
        }
 
@@ -427,10 +468,18 @@ build_insn (opcode, opers)
        }
 
       /* truncate to the proper number of bits */
-      /* FIXME: overflow checking here? */
+      if ((opers[i].X_op == O_constant) && check_range (number, bits, flags & OPERAND_SIGNED))
+       as_bad("operand out of range: %d",number);
       number &= 0x7FFFFFFF >> (31 - bits);
       insn = insn | (number << shift);
     }
+
+  /* kludge: for DIVS, we need to put the operands in twice */
+  /* on the second pass, format is changed to LONG_R to force */
+  /* the second set of operands to not be shifted over 15 */
+  if ((opcode->opcode == OPCODE_DIVS) && (format==LONG_L))
+    insn = build_insn (opcode, opers, insn);
+      
   return insn;
 }
 
@@ -480,7 +529,14 @@ write_1_short (opcode, insn, fx)
   char *f = frag_more(4);
   int i;
 
-  insn |= FM00 | (NOP << 15);
+  /* the other container needs to be NOP */
+  /* according to 4.3.1: for FM=00, sub-instructions performed only
+     by IU cannot be encoded in L-container. */
+  if (opcode->unit == IU)
+    insn |= FM00 | (NOP << 15);                /* right container */
+  else
+    insn = FM00 | (insn << 15) | NOP;  /* left container */
+
   /*  printf("INSN: %08x\n",insn);  */
   number_to_chars_bigendian (f, insn, 4);
   for (i=0; i < fx->fc; i++) 
@@ -720,7 +776,7 @@ do_assemble (str, opcode)
   *opcode = (struct d10v_opcode *)hash_find (d10v_hash, name);
   if (*opcode == NULL)
     {
-      as_bad ("unknown opcode");
+      as_fatal ("unknown opcode: %s",name);
       return;
     }
 
@@ -730,56 +786,94 @@ do_assemble (str, opcode)
   /* get all the operands and save them as expressions */
   numops = get_operands (myops);
 
-  /* now search the opcode table table for one with operands */
-  /* that match what we've got */
-  do
+  /* now see if the operand is a fake.  If so, find the correct size */
+  /* instruction, if possible */
+  match = 0;
+  if ((*opcode)->format == OPCODE_FAKE)
     {
-      match = 1;
-      for (i = 0; (*opcode)->operands[i]; i++) 
+      int opnum = (*opcode)->operands[0];
+      if (myops[opnum].X_op == O_constant)
        {
-         int flags = d10v_operands[(*opcode)->operands[i]].flags;
-
-         if (myops[i].X_op==0) 
+         next_opcode=(*opcode)+1;
+         for (i=0; (*opcode)->operands[i+1]; i++)
            {
-             match=0;
-             break;
+             int bits = d10v_operands[next_opcode->operands[opnum]].bits;
+             int flags = d10v_operands[next_opcode->operands[opnum]].flags;
+             if (!check_range (myops[opnum].X_add_number, bits, flags & OPERAND_SIGNED))
+               {
+                 match = 1;
+                 break;
+               }
+             next_opcode++;
            }
-
-         if (flags & OPERAND_REG) 
+       }
+      else
+       {
+         /* not a constant, so use a long instruction */
+         next_opcode = (*opcode)+2;
+         match = 1;
+       }
+      if (match)
+       *opcode = next_opcode;
+      else
+       as_fatal ("value out of range");
+    }
+  else
+    {
+      /* now search the opcode table table for one with operands */
+      /* that match what we've got */
+      while (!match)
+       {
+         match = 1;
+         for (i = 0; (*opcode)->operands[i]; i++) 
            {
-             if ((myops[i].X_op != O_register) ||
-                 ((flags & OPERAND_ACC) != (myops[i].X_add_number & OPERAND_ACC)) ||
-                 ((flags & OPERAND_FLAG) != (myops[i].X_add_number & OPERAND_FLAG)) ||
-                 ((flags & OPERAND_CONTROL) != (myops[i].X_add_number & OPERAND_CONTROL)))
+             int flags = d10v_operands[(*opcode)->operands[i]].flags;
+             int X_op = myops[i].X_op;
+             int num = myops[i].X_add_number;
+             
+             if (X_op==0) 
                {
                  match=0;
                  break;
-               }         
-           }
-
-         if (((flags & OPERAND_MINUS) && ((myops[i].X_op != O_absent) || (myops[i].X_add_number != OPERAND_MINUS))) ||
-             ((flags & OPERAND_PLUS) && ((myops[i].X_op != O_absent) || (myops[i].X_add_number != OPERAND_PLUS))) ||
-             ((flags & OPERAND_ATMINUS) && ((myops[i].X_op != O_absent) || (myops[i].X_add_number != OPERAND_ATMINUS))) ||
-             ((flags & OPERAND_ATPAR) && ((myops[i].X_op != O_absent) || (myops[i].X_add_number != OPERAND_ATPAR))) ||
-             ((flags & OPERAND_ATSIGN) && ((myops[i].X_op != O_absent) || (myops[i].X_add_number != OPERAND_ATSIGN)))) 
-           {
-             match=0;
-             break;
+               }
+             
+             if (flags & OPERAND_REG) 
+               {
+                 if ((X_op != O_register) ||
+                     ((flags & OPERAND_ACC) != (num & OPERAND_ACC)) ||
+                     ((flags & OPERAND_FLAG) != (num & OPERAND_FLAG)) ||
+                     ((flags & OPERAND_CONTROL) != (num & OPERAND_CONTROL)))
+                   {
+                     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)))) 
+               {
+                 match=0;
+                 break;
+               }
+             
            }
+         
+         /* we're only done if the operands matched AND there
+            are no more to check */
+         if (match && myops[i].X_op==0) 
+           break;
+         
+         next_opcode = (*opcode)+1;
+         if (next_opcode->opcode == 0) 
+           break;
+         if (strcmp(next_opcode->name, (*opcode)->name))
+           break;
+         (*opcode) = next_opcode;
        }
-
-      /* we're only done if the operands matched AND there
-        are no more to check */
-      if (match && myops[i].X_op==0) 
-       break;
-
-      next_opcode = (*opcode)+1;
-      if (next_opcode->opcode == 0) 
-       break;
-      if (strcmp(next_opcode->name, (*opcode)->name))
-         break;
-      (*opcode) = next_opcode;
-    } while (!match);
+    }
 
   if (!match)  
     {
@@ -812,8 +906,8 @@ do_assemble (str, opcode)
 
   /* at this point, we have "opcode" pointing to the opcode entry in the
      d10v opcode table, with myops filled out with the operands. */
-  insn = build_insn ((*opcode), myops); 
-  /*  printf("sub-insn = %lx\n",insn); */
+  insn = build_insn ((*opcode), myops, 0); 
+  /* printf("sub-insn = %lx\n",insn); */
 
   return (insn);
 }
@@ -947,12 +1041,13 @@ md_apply_fix3 (fixp, valuep, seg)
    instructions to see if it can package them with the next instruction, there may
    be a short instruction that still needs written.  */
 int
-d10v_cleanup()
+d10v_cleanup (done)
+     int done;
 {
   segT seg;
   subsegT subseg;
 
-  if (prev_opcode) 
+  if ( prev_opcode && (done  || (now_seg == prev_seg) && (now_subseg == prev_subseg)))
     {
       seg = now_seg;
       subseg = now_subseg;