/*
    (C) 2017-2019 Renesas Electronics Corporation.
     This software is released under the terms and conditions described in LICENSE_RENESAS.TXT included in the project.
*/
/**
  * @file    rl78g14_uart.c
  * @author  Renesas Electronics Corporation
  * @brief   Sample UART driver program.
**/

/***********************************************************
 * include
 **********************************************************/
    #include <stdint.h>
    #include <assert.h>
    #include "board.h"
    #include "rl78g14_uartx.h"
    #include "rl78g14_gpio.h"
    



#if defined(R5F104PJ) || defined(R5F104JJ)
#define R5F104xJ
#endif

#if (RP_MCU_UART == RP_MCU_UART_0_1) // #if (RP_MCU_UART == 0)
#if defined(R5F104xJ)
#pragma warning ("only chips >256k memory have UART0_1 redirection!!!")
#endif
#endif


#if defined(RP_MCU_UART) && (RP_MCU_UART == 0)
#ifndef RP_UART0
#define RP_UART0    0
#endif
#endif

#if defined(RP_MCU_UART) && (RP_MCU_UART == RP_MCU_UART_0_1)
#ifndef RP_UART0
#define RP_UART0    RP_MCU_UART_0_1
#endif
#endif

#if defined(RP_MCU_UART) && (RP_MCU_UART == 1)
#ifndef RP_UART1
#define RP_UART1    1
#endif
#endif

#if defined(RP_MCU_UART) && (RP_MCU_UART == 2)
#ifndef RP_UART2
#define RP_UART2    2
#endif
#endif

#if defined(RP_MCU_UART) && (RP_MCU_UART == 3)
#ifndef RP_UART3
#define RP_UART3    3
#endif
#endif

/***********************************************************
 * define
 **********************************************************/
/* Interrupt Level */
#define RP_APL_INTLEVEL_UARTRXs     (0)  // Max Level
#define RP_APL_INTLEVEL_UARTRXs_b0  (RP_APL_INTLEVEL_UARTRXs & 0x01)
#define RP_APL_INTLEVEL_UARTRXs_b1  ((RP_APL_INTLEVEL_UARTRXs & 0x02) >> 1)

#define	PORT_HI				1
#define	PORT_LO				0
#define	PORT_INPUT			1
#define	PORT_OUTPUT			0
#define	PORT_ANALOG			1
#define	PORT_DIGITAL		0

/***********************************************************
 *
 **********************************************************/
#if RP_CPU_CLK == 32
#define UART_RECEIVE_DIVISOR	0x8800
#define UART_TRANSMIT_DIVISOR	0x8800
#elif RP_CPU_CLK == 16
//SDR = 68 for 115200 bps at 16MHz = 0x44 at SDR[16:9] = 0x88 at SDR[16:8]
#define UART_RECEIVE_DIVISOR	0x8800
#define UART_TRANSMIT_DIVISOR	0x8800
#elif RP_CPU_CLK == 8
//SDR = 34 for 115200 bps at  8MHz = 0x22 at SDR[16:9] = 0x44 at SDR[16:8]
#define UART_RECEIVE_DIVISOR	0x4400
#define UART_TRANSMIT_DIVISOR	0x4400
#elif RP_CPU_CLK == 4
#define UART_RECEIVE_DIVISOR	0x2200
#define UART_TRANSMIT_DIVISOR	0x2200
#endif

/***********************************************************
 *
 **********************************************************/

/***********************************************************************
 * program start
 **********************************************************************/

void UartMcuInit( Uart_t *obj, uint8_t uartId, PinNames tx, PinNames rx )
{
    const unsigned default_mode = (SAU_PARITY_NONE  | SAU_STOP_1 | SAU_LENGTH_8);
    RpMcuUartXInit(obj, uartId,BPS115200, default_mode, default_mode);
}

void UartMcuConfig( Uart_t *obj, UartMode_t mode
                    , uint32_t baudrate
                    , WordLength_t wordLength, StopBits_t stopBits, Parity_t parity
                    , FlowCtrl_t flowCtrl )
{
    assert( baudrate <= 115200); //TODO more rates
    assert( wordLength == UART_8_BIT ); //TODO
    assert( mode == RX_TX ); //TODO
    assert( flowCtrl == NO_FLOW_CTRL ); //TODO

    unsigned bps  = 115200 / baudrate;

    unsigned iomode = SAU_LENGTH_8;
    switch(stopBits){
        case UART_1_STOP_BIT : iomode |= SAU_STOP_1;break;
        case UART_2_STOP_BIT : iomode |= SAU_STOP_2;break;
        //UART_0_5_STOP_BIT,
        //UART_1_5_STOP_BIT
        default:
            assert(false);
            iomode |= SAU_STOP_1;
    }
    switch(parity){
        case EVEN_PARITY : iomode |= SAU_PARITY_EVEN;break;
        case ODD_PARITY  : iomode |= SAU_PARITY_ODD;break;
        //case NO_PARITY :
    }

    RpMcuUartXConfig(obj->UartId, bps, iomode, iomode);
}
	

void UartMcuDeInit( Uart_t *obj )
{
    //TODO
}
	


uint8_t UartMcuGetChar( Uart_t *obj, uint8_t *data )
{
    if( IsFifoEmpty( &obj->FifoRx ) == false )
    {
        __DI( );
        *data = FifoPop( &obj->FifoRx );
        __EI( );
        return 0;
    }
    return 1;
}

