/*
   Copyright (C) 1998 T. 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 gpasm; 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 "14bit-registers.h"
//#include "14bit-instructions.h"
#include "../config.h"
#include "16bit-processors.h"

#include <string>
#include "stimuli.h"
#include "symbol.h"
#include "eeprom.h"


//------------------------------------------------------------------------
// Configuration bits
//
// The 16bit-core PIC devices contain configuration memory starting at 
// address 0x300000.
//


//------------------------------------------------------------------------
// Config1H - default 

class Config1H : public ConfigMemory 
{
#define FOSC0   (1<<0)
#define FOSC1   (1<<1)
#define FOSC2   (1<<2)
#define OSCEN   (1<<5)

#define CONFIG1H_default (OSCEN | FOSC2 | FOSC1 | FOSC0)
public:
  Config1H(_16bit_processor *pCpu, unsigned int addr)
    : ConfigMemory("CONFIG1H", CONFIG1H_default, "Oscillator configuration", pCpu, addr)
  {
  }
};


//------------------------------------------------------------------------
// Config2H - default 
//  The default Config2H register controls the 18F series WDT. 
class Config2H : public ConfigMemory 
{
#define WDTEN   (1<<0)
#define WDTPS0  (1<<1)
#define WDTPS1  (1<<2)
#define WDTPS2  (1<<3)

#define CONFIG2H_default (WDTEN | WDTPS0 | WDTPS1 | WDTPS2)
public:
  Config2H(_16bit_processor *pCpu, unsigned int addr)
    : ConfigMemory("CONFIG2H", CONFIG2H_default, "WatchDog configuration", pCpu, addr)
  {
  }
  virtual void set(gint64 v)
  {
    Integer::set(v);
    if (m_pCpu)
      m_pCpu->wdt.initialize((v & WDTEN) == WDTEN);
  }
};



#define PWRTEN  1<<0
#define BOREN   1<<1
#define BORV0   1<<2
#define BORV1   1<<2

#define CCP2MX  1<<0

#define STVREN  1<<0
//-------------------------------------------------------------------
_16bit_processor::_16bit_processor(const char *_name, const char *desc)
  : pic_processor(_name,desc),
    pir1(0,0), pir2(0,0)
{

  package = 0;
  cout << "FIXME: 16bit processor is assuming that PLL is on - should check config bits\n";
  pll_factor = 2;

  pc = new Program_Counter16();
  pc->set_trace_command(trace.allocateTraceType(new PCTraceType(this,1)));

  m_porta = new PicPortRegister("porta",8,0x7f);
  m_trisa = new PicTrisRegister("trisa", m_porta);
  m_lata  = new PicLatchRegister("lata", m_porta);
  m_lata->setEnableMask(0x7f);

  m_portb = new PicPortRegister("portb",8,0xff);
  m_trisb = new PicTrisRegister("trisb", m_portb);
  m_latb  = new PicLatchRegister("latb", m_portb);

  m_portc = new PicPortRegister("portc",8,0xff);
  m_trisc = new PicTrisRegister("trisc", m_portc);
  m_latc  = new PicLatchRegister("latc", m_portc);

}

//-------------------------------------------------------------------
_16bit_processor::~_16bit_processor()
{

}

//-------------------------------------------------------------------
pic_processor *_16bit_processor::construct()
{
    cout << "creating 16bit processor construct\n";

  _16bit_processor *p = new _16bit_processor;

  if(verbose)
    cout << " 18c242 construct\n";

  p->create();
  p->create_invalid_registers();
  p->create_symbols();
  p->name_str = "generic 16bit processor";
  symbol_table.add_module(p,p->name_str.c_str());

  return p;


}

//-------------------------------------------------------------------
void _16bit_processor :: create_sfr_map()
{
  if(verbose)
    cout << "creating 18cxxx common registers\n";

  add_file_registers(0x0, 0xf7f, 0);

  RegisterValue porv(0,0);

  add_sfr_register(m_porta,       0xf80,porv);
  add_sfr_register(m_portb,       0xf81,porv);
  add_sfr_register(m_portc,       0xf82,porv);

  add_sfr_register(m_lata,        0xf89,porv);
  add_sfr_register(m_latb,        0xf8a,porv);
  add_sfr_register(m_latc,        0xf8b,porv);

  add_sfr_register(m_trisa,       0xf92,RegisterValue(0x7f,0));
  add_sfr_register(m_trisb,       0xf93,RegisterValue(0xff,0));
  add_sfr_register(m_trisc,       0xf94,RegisterValue(0xff,0));

  add_sfr_register(&pie1,	  0xf9d,porv,"pie1");
  add_sfr_register(&pir1,	  0xf9e,porv,"pir1");

  add_sfr_register(&ipr1,	  0xf9f,porv,"ipr1");
  add_sfr_register(&pie2,	  0xfa0,porv,"pie2");
  add_sfr_register(&pir2,	  0xfa1,porv,"pir2");
  add_sfr_register(&ipr2,	  0xfa2,porv,"ipr2");


  usart.initialize(&pir_set_def,&(*m_portc)[6], &(*m_portc)[7],
		   new _TXREG(&usart), new _RCREG(&usart));

  add_sfr_register(&usart.rcsta,    0xfab,porv,"rcsta");
  add_sfr_register(&usart.txsta,    0xfac,RegisterValue(0x02,0),"txsta");
  add_sfr_register(usart.txreg,     0xfad,porv,"txreg");
  add_sfr_register(usart.rcreg,     0xfae,porv,"rcreg");
  add_sfr_register(&usart.spbrg,    0xfaf,porv,"spbrg");

  add_sfr_register(&t3con,	  0xfb1,porv,"t3con");
  add_sfr_register(&tmr3l,	  0xfb2,porv,"tmr3l");
  add_sfr_register(&tmr3h,	  0xfb3,porv,"tmr3h");

  add_sfr_register(&ccp2con,	  0xfba,porv,"ccp2con");
  add_sfr_register(&ccpr2l,	  0xfbb,porv,"ccpr2l");
  add_sfr_register(&ccpr2h,	  0xfbc,porv,"ccpr2h");
  add_sfr_register(&ccp1con,	  0xfbd,porv,"ccp1con");
  add_sfr_register(&ccpr1l,	  0xfbe,porv,"ccpr1l");
  add_sfr_register(&ccpr1h,	  0xfbf,porv,"ccpr1h");

  add_sfr_register(&adcon1,	  0xfc1,porv,"adcon1");
  add_sfr_register(&adcon0,	  0xfc2,porv,"adcon0");
  add_sfr_register(&adresl,	  0xfc3,porv,"adresl");
  add_sfr_register(&adresh,	  0xfc4,porv,"adresh");

  add_sfr_register(&ssp.sspcon2,  0xfc5,porv,"sspcon2");
  add_sfr_register(&ssp.sspcon,   0xfc6,porv,"sspcon1");
  add_sfr_register(&ssp.sspstat,  0xfc7,porv,"sspstat");
  add_sfr_register(&ssp.sspadd,   0xfc8,porv,"sspadd");
  add_sfr_register(&ssp.sspbuf,   0xfc9,porv,"sspbuf");

  add_sfr_register(&t2con,	  0xfca,porv,"t2con");
  add_sfr_register(&pr2,	  0xfcb,RegisterValue(0xff,0),"pr2");
  add_sfr_register(&tmr2,	  0xfcc,porv,"tmr2");

  add_sfr_register(&t1con,	  0xfcd,porv,"t1con");
  add_sfr_register(&tmr1l,	  0xfce,porv,"tmr1l");
  add_sfr_register(&tmr1h,	  0xfcf,porv,"tmr1h");

  add_sfr_register(&rcon,	  0xfd0,porv,"rcon");
  add_sfr_register(&wdtcon,	  0xfd1,porv,"wdtcon");
  add_sfr_register(&lvdcon,	  0xfd2,porv,"lvdcon");
  add_sfr_register(&osccon,	  0xfd3,porv,"osccon");
  // 0x4 is not defined
  add_sfr_register(&t0con,	  0xfd5,RegisterValue(0xff,0),"t0con");
  add_sfr_register(&tmr0l,	  0xfd6,porv,"tmr0l");
  add_sfr_register(&tmr0h,	  0xfd7,porv,"tmr0h");
  t0con.put(0xff);  /**FIXME - need a way to set this to 0xff at reset*/

  add_sfr_register(status,       0xfd8);

  add_sfr_register(&ind2.fsrl,	  0xfd9,porv,"fsr2l");
  add_sfr_register(&ind2.fsrh,    0xfda,porv,"fsr2h");
  add_sfr_register(&ind2.plusw,   0xfdb,porv,"plusw2");
  add_sfr_register(&ind2.preinc,  0xfdc,porv,"preinc2");
  add_sfr_register(&ind2.postdec, 0xfdd,porv,"postdec2");
  add_sfr_register(&ind2.postinc, 0xfde,porv,"postinc2");
  add_sfr_register(&ind2.indf,    0xfdf,porv,"indf2");

  add_sfr_register(&bsr,          0xfe0,porv,"bsr");

  add_sfr_register(&ind1.fsrl,	  0xfe1,porv,"fsr1l");
  add_sfr_register(&ind1.fsrh,    0xfe2,porv,"fsr1h");
  add_sfr_register(&ind1.plusw,   0xfe3,porv,"plusw1");
  add_sfr_register(&ind1.preinc,  0xfe4,porv,"preinc1");
  add_sfr_register(&ind1.postdec, 0xfe5,porv,"postdec1");
  add_sfr_register(&ind1.postinc, 0xfe6,porv,"postinc1");
  add_sfr_register(&ind1.indf,    0xfe7,porv,"indf1");

  add_sfr_register(W,            0xfe8);

  add_sfr_register(&ind0.fsrl,	  0xfe9,porv,"fsr0l");
  add_sfr_register(&ind0.fsrh,    0xfea,porv,"fsr0h");
  add_sfr_register(&ind0.plusw,   0xfeb,porv,"plusw0");
  add_sfr_register(&ind0.preinc,  0xfec,porv,"preinc0");
  add_sfr_register(&ind0.postdec, 0xfed,porv,"postdec0");
  add_sfr_register(&ind0.postinc, 0xfee,porv,"postinc0");
  add_sfr_register(&ind0.indf,    0xfef,porv,"indf0");

  add_sfr_register(&intcon3, 0xff0, porv,"intcon3");
  add_sfr_register(&intcon2, 0xff1, porv,"intcon2");
  add_sfr_register(&intcon,  0xff2, porv,"intcon");

  add_sfr_register(&prodl, 0xff3, porv,"prodl");
  add_sfr_register(&prodh, 0xff4, porv,"prodh");

  add_sfr_register(&tbl.tablat,  0xff5, porv,"tablat");
  add_sfr_register(&tbl.tabptrl, 0xff6, porv,"tabptrl");
  add_sfr_register(&tbl.tabptrh, 0xff7, porv,"tabptrh");
  add_sfr_register(&tbl.tabptru, 0xff8, porv,"tabptru");

  if(pcl)
    delete pcl;
  pcl = new PCL16();

  add_sfr_register(pcl,     0xff9);
  add_sfr_register(pclath,  0xffa);
  add_sfr_register(&pclatu, 0xffb, porv, "pclatu");

  stack = &stack16;
  add_sfr_register(&stack16.stkptr,  0xffc,porv,"stkptr");
  add_sfr_register(&stack16.tosl,    0xffd,porv,"tosl");
  add_sfr_register(&stack16.tosh,    0xffe,porv,"tosh");
  add_sfr_register(&stack16.tosu,    0xfff,porv,"tosu");


  EEPROM *e = get_eeprom();

  if (e) {
    add_sfr_register(e->get_reg_eedata(), 0xfa8);
    add_sfr_register(e->get_reg_eeadr(), 0xfa9);
    add_sfr_register(e->get_reg_eecon1(), 0xfa6, RegisterValue(0,0));
    add_sfr_register(e->get_reg_eecon2(), 0xfa7);
  }

  // Initialize all of the register cross linkages
  pir_set_def.set_pir1(&pir1);
  pir_set_def.set_pir2(&pir2);

  tmr2.ssp_module = &ssp;

  tmr1l.tmrh   = &tmr1h;
  tmr1l.t1con  = &t1con;
  tmr1l.setInterruptSource(new InterruptSource(&pir1, PIR1v1::TMR1IF));
  tmr1l.ccpcon = &ccp1con;

  tmr1h.tmrl  = &tmr1l;

  t1con.tmrl  = &tmr1l;

  t2con.tmr2  = &tmr2;
  tmr2.pir_set = &pir_set_def; //get_pir_set();
  tmr2.pr2    = &pr2;
  tmr2.t2con  = &t2con;
  tmr2.ccp1con = &ccp1con;
  tmr2.ccp2con = &ccp2con;
  pr2.tmr2    = &tmr2;


  tmr3l.tmrh  = &tmr3h;
  tmr3l.t1con = &t3con;
  tmr3l.setInterruptSource(new InterruptSource(&pir2, PIR2v2::TMR3IF));
  tmr3l.ccpcon = &ccp1con;

  tmr3h.tmrl  = &tmr3l;

  t3con.tmrl  = &tmr3l;
  t3con.tmr1l = &tmr1l;
  t3con.ccpr1l = &ccpr1l;
  t3con.ccpr2l = &ccpr2l;

  ccp1con.setCrosslinks(&ccpr1l, &pir_set_def, &tmr2);
  ccp1con.setIOpin(&((*m_portc)[2]));
  ccpr1l.ccprh  = &ccpr1h;
  ccpr1l.tmrl   = &tmr1l;
  ccpr1h.ccprl  = &ccpr1l;

  pir1.set_intcon(&intcon);
  pir1.set_pie(&pie1);
  pie1.pir = &pir1;
  pie1.new_name("pie1");

  pir2.set_intcon(&intcon);
  pir2.set_pie(&pie2);
  
  pie2.pir    = &pir2;
  pie2.new_name("pie2");

  // All of the status bits on the 16bit core are writable
  status->write_mask = 0xff;


  adcon0.setAdresLow(&adresl);
  adcon0.setAdres(&adresh);
  adcon0.setAdcon1(&adcon1);
  adcon0.setIntcon(&intcon);
  adcon0.setPir(&pir1);
  adcon0.setChannel_Mask(7); // Greater than 4 channels
  adcon0.setA2DBits(10);

  adcon1.setValidCfgBits(ADCON1::PCFG0 | ADCON1::PCFG1 | 
			 ADCON1::PCFG2 | ADCON1::PCFG3,0);

  adcon1.setNumberOfChannels(8);	// Allow 8 channel configuration
  adcon1.setChannelConfiguration(0, 0xff);
  adcon1.setChannelConfiguration(1, 0xff);
  adcon1.setChannelConfiguration(2, 0x1f);
  adcon1.setChannelConfiguration(3, 0x1f);
  adcon1.setChannelConfiguration(4, 0x0b);
  adcon1.setChannelConfiguration(5, 0x0b);
  adcon1.setChannelConfiguration(6, 0x00);
  adcon1.setChannelConfiguration(7, 0x00);
  adcon1.setChannelConfiguration(8, 0xff);
  adcon1.setChannelConfiguration(9, 0x3f);
  adcon1.setChannelConfiguration(10, 0x3f);
  adcon1.setChannelConfiguration(11, 0x3f);
  adcon1.setChannelConfiguration(12, 0x1f);
  adcon1.setChannelConfiguration(13, 0x0f);
  adcon1.setChannelConfiguration(14, 0x01);
  adcon1.setChannelConfiguration(15, 0x0d);

  adcon1.setVrefHiConfiguration(1, 3);
  adcon1.setVrefHiConfiguration(3, 3);
  adcon1.setVrefHiConfiguration(5, 3);
  adcon1.setVrefHiConfiguration(8, 3);
  adcon1.setVrefHiConfiguration(10, 3);
  adcon1.setVrefHiConfiguration(11, 3);
  adcon1.setVrefHiConfiguration(12, 3);
  adcon1.setVrefHiConfiguration(13, 3);
  adcon1.setVrefHiConfiguration(15, 3);

  adcon1.setVrefLoConfiguration(8, 2);
  adcon1.setVrefLoConfiguration(11, 2);
  adcon1.setVrefLoConfiguration(12, 2);
  adcon1.setVrefLoConfiguration(13, 2);
  adcon1.setVrefLoConfiguration(15, 2);

  adcon1.setNumberOfChannels(5);
  adcon1.setIOPin(0, &(*m_porta)[0]);
  adcon1.setIOPin(1, &(*m_porta)[1]);
  adcon1.setIOPin(2, &(*m_porta)[2]);
  adcon1.setIOPin(3, &(*m_porta)[3]);
  adcon1.setIOPin(4, &(*m_porta)[5]);
  // AN5,AN6 and AN7 exist only on devices with a PORTE.
}

