/***************************************************************************
 *   Copyright (C) 2021 by Santiago González                               *
 *                                                                         *
 ***( see copyright.txt file at root folder )*******************************/

#include "picocunit.h"
#include "datautils.h"
#include "mcupin.h"
#include "e_mcu.h"
#include "simulator.h"

PicPwmUnit* PicOcUnit::createPwmUnit( eMcu* mcu, QString name, int type ) // Static
{
    if( type == 00 ) return new PicPwmUnit00( mcu, name );
    if( type == 01 ) return new PicPwmUnit01( mcu, name );
    return nullptr;
}

PicOcUnit::PicOcUnit( eMcu* mcu, QString name )
         : McuOcUnit( mcu, name )
{
    m_enhanced = name.contains("+");
    m_resetTimer = false;
}
PicOcUnit::~PicOcUnit( ){}

void PicOcUnit::setup()
{
    m_GODO = getRegBits("GO/DONE", m_mcu );
}

void PicOcUnit::runEvent()  // Compare match
{
    if( !m_enabled ) return;

    if( m_resetTimer ) // 1 Timer cycle after last match
    {
        m_resetTimer = false;
        m_timer->resetTimer();
        return;
    }
    if( m_specEvent )
    {
         m_resetTimer = true;
         uint64_t cycles = m_timer->psPerTick()*m_mcu->psInst();
         Simulator::self()->addEvent( cycles, this ); // Reset Timer next Timer cycle

         m_mcu->writeReg( m_GODO.regAddr, *m_GODO.reg | m_GODO.mask );  // Set ADC GO/DONE bit
    }
    m_interrupt->raise();   // Trigger interrupt
    drivePin( m_comAct );
}

void PicOcUnit::configure( uint8_t CCPxM )  // CCPxM0,CCPxM1,CCPxM2,CCPxM3
{
    bool ctrlPin = true;
    m_specEvent = false;
    setOcActs( ocNON, ocNON );

    switch( CCPxM ) {
    case 2: setOcActs( ocTOG, ocNON );     break; // Toggle OC Pin - by ci (works in non-enhanced real device)
    case 8: setOcActs( ocSET, ocCLR );     break; // Set OC Pin
    case 9: setOcActs( ocCLR, ocSET );     break; // Clear OC Pin
    case 10: ctrlPin = false;              break; // Only interrupt
    case 11: ctrlPin = false; m_specEvent = true; // Special event
    }
    m_ocPin->controlPin( ctrlPin, false ); // Connect/Disconnect PORT

    if( !m_enabled){
        m_enabled = true;
        m_ocPin->setOutState( false ); // Enabling, start low state
    }
}

void PicOcUnit::ocrWriteL( uint8_t val )
{
    m_comMatch = (m_comMatch & 0xFF00) | val;
    if( m_enabled ) sheduleEvents( m_timer->ovfMatch(), m_timer->getCount() );
}

void PicOcUnit::ocrWriteH( uint8_t val )
{
    m_comMatch = (m_comMatch & 0x00FF) | (uint16_t)val<<8;
    if( m_enabled ) sheduleEvents( m_timer->ovfMatch(), m_timer->getCount() );
}

//------------------------------------------------------
//-- PIC PWM Unit --------------------------------------

PicPwmUnit::PicPwmUnit( eMcu* mcu, QString name )
          : McuOcUnit( mcu, name )
{
    m_enhanced = name.contains("+");
}
PicPwmUnit::~PicPwmUnit( ){}

void PicPwmUnit::runEvent()  // Compare match
{
    if( !m_enabled ) return;

    m_interrupt->raise();   // Trigger interrupt
    drivePin( m_comAct );
}

void PicPwmUnit::configure( uint8_t newCCPxCON )
{
    m_cLow = getRegBitsVal( newCCPxCON, m_DCxB );

    setOcActs( ocCLR, ocSET ); // Clear Out on match, set on TOV

    if( m_enhanced ) // PWM Mode Enhanced
    {
        switch( newCCPxCON & 0x0F ) {
            case 12: // All Pins Active High
            {

            } break;
            case 13: // P1A,C Active High; P1B,D Active Low;
            {

            } break;
            case 14: // P1A,C Active Low; P1B,D Active High;
            {

            } break;
            case 15: // All Pins Active Low
            {

            } break;
    }   }
    m_ocPin->controlPin( true, false ); // Connect/Disconnect PORT
    m_ocPin->setOutState( false );
    m_enabled = true;
}

void  PicPwmUnit::sheduleEvents( uint32_t ovf, uint32_t countVal, int ) // Use CCPRxL + 2 bits from CCPxCON
{
    m_comMatch = (m_CCPRxL<<2) | m_cLow;
    McuOcUnit::sheduleEvents( ovf, countVal, 2 );
}

void PicPwmUnit::ocrWriteL( uint8_t val ) // CCPRxL
{
    m_CCPRxL = val;
    m_comMatch = (m_comMatch & 0xFF00) | val;
    //m_timer->updtCount();
    sheduleEvents( m_timer->ovfMatch(), m_timer->getCount() );
}

//------------------------------------------------------
//-- PIC 16f88x PWM Unit -------------------------------

PicPwmUnit00::PicPwmUnit00( eMcu* mcu, QString name )
            : PicPwmUnit( mcu, name )
{
}
PicPwmUnit00::~PicPwmUnit00( ){}

void PicPwmUnit00::setup()
{
    QString n = m_name.right(1); // name="PWM+2" => n="2"

    m_DCxB  = getRegBits("DC"+n+"B0,DC"+n+"B1", m_mcu );
    if( m_enhanced ) m_PxM = getRegBits("P"+n+"M0,P"+n+"M1", m_mcu );
}

//------------------------------------------------------
//-- PIC 16f627 PWM Unit -------------------------------

PicPwmUnit01::PicPwmUnit01( eMcu* mcu, QString name )
            : PicPwmUnit( mcu, name )
{
}
PicPwmUnit01::~PicPwmUnit01( ){}

void PicPwmUnit01::setup()
{
    QString n = m_name.right(1); // name="PWM+2" => n="2"

    m_DCxB  = getRegBits( "CCP"+n+"Y,CCP"+n+"X", m_mcu );
    //if( m_enhanced ) m_PxM = getRegBits( "P"+n+"M0,P"+n+"M1", mcu );
}