uint8_t UartMcuGetBuffer( Uart_t *obj, uint8_t *buffer, uint16_t size, uint16_t *nbReadBytes ){
    unsigned need = size - *nbReadBytes;
    if (need <= 0)
        return 0;// OK

    uint8_t *data = buffer + *nbReadBytes;
    __DI( );
    for (;(need > 0); --need)
    {
        if (IsFifoEmpty( &obj->FifoRx ) == false)
            *data++ = FifoPop( &obj->FifoRx );
        else {
            __EI( );
            *nbReadBytes = data - buffer;
            return 1; // Busy
        }
    }
    __EI( );
    *nbReadBytes = data - buffer;
    return 0; // Busy
}

uint8_t UartMcuPutChar( Uart_t *obj, uint8_t data )
{
    return RpMcuUartXPutChar(obj, data);
}


uint8_t UartMcuPutBuffer( Uart_t *obj, uint8_t *data, uint16_t size ){
    return RpMcuUartXPutData(obj, data, size);
}



//======================================================================
/***********************************************************
 *
 **********************************************************/
/* Serial port buffer */
#ifndef RP_MCU_UART_BUFSIZ
#define RP_MCU_UART_BUFSIZ    (64)
#endif

#if defined(RP_MCU_UART)
Uart_t stdio_uart = {
      .IsInitialized = false,
      .IrqNotify     = NULL,
};

#if defined(RP_USE_DEFAULT_UART_RX_HANDLER)
uint8_t RpMcuUartRxBuf[RP_MCU_UART_BUFSIZ];  // UART RX Buffer
#endif
uint8_t RpMcuUartTxBuf[RP_MCU_UART_BUFSIZ];  // UART RX Buffer


void RpMcuUartInit(void){

#if defined(RP_USE_DEFAULT_UART_RX_HANDLER)
    FifoInit(&stdio_uart.FifoRx, RpMcuUartRxBuf, RP_MCU_UART_BUFSIZ);
#else
    FifoInit(&stdio_uart.FifoRx, NULL, 0);
#endif
    FifoInit(&stdio_uart.FifoTx, RpMcuUartTxBuf, RP_MCU_UART_BUFSIZ);

    const unsigned default_node = (SAU_PARITY_NONE  | SAU_STOP_1 | SAU_LENGTH_8);
    RpMcuUartXInit(&stdio_uart, RP_MCU_UART
                    , BPS115200, default_node, default_node);
}

int RpMcuUartPutChar( unsigned char ch ){
    return RpMcuUartXPutChar(&stdio_uart, ch );
}

#else
// app have no stdio
void RpMcuUartInit(void){}
#endif




static void RpMcuUartPinInit(Uart_t *obj);

#if defined(RP_UART0)
Uart_t* rpUART0ctx = NULL;
#endif
#if defined(RP_UART1)
Uart_t* rpUART1ctx = NULL;
#endif
#if defined(RP_UART2)
Uart_t* rpUART2ctx = NULL;
#endif
#if defined(RP_UART3)
Uart_t* rpUART3ctx = NULL;
#endif

int RpMcuUartXInit( Uart_t *obj, uint8_t uartId, unsigned pbs_rate
                    , unsigned rx_mode, unsigned tx_mode )
{
    assert( uartId < 4);
    switch(uartId){
#if defined(RP_UART0)
        case 0:
            rpUART0ctx = obj;
            break;
#endif
#if defined(RP_UART1)
        case 1:
            rpUART1ctx = obj;
            break;
#endif
#if defined(RP_UART2)
        case 2:
            rpUART2ctx = obj;
            break;
#endif
#if defined(RP_UART3)
        case 2:
            rpUART3ctx = obj;
            break;
#endif
        default:
            return RP_UART_NOT_IMPLEMENTED;
    };
    obj->UartId = uartId;
    RpMcuUartPinInit(obj);
    int ok = RpMcuUartXConfig( uartId, pbs_rate, rx_mode, tx_mode);
    if (ok > 0){
        obj->IsInitialized = true;
    }
    return ok;
}



/***********************************************************************
 *	function Name  : RpMcuUartInit
 *	parameters     : none
 *	return value   : none
 *	description    : Initialize UART settings.
 **********************************************************************/
static inline void RpMcuUartX_isr_setup(char uartId);
static inline void RpMcuUartX_RXEnable(char uartId);

#define  RP_SDR0 (&SDR00)
#define  RP_SDR1 (&SDR10)

