/* ###--------------------------------------------------------------### */
/* function     : bvl_crtabl                                            */
/* description  : combine at most two ABLs and build a new one          */
/*    		  The following operations can be performed :		*/
/*		    CONC    perform concatenation			*/
/*		    NOPI    initialize a structure for a signal (scalar	*/
/*		            or array)					*/
/*		    NOPS    initialize a structure for a literal	*/
/*		    NE      create a structure with an ABL representing	*/
/*		            the 'non equality' of two expressions	*/
/*		    EQ      create a structure with an ABL representing	*/
/*		            the 'equality' of two expressions		*/
/*		    NOT     perform logical not of an expression	*/
/*		    AND     perform logical and  between two expressions*/
/*		    OR      perform logical or   between two expressions*/
/*		    NAND    perform logical nand between two expressions*/
/*		    NOR     perform logical nor  between two expressions*/
/*		    XOR     perform logical xor  between two expressions*/
/*		    ANDM    perform logical and  between two expressions*/
/*		            (the second expression is a scalar)		*/
/* called func. : createAtom , createExpr, addQExpr , bvl_toolbug,	*/
/*		  bvl_error , addchain   , freechain			*/
/* ###--------------------------------------------------------------### */

bvl_ablstr bvl_crtabl ( oper, expr1, expr2, left, right )

