/****************************************************************************
    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 <ctype.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include "gsim.h"

#define DEBUG_SSTATE 0

#define isBDig(x)	((x) == SYM_ONE || (x) == SYM_ZERO)

#define ABS(n)		((n) < 0 ? -(n) : (n))

static char *sym_table_lc = "*01@zlhx";
static char *sym_table = "*01@zLHx";
static int sym_table_len = 8;

static int sstate_print_showbits = 1;

static struct sstate_fl *sstate_freePool = 0;

/*
  Allocate a temporary SState object
 */
SState *alloc_SState()
{
  struct sstate_fl *S;
  static int fl_count = 0;
  static int ma_count = 0;

  if (!sstate_freePool) {
    S = (struct sstate_fl*) malloc(sizeof(struct sstate_fl));
    SState_init((SState*)S,1);
    S->state.status = 1;
    ma_count++;
  } else {
    S = sstate_freePool;
    sstate_freePool = sstate_freePool->next;
    fl_count++;
    if (S->state.status != -1) {
      printf("echo bad state object in alloc_State() (status=%d).\n",S->state.status);
      assert(0);
    }
    S->state.status = 2;
  }

#if DEBUG_SSTATE
  if (((fl_count+ma_count) % 10000) == 0) {
    printf("echo sstate: fl:%d ma:%d   (%f)\n",fl_count,ma_count,(double)ma_count/(double)fl_count);
    fflush(stdout);
  }
#endif

  return (SState*)S;
}

void free_SState(SState *S)
{
  struct sstate_fl *flS = (struct sstate_fl *)S;
  flS->next = sstate_freePool;
  sstate_freePool = flS;

  if (S->status != 1 && S->status != 2) {
    printf("echo bad state object in free_State() (status=%d).\n",S->status);
    assert(0);
  }
  S->status = -1;
}

/*
         Logic values
         0  1  x  z  L  H
-------------------------
one      0  1  1  0  0  1
zero     1  0  1  0  1  0
flt      0  0  1  1  1  1
*/

static int charToSym(char c)
{
  char *x;
  int p;

  if (isupper((int)c))
    c = tolower(c);
  x = strchr(sym_table_lc,c);
  p = x ? (x-sym_table_lc) : SYM_ZERO;

  return p;
}

void SState_init(SState *S,int nbits)
{
  int wc = SSNUMWORDS(nbits);

  S->status = 0;
  S->nbits = nbits;
  S->nalloc = wc;
  S->one = (unsigned*)malloc(wc*sizeof(unsigned));
  S->zero = (unsigned*)malloc(wc*sizeof(unsigned));
  S->flt = (unsigned*)malloc(wc*sizeof(unsigned));

  SState_zero(S);
}

void SState_uninit(SState *S)
{
  free(S->one);
  free(S->zero);
  free(S->flt);
}

void SState_reinit(SState *S,int nbits)
{
  int nwc = SSNUMWORDS(nbits);

  if (nwc <= S->nalloc) {
    S->nbits = nbits;
    S->nalloc = nwc;
    SState_zero(S);
  } else {
    SState_uninit(S);
    SState_init(S,nbits);
  }
}

int SState_eq(SState *A,SState *B)
{
  int wc = SSNUMWORDS(A->nbits);
  unsigned mask = (A->nbits==SSWORDSIZE) ? SSWORDMASK : ((1<<(A->nbits & SSBITMASK))-1);
  int i;

  

  if (A->nbits != B->nbits) return 0;

  for (i = 0;i < wc;i++) {
    if (i == wc-1) {
      if ((A->one[i]&mask) != (B->one[i]&mask)) return 0;
      if ((A->zero[i]&mask) != (B->zero[i]&mask)) return 0;
      if ((A->flt[i]&mask) != (B->flt[i]&mask)) return 0;
    } else {
      if (A->one[i] != B->one[i]) return 0;
      if (A->zero[i] != B->zero[i]) return 0;
      if (A->flt[i] != B->flt[i]) return 0;
    }
  }

  return 1;  
}

void SState_unknown(SState *S)
{
  int wc = SSNUMWORDS(S->nbits);
  int i;

  for (i = 0;i < wc;i++) {
    S->zero[i] = ~0;
    S->one[i] = ~0;
    S->flt[i] = ~0;
  }
}

void SState_float(SState *S)
{
  int wc = SSNUMWORDS(S->nbits);
  int i;

  for (i = 0;i < wc;i++) {
    S->zero[i] = 0;
    S->one[i] = 0;
    S->flt[i] = ~0;
  }
}

void SState_zero(SState *S)
{
  int wc = SSNUMWORDS(S->nbits);
  int i;

  for (i = 0;i < wc;i++) {
    S->zero[i] = ~0;
    S->one[i] = 0;
    S->flt[i] = 0;
  }
}

void SState_one(SState *S)
{
  int wc = SSNUMWORDS(S->nbits);
  int i;

  for (i = 0;i < wc;i++) {
    S->one[i] = ~0;
    S->flt[i] = 0;
    S->zero[i] = 0;
  }
}