int RpMcuUartXConfig( uint8_t uartId, unsigned pbs_rate
                    , unsigned rx_mode, unsigned tx_mode )
{
    assert( uartId < 4);
    RP_SAUnit*  SA;
    uint8_t     k = 0;
    switch(uartId){
#if defined(RP_UART0)
        case 0:
            SA = RP_SAU0;
            k  = 0;
            break;
#endif
#if defined(RP_UART1)
        case 1:
            SA = RP_SAU0;
            k  = 2;
            break;
#endif
#if defined(RP_UART2)
        case 2:
            SA = RP_SAU1;
            k  = 0;
            break;
#endif
#if defined(RP_UART3)
        case 2:
            SA = RP_SAU1;
            k  = 2;
            break;
#endif
        default:
            return RP_UART_NOT_IMPLEMENTED;
    };

    bool        issau0 = (SA == RP_SAU0);

	if (issau0)
	    SAU0EN = 1U;    // Enable Serial array unit(UART0=SAU0)
	else
	    SAU1EN = 1U;    // Enable Serial array unit(UART0=SAU0)

	BoardNop();		// Wait for safe
	BoardNop();
	BoardNop();
	BoardNop();

	if (k == 0) {
    // Select clock(prescaler), Use CK00 for UART0
    SA->sps = (SA->sps & (~0x000F)) | (pbs_rate & 0x000F);
        
	/* UART0 initial setting */
    SA->st.r |= SAU_CH1_STOP_TRG_ON | SAU_CH0_STOP_TRG_ON;    // UART2 receive & transmit disable
	}
	else{
	    // Select clock(prescaler), Use CK00 for UART0
	    SA->sps = (SA->sps & (~0x00F0)) | ((pbs_rate<<4)& 0x00F0);

	    /* UART0 initial setting */
	    SA->st.r |= SAU_CH3_STOP_TRG_ON | SAU_CH2_STOP_TRG_ON;    // UART2 receive & transmit disable
	}

	RpMcuUartX_isr_setup(uartId);

	enum{
	    SAU_MODE_Msk = SAU_PARITY_ODD
	                 | SAU_STOP_1 | SAU_STOP_2
	                 | SAU_LENGTH_8
	                ,
	};

    SA->smr[k] = SAU_SMRMN_INITIALVALUE | SAU_CLOCK_SELECT_CK00
                | SAU_TRIGGER_SOFTWARE
                | SAU_MODE_UART | SAU_BUFFER_EMPTY;
    SA->scr[k] = SAU_TRANSMISSION | SAU_INTSRE_MASK | SAU_LSB
                | (tx_mode & SAU_MODE_Msk); //SAU_PARITY_NONE  | SAU_STOP_1 | SAU_LENGTH_8;

    int8_t k1  =k+1;
    NFEN0 |= SAU_RXD0_FILTER_ON << (uartId*2);        // noise filter on(UART0)
    SA->sir[k1].r = SAU_SIRMN_FECTMN | SAU_SIRMN_PECTMN | SAU_SIRMN_OVCTMN;    /* clear error flag */

    SA->smr[k1] = SAU_SMRMN_INITIALVALUE | SAU_CLOCK_SELECT_CK00
                | SAU_TRIGGER_RXD | SAU_EDGE_FALL
                | SAU_MODE_UART | SAU_TRANSFER_END;
    SA->scr[k1] = SAU_RECEPTION | SAU_INTSRE_ENABLE | SAU_LSB
               | (rx_mode & SAU_MODE_Msk); //SAU_PARITY_NONE  | SAU_STOP_1 | SAU_LENGTH_8;

    //SDR regs are twisted by chanels: SDR00,SDR01,SDR12,SDR13
    if (issau0 == (k==0) ) {
        RP_SDR0[k]  = UART_TRANSMIT_DIVISOR;
        RP_SDR0[k1] = UART_RECEIVE_DIVISOR;
    }
    else {
        RP_SDR1[k]  = UART_TRANSMIT_DIVISOR;
        RP_SDR1[k1] = UART_RECEIVE_DIVISOR;
    }

    SA->so  |= SAU_CH0_DATA_OUTPUT_1<< k;
    SA->sol.r &= (~SAU_CHANNEL0_INVERTED) << k; /* output level normal */
    SA->soe.r |= SAU_CH0_OUTPUT_ENABLE<< k;    /* enable UART0 output */

    RpMcuUartX_RXEnable(uartId);

    SA->ss.r |= (SAU_CH1_START_TRG_ON | SAU_CH0_START_TRG_ON)<<k;    /* enable UART0 receive and transmit */

#if defined(RP_USE_DEFAULT_UART_RX_HANDLER)
    if (uartId == RP_UART) {
	/* Init UART TX/RX Buffer pointer */
	RpMcuUartRxBuf_RdPtr = 0x00;
	RpMcuUartRxBuf_WrPtr = 0x00;
    }
#endif

    return RP_UART_OK;
}

static inline void RpMcuUartX_isr_setup( char uartId ){
    switch(uartId){
#if defined(RP_UART0)
        case 0:
            STMK0 = 1U;    /* disable INTST0 interrupt */
            STIF0 = 0U;    /* clear INTST0 interrupt flag */
            SRMK0 = 1U;    /* disable INTSR0 interrupt */
            SRIF0 = 0U;    /* clear INTSR0 interrupt flag */
            SREMK0 = 1U;   /* disable INTSRE0 interrupt */
            SREIF0 = 0U;   /* clear INTSRE0 interrupt flag */

            /* Set INTST0 low priority */
            STPR10 = 1U;
            STPR00 = 1U;
            /* Set INTSR0 priority */
            SRPR10 = RP_APL_INTLEVEL_UARTRXs_b1;
            SRPR00 = RP_APL_INTLEVEL_UARTRXs_b0;
            /* Set INTSRE0 low priority */
            SREPR10 = 1U;
            SREPR00 = 1U;

            break;
#endif
#if defined(RP_UART1)
        case 1:
            STMK1 = 1U;    /* disable INTST0 interrupt */
            STIF1 = 0U;    /* clear INTST0 interrupt flag */
            SRMK1 = 1U;    /* disable INTSR0 interrupt */
            SRIF1 = 0U;    /* clear INTSR0 interrupt flag */
            SREMK1 = 1U;   /* disable INTSRE0 interrupt */
            SREIF1 = 0U;   /* clear INTSRE0 interrupt flag */

            /* Set INTST1 low priority */
            STPR11 = 1U;
            STPR01 = 1U;
            /* Set INTSR1 low priority */
            SRPR11 = RP_APL_INTLEVEL_UARTRXs_b1;
            SRPR01 = RP_APL_INTLEVEL_UARTRXs_b0;
            /* Set INTSRE1 low priority */
            SREPR11 = 1U;
            SREPR01 = 1U;
            break;
#endif
#if defined(RP_UART2)
        case 2:
            STMK2 = 1U;    /* disable INTST0 interrupt */
            STIF2 = 0U;    /* clear INTST0 interrupt flag */
            SRMK2 = 1U;    /* disable INTSR0 interrupt */
            SRIF2 = 0U;    /* clear INTSR0 interrupt flag */
            SREMK2 = 1U;   /* disable INTSRE0 interrupt */
            SREIF2 = 0U;   /* clear INTSRE0 interrupt flag */

            /* Set INTST2 low priority */
            STPR12 = 1U;
            STPR02 = 1U;
            /* Set INTSR2 priority */
            SRPR12 = RP_APL_INTLEVEL_UARTRXs_b1;
            SRPR02 = RP_APL_INTLEVEL_UARTRXs_b0;
            /* Set INTSRE2 low priority */
            SREPR12 = 1U;
            SREPR02 = 1U;
            break;
#endif
#if defined(RP_UART3)
        case 2:
            STMK3 = 1U;    /* disable INTST0 interrupt */
            STIF3 = 0U;    /* clear INTST0 interrupt flag */
            SRMK3 = 1U;    /* disable INTSR0 interrupt */
            SRIF3 = 0U;    /* clear INTSR0 interrupt flag */
            SREMK3 = 1U;   /* disable INTSRE0 interrupt */
            SREIF3 = 0U;   /* clear INTSRE0 interrupt flag */

            /* Set INTST3 low priority */
            STPR13 = 1U;
            STPR03 = 1U;
            /* Set INTSR2 priority */
            SRPR13 = RP_APL_INTLEVEL_UARTRXs_b1;
            SRPR03 = RP_APL_INTLEVEL_UARTRXs_b0;
            /* Set INTSRE2 low priority */
            SREPR13 = 1U;
            SREPR03 = 1U;
            break;
#endif
    };
}

