;****************************************************************************
;* Copyright 1994 MBARI							    *
;****************************************************************************
;* Summary  : Serial I/O routines for OASIS Microcontroller		    *
;* Filename : serial.s                                                      *
;* Author   : Robert Herlien						    *
;* Project  : OASIS Mooring Controller                                      *
;* $Revision: 4.4 $							    *
;* Created  : 11/12/91							    *
;*									    *
;* MBARI provides this documentation and code "as is", with no warranty,    *
;* express or implied, of its quality or consistency. It is provided without*
;* support and without obligation on the part of the Monterey Bay Aquarium  *
;* Research Institute to assist in its use, correction, modification, or    *
;* enhancement. This information should not be published or distributed to  *
;* third parties without specific written permission from MBARI.            *
;*									    *
;****************************************************************************
;
$TITLE("OASIS Serial Functions")
;
SERIAL		MODULE
;
$INCLUDE(..\C196\INCLUDE\8096.INC)
$INCLUDE(OASIS.INC)
$INCLUDE(IO.INC)


;*****************************************************************************
;
EXTRN	bad_int:ENTRY		;Spurious interrupt handler
EXTRN	ring_put:ENTRY		;Put character into ring buffer
EXTRN	ring_get:ENTRY		;Get character from ring buffer
EXTRN	enbl_xmit:ENTRY		;Routine to enable serial Tx


		RSEG			;Register segment

EXTRN	ioctrl:BYTE			;Local copy of oasis_ctrl
EXTRN	tick:WORD			;tick counter
EXTRN	tx_status:NULL			;Tx status for each UART
EXTRN	do_xon:NULL			;For each UART, TRUE if doing XON/XOFF
EXTRN	xoff_timer:NULL			;XOFF timer for each UART


PUBLIC	ser_stat, uarta_stat, uartb_stat

ser_stat:	dsb	1		;Copy of sp_stat register
uarta_stat:	dsb	1		;Copy of uarta_usr
uartb_stat:	dsb	1		;Copy of uartb_usr


;  Debug Variables

;curIntStates:	dsw	1
;maxIntStates:	dsw	1


		DSEG

EXTRN	rx_ring:NULL			;Receive rings for each UART
EXTRN	tx_ring:NULL			;Transmit rings for each UART


;****************************************************************
;
;		Interrupt Vectors
;
;****************************************************************
;
		CSEG AT 2030H

	dcw  xmit_int, rcv_int, bad_int, bad_int
	dcw  bad_int, ext_int, bad_int, bad_int


		CSEG


;*****************************************************************************
; CHK_XON - See if we're doing XON/XOFF, and received char is an XON or XOFF
;	Inputs: tmp2 = received char
;		tmp4 = serial port number
;	Output: plmreg = TRUE if XON/XOFF char
;		         0000 if not
;	Preserves tmp2 & tmp4
;
chk_xon:
	ld	plmreg, R0			;Prepare FALSE return
	add	tmp6, tmp4, #do_xon
	cmpb	R0, [tmp6]			;Check do_xon[port]
	be	xon_ret				;If FALSE, return FALSE
xon_chk1:
	add	tmp6, tmp4, #tx_status		;Point to tx_status[port]
	cmp	tmp2, #XON			;Check for XON/XOFF char
	be	is_xon
	cmp	tmp2, #XOFF
	bne	xon_ret				;If neither, return FALSE
is_xoff:					;XOFF routine
	ldb	tmp0, [tmp6]
	orb	tmp0, #XOFFSTS			;Turn on XOFFSTS
	stb	tmp0, [tmp6]			;   in tx_status[port]
	add	tmp6, tmp4, #xoff_timer
	stb	R0, [tmp6]			;Store zero in xoff_timer[port]
	ldbse	plmreg, #0ffh			;Return TRUE
	ret
