
/// SERIAL.C

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>

#include "ioserial.h"
#include "config.h"
#include "pic24.h"
#include "buffer.h"
#include "timers.h"

/// Note: buffer size must be power of 2 for buffer handling code to work
/// full licor data buffer size ~365 bytes
#define RX_BUFF_BYTES    512 //256
#define TX_BUFF_BYTES    128

/// __LOCAL DATA__*************************************************
static  ByteBuffer rxBuff[UART_COUNT]; /// typedefed as volatile
static volatile unsigned char rxBuffData[UART_COUNT][RX_BUFF_BYTES];
static volatile int rxOverFlowFlag[UART_COUNT];
static  ByteBuffer txBuff[UART_COUNT]; /// typedefed as volatile
static volatile unsigned char txBuffData[UART_COUNT][TX_BUFF_BYTES];
static volatile int txOverFlowFlag[UART_COUNT];
/// __PUBLIC DATA__**************************************************
/// scratch space for whatever (used for most console output

char *g_msg_buffer=NULL;
 volatile uint16_t g_rx_flag=0x00;
 volatile uint32_t srval=0;

/// For all 4 UARTS: initialize software buffers,
/// turn on Rx interrupt (Tx off), and set default baud rates

void serInit() {
    int i;
    g_msg_buffer= (char *)palloc(SER_PRINTF_BUFFER_BYTES);
    // initialize software buffers
    for (i = 0; i < UART_COUNT; ++i) {
        // init buffers
        rxBuff[i].data = rxBuffData[i];
        rxBuff[i].head = 0;
        rxBuff[i].tail = 0;
        rxBuff[i].ptr_mask = (RX_BUFF_BYTES - 1);
        txBuff[i].data = txBuffData[i];
        txBuff[i].head = 0;
        txBuff[i].tail = 0;
        txBuff[i].ptr_mask = (TX_BUFF_BYTES - 1);

        // init flags
        rxOverFlowFlag[i] = FALSE;
        txOverFlowFlag[i] = FALSE;
    }

//    //UART1 - Console Serial //HOST SERIAL
//    (IFS0bits.U1RXIF = 0); //U1RX_Clear_Intr_Status_Bit;
//    (IFS0bits.U1TXIF = 0); //U1TX_Clear_Intr_Status_Bit;
//    ConfigIntUART1(UART_RX_INT_EN | UART_RX_INT_PR4 |
//            UART_TX_INT_DIS | UART_TX_INT_PR2);
//    OpenUART1(UART_EN | UART_IDLE_CON | UART_BRGH_FOUR,
//            UART_INT_TX_BUF_EMPTY | UART_TX_ENABLE, BRG4_VAL(9600));
//
//    //UART2 -  Host Serial //LICOR SERIAL
//    U2RX_Clear_Intr_Status_Bit;
//    U2TX_Clear_Intr_Status_Bit;
//    ConfigIntUART2(UART_RX_INT_EN | UART_RX_INT_PR4 |
//            UART_TX_INT_DIS | UART_TX_INT_PR2);
//    OpenUART2(UART_EN | UART_IDLE_STOP | UART_BRGH_FOUR,
//            UART_INT_TX_BUF_EMPTY | UART_TX_ENABLE, BRG4_VAL(9600));
//
//    //UART3 - Licor Serial //SPARE SERIAL
//    U3RX_Clear_Intr_Status_Bit;
//    U3TX_Clear_Intr_Status_Bit;
//    ConfigIntUART3(UART_RX_INT_EN | UART_RX_INT_PR4 |
//            UART_TX_INT_DIS | UART_TX_INT_PR2);
//    OpenUART3(UART_EN | UART_IDLE_STOP | UART_BRGH_FOUR,
//            UART_INT_TX_BUF_EMPTY | UART_TX_ENABLE, BRG4_VAL(9600));

//    //UART4 - Unused //CONSOLE SERIAL
//    U4RX_Clear_Intr_Status_Bit;
//    U4TX_Clear_Intr_Status_Bit;
//    ConfigIntUART4(UART_RX_INT_EN | UART_RX_INT_PR4 |
//            UART_TX_INT_DIS | UART_TX_INT_PR2);
//    OpenUART4(UART_EN | UART_IDLE_CON | UART_BRGH_FOUR,
//            UART_INT_TX_BUF_EMPTY | UART_TX_ENABLE, BRG4_VAL(9600));

}

