/*
   Copyright (C) 1998-2000 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.  */

#ifndef __PIC_PROCESSORS_H__
#define __PIC_PROCESSORS_H__
#include <glib.h>
#include <stdio.h>

#include "gpsim_classes.h"
#include "modules.h"
#include "processor.h"
#include "program_files.h"

#include "14bit-registers.h"
#include "trigger.h"

class EEPROM;
class instruction;
class Register;
class sfr_register;
class pic_register;
class ConfigMemory;

enum PROCESSOR_TYPE
{
  _PIC_PROCESSOR_,
  _14BIT_PROCESSOR_,
  _12BIT_PROCESSOR_,
  _16BIT_PROCESSOR_,
  _P10F200_,
  _P10F202_,
  _P12C508_,
  _P12C509_,
  _P16C84_,
  _P16CR83_,
  _P16CR84_,
  _P12CE518_,
  _P12CE519_,
  _P16F83_,
  _P16F84_,
  _P16C71_,
  _P16C712_,
  _P16C716_,
  _P16C54_,
  _P16C55_,
  _P16C56_,
  _P16C61_,
  _P16C62_,
  _P16C62A_,
  _P16CR62_,
  _P16F627_,
  _P16F628_,
  _P16F648_,
  _P16C63_,
  _P16C64_,
  _P16C64A_,
  _P16CR64_,
  _P16C65_,
  _P16C65A_,
  _P16C72_,
  _P16C73_,
  _P16C74_,
  _P16F73_,
  _P16F74_,
  _P16F87_,
  _P16F88_,
  _P16F871_,
  _P16F873_,
  _P16F873A_,
  _P16F874_,
  _P16F874A_,
  _P16F876_,
  _P16F876A_,
  _P16F877_,
  _P16F877A_,
  _P17C7xx_,
  _P17C75x_,
  _P17C752_,
  _P17C756_,
  _P17C756A_,
  _P17C762_,
  _P17C766_,
  _P18Cxx2_,
  _P18C2x2_,
  _P18C242_,
  _P18F242_,
  _P18F248_,
  _P18F448_,
  _P18C252_,
  _P18F252_,
  _P18C442_,
  _P18C452_,
  _P18F442_,
  _P18F452_,
  _P18F1220_,
  _P18F1320_
};

// Configuration modes.
//  The configuration mode bits are the config word bits remapped.
//  The remapping removes processor dependent bit definitions.
class ConfigMode {
 public:

  enum {
    CM_FOSC0 = 1<<0,    // FOSC0 and  FOSC1 together define the PIC clock
    CM_FOSC1 = 1<<1,    // All PICs todate have these two bits, but the
                        // ones with internal oscillators use them differently
    CM_WDTE =  1<<2,    // Watch dog timer enable
    CM_CP0 =   1<<3,    // Code Protection
    CM_CP1 =   1<<4,
    CM_PWRTE = 1<<5,    // Power on/Reset timer enable
    CM_BODEN = 1<<6,    // Brown out detection enable
    CM_CPD =   1<<7,
    CM_MCLRE = 1<<8,    // MCLR enable

    CM_FOSC1x = 1<<9,   // Hack for internal oscillators
  };

  int config_mode;
  int valid_bits;
  ConfigMode() { 
    config_mode = 0xffff; 
    valid_bits = CM_FOSC0 | CM_FOSC1 | CM_WDTE;
  };

  virtual void set_config_mode(int new_value) { config_mode = new_value & valid_bits;};
  virtual void set_valid_bits(int new_value) { valid_bits = new_value;};
  void set_fosc0(){config_mode |= CM_FOSC0;};
  void clear_fosc0(){config_mode &= ~CM_FOSC0;};
  bool get_fosc0(){return (config_mode & CM_FOSC0);};
  void set_fosc1(){config_mode |= CM_FOSC1;};
  void clear_fosc1(){config_mode &= ~CM_FOSC1;};
  bool get_fosc1(){return (0 != (config_mode & CM_FOSC1));};
  bool get_fosc1x(){return (0 != (config_mode & CM_FOSC1x));};

  void set_cp0()  {config_mode |= CM_CP0;  valid_bits |= CM_CP0;};
  void clear_cp0(){config_mode &= ~CM_CP0; valid_bits |= CM_CP0;};
  bool get_cp0()  {return (0 != (config_mode & CM_CP0));};
  void set_cp1()  {config_mode |= CM_CP1;  valid_bits |= CM_CP1;};
  void clear_cp1(){config_mode &= ~CM_CP1; valid_bits |= CM_CP1;};
  bool get_cp1()  {return (0 != (config_mode & CM_CP1));};

  void enable_wdt()  {config_mode |= CM_WDTE;};
  void disable_wdt() {config_mode &= ~CM_WDTE;};
  bool get_wdt()     {return (0 != (config_mode & CM_WDTE));};

  void enable_mclre()  {config_mode |= CM_MCLRE;};
  void disable_mclre() {config_mode &= ~CM_MCLRE;};
  bool get_mclre()     {return (0 != (config_mode & CM_MCLRE));};

  void enable_pwrte()   {config_mode |= CM_PWRTE;  valid_bits |= CM_PWRTE;};
  void disable_pwrte()  {config_mode &= ~CM_PWRTE; valid_bits |= CM_PWRTE;};
  bool get_pwrte()      {return (0 != (config_mode & CM_PWRTE));};
  bool is_valid_pwrte() {return (0 != (valid_bits & CM_PWRTE));};