//-------------------------------------------------------------------
//
// 
//    create
//
//  The purpose of this member function is to 'create' those things
// that are unique to the 16-bit core processors.

void _16bit_processor :: create ()
{

  if(verbose)
    cout << " _16bit_processor :: create\n" ;

  fast_stack.init(this);
  ind0.init(this);
  ind1.init(this);
  ind2.init(this);

  pic_processor::create();
  create_sfr_map();

  tmr0l.initialize();

  intcon.set_rcon(&rcon);
  intcon.set_intcon2(&intcon2);
  intcon.set_cpu(this);

  tbl.initialize(this);
  tmr0l.start(0);

  if(pma) {

    pma->SpecialRegisters.push_back(&bsr);
    rma.SpecialRegisters.push_back(&bsr);
  }


}

//
// create_symbols
//
//  Create symbols for a generic 16-bit core. This allows symbolic
// access to the pic. (e.g. It makes it possible to access the 
// status register by name instead of by its address.)
//

void _16bit_processor::create_symbols ()
{
  pic_processor::create_symbols();
}


//-------------------------------------------------------------------
// void _16bit_processor::interrupt ()
//
//  When the virtual function cpu->interrupt() is called during 
// pic_processor::run() AND the cpu gpsim is simulating is an 18cxxx
// device then we end up here. For an interrupt to have occured,
// the interrupt processing logic must have just ran. One of the
// responsibilities of that logic is to determine at what address
// the interrupt should begin executing. That address is placed
// in 'interrupt_vector'.
//
//-------------------------------------------------------------------
void _16bit_processor::
interrupt ()
{
  
  bp.clear_interrupt();

  stack->push(pc->value);

  // Save W,status, and BSR if this is a high priority interrupt.

  fast_stack.push();

  intcon.clear_gies();  // The appropriate gie bits get cleared (not just gieh)

  pc->jump(intcon.get_interrupt_vector());

}
//-------------------------------------------------------------------
void _16bit_processor::por()
{
  pic_processor::por();
}