static inline void RpMcuUartX_RXEnable(char uartId){
    switch(uartId){
#if defined(RP_UART0)
        case 0:
            STIF0 = 0U;    /* clear INTST0 interrupt flag */
            SRIF0 = 0U;    /* clear INTSR0 interrupt flag */
            SREIF0 = 0U;   /* clear INTSRE0 interrupt flag */
            SRMK0 = 0U;    /* enable INTSR0 interrupt */
            SREMK0 = 0U;   /* enable INTSRE0 interrupt */
            STMK0 = 0U;    /* enable INTST0 interrupt */
            break;
#endif
#if defined(RP_UART1)
        case 1:
            STIF1 = 0U;    /* clear INTST0 interrupt flag */
            SRIF1 = 0U;    /* clear INTSR0 interrupt flag */
            SREIF1= 0U;   /* clear INTSRE0 interrupt flag */
            SRMK1 = 0U;    /* enable INTSR0 interrupt */
            SREMK1= 0U;   /* enable INTSRE0 interrupt */
            STMK1 = 0U;    /* enable INTST0 interrupt */
            break;
#endif
#if defined(RP_UART2)
        case 2:
            STIF2 = 0U;    /* clear INTST0 interrupt flag */
            SRIF2 = 0U;    /* clear INTSR0 interrupt flag */
            SREIF2= 0U;   /* clear INTSRE0 interrupt flag */
            SRMK2 = 0U;    /* enable INTSR0 interrupt */
            SREMK2= 0U;   /* enable INTSRE0 interrupt */
            STMK2 = 0U;    /* enable INTST0 interrupt */
            break;
#endif
#if defined(RP_UART3)
        case 2:
            STIF3 = 0U;    /* clear INTST0 interrupt flag */
            SRIF3 = 0U;    /* clear INTSR0 interrupt flag */
            SREIF3= 0U;   /* clear INTSRE0 interrupt flag */
            SRMK3 = 0U;    /* enable INTSR0 interrupt */
            SREMK3= 0U;   /* enable INTSRE0 interrupt */
            STMK3 = 0U;    /* enable INTST0 interrupt */
            break;
#endif
    };
}

// no need disable/enable TX irq, yet
#define RpMcuUartX_TXEnable(id)


static
void RpMcuUartPinInit(Uart_t *obj){

    PinNames rxpin;
    PinNames txpin;

    switch(obj->UartId){
#if defined(RP_UART0)
        case 0:
#if (RP_UART0 == RP_MCU_UART_0_1) // #if (RP_MCU_UART == 0)
            rxpin = P1_1;
            txpin = P1_2;
            PIOR0 |= (1<<6);    //onlu chips K, L have this
#else
            if (PIOR0 & (1<<1)){
                rxpin = P1_6;
                txpin = P1_7;
            }
            else{
                rxpin = P5_0;
                txpin = P5_1;
            }
#endif

            break;
#endif
#if defined(RP_UART1)
#   if !defined(R5F104xJ)
#       pragma warning ("only 100pin chips have UART1!!!")
#   endif
        case 1:
            if (PIOR0 & (1<<6)){
                rxpin = P8_1;
                txpin = P8_2;
            }
            else{
                rxpin = P0_3;
                txpin = P0_2;
            }
            break;
#endif
#if defined(RP_UART2)
        case 2:
            if (PIOR0 & (1<<1)){
                rxpin = P7_7;
                txpin = P7_6;
            }
            else{
                rxpin = P1_3;
                txpin = P1_4;
            }
            break;
#endif
#if defined(RP_UART3)
        case 2:
            rxpin = P14_3;
            txpin = P14_4;
            break;
#endif
    };

    RpMcuGpioMcuInit( &obj->Rx , rxpin, PIN_INPUT, PIN_TTL, PIN_PULL_UP, 1);
    RpMcuGpioMcuInit( &obj->Tx , txpin, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 1);
}


#if defined(RP_USE_DEFAULT_UART_RX_HANDLER)
/***********************************************************************
 *	function Name  : RpMcuUartGetChar
 *	parameters     : none
 *	return value   : Received character.
 *	description    : Get character from UART.
 **********************************************************************/
