/*
   Copyright (C) 1998 Scott Dattalo

This file is part of gpsim.

gpsim 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, or (at your option)
any later version.

gpsim 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 gpsim; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */


#include <stdio.h>
#include <iostream>
#include <iomanip>
#include <string>
#include <assert.h>

#include "../config.h"
#include "cmd_gpsim.h"
#include "ioports.h"
#include "trace.h"

#include "stimuli.h"

#include "xref.h"
//#define DEBUG
#if defined(DEBUG)
#define Dprintf(arg) {printf("%s:%d ",__FILE__,__LINE__); printf arg; }
#else
#define Dprintf(arg) {}
#endif

//--------------------------------------------------
// 
//--------------------------------------------------
PeripheralSignalSource::PeripheralSignalSource(PinModule *_pin)
  : m_pin(_pin), m_cState('?')
{
  assert(m_pin);
}

// getState is called when the PinModule is attempting to
// update the output state for the I/O Pin.

char PeripheralSignalSource::getState()
{
  return m_cState;
}

/// putState is called when the peripheral output source
/// wants to change the output state.
void PeripheralSignalSource::putState(const char new3State)
{
  if (new3State != m_cState) {
    m_cState = new3State;
    m_pin->updatePinModule();
  }
}

void PeripheralSignalSource::toggle()
{
  switch (m_cState) {
  case '1':
  case 'W':
    putState('0');
    break;
  case '0':
  case 'w':
    putState('1');
    break;
  }
}

//-------------------------------------------------------------------
//
//                 ioports.cc
//
// The ioport infrastructure for gpsim is provided here. The class
// taxonomy for the IOPORT class is:
//
//  file_register 
//     |-> sfr_register
//            |-> IOPORT
//                  |-> PORTA
//                  |-> PORTB
//                  |-> PORTC
//                  |-> PORTD
//                  |-> PORTE
//                  |-> PORTF
// 
// Each I/O port has an associated array of I/O pins which provide an
// interface to the virtual external world of the stimuli.
//
//-------------------------------------------------------------------

class SignalSource : public SignalControl
{
public:
  SignalSource(PortRegister *_reg, unsigned int bitPosition)
    : m_register(_reg), m_bitMask(1<<bitPosition)
  {
  }
  char getState()
  {
    // return m_register ? 
    //  (((m_register->getDriving()&m_bitMask)!=0)?'1':'0') : 'Z';
    char r = m_register ? (((m_register->getDriving()&m_bitMask)!=0)?'1':'0') : 'Z';
    /**/
    Dprintf(("SignalSource::getState() %s  bitmask:0x%x state:%c\n",
	     (m_register?m_register->name().c_str():"NULL"),
	     m_bitMask,r));
    /**/
    return r;
  }
private:
  PortRegister *m_register;
  unsigned int  m_bitMask;
};


PortSink::PortSink(PortRegister *portReg, unsigned int iobit)
  : m_PortRegister(portReg), m_iobit(iobit)
{
  assert (m_PortRegister);
}

void PortSink::setSinkState(char cNewSinkState)
{
  Dprintf((" PortSink::setSinkState:bit=%d,val=%c\n",m_iobit,cNewSinkState));

  m_PortRegister->setbit(m_iobit,cNewSinkState);
}
//------------------------------------------------------------------------
PortRegister::PortRegister(unsigned int numIopins, unsigned int _mask)
  : sfr_register(),
    PortModule(numIopins),
    mEnableMask(_mask),  
    drivingValue(0), rvDrivenValue(0,0)

{

}

void PortRegister::setEnableMask(unsigned int newEnableMask)
{
  //unsigned int maskDiff = getEnableMask() ^ newEnableMask;
  unsigned int oldEnableMask = getEnableMask();

  for (unsigned int i=0, m=1; i<mNumIopins; i++, m<<= 1)
    if ((newEnableMask & m) && ! (oldEnableMask & m )) 
    {
      PinModule *pmP = PortModule::getIOpins(i); 
      if (!pmP)
      {
          pmP = new PinModule(this,i);
          PortModule::addPinModule(pmP,i);
          pmP->setDefaultSource(new SignalSource(this, i));
          pmP->addSink(new PortSink(this, i));
      }
      else
      {
        if (pmP->getSourceState() == '?')
	{
           pmP->setDefaultSource(new SignalSource(this, i));
           pmP->addSink(new PortSink(this, i));
	}
      }
    }

  mEnableMask = newEnableMask;
}

