; =============================================================================
;
;                          Litos8 serial COM ports
;
; =============================================================================

; -----------------------------------------------------------------------------
; Exported user functions (11):
;	COMSet - set COM port
;	COMSend - send byte to COM port
;	COMRecv - receive byte from COM port (with waiting)
;	COMRecvNoWait - receive byte from COM port (without waiting)
;	COMRecvTest - test if some byte from COM port is ready
;	COMState - get COM port state
;	COMPort - get address of COM port
;	COMCheck - check COM port
;	COMGetNum - get number of COM ports
;	COMRead - read data from COM port
;	COMWrite - write data to COM port
; -----------------------------------------------------------------------------

		CODEINIT_SECTION

; -----------------------------------------------------------------------------
;                       INIT: Initialize COM ports
; -----------------------------------------------------------------------------

; ------------- initialize COM ports to 9600 Baud/no parity/1 stop/8 bits

COMInit:	mov	bl,COM_MAX-1	; BL <- index of last COM
COMInit2:	mov	al,(7<<5)+(0<<3)+(0<<2)+3 ; 9600 Baud/no par/1 stop/8b
		call	COMSet		; setup COM port
		dec	bl		; BL <- next COM port
		jns	short COMInit2	; initialize next COM port
		ret


		CODE_SECTION

; -----------------------------------------------------------------------------
;                              Set COM port
; -----------------------------------------------------------------------------
; INPUT:	AL = setup parameters
;			bit 0,1: data bits 0=5b, 1=6b, 2=7b, 3=8b
;			bit 2: stop bits 0=1 bit, 1=2 bits (or 1.5 with 5 bits)
;			bit 3,4: parity 0 or 2=none, 1=odd, 3=even
;			bit 5..7: baud rate 0=110 Baud, 1=150 Baud, 2=300 Baud,
;				3=600 Baud, 4=1200 Baud, 5=2400 Baud,
;				6=4800 Baud, 7=9600 Baud
;		BL = COM port index (0..3)
; OUTPUT:	CY = invalid COM port or operation error
; -----------------------------------------------------------------------------

COMSet:		push	ax		; push AX
		mov	ah,0		; AH <- function code
		call	COMInt		; COM port service
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                             Send byte to COM port
; -----------------------------------------------------------------------------
; INPUT:	AL = byte to send
;		BL = COM port index (0..3)
; OUTPUT:	CY = invalid COM port or operation error (time-out)
; -----------------------------------------------------------------------------

COMSend:	push	ax		; push AX
		mov	ah,1		; AH <- function code
		call	COMInt		; COM port service
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                       Receive byte from COM port (without waiting)
; -----------------------------------------------------------------------------
; INPUT:	BL = COM port index (0..3)
; OUTPUT:	AL = byte from COM port (or 0 on error)
;		CY = invalid COM port or operation error (time-out, AL = 0)
; -----------------------------------------------------------------------------

COMRecvNoWait:	call	COMRecvTest	; test if some byte is ready
		mov	al,0		; AL <- 0, invalid byte
		jc	short COMRecv8	; byte is not ready

; === COMRecvWait must follow

; -----------------------------------------------------------------------------
;                       Receive byte from COM port (with waiting)
; -----------------------------------------------------------------------------
; INPUT:	BL = COM port index (0..3)
; OUTPUT:	AL = byte from COM port (or 0 on error)
;		CY = invalid COM port or operation error (time-out, AL = 0)
; -----------------------------------------------------------------------------

COMRecv:	push	bx		; push BX
		push	ax		; push AX
		mov	ah,2		; AH <- function code
		call	COMInt		; COM port service
		cmp	ah,1		; check status
		cmc			; CY = some error
		xchg	ax,bx		; BL <- character
		pop	ax		; pop AX
		mov	al,bl		; AL <- character
		jnc	short COMRecv4	; operation OK
		mov	al,0		; AL <- invalid character
COMRecv4:	pop	bx		; pop BX
COMRecv8:	ret

; -----------------------------------------------------------------------------
;                   Test if some byte from COM port is ready
; -----------------------------------------------------------------------------
; INPUT:	BL = COM port index (0..3)
; OUTPUT:	CY = invalid COM port or byte is not ready
; -----------------------------------------------------------------------------

COMRecvTest:	push	ax		; push AX
		call	COMState	; get COM state
		jc	COMRecvTest6	; error
		and	ax,B8+B5	; data ready status and data set ready
		cmp	ax,B8+B5	; CY <- byte is not ready