/*
  return non-zero if S has only 0 and 1 bits
 */
int SState_isValue(SState *S)
{
  int wc = SSNUMWORDS(S->nbits);
  unsigned top_mask = LMASK(S->nbits & SSBITMASK);
  unsigned mask = SSWORDMASK;
  int i;

  for (i = 0;i < wc;i++) {
    if (i == wc-1 && (S->nbits & SSBITMASK))
      mask = top_mask;
      

    if ((S->flt[i] & mask)) return 0;
  }
  return 1;
}

void SState_xclear(SState *S)
{
  int wc = SSNUMWORDS(S->nbits);
  int i;

  for (i = 0;i < wc;i++) {
    S->one[i] = 0;
    S->flt[i] = 0;
    S->zero[i] = 0;
  }
}

static int getSymBit(SState *S,int i)
{
  int w;
  unsigned b;
  int x = 0;

  if (i >= S->nbits)
    return SYM_ZERO;

  w = i >> SSWORDSHIFT;
  b = 1 << (i & SSBITMASK);
  if ((S->zero[w] & b)) x |= 0x1;
  if ((S->one[w] & b))  x |= 0x2;
  if ((S->flt[w] & b))  x |= 0x4;

  return x;
}

static void putSymBit(SState *S,int i,int p)
{
  int w;
  unsigned b;

  if (i >= S->nbits) return;

  w = i >> SSWORDSHIFT;
  b = 1 << (i & SSBITMASK);
  if ((p & 0x1)) S->zero[w] |= b; else S->zero[w] &= ~b;
  if ((p & 0x2)) S->one[w] |= b; else S->one[w] &= ~b;
  if ((p & 0x4)) S->flt[w] |= b; else S->flt[w] &= ~b;
}

int SState_getBitSym(SState *S,int n)
{
  return getSymBit(S,n);
}


static void SState_extend(SState *S,int d)
{
  int i;
  int p = (d > 0) ? getSymBit(S,d-1) : SYM_ZERO;
  if (p == SYM_ONE) p = SYM_ZERO;

  for (i = d;i < S->nbits;i++)
    putSymBit(S,i,p);
}

static void SState_extendSym(SState *S,int d,int sym)
{
  int i;

  for (i = d;i < S->nbits;i++)
    putSymBit(S,i,sym);
}

void SState_expandExtend(SState *R,int nbits)
{
  if (nbits != R->nbits) { 
    SState *X = alloc_SState();
    int sym = getSymBit(R,R->nbits-1);
    int ob = R->nbits;

    SState_reinit(X,nbits);
    SState_copy(X,R);
    SState_reinit(R,nbits);
    SState_copy(R,X);
    SState_extendSym(R,ob,sym);

    free_SState(X);
  }
}

int SState_convertToInt(SState *S,unsigned *I)
{
  if (S->nbits > SSWORDSIZE) return -1;
  if ((LMASK(S->nbits) & S->flt[0])) return -1;
  *I = S->one[0];
  return 0;
}

int SState_convertFromInt(SState *S,unsigned I)
{
  if (S->nbits > SSWORDSIZE) return -1;
  S->one[0] = I;
  S->zero[0] = ~I;
  S->flt[0] = 0;
  return 0;
}

static void SState_convertBits(SState *S,char *A,int nbits)
{
  int d = strlen(A);
  int i;

  if (!nbits) nbits = d;

  SState_reinit(S,nbits);
  SState_xclear(S);

  for (i = 0;i < d;i++) {
    putSymBit(S,i,charToSym(A[d-i-1]));
  }
  SState_extend(S,d);
}

static void SState_convertHex(SState *S,char *A,int nbits)
{
  int d = strlen(A);
  int i;

  if (!nbits) nbits = d*4;

  SState_reinit(S,nbits);
  SState_xclear(S);

  for (i = 0;i < d;i++) {
    char c = A[d-i-1];
    if (isxdigit((int)c)) {
      int b = 4*i;
      int v;
      if (isdigit((int)c))
	v = c - '0';
      else {
	if (isupper((int)c)) c = tolower(c);
	v = c - 'a' + 10;
      }
      putSymBit(S,b,  (v&0x1)?SYM_ONE:SYM_ZERO);
      putSymBit(S,b+1,(v&0x2)?SYM_ONE:SYM_ZERO);
      putSymBit(S,b+2,(v&0x4)?SYM_ONE:SYM_ZERO);
      putSymBit(S,b+3,(v&0x8)?SYM_ONE:SYM_ZERO);
    } else {
      int p = charToSym(c);
      int b = 4*i;
      putSymBit(S,b,p);
      putSymBit(S,b+1,p);
      putSymBit(S,b+2,p);
      putSymBit(S,b+3,p);
    }
  }
  SState_extend(S,4*d);
}

