/*
   Copyright (C) 1998 T. Scott Dattalo
   Copyright (C) 2009,2010,2013 Roy R. Rankin

This file is part of the libgpsim library of gpsim

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library 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
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see
<http://www.gnu.org/licenses/lgpl-2.1.html>.
*/

#ifndef SRC_14_BIT_TMRS_H_
#define SRC_14_BIT_TMRS_H_

#include "registers.h"
#include "ioports.h"
#include "ssp.h"
#include "stimuli.h"
#include "trigger.h"

class TMRL;
class TMR2;
class CCPRL;
class CCPCON;
class PWM1CON;
class ADCON0;
class PIR_SET;
class InterruptSource;
class TMR1_Interface;
class CWG;
class CLC_BASE;
class COG;

class _14bit_processor;

class PIR;
class Processor;
class SSP_MODULE;
class INT_SignalSink;   // I/O pin interface
class CCPSignalSource;  // I/O pin interface
class CCPSignalSink;    // I/O pin interface
class Tristate;		      // I/O pin high impedance


//---------------------------------------------------------
// Todo
//
// The timer base classes need to be abstracted one more
// layer. The 18fxxx parts have a new timer, TMR3, that's
// almost but not quite, identical to the 16fxx's TMR1.


//---------------------------------------------------------
// CCPCON - Capture and Compare registers
//---------------------------------------------------------
class CCPRH : public sfr_register
{
public:
    CCPRH(Processor *pCpu, const char *pName, const char *pDesc = nullptr);

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

    CCPRL *ccprl = nullptr;
    bool  pwm_mode = false;
    unsigned int pwm_value = 0;
};


class CCPRL : public sfr_register
{
public:
    CCPRL(Processor *pCpu, const char *pName, const char *pDesc = nullptr);

    void put(unsigned int new_value);
    void capture_tmr();
    void start_compare_mode(CCPCON *ref = nullptr);
    void stop_compare_mode();
    bool test_compare_mode();
    void start_pwm_mode();
    void stop_pwm_mode();
    void assign_tmr(TMRL *ptmr);

    CCPRH  *ccprh = nullptr;
    CCPCON *ccpcon = nullptr;
    TMRL   *tmrl = nullptr;
};


//
// Enhanced Capture/Compare/PWM Auto-Shutdown control register
//
class ECCPAS : public sfr_register
{
public:
    ECCPAS(Processor *pCpu, const char *pName, const char *pDesc = nullptr);
    ~ECCPAS();

    /* Bit definitions for the register */
    enum
    {
        PSSBD0  = 1 << 0,	// Pins P1B and P1D Shutdown control bits
        PSSBD1  = 1 << 1,
        PSSAC0  = 1 << 2,	// Pins P1A and P1C Shutdown control bits
        PSSAC1  = 1 << 3,
        ECCPAS0 = 1 << 4,	// ECCP Auto-shutdown Source Select bits
        ECCPAS1 = 1 << 5,
        ECCPAS2 = 1 << 6,
        ECCPASE = 1 << 7	// ECCP Auto-Shutdown Event Status bit
    };

    void put(unsigned int new_value);
    void put_value(unsigned int new_value);
    void setBitMask(unsigned int bm) { mValidBits = bm; }
    void setIOpin(PinModule *p0, PinModule *p1, PinModule *p2);
    void c1_output(int value);
    void c2_output(int value);
    void set_trig_state(int index, bool state);
    bool shutdown_trigger(int);
    void link_registers(PWM1CON *_pwm1con, CCPCON *_ccp1con);

private:
    PWM1CON 	*pwm1con = nullptr;
    CCPCON  	*ccp1con = nullptr;
    PinModule 	*m_PinModule = nullptr;
    INT_SignalSink  *m_sink = nullptr;
    bool		trig_state[3];
};


//
// Enhanced PWM control register
//
class PWM1CON : public sfr_register
{
public:
    PWM1CON(Processor *pCpu, const char *pName, const char *pDesc = nullptr);
    ~PWM1CON();

