/****************************************************************************
    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 DIV_Q	0
#define DIV_R	1
#define DIV_A	2
#define DIV_B	3

#define DIV_DELAY_IQ	0
#define DIV_DELAY_IR	1

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

static void Div_processEvent(SGate*,EvQueue*,SEvent*);
static int Div_checkGate(SGate*);

static SGateInfo div_info = {
  0,
  "div",0x1,
  4,{{"Q",GIO_OUT,0},
     {"R",GIO_OUT,0},
     {"A",GIO_IN,0},
     {"B",GIO_IN,0}},

  {{"A/B-Q",bit(2)|bit(3),0},
   {"A/B-R",bit(2)|bit(3),1},
   {0}},

  Generic_copyGate,
  Div_processEvent,
  Div_checkGate,
  Nop_initGate,
  Generic_setProp,
  0,
  0,
  Generic_propFrwdDelay,
  Generic_propBackDelay,
  Generic_delay,
};

void init_div()
{
  SGateInfo_register(&div_info,0);
}

static int Div_checkGate(SGate *g)
{
  SPort *Q = g->g_ports.port[DIV_Q];
  SPort *R = g->g_ports.port[DIV_R];
  SPort *A = g->g_ports.port[DIV_A];
  SPort *B = g->g_ports.port[DIV_B];

  if (Q->p_net->n_nbits > DIV_MAXBITS || R->p_net->n_nbits > DIV_MAXBITS ||
      A->p_net->n_nbits > DIV_MAXBITS || B->p_net->n_nbits > DIV_MAXBITS) {
    errorGate(g->g_name,"Divider inputs/outputs greater than %s bits unsupported.",DIV_MAXBITS);
    return -1;
  }

  return 0;
}

static void Div_processEvent(SGate *g,EvQueue *evQ,SEvent *E)
{
  SPort *Q = g->g_ports.port[DIV_Q];
  SPort *R = g->g_ports.port[DIV_R];
  SState *aS = SGate_allocPortState(g,DIV_A);
  SState *bS = SGate_allocPortState(g,DIV_B);
  SState *qS = alloc_SState();
  SState *rS = alloc_SState();

  SState_reinit(qS,Q->p_state.nbits);
  SState_reinit(rS,R->p_state.nbits);

  if (!SState_isValue(aS) || !SState_isValue(bS)) {
    SState_unknown(qS);
    SState_unknown(rS);
  } else {
#if USELONGLONG
    unsigned long long A,B,q,r;

    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);

    if (B) {
      q = A / B;
      r = A % B;
    } else
      q = r = 0;

    qS->one[0] = q;
    qS->zero[0] = ~q;
    qS->flt[0] = 0;
    if (qS->nbits > SSWORDSIZE) {
      q >>= SSWORDSIZE;
      qS->one[1] = q;
      qS->zero[1] = ~q;
      qS->flt[1] = 0;
    }

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

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

    if (B) {
      q = A / B;
      r = A % B;
    } else 
      q = r = 0;

    qS->one[0] = q;
    qS->zero[0] = ~q;
    qS->flt[0] = 0;

    rS->one[0] = q;
    rS->zero[0] = ~q;
    rS->flt[0] = 0;
#endif
  }

  EvQueue_setPort(evQ,Q,qS,g->g_delayParms[DIV_DELAY_IQ]);
  EvQueue_setPort(evQ,R,rS,g->g_delayParms[DIV_DELAY_IR]);

  free_SState(aS);
  free_SState(bS);
  free_SState(rS);
  free_SState(qS);
}