void PortRegister::put(unsigned int new_value)
{
  trace.raw(write_trace.get() | value.data);

  put_value(new_value);
}
void PortRegister::put_value(unsigned int new_value)
{
  Dprintf(("PortRegister::put_value old=0x%x:new=0x%x\n",value.data,new_value));

  unsigned int diff = mEnableMask & (new_value ^ value.data);
  drivingValue = new_value & mEnableMask;
  value.data = drivingValue;

  if(diff) {
    // If no stimuli are connected to the Port pins, then the driving
    // value and the driven value are the same. If there are external
    // stimuli (or perhaps internal peripherals) overdriving or overriding
    // this port, then the call to updatePort() will update 'drivenValue'
    // to its proper value. In either case, calling updatePort ensures 
    // the drivenValue is updated properly
    
    updatePort();
  }
}
//------------------------------------------------------------------------
// PortRegister::updateUI()  UI really means GUI.
// We just pass control to the update method, which is defined in gpsimValue.

void PortRegister::updateUI()
{
  update();
}
//------------------------------------------------------------------------
// PortRegister::setbit
//
// This method is called whenever a stimulus changes the state of
// an I/O pin associated with the port register. 3-state logic is
// used.
// FIXME -  rvDrivenValue and value are always the same, so why have
// FIXME -  both?

void PortRegister::setbit(unsigned int bit_number, char new3State)
{
  if(bit_number <= bit_mask) {

    trace.raw(write_trace.get()  | value.data);
    trace.raw(write_trace.geti() | value.init);

    Dprintf(("PortRegister::setbit() %s bit=%d,val=%c\n",name().c_str(), bit_number,new3State));

    if (new3State=='1' || new3State=='W') {
      rvDrivenValue.data |= (1<<bit_number);
      rvDrivenValue.init &= ~(1<<bit_number);
    } else if (new3State=='0' || new3State=='w') {
      rvDrivenValue.data &= ~(1<<bit_number);
      rvDrivenValue.init &= ~(1<<bit_number);
    } else 
      // Not a 0 or 1, so it must be unknown.
      rvDrivenValue.init |= (1<<bit_number);

    value = rvDrivenValue;
  }

}

unsigned int PortRegister::get()
{
  trace.raw(read_trace.get()  | rvDrivenValue.data);
  trace.raw(read_trace.geti() | rvDrivenValue.init);

  return rvDrivenValue.data;
}
unsigned int PortRegister::get_value()
{
  return rvDrivenValue.data;
}
void PortRegister::putDrive(unsigned int new_value)
{
  put(new_value);
}
unsigned int PortRegister::getDriving()
{
  return drivingValue;
}
//========================================================================
//========================================================================
static PinModule AnInvalidPinModule;

PortModule::PortModule(unsigned int numIopins)
  : mNumIopins(numIopins)
{

  iopins = new PinModule *[mNumIopins];
  for (unsigned int i=0; i<mNumIopins; i++)
    iopins[i] = &AnInvalidPinModule;
  //iopins[i] = new PinModule(this,i);

}
PortModule::~PortModule()
{
  for (unsigned int i=0; i<mNumIopins; i++)
    delete iopins[i];

  delete iopins;
}

PinModule &PortModule::operator [] (unsigned int iPinNumber)
{
  if (iPinNumber < mNumIopins)
    return *iopins[iPinNumber];

  // error...
  return AnInvalidPinModule;
}

PinModule * PortModule::getIOpins(unsigned int iPinNumber)
{

  if (iPinNumber < mNumIopins && iopins[iPinNumber] != &AnInvalidPinModule)
    return iopins[iPinNumber];

  // error...
  return (PinModule *)0;
}


void PortModule::updatePort()
{
  for (unsigned int i=0; i<mNumIopins; i++) 
    if (iopins[i])
      iopins[i]->updatePinModule();
}
void PortModule::updateUI()
{
  // hmmm nothing 
}

