/*
   Copyright (C) 2006 Roy R Rankin

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 "../config.h"
#include "stimuli.h"
#include "psp.h"

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

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


class CS_SignalSink : public SignalSink
{
public:
  CS_SignalSink(PSP *_psp)
    : m_psp(_psp)
  {
    assert(_psp);
  }

  void setSinkState(char new3State)
  {
    m_psp->setCS_State(new3State);
  }
private:
  PSP *m_psp;
};

class RD_SignalSink : public SignalSink
{
public:
  RD_SignalSink(PSP *_psp)
    : m_psp(_psp)
  {
    assert(_psp);
  }

  void setSinkState(char new3State)
  {
    m_psp->setRD_State(new3State);
  }
private:
  PSP *m_psp;
};

class WR_SignalSink : public SignalSink
{
public:
  WR_SignalSink(PSP *_psp)
    : m_psp(_psp)
  {
    assert(_psp);
  }

  void setSinkState(char new3State)
  {
    m_psp->setWR_State(new3State);
  }
private:
  PSP *m_psp;
};

//
// setup information for PSP module
//
void PSP::initialize( PIR_SET *_pir_set, PicPSP_PortRegister *_port_set,
	PicTrisRegister *_port_tris, PicPSP_TrisRegister *_cntl_tris,
        PinModule *pin_RD, PinModule *pin_WR, PinModule *pin_CS)
{
   if (verbose & 2)
     cout << "PSP::initialize called\n";
   pir_set = _pir_set;
   parallel_port = _port_set;
   parallel_port->setPSP(this);
   parallel_tris = _port_tris;
   cntl_tris = _cntl_tris;
   //
   // The rest of this function allows catching of changes to PSP contol signals
   //
   if (!m_rd_sink) 
   {
      m_rd_sink = new RD_SignalSink(this);
      Not_RD = pin_RD;
      if (Not_RD)
        Not_RD->addSink(m_rd_sink);
   }
   if (!m_cs_sink) 
   {
      m_cs_sink = new CS_SignalSink(this);
      Not_CS = pin_CS;
      if (Not_CS)
        Not_CS->addSink(m_cs_sink);
   }
   if (!m_wr_sink) 
   {
      m_wr_sink = new WR_SignalSink(this);
      Not_WR = pin_WR;
      if (Not_WR)
        Not_WR->addSink(m_wr_sink);
   }

}
//
// process changes on the control pins
//
void PSP::state_control()
{
    if (! pspmode())
	return;
    if (verbose & 2)
      cout << "PSP state change cs=" << cs << " wr=" << wr << " rd=" << rd <<endl;

    if (rd && wr && cs)	// this is an error condition
    {
	cerr << "PSP: Error CS, WR and RD must not all be low\n";
	parallel_tris->put(0xff);
	state = ST_INACTIVE;
	return;
    }
    else if (cs && rd)
    {
	parallel_tris->put(0);
	parallel_port->put_value(put_value);
	cntl_tris->put_value(cntl_tris->get() & ~OBF);
	state = ST_READ;
    }
    else if (cs && wr)
    {
	parallel_tris->put(0xff);
	get_value = parallel_port->get_value();
	state = ST_WRITE;
    }
    else
    {
  	if (state != ST_INACTIVE)
        {
	    pir_set->set_pspif();
	}

	//
	// On first bus write set IBF flag.
 	// if a second bus write occurs prior to read of pic port (portd)
	// IBOV flag is also set.
	//
	if (state == ST_WRITE)
	{
	    unsigned int trise_val = cntl_tris->get();
	    if (trise_val & IBF)
		cntl_tris->put_value(trise_val | IBOV);
	    else
	        cntl_tris->put_value(trise_val | IBF);
	}

	parallel_tris->put(0xff);
	state = ST_INACTIVE;
    }
    return;
}
//
// The next three functions are called when their control pin change state
// The control pins are active low which is converted to active high signals
void PSP::setRD_State(char new3State)
{
	rd = new3State == '0';
	state_control();
}
void PSP::setCS_State(char new3State)
{
	cs = new3State == '0';
	state_control();
}
void PSP::setWR_State(char new3State)
{
	wr = new3State == '0';
	state_control();
}

//
// psp_put is called on write to portd when pspmode is active
// set OBF register bit and save value for next bus read
//
void PSP::psp_put(unsigned int new_value)
{
    cntl_tris->put_value(cntl_tris->get() | OBF);
    put_value = new_value;
}
//
// psp_get is called on read of portd when pspmode is active so
// we can clear the IBF flag
//
unsigned int PSP::psp_get(void)
{
	cntl_tris->put_value(cntl_tris->get() & ~IBF);
	return(get_value);
}


syntax highlighted by Code2HTML, v. 0.9.1