int16_t RpMcuUartGetChar( void )
{
	uint8_t ch;

	if ( RpMcuUartRxBuf_RdPtr == RpMcuUartRxBuf_WrPtr )
	{
		return( EOF );
	}
	else
	{
		ch = RpMcuUartRxBuf[RpMcuUartRxBuf_RdPtr];
		RpMcuUartRxBuf_RdPtr++;
		if ( RpMcuUartRxBuf_RdPtr >= RP_MCU_UART_BUFSIZ )
		{
			RpMcuUartRxBuf_RdPtr = 0;
		}
		return( (int16_t)ch );
	}
}

uint8_t RpMcuUartGetBuffer( uint8_t *buffer, uint16_t size, uint16_t *nbReadBytes ){
    unsigned need = size - *nbReadBytes;

    if (need > 0){
        if (RpMcuUartRxBuf_RdPtr > RpMcuUartRxBuf_WrPtr){
            unsigned have = RP_MCU_UART_BUFSIZ - RpMcuUartRxBuf_RdPtr;
            if (have > need)
                have = need;
            memcpy( buffer + *nbReadBytes, RpMcuUartRxBuf+RpMcuUartRxBuf_RdPtr, have);
            *nbReadBytes += have;
            need         -= have;

            RpMcuUartRxBuf_RdPtr += have;
            if ( RpMcuUartRxBuf_RdPtr >= RP_MCU_UART_BUFSIZ )
                RpMcuUartRxBuf_RdPtr = 0;
            if (need <= 0)
                return 0;
        }

        unsigned have = RpMcuUartRxBuf_WrPtr - RpMcuUartRxBuf_RdPtr;
        if (have > 0){
            if (have > need)
                have = need;
            memcpy( buffer + *nbReadBytes, RpMcuUartRxBuf+RpMcuUartRxBuf_RdPtr, have);
            *nbReadBytes += have;
            need         -= have;

            RpMcuUartRxBuf_RdPtr += have;
            if (need <= 0)
                return 0;
        }

        return 1;   //BUSY
    }
    else
        return 0;
}
#endif

static inline
int RpUartWrite(unsigned char id, uint8_t ch){
    /* Send character immediately */
    switch(id){
#if defined(RP_UART0)
        case 0:
            if ((SSR00 & (SAU_DATA_STORED)) == 0){
                TXD0 = ch;
                return 1;
            }
            break;
#endif
#if defined(RP_UART1)
        case 1:
            if ((SSR02 & (SAU_DATA_STORED)) == 0){
                TXD1 = ch;
                return 1;
            }
            break;
#endif
#if defined(RP_UART2)
        case 2:
            if ((SSR10 & (SAU_DATA_STORED)) == 0){
                TXD2 = ch;
                return 1;
            }
            break;
#endif
#if defined(RP_UART3)
        case 2:
            if ((SSR12 & (SAU_DATA_STORED)) == 0){
                TXD3 = ch;
                return 1;
            }
            break;
#endif
        default:
            return RP_UART_NOT_IMPLEMENTED;
    };
    return 0;
}

/***********************************************************************
 *	function Name  : RpMcuUartPutChar
 *	parameters     : ch : Character to be sent.
 *	return value   : none
 *	description    : Put character to UART.
 **********************************************************************/
int RpMcuUartXPutChar(Uart_t *obj, uint8_t ch )
{
    Fifo_t* dst = &obj->FifoTx;
    if ( IsFifoFull(dst) )
        return 0;

    unsigned char id = obj->UartId;

    if ( IsFifoEmpty(dst) )
    if ( RpUartWrite(id, ch) > 0 ){
        return 1;
    }

    __DI( );
    FifoPush(dst, ch);
    ch = FifoHead(dst);
    if (RpUartWrite(id, ch) > 0)
        FifoPop(dst);
    __EI();

    return 1;
}

int RpMcuUartXPutData(Uart_t *obj, const void* str, unsigned len){
    assert(obj != NULL);
    if ((len <= 0) || (str == NULL))
        return 0;

    Fifo_t* dst = &obj->FifoTx;
    if ( IsFifoFull(dst) )
        return 0;

    uint8_t ch;
    const uint8_t* s = (const uint8_t*)str;
    ch = *s++;
    // startup transfer
    int ok = RpMcuUartXPutChar(obj, ch);
    if (ok <= 0)
        return ok;
    --len;

    __DI( );
    for (;(len > 0) && !IsFifoFull(dst) ; --len){
        FifoPush(dst, *s++);
    }
    //retry transmit
    ch = FifoHead(dst);
    if (RpUartWrite(obj->UartId, ch) > 0)
        FifoPop(dst);
    __EI( );

    return (s - (const uint8_t*)str);
}

static
bool RpMcuUartXBusy(char id)
{
	bool ret;

	RP_SAUnit*  SA = (id < 2)? RP_SAU0 : RP_SAU1;
    uint8_t     k =  (id&1)<1;

	/* Check whether UART Tx is done or not */
    unsigned stat = SA->ssr[k].r;
	if ((stat & (SAU_DATA_STORED | SAU_UNDER_EXECUTE)) == 0)
	{
		ret = true;
	}
	else
	{
		ret = false;
	}

	return (ret);
}

/***********************************************************************
 *  function Name  : RpMcuUartCheckTxDone
 *  parameters     : none
 *  return value   : true: UART Tx done, false: UART Tx udner execution
 *  description    : Check whether UART Tx is done or not
 **********************************************************************/