void PortModule::updatePin(unsigned int iPinNumber)
{
  if (iPinNumber < mNumIopins)
    iopins[iPinNumber]->updatePinModule();
}

SignalSink *PortModule::addSink(SignalSink *new_sink, unsigned int iPinNumber)
{
  if (iPinNumber < mNumIopins)
    iopins[iPinNumber]->addSink(new_sink);
  return new_sink;
}

IOPIN *PortModule::addPin(IOPIN *new_pin, unsigned int iPinNumber)
{
  if (iPinNumber < mNumIopins) {
    // If there is not a PinModule for this pin, then add one.
    if (iopins[iPinNumber] == &AnInvalidPinModule)
      iopins[iPinNumber] = new PinModule(this,iPinNumber);

    iopins[iPinNumber]->setPin(new_pin);
  }
  return new_pin;
}

void PortModule::addPinModule(PinModule *newModule, unsigned int iPinNumber)
{
  if (iPinNumber < mNumIopins  && iopins[iPinNumber] == &AnInvalidPinModule)
    iopins[iPinNumber] = newModule;
}

IOPIN *PortModule::getPin(unsigned int iPinNumber)
{
  if (iPinNumber < mNumIopins) {
    return &iopins[iPinNumber]->getPin();
  }
  return 0;
}

//------------------------------------------------------------------------
// PinModule

PinModule::PinModule()
  : PinMonitor(),
    m_cLastControlState('?'), m_cLastSinkState('?'),
    m_cLastSourceState('?'), m_cLastPullupControlState('?'),
    m_defaultSource(0), m_activeSource(0),
    m_defaultControl(0), m_activeControl(0),
    m_defaultPullupControl(0), m_activePullupControl(0),
    m_pin(0), m_port(0), m_pinNumber(0)
{

}

PinModule::PinModule(PortModule *_port, unsigned int _pinNumber, IOPIN *_pin)
  : PinMonitor(),
    m_cLastControlState('?'), m_cLastSinkState('?'),
    m_cLastSourceState('?'), m_cLastPullupControlState('?'),
    m_defaultSource(0), m_activeSource(0),
    m_defaultControl(0), m_activeControl(0),
    m_defaultPullupControl(0), m_activePullupControl(0),
    m_pin(_pin), m_port(_port), m_pinNumber(_pinNumber),
    m_bForcedUpdate(false)
{
  setPin(m_pin);
}

void PinModule::setPin(IOPIN *new_pin)
{
  // Replace our pin only if this one is valid and we don't have one already.
  if (!m_pin && new_pin) {
    m_pin = new_pin;
    m_pin->setMonitor(this);
    m_cLastControlState = getControlState();
    m_cLastSourceState = getSourceState();
  }

}
void PinModule::refreshPinOnUpdate(bool bForcedUpdate)
{
  m_bForcedUpdate = bForcedUpdate;
}

void PinModule::updatePinModule()
{
  if (!m_pin)
    return;

  bool bStateChange=m_bForcedUpdate;

  Dprintf(("PinModule::updatePinModule():%s enter cont=%c,source=%c,pullup%c\n",
	   (m_pin ? m_pin->name().c_str() : "NOPIN"),
	    m_cLastControlState,m_cLastSourceState,m_cLastPullupControlState));

  char cCurrentControlState = getControlState();

  if (cCurrentControlState != m_cLastControlState) {
    m_cLastControlState = cCurrentControlState;
    m_pin->update_direction((cCurrentControlState=='1') ? IOPIN::DIR_INPUT : IOPIN::DIR_OUTPUT,
			    false);
    bStateChange = true;
  }

  char cCurrentSourceState = getSourceState();

  if (cCurrentSourceState != m_cLastSourceState) {
    m_cLastSourceState = cCurrentSourceState;
    m_pin->setDrivingState(cCurrentSourceState);
    bStateChange = true;
  }

  char cCurrentPullupControlState = getPullupControlState();

  if (cCurrentPullupControlState != m_cLastPullupControlState) {
    m_cLastPullupControlState = cCurrentPullupControlState;
    m_pin->update_pullup(m_cLastPullupControlState,false);
    bStateChange = true;
  }  

  if (bStateChange) {

    Dprintf(("PinModule::updatePinModule() exit cont=%c,source=%c,pullup%c\n",
	     m_cLastControlState,m_cLastSourceState,
	     m_cLastPullupControlState));

    if (m_pin->snode)
      m_pin->snode->update();
    else
      setDrivenState(cCurrentSourceState);
  }

}