/// No protection, so it might gargble any ongoing transmission

int serSetBaud(SerPort port, UINT32 baud) {
    UINT16 brgVal;

    if (0 == baud) return (ERR_FAILURE);
    brgVal = BRG4_VAL(baud);

    switch (port) {
        case SER_HOST:
            U1BRG = brgVal;
            break;
        case SER_LICOR:
            U2BRG = brgVal;
            break;
        case SER_CONS:
            U3BRG = brgVal;
            break;
        default:
            return (ERR_FAILURE);
    }
    return (ERR_SUCCESS);
}

/// READ FUNCTIONS

/// Remove and use one byte from serial port's software buffer

byte serGetChar(serial_port_t port)
{
    byte retval=0;
    serGetByte(port,&retval);
    return retval;
}

#include "iodigital.h"
int serGetByte(SerPort port, unsigned char* b) {
    int stat=FALSE;

    // make sure the port is valid
    if (port>=0 && port<UART_COUNT) {
        // try to get a byte from the buffer
        enterCriticalSection(); //stop more bytes from coming in
        stat = bufGet(b, &rxBuff[port]);
        exitCriticalSection();
    }else{
        *b = '\0';
    }

    return stat;
}

/// Use this for checking what the next char in a stream is without
/// messing with it, useful for catching 2byte line-endings

unsigned char serPeek(SerPort port, unsigned char *b) {

    unsigned char retval=FALSE;

    if (port>=0 && port<UART_COUNT) {
        if (serIsAvail(port)){
           *b = rxBuff[port].data[rxBuff[port].tail];
           retval=TRUE;
        }
    }
    return retval;
}

/// Gets all bytes from a port's SW buffer until designated time or
/// number of bytes is reached, null terms whatever is received.

int serGetSTO(SerPort port, char* buffer, int buflen, UINT32 period) {
    Timer tmr;
    int i = 0;
    BYTE in=0;

    tkTmrSet(&tmr, period);
    tkTmrStart(&tmr);

    while (tkTmrUpdate(&tmr)) {
        while (serIsAvail(port) && (buflen - 1 > i)) {
            serGetByte(port, &in);
            if (in == '\r' || in == '\n') {
                buffer[i] = '\0';
                return (i);
            } else {
                buffer[i++] = in;
            }
        }
    }
    buffer[i] = '\0'; //end the string
    return (i);
}

/// Fill @p buffer until the string in @p term is matched or buffer
/// is full. Null terminates string before @p term, if found.

int serGetUntil(SerPort port, const char* ends, char* buffer, int buflen) {
    unsigned char inchar;
    int j = 0;
    int i = 0;

    while (serGetByte(port, &inchar) && buflen > i + 1) {
        if (inchar == ends[j++]) {
            if ('\0' == ends[j]) {
                buffer[i - j] = '\0';
                return (i);
            }
        } else {
            j = 0;
        }
        buffer[i++] = inchar;
    }
    buffer[i] = '\0';

    return (i);
}

/// Fill @p buffer until the string in @p term is matched or buffer
/// is full, or timeout expires. Null terminates string before @p term, if found.

int serGetUntilTO(SerPort port, const char* ends, char* buffer, int buflen, UINT32 tmout_ms, bool keep)
{
     Timer tmr={0};

   unsigned char inchar='\0';
    int j = 0;
    int i = 0;
//    DPRINT("%s -  srch[%s/%d] @dest[%06p] dlen[%d] to[%lu] keep[%c]\r\n",__FUNCTION__,ends,strlen(ends),buffer,buflen,tmout_ms,(keep?'Y':'N'));
//    DPRINT("serGetUntilTO : ");
    tkTmrSet(&tmr, tmout_ms);
    tkTmrStart(&tmr);
    while(tkTmrUpdate(&tmr)){

        while (serGetByte(port, &inchar) && (buflen > (i + 1))) {
 //            DPRINT((inchar >0x20?"%c":"[%02X]"),inchar);
            if (inchar == ends[j++]) {

                if ('\0' == ends[j]) {
                   buffer[i++] = inchar;
                   if(keep){
                       buffer[i]='\0';
                    }else{
                       buffer[i - j] = '\0';
                    }
    // MPRINT(MOD_SER,"\r\n%s - MATCH tmr[%lu/%lu] i[%d]\r\n",__FUNCTION__,tmr.ticksLeft,tmout_ms,i);
                   return (i);
                }
            } else {
                j = 0;
            }
           buffer[i++] = inchar;
        }

        buffer[i] = '\0';
        if(buflen<=(i+1)){
 //         MPRINT(MOD_SER,"\r\n%s - FULL tmr[%lu/%lu] i[%d]\r\n",__FUNCTION__,tmr.ticksLeft,tmout_ms,i);
          break;
        }
    }

  // MPRINT(MOD_SER,"\r\n%s - TMOUT tmr[%lu/%lu] i[%d]\r\n",__FUNCTION__,tmr.ticksLeft,tmout_ms,i);

     return (i);
}