    /* Bit definitions for the register */
    enum
    {
        PDC0    = 1 << 0,	// PWM delay count bits
        PDC1    = 1 << 1,
        PDC2    = 1 << 2,
        PDC3    = 1 << 3,
        PDC4    = 1 << 4,
        PDC5    = 1 << 5,
        PDC6    = 1 << 6,
        PRSEN   = 1 << 7	// PWM Restart Enable bit
    };

    void put(unsigned int new_value);
    void setBitMask(unsigned int bm) { mValidBits = bm; }
};


//
// Enhanced PWM Pulse Steering control register
//
class PSTRCON : public sfr_register
{
public:
    PSTRCON(Processor *pCpu, const char *pName, const char *pDesc = nullptr);
    ~PSTRCON();

    /* Bit definitions for the register */
    enum
    {
        STRA    = 1 << 0,	// Steering enable bit A
        STRB    = 1 << 1,	// Steering enable bit B
        STRC    = 1 << 2,	// Steering enable bit C
        STRD    = 1 << 3,	// Steering enable bit D
        STRSYNC = 1 << 4,	// Steering Sync bit
    };

    void put(unsigned int new_value);
};


//---------------------------------------------------------
// CCPCON - Capture and Compare Control register
//---------------------------------------------------------

class CCPCON : public sfr_register, public TriggerObject, public apfpin
{
public:
    CCPCON(Processor *pCpu, const char *pName, const char *pDesc = nullptr);
    ~CCPCON();

    /* Bit definitions for the register */
    enum
    {
        CCPM0 = 1 << 0,
        CCPM1 = 1 << 1,
        CCPM2 = 1 << 2,
        CCPM3 = 1 << 3,
        CCPY  = 1 << 4,
        CCPX  = 1 << 5,
        P1M0  = 1 << 6,	// CCP1 EPWM Output config bits 16f88x
        P1M1  = 1 << 7
    };

    /* Define the Modes (based on the CCPM bits) */
    enum
    {
        ALL_OFF0 = 0,
        ALL_OFF1 = 1,
        ALL_OFF2 = 2,
        ALL_OFF3 = 3,
        CAP_FALLING_EDGE = 4,
        CAP_RISING_EDGE  = 5,
        CAP_RISING_EDGE4 = 6,
        CAP_RISING_EDGE16 = 7,
        COM_SET_OUT = 8,
        COM_CLEAR_OUT = 9,
        COM_INTERRUPT = 0xa,
        COM_TRIGGER = 0xb,
        PWM0 = 0xc,
        PWM1 = 0xd,
        PWM2 = 0xe,
        PWM3 = 0xf
    };

    enum
    {
        CCP_PIN = 0,
        PxB_PIN,
        PxC_PIN,
        PxD_PIN
    };

    void setBitMask(unsigned int bv) { mValidBits = bv; }
    virtual void new_edge(unsigned int level);
    void compare_match();
    virtual void pwm_match(int new_state);
    void drive_bridge(int level, int new_value);
    void shutdown_bridge(int eccpas);
    virtual void put(unsigned int new_value);
    char getState();
    bool test_compare_mode();
    void callback();
    void releasePins(int);
    void releaseSink();

    void setCrosslinks(CCPRL *, PIR *, unsigned int _mask, TMR2 *, ECCPAS *_eccpas = nullptr);
    void setADCON(ADCON0 *);

    virtual void setIOpin(PinModule *pin, int data);
    void setIOpin(PinModule *p1, PinModule *p2 = nullptr, PinModule *p3 = nullptr, PinModule *p4 = nullptr);
    void setIOPin1(PinModule *p1);
    void setIOPin2(PinModule *p2);
    void setIOPin3(PinModule *p3);
    void setIOPin4(PinModule *p4);
    void set_tmr2(TMR2 *_tmr2) { tmr2 = _tmr2; }
    virtual bool is_pwm() { return value.get() & (PWM0 | PWM1); }
    virtual unsigned int pwm_latch_value()
    {
        return ((value.get() >> 4) & 3) | 4 * ccprl->value.get();
    }
    void set_cwg(CWG *_cwg) { m_cwg = _cwg;}
    void set_cog(COG *_cog) { m_cog = _cog;}
    void set_clc(CLC_BASE *_clc, int i) { m_clc[i] = _clc;}