bool RpMcuUartXCheckTxDone(Uart_t *obj){
    assert( obj->UartId < 4);
    return RpMcuUartXBusy(obj->UartId);
}

#if defined(RP_MCU_UART)
#if (RP_MCU_UART < 4)
bool RpMcuUartCheckTxDone(void){
    return RpMcuUartXBusy(RP_MCU_UART);
}
#elif (RP_MCU_UART == RP_MCU_UART_0_1)
bool RpMcuUartCheckTxDone(){
    return RpMcuUartXBusy(0);
}
#endif
#endif



static inline
int RpUartRead(unsigned char id){
    /* Send character immediately */
    switch(id){
#if defined(RP_UART0)
        case 0: return RXD0;
#endif
#if defined(RP_UART1)
        case 1: return RXD1;
#endif
#if defined(RP_UART2)
        case 2: return RXD2;
#endif
#if defined(RP_UART3)
        case 2: return RXD3;
#endif
        default:
            return RP_UART_NOT_IMPLEMENTED;
    };
    //return 0;
}



//=========================================================================================

/***********************************************************
 * inline asm
 **********************************************************/
#if RTOS_USE && defined(__CCRL__)

#define RDRV_UART_INTHDRSTACK_SIZE  (16)

#define RTOSUartHandlerStack(id)    static uint16_t RTOSUart##id##HandlerStack[RDRV_UART_INTHDRSTACK_SIZE]
#define RTOSUartHandlerStackTop(id) (&RTOSUart##id##HandlerStack[RDRV_UART_INTHDRSTACK_SIZE])

#pragma inline_asm RpMcuIntrSpChange_Asm
void RpMcuIntrSpChange_Asm( uint16_t pstk )    // #pragma inline_asm
{
    movw    bc, sp
    movw    sp, ax  ; move SP
    push    bc      ; store original SP to the buffer
    subw    sp, #0x02
}

#pragma inline_asm RpMcuIntrSpBack_Asm
void RpMcuIntrSpBack_Asm( void )    // #pragma inline_asm
{
    addw    sp, #0x02
    pop     ax
    movw    sp, ax   ; move SP to the original
}

#define isr_entry(id) RpMcuIntrSpChange_Asm( (uint16_t)RTOSUartHandlerStackTop(id) )
#define isr_leave() RpMcuIntrSpBack_Asm()
#else
#define RTOSUartHandlerStack(id)
#define isr_entry(id)
#define isr_leave()
#endif //RTOS_USE && defined(__CCRL__)

#ifdef __CCRL__
#define __ISR __near
#define __ISRH __inline
#else
#define __ISR __interrupt static
#define __ISRH
#endif /* __CCRL__ */




/***********************************************************
 * prototype
 **********************************************************/

static __ISRH void RpMcuUartXRxInterruptHandlerPrc(Uart_t *obj, char id);
static __ISRH void RpMcuUartXTxInterruptHandlerPrc(Uart_t *obj, char id);
static __ISRH void RpMcuUartXRxErrorInterruptHandlerPrc(Uart_t *obj, char id);

/***********************************************************************
 *  function Name  : RpMcuUartRxErrorInterruptHandler
 *  parameters     : none
 *  return value   : none
 *  description    : UART RxErr interrupt handler.
 **********************************************************************/
#define UART_ISRn_RXERROR(n) \
__ISR \
void RpMcuUart##n##RxErrorInterruptHandler( void ) {\
    isr_entry(n);\
    RpMcuUartXRxErrorInterruptHandlerPrc(rpUART##n##ctx, n);\
    isr_leave();\
}

/***********************************************************************
 *  function Name  : RpMcuUartRxInterruptHandler
 *  parameters     : none
 *  return value   : none
 *  description    : UART Rx interrupt handler.
 **********************************************************************/
#define UART_ISRn_RX(n) \
__ISR \
void    RpMcuUart##n##RxInterruptHandler( void ){\
    isr_entry(n);\
    RpMcuUartXRxInterruptHandlerPrc(rpUART##n##ctx, n);\
    isr_leave();\
}

#define UART_ISRn_TX(n) \
__ISR \
void    RpMcuUart##n##TxInterruptHandler( void ){\
    isr_entry(n);\
    RpMcuUartXTxInterruptHandlerPrc(rpUART##n##ctx, n);\
    isr_leave();\
}

/***********************************************************
 * pragma
 **********************************************************/
#if defined(RP_UART0)
/* Stack area for interrupt handles is defined below("unsigned short RpMcuUart0HandlerStack[16]") */
#if defined (__CCRL__)
    #pragma interrupt RpMcuUart0RxInterruptHandler (vect=INTSR0)
    #pragma interrupt RpMcuUart0TxInterruptHandler (vect=INTST0)
    #pragma interrupt RpMcuUart0RxErrorInterruptHandler (vect=INTSRE0)
#elif defined(__ICCRL78__)
    #pragma vector = INTSR0_vect
         __interrupt void RpMcuUart0RxInterruptHandler(void);
    #pragma vector = INTST0_vect
         __interrupt void RpMcuUart0TxInterruptHandler(void);
    #pragma vector = INTSRE0_vect
         __interrupt void RpMcuUart0RxErrorInterruptHandler(void);
#endif // #if defined (__CCRL__)

RTOSUartHandlerStack(0);

UART_ISRn_RXERROR(0);

UART_ISRn_RX(0);

UART_ISRn_TX(0);

#endif//#if defined(RP_UART0)

#if defined(RP_UART1)
/* Stack area for interrupt handles is defined below("unsigned short RpMcuUart0HandlerStack[16]") */
#if defined (__CCRL__)
    #pragma interrupt RpMcuUart1RxInterruptHandler (vect=INTSR1)
    #pragma interrupt RpMcuUart1TxInterruptHandler (vect=INTST1)
    #pragma interrupt RpMcuUart1RxErrorInterruptHandler (vect=INTSRE1)
