/* RS232 serial ports no longer exist.
Be sure to Adam 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 CommHndlr(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
      FifoU8Ins(c->RX_fifo,InU8(c->base+UART_RDR));
    if (stat & 2) {     //TX
      if (FifoU8Rem(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()
{
  CommHndlr(2);
  CommHndlr(4);
  OutU8(0x20,0x20);
}

interrupt U0 IRQComm4()
{
  CommHndlr(1);
  CommHndlr(3);
  OutU8(0x20,0x20);
}

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(0x21,InU8(0x21) & (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
  FifoU8Ins(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 cnt)
{
  while (cnt--)
    CommPutChar(port,*buf++);
}

public U0 CommPrint(I64 port,U8 *fmt,...)
{
  U8 *buf=StrPrintJoin(NULL,fmt,argc,argv);
  CommPutS(port,buf);
  Free(buf);
}