void SState_convert(SState *S,char *A)
{
  int nbits = 0;
  char c,*p;

  if (sscanf(A,"%d'%c",&nbits,&c) == 2) {
    p = strchr(A,'\'')+2;
  } else if (sscanf(A,"'%c",&c) == 1) {
    nbits = 0;
    p = strchr(A,'\'')+2;
  } else {
    nbits = 0;
    p = A;
    c = 'b';
  }

  if (c == 'b')
    SState_convertBits(S,p,nbits);
  else if (c == 'h')
    SState_convertHex(S,p,nbits);
  else {
    S->nbits = 1;
    *S->one = *S->zero = *S->flt = 0;
  }
}

void SState_printSymbol(int x,FILE *f)
{
  if (x >= 0 && x < sym_table_len)
    fputc(sym_table[x],f);
  else
    fprintf(f,":%d:",x);
}

int SState_symbolStr(int x,char *p)
{
  if (x >= 0 && x < sym_table_len) {
    *p = sym_table[x];
    return 1;
  } else
    return sprintf(p,":%d:",x);
}

int SState_getstr(SState *S,char *p)
{
  int N = S->nbits;
  int M = (N & 0x3) ? (N | 0x3)+1 : N;
  int i;
  char *q = p;

  *p = 0;

  if (N == 1) {
    int x = getSymBit(S,0);
    p += sprintf(p,"1'b");
    p += SState_symbolStr(x,p);
    *p = 0;
    return p-q;
  }

  p += sprintf(p,"%d'h",N);

  for (i = N-1;i >= 0;i--)
    if (getSymBit(S,i) != SYM_FLOAT)
      break;
  if (i < 0) {
    p += sprintf(p,"z");
    return p-q;
  }

  for (i = M;i > 0;i -= 4) {
    int x0 = getSymBit(S,i-4);
    int x1 = getSymBit(S,i-3);
    int x2 = getSymBit(S,i-2);
    int x3 = getSymBit(S,i-1);
    int bits_same,logic_digs;

    bits_same = (x3 == x0 || i-1 >= N)
      && (x2 == x0 || i-2 >= N)
	&& (x1 == x0 || i-3 >= N);
    logic_digs = isBDig(x0) && isBDig(x1) && isBDig(x2) && isBDig(x3);

    if (bits_same && !isBDig(x0))
      p += SState_symbolStr(x0,p);
    else if (logic_digs) {
      int d = 0;
      if (x0 == SYM_ONE) d |= 0x1;
      if (x1 == SYM_ONE) d |= 0x2;
      if (x2 == SYM_ONE) d |= 0x4;
      if (x3 == SYM_ONE) d |= 0x8;
      if (d <= 9)
	p += sprintf(p,"%d",d);
      else
	p += sprintf(p,"%c",d-10+'a');
    } else {
      if (sstate_print_showbits) {
	p += sprintf(p,"(");
	if (i-1 < N) p += SState_symbolStr(x3,p);
	if (i-2 < N) p += SState_symbolStr(x2,p);
	if (i-3 < N) p += SState_symbolStr(x1,p);
	if (i-4 < N) p += SState_symbolStr(x0,p);
	p += sprintf(p,")");
      } else
	p += sprintf(p,"?");
    }
  }
  *p = 0;

  return p-q;
}


void SState_print(SState *S,FILE *f)
{
  char buf[STRMAX];

  SState_getstr(S,buf);
  fprintf(f,"%s",buf);
}

void SState_makeSameSize(SState *A,SState *B)
{
  if (A->nbits < B->nbits) {
    int d = A->nbits-1;
    A->nbits = B->nbits;
    SState_extend(A,d);
  } else if (B->nbits < A->nbits) {
    int d = B->nbits-1;
    B->nbits = A->nbits;
    SState_extend(B,d);
  }
}

void SState_copy(SState *R,SState *A)
{
  int wc = SSNUMWORDS(R->nbits);
  int i;

  for (i = 0;i < wc;i++) {
    R->one[i] = A->one[i];
    R->zero[i] = A->zero[i];
    R->flt[i] = A->flt[i];
  }
}

/*
  Copy a range of bits.  Copies bits in the range [ah:al] of A into R
  starting at bit rl
 */