#elif defined(__ICCRL78__)
    #pragma vector = INTSR1_vect
         __interrupt void RpMcuUart1RxInterruptHandler(void);
    #pragma vector = INTST1_vect
         __interrupt void RpMcuUart1TxInterruptHandler(void);
    #pragma vector = INTSRE1_vect
         __interrupt void RpMcuUart1RxErrorInterruptHandler(void);
#endif // #if defined (__CCRL__)

RTOSUartHandlerStack(1);

UART_ISRn_RXERROR(1);

UART_ISRn_RX(1);

UART_ISRn_TX(1);

#endif

#if defined(RP_UART2)
/* Stack area for interrupt handles is defined below("unsigned short RpMcuUart2HandlerStack[16]") */
#if defined (__CCRL__)
    #pragma interrupt RpMcuUart2RxInterruptHandler (vect=INTSR2)
    #pragma interrupt RpMcuUart2TxInterruptHandler (vect=INTST2)
    #pragma interrupt RpMcuUart2RxErrorInterruptHandler (vect=INTSRE2)
#elif defined(__ICCRL78__)
    #pragma vector = INTSR2_vect
    __interrupt void RpMcuUart2RxInterruptHandler(void);
    #pragma vector = INTST2_vect
         __interrupt void RpMcuUart2TxInterruptHandler(void);
    #pragma vector = INTSRE2_vect
    __interrupt void RpMcuUart2RxErrorInterruptHandler(void);
#endif // #if defined (__CCRL__)

RTOSUartHandlerStack(2);

UART_ISRn_RXERROR(2);

UART_ISRn_RX(2);

UART_ISRn_TX(2);

#endif //defined(RP_UART2)


#if defined(RP_UART3)
/* Stack area for interrupt handles is defined below("unsigned short RpMcuUart2HandlerStack[16]") */
#if defined (__CCRL__)
    #pragma interrupt RpMcuUart3RxInterruptHandler (vect=INTSR3)
    #pragma interrupt RpMcuUart3TxInterruptHandler (vect=INTST3)
    #pragma interrupt RpMcuUart3RxErrorInterruptHandler (vect=INTSRE3)
#elif defined(__ICCRL78__)
    #pragma vector = INTSR3_vect
    __interrupt void RpMcuUart3RxInterruptHandler(void);
    #pragma vector = INTST3_vect
         __interrupt void RpMcuUart3TxInterruptHandler(void);
    #pragma vector = INTSRE3_vect
    __interrupt void RpMcuUart3RxErrorInterruptHandler(void);
#endif // #if defined (__CCRL__)

RTOSUartHandlerStack(3);

UART_ISRn_RXERROR(3);

UART_ISRn_RX(3);

UART_ISRn_TX(3);

#endif //defined(RP_UART2)



//--------------------------------------------------------------------------
#if !defined(RP_USE_DEFAULT_UART_RX_HANDLER)
    extern volatile RpUARTCallback pFuncUartRcvHandler;
#endif /* RP_USE_DEFAULT_UART_RX_HANDLER */

/***********************************************************************
 *  function Name  : RpMcuUartRxInterruptHandlerPrc
 *  parameters     : none
 *  return value   : none
 *  description    : UART Rx interrupt process.
 **********************************************************************/

static __ISRH void RpMcuUartRx_isrh(char ch);

static __ISRH
void RpMcuUartXRxInterruptHandlerPrc( Uart_t *obj, char id )
{
    /* Read character from UART */
    int ch;
    ch = RpUartRead(id);

#if defined(RP_MCU_UART)
    if (RP_MCU_UART == id){
        RpMcuUartRx_isrh(ch);
        return;
    }
#endif

    Fifo_t* fifo = &obj->FifoRx;
    if (fifo->Data != NULL)
        FifoPush(fifo, ch);

    if (obj->IrqNotify)
        (*obj->IrqNotify)(UART_NOTIFY_RX);
}



static __ISRH
void RpMcuUartXTxInterruptHandlerPrc( Uart_t *obj, char id )
{
    /* Read character from UART */
    Fifo_t* fifo = &obj->FifoRx;
    if (fifo->Data != NULL){
        if (!IsFifoEmpty(fifo)){
            int ch;
            ch = FifoHead(fifo);
            if (RpUartWrite(id, ch) > 0)
                FifoPop(fifo);
        }
    }
    if (obj->IrqNotify)
        (*obj->IrqNotify)(UART_NOTIFY_TX);
}


/***********************************************************************
 *  function Name  : RpMcuUartRxErrorInterruptHandlerPrc
 *  parameters     : none
 *  return value   : none
 *  description    : UART RxErr interrupt process.
 **********************************************************************/
static __ISRH
void RpMcuUartXRxErrorInterruptHandlerPrc(Uart_t *obj, char id)
{
    RP_SAUnit*  SA = (obj->UartId < 2)? RP_SAU0 : RP_SAU1;
    uint8_t     k =  (obj->UartId&1)<1;

    unsigned stat = SA->ssr[k+1].r & SAU_ERRORS;

    // read dummy received byte
    unsigned ch = RpUartRead(id) ;

    // clear error flags
    SA->sir[k+1].r = stat;

#if defined(RP_MCU_UART)
    if (RP_MCU_UART == id){
#if !defined(RP_USE_DEFAULT_UART_RX_HANDLER)
        //errors are pass as negative value, high byte - flags
        (*pFuncUartRcvHandler)( ch | (stat << 8) | 0x8000 );
#endif
    }
    return;
#endif

    Fifo_t* fifo = &obj->FifoRx;
    if (fifo->Data != NULL)
        FifoPush(fifo, ch);

    if (obj->IrqNotify)
        (*obj->IrqNotify)(UART_NOTIFY_ERROR | stat | (ch<<8));
}



