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

#include "i51usart.h"
#include "usarttx.h"
#include "usartrx.h"
#include "mcutimer.h"
#include "e_mcu.h"
#include "datautils.h"

#define SCON *m_scon

I51Usart::I51Usart( eMcu* mcu, QString name, int number )
        : McuUsart( mcu, name, number )
{
}
I51Usart::~I51Usart(){}

void I51Usart::setup()
{
    setDataBits( 8 );

    m_receiver->setFifoSize( 1 );
    m_stopBits = 1;
    m_parity   = parNONE;

    m_timer1 = m_mcu->getTimer( "TIMER1" );

    m_scon = m_mcu->getReg( "SCON" );

    m_SM     = getRegBits( "SM1,SM0", m_mcu );
    m_bit9Tx = getRegBits( "TB8", m_mcu );
    m_bit9Rx = getRegBits( "RB8", m_mcu );
    m_SM2    = getRegBits( "SM2", m_mcu );

    m_SMOD = getRegBits( "SMOD", m_mcu );
}

void I51Usart::reset()
{
    m_sender->enable( true ); // Sender always enabled
    m_mode = 0xFF;
    m_smodDiv = false;
    m_smodVal = 0;
    m_counter = 0;
}

void I51Usart::configureA( uint8_t newSCON ) //SCON
{
    uint8_t mode = getRegBitsVal( newSCON, m_SM );
    bool     sm2 = getRegBitsBool( newSCON, m_SM2 );

    if( mode == m_mode ){
        if( mode == 2 || mode == 3 ) m_receiver->ignoreData( sm2 );
        return;
    }
    m_mode = mode;

    setSynchronous( mode == 0 );

    bool useTimer = false;

    switch( mode )
    {
        case 0:             // Synchronous 8 bit
            m_uartSync->setPeriod( m_mcu->psInst() );// Fixed baudrate 32 or 64
            m_uartSync->setClkOffset( 200*1e3 );     // 200 ns first clock edge
            sm2 = 0;
            break;
        case 1:             // Asynchronous Timer1 8 bits
            useTimer = true;
            setDataBits( 8 );
            sm2 = 0;
            break;
        case 2:             // Asynchronous MCU Clock 9 bits
            setPeriod( m_mcu->psInst() );// Fixed baudrate 32 or 64
            setDataBits( 9 );
            break;
        case 3:             // Asynchronous Timer1 9 bits
            useTimer = true;
            setDataBits( 9 );
            break;
    }
    m_receiver->ignoreData( sm2 );

    m_timer1->getInterrupt()->callBack( this, useTimer );
    if( useTimer ) setPeriod( 0 );
}

void I51Usart::configureB( uint8_t newPCON )
{
    m_smodVal = getRegBitsVal( newPCON, m_SMOD );
}

void I51Usart::sendByte( uint8_t data )
{
    if( m_mcu->state() == mcuStopped ) return;

    if( m_synchronous ) m_uartSync->sendSyncData( data );
    else                m_sender->processData( data );
}

void I51Usart::callBack()
{
    if( ++m_counter != 16 ) return;

    m_counter = 0;
    if( (m_smodVal & 1) == 0 ) // half speed
    {
        m_smodDiv = !m_smodDiv;
        if( m_smodDiv ) return;
    }
    m_sender->runEvent();
    m_receiver->runEvent();
}

void I51Usart::readByte( uint8_t ) // Reading SBUF
{
    if( m_mcu->isCpuRead() )
    {
        m_stopBitError = false;
        uint8_t data = m_receiver->getData(); // This calls I51Usart::setRxFlags()

        if( m_mode == 1 && m_stopBitError ) return; // Ignore frame
        m_mcu->m_regOverride = data;
    }
}

void I51Usart::setRxFlags( uint16_t frame )
{
    m_stopBitError = (frame & frameError) != 0;

    //writeRegBits( m_FE , frame & frameError );  // frameError
    //writeRegBits( m_DOR, frame & dataOverrun ); // overrun error
    //writeRegBits( m_UPE, frame & parityError ); // parityError
}