void SState_copyRange(SState *R,int rl,SState *A,int ah,int al)
{
  int awc = SSNUMWORDS(A->nbits);					/* Words in A */
  int rwc = SSNUMWORDS(R->nbits);					/* Words in R */
  int rh = rl+(ah-al);							/* High bit of target in R */
  int r_low_w  = rl >> SSWORDSHIFT;					/* Low word of destination */
  int r_high_w = rh >> SSWORDSHIFT;					/* High word of source */
  unsigned low_mask  = HMASKZ(rl&SSBITMASK);				/* Mask for low word of destination */
  unsigned high_mask = LMASK((rh+1)&SSBITMASK);				/* Mask for high word of destination */
  int ar_shift = ABS(rl-al);						/* Difference between start points */
  int ar_shift_b = ar_shift &  SSBITMASK;				/* Bits into word to shift */
  int ar_shift_w = ar_shift >> SSWORDSHIFT;				/* # of words to shift */
  int i;

  assert(ah >= al);

  for (i = rwc-1;i >= 0;i--) {
    unsigned mask = SSWORDMASK;
    unsigned aone,azero,aflt;

    if (i < r_low_w || i > r_high_w) continue;
    if (i == r_low_w)  	mask &= low_mask;
    if (i == r_high_w)	mask &= high_mask;

    aone = azero = aflt = 0;
    if (rl >= al) {
      int j = i-ar_shift_w;
      int k = j-1;

      if (j < awc && j >= 0) {
	aone  |= A->one[j]  << ar_shift_b;
	azero |= A->zero[j] << ar_shift_b;
	aflt  |= A->flt[j]  << ar_shift_b;
      }
      if (k < awc && k >= 0) {
	int shift = SSWORDSIZE - ar_shift_b;
	
	aone  |= (A->one[k]  >> shift) & LMASK(ar_shift_b);
	azero |= (A->zero[k] >> shift) & LMASK(ar_shift_b);
	aflt  |= (A->flt[k]  >> shift) & LMASK(ar_shift_b);
      }
    } else {
      int j = i+ar_shift_w;
      int k = j+1;

      if (j < awc && j >= 0) {
	aone  |= A->one[j]  >> ar_shift_b;
	azero |= A->zero[j] >> ar_shift_b;
	aflt  |= A->flt[j]  >> ar_shift_b;
      }
      if (k < awc && k >= 0) {
	int shift = SSWORDSIZE - ar_shift_b;
	
	aone  |= (A->one[k]  << shift) & HMASK(ar_shift_b);
	azero |= (A->zero[k] << shift) & HMASK(ar_shift_b);
	aflt  |= (A->flt[k]  << shift) & HMASK(ar_shift_b);
      }
    }

    R->one[i]  = (mask&aone)  | (~mask&R->one[i]);
    R->zero[i] = (mask&azero) | (~mask&R->zero[i]);
    R->flt[i]  = (mask&aflt)  | (~mask&R->flt[i]);
  }
}

void SState_not(SState *R,SState *A)
{
  int wc = SSNUMWORDS(R->nbits);
  int i;

  for (i = 0;i < wc;i++) {
    register unsigned one = A->zero[i];
    register unsigned zero = A->one[i];
    R->one[i] = one|A->flt[i];
    R->zero[i] = zero|A->flt[i];
    R->flt[i] = A->flt[i];
  }
}

void SState_buf(SState *R,SState *A)
{
  int wc = SSNUMWORDS(R->nbits);
  int i;

  for (i = 0;i < wc;i++) {
    R->one[i] = A->one[i]|A->flt[i];
    R->zero[i] = A->zero[i]|A->flt[i];
    R->flt[i] = A->flt[i];
  }
}

/*
OR                 one                zero                flt          
   0 1 x z L H        0 1 x z L H        0 1 x z L H        0 1 x z L H
  +-----------       +-----------       +-----------       +-----------
 0|0 1 x x x x      0|0 1 1 1 1 1      0|1 0 1 1 1 1      0|0 0 1 1 1 1
 1|1 1 1 1 1 1      1|1 1 1 1 1 1      1|0 0 0 0 0 0      1|0 0 0 0 0 0
 x|x 1 x x x x      x|1 1 1 1 1 1      x|1 0 1 1 1 1      x|1 0 1 1 1 1
 z|x 1 x x x x      z|1 1 1 1 1 1      z|1 0 1 1 1 1      z|1 0 1 1 1 1
 L|x 1 x x x x      L|1 1 1 1 1 1      L|1 0 1 1 1 1      L|1 0 1 1 1 1
 H|x 1 x x x x      H|1 1 1 1 1 1      H|1 0 1 1 1 1      H|1 0 1 1 1 1

*/
void SState_or(SState *R,SState *A,SState *B,int invert)
{
  int wc = SSNUMWORDS(R->nbits);
  register int i;

  for (i = 0;i < wc;i++) {
    register unsigned RisOne = (A->one[i]&~A->flt[i])|(B->one[i]&~B->flt[i]);
    register unsigned inHasFloat = A->flt[i]|B->flt[i];

    if (invert) {
      R->one[i] = ~RisOne;
      R->zero[i] = RisOne|inHasFloat;
    } else {
      R->one[i] = RisOne|inHasFloat;
      R->zero[i] = ~RisOne;
    }
    R->flt[i] = (inHasFloat&~RisOne);
  }
}