/***********************************************************
 *
 **********************************************************/
#if defined(RP_USE_DEFAULT_UART_RX_HANDLER)
uint8_t RpMcuUartRxBuf[RP_MCU_UART_BUFSIZ];  // UART RX Buffer
uint16_t RpMcuUartRxBuf_RdPtr;             // UART RX Buffer read pointer
uint16_t RpMcuUartRxBuf_WrPtr;             // UART RX Buffer write pointer
#endif

//-------------------------------------------------------------
#if !defined(RP_USE_DEFAULT_UART_RX_HANDLER)
static __ISRH
void RpMcuUartRx_isrh( char ch )
{
	(*pFuncUartRcvHandler)(ch);
}
#else  // RP_USE_DEFAULT_UART_RX_HANDLER
static __ISRH
void RpMcuUartRx_isrh( char ch )
{
	volatile int16_t len;

	/* Get number of characters in buffer */
	len = (int16_t)RpMcuUartRxBuf_WrPtr - (int16_t)RpMcuUartRxBuf_RdPtr;
	if (len < 0)
	{
		len += RP_MCU_UART_BUFSIZ;
	}

	/* Buffer overflow */
	if (len > RP_MCU_UART_BUFSIZ)
	{
		return;
	}

	/* Update poiner and Store character to buffer */
	RpMcuUartRxBuf[RpMcuUartRxBuf_WrPtr] = ch;

	RpMcuUartRxBuf_WrPtr++;
	if (RpMcuUartRxBuf_WrPtr >= RP_MCU_UART_BUFSIZ)
	{
		RpMcuUartRxBuf_WrPtr = 0;
	}
}
#endif


/******************************************************************************
Function Name:       putchar
Parameters:          Character to be sent.
Return value:        none.
Description:         Put character to UART.
******************************************************************************/
#ifdef __ICCRL78__
int putchar(int data)		// __far_func isn't allowed
#else
int	__far
putchar(int data)
#endif
{

	/* Add carriage return code */
	if (data == '\n')
	{
		RpMcuUartPutChar('\r');
	}

	RpMcuUartPutChar((unsigned char)data);

	return 1;
}

/******************************************************************************
Function Name:       RpMcuPrint
Parameters:          str
                       Pointer to string.
Return value:        none.
Description:         Put strings to UART.
******************************************************************************/
#ifdef __CCRL__
void RpMcuPrint(char __far *str)
#elif __ICCRL78__
void RpMcuPrint(char __far *str)
#else
void RpMcuPrint(char *str)
#endif
{
	/* Loop till end of strings */
	while (*str != '\0')
	{

#if defined(RDRV_UART_NEWLINE_CR_LF)
		if (*str == '\n')
		{
			RpMcuUartPutChar('\r');
		}
#endif

		/* Put character to UART */
		RpMcuUartPutChar((unsigned char)*str++);
	}
}

/******************************************************************************
Function Name:       RpMcuPrintHex
Parameters:          Num
                       Number(HEX).
                     Len
                       Length of strings.
Return value:        none.
Description:         Put numerical strings to UART.
******************************************************************************/
void RpMcuPrintHex(unsigned long Num, unsigned char Len)
{
	unsigned char	tmp;

	/* Loop till end of strings */
	while (Len > 0)
	{
		/* Get digit */
		tmp = (unsigned char)((Num >> ((Len - 1) * 4)) & 0x0F);

		/* numerical -> HEX character and put to UART */
		if (tmp <= 0x09)
		{
			RpMcuUartPutChar('0' + tmp);
		}
		else
		{
			RpMcuUartPutChar('A' + (tmp - 0x0A));
		}

		/* Decrement length */
		Len--;
	}
}


/******************************************************************************
Function Name:       RpMcuPrintDec
Parameters:          DecNum
                       Number(DEC).
                     Len
                       Length of strings.
Return value:        none.
Description:         Put numerical strings to UART.
******************************************************************************/
void RpMcuPrintDec(long DecNum, unsigned char Len, unsigned char SupCh)
{
	long			digit = 1;
	unsigned char	SupressFlg = 1;
	unsigned char	Num;
	unsigned char	i;

	/* minus number? */
	if (DecNum < 0)
	{
		/* Put "-" character */
		RpMcuUartPutChar('-');
		DecNum = -DecNum;
	}

	/* Count digit */
	for (i = 1; i < Len; i++)
	{
		digit *= 10;
	}

	while (digit > 0)
	{
		if (digit == 1)
		{
			/* Suppression off (Lowest digit) */
			SupressFlg = 0;
		}

		/* Get top digit number */
		Num = (unsigned char)((DecNum / digit) % 10);

		if (SupressFlg == 1)
		{
			if (Num == 0)
			{
				if (SupCh != '\0')
				{
					RpMcuUartPutChar(SupCh);
				}
			}
			else
			{
				/* Put a character(top digit number) */
				putc((unsigned char)('0' + Num));

				/* Supression off */
				SupressFlg = 0;
			}
		}
		else
		{
			/* Put a character(top digit number) */
			putc((unsigned char)('0' + Num));
		}

		/* remove top digit number and move a figure one place to the right */
		DecNum = DecNum - (Num * digit);
		digit = digit / 10;
	}
}


/*******************************************************************************
 * Copyright (C) 2017-2019 Renesas Electronics Corporation.
 ******************************************************************************/

