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

#include <iostream>
#include <stdio.h>


class InvalidRegister;   // Forward reference


#ifndef __16_BIT_REGISTERS_H__
#define __16_BIT_REGISTERS_H__

#include "pic-processor.h"
#include "14bit-registers.h"
#include "14bit-tmrs.h"
#include "pir.h"
#include "uart.h"
#include "a2dconverter.h"
#include "ssp.h"
#define _16BIT_REGISTER_MASK   0xfff

class _16bit_processor;

class stimulus;  // forward reference
class IOPIN;
class source_stimulus;
class Stimulus_Node;
class PORTB;


//---------------------------------------------------------
// BSR register
//

class BSR : public sfr_register
{
public:

  unsigned int register_page_bits;

  void put(unsigned int new_value);
  void put_value(unsigned int new_value);

};


//---------------------------------------------------------
// 
// Indirect_Addressing
//
// This class coordinates the indirect addressing on the 18cxxx
// parts. Each of the registers comprising the indirect addressing
// subsystem: FSRnL,FSRnH, INDFn, POSTINCn, POSTDECn, PREINCn, and
// PLUSWn are each individually defined as sfr_registers AND included
// in the Indirect_Addressing class. So accessing these registers
// is the same as accessing any register: through the core cpu's
// register memory. The only difference for these registers is that
// the 

class Indirect_Addressing;   // Forward reference

//---------------------------------------------------------
// FSR registers

class FSRL : public sfr_register
{
 public:
  Indirect_Addressing  *iam;  // The IA module to which FSRL belongs

  void put(unsigned int new_value);
  void put_value(unsigned int new_value);
      
};

class FSRH : public sfr_register
{
 public:
  Indirect_Addressing  *iam;  // The IA module to which FSRH belongs

  void put(unsigned int new_value);
  void put_value(unsigned int new_value);
      
};

class INDF16 : public sfr_register
{
 public:
  Indirect_Addressing  *iam;  // The IA module to which INDF belongs

  void put(unsigned int new_value);
  void put_value(unsigned int new_value);
  unsigned int get();
  unsigned int get_value();
      
};

class PREINC : public sfr_register
{
 public:
  Indirect_Addressing  *iam;  // The IA module to which PREINC belongs

  void put(unsigned int new_value);
  void put_value(unsigned int new_value);
  unsigned int get();
  unsigned int get_value();
      
};

class POSTINC : public sfr_register
{
 public:
  Indirect_Addressing  *iam;  // The IA module to which POSTINC belongs

  void put(unsigned int new_value);
  void put_value(unsigned int new_value);
  unsigned int get();
  unsigned int get_value();
      
};

class POSTDEC : public sfr_register
{
 public:
  Indirect_Addressing  *iam;  // The IA module to which POSTDEC belongs

  void put(unsigned int new_value);
  void put_value(unsigned int new_value);
  unsigned int get();
  unsigned int get_value();
      
};

class PLUSW : public sfr_register
{
 public:
  Indirect_Addressing  *iam;  // The IA module to which PLUSW belongs

  void put(unsigned int new_value);
  void put_value(unsigned int new_value);
  unsigned int get();
  unsigned int get_value();
      
};

class Indirect_Addressing
{
public:
  _16bit_processor *cpu;

  unsigned int fsr_value;     // 16bit concatenation of fsrl and fsrh
  unsigned int fsr_state;     /* used in conjunction with the pre/post incr
			       * and decrement. This is mainly needed for
			       * those instructions that perform read-modify-
			       * write operations on the indirect registers
			       * eg. btg POSTINC1,4 . The post increment must
			       * occur after the bit is toggled and not during
			       * the read operation that's determining the 
			       * current state.
			       */
  int     fsr_delta;          /* If there's a pending update to the fsr register
			       * pair, then the magnitude of that update is
			       * stored here.
			       */
  guint64 current_cycle;      /* Stores the cpu cycle when the fsr was last
			       * changed. 
			       */
  FSRL    fsrl;
  FSRH    fsrh;
  INDF16  indf;
  PREINC  preinc;
  POSTINC postinc;
  POSTDEC postdec;
  PLUSW   plusw;

  void init(_16bit_processor *new_cpu);
  void put(unsigned int new_value);
  unsigned int get();
  unsigned int get_value();
  void put_fsr(unsigned int new_fsr);
  unsigned int get_fsr_value(){return (fsr_value & 0xfff);};
  void update_fsr_value();
  void preinc_fsr_value();
  void postinc_fsr_value();
  void postdec_fsr_value();
  int  plusw_fsr_value();