/*
AND                 one                zero                flt          
   0 1 x z L H        0 1 x z L H        0 1 x z L H        0 1 x z L H
  +-----------       +-----------       +-----------       +-----------
 0|0 0 0 0 0 0      0|0 0 0 0 0 0      0|1 1 1 1 1 1      0|0 0 0 0 0 0
 1|0 1 x x x x      1|0 1 1 1 1 1      1|1 0 1 1 1 1      1|0 0 1 1 1 1
 x|0 x x x x x      x|0 1 1 1 1 1      x|1 1 1 1 1 1      x|0 1 1 1 1 1
 z|0 x x x x x      z|0 1 1 1 1 1      z|1 1 1 1 1 1      z|0 1 1 1 1 1
 L|0 x x x x x      L|0 1 1 1 1 1      L|1 1 1 1 1 1      L|0 1 1 1 1 1
 H|0 x x x x x      H|0 1 1 1 1 1      H|1 1 1 1 1 1      H|0 1 1 1 1 1

*/
void SState_and(SState *R,SState *A,SState *B,int invert)
{
  int wc = SSNUMWORDS(R->nbits);
  register int i;

  for (i = 0;i < wc;i++) {
    register unsigned RisZero = (A->zero[i]&~A->flt[i])|(B->zero[i]&~B->flt[i]);
    register unsigned inHasFloat = A->flt[i]|B->flt[i];

    if (invert) {
      R->one[i] = RisZero|inHasFloat;
      R->zero[i] = ~RisZero;
    } else {
      R->one[i] = ~RisZero;
      R->zero[i] = RisZero|inHasFloat;
    }
    R->flt[i] = (inHasFloat&~RisZero);
  }
}

/*
XOR                 one                zero                flt          
   0 1 x z L H        0 1 x z L H        0 1 x z L H        0 1 x z L H
  +-----------       +-----------       +-----------       +-----------
 0|0 1 x x x x      0|0 1 1 1 1 1      0|1 0 1 1 1 1      0|0 0 1 1 1 1
 1|1 0 x x x x      1|1 0 1 1 1 1      1|0 1 1 1 1 1      1|0 0 1 1 1 1
 x|x x x x x x      x|1 1 1 1 1 1      x|1 1 1 1 1 1      x|1 1 1 1 1 1
 z|x x x x x x      z|1 1 1 1 1 1      z|1 1 1 1 1 1      z|1 1 1 1 1 1
 L|x x x x x x      L|1 1 1 1 1 1      L|1 1 1 1 1 1      L|1 1 1 1 1 1
 H|x x x x x x      H|1 1 1 1 1 1      H|1 1 1 1 1 1      H|1 1 1 1 1 1

*/
void SState_xor(SState *R,SState *A,SState *B,int invert)
{
  int wc = SSNUMWORDS(R->nbits);
  register int i;

  for (i = 0;i < wc;i++) {
    register unsigned RisOne = A->one[i]^B->one[i];
    register unsigned inHasFloat = A->flt[i]|B->flt[i];

    if (invert) {
      R->one[i]  = ~(RisOne)|inHasFloat;
      R->zero[i] = RisOne|inHasFloat;
    } else {
      R->one[i]  = RisOne|inHasFloat;
      R->zero[i] = ~(RisOne)|inHasFloat;
    }
    R->flt[i]  = inHasFloat;
  }
}

/*
bufif
      /E\           one                zero                flt          
   0 1 x z L H        0 1 x z L H        0 1 x z L H        0 1 x z L H
  +-----------       +-----------       +-----------       +-----------
 0|z 0 L L L L      0|0 0 0 0 0 0      0|0 1 1 1 1 1      0|1 0 1 1 1 1
 1|z 1 H H H H      1|0 1 1 1 1 1      1|0 0 0 0 0 0      1|1 0 1 1 1 1
Ix|z x x x x x      x|0 1 1 1 1 1      x|0 1 1 1 1 1      x|1 1 1 1 1 1
 z|z x x x x x      z|0 1 1 1 1 1      z|0 1 1 1 1 1      z|1 1 1 1 1 1
 L|z x x x x x      L|0 1 1 1 1 1      L|0 1 1 1 1 1      L|1 1 1 1 1 1
 H|z x x x x x      H|0 1 1 1 1 1      H|0 1 1 1 1 1      H|1 1 1 1 1 1

*/
void SState_bufif(SState *R,SState *I,SState *E)
{
  int wc = SSNUMWORDS(R->nbits);
  register int i;

  for (i = 0;i < wc;i++) {
    register unsigned IisZero = I->zero[i] & ~I->one[i] & ~I->flt[i];
    register unsigned EisZero = E->zero[i] & ~E->one[i] & ~E->flt[i];
    register unsigned IisOne  = ~I->zero[i] & I->one[i] & ~I->flt[i];
    register unsigned EisOne  = ~E->zero[i] & E->one[i] & ~E->flt[i];
    R->one[i] = ~(IisZero|EisZero);
    R->zero[i] = ~(EisZero|IisOne);
    R->flt[i] = ~((IisZero|IisOne)&EisOne);
  }
}