/// Throws away input until  @p targ or @p term string is found or
/// everything has been read from Rx buffer.

int serScanUntil(SerPort port, char* targ, const char* term) {
    unsigned char inchar;
    int i = 0;
    int j = 0;

    while (serGetByte(port, &inchar)) {
        if (inchar == targ[i++]) {
            if ('\0' == targ[i]) {
                return (TRUE);
            }
        } else {
            i = 0;
        }

        if (inchar == term[j++]) {
            if ('\0' == term[j]) {
                return (FALSE);
            }
        } else {
            j = 0;
        }
    }
    return (FALSE);
}

/// Throws away input until @p targ string is found or timeout.
/// Returns true if @p targ is found or false otherwise

int serScanTO(SerPort port, char *targ, UINT32 period) {
    Timer tmr;
    BYTE inC;
    int i = 0;
    int j = 0;
    uint32_t rem=0;
    int k=0;
    size_t len=strlen(targ);
    tkTmrSet(&tmr, period);
    tkTmrStart(&tmr);
    while ( (rem=tkTmrUpdate(&tmr))>0) {
         if (serGetByte(port, &inC)==TRUE) {
            j++;
            if (inC == targ[i++]) {
                if ('\0' == targ[i]) {
                    return (TRUE);
                }
            } else {
                i = 0;
            }
         }else{
             k++;
         }
    }
    MPRINT(MOD_SER,"\r\n%s - TMOUT j/i/k/len/rem/to[%d/%d/%d/%zd/%"FMT_U32"/%"FMT_U32"] targ[%s]\r\n",__FUNCTION__,j,i,k,len,rem,period,targ);
    return (FALSE);
}

/// WRITE FUNCTIONS

/// Put a byte in port's software TX buffer and turn on that
/// TX interrupt, returns FALSE if buffer is full or bad port number

int serPutChar(SerPort port, char b) {
    // make sure the port is valid
    if (port < 0 || port > 3) {
        return (FALSE);
    }
    if (!bufPut((BYTE) b, &txBuff[port])) {
        return (FALSE);
    }
    switch (port) {
        case 0: P24_U1_TX_INT_EN();//EnableIntU1TX;
            break;
        case 1: P24_U2_TX_INT_EN();//EnableIntU2TX;
            break;
        case 2: P24_U3_TX_INT_EN();//EnableIntU3TX;
            break;
//        case 3: EnableIntU4TX;
//            break;
        default:
            return (FALSE);
    }
    return (TRUE);
}



/// Send bytes to port's SW Tx buffer until null is found
/// if the SW Tx buffer fills up this will keep trying while
/// bytes are transmitted and space made available

int serPutString(SerPort port, char* buff) {
    int i = 0;
    // make sure the port is valid
    if (port < 0 || port > 3) {
        return (0);
    }
    while (buff[i] != '\0') {
        if (serPutChar(port, buff[i]))
            ++i;
    }
    return i;
}

/// Send bytes to port's SW Tx buffer until null is found
/// if the SW Tx buffer fills up this will keep trying while
/// bytes are transmitted and space made available

