/* RS232 serial ports no longer exist.
Be sure to Zenith 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);
}