    PSTRCON *pstrcon = nullptr;
    PWM1CON *pwm1con = nullptr;
    ECCPAS  *eccpas = nullptr;
    char    index;
    COG     *m_cog;
    CWG     *m_cwg;
    CLC_BASE     *m_clc[4];


protected:
    PinModule 	*m_PinModule[4];
    CCPSignalSource *m_source[4];
    bool		source_active[4];
    CCPSignalSink *m_sink = nullptr;
    Tristate	*m_tristate = nullptr;
    bool  	m_bInputEnabled = false;    // Input mode for capture/compare
    bool  	m_bOutputEnabled = false;   // Output mode for PWM
    char  	m_cOutputState;
    int   	edges = 0;
    guint64 	future_cycle;
    bool 		delay_source0 = false, delay_source1 = false;
    bool 		bridge_shutdown = false;

    CCPRL   *ccprl = nullptr;
    PIR     *pir = nullptr;
    TMR2    *tmr2 = nullptr;
    ADCON0  *adcon0 = nullptr;
    unsigned int pir_mask = 0;
};


class PWMxCON : public CCPCON
{
public:
    PWMxCON(Processor *pCpu, const char *pName, const char *pDesc = nullptr, char _index = 1);

    enum
    {
        PWMxEN = 1 << 7,
        PWMxOE = 1 << 6,
        PWMxOUT = 1 << 5,
        PWMxPOL = 1 << 4
    };
    virtual void put(unsigned int new_value);
    virtual bool outPinEnabled() { return value.get() & PWMxOE;}
    virtual void put_value(unsigned int new_value);
    virtual void pwm_match(int level);
    virtual bool is_pwm() { return true; }
    virtual void new_edge(unsigned int /* level */ ) {}
    virtual unsigned int pwm_latch_value()
    {
        return (pwmdch->value.get() << 2) + (pwmdcl->value.get() >> 6);
    }
    void set_pwmdc(sfr_register *_pwmdcl, sfr_register *_pwmdch)
    {
        pwmdcl = _pwmdcl;
        pwmdch = _pwmdch;
    }

    void set_cwg(CWG *_cwg) { m_cwg = _cwg; }
    void set_clc(CLC_BASE *_clc, int i) { m_clc[i] = _clc; }

private:
    sfr_register *pwmdcl = nullptr;
    sfr_register *pwmdch = nullptr;
    CWG		  *m_cwg = nullptr;
    CLC_BASE	  *m_clc[4];
    char	  index;
};

// Output pin enabled if selected in a RxyPPS register
class PWMxCON_PPS : public PWMxCON
{
public:
    PWMxCON_PPS(Processor *pCpu, const char *pName,
                const char *pDesc=0, char _index=1) :
        PWMxCON(pCpu, pName, pDesc, _index)
    {
    }
    virtual bool outPinEnabled() { return m_PinModule[0] != nullptr;}
};


class TRISCCP : public sfr_register
{
public:
    TRISCCP(Processor *pCpu, const char *pName, const char *pDesc = nullptr);

    enum
    {
        TT1CK = 1 << 0,
        TCCP  = 1 << 2
    };
    void put(unsigned int value);

private:
    bool first;
};


class DATACCP : public sfr_register
{
public:
    DATACCP(Processor *pCpu, const char *pName, const char *pDesc = nullptr);

    enum
    {
        TT1CK = 1 << 0,
        DCCP  = 1 << 2
    };
    void put(unsigned int value);

private:
    bool first;
};


class TMR1_Freq_Attribute;