short      oper;
bvl_ablstr expr1;
bvl_ablstr expr2;
int        left;
int        right;

  {
  char            name[256];
  char           *name2;
  struct chain   *pt_abl1;
  struct chain   *pt_abl2;
  struct chain   *pt_aux1;
  struct chain   *pt_aux2;
  bvl_ablstr      result;
  char            lcl_buffer[256];
  short           inc;
  short           i;

  result.IDENT    = NULL;
  result.LIST_ABL = NULL;
  result.WIDTH    = 0;

  switch (oper)
    {
    case CONC :
      if ((expr1.LIST_ABL == NULL) || (expr2.LIST_ABL == NULL))
        bvl_toolbug (4,"bvl_crtabl",NULL,0);
      else
        {
        if (expr1.LIST_ABL == expr2.LIST_ABL)
          bvl_toolbug (16,"bvl_crtabl",NULL,0);
        else
          {
          pt_aux2 = expr2.LIST_ABL;
          while (pt_aux2->NEXT != NULL)
            pt_aux2 = pt_aux2->NEXT;

          pt_aux2->NEXT = expr1.LIST_ABL;

          result.LIST_ABL = expr2.LIST_ABL;
          result.WIDTH    = expr1.WIDTH + expr2.WIDTH;

          expr1.LIST_ABL  = NULL;
          expr2.LIST_ABL  = NULL;
          }
        }
      break;

      case NOPI :
        if ( expr1.IDENT == NULL )
          bvl_toolbug (2,"bvl_crtabl",NULL,0);
        else
          {
          if ((left == -1) && (right == -1))
            {
            result.LIST_ABL = addchain(result.LIST_ABL,createAtom(expr1.IDENT));
            result.WIDTH    = 1;
            }
          else
            {
            if (left <= right)
              {
              inc = 1;
              result.WIDTH = right - left + 1;
              }
            else
              {
              inc = -1;
              result.WIDTH = left - right + 1;
              }

            for (i=left ; i!=(right+inc) ; i+=inc)
              {
              sprintf (name,"%s %i",expr1.IDENT,i);
              name2           = namealloc (name);
              result.LIST_ABL = addchain (result.LIST_ABL,createAtom(name2));
              }
            }
          expr1.IDENT = NULL;
          }
        break;

      case NOPS :
        if ( expr1.IDENT == NULL )
          bvl_toolbug (2,"bvl_crtabl",NULL,0);
        else
          {
          bvl_tobin (lcl_buffer,expr1.IDENT,-1,-1);
          if ((left == -1) && (right == -1))
            {
            left = 0;
            right = strlen (lcl_buffer) - 1;
            }

          for (i=left ; i<=right ; i++)
            {
            switch ( lcl_buffer[i] )
              {
              case '0' :
                result.LIST_ABL = addchain (result.LIST_ABL,createAtom("'0'"));
                break;
              case '1' :
                result.LIST_ABL = addchain (result.LIST_ABL,createAtom("'1'"));
                break;
/*-----------        Beware Not VHDL        -------------*/
              case 'd' :
                result.LIST_ABL = addchain (result.LIST_ABL,createAtom("'D'"));
                break;
              default  :
                bvl_toolbug (15,"bvl_crtabl",NULL,expr1.IDENT[i]);
              }
            }
          result.WIDTH = right - left + 1;
          }
        break;

      case STABLE :
        if (expr1.LIST_ABL == NULL)
	  bvl_toolbug (3,"bvl_crtabl",NULL,0);
        else
          {
	  pt_aux1  = expr1.LIST_ABL;
          while (pt_aux1 != NULL)
            {
            pt_abl1  = createExpr (STABLE);
            addQExpr (pt_abl1, (struct chain *)pt_aux1->DATA);
            pt_aux1->DATA = pt_abl1;
            pt_aux1  = pt_aux1->NEXT;
            }

          result.LIST_ABL = expr1.LIST_ABL;
          result.WIDTH    = expr1.WIDTH;

          expr1.LIST_ABL  = NULL;
          }
        break;

      case NOT :
        if (expr1.LIST_ABL == NULL)
	  bvl_toolbug (3,"bvl_crtabl",NULL,0);
        else
          {
	  pt_aux1  = expr1.LIST_ABL;
          while (pt_aux1 != NULL)
            {
            pt_abl1  = createExpr (NOT);
            addQExpr (pt_abl1, (struct chain *)pt_aux1->DATA);
            pt_aux1->DATA = pt_abl1;
            pt_aux1  = pt_aux1->NEXT;
            }

          result.LIST_ABL = expr1.LIST_ABL;
          result.WIDTH    = expr1.WIDTH;

          expr1.LIST_ABL  = NULL;
          }
        break;

      case EQ :
        if ((expr1.LIST_ABL == NULL) || (expr2.LIST_ABL == NULL))
	  bvl_toolbug (4,"bvl_crtabl",NULL,0);
        else
          {
          if (expr1.WIDTH != expr2.WIDTH)
            {
            bvl_error (38,NULL);
            pt_abl2 = createAtom ("'1'");

	    pt_aux1 = expr1.LIST_ABL;
            while (pt_aux1 != NULL)
              {
              freeExpr (pt_aux1->DATA);
              pt_aux1 = pt_aux1->NEXT;
              }
	    pt_aux2 = expr2.LIST_ABL;
            while (pt_aux2 != NULL)
              {
              freeExpr (pt_aux2->DATA);
              pt_aux2 = pt_aux2->NEXT;
              }
            }
          else
            {
	    pt_aux1 = expr1.LIST_ABL;
	    pt_aux2 = expr2.LIST_ABL;

            pt_abl1 = createExpr (XOR);
            addQExpr (pt_abl1, (struct chain *)pt_aux1->DATA);
            addQExpr (pt_abl1, (struct chain *)pt_aux2->DATA);

            pt_aux1 = pt_aux1->NEXT;
            pt_aux2 = pt_aux2->NEXT;

	    while (pt_aux1 != NULL)
	      {
              pt_abl2 = createExpr (OR);
              addQExpr (pt_abl2,pt_abl1);

              pt_abl1 = createExpr (XOR);
              addQExpr (pt_abl1, (struct chain *)pt_aux1->DATA);
              addQExpr (pt_abl1, (struct chain *)pt_aux2->DATA);

              addQExpr (pt_abl2, pt_abl1);
              pt_abl1 = pt_abl2;

	      pt_aux1 = pt_aux1->NEXT;
	      pt_aux2 = pt_aux2->NEXT;
	      }
            pt_abl2 = createExpr (NOT);
            addQExpr (pt_abl2, pt_abl1);

            }
          result.LIST_ABL = addchain (result.LIST_ABL,pt_abl2);
          result.WIDTH    = 1;
          freechain (expr1.LIST_ABL);
          freechain (expr2.LIST_ABL);
          expr1.LIST_ABL = NULL;
          expr2.LIST_ABL = NULL;
          }
        break;

      case NE :

        if ((expr1.LIST_ABL == NULL) || (expr2.LIST_ABL == NULL))
	  bvl_toolbug (4,"bvl_crtabl",NULL,0);
        else
          {
          if (expr1.WIDTH != expr2.WIDTH)
            {
            bvl_error(38,NULL);
            pt_abl1 = createAtom ("'1'");

	    pt_aux1 = expr1.LIST_ABL;
            while (pt_aux1 != NULL)
              {
              freeExpr (pt_aux1->DATA);
              pt_aux1 = pt_aux1->NEXT;
              }
	    pt_aux2 = expr2.LIST_ABL;
            while (pt_aux2 != NULL)
              {
              freeExpr (pt_aux2->DATA);
              pt_aux2 = pt_aux2->NEXT;
              }
            }
          else
            {
	    pt_aux1 = expr1.LIST_ABL;
	    pt_aux2 = expr2.LIST_ABL;

            pt_abl1 = createExpr (XOR);
            addQExpr (pt_abl1, (struct chain *)pt_aux1->DATA);
            addQExpr (pt_abl1, (struct chain *)pt_aux2->DATA);

            pt_aux1 = pt_aux1->NEXT;
            pt_aux2 = pt_aux2->NEXT;

	    for (i=2 ; i<=expr1.WIDTH ; i++)
	      {
              pt_abl2 = createExpr (OR);
              addQExpr (pt_abl2, pt_abl1);

              pt_abl1 = createExpr (XOR);
              addQExpr (pt_abl1, (struct chain *)pt_aux1->DATA);
              addQExpr (pt_abl1, (struct chain *)pt_aux2->DATA);

              addQExpr (pt_abl2, pt_abl1);
              pt_abl1 = pt_abl2;

	      pt_aux1 = pt_aux1->NEXT;
	      pt_aux2 = pt_aux2->NEXT;
	      }

            }
          result.LIST_ABL = addchain(result.LIST_ABL,pt_abl1);
          result.WIDTH    = 1;
          freechain (expr1.LIST_ABL);
          freechain (expr2.LIST_ABL);
          expr1.LIST_ABL = NULL;
          expr2.LIST_ABL = NULL;
          }
        break;

      case AND  :
      case NAND :
      case OR   :
      case NOR  :
      case XOR  :

        if (expr1.LIST_ABL == NULL)
          {
          if (expr2.LIST_ABL == NULL)
	    bvl_toolbug (4,"bvl_crtabl",NULL,0);
          else
            {
            result.LIST_ABL = expr2.LIST_ABL;
            result.WIDTH    = expr2.WIDTH;
            expr2.LIST_ABL  = NULL;
            }
          }
        else
          {
          if (expr2.LIST_ABL == NULL)
            {
            result.LIST_ABL = expr1.LIST_ABL;
            result.WIDTH    = expr1.WIDTH;
            expr1.LIST_ABL  = NULL;
            }
          else
            {
            if (expr1.LIST_ABL == expr2.LIST_ABL)
              bvl_toolbug (16,"bvl_crtabl",NULL,0);
            else
              {
              if (expr1.WIDTH != expr2.WIDTH)
                bvl_error(38,NULL);
              else
                {
	        pt_aux1 = expr1.LIST_ABL;
	        pt_aux2 = expr2.LIST_ABL;

	        for (i=1 ; i<=expr1.WIDTH ; i++)
	          {
                  pt_abl1 = createExpr (oper);
                  addQExpr (pt_abl1, (struct chain *)pt_aux1->DATA);
                  addQExpr (pt_abl1, (struct chain *)pt_aux2->DATA);

                  pt_aux1->DATA = (void *)pt_abl1;

	          pt_aux1       = pt_aux1->NEXT;
	          pt_aux2       = pt_aux2->NEXT;
		  }
                }
              result.LIST_ABL = expr1.LIST_ABL;
              result.WIDTH    = expr1.WIDTH;
              freechain (expr2.LIST_ABL);
              expr1.LIST_ABL  = NULL;
              expr2.LIST_ABL  = NULL;
              }
            }
          }
        break;

      case ANDM :
        if ((expr1.LIST_ABL == NULL) || (expr2.LIST_ABL == NULL))
	    bvl_toolbug (4,"bvl_crtabl",NULL,0);
        else
          {
          if (expr2.WIDTH != 1)
            bvl_error(38,NULL);
          else
            {
            pt_aux1 = expr1.LIST_ABL;
            pt_aux2 = expr2.LIST_ABL;
            while (pt_aux1 != NULL)
              {
              pt_abl1 = createExpr (AND);
              addQExpr (pt_abl1,          (struct chain *)pt_aux1->DATA);
              addQExpr (pt_abl1, copyExpr((struct chain *)pt_aux2->DATA));
              pt_aux1->DATA = (void *)pt_abl1;

              pt_aux1       = pt_aux1->NEXT;
              }
            }
          result.LIST_ABL = expr1.LIST_ABL;
          result.WIDTH    = expr1.WIDTH;
	  pt_aux2 = expr2.LIST_ABL;
          while (pt_aux2 != NULL)
            {
            freeExpr (pt_aux2->DATA);
            pt_aux2 = pt_aux2->NEXT;
            }
          freechain (expr2.LIST_ABL);
          expr2.LIST_ABL  = NULL;
          expr1.LIST_ABL  = NULL;
          }
        break;

      default :
	bvl_toolbug (1,"bvl_crtabl",NULL,0);
      }

    return (result);
    }