/*
nmos
      /G\           one                zero                flt          
   0 1 x z L H        0 1 x z L H        0 1 x z L H        0 1 x z L H  01z
  +-----------       +-----------       +-----------       +-----------  ---
 0|z 0 L L L L      0|0 0 0 0 0 0      0|0 1 1 1 1 1      0|1 0 1 1 1 1  100
 1|z 1 H H H H      1|0 1 1 1 1 1      1|0 0 0 0 0 0      1|1 0 1 1 1 1  010
Ix|z x x x x x      x|0 1 1 1 1 1      x|0 1 1 1 1 1      x|1 1 1 1 1 1  111
 z|z z z z z z      z|0 0 0 0 0 0      z|0 0 0 0 0 0      z|1 1 1 1 1 1  001
 L|z L L L L L      L|0 0 0 0 0 0      L|0 1 1 1 1 1      L|1 1 1 1 1 1  101
 H|z H H H H H      H|0 1 1 1 1 1      H|0 0 0 0 0 0      H|1 1 1 1 1 1  011
*/
void SState_nmos(SState *R,SState *I,SState *G)
{
  int wc = SSNUMWORDS(R->nbits);
  register int i;

  for (i = 0;i < wc;i++) {
    register unsigned GisZero = G->zero[i] & ~G->one[i] & ~G->flt[i];
    register unsigned GisOne = ~G->zero[i] & G->one[i] & ~G->flt[i];
    register unsigned IisLogic = (I->zero[i] ^ I->one[i]) & ~I->flt[i];

    R->one[i]  = I->one[i] & ~GisZero;
    R->zero[i] = I->zero[i] & ~GisZero;
    R->flt[i] = ~(IisLogic & GisOne);
  }
}

/*
pmos
      /G\           one                zero                flt          
   0 1 x z L H        0 1 x z L H        0 1 x z L H        0 1 x z L H  01z
  +-----------       +-----------       +-----------       +-----------  ---
 0|0 z L L L L      0|0 0 0 0 0 0      0|1 0 1 1 1 1      0|0 1 1 1 1 1  100
 1|1 z H H H H      1|1 0 1 1 1 1      1|0 0 0 0 0 0      1|0 1 1 1 1 1  010
Ix|z z x x x x      x|1 0 1 1 1 1      x|1 0 1 1 1 1      x|1 1 1 1 1 1  111
 z|z z z z z z      z|0 0 0 0 0 0      z|0 0 0 0 0 0      z|1 1 1 1 1 1  001
 L|L z L L L L      L|0 0 0 0 0 0      L|1 0 1 1 1 1      L|1 1 1 1 1 1  101
 H|H z H H H H      H|1 0 1 1 1 1      H|0 0 0 0 0 0      H|1 1 1 1 1 1  011
*/
void SState_pmos(SState *R,SState *I,SState *G)
{
  int wc = SSNUMWORDS(R->nbits);
  register int i;

  for (i = 0;i < wc;i++) {
    register unsigned GisZero = G->zero[i] & ~G->one[i] & ~G->flt[i];
    register unsigned GisOne = ~G->zero[i] & G->one[i] & ~G->flt[i];
    register unsigned IisLogic = (I->zero[i] ^ I->one[i]) & ~I->flt[i];

    R->one[i]  = I->one[i] & ~GisOne;
    R->zero[i] = I->zero[i] & ~GisOne;
    R->flt[i] = ~(IisLogic & GisZero);
  }

}

/*
  Wire merge function

WIRE                one                zero                flt          
   0 1 x z L H        0 1 x z L H        0 1 x z L H        0 1 x z L H  01z
  +-----------       +-----------       +-----------       +-----------  ---
 0|0 x x 0 0 x      0|0 1 1 0 0 1      0|1 1 1 1 1 1      0|0 1 1 0 0 1  100
 1|x 1 x 1 x 1      1|1 1 1 1 1 1      1|1 0 1 0 1 0      1|1 0 1 0 1 0  010
 x|x x x x x x      x|1 1 1 1 1 1      x|1 1 1 1 1 1      x|1 1 1 1 1 1  111
 z|0 1 x z L H      z|0 1 1 0 0 1      z|1 0 1 0 1 0      z|0 0 1 1 1 1  001
 L|0 x x L L x      L|0 1 1 0 0 1      L|1 1 1 1 1 1      L|0 1 1 1 1 1  101
 H|x 1 x H x H      H|1 1 1 1 1 1      H|1 0 1 0 1 0      H|1 0 1 1 1 1  011
*/
void SState_wire(SState *R,SState *A,SState *B)
{
  int wc = SSNUMWORDS(R->nbits);
  register int i;

  for (i = 0;i < wc;i++) {
    register unsigned RisOne = A->one[i]|B->one[i];
    register unsigned RisZero = A->zero[i]|B->zero[i];
    register unsigned RhasFloat = (A->flt[i]&B->flt[i]);

    R->one[i]  = RisOne;
    R->zero[i] = RisZero;
    R->flt[i]  = ~(RisOne ^ RisZero) | RhasFloat;
  }
}

/*
 * Does S contain only 1s and 0s?
 */
int SState_isLogic(SState *S)
{
  int wc = SSNUMWORDS(S->nbits);
  unsigned mask = LMASK((S->nbits & SSBITMASK));
  int i;

  for (i = 0;i < wc-1;i++) {
    if (S->flt[i]) return 0;
  }

  if ((S->flt[wc-1] & mask)) return 0;

  return 1;
}