//---------------------------------------------------------
// T1CON - Timer 1 control register

class T1CON : public sfr_register
{
public:
    T1CON(Processor *pCpu, const char *pName, const char *pDesc = nullptr);
    ~T1CON();

    enum
    {
        TMR1ON  = 1 << 0,
        TMR1CS  = 1 << 1,
        T1SYNC  = 1 << 2,
        T1OSCEN = 1 << 3,
        T1CKPS0 = 1 << 4,
        T1CKPS1 = 1 << 5,
        T1RD16  = 1 << 6,
        TMR1GE  = 1 << 6, // TMR1 Gate Enable used if TMR1L::setGatepin() has been called
        T1GINV  = 1 << 7
    };

    TMRL       *tmrl = nullptr;
    TMR1_Freq_Attribute *freq_attribute;
    Processor  *cpu;

    unsigned int get();

    // For (at least) the 18f family, there's a 4X PLL that effects the
    // the relative timing between gpsim's cycle counter (which is equivalent
    // to the cumulative instruction count) and the external oscillator. In
    // all parts, the clock source for the timer is fosc, the external oscillator.
    // However, for the 18f parts, the instructions execute 4 times faster when
    // the PLL is selected.

    virtual unsigned int get_prescale();

    virtual unsigned int get_tmr1cs() { return ((value.get() & TMR1CS) ? 2 : 0); }
    virtual bool get_tmr1on() { return (value.get() & TMR1ON); }
    virtual bool  get_t1oscen() { return (value.get() & T1OSCEN); }
    virtual bool get_tmr1GE() { return (value.get() & TMR1GE); }
    virtual bool get_t1GINV() { return (value.get() & T1GINV); }
    virtual bool get_t1sync() { return (value.get() & T1SYNC); }
    virtual void put(unsigned int new_value);

    double t1osc();
};


class T1GCon_GateSignalSink;
class T1CON_G;

// Timer 1 gate control Register
class T1GCON : public sfr_register, public apfpin
{
public:
    T1GCON(Processor *pCpu, const char *pName, const char *pDesc = nullptr, T1CON_G *t1con_g = nullptr);
    ~T1GCON();

    enum
    {
        T1GSS0	= 1 << 0,		// Gate source select bits
        T1GSS1	= 1 << 1,
        T1GVAL  = 1 << 2,		// Current state bit
        T1GGO	= 1 << 3,		// Gate Single-Pulse Acquisition Status bit
        T1GSPM	= 1 << 4, 	// Gate Single-Pulse Mode bit
        T1GTM	= 1 << 5,		// Gate Toggle Mode bit
        T1GPOL	= 1 << 6,		// Gate Polarity bit
        TMR1GE  = 1 << 7,		// Gate Enable bit
    };


    void put(unsigned int new_value);
    virtual void setIOpin(PinModule *pin, int arg=0) { setGatepin(pin); }
    virtual void setGatepin(PinModule *);
    virtual void PIN_gate(bool);
    virtual void T0_gate(bool);
    virtual void T2_gate(bool);
    virtual void CM1_gate(bool);
    virtual void CM2_gate(bool);
    virtual void new_gate(bool);
    void set_WRmask(unsigned int mask) { write_mask = mask; }
    void set_tmrl(TMRL *_tmrl) { tmrl = _tmrl; }
    virtual void setInterruptSource(InterruptSource * _int) { m_Interrupt = _int; }
    virtual InterruptSource * getInterruptSource() { return m_Interrupt; }
    virtual bool get_tmr1GE() { return (value.get() & TMR1GE); }
    bool get_t1GPOL() { return (value.get() & T1GPOL); }
    virtual bool tmr1_isON();

private:
    T1GCon_GateSignalSink *sink = nullptr;
    unsigned int 	write_mask;
    TMRL		*tmrl = nullptr;
    T1CON_G	*t1con_g;
    InterruptSource *m_Interrupt = nullptr;
    bool 		PIN_gate_state = false;
    bool 		T0_gate_state = false; // can also be Tx = PRx where x=2,4,6
    bool 		CM1_gate_state = false;
    bool 		CM2_gate_state = false;
    bool 		last_t1g_in = false;
    PinModule	*gate_pin = nullptr;
};