COMRecvTest6:	pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                             Get COM port state
; -----------------------------------------------------------------------------
; INPUT:	BL = COM port index (0..3)
; OUTPUT:	AL = modem status
;			bit 0: 1=delta clear to send
;			bit 1: 1=delta data set ready
;			bit 2: 1=trailing edge ring detector
;			bit 3: 1=delta recv line signal detect
;			bit 4: 1=clear to send
;			bit 5: 1=data set ready
;			bit 6: 1=ring indicator
;			bit 7: 1=received line detect signal
;		AH = line status
;			bit 0: 1=data ready status
;			bit 1: 1=overrun error
;			bit 2: 1=parity error
;			bit 3: 1=framing error
;			bit 4: 1=break detect
;			bit 5: 1=trans holding reg empty
;			bit 6: 1=trans shift reg empty
;			bit 7: 1=time-out or general error
;		CY = invalid COM port or operation error
; -----------------------------------------------------------------------------

COMState:	mov	ah,3		; AH <- function code

; === COMInt must follow

; -----------------------------------------------------------------------------
;                         Call COM port interrupt
; -----------------------------------------------------------------------------
; INPUT:	BL = COM port index (0..3)
; OUTPUT:	CY = invalid COM port or operation error
;		AH = line status
;		AL = modem status or received byte
; -----------------------------------------------------------------------------

COMInt:		call	COMCheck	; check COM port
		jc	short COMInt4	; invalid COM port
		push	dx		; push DX
		mov	dl,bl		; DL <- COM port		
		int	14h		; call COM port service
		pop	dx		; pop DX
		rol	ah,1
		ror	ah,1		; return AH, CF <- error flag
COMInt4:	ret

; -----------------------------------------------------------------------------
;                           Get address of COM port
; -----------------------------------------------------------------------------
; INPUT:	BL = COM port index (0..3)
; OUTPUT:	AX = addres of COM port (3f8h=COM1, 2f8h=COM2, 0=no port)
;		CY = invalid COM port (AX = 0)
; -----------------------------------------------------------------------------

COMPort:	xor	ax,ax		; AX <- 0, invalid COM port
		call	COMCheck	; check COM port
		jc	short COMPort4	; invalid COM port
		push	bx		; push BX
		mov	bh,0		; BX <- COM port index
		shl	bx,1		; BX <- offset in address table
		mov	ax,[400h+bx]	; AX <- COM port address
		pop	bx		; pop BX
COMPort4:	ret

; -----------------------------------------------------------------------------
;                              Check COM port
; -----------------------------------------------------------------------------
; INPUT:	BL = COM port index (0..3)
; OUTPUT:	CY = invalid COM port
; -----------------------------------------------------------------------------

COMCheck:	push	bx		; push BX
		cmp	bl,COM_MAX	; check COM index
		cmc			; CY = error
		jc	short COMCheck4	; invalid COM port
		mov	bh,0		; BX <- COM port index
		shl	bx,1		; BX <- offset in address table
		cmp	word [400h+bx],1 ; check if COM port address is valid
COMCheck4:	pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                          Get number of COM ports
; -----------------------------------------------------------------------------
; OUTPUT:	AL = number of COM ports
; -----------------------------------------------------------------------------

COMGetNum:	mov	al,COM_MAX	; AL <- counter of COM ports
		push	bx		; push BX
		mov	bl,COM_MAX-1	; BL <- index of last COM
COMGetNum2:	call	COMCheck	; check COM port
		sbb	al,0		; decrease number of COM ports
		dec	bl		; BL <- next COM port
		jns	short COMGetNum2 ; check next COM port
		pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                            Read data from COM port
; -----------------------------------------------------------------------------
; INPUT:	BL = COM port index (0..3)
;		CX = number of bytes
;		DI = destination buffer
; OUTPUT:	CY = invalid COM port or operation error (CX > 0)
;		CX = remaining bytes (or CX = 0 if no error NC)
; -----------------------------------------------------------------------------

; ------------- push registers

COMRead:	push	ax		; push AX
		push	di		; push DI

; ------------- check zero length

		clc			; clear error flag
		jcxz	COMRead6	; no data

; ------------- read data
	
COMRead2:	call	COMRecv		; receive byte from COM port
		jc	short COMRead6	; error
		stosb			; store byte to buffer
		loop	COMRead2	; receive next byte

; ------------- pop registers

COMRead6:	pop	di		; pop DI
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                           Write data to COM port
; -----------------------------------------------------------------------------
; INPUT:	BL = COM port index (0..3)
;		CX = number of bytes
;		DI = source buffer
; OUTPUT:	CY = invalid COM port or operation error (CX > 0)
;		CX = remaining bytes (or CX = 0 if no error NC)
; -----------------------------------------------------------------------------

; ------------- push registers

COMWrite:	push	ax		; push AX
		push	si		; push SI

; ------------- check zero length

		clc			; clear error flag
		jcxz	COMWrite6	; no data

; ------------- write data

		mov	si,di		; SI <- source buffer	
COMWrite2:	lodsb			; AL <- load byte from buffer
		call	COMSend		; send byte to COM port
		jc	short COMWrite6	; error
		loop	COMWrite2	; send next byte

; ------------- pop registers

COMWrite6:	pop	si		; pop SI
		pop	ax		; pop AX
		ret