/*
 * Shift I and place the result in R.
 *
 * Arguments:
 *
 *    R		Return value from operation
 *    I		Value to be shifted
 *    n		Bits to shift (>0 for left shift, <0 for right shift)
 *    in1	Shift-in value to use in 'one' field
 *    in0	Shift-in value to use in 'zero' field
 *    inZ	Shift-in value to use in 'flt' field
 */
void SState_roll(SState *R,SState *I,int n)
{
  int bits = R->nbits;
  unsigned mask = (bits<32) ? ((1<<bits)-1) : ~0;

  if (n < 0)
    n += bits;

  assert(I->nbits <= 32);
  assert(n >= 0);

  if (n > 0) {
    unsigned emask = (n<32) ? ((1<<n)-1) : ~0;

    R->one[0] = (I->one[0] << n) | ((I->one[0] >> (bits-n)) & emask);
    R->zero[0] = (I->zero[0] << n) | ((I->zero[0] >> (bits-n)) & emask);
    R->flt[0] = (I->flt[0] << n) | ((I->flt[0] >> (bits-n)) & emask);
  } else {
    R->one[0] = I->one[0];
    R->zero[0] = I->zero[0];
    R->flt[0] = I->flt[0];
  }

  R->one[0] &= mask;
  R->zero[0] &= mask;
  R->flt[0] &= mask;
}


/*
 * Shift I and place the result in R.
 *
 * Arguments:
 *
 *    R		Return value from operation
 *    I		Value to be shifted
 *    n		Bits to shift (>0 for left shift, <0 for right shift)
 *    in1	Shift-in value to use in 'one' field
 *    in0	Shift-in value to use in 'zero' field
 *    inZ	Shift-in value to use in 'flt' field
 */
void SState_shift(SState *R,SState *I,int n,int in1,int in0,int inZ)
{
  int bits = R->nbits;
  unsigned mask = (bits<32) ? ((1<<bits)-1) : ~0;

  assert(I->nbits <= 32);

  if (n > 0) {
    unsigned emask = (n<32) ? ((1<<n)-1) : ~0;

    R->one[0] = (I->one[0] << n) | (in1*emask);
    R->zero[0] = (I->zero[0] << n) | (in0*emask);
    R->flt[0] = (I->flt[0] << n) | (inZ*emask);
  } else if (n < 0) {
    unsigned emask;

    n = -n;
    emask = ((n<32) ? ((1<<n)-1) : ~0) << (bits-n);

    R->one[0] = (I->one[0] >> n) | (in1*emask);
    R->zero[0] = (I->zero[0] >> n) | (in0*emask);
    R->flt[0] = (I->flt[0] >> n) | (inZ*emask);
  } else {
    R->one[0] = I->one[0];
    R->zero[0] = I->zero[0];
    R->flt[0] = I->flt[0];
  }

  R->one[0] &= mask;
  R->zero[0] &= mask;
  R->flt[0] &= mask;
}