//
// T1CON_G	combines T1CON and T1GCON into one virtual register
//
class T1CON_G : public T1CON
{
public:
    T1CON_G(Processor *pCpu, const char *pName, const char *pDesc = nullptr);
    ~T1CON_G();

    enum
    {
        TMR1ON  = 1 << 0,
        T1SYNC  = 1 << 2,
        T1OSCEN = 1 << 3,
        T1CKPS0 = 1 << 4,
        T1CKPS1 = 1 << 5,
        TMR1CS0  = 1 << 6,
        TMR1CS1  = 1 << 7,
    };

    void t1_cap_increment();

    // RRR unsigned int get();

    // For (at least) the 18f family, there's a 4X PLL that effects the
    // the relative timing between gpsim's cycle counter (which is equivalent
    // to the cumulative instruction count) and the external oscillator. In
    // all parts, the clock source for the timer is fosc, the external oscillator.
    // However, for the 18f parts, the instructions execute 4 times faster when
    // the PLL is selected.

    virtual unsigned int get_prescale()
    {
        return ((value.get() & (T1CKPS0 | T1CKPS1)) >> 4);
    }

    virtual unsigned int get_tmr1cs()
    {
        return ((value.get() & (TMR1CS1 | TMR1CS0)) >> 6);
    }
    virtual bool get_tmr1on() { return (value.get() & TMR1ON); }
    virtual bool get_t1oscen() { return (value.get() & T1OSCEN); }
    virtual bool get_t1sync() { return (value.get() & T1SYNC); }
    virtual void put(unsigned int new_value);
    virtual bool get_tmr1GE() { return t1gcon.get_tmr1GE(); }
    virtual bool get_t1GINV() { return true; }

    TMRL  *tmrl = nullptr;
    TMR1_Freq_Attribute *freq_attribute = nullptr;
    T1GCON t1gcon;
};


//---------------------------------------------------------
// TMRL & TMRH - Timer 1
class TMRH : public sfr_register
{
public:
    TMRH(Processor *pCpu, const char *pName, const char *pDesc = nullptr);

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

    TMRL *tmrl = nullptr;
};


class TMR1CapComRef;

class TMRL : public sfr_register, public TriggerObject, public SignalSink, public apfpin
{
public:
    TMRL(Processor *pCpu, const char *pName, const char *pDesc = nullptr);
    ~TMRL();

    TMRH  *tmrh = nullptr;
    T1CON *t1con = nullptr;

    unsigned int
    prescale,
    prescale_counter,
    break_value;
    unsigned int value_16bit = 0;         /* Low and high concatenated */

    double ext_scale;

    TMR1CapComRef * compare_queue = nullptr;

    guint64 synchronized_cycle = 0;
    guint64 future_cycle = 0;
    gint64 last_cycle = 0;  // last_cycle can be negative for small cycle counts

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

    void set_ext_scale();

    virtual void release();

    virtual void put(unsigned int new_value);
    virtual unsigned int get();
    virtual unsigned int get_value();
    virtual unsigned int get_low_and_high();
    virtual void on_or_off(int new_state);
    virtual void current_value();
    virtual void new_clock_source();
    virtual void update();
    virtual void clear_timer();
    virtual void setSinkState(char);
    virtual void setIOpin(PinModule *pin, int arg=0);
    virtual void setGatepin(PinModule *);
    virtual void IO_gate(bool);
    virtual void compare_gate(bool);
    virtual void setInterruptSource(InterruptSource *);
    virtual InterruptSource * getInterruptSource() { return m_Interrupt; }
    virtual void sleep();
    virtual void wake();

    void set_compare_event(unsigned int value, CCPCON *host);
    void clear_compare_event(CCPCON *host);

