/* RS232 serial ports no longer exist.
Be sure to System Include this by placing
it in your start-up scripts.
*/

#help_index "Comm"

#define UART_THR    0
#define UART_RDR    0
#define UART_BRDL   0
#define UART_IER    1
#define UART_BRDH   1
#define UART_IIR    2
#define UART_LCR    3
#define UART_MCR    4
#define UART_LSR    5
#define UART_MSR    6

#define COMf_ENABLED    0
class CComm
{
    I64      base,
             flags;
    CFifoU8 *RX_fifo;
    CFifoU8 *TX_fifo;

} comm_ports[5];

U0 CommHandler(I64 port)
{
    CComm   *c = &comm_ports[port];
    I64      b = 0, stat;

    if (Bt(&c->flags, COMf_ENABLED))
    {
        stat = InU8(c->base+UART_IIR);
        if (stat & 4)   //RX
            FifoU8Insert(c->RX_fifo, InU8(c->base + UART_RDR));
        if (stat & 2)
        { //TX
            if (FifoU8Remove(c->TX_fifo, &b))
                OutU8(c->base + UART_THR, b);
            else
                OutU8(c->base + UART_IER, 1); //RX but no THR empty
        }
    }
}

interrupt U0 IRQComm3()
{
    CommHandler(2);
    CommHandler(4);
    OutU8(PIC_1, PIC_EOI);
}

interrupt U0 IRQComm4()
{
    CommHandler(1);
    CommHandler(3);
    OutU8(PIC_1, PIC_EOI);
}

U0 CommInit()
{
    MemSet(&comm_ports, 0, sizeof(comm_ports));
    comm_ports[1].base = 0x3F8;
    comm_ports[2].base = 0x2F8;
    comm_ports[3].base = 0x3E8;
    comm_ports[4].base = 0x2E8;
    IntEntrySet(0x23, &IRQComm3);
    IntEntrySet(0x24, &IRQComm4);
}
CommInit;

public CComm *CommInit8n1(I64 port, I64 baud)
{
    CComm *c = &comm_ports[port];

    PUSHFD
    CLI
    if (LBts(&c->flags, COMf_ENABLED))
    {
        FifoU8Del(c->RX_fifo);
        FifoU8Del(c->TX_fifo);
    }
    c->RX_fifo = FifoU8New(256);
    c->TX_fifo = FifoU8New(256);
    OutU8(c->base + UART_LCR, 0);                               //Set for IER
    OutU8(c->base + UART_IER, 0);                               //Disable all IRQ
    OutU8(c->base + UART_LCR, 0x80);                            //Enable baud rate control
    OutU8(c->base + UART_BRDL, (0x180 / (baud / 300)) & 0xFF);  //LSB
    OutU8(c->base + UART_BRDH, (0x180 / (baud / 300)) / 256);   //MSB
    OutU8(c->base + UART_LCR, 3);                               //8-none-1

    InU8(c->base + UART_RDR);                               //read garbage
    InU8(c->base + UART_LSR);

    OutU8(c->base + UART_MCR, 4);
    OutU8(c->base + UART_IER, 0);                           //Disable all IRQ
    OutU8(c->base + UART_MCR, 0xA);                         //out2 and rts
    OutU8(PIC_1_DATA, InU8(PIC_1_DATA) & (0xFF - 0x18));    //Enable 8259 IRQ 3 & 4
    OutU8(c->base + UART_IER, 1);                           //RX but no THR empty
    POPFD

    return c;
}

public U0 CommPutChar(I64 port, U8 b)
{
    CComm *c = &comm_ports[port];

    PUSHFD
    CLI
    FifoU8Insert(c->TX_fifo, b);
    OutU8(c->base + UART_IER, 3);   //RX and THR empty
    POPFD
    Sleep(10); //!!! Remove this line!!!    Linux echo_socket is too slow.
}

U0 CommPutS(I64 port, U8 *st)
{
    I64 b;

    while (b = *st++)
        CommPutChar(port, b);
}

public U0 CommPutBlk(I64 port, U8 *buf, I64 count)
{
    while (count--)
        CommPutChar(port, *buf++);
}

public U0 CommPrint(I64 port, U8 *format, ...)
{
    U8 *buf = StrPrintJoin(NULL, format, argc, argv);

    CommPutS(port, buf);
    Free(buf);
}