int testLogic(char *command)
{
  char buf1[STRMAX],buf2[STRMAX];
  static SState R,A,B;
  static int isInit = 0;
  int n1,n2,n3;

  if (!isInit) {
    SState_init(&R,8);
    SState_init(&A,8);
    SState_init(&B,8);
    isInit = 1; 
 }

  if (sscanf(command," state %s",buf1) == 1) {
    SState_convert(&R,buf1);
    printf("state = ");
    SState_print(&R,stdout);
    printf("\n");
    return 1;
  } else if (sscanf(command," not %s",buf1) == 1) {
    SState_convert(&A,buf1);
    SState_reinit(&R,A.nbits);
    SState_not(&R,&A);
    printf("state = "); SState_print(&A,stdout); printf("\n");
    printf("not   = "); SState_print(&R,stdout); printf("\n");
    return 1;
  } else if (sscanf(command," and %s %s",buf1,buf2) == 2) {
    SState_convert(&A,buf1);
    SState_convert(&B,buf2);
    SState_makeSameSize(&A,&B);
    SState_reinit(&R,A.nbits);
    SState_and(&R,&A,&B,0);
    printf("A   = "); SState_print(&A,stdout); printf("\n");
    printf("B   = "); SState_print(&B,stdout); printf("\n");
    printf("and = "); SState_print(&R,stdout); printf("\n");
    return 1;
  } else if (sscanf(command," or %s %s",buf1,buf2) == 2) {
    SState_convert(&A,buf1);
    SState_convert(&B,buf2);
    SState_makeSameSize(&A,&B);
    SState_reinit(&R,A.nbits);
    SState_or(&R,&A,&B,0);
    printf("A   = "); SState_print(&A,stdout); printf("\n");
    printf("B   = "); SState_print(&B,stdout); printf("\n");
    printf("or  = "); SState_print(&R,stdout); printf("\n");
    return 1;
  } else if (sscanf(command," xor %s %s",buf1,buf2) == 2) {
    SState_convert(&A,buf1);
    SState_convert(&B,buf2);
    SState_makeSameSize(&A,&B);
    SState_reinit(&R,A.nbits);
    SState_xor(&R,&A,&B,0);
    printf("A   = "); SState_print(&A,stdout); printf("\n");
    printf("B   = "); SState_print(&B,stdout); printf("\n");
    printf("xor = "); SState_print(&R,stdout); printf("\n");
    return 1;
  } else if (sscanf(command," nand %s %s",buf1,buf2) == 2) {
    SState_convert(&A,buf1);
    SState_convert(&B,buf2);
    SState_makeSameSize(&A,&B);
    SState_reinit(&R,A.nbits);
    SState_and(&R,&A,&B,1);
    printf("A    = "); SState_print(&A,stdout); printf("\n");
    printf("B    = "); SState_print(&B,stdout); printf("\n");
    printf("nand = "); SState_print(&R,stdout); printf("\n");
    return 1;
  } else if (sscanf(command," nor %s %s",buf1,buf2) == 2) {
    SState_convert(&A,buf1);
    SState_convert(&B,buf2);
    SState_makeSameSize(&A,&B);
    SState_reinit(&R,A.nbits);
    SState_or(&R,&A,&B,1);
    printf("A   = "); SState_print(&A,stdout); printf("\n");
    printf("B   = "); SState_print(&B,stdout); printf("\n");
    printf("nor = "); SState_print(&R,stdout); printf("\n");
    return 1;
  } else if (sscanf(command," xnor %s %s",buf1,buf2) == 2) {
    SState_convert(&A,buf1);
    SState_convert(&B,buf2);
    SState_makeSameSize(&A,&B);
    SState_reinit(&R,A.nbits);
    SState_xor(&R,&A,&B,1);
    printf("A    = "); SState_print(&A,stdout); printf("\n");
    printf("B    = "); SState_print(&B,stdout); printf("\n");
    printf("xnor = "); SState_print(&R,stdout); printf("\n");
    return 1;
  } else if (sscanf(command," bufif %s %s",buf1,buf2) == 2) {
    SState_convert(&A,buf1);
    SState_convert(&B,buf2);
    SState_makeSameSize(&A,&B);
    SState_reinit(&R,A.nbits);
    SState_bufif(&R,&A,&B);
    printf("A     = "); SState_print(&A,stdout); printf("\n");
    printf("B     = "); SState_print(&B,stdout); printf("\n");
    printf("bufif = "); SState_print(&R,stdout); printf("\n");
    return 1;
  } else if (sscanf(command," nmos %s %s",buf1,buf2) == 2) {
    SState_convert(&A,buf1);
    SState_convert(&B,buf2);
    SState_makeSameSize(&A,&B);
    SState_reinit(&R,A.nbits);
    SState_nmos(&R,&A,&B);
    printf("I     = "); SState_print(&A,stdout); printf("\n");
    printf("G     = "); SState_print(&B,stdout); printf("\n");
    printf("nmos  = "); SState_print(&R,stdout); printf("\n");
    return 1;
  } else if (sscanf(command," cpr %s %d %s %d %d",buf1,&n1,buf2,&n2,&n3) == 5) {
    SState_convert(&A,buf1);
    SState_convert(&B,buf2);
    SState_reinit(&R,A.nbits);
    SState_copy(&R,&A);
    SState_copyRange(&R,n1,&B,n2,n3);
    printf("A[%d]    = ",n1); SState_print(&A,stdout); printf("\n");
    printf("B[%d:%d]  = ",n2,n3); SState_print(&B,stdout); printf("\n");
    printf("cpr     = "); SState_print(&R,stdout); printf("\n");
  } else if (sscanf(command," pmos %s %s",buf1,buf2) == 2) {
    SState_convert(&A,buf1);
    SState_convert(&B,buf2);
    SState_makeSameSize(&A,&B);
    SState_reinit(&R,A.nbits);
    SState_pmos(&R,&A,&B);
    printf("I     = "); SState_print(&A,stdout); printf("\n");
    printf("G     = "); SState_print(&B,stdout); printf("\n");
    printf("pmos  = "); SState_print(&R,stdout); printf("\n");
    return 1;
  } else if (sscanf(command," eq %s %s",buf1,buf2) == 2) {
    SState_convert(&A,buf1);
    SState_convert(&B,buf2);
    SState_makeSameSize(&A,&B);
    printf("A     = "); SState_print(&A,stdout); printf("\n");
    printf("B     = "); SState_print(&B,stdout); printf("\n");
    printf("eq    = %d\n",SState_eq(&A,&B));
    return 1;
  } else if (sscanf(command," wfunc %s %s",buf1,buf2) == 2) {
    SState_convert(&A,buf1);
    SState_convert(&B,buf2);
    SState_makeSameSize(&A,&B);
    SState_reinit(&R,A.nbits);
    SState_wire(&R,&A,&B);
    printf("A    = "); SState_print(&A,stdout); printf("\n");
    printf("B    = "); SState_print(&B,stdout); printf("\n");
    printf("wire = "); SState_print(&R,stdout); printf("\n");
    return 1;
  } else if (sscanf(command," echo %s",buf1) == 1) {
    printf("%s\n",buf1);
  }
  return 0;
}