  /* bool is_indirect_register(unsigned int reg_address)
   *
   * The purpose of this routine is to determine whether or not the
   * 'reg_address' is the address of an indirect register. This is
   * used by the 'put' and 'get' functions of the indirect registers.
   * Indirect registers are forbidden access to other indirect registers.
   * (Although double indirection in a single instruction cycle would
   * be powerful!).
   *
   * The indirect registers reside at the following addresses
   * 0xfeb - 0xfef, 0xfe3 - 0xfe7, 0xfdb- 0xfdf
   * If you look at the binary representation of these ranges:
   * 1111 1110 1011, 1111 1110 1100 - 1111 1110 1111    (0xfeb,0xfec - 0xfef)
   * 1111 1110 0011, 1111 1110 0100 - 1111 1110 0111    (0xfe3,0xfe4 - 0xfe7)
   * 1111 1101 1011, 1111 1101 1100 - 1111 1101 1111    (0xfdb,0xfdc - 0xfdf)
   * ------------------------------------------------------------------------
   * 1111 11xx x011, 1111 11vv v1yy - 1111 11vv v1yy
   *
   * Then you'll notice that indirect register addresses share
   * the common bit pattern 1111 11xx x011 for the left column.
   * Furthermore, the middle 3-bits, xxx, can only be 3,4, 5.
   * The ranges in the last two columns share the bit pattern
   * 1111 11vv v1yy. The middle 3-bits, vvv, again can only be 
   * 3,4, or 5. The least two lsbs, yy, are don't cares.
   */

  inline bool is_indirect_register(unsigned int reg_address)
    {
      if( ((reg_address & 0xfc7) == 0xfc3) || ((reg_address & 0xfc4) == 0xfc4))
	{
	  unsigned midbits = (reg_address >> 3) & 0x7;
	  if(midbits >= 3 && midbits <= 5)
	    return 1;
	}
      return 0;
    }


};

//---------------------------------------------------------
class Fast_Stack
{
 public:

  unsigned int w,status,bsr;
  _16bit_processor *cpu;

  void init(_16bit_processor *new_cpu);
  void push();
  void pop();

};

//---------------------------------------------------------
class PCL16 : public PCL
{
public:

  virtual unsigned int get();
  virtual unsigned int get_value();

  PCL16();
};

//---------------------------------------------------------
// Program Counter
//

class Program_Counter16 : public Program_Counter
{
public:
  //virtual void increment();
  //virtual void skip();
  //virtual void jump(unsigned int new_value);
  //virtual void interrupt(unsigned int new_value);
  virtual void computed_goto(unsigned int new_value);
  //virtual void new_address(unsigned int new_value);
  virtual void put_value(unsigned int new_value);
  virtual unsigned int get_value();
  //virtual unsigned int get_next();

  Program_Counter16();
};


//---------------------------------------------------------
// Stack
//
class Stack16;
#define  Stack16_MASK  0x1f

class STKPTR : public sfr_register
{
 public:
  Stack16 *stack;

  void put(unsigned int new_value);
  void put_value(unsigned int new_value);
  unsigned int get();
  unsigned int get_value();
    
};

class TOSL : public sfr_register
{
 public:
  Stack16 *stack;

  void put(unsigned int new_value);
  void put_value(unsigned int new_value);
  unsigned int get();
  unsigned int get_value();
      
};

class TOSH : public sfr_register
{
 public:
  Stack16 *stack;

  void put(unsigned int new_value);
  void put_value(unsigned int new_value);
  unsigned int get();
  unsigned int get_value();
      
};

class TOSU : public sfr_register
{
 public:
  Stack16 *stack;

  void put(unsigned int new_value);
  void put_value(unsigned int new_value);
  unsigned int get();
  unsigned int get_value();
      
};


class Stack16 : public Stack
{
public:
  STKPTR stkptr;
  TOSL   tosl;
  TOSH   tosh;
  TOSU   tosu;

  Stack16();
  virtual void push(unsigned int);
  virtual unsigned int pop();
  virtual void reset();
  virtual unsigned int get_tos();
  virtual void put_tos(unsigned int);

};


class TMR0_16;

//---------------------------------------------------------
class RCON :  public sfr_register
{
public:

enum
{
  BOR  = 1<<0,
  POR  = 1<<1,
  PD   = 1<<2,
  TO   = 1<<3,
  RI   = 1<<4,
  LWRT = 1<<6,
  IPEN = 1<<7
};
};