void PinModule::setDefaultControl(SignalControl *newDefaultControl)
{
  if(!m_defaultControl && newDefaultControl) {
    m_defaultControl = newDefaultControl;
    setControl(m_defaultControl);
  }
  else
	delete newDefaultControl;
}
void PinModule::setControl(SignalControl *newControl)
{
  m_activeControl = newControl ? newControl : m_defaultControl;
}

void PinModule::setDefaultSource(SignalControl *newDefaultSource)
{
  if(!m_defaultSource && newDefaultSource) {
    m_defaultSource = newDefaultSource;
    setSource(m_defaultSource);
  }
}
void PinModule::setSource(SignalControl *newSource)
{
  m_activeSource = newSource ? newSource : m_defaultSource;
}

void PinModule::setDefaultPullupControl(SignalControl *newDefaultPullupControl)
{
  if(!m_defaultPullupControl && newDefaultPullupControl) {
    m_defaultPullupControl = newDefaultPullupControl;
    setPullupControl(m_defaultPullupControl);
  }
}
void PinModule::setPullupControl(SignalControl *newPullupControl)
{
  m_activePullupControl = newPullupControl ? newPullupControl : m_defaultPullupControl;
}

char PinModule::getControlState()
{
  return m_activeControl ? m_activeControl->getState() : '?';
}
char PinModule::getSourceState()
{
  return m_activeSource ? m_activeSource->getState() : '?';
}
char PinModule::getPullupControlState()
{
  return m_activePullupControl ? m_activePullupControl->getState() : '?';
}


void PinModule::setDrivenState(char new3State)
{
  m_cLastSinkState = new3State;

  list <SignalSink *> :: iterator ssi;
  for (ssi = sinks.begin(); ssi != sinks.end(); ++ssi)
    (*ssi)->setSinkState(new3State);
}

void PinModule::setDrivingState(char new3State)
{
  //printf("PinModule::%s -- does nothing\n",__FUNCTION__);
}

void PinModule::set_nodeVoltage(double)
{
  //printf("PinModule::%s -- does nothing\n",__FUNCTION__);
}
void PinModule::putState(char)
{
  //printf("PinModule::%s -- does nothing\n",__FUNCTION__);
}
void PinModule::setDirection()
{
  //printf("PinModule::%s -- does nothing\n",__FUNCTION__);
}

void PinModule::updateUI()
{
  m_port->updateUI();
}



// The IOPORT class is deprecated.

#if defined(OLD_IOPORT_DESIGN)

//-------------------------------------------------------------------
//
// IOPORT::update_stimuli
//
//   input: none
//  return: the states of the stimuli that are driving this ioport
//
//  This member function will update each node that is attached to the
//  iopins of this port. If there are no pins attached, 0 is returned.
//  
//
//-------------------------------------------------------------------
int IOPORT::update_stimuli(void)
{

  unsigned int v = value.get();

  return v ^ get_value();
}



//-------------------------------------------------------------------
//-------------------------------------------------------------------
double IOPORT::get_bit_voltage(unsigned int bit_number)
{

  double v;

  if(pins[bit_number]) {
    if(pins[bit_number]->snode) {
      cout << "Warning IOPORT::get_bit_voltage has changed\n";
      v = pins[bit_number]->snode->get_nodeVoltage();
    }
    else
      v = pins[bit_number]->get_Vth();
  }
  else
    v = (value.get() &  (1<<bit_number)) ?  5.0 : 0.0;


  return v;
}      

//-------------------------------------------------------------------
//-------------------------------------------------------------------
bool IOPORT::get_bit(unsigned int bit_number)
{
  //cout << "get_bit, latch " << internal_latch << " bit " << bit_number << endl;
  return (internal_latch &  (1<<bit_number )) ? true : false;

}