//-------------------------------------------------------------------
void _16bit_processor::option_new_bits_6_7(unsigned int bits)
{

  //portb.rbpu_intedg_update(bits);
  //cout << "16bit, option bits 6 and/or 7 changed\n";

}


//-------------------------------------------------------------------
// Fetch the rom contents at a particular address.
unsigned int _16bit_processor::get_program_memory_at_address(unsigned int address)
{
  unsigned int uIndex = map_pm_address2index(address);

  
  if (uIndex < program_memory_size())
    return  program_memory[uIndex] ? program_memory[uIndex]->get_opcode() : 0xffffffff;

  if (address >= CONFIG1L && address <= 0x30000D)
    return get_config_word(address);

  static const unsigned int DEVID1 = 0x3ffffe;
  static const unsigned int DEVID2 = 0x3fffff;
  if (address == DEVID1)
    return 0;
  if (address == DEVID2)
    return 0;

  return 0xffffffff;
}

unsigned int _16bit_processor::get_config_word(unsigned int address)
{

  if (!(address >= CONFIG1L && address <= 0x30000D))
    return 0xffffffff;

  address -= CONFIG1L;

  if (m_configMemory) {
    address &= 0xfffe; // Clear LSB
    unsigned int ret = 0xffff;

    if (m_configMemory[address])
      ret = (ret & 0xff00) |  (((unsigned int )(m_configMemory[address]->getVal())) & 0x00ff);

    address++;

    if (m_configMemory[address])
      ret = (ret & 0x00ff) |  ((((unsigned int )(m_configMemory[address]->getVal()))<<8) & 0xff00);

    return ret;
  }
  return 0xffffffff;
}

