/* $Header:opeval.c 12.0$ */
/* $ACIS:opeval.c 12.0$ */
/* $Source: /ibm/acis/usr/src/lib/c2_ca/RCS/opeval.c,v $ */

#ifndef lint
static char *rcsid = "$Header:opeval.c 12.0$";
#endif

/* OPEVAL.C */
 
/* Evaluate opcodes and operands of instructions
 
   External routines:
      OpcodeEval()     Return opStat pointer for an opcode
      Op1Eval()        Evaluate 1st operand
      Op2Eval()        Evaluate 2nd operand
      RegNumber()      Return the number of a register reference
      FregEval()       Convert _freg operand to usable form
 
*/
 
#include "stdio.h"
#include "opt.h"
#include "error.h"
#include "instps.h"
 
#define xdig2bin(c)  (isdig(c) ? (c)-'0' : (c)+10-(isupc(c)?'A':'a'))
 
extern struct opStat OpTab[];
 
#define HASHSIZE 2 * i_highopno
static int hashBuilt = FALSE;
static short opHash [HASHSIZE];
 
 
struct opStat *lookupOp();
 
 
/* Evaluate an Opcode.
   Return a pointer to the opcode table for it
*/
struct opStat *OpcodeEval( opcode )
register char *opcode;    /* pointer to an opcode */
{
   register struct opStat *cp;
 
   if( !hashBuilt )  {
      buildHash();
      hashBuilt = TRUE;
      }
 
   cp = lookupOp( opcode );
   if( cp == NULL )  {
      Error( E_OPCODE, opcode, NULL );
      return NULL;
      }
   return cp;
}
 
 
/* build hash table of opcodes */
static buildHash()
{
   register int i, h;
   register struct opStat *cp;
 
   for( i=0; i<HASHSIZE; ++i )
      opHash[i] = 0;
 
   for (i=0, cp=OpTab; *(cp->opname); cp++, i++ )   {
      h = hash(cp->opname);
  /*  if(Debug) fprintf(stderr,"Hash: %d of %s to %d prev %d\n",
                        h, cp->opname, i, opHash[h] );    */
      if( opHash[h]==0 || opHash[h]>i )
         opHash[h] = i;
      }
}
 
/* look up an opcode in the opcode table */
static struct opStat *lookupOp( opcode )
register char *opcode;
{
   register int i;
   register struct opStat *cp;
 
   i = hash(opcode);
/* if(Debug) fprintf(stderr,"lookupOp: hash %d of %s is %d '%s'\n",
                i, opcode, opHash[i], OpTab[opHash[i]].opname ); */
   for (cp = &OpTab[opHash[i]]; *(cp->opname); cp++ )
      if (!strcmp(cp->opname, opcode))
         return cp;
   return NULL;
}
 
/* hash an opcode */
static hash(s)
register char *s;
{
   register char s0, s1;
   register int i;
   s0 = *s;
   s1 = *(++s);
   if( s1==0 )
      return s0<<1;
   i = ( s0*s1 + *(++s) );
   while( i >= HASHSIZE )
      i -= HASHSIZE;
   return i;
}
 
   /* ************************************************** */
 
 
/* Evaluate a first operand of an instruction.
   It can either be a register or an unknown
*/
/* ---
Op1Eval( op1 )
register char *op1;
{
   if (RegNumber(op1) >= 0)
      return optype_reg;
   return type_unknown;
}
--- */
 
                  /* ---------------- */
 
/* Evaluate the second or third operand of an instruction.
   It can be a register or an unknown.  (We really don't
   care at this point what it is if not a register.
   individual optimizations may examine operand further.)
*/
/* ---
Op2Eval( op2 )
register char *op2;
{
   if (RegNumber(op2) >= 0)
      return optype_reg;
   return type_unknown;
}
--- */
 
                  /* ---------------- */
 
 
/* Evaluate operand to see if has form of a register.
   If is 'sp', 'dp', 'bp', 'fp', or 'r0' thru 'r15'
   return register number which corresponds.
   Else return -1.
*/
int RegNumber( s )
register char *s;
{
   if(s==NULL)    return -1;
   if(*s==0)      return -1;
   if(*(s+1)==0)  return -1;
 
   /* special forms:  sp, dp, bp, fp */
   if (*(s+1)=='p')
      if (*(s+2)=='\0')  {
         if (*s=='s')   return SPREG;    /* sp */
         if (*s=='d')   return DPREG;    /* dp */
/*       if (*s=='b')   return BPREG;    /* bp */
         if (*s=='f')   return FPREG;    /* fp */
         }
      else return -1;
 
   /* 'r' form */
   if (*s!='r')   return -1;
   s++;
 
   /* a number from 0 to 9  */
   if (*(s+1)=='\0')
      if (*s>='0' && *s<='9')
         return (*s)-'0';
      else return -1;
 
   /* a number from 10 to 15 */
   if (*(s+2)=='\0')
      if (*s++=='1')
         if (*s>='0' && *s<='5')
            return 10 + (*s) - '0';
 
   /* all else failed; must not be a register */
   return -1;
}
 
                  /* ---------------- */
 
/* given the operand of a _freg, return a mask indicating
   which registers are free                              */
FregEval( c )
char *c;
{
   register int n, result;
   static char allfree[] = ALL_FREE;
 
   /* check for old form _freg which means everything free */
   if( c == NULL || *c!='0' )
      c = allfree;
   if(Debug) fprintf(stderr,"\n\nFregEval input is %s\n",c);
   n = 0;
 
   /* see if _freg operand looks ok & convert to an int */
   if( *c++ != '0' )                 return 0;
   if( *c++ != 'x' )                 return 0;
/*   if( *(c+1) != 0 )  {
      if( !isxdig(*c) )              return 0;
      n = xdig2bin(*c) << 4;
      c++;
      if(Debug)  fprintf(stderr, "n = 0x%x\n", n );
      }
   n += xdig2bin( *c );*/
   { int tmp; sscanf(c,"%4x",&tmp); n=tmp; }
   if(Debug)  fprintf(stderr, "n = 0x%x\n", n );
 
   result = 0;
   if ( (FREG_R0  & n) != 0 )   result |= 0x8000;
   if ( (FREG_R1  & n) != 0 )   result |= 0x4000;
   if ( (FREG_R2  & n) != 0 )   result |= 0x2000;
   if ( (FREG_R3  & n) != 0 )   result |= 0x1000;
   if ( (FREG_R4  & n) != 0 )   result |= 0x0800;
   if ( (FREG_R5  & n) != 0 )   result |= 0x0400;
   if ( (FREG_R6  & n) != 0 )   result |= 0x0200;
   if ( (FREG_R7  & n) != 0 )   result |= 0x0100;
   if ( (FREG_R8  & n) != 0 )   result |= 0x0080;
   if ( (FREG_R9  & n) != 0 )   result |= 0x0040;
   if ( (FREG_R10 & n) != 0 )   result |= 0x0020;
   if ( (FREG_R11 & n) != 0 )   result |= 0x0010;
   if ( (FREG_R12 & n) != 0 )   result |= 0x0008;
   if ( (FREG_R13 & n) != 0 )   result |= 0x0004;
   if ( (FREG_R14 & n) != 0 )   result |= 0x0002;
   if ( (FREG_R15 & n) != 0 )   result |= 0x0001;
 
   if(Debug)  fprintf(stderr, "FregEval result is 0x%04x\n", result);
   return result;
}