    void set_T1GSS(bool arg);
    virtual void increment();   // Used when TMR1 is attached to an external clock
    CLC_BASE       *m_clc[4];

private:
    TMR1_Interface *tmr1_interface = nullptr;
    char m_cState;
    bool m_GateState = false;		// Only changes state if setGatepin() has been called
    bool m_compare_GateState;
    bool m_io_GateState;
    bool m_bExtClkEnabled = false;
    bool m_sleeping = false;
    bool m_t1gss;			// T1 gate source
    // true - IO pin controls gate,
    // false - compare controls gate
    InterruptSource *m_Interrupt = nullptr;
};


class PR2 : public sfr_register
{
public:
    PR2(Processor *pCpu, const char *pName, const char *pDesc = nullptr);

    void put(unsigned int new_value);

    TMR2 *tmr2 = nullptr;
};


//---------------------------------------------------------
// T2CON - Timer 2 control register

class T2CON : public sfr_register
{
public:
    T2CON(Processor *pCpu, const char *pName, const char *pDesc = nullptr);

    enum
    {
        T2CKPS0 = 1 << 0,
        T2CKPS1 = 1 << 1,
        TMR2ON  = 1 << 2,
        TOUTPS0 = 1 << 3,
        TOUTPS1 = 1 << 4,
        TOUTPS2 = 1 << 5,
        TOUTPS3 = 1 << 6
    };

    inline unsigned int get_t2ckps0() { return (value.get() & T2CKPS0); }

    inline unsigned int get_t2ckps1() { return (value.get() & T2CKPS1); }

    inline unsigned int get_tmr2on() { return (value.get() & TMR2ON); }

    inline unsigned int get_post_scale()
    {
        return ((value.get() & (TOUTPS0 | TOUTPS1 | TOUTPS2 | TOUTPS3)) >> 3);
    }

    virtual unsigned int get_pre_scale()
    {
        //  ps1:ps0 prescale
        //   0   0     1
        //   0   1     4
        //   1   x     16
        if (value.get() & T2CKPS1)
        {
            return 16;

        }
        else if (value.get() & T2CKPS0)
        {
            return 4;

        }
        else
        {
            return 1;
        }
    }

    void put(unsigned int new_value);

    TMR2 *tmr2 = nullptr;
};


//---------------------------------------------------------
// T2CON_64 - Timer 2 control register with prescale including 64

class T2CON_64 : public T2CON
{
public:
    T2CON_64(Processor *pCpu, const char *pName, const char *pDesc = nullptr)
        : T2CON(pCpu, pName, pDesc)
    {
    }

    virtual unsigned int get_pre_scale()
    {
        //  ps1:ps0 prescale
        //   0   0      1
        //   0   1      4
        //   1   0     16
        //   1   1     64
        switch (value.get() & (T2CKPS1 |  T2CKPS0))
        {
        case 0:
            return 1;
            break;

        case 1:
            return 4;
            break;

        case 2:
            return 16;
            break;

        case 3:
            return 64;
            break;
        }

        return 1;	// just to shutup compiler
    }
};


#define MAX_PWM_CHANS   5
class TMR2_Interface;

//---------------------------------------------------------
// TMR2 - Timer
class TMR2 : public sfr_register, public TriggerObject
{
protected:
    CCPCON * ccp[MAX_PWM_CHANS];

public:
    TMR2(Processor *pCpu, const char *pName, const char *pDesc = nullptr);
    ~TMR2();

    /* Define the way in which the tmr2 callback function may be updated. */
    enum TMR2_UPDATE_TYPES
    {
        TMR2_WRAP        = 1 << 0,	 // wrap TMR2
        TMR2_PR2_UPDATE  = 1 << 1,     // update pr2 match
        TMR2_PWM1_UPDATE = 1 << 2,     // wrt ccp1
        //    TMR2_PWM2_UPDATE = 1<<3,       // wrt ccp2
        // PWM must be last as a variable number of channels follows
        TMR2_ANY_PWM_UPDATE = 0xfc,    // up to six PWM channels
        TMR2_DONTCARE_UPDATE = 0xff    // whatever comes next
    };