//-------------------------------------------------------------------
//  IOPORT::get_value(void)
//
//   inputs:  none
//  returns:  the current state of the ioport
//
// If there are stimuli attached to the iopins, then their current
// state is obtained. If there aren't any attached, then the last
// value written to the ioport is obtained.
//
//-------------------------------------------------------------------

unsigned int IOPORT::get_value(void)
{

  // Update the stimuli - if there are any

  unsigned int current_value = value.get();
  
  unsigned int i=0;
  unsigned int m=1;

  for(i=0; i<num_iopins; i++, m<<=1) {
    if(pins[i] && pins[i]->snode) {

      double v = pins[i]->snode->get_nodeVoltage();

      if(current_value & m) {
	// this io bit is currently a high
	if(v <= pins[i]->get_h2l_threshold())
	  current_value ^= m;
      } else
	if (v > pins[i]->get_l2h_threshold())
	  current_value ^= m;
    }
  }

  value.put(current_value);

  return(value.get());
}
//-------------------------------------------------------------------
//  IOPORT::get(void)
//
//   inputs:  none
//  returns:  the current state of the ioport
//
// get is identical to get_value except that tracing is performed.
//
//-------------------------------------------------------------------

unsigned int IOPORT::get(void)
{

  trace.raw(read_trace.get() | value.get());
  return get_value();
}

//-------------------------------------------------------------------
//  IOPORT::put(unsigned int new_value)
//
//  inputs:  new_value - 
//                       
//  returns: none
//
//  The I/O Port is updated with the new value. If there are any stimuli
// attached to the I/O pins then they will be updated as well.
//
//-------------------------------------------------------------------

void IOPORT::put(unsigned int new_value)
{


  // The I/O Ports have an internal latch that holds the state of the last
  // write, even if the I/O pins are configured as inputs. If the tris port
  // changes an I/O pin from an input to an output, then the contents of this
  // internal latch will be placed onto the external I/O pin.

  internal_latch = new_value;

  trace.raw(write_trace.get() | value.get());

  unsigned int current_value = value.get();

  value.put(new_value);

  if(stimulus_mask && (current_value != new_value)) {

    unsigned int diff = current_value ^ new_value;

    // Update all I/O pins that have stimuli attached to
    // them and their state is being changed by this put() operation.

    for(unsigned int i = 0; i<num_iopins; i++,diff>>=1)
      if((diff&1) && pins[i] && pins[i]->snode)
	pins[i]->snode->update();
  }

}

//-------------------------------------------------------------------
// void IOPORT::put_value(unsigned int new_value)
//
//  When there's a gui initiated change to the IO port, we'll pass
// though here. There are three things that we do. First, we update
// the I/O port the way the gui asks us. Note however, that it's 
// possible that the gui's requested will go un-honored (if for example,
// we try to force an output to change states or if there's a stimulus
// driving the bus already). 
//   Next, after updating the IO port (and all of it's connected stimuli),
// we'll call the gui to update its windows. This is done through the
// xref->update call.
//   Finally, we'll check all of the I/O pins that have changed as a
// result of the IO port update and individually call each of their 
// cross references.
//
//-------------------------------------------------------------------
void IOPORT::put_value(unsigned int new_value)
{
  unsigned int i,j;
  unsigned int old_value = value.get();
  unsigned int diff;

 
  value.put(new_value);

  // Update the stimuli - if there are any
  if(stimulus_mask)
    update_stimuli();

  
  update();
  
  // Find the pins that have changed states
  diff = (old_value ^ value.get()) & valid_iopins;

  // Update the cross references for each pin that has changed.
  for(i=0,j=1; i<num_iopins; i++,j<<=1) {

    if((j & diff) && pins[i])
      pins[i]->update();

  }

}

//-------------------------------------------------------------------
//-------------------------------------------------------------------
void IOPORT::setbit(unsigned int bit_number, bool new_value)
{

  int bit_mask = 1<<bit_number;
  unsigned int current_value = value.get();
  bool current_bit_value = (current_value & bit_mask) ? true : false;

  if( current_bit_value != new_value)
    {
      trace_register_write();
      value.put(current_value ^ bit_mask);

      internal_latch = (current_value & bit_mask) | (internal_latch & ~bit_mask);
    }

}