int serPutPrettyString(SerPort port, char* buff, byte *flag) {
    int i = 0;
    byte leflag=0x0;

    // make sure the port is valid
    if (port > 0 && port < 3 && NULL!=flag) {

        leflag=*flag;
        while (buff[i] != '\0') {
            if(leflag!=0){
                while(!serPutChar(port, '\r'))Nop();
                while(!serPutChar(port, '\n'))Nop();
            }
            if(buff[i]=='\r'){
                i++;
                if(leflag==0x2){
                    leflag=0x0;
                }else{
                    leflag=0x1;
                }
            }else if(buff[i]=='\n'){
                i++;
                if(leflag==0x1){
                    leflag=0x0;
                }else{
                    leflag=0x2;
                }
            }else{
                if(serPutChar(port, buff[i]))
                    i++;
                leflag=0x0;
            }

        }
        *flag=leflag;
    }
     return i;
}

/// Write a string to port, uses printf like formating

int serPrintf(SerPort port, const char* format, ...){
    int writ;
    va_list args;
    va_start(args, format);
    writ = vsprintf(g_msg_buffer, format, args);
    serPutString(port, g_msg_buffer);
    va_end(args);
    return(writ);
}

/// Send string to console if "TERSE" is not 0.
/// is blocking to make sure message gets out.

int serDebugS(char* src){
    UINT16 verboseVal;

    if(ERR_FAILURE == config_get(CK_VERBOSE, &verboseVal)){
        serPrintf(SER_CONS, "%s unset (set>0)\r\n",CK_VERBOSE);
        return(ERR_FAILURE);
    }
    if(verboseVal>0){
        verboseVal = serPutString(SER_CONS, src);
        serTxWait(SER_CONS);
        return(verboseVal);
    }
    return(ERR_SUCCESS);
}

int serDebugf(const char* format, ...){
    int writ;
    va_list args;
    UINT16 verboseVal;

    if(ERR_FAILURE == config_get(CK_VERBOSE, &verboseVal)){
        serPrintf(SER_CONS, "%s unset (set>0)\r\n",CK_VERBOSE);
        return(0);
    }
    if(verboseVal <= 0){
        return(0);
    }
    va_start(args, format);
    writ = vsprintf(g_msg_buffer, format, args);
    serPutString(SER_CONS, g_msg_buffer);
    serTxWait(SER_CONS);
    va_end(args);
    return(writ);
}



/// Send one byte directly to port's Tx HW buffer bypassing SW buffer
/// FAILS IF BUFFER IS FULL!

int serSetByte(SerPort port, BYTE b) {

    switch (port) {
        case 0:
            if (!U1STAbits.UTXBF) { //not Tx Buffer Full
                U1TXREG = b; //fill Tx reg
                return (TRUE);
            }
            break;
        case 1:
            if (!U2STAbits.UTXBF) {
                U2TXREG = b;
                return (TRUE);
            }
            break;
        case 2:
            if (!U3STAbits.UTXBF) {
                U3TXREG = b;
                return (TRUE);
            }
            break;
//        case 3:
//            if (!U4STAbits.UTXBF) {
//                U4TXREG = b;
//                return (TRUE);
//            }
            break;
        default:
            break;
    }
    return FALSE;
}



/// CONTROL FUNCTIONS


/// Returns the number of bytes in port's Rx buffer

int serIsAvail(SerPort port) {
    // make sure the port is valid
    if (0 > port || 3 < port) {
        return 0;
    }
    return (bufContains(&rxBuff[port]));
}



/// Returns whether or nor port's Rx buffer is full

int serIsRxFull(SerPort port) { //UNUSED
    int stat;
    // make sure the port is valid
    if (0 > port || 3 < port) {
        return FALSE;
    }
    // get the buffer status
    enterCriticalSection();
    stat = bufIsFull(&rxBuff[port]);
    exitCriticalSection();

    return stat;
}



/// Returns whether or nor port's HW Tx buffer is full

int serIsTxFull(SerPort port) { //used with debugging prints
    // return the hardware FIFO status
    switch (port) {
        case 0:
            return (U1STAbits.UTXBF);
        case 1:
            return (U2STAbits.UTXBF);
        case 2:
            return (U3STAbits.UTXBF);
//        case 3:
//            return (U4STAbits.UTXBF);
        default:
            return (TRUE);
    }
}



/// Has port's overflow flag been set?

int serGetRxOverFlow(SerPort port) { //UNUSED
    if (0 > port || 3 < port) {
        return FALSE;
    }
    return (rxOverFlowFlag[port]);
}



/// Clear port's overflow flag

void serClrRxOverFlow(SerPort port) {
    if (0 > port || 3 < port) {
        return;
    }
    rxOverFlowFlag[port] = FALSE;
}



