/****************************************************************************
    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 MULT_Z	0
#define MULT_A	1
#define MULT_B	2

#define MULT_DELAY	0

#if USELONGLONG
#define MULT_MAXBITS	(2*SSWORDSIZE)
#else
#define MULT_MAXBITS	SSWORDSIZE
#endif

static void Mult_processEvent(SGate*,EvQueue*,SEvent*);
static int Mult_checkGate(SGate*);

static SGateInfo mult_info = {
  0,
  "mult",0x1,
  3,{{"P",GIO_OUT,0},
     {"A",GIO_IN,0},
     {"B",GIO_IN,0}},

  {{"A/B-P",bit(1)|bit(2),0},
   {0}},

  Generic_copyGate,
  Mult_processEvent,
  Mult_checkGate,
  Nop_initGate,
  Generic_setProp,
  0,
  0,
  Generic_propFrwdDelay,
  Generic_propBackDelay,
  Generic_delay,
};

void init_mult()
{
  SGateInfo_register(&mult_info,0);
}

static int Mult_checkGate(SGate *g)
{
  SPort *Z = g->g_ports.port[MULT_Z];

  if (Z->p_net->n_nbits > MULT_MAXBITS) {
    errorGate(g->g_name,"Multiplier outputs greater than %s bits unsupported.",MULT_MAXBITS);
    return -1;
  }

  return 0;
}

static void Mult_processEvent(SGate *g,EvQueue *Q,SEvent *E)
{
  SPort *Z = g->g_ports.port[MULT_Z];
  SState *aS = SGate_allocPortState(g,MULT_A);
  SState *bS = SGate_allocPortState(g,MULT_B);
  SState *S = alloc_SState();

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

  if (!SState_isValue(aS) || !SState_isValue(bS)) {
    SState_unknown(S);
  } else {
#if USELONGLONG
    unsigned long long A,B,P;

    if (aS->nbits > SSWORDSIZE)
      A = aS->one[0] | ((((unsigned long long)aS->one[1]) << SSWORDSIZE)&LMASK(aS->nbits&SSBITMASK));
    else
      A = aS->one[0] & LMASK(aS->nbits);

    if (bS->nbits > SSWORDSIZE)
      B = bS->one[0] | ((((unsigned long long)bS->one[1]) << SSWORDSIZE)&LMASK(bS->nbits&SSBITMASK));
    else
      B = bS->one[0] & LMASK(bS->nbits);

    P = A*B;

    S->one[0] = P;
    S->zero[0] = ~P;
    S->flt[0] = 0;
    if (S->nbits > SSWORDSIZE) {
      P >>= SSWORDSIZE;
      S->one[1] = P;
      S->zero[1] = ~P;
      S->flt[1] = 0;
    }
#else
    unsigned A,B,P;

    A = aS->one[0] & LMASK(aS->nbits);
    B = bS->one[0] & LMASK(bS->nbits);

    P = A*B;

    S->one[0] = P;
    S->zero[0] = ~P;
    S->flt[0] = 0;
#endif
  }

  EvQueue_setPort(Q,Z,S,g->g_delayParms[MULT_DELAY]);

  free_SState(aS);
  free_SState(bS);
  free_SState(S);
}
