/****************************************************************************
    Copyright (C) 1987-2005 by Jeffery P. Hansen

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "gsim.h"

#define BUFIF_Z	0
#define BUFIF_I	1
#define BUFIF_E	2

#define BUFIF_DELAY_EZ	0
#define BUFIF_DELAY_IZ	1

static void Bufif_processEvent(SGate*,EvQueue*,SEvent*);
static int Bufif_checkGate(SGate *g);

static SGateInfo bufif_info = {
  0,
  "bufif1:notif1:bufif0:notif0",0x5,
  3,{{"Z",GIO_TRI,0},
       {"I",GIO_IN,0},
       {"E",GIO_IN,0}},
  {{"E-Z",bit(2),0},{"I-Z",bit(1),0},{0}},

  Generic_copyGate,
  Bufif_processEvent,
  Bufif_checkGate,
  Nop_initGate,
  Generic_setProp,
  0,
  0,
  Generic_propFrwdDelay,
  Generic_propBackDelay,
  Generic_delay,
};

void init_bufif()
{
  SGateInfo_register(&bufif_info,0);
}


static void Bufif_processEvent(SGate *g,EvQueue *Q,SEvent *E)
{
  SPort *Z = g->g_ports.port[BUFIF_Z];
  SState *iS = SGate_allocPortState(g,BUFIF_I);
  SState *eS = SGate_allocPortState(g,BUFIF_E);
  SState *S = alloc_SState();
  int delay;

  SState_expandExtend(eS,iS->nbits);
#if 0
  printf("echo bufif: ");
  SState_print(eS,stdout);
  printf("  ->  ");
  SState_print(eS,stdout);
  printf("\n");
#endif

  SState_bufif(S,iS,eS);

  if (IsChangeOn(E,g,BUFIF_I))
    delay = g->g_delayParms[BUFIF_DELAY_IZ];
  else
    delay = g->g_delayParms[BUFIF_DELAY_EZ];

  EvQueue_setPort(Q,Z,S,delay);

  free_SState(iS);
  free_SState(eS);
  free_SState(S);
}

int Bufif_checkGate(SGate *g)
{
  int ob,ib;

  if (g->g_ports.num != 3) {
    errorGate(g->g_name,"%s gates should have exactly three pins.",g->g_type->gi_name);
    return -1;
  }

  ob = g->g_ports.port[0]->p_net->n_nbits;
  ib = g->g_ports.port[1]->p_net->n_nbits;

  if (ib != ob) {
    errorGate(g->g_name,
	      "Input and output on %s gates must have matching bit width."
	      ,g->g_type->gi_name);
    return -1;
  }

  return 0;
}