  virtual void print();

};


//---------------------------------------------------------
// Watch Dog Timer
//

class WDT : public TriggerObject
{
public:
  WDT(pic_processor *, double _timeout);
  void put(unsigned int new_value);
  virtual void initialize(bool enable);
  void set_timeout(double);
  virtual void reset();
  void clear();
  virtual void callback();
  virtual void start_sleep();
  virtual void new_prescale();
  virtual void update();
  virtual void callback_print();
  void set_breakpoint(unsigned int bpn);
  bool hasBreak() { return breakpoint != 0;}

protected:
  pic_processor *cpu;           // The cpu to which this wdt belongs.

  unsigned int
    value,
    breakpoint,
    prescale;
  guint64
    future_cycle;

  double timeout;   // When no prescaler is assigned
  bool   wdte;
  bool   warned;


};

/*==================================================================
 * FIXME - move these global references somewhere else
 */
#include "cmd_gpsim.h"
extern guint64 gui_update_rate; // The rate (in simulation cycles) at which the gui is updated

/*==================================================================
 *
 * Here are the base class declarations for the pic processors
 */

/*
 * First, forward-declare a few class references
 */

enum IOPIN_TYPES
{
  INPUT_ONLY,          // e.g. MCLR
  BI_DIRECTIONAL,      // most iopins
  BI_DIRECTIONAL_PU,   // same as bi_directional, but with pullup resistor. e.g. portb
  OPEN_COLLECTOR       // bit4 in porta on the 18 pin midrange devices.
};

/*
 * Define a base class processor for the pic processor family
 *
 * All pic processors are derived from this class.
 */

class pic_processor : public Processor
{
public:



  unsigned int config_word;      // as read from hex or cod file
  ConfigMode   *config_modes;    // processor dependent configuration bits.

  unsigned int pll_factor;       // 2^pll_factor is the speed boost the PLL adds 
                                 // to the instruction execution rate.

  WDT          wdt;

  INDF         *indf;
  FSR          *fsr;
  Stack         *stack;

  Status_register *status;
  WREG          *W;
  OPTION_REG   option_reg;
  PCL          *pcl;
  PCLATH       *pclath;

  TMR0         tmr0;
  int          num_of_gprs;

  EEPROM      *eeprom;       // set to NULL for PIC's that don't have a data EEPROM

  bool LoadProgramFile(const char *pFilename,
		       FILE *pFile,
		       const char *pProcessorName
		       );

  void add_sfr_register(Register *reg, unsigned int addr,
			RegisterValue por_value=RegisterValue(0,0),const char *new_name=0);

  void init_program_memory(unsigned int memory_size);
  void build_program_memory(int *memory,int minaddr, int maxaddr);

  virtual instruction * disasm ( unsigned int address,unsigned int inst)=0;
  virtual void create_config_memory() = 0;
  virtual void tris_instruction(unsigned int tris_register) {return;};
  virtual void create_symbols();
  virtual void create_stack() {stack = new Stack;};
  virtual void run(bool refresh=true);
  virtual void finish();

  void sleep();
  virtual void enter_sleep();
  void step(unsigned int steps,bool refresh=true);
  void step_over(bool refresh=true);

  virtual void step_one(bool refresh=true) {
    program_memory[pc->value]->execute();
  }

  // Take a snap shot of the internal state.
  virtual void save_state();

  virtual void interrupt() { return; };
  void pm_write();

  virtual bool set_config_word(unsigned int address, unsigned int cfg_word);
  virtual unsigned int get_config_word(unsigned int address);
  virtual unsigned int config_word_address() const {return 0x2007;};
  virtual ConfigMode *create_ConfigMode() { return new ConfigMode; };
  virtual void reset(RESET_TYPE r);

  virtual void por();
  virtual void create();

  virtual PROCESSOR_TYPE isa(){return _PIC_PROCESSOR_;};
  virtual PROCESSOR_TYPE base_isa(){return _PIC_PROCESSOR_;};

  /* The program_counter class calls these two functions to get the upper bits of the PC
   * for branching (e.g. goto) or modify PCL instructions (e.g. addwf pcl,f) */
  virtual unsigned int get_pclath_branching_jump()=0;
  virtual unsigned int get_pclath_branching_modpcl()=0;

  virtual void option_new_bits_6_7(unsigned int)=0;

  virtual void set_eeprom(EEPROM *e);
  virtual EEPROM *get_eeprom() { return (eeprom); }

  pic_processor(const char *_name=0, const char *desc=0);
  virtual ~pic_processor();

protected:
  ConfigMemory **m_configMemory;
};


#define cpu_pic ( (pic_processor *)cpu)

//------------------------------------------------------------------------
// Base Class for configuration memory
//
// The configuration memory is only a tiny portion of the overall processor
// program memory space (only 1-word on the mid range devices). So, explicit
// attributes are created for each memory configuration word. Since the meaning
// of configuration memory varies from processor to processor, it is up to 
// each process to derive from this class.

class ConfigMemory : public Integer
{
public:
  ConfigMemory(const char *_name, unsigned int default_val, const char *desc,
	       pic_processor *pCpu, unsigned int addr);
protected:
  pic_processor *m_pCpu;
  unsigned int m_addr;
};

#endif