    int pwm_mode = 0;
    int update_state;
    int last_update = 0;

    unsigned int prescale;
    unsigned int prescale_counter = 0;
    unsigned int break_value = 0;
    unsigned int duty_cycle[MAX_PWM_CHANS];     /* for ccp channels */
    int 		post_scale = 0;
    guint64	last_cycle = 0;
    guint64	future_cycle = 0;

    PR2  		*pr2 = nullptr;
    PIR_SET 	*pir_set = nullptr;
    T2CON 	*t2con = nullptr;
    SSP_MODULE	*ssp_module[2];
    T1GCON	*m_txgcon = nullptr;
    CLC_BASE      *m_clc[4];

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

    virtual unsigned int max_counts() { return 256; }
    void put(unsigned int new_value);
    unsigned int get();
    void on_or_off(int new_state);
    void start();
    void new_pre_post_scale();
    void new_pr2(unsigned int new_value);
    void current_value();
    void update(int ut = TMR2_DONTCARE_UPDATE);
    void pwm_dc(unsigned int dc, unsigned int ccp_address);
    void stop_pwm(unsigned int ccp_address);
    virtual unsigned int get_value();
    virtual void setInterruptSource(InterruptSource * _int) { m_Interrupt = _int; }
    virtual InterruptSource * getInterruptSource() { return m_Interrupt; }
    char tmr_number;		// should be '2', '4' or '6'
    bool add_ccp(CCPCON * _ccp);
    bool rm_ccp(CCPCON *_ccp);
    InterruptSource *m_Interrupt = nullptr;
    TMR2_Interface *tmr2_interface = nullptr;
};


//---------------------------------------------------------
//
// TMR2_MODULE
//
//

class TMR2_MODULE
{
public:
    TMR2_MODULE();

    void initialize(T2CON *t2con, PR2 *pr2, TMR2  *tmr2);

    _14bit_processor *cpu = nullptr;
    char * name_str = nullptr;
    T2CON *t2con = nullptr;
    PR2   *pr2 = nullptr;
    TMR2  *tmr2 = nullptr;
};


//---------------------------------------------------------
//
// TMR1_MODULE
//
//

class TMR1_MODULE
{
public:
    TMR1_MODULE();

    void initialize(T1CON *t1con, PIR_SET *pir_set);

    _14bit_processor *cpu;
    char * name_str;
    T1CON *t1con;
    PIR_SET  *pir_set;
};


class CCPTMRS14 : public sfr_register
{
public:
    CCPTMRS14(Processor *pCpu, const char *pName, const char *pDesc = nullptr);

    enum
    {
        C1TSEL0 = 1 << 0,   // CCP1 (pwm1)
        C1TSEL1 = 1 << 1,
        C2TSEL0 = 1 << 2,   // CCP2 (pwm2)
        C2TSEL1 = 1 << 3,
        P3TSEL0 = 1 << 4,   // PWM3
        P3TSEL1 = 1 << 5,
        P4TSEL0 = 1 << 6,   // PWM4
        P4TSEL1 = 1 << 7,
    };

    void set_tmr246(TMR2 *_t2, TMR2 *_t4, TMR2 *_t6)
    {
        t2 = _t2;
        t4 = _t4;
        t6 = _t6;
    }
    void set_ccp(CCPCON *_c1, CCPCON *_c2, CCPCON *_c3, CCPCON *_c4)
    {
        ccp[0] = _c1;
        ccp[1] = _c2;
        ccp[2] = _c3;
        ccp[3] = _c4;
    }
    void put(unsigned int value);

    TMR2 *t2 = nullptr;
    TMR2 *t4 = nullptr;
    TMR2 *t6 = nullptr;
    CCPCON *ccp[4];
};


#endif