/// Reset the software buffer and clear the hardware regs

void serRxFlush(SerPort port) {
    unsigned reg; //dummy to clear HW Rx buffer
    // make sure the port is valid
    if (0 > port || 3 < port) {
        return;
    }
    enterCriticalSection();
    // clear out the software FIFO
    bufClear(&rxBuff[port]);
    // clear out the hardware FIFO
    switch (port) {
        case 0: while (U1STAbits.URXDA) reg = U1RXREG;
            break;
        case 1: while (U2STAbits.URXDA) reg = U2RXREG;
            break;
        case 2: while (U3STAbits.URXDA) reg = U3RXREG;
            break;
//        case 3: while (U4STAbits.URXDA) reg = U4RXREG;
//            break;
        default: break;
    }

    exitCriticalSection();

    return;
}



/// Clear out the SW and HW Tx buffers, does not send what's in them

void serTxFlush(SerPort port) {
    if (0 > port || 3 < port) {
        return;
    }
    enterCriticalSection();
    bufClear(&txBuff[port]);
    //UTXEN = Transmit Enable bit
    switch (port) {
        case 0:
            U1STAbits.UTXEN = 0;
            Nop();
            U1STAbits.UTXEN = 1;
            break;
        case 1:
            U2STAbits.UTXEN = 0;
            Nop();
            U2STAbits.UTXEN = 1;
            break;
        case 2:
            U3STAbits.UTXEN = 0;
            Nop();
            U3STAbits.UTXEN = 1;
            break;
//        case 3:
//            U4STAbits.UTXEN = 0;
//            Nop();
//            U4STAbits.UTXEN = 1;
//            break;
        default:
            break;
    }
    exitCriticalSection();
    return;
}



/// Wait until all bytes in port's Tx buffers are written

void serTxWait(SerPort port) { //UNUSED
    //TRMT = Transmit Shift Register is Empty bit
    switch (port) {
        case 0:
            while (!U1STAbits.TRMT) Nop();
            break;
        case 1:
            while (!U2STAbits.TRMT) Nop();
            break;
        case 2:
            while (!U3STAbits.TRMT) Nop();
            break;
//        case 3:
//            while (!U4STAbits.TRMT) Nop();
//            break;
        default:break;
    }
}



/// Interrupt Service Routines

/// note: the functions in buffer.c are not used here
/// as calling an external function from and interrupt
/// service routine increases interrupt overhead

#ifndef USE_MCC_UART_ISR
/// UART1 receive ISR

void __attribute__((interrupt, no_auto_psv)) _U1RXInterrupt(void) {
    while (U1STAbits.URXDA) { //while data available
        if (((rxBuff[0].head + 1) & rxBuff[0].ptr_mask) == rxBuff[0].tail) {
            rxOverFlowFlag[0] = U1RXREG; //read byte to move it out off HW buffer
            rxOverFlowFlag[0] = TRUE; //set overflow flag and bail
        } else { //there is space in SW buffer
            rxBuff[0].data[rxBuff[0].head] = (unsigned char) U1RXREG;
            rxBuff[0].head = (rxBuff[0].head + 1) & rxBuff[0].ptr_mask;
        }
    }

    g_rx_flag|=0x1;

//    U1RX_Clear_Intr_Status_Bit;
    P24_U1_RX_INT_CLR_STAT();
    return;
}


/// UART2 receive ISR

void __attribute__((interrupt, no_auto_psv)) _U2RXInterrupt(void) {
    while (U2STAbits.URXDA) {
        if (((rxBuff[1].head + 1) & rxBuff[1].ptr_mask) == rxBuff[1].tail) {
            rxOverFlowFlag[1] = U2RXREG;
            rxOverFlowFlag[1] = TRUE;
        } else {
            rxBuff[1].data[rxBuff[1].head] = (unsigned char) U2RXREG;
            rxBuff[1].head = (rxBuff[1].head + 1) & rxBuff[1].ptr_mask;
        }
    }
    g_rx_flag|=0x2;
//    U2RX_Clear_Intr_Status_Bit;
    P24_U2_RX_INT_CLR_STAT();
    return;
}


/// UART3 receive ISR

