/****************************************************************************
    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 TAP_Z	0
#define TAP_I	1

#define TAP_DELAY 0 

struct tap_data {
  int msb;
  int lsb;
};

static SGate *Tap_copyGate(SGate *sg,const char *name,SModule *M);
static void Tap_processEvent(SGate*,EvQueue*,SEvent*);
static int Tap_checkGate(SGate *g);
static void Tap_setProp(SGate*,const char*,const void*);
static void Tap_propFrwdDelay(SPort *P,simTime t);
static void Tap_propBackDelay(SPort *P,simTime t);
static simTime Tap_delay(SPort *Pfrom,SPort *Pto);

static SGateInfo tap_info = {
  0,
  "tran",0x0,
  2,{{"Z",GIO_OUT,0},
       {"I",GIO_IN,0}},

  {{0}},

  Tap_copyGate,
  Tap_processEvent,
  Tap_checkGate,
  Nop_initGate,
  Tap_setProp,
  0,
  0,
  Tap_propFrwdDelay,
  Tap_propBackDelay,
  Tap_delay
};

void init_tap()
{
  SGateInfo_register(&tap_info,0);
}


static SGate *Tap_copyGate(SGate *sg,const char *name,SModule *M)
{
  SGate *ng = Generic_copyGate(sg,name,M);
  struct tap_data *nd = (struct tap_data *) malloc(sizeof(struct tap_data));
  struct tap_data *sd = (struct tap_data *) sg->g_data;

  ng->g_data = nd; 
  *nd = *sd;

  return ng;
}

static void Tap_processEvent(SGate *g,EvQueue *Q,SEvent *E)
{
  SPort *Z = g->g_ports.port[TAP_Z];
  SState *I = SGate_allocPortState(g,TAP_I);
  SState *S = alloc_SState();
  struct tap_data *d = (struct tap_data *)g->g_data;
  int wc = SSNUMWORDS(I->nbits);
  int w = d->lsb >> SSWORDSHIFT;
  int b = d->lsb & SSBITMASK;

  SState_reinit(S,Z->p_state.nbits);

  S->flt[0] = I->flt[w] >> b;
  S->one[0] = I->one[w] >> b;
  S->zero[0] = I->zero[w] >> b;

  if (w+1 < wc && (SSWORDSIZE-b) > (d->msb-d->lsb+1)) {
    S->flt[0] |= I->flt[w+1] << (SSWORDSIZE-b);
    S->one[0] |= I->one[w+1] << (SSWORDSIZE-b);
    S->zero[0] |= I->zero[w+1] << (SSWORDSIZE-b);
  }

  EvQueue_setPort(Q,Z,S,0);		/* no delay */

  free_SState(I);
  free_SState(S);
}

int Tap_checkGate(SGate *g)
{
  SPort *Z = g->g_ports.port[TAP_Z];
  SPort *I = g->g_ports.port[TAP_I];
  struct tap_data *d = (struct tap_data *)g->g_data;
  int Zbits = Z->p_net->n_nbits;
  int Ibits = I->p_net->n_nbits;

  if (d->msb < d->lsb || d->lsb < 0) {
    errorGate(g->g_name,"Range \\[%d:%d\\] is illegal "
	      "(msb must be >= lsb and lsb must be >= 0)."
	      ,d->msb,d->lsb);
    return -1;
  }

  if (Zbits > 32) {
    errorGate(g->g_name,"Ranges greater than 32 bits are unsupported.");
    return -1;
  }


  if (d->msb >= Ibits) {
    errorGate(g->g_name,"Range \\[%d:%d\\] falls outside range of "
	      "addressed wire."
	      ,d->msb,d->lsb);
    return -1;
  }

  if (d->msb-d->lsb+1 != Zbits) {
    errorGate(g->g_name,"Number of bits in range \\[%d:%d\\] does "
	      "not match size of output.",d->msb,d->lsb);
    return -1;
  }

  return 0;
}

static void Tap_setProp(SGate *g,const char *prop,const void *val)
{
  struct tap_data *d = (struct tap_data *)g->g_data;
  int *n = (int*)val;

  if (!d) {
    d = (struct tap_data *) malloc(sizeof(struct tap_data));
    g->g_data = d; 
  }

  if (strcmp(prop,"/msb") == 0)
    d->msb = *n;
  else if (strcmp(prop,"/lsb") == 0)
    d->lsb = *n;
}

static void Tap_propFrwdDelay(SPort *P,simTime t)
{
  SGate *g = P->p_gate;

  if (P == g->g_ports.port[TAP_I])
    SNet_propFrwdDelay(g->g_ports.port[TAP_Z]->p_net,t);
}

static void Tap_propBackDelay(SPort *P,simTime t)
{
  SGate *g = P->p_gate;

  if (P == g->g_ports.port[TAP_Z])
    SNet_propBackDelay(g->g_ports.port[TAP_I]->p_net,t);
}

static simTime Tap_delay(SPort *Pfrom,SPort *Pto)
{
  if (!Pto) return 0;

  if (Pfrom == Pto)
    return NO_TIME;

  return 0;
}