//-------------------------------------------------------------------
//-------------------------------------------------------------------
void IOPORT::change_pin_direction(unsigned int bit_number, bool new_direction)
{

  cout << " IOPORT::" << __FUNCTION__ <<'(' << bit_number << ',' << new_direction << ") doesn't do anything.\n";

}

//-------------------------------------------------------------------
// getIO(unsigned int pin_number)
//  return the I/O pin at the bit position requested.
//-------------------------------------------------------------------
IOPIN *IOPORT::getIO(unsigned int pin_number)
{
  if(pins && pin_number < num_iopins)
    return pins[pin_number];

  return 0;
}
//-------------------------------------------------------------------
// attach_iopin
//   This will store a pointer to the iopin that is associated with
// one of the bits of the I/O port.
//
//-------------------------------------------------------------------
IOPIN *IOPORT::addPin(IOPIN * new_pin, unsigned int bit_position)
{

  if(bit_position < num_iopins)

    pins[bit_position] = new_pin;

  else
    cout << "Warning: iopin pin number ("<<bit_position 
	 <<") is invalid for " << name() << ". Max iopins " << num_iopins << '\n';

  if(verbose)
    cout << "attaching iopin to ioport " << name() << '\n';

  return new_pin;
}
//-------------------------------------------------------------------
// attach_iopin
//   This will store a pointer to the iopin that is associated with
// one of the bits of the I/O port.
//
//-------------------------------------------------------------------
void IOPORT::attach_iopin(IOPIN * new_pin, unsigned int bit_position)
{

  if(bit_position < num_iopins)

    pins[bit_position] = new_pin;

  else
    cout << "Warning: iopin pin number ("<<bit_position 
	 <<") is invalid for " << name() << ". Max iopins " << num_iopins << '\n';

  if(verbose)
    cout << "attaching iopin to ioport " << name() << '\n';
}

//-------------------------------------------------------------------
//-------------------------------------------------------------------
void IOPORT::attach_stimulus(stimulus *new_stimulus, unsigned int bit_position)
{

  if(pins  && (bit_position < num_iopins) && pins[bit_position]) {

    stimulus_mask |= (1<<bit_position);

    if(pins[bit_position]->snode == 0)
      {
	// If this I/O pin is not attached to a node yet, 
	// then create a node and attach it.

	pins[bit_position]->snode = new Stimulus_Node();
	pins[bit_position]->snode->attach_stimulus(pins[bit_position]);
      }

    // attach the new stimulus to the same node as this I/O pin's

    pins[bit_position]->snode->attach_stimulus(new_stimulus);
  }

}

//-------------------------------------------------------------------
//-------------------------------------------------------------------
void IOPORT::attach_node(Stimulus_Node *new_node, unsigned int bit_position)
{

  if(pins[bit_position])
    {
      stimulus_mask |= (1<<bit_position);
      //      pins[bit_position]->snode == new_node;
    }
  else
    cout << "Error: attaching node to a non-existing I/O pin.\n";

}

//-------------------------------------------------------------------
// trace_register_write
//   - a wrapper for trace.register_write
// This provides an option for IOPORTs derived from the IOPORT class 
// to override the behavior of IOPORT traces.
//-------------------------------------------------------------------
void IOPORT::trace_register_write(void)
{
  trace.raw(write_trace.get() | value.get());
  //trace.register_write(address,value.get());
}

IOPORT::IOPORT(unsigned int _num_iopins)
  : sfr_register()
{
  stimulus_mask = 0;
  num_iopins = _num_iopins;
  valid_iopins = (1<<num_iopins) - 1;
  address = 0;
  value.put(0);
  internal_latch = 0;

  pins = (IOPIN **) new char[sizeof (IOPIN *) * num_iopins];

  for(unsigned int i=0; i<num_iopins; i++)
    pins[i] = 0;

  new_name("ioport");
}

IOPORT::~IOPORT()
{
    for(unsigned int i=0; i<num_iopins; i++)
    {
	if(pins[i] != 0)
	    delete pins[i];
    }
    delete pins;
}

#endif


syntax highlighted by Code2HTML, v. 0.9.1