/*
   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 "../config.h"
#include "pic-processor.h"
#include "14bit-processors.h"  // %%% FIXME %%% remove the dependencies on this
#include "pic-ioports.h"
#include "interface.h"
#include "p16x6x.h"
#include "p16f62x.h"

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

//-------------------------------------------------------------------
//
//                 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 PicSignalSource : public SignalControl
{
public:
  PicSignalSource(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(("PicSignalSource::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;
};



//------------------------------------------------------------------------

PicPortRegister::PicPortRegister(const char *port_name,
				 unsigned int numIopins, 
				 unsigned int enableMask)
  : PortRegister(numIopins, false), m_tris(0)
{
  new_name(port_name);
  PortRegister::setEnableMask(enableMask);
}

class PicSignalControl : public SignalControl
{
public:
  PicSignalControl(Register *_reg, unsigned int bitPosition)
    : m_register(_reg), m_bitMask(1<<bitPosition)
  {
  }
  char getState()
  {
    return m_register ? m_register->get3StateBit(m_bitMask) : '?';
  }
private:
  Register *m_register;
  unsigned int m_bitMask;
};

void PicPortRegister::setTris(PicTrisRegister *new_tris)
{
  if (!m_tris)
    m_tris = new_tris;

    unsigned int mask = getEnableMask();
    for (unsigned int i=0, m = 1; i<mNumIopins; i++, m <<= 1) {
      if (mask & m)
          operator[](i).setDefaultControl(new PicSignalControl(m_tris, i));
    }
}
//------------------------------------------------------------------------

PicTrisRegister::PicTrisRegister(const char *tris_name, PicPortRegister *_port)
  : sfr_register(),m_port(_port)
{
  new_name(tris_name);
  if (m_port)
    m_port->setTris(this);
}
void PicTrisRegister::put(unsigned int new_value)
{
  trace.raw(write_trace.get() | value.data);
  value.data = new_value;
  if (m_port)
    m_port->updatePort();
}

unsigned int PicTrisRegister::get()
{
  trace.raw(read_trace.get() | value.data);
  return value.data;
}

//------------------------------------------------------------------------

PicPSP_TrisRegister::PicPSP_TrisRegister(const char *tris_name, PicPortRegister *_port)
  : sfr_register(),m_port(_port)
{
  new_name(tris_name);
  if (m_port)
    m_port->setTris((PicTrisRegister *)this);
}
// If not in PSPMODE, OBF and IBF are always clear
// When in PSPMODE, OBF and IBF can only be cleared by reading and writing
// to the PSP parallel port and are set by bus transfers.
//
void PicPSP_TrisRegister::put(unsigned int new_value)
{
  unsigned int mask = (PSP::OBF | PSP::IBF);
  unsigned int fixed;

  trace.raw(write_trace.get() | value.data);
  if (! (new_value & PSP::PSPMODE))
	fixed = 0;
   else
	fixed = value.data & mask;

  value.data = (new_value & ~mask) | fixed;
  if (m_port)
    m_port->updatePort();
}
// used by gpsim to change register value
void PicPSP_TrisRegister::put_value(unsigned int new_value)
{

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

  value.data = new_value;
  if (m_port)
    m_port->updatePort();
}

unsigned int PicPSP_TrisRegister::get(void)
{
  return value.data;
}


//------------------------------------------------------------------------



PicPortBRegister::PicPortBRegister(const char *port_name, unsigned int numIopins, unsigned int enableMask)
  : PicPortRegister(port_name, numIopins, enableMask),
    m_bRBPU(false),
    m_bIntEdge(false)
{
}

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

  cpu14->intcon->set_rbif(false);

//  unsigned int diff = mEnableMask & (new_value ^ value.data);
//RRR  if(diff) {
    drivingValue = new_value & mEnableMask;
    value.data = drivingValue;
    // 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.
    updatePort();
//RRR  }

}

unsigned int PicPortBRegister::get()
{
  cpu14->intcon->set_rbif(false);

  return rvDrivenValue.data;
}

//------------------------------------------------------------------------
// setbit
// FIXME - a sink should be created for the intf and rbif functions.

void PicPortBRegister::setbit(unsigned int bit_number, char new3State)
{
  Dprintf(("PicPortBRegister::setbit() bit=%d,val=%c\n",bit_number,new3State));

  bool bNewValue = new3State=='1' || new3State=='W';
  if (bit_number == 0 && (((rvDrivenValue.data&1)==1)!=m_bIntEdge) 
      && (bNewValue == m_bIntEdge))
    cpu14->intcon->set_intf(true);


  PortRegister::setbit(bit_number, new3State);

  unsigned int bitMask = (1<<bit_number) & 0xF0;

  if ( (drivingValue ^ rvDrivenValue.data) & m_tris->get() & bitMask )
    cpu14->intcon->set_rbif(true);
}

void PicPortBRegister::setRBPU(bool bNewRBPU)
{
  m_bRBPU = !bNewRBPU;

  Dprintf(("PicPortBRegister::setRBPU() =%d\n",(m_bRBPU?1:0)));

  unsigned int mask = getEnableMask();
  for (unsigned int i=0, m=1; mask; i++, m<<= 1)
    if (mask & m) {
      mask ^= m;
      operator[](i).getPin().update_pullup(m_bRBPU ? '1' : '0',true);
    }

}

void PicPortBRegister::setIntEdge(bool bNewIntEdge)
{
  m_bIntEdge = bNewIntEdge;
}


PicPSP_PortRegister::PicPSP_PortRegister(const char *port_name,
                                 unsigned int numIopins,
                                 unsigned int enableMask)
  : PortRegister(numIopins, false), m_tris(0), m_psp(0)
{
  new_name(port_name);
  PortRegister::setEnableMask(enableMask);
}

void PicPSP_PortRegister::put(unsigned int new_value)
{
  trace.raw(write_trace.get() | value.data);
  unsigned int diff = mEnableMask & (new_value ^ value.data);

  if (m_psp && m_psp->pspmode())
  {
	m_psp->psp_put(new_value);
  }
  else if(diff) {
    drivingValue = new_value & mEnableMask;
    value.data = drivingValue;
    // 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.
    updatePort();
  }

}
unsigned int PicPSP_PortRegister::get()
{

  if (m_psp && m_psp->pspmode())
	return(m_psp->psp_get());

  return rvDrivenValue.data;
}


void PicPSP_PortRegister::setTris(PicTrisRegister *new_tris)
{
  if (!m_tris)
    m_tris = new_tris;

    unsigned int mask = getEnableMask();
    for (unsigned int i=0, m = 1; i<mNumIopins; i++, m <<= 1) {
      if (mask & m)
          operator[](i).setDefaultControl(new PicSignalControl(m_tris, i));
    }
}

//------------------------------------------------------------------------
// Latch Register

PicLatchRegister::PicLatchRegister(const char *_name, PortRegister *_port)
  : m_port(_port), m_EnableMask(0xff)
{
  new_name(_name);
}

void PicLatchRegister::put(unsigned int new_value)
{
  trace.raw(write_trace.get() | value.data);
  value.data = new_value & m_EnableMask;
  m_port->put_value(value.data);
}
void PicLatchRegister::put_value(unsigned int new_value)
{
  value.data = new_value & m_EnableMask;
  m_port->put_value(value.data);
}
unsigned int PicLatchRegister::get()
{
  trace.raw(read_trace.get()  | value.data);
  trace.raw(read_trace.geti() | value.init);

  return value.data;
}
void PicLatchRegister::setbit(unsigned int bit_number, char new_value)
{
  printf("PicLatchRegister::setbit() -- shouldn't be called\n");
}
void PicLatchRegister::setEnableMask(unsigned int nEnableMask)
{
  m_EnableMask = nEnableMask;
}



syntax highlighted by Code2HTML, v. 0.9.1