is_xon:						;XON routine
	ldb	tmp0, [tmp6]
	andb	tmp0, #(NOT XOFFSTS)		;Turn off XOFFSTS in tx_status
	stb	tmp0, [tmp6]			;   in tx_status[port]
	push	tmp2
	push	tmp4
	lcall	enbl_xmit			;Call enbl_xmit(port)
	pop	tmp4				;Restore port and rcvd char
	pop	tmp2
	ldbse	plmreg, #0ffh			;Return TRUE
xon_ret:
	ret


;*****************************************************************************
; RCV_INT - Receive character from CPU serial port Interrupt Handler
;
;	Void rcv_int( Void )
;
rcv_int:
	pusha				;Save flags
	orb	ser_stat, sp_stat	;Get serial status
	bbc	ser_stat, SER_RI_BIT, rcvi_nochr
	push_cregs			;Macro defined in <oasis.inc>

	andb	ser_stat, #(NOT SER_RI)
	ldbze	tmp2, sbuf		;Get serial character
	ld	tmp4, #CPU_SER		;Indicate CPU serial port
	scall	chk_xon			;Check if XON/XOFF char
	cmp	R0, plmreg		;If so, skip ring put
	bne	rcvi_rtn

	push	tmp2			;Pass rcvd byte
	push	#(rx_ring + (CPU_SER * RING_STRUC_SIZE))
	lcall	ring_put		;Put character in rcv ring
	add	SP, #4

rcvi_rtn:
	pop_cregs			;Macro defined in <oasis.inc>
rcvi_nochr:
	popa				;Restore flags
	ret


;*****************************************************************************
; XMIT_INT - Transmit ready on CPU serial port Interrupt Handler
;
;	Void xmit_int( Void )
;
xmit_int:
	pusha				;Save flags
	push_cregs			;Macro defined in <oasis.inc>

	cmpb	R0, tx_status + CPU_SER	;If xmitter off or XOFF on, return
	bne	xmiti_rtn

	orb	ser_stat, sp_stat	;Get serial status
	bbc	ser_stat, SER_TI_BIT, xmiti_rtn	;If not ready, return

	push	#(tx_ring + (CPU_SER * RING_STRUC_SIZE))
	lcall	ring_get		;Get char from ring buffer
	add	SP, #2
	cmp	plmreg, #0ffffh		;Check for no more characters
	be	xmiti_off		;If no more, turn off xmitter
	stb	plmreg, sbuf
	andb	ser_stat, #(NOT (SER_TI OR SER_TXE))

xmiti_rtn:
	pop_cregs			;Macro defined in <oasis.inc>
	popa				;Restore flags
	ret

xmiti_off:
	orb	tx_status + CPU_SER, #XMITOFF
	br	xmiti_rtn


;*****************************************************************************
; EXT_INT - External Interrupt Handler - 82C52 UART Interrupt
;
;	Void ext_int( Void )
;
; Comment --  Because the CPU will only respond to an edge on the EXTINT pin,
;   and because we're servicing 4 different sources of interrupts here
;   (2 Tx, 2 Rx), we have to loop while the EXTINT pin is true.  This is
;   because a 2nd interrupt can come in while we're servicing the first.
;   In this case, we would clear the first interrupt source, but the EXTINT
;   pin would remain true and the CPU would never see a second positive edge.
;   This has been the source of endless debugging headaches.

ext_int:
	pusha				;Save flags
	push_cregs			;Save all C registers
				;We may loop a long time, so let other ints in
	ldb	int_mask, #SW_TIMER
	ldb	imask1, #(XMIT OR RCV)
;	st	timer1, curIntStates
	ei

ext_again:				;Loop while ext int asserted
	ldb	tmp0b, piaa_pda		;Get interrupt bits
	bbs	tmp0b, UARTA_DR_INT, rcva_int	  ;Check for rcv int on UART A
	bbs	tmp0b, UARTB_DR_INT, rcvb_int	  ;Ditto UART B
	bbs	tmp0b, UARTA_TBRE_INT, xmita_int ;Check for xmit int on A
	bbc	tmp0b, UARTB_TBRE_INT, ext_donechk