void __attribute__((interrupt, no_auto_psv)) _U3RXInterrupt(void) {
    while (U3STAbits.URXDA) {
        if (((rxBuff[2].head + 1) & rxBuff[2].ptr_mask) == rxBuff[2].tail) {
            rxOverFlowFlag[2] = U3RXREG;
            rxOverFlowFlag[2] = TRUE;
        } else {
            rxBuff[2].data[rxBuff[2].head] = (unsigned char) U3RXREG;
            rxBuff[2].head = (rxBuff[2].head + 1) & rxBuff[2].ptr_mask;
        }
    }
    g_rx_flag|=0x4;

//    U3RX_Clear_Intr_Status_Bit;
    P24_U3_RX_INT_CLR_STAT();
    return;
}


/// UART4 receive ISR

//void __attribute__((interrupt, no_auto_psv)) _U4RXInterrupt(void) {
//    while (U4STAbits.URXDA) {
//        if (((rxBuff[3].head + 1) & rxBuff[3].ptr_mask) == rxBuff[3].tail) {
//            rxOverFlowFlag[3] = U4RXREG;
//            rxOverFlowFlag[3] = TRUE;
//        } else {
//            rxBuff[3].data[rxBuff[3].head] = (unsigned char) U4RXREG;
//            rxBuff[3].head = (rxBuff[3].head + 1) & rxBuff[3].ptr_mask;
//        }
//    }
//    U4RX_Clear_Intr_Status_Bit;
//    return;
//}


/// #warning "stubbing out UART ISRs (using MCC versions)"
/// UART1 transmit ISR

void __attribute__((interrupt, no_auto_psv)) _U1TXInterrupt(void) {
    while (!U1STAbits.UTXBF) { //not HW buffer full
        if (txBuff[0].tail == txBuff[0].head) { //buffer is empty
//            DisableIntU1TX; //turn off transmit, leave flag set for next time
            P24_U1_TX_INT_DI();
            break;
        } else {
//            U1TX_Clear_Intr_Status_Bit; //only clear flag if byte is written
            P24_U1_TX_INT_CLR_STAT();
            U1TXREG = txBuff[0].data[txBuff[0].tail]; //push byte
            txBuff[0].tail = (txBuff[0].tail + 1) & txBuff[0].ptr_mask; //inc. tail
        }
    }
}


/// UART2 transmit ISR

void __attribute__((interrupt, no_auto_psv)) _U2TXInterrupt(void) {
    while (!U2STAbits.UTXBF) { //not HW buffer full
        if (txBuff[1].tail == txBuff[1].head) { //buffer is empty
//            DisableIntU2TX; //turn off transmit
            P24_U2_TX_INT_DI();
            break;
        } else {
//            U2TX_Clear_Intr_Status_Bit;
            P24_U2_TX_INT_CLR_STAT();
            U2TXREG = txBuff[1].data[txBuff[1].tail]; //push byte
            txBuff[1].tail = (txBuff[1].tail + 1) & txBuff[1].ptr_mask; //inc. tail
        }
    }
}


/// UART3 transmit ISR

void __attribute__((interrupt, no_auto_psv)) _U3TXInterrupt(void) {
    while (!U3STAbits.UTXBF) { //not HW buffer full
        if (txBuff[2].tail == txBuff[2].head) { //buffer is empty
//            DisableIntU3TX; //turn off transmit
            P24_U3_TX_INT_DI();
            break;
        } else {
//            U3TX_Clear_Intr_Status_Bit;
            P24_U3_TX_INT_CLR_STAT();
            U3TXREG = txBuff[2].data[txBuff[2].tail]; //push byte
            txBuff[2].tail = (txBuff[2].tail + 1) & txBuff[2].ptr_mask; //inc. tail
        }
    }
}


/// UART4 transmit ISR

//void __attribute__((interrupt, no_auto_psv)) _U4TXInterrupt(void) {
//    while (!U4STAbits.UTXBF) { //not HW buffer full
//        if (txBuff[3].tail == txBuff[3].head) { //buffer is empty
//            DisableIntU4TX; //turn off transmit
//            break;
//        } else {
//            U4TX_Clear_Intr_Status_Bit;
//            U4TXREG = txBuff[3].data[txBuff[3].tail]; //push byte
//            txBuff[3].tail = (txBuff[3].tail + 1) & txBuff[3].ptr_mask; //inc. tail
//        }
//    }
//}


#endif /// USE_MCC_UART_ISR