bool  _16bit_processor::set_config_word(unsigned int address, unsigned int cfg_word)
{
  if (address >= CONFIG1L && address <= 0x30000D) {

    cout << "Setting config word 0x"<<hex<<address<< " = 0x"<<cfg_word<< endl;
    address -= CONFIG1L;

    if (m_configMemory) {

      address &= 0xfffe;

      if(m_configMemory[address])
	m_configMemory[address]->set((int)(cfg_word&0xff));

      address++;

      if(m_configMemory[address])
	m_configMemory[address]->set((int)((cfg_word>>8)&0xff));

      return true;
    }
  }

  return false;
}

//-------------------------------------------------------------------
void _16bit_processor::create_config_memory()
{
  m_configMemory = new ConfigMemory *[configMemorySize()];

  for (unsigned int i=0; i<configMemorySize(); i++)
    m_configMemory[i] = 0;

  m_configMemory[CONFIG1H-CONFIG1L] = new Config1H(this, CONFIG1H);
  m_configMemory[CONFIG2H-CONFIG1L] = new Config2H(this, CONFIG2H);

}

//-------------------------------------------------------------------
//
// Package stuff
//
void _16bit_processor::create_iopin_map()
{
  cout << "_16bit_processor::create_iopin_map WARNING --- not creating package \n";
}



syntax highlighted by Code2HTML, v. 0.9.1