xmitb_int:				;UART B xmit int
	orb	uartb_stat, uartb_usr	;Get UART B status
	cmpb	R0, tx_status + UARTB	;check for XMIT on and no XOFF
	bne	ext_txb_off		;if XMIT off or XOFF, turn off txb ints
;	bbc	uartb_stat, XMIT_RDY_BIT, ext_donechk
		;Check deleted.  If TBRE pin asserted, UART Tx is ready.

	push	#(tx_ring + (UARTB * RING_STRUC_SIZE))
	lcall	ring_get		;Get char from ring buffer
	add	SP, #2
	cmp	plmreg, #0ffffh		;Check for no more characters
	be	turn_xmitb_off		;If no more, turn off xmitter
	stb	plmreg, uartb_data
	andb	uartb_stat, #(NOT (XMIT_RDY OR XMIT_DONE))
	sjmp	ext_donechk

turn_xmitb_off:
	orb	tx_status + UARTB, #XMITOFF
ext_txb_off:
	andb	ioctrl, #(NOT UARTB_INT)
	sjmp	ext_donechk

rcva_int:				;UART A rcv int
	orb	uarta_stat, uarta_usr
	bbc	uarta_stat, RCV_RDY_BIT, ext_donechk

	ldbze	tmp2, uarta_data	;Get serial character
	andb	uarta_stat, #(NOT RCV_RDY)
	ld	tmp4, #UARTA		;Indicate UART A
	scall	chk_xon			;Check if XON/XOFF char
	cmp	R0, plmreg		;If so, skip ring put
	bne	ext_donechk

	push	tmp2			;Pass rcvd byte
	push	#(rx_ring + (UARTA * RING_STRUC_SIZE))
	lcall	ring_put		;Put character in rcv ring
	add	SP, #4
	sjmp	ext_donechk

rcvb_int:				;UART B rcv int
	orb	uartb_stat, uartb_usr
	bbc	uartb_stat, RCV_RDY_BIT, ext_donechk

	ldbze	tmp2, uartb_data	;Get serial character
	andb	uartb_stat, #(NOT RCV_RDY)
	ld	tmp4, #UARTB		;Indicate UART B
	scall	chk_xon			;Check if XON/XOFF char
	cmp	R0, plmreg		;If so, skip ring put
	bne	ext_donechk

	push	tmp2			;Pass rcvd byte
	push	#(rx_ring + (UARTB * RING_STRUC_SIZE))
	lcall	ring_put		;Put character in rcv ring
	add	SP, #4

ext_donechk:	
	stb	ioctrl, oasis_ctrl	;write out copy of oasis_ctrl

	bbs	ioport2, EXTINT_BIT, ext_again
					;Loop as long as ext int asserted
extint_done:
;Test Code
;	sub	curIntStates, timer1, curIntStates
;	cmp	curIntStates, maxIntStates
;	bnh	extdone1
;	st	curIntStates, maxIntStates
;	nop
;	nop
;extdone1:
;End Test Code

	pop_cregs			;Restore registers
	popa				;Restore flags
	ret

xmita_int:				;UART A xmit int
	orb	uarta_stat, uarta_usr	;Get UART A status
	cmpb	R0, tx_status + UARTA	;check for XMIT on and no XOFF
	bne	ext_txa_off		;if XMIT off or XOFF, turn off txa ints
;	bbc	uarta_stat, XMIT_RDY_BIT, ext_donechk
		;Check deleted.  If TBRE pin asserted, UART Tx is ready.

	push	#(tx_ring + (UARTA * RING_STRUC_SIZE))
	lcall	ring_get		;Get char from ring buffer
	add	SP, #2
	cmp	plmreg, #0ffffh		;Check for no more characters
	be	turn_xmita_off		;If no more, turn off xmitter
	stb	plmreg, uarta_data
	andb	uarta_stat, #(NOT (XMIT_RDY OR XMIT_DONE))
	sjmp	ext_donechk

turn_xmita_off:
	orb	tx_status + UARTA, #XMITOFF
ext_txa_off:
	andb	ioctrl, #(NOT UARTA_INT)
	sjmp	ext_donechk

	END