//---------------------------------------------------------
class CPUSTA :  public sfr_register
{
public:

enum
{
  BOR      = 1<<0,
  POR      = 1<<1,
  PD       = 1<<2,
  TO       = 1<<3,
  GLINTD   = 1<<4,
  STKAV    = 1<<5,
};
};


//---------------------------------------------------------
// T0CON - Timer 0 control register
class T0CON : public OPTION_REG
{
 public:

  enum {
    T08BIT = 1<<6,
    TMR0ON = 1<<7
  };

  T0CON();
  void put(unsigned int new_value);
};

//---------------------------------------------------------
// TMR0 - Timer for the 16bit core.
//
// The 18cxxx extends TMR0 to a 16-bit timer. However, it maintains 
// an 8-bit mode that is compatible with the 8-bit TMR0's in the 
// 14 and 12-bit cores. The 18cxxx TMR0 reuses this code by deriving
// from the TMR0 class and providing definitions for many of the
// virtual functions.

class TMR0H : public sfr_register
{
 public:

  void put(unsigned int new_value);
  void put_value(unsigned int new_value);
  unsigned int get();
  unsigned int get_value();

};

class TMR0_16 : public TMR0
{
public:

  TMR0_16();

  T0CON  *t0con;
  INTCON *intcon;
  TMR0H  *tmr0h;
  unsigned int value16;

  virtual void callback();
  virtual void callback_print();

  virtual void increment();
  virtual unsigned int get();
  virtual unsigned int get_value();
  virtual void put_value(unsigned int new_value);
  virtual unsigned int get_prescale();
  virtual unsigned int max_counts();
  virtual void set_t0if();
  virtual bool get_t0cs();
  virtual void initialize();
  virtual void start(int new_value,int sync=0);
};


//---------------------------------------------------------
class TMR3H : public TMRH
{
public:

};

class TMR3L : public TMRL
{
public:

};

class T3CON : public T1CON
{
public:
enum
{
  T3CCP1 = 1<<3,
  T3CCP2 = 1<<6,
};

  CCPRL *ccpr1l;
  CCPRL *ccpr2l;
  TMRL  *tmr1l; 


  virtual void put(unsigned int new_value);

};

//---------------------------------------------------------
//
// TMR3_MODULE
//
// 

class TMR3_MODULE
{
public:

  _16bit_processor *cpu;
  char * name_str;

  T3CON *t3con;
  PIR_SET  *pir_set;

  TMR3_MODULE();
  void initialize(T3CON *t1con, PIR_SET *pir_set);

};

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

class ADCON0_16 : public ADCON0
{
public:
  ADCON0_16();
  virtual void set_interrupt();

  virtual void setPir(PIR *);
private:
  PIR *m_pPir;
};

//-------------------------------------------------------------------
#if 0

class TABLAT : public sfr_register
{
 public:

  void put(unsigned int new_value);
  void put_value(unsigned int new_value);
  unsigned int get();
  unsigned int get_value();

};

class TABPTRL : public sfr_register
{
 public:

  void put(unsigned int new_value);
  void put_value(unsigned int new_value);
  unsigned int get();
  unsigned int get_value();

};

class TABPTRH : public sfr_register
{
 public:

  void put(unsigned int new_value);
  void put_value(unsigned int new_value);
  unsigned int get();
  unsigned int get_value();

};

class TABPTRU : public sfr_register
{
 public:

  void put(unsigned int new_value);
  void put_value(unsigned int new_value);
  unsigned int get();
  unsigned int get_value();

};

#endif

class TBL_MODULE
{
 public:
  unsigned int state;
  unsigned int internal_latch;

  _16bit_processor *cpu;

#if 0
  TABLAT    tablat;
  TABPTRL   tabptrl;
  TABPTRH   tabptrh;
  TABPTRU   tabptru;
#endif
  sfr_register   tablat,
                 tabptrl,
                 tabptrh,
                 tabptru;

  void increment();
  void decrement();
  void read();
  void write();
  void initialize(_16bit_processor *);

};




//////////////////////////////////////////
//////////////////////////////////////////
//   vapid Place holders
//////////////////////////////////////////
//////////////////////////////////////////


class LVDCON : public  sfr_register
{
 public:
  unsigned int valid_bits;

  enum {
    LVDL0 = 1<<0,
    LVDL1 = 1<<1,
    LVDL2 = 1<<2,
    LVDL3 = 1<<3,
    LVDEN = 1<<4,
    IRVST = 1<<5,
  };

  LVDCON() {
    valid_bits = 0x3f;;
  }
};


#endif
