; *****************************************************************************************
;       Virtual Peripheral Universal Full Duplex UART with Buffering
;
;       Length: xxx bytes (total)
;       Authors: Chip Gracey, President, Parallax Inc. 10/03/97 to 07/09/98
;		 Craig Webb, Consultant to Scenix Semiconductor Inc.
;                Christopher Waters, Celsius Research Ltd.
;		 Stephen Holland, Scenix Semiconductor Inc.
;       Modifications:
;		 11/18/98, Craig Webb
;			- documented source and cleaned up code
;		 01/07/99, Christopher Waters
;			- added TX and RX FIFO buffering for full-duplex 
;		 02/15/99, Craig Webb
;			- expanded for 2 channel operation
;			- minimal clock frequency at 19.2Kbps
;		 03/06/99, Stephen Holland
;			- modified for 50MHz and up to 115.2Kbps operation
;		 03/11/99, Stephen Holland
;			- added autobaud capability for both channels
;		 03/19/99, Stephen Holland
;			- extended autobaud capability for operation from 110 - 115.2Kbps
;		 03/29/99, Stephen Holland
;			- added IFDEF to allow using same code for SX28 or SX48/52 targets
;		 03/30/99, Stephen Holland
;			- modified second channel for fixed 9600bps operation
;		 04/05/99-04/10/99, Stephen Holland
;			- added 166552-like bus interface and debugged
;		 04/12/99, Stephen Holland
;			- debugged additional handshaking lines DTR & DSR
;		 11/01/2004, Jim Brain
;			- made autobaud detection conditional code
;                       - added high speed configurable UART2 back in 
;                       - cleaned up code for prod SX52
;                       - added conditional to inline uart vp code, (saves 7 cycles per uart)
;                       - moved autobaud code to subroutines, to save space.
;
;*****************************************************************************************
; Assembler directives
;
; uses: SX28AC, 2 pages of program memory, 8 banks of RAM, high speed osc.
;       operating in turbo mode, with 8-level stack & extended option reg.
;                
;*****************************************************************************************
LIST Q=37
IRC_CAL IRC_FAST
; if you change this, change xtal as well
FREQ 50000000					;oscillator frequency


;*****************************************************************************************
; Conditional compilation defines
;
; AUTO_BAUD.  Define if you want to use the auto baud rate selection mechanism. (not tested)
; INLINE_VP.  Use if you want to inline the UART VP routines.  Saves 7 cycles per UART
;                
;*****************************************************************************************
;SX28
SX48_52
;AUTO_BAUD
INLINE_VP

include "stdinc.src"

		ID      'DUART'			;program ID label
		RESET   reset_entry		;set reset/boot address

;*****************************************************************************************
; Program constants
;*****************************************************************************************
; Ring buffer sizes
;	Note: if RX+TX values>16 then ring buffer bank definition will need to be adjusted

rx_ring_size_1	equ	16			;size (in bytes) of the rx ring buffer
tx_ring_size_1	equ	16			;size of the tx ring buffer
rx_ring_size_2	equ	16			;size (in bytes) of the rx ring buffer
tx_ring_size_2	equ	16			;size of the tx ring buffer

int_period	=	217			;cycles between interrupt passes
xtal		=	50000000
RTCC_prescalar	=	1

;int_period	=	163			;cycles between interrupt passes
;xtal		=	75000000
;RTCC_prescalar	=	2

;*****************************************************************************************
; Port Assignment: Bit variables
;*****************************************************************************************
IF SX_TYPE = 0		;SX28 port definitions

RA_init		equ     %1011			;initialize port RA
RA_IO		equ     %0100			;Set RA in/out directions;
RB_init		equ	%00000000		;initialize port RB
RB_IO		equ	%11111111		;Sets RB in/out directions
RC_init		equ     %10001010		;initialize port RC
RC_IO		equ     %00100101		;Sets RC in/out directions

rx_pin_1	equ	rc.0			;UART channel 1 receive input
tx_pin_1	equ	rc.1			;UART channel 1 transmit output
RTS_1		equ	rc.2			;RTS input
CTS_1		equ	rc.3			;CTS output
DSR_1		equ	rc.4			;DSR output
DTR_1		equ	rc.5			;DTR input
RI_1		equ	rc.6			;RI output
CD_1		equ	rc.7			;CD output

ELSE		;SX52 port definitions

RA_init		equ     %00000000		;initialize port RA
RA_IO		equ     %11111111		;Set RA in/out directions;
RB_init		equ	%00000000		;initialize port RB(NS16552-like data bus interface)
RB_IO		equ	%11111111		;Sets RB in/out directions
RC_init		equ     %11011010		;initialize port RC(UART channel 1 handshaking)
RC_IO		equ     %00100101		;Sets RC in/out directions
RD_init		equ     %11011010		;initialize port RD(UART channel 2 handshaking)
RD_IO		equ     %00100101		;Sets RD in/out directions
RE_init		equ     %00000000		;initialize port RE(NS16552-like address bus interface)
RE_IO		equ     %11111111		;Sets RE in/out directions

a0		equ	re.0			;UART address bit 0 input
a1		equ	re.1			;UART address bit 1 input
a2		equ	re.2			;UART address bit 2 input
chsl		equ	re.3			;UART channel select input
cs		equ	re.4			;UART chip select input
read		equ	re.5			;UART !read input
write		equ	re.6			;UART !write input

data_bus	equ	rb			;UART 8-bit data bus


; UART (channel 1)
rx_pin_1	equ	rc.0			;RXD input
tx_pin_1	equ	rc.1			;TXD output
RTS_1		equ	rc.2			;RTS input
CTS_1		equ	rc.3			;CTS output
DSR_1		equ	rc.4			;DSR output
DTR_1		equ	rc.5			;DTR input
RI_1		equ	rc.6			;RI output
CD_1		equ	rc.7			;CD output

; UART (channel 2)
rx_pin_2	equ	rd.0			;RXD input
tx_pin_2	equ	rd.1			;TXD output
RTS_2		equ	rd.2			;RTS input
CTS_2		equ	rd.3			;CTS output
DSR_2		equ	rd.4			;DSR output
DTR_2		equ	rd.5			;DTR input
RI_2		equ	rd.6			;RI output
CD_2		equ	rd.7			;CD output

ENDIF

B75		equ	0
B110		equ	1
B134		equ	2
B150		equ	3
B300		equ	4
B600		equ	5
B1200		equ	6
B2400		equ	7
B4800		equ	8
B9600		equ	9
B19200		equ	10
B38400		equ	11
B57600		equ	12
B115200		equ	13

;*****************************************************************************************
; Global Register definitions
;*****************************************************************************************
		org     $0a			;start of program registers

temp            ds      1                       ;temporary storage
isr_temp	ds	1			;used by isr - must be global
flags		ds	1
	uart1_en	equ	flags.0
	uart2_en	equ	flags.1
	rx_flag_1	equ	flags.2
	rx_flag_2	equ	flags.3
	speed_1		equ	flags.4
	speed_2		equ	flags.5
IFDEF AUTO_BAUD
	lo_detected_1	equ	flags.6
	lo_detected_2	equ	flags.7
ENDIF

line_status_1	ds	1
	rx_dr_1		equ	line_status_1.0	;Receiver Data Ready(DR) indicator
	buffer_oe_1	equ	line_status_1.1	;Buffer Overrun Error(OE) indicator
	parity_e_1	equ	line_status_1.2	;Parity Error(PE) indicator
	framing_e_1	equ	line_status_1.3	;Framing Error(FE) indicator
	break_int_1	equ	line_status_1.4	;Break Interrupt(BI) indicator
	thre_1		equ	line_status_1.5	;Transmitter Holding Register Empty(THRE) indicator
	temt_1		equ	line_status_1.6	;Transmitter Empty(TEMT) indicator
	lsr7_1		equ	line_status_1.7	;LSR7

line_status_2	ds	1
	rx_dr_2		equ	line_status_2.0	;Receiver Data Ready(DR) indicator
	buffer_oe_2	equ	line_status_2.1	;Buffer Overrun Error(OE) indicator
	parity_e_2	equ	line_status_2.2	;Parity Error(PE) indicator
	framing_e_2	equ	line_status_2.3	;Framing Error(FE) indicator
	break_int_2	equ	line_status_2.4	;Break Interrupt(BI) indicator
	thre_2		equ	line_status_2.5	;Transmitter Holding Register Empty(THRE) indicator
	temt_2		equ	line_status_2.6	;Transmitter Empty(TEMT) indicator
	lsr7_2		equ	line_status_2.7	;LSR7
rtcc_period	ds	1

;*****************************************************************************************
; RAM Bank Register definitions
;*****************************************************************************************

	;*********************************************************************************
	; Bank 1
	;*********************************************************************************
		org     bank1


uart_1_tx	=       $                       ;UART 1 TX bank

divisor_1	ds	2
tx_high_1	ds      1                       ;hi byte to transmit
tx_low_1	ds      1                       ;low byte to transmit
tx_count_1	ds      1                       ;number of bits sent
tx_divide_1	ds      2                       ;xmit timing (/16) counter2
tx_ring_ip_1	ds	1			;transmit ring in pointer
tx_ring_op_1	ds	1			;transmit ring out pointer
tx_ring_cnt_1	ds	1			;transmit ring contents count

string1		ds      1                       ;indirect ptr to output string

	;*********************************************************************************
	; Bank 2
	;*********************************************************************************
		org     bank2

uart_2_tx	=       $                       ;UART 2 TX bank

divisor_2	ds	2
tx_high_2	ds      1                       ;hi byte to transmit
tx_low_2	ds      1                       ;low byte to transmit
tx_count_2	ds      1                       ;number of bits sent
tx_divide_2	ds      2                       ;xmit timing (/16) counter
tx_ring_ip_2	ds	1			;transmit ring in pointer
tx_ring_op_2	ds	1			;transmit ring out pointer
tx_ring_cnt_2	ds	1			;transmit ring contents count

string2		ds      1                       ;indirect ptr to output string

	;*********************************************************************************
	; Bank 3
	;*********************************************************************************
		org	bank3

uart_rx		=	$			;UART RX bank

rx_count_1      ds      1                       ;number of bits received
rx_divide_1	ds      2                       ;receive timing counter
rx_byte_1	ds      1                       ;buffer for incoming byte
rx_ring_ip_1	ds	1			;receive ring in pointer
rx_ring_op_1	ds	1			;receive ring out pointer
rx_ring_cnt_1	ds	1			;receive ring contents count

rx_count_2      ds      1                       ;number of bits received
rx_divide_2	ds      2                       ;receive timing counter
rx_byte_2	ds      1                       ;buffer for incoming byte
rx_ring_ip_2	ds	1			;receive ring in pointer
rx_ring_op_2	ds	1			;receive ring out pointer
rx_ring_cnt_2	ds	1			;receive ring contents count

	;*********************************************************************************
	; Bank 4
	;*********************************************************************************
		org	bank4

uart_tx_ring_1	=	$			;UART channel 1 TX ring buffers
tx_ring_1	ds	tx_ring_size_1

	;*********************************************************************************
	; Bank 5
	;*********************************************************************************
		org	bank5

uart_rx_ring_1	=	$			;UART channel 1 RX ring buffers
rx_ring_1	ds	rx_ring_size_1

	;*********************************************************************************
	; Bank 6
	;*********************************************************************************
		org	bank6

uart_tx_ring_2	=	$			;UART channel 2 TX ring buffers
tx_ring_2	ds	tx_ring_size_2

	;*********************************************************************************
	; Bank 7
	;*********************************************************************************
		org	bank7

uart_rx_ring_2	=	$			;UART channel 2 RX ring buffers
rx_ring_2	ds	rx_ring_size_2

;*****************************************************************************************
; Watches 
;*****************************************************************************************
		watch	rx_ring_ip_1,8,uhex
		watch	rx_ring_cnt_1,8,udec
		watch	rx_ring_1,16,fstr
		watch	tx_ring_ip_1,8,uhex
		watch	tx_ring_cnt_1,8,udec
		watch	tx_ring_1,16,fstr
		watch	rx_ring_ip_2,8,uhex
		watch	rx_ring_cnt_2,8,udec
		watch	rx_ring_2,16,fstr
		watch	tx_ring_ip_2,8,uhex
		watch	tx_ring_cnt_2,8,udec
		watch	tx_ring_2,16,fstr
		watch	divisor_1,16,udec

;*****************************************************************************************
; Macros
;*****************************************************************************************
	; UART ring macro
	; Advance the pointer through the ring, wrapping around if necessary
	; This could be more efficient for aligned and power of 2 ring sizes.
	;*********************************************************************************
ringadv	macro	3		; Arguments ptr,base,size	
	inc	\1		; Increment the pointer
; Check for wrap around
	mov	w,\1		; Load the pointer
	xor	w,#(\2+\3)	; Check if ptr = base+size
	mov	w,#\2	
	snz	
	mov	\1,w		; Equal, set ptr to base
	endm

	;*********************************************************************************
	; VP: uart_1_transmit
	; 110 - 57.6Kbaud UART with RTS/CTS handshake
	; ** Part of the Interrupt Service Routine **
	;*********************************************************************************
UART1_TRANSMIT	macro
		bank    uart_1_tx		;switch to serial register bank
		inc	tx_divide_1		;ready to transmit bit?
		snz
		inc	tx_divide_1+1
		sz				;if tx_divide_1+1=0 then skip
		jmp	uart_1_receive		;else, exit
:continue	mov	w,divisor_1		;reload baud period divisor_1(16-bit)
		mov	tx_divide_1,w
		not	tx_divide_1		;compliment tx_divide_1
		mov	w,divisor_1+1
		mov	tx_divide_1+1,w
		not	tx_divide_1+1		;compliment tx_divide_1+1

		test    tx_count_1              ;are we sending?
		sz
		jmp	:txbit			;yes, send next bit
		mov	w,tx_ring_cnt_1		;is tx ring empty?
		snz
		jmp	uart_1_receive		;yes, go to :transmit_out
	;	jnb	DTR_1,uart_1_receive			
	;	jnb	RTS_1,uart_1_receive			
:txring		mov	w,tx_ring_op_1		;move one character from the ring to the
		mov	fsr,w			; transmitter using indirect addressing
		mov	w,indf
		bank	uart_1_tx		;switch back to the uart bank
		not     w                       ;ready bits (inverse logic)
		mov     tx_high_1,w             ; store data byte
		setb    tx_low_1.7              ; set up start bit
		mov     tx_count_1,#10          ;1 start + 8 data + 1 stop bit
		dec	tx_ring_cnt_1		;decrement tx ring byte count

		ringadv	tx_ring_op_1,tx_ring_1,tx_ring_size_1	;advance ring pointer
	
:txbit		clc                             ;ready stop bit
		rr      tx_high_1               ; and shift to next bit
		rr      tx_low_1                ;
		dec     tx_count_1              ;decrement bit counter
		movb    tx_pin_1,/tx_low_1.6    ;output next bit

uart_1_receive
		bank    uart_rx	
	        movb    c,rx_pin_1              ;get current rx bit
		test    rx_count_1              ;currently receiving byte?
		sz
		jmp     :rxbit                  ;if so, jump ahead
		mov     w,#9                    ;in case start, ready 9 bits
		sc                              ;skip ahead if not start bit
		mov     rx_count_1,w            ;it is, so renew bit count
		clc
		bank	uart_1_tx
		mov	w,divisor_1		;reload 1.5 bit period divisor_1(16-bit)
		bank    uart_rx	
		mov	rx_divide_1,w
		bank	uart_1_tx
		mov	w,divisor_1+1
		bank    uart_rx	
		mov	rx_divide_1+1,w
		mov	w,>>rx_divide_1+1	;w=divisor_1/2
		snc
		inc	rx_divide_1+1		;16-bit add
		mov	w,>>rx_divide_1		
		add	rx_divide_1,w
		snc
		inc	rx_divide_1+1
		not	rx_divide_1		;compliment rx_divide_1
		not	rx_divide_1+1		;compliment rx_divide_1+1

:rxbit
		inc	rx_divide_1		;ready to transmit bit?
		snz
		inc	rx_divide_1+1
		sz				;if rx_divide_1+1=0 then skip
IFDEF INLINE_VP
		jmp	:done
ELSE
		retp				; Return to the interrupt handler
ENDIF

		bank	uart_1_tx
		mov	w,divisor_1		;reload 1 bit period divisor_1(16-bit)
		bank    uart_rx	
		mov	rx_divide_1,w
		bank	uart_1_tx
		mov	w,divisor_1+1
		bank    uart_rx	
		mov	rx_divide_1+1,w
		not	rx_divide_1		;compliment rx_divide_1
		not	rx_divide_1+1		;compliment rx_divide_1+1

:get_bit        movb    c,rx_pin_1              ;get current rx bit
		dec     rx_count_1              ;last bit?
		sz                              ;if not
		rr      rx_byte_1               ;  then save bit
		sz                             	;and skip to end
IFDEF INLINE_VP
		jmp	:done
ELSE
		retp				; Return to the interrupt handler
ENDIF
		mov	w,rx_ring_cnt_1		; Will this byte make the receive buffer full?
		inc	wreg
		xor	w,#rx_ring_size_1	; Compare with the buffer size
		sz
		jmp	:rx_ok			; Not full
		clrb	CTS_1			; CTS = false - not ready to receive data
		clrb	DSR_1			; DSR = false - not ready to receive data
		setb	buffer_oe_1		; Signal receive buffer overflow
IFDEF INLINE_VP
		jmp	:done
ELSE
		retp				; Return to the interrupt handler
ENDIF
:rx_ok		setb	CTS_1			; CTS = true - ready to receive data
		setb	DSR_1			; DSR = true - ready to receive data
		mov	w,rx_byte_1		; Save the received byte in global
		mov	isr_temp,w
		mov	w,rx_ring_ip_1		; Store character in receive buffer
		mov	fsr,w			; Set indirect address
		mov	w,isr_temp			; temp must be global
		mov	indf,w			; Store the received byte
		bank    uart_rx			; Restore bank

		ringadv	rx_ring_ip_1,rx_ring_1,rx_ring_size_1

		inc	rx_ring_cnt_1		; Increment the ring buffer count
:done
ENDM

	;*********************************************************************************
	; VP: uart_2_transmit
	; 110 - 57.6Kbaud UART with RTS/CTS handshake
	; ** Part of the Interrupt Service Routine **
	;*********************************************************************************
UART2_TRANSMIT	macro
		bank    uart_2_tx		;switch to serial register bank
		inc	tx_divide_2		;ready to transmit bit?
		snz
		inc	tx_divide_2+1
		sz				;if tx_divide_1+1=0 then skip
		jmp	uart_2_receive		;else, exit
:continue	mov	w,divisor_2		;reload baud period divisor_1(16-bit)
		mov	tx_divide_2,w
		not	tx_divide_2		;compliment tx_divide_1
		mov	w,divisor_2+1
		mov	tx_divide_2+1,w
		not	tx_divide_2+1		;compliment tx_divide_1+1
		test    tx_count_2              ;are we sending?
		sz
		jmp	:txbit			;yes, send next bit
		mov	w,tx_ring_cnt_2		;is tx ring empty?
		snz
		jmp	uart_2_receive		;yes, go to :transmit_out
	;	jnb	DTR_2,uart_2_receive			
	;	jnb	RTS_2,uart_2_receive			
:txring		mov	w,tx_ring_op_2		;move one character from the ring to the
		mov	fsr,w			; transmitter using indirect addressing
		mov	w,indf
		bank	uart_2_tx		;switch back to the uart bank
		not     w                       ;ready bits (inverse logic)
		mov     tx_high_2,w             ; store data byte
		setb    tx_low_2.7              ; set up start bit
		mov     tx_count_2,#10          ;1 start + 8 data + 1 stop bit
		dec	tx_ring_cnt_2		;decrement tx ring byte count

		ringadv	tx_ring_op_2,tx_ring_2,tx_ring_size_2	;advance ring pointer
	
:txbit		clc                             ;ready stop bit
		rr      tx_high_2               ; and shift to next bit
		rr      tx_low_2                ;
		dec     tx_count_2              ;decrement bit counter
		movb    tx_pin_2,/tx_low_2.6    ;output next bit

uart_2_receive
		bank    uart_rx	
	        movb    c,rx_pin_2              ;get current rx bit
		test    rx_count_2              ;currently receiving byte?
		sz
		jmp     :rxbit                  ;if so, jump ahead
		mov     w,#9                    ;in case start, ready 9 bits
		sc                              ;skip ahead if not start bit
		mov     rx_count_2,w            ;it is, so renew bit count
		clc
		mov	w,divisor_2		;reload 1 bit period divisor_1(16-bit)
		bank    uart_rx	
		mov	rx_divide_2,w
		bank	uart_2_tx
		mov	w,divisor_2+1
		bank    uart_rx	
		mov	rx_divide_2+1,w
		not	rx_divide_2		;compliment rx_divide_1
		not	rx_divide_2+1		;compliment rx_divide_1+1
:rxbit
		inc	rx_divide_2		;ready to transmit bit?
		snz
		inc	rx_divide_2+1
		sz				;if rx_divide_1+1=0 then skip
IFDEF INLINE_VP
		jmp	:done
ELSE
		retp				; Return to the interrupt handler
ENDIF
		bank	uart_2_tx
		mov	w,divisor_2		;reload 1 bit period divisor_1(16-bit)
		bank    uart_rx	
		mov	rx_divide_2,w
		bank	uart_2_tx
		mov	w,divisor_2+1
		bank    uart_rx	
		mov	rx_divide_2+1,w
		not	rx_divide_2		;compliment rx_divide_1
		not	rx_divide_2+1		;compliment rx_divide_1+1
:get_bit        movb    c,rx_pin_2              ;get current rx bit
		dec     rx_count_2              ;last bit?
		sz                              ;if not
		rr      rx_byte_2               ;  then save bit
		sz                             	;and skip to end
IFDEF INLINE_VP
		jmp	:done
ELSE
		retp				; Return to the interrupt handler
ENDIF
		mov	w,rx_ring_cnt_2		; Is the receive buffer already full?
		inc	wreg
		xor	w,#rx_ring_size_2	; Compare with the buffer size
		sz
		jmp	:rx_ok			; Not full
		clrb	CTS_2			; CTS = false - not ready to receive data
		clrb	DSR_2			; DSR = false - not ready to receive data
		setb	buffer_oe_2		; Signal receive buffer overflow
IFDEF INLINE_VP
		jmp	:done
ELSE
		retp				; Return to the interrupt handler
ENDIF
:rx_ok		setb	CTS_2			; CTS = true - ready to receive data
		setb	DSR_2			; DSR = true - ready to receive data
		mov	w,rx_byte_2		; Save the received byte in global
		mov	isr_temp,w
		mov	w,rx_ring_ip_2		; Store character in receive buffer
		mov	fsr,w			; Set indirect address
		mov	w,isr_temp			; temp must be global
		mov	indf,w			; Store the received byte
		bank    uart_rx			; Restore bank

		ringadv	rx_ring_ip_2,rx_ring_2,rx_ring_size_2

		inc	rx_ring_cnt_2		; Increment the ring buffer count
:done
endm

	;*********************************************************************************
	; VP: uart_1_transmit_fast
	; 57.6K - 115.2Kbaud UART with RTS/CTS handshake
	; ** Part of the Interrupt Service Routine **
	;*********************************************************************************
UART1_TRANSMIT_FAST	macro
		bank    uart_1_tx		;switch to serial register bank
		decsz	tx_divide_1		;ready to transmit bit?
		jmp	uart_1_rx_fast
		mov	tx_divide_1,divisor_1	;reload baud period
		test    tx_count_1              ;are we sending?
		sz
		jmp	:txbit			;yes, send next bit
		mov	w,tx_ring_cnt_1		;is tx ring empty?
		snz
		jmp	uart_1_rx_fast		;yes, go to :transmit_out
	;	jnb	DTR_1,uart_1_rx_fast
	;	jnb	RTS_1,uart_1_rx_fast
:txring		mov	w,tx_ring_op_1		;move one character from the ring to the
		mov	fsr,w			; transmitter using indirect addressing
		mov	w,indf
		bank	uart_1_tx		;switch back to the uart bank
		not     w                       ;ready bits (inverse logic)
		mov     tx_high_1,w             ; store data byte
		setb    tx_low_1.7              ; set up start bit
		mov     tx_count_1,#10          ;1 start + 8 data + 1 stop bit
		dec	tx_ring_cnt_1		;decrement tx ring byte count

		ringadv	tx_ring_op_1,tx_ring_1,tx_ring_size_1	;advance ring pointer
	
:txbit		clc                             ;ready stop bit
		rr      tx_high_1               ; and shift to next bit
		rr      tx_low_1                ;
		dec     tx_count_1		;decrement bit counter
		movb    tx_pin_1,/tx_low_1.6    ;output next bit

uart_1_rx_fast	bank    uart_rx			;switch to serial register bank
	        movb    c,rx_pin_1              ;get current rx bit
		test    rx_count_1              ;currently receiving byte?
		sz
		jmp     :rxbit                  ;if so, jump ahead
		mov     w,#9                    ;in case start, ready 9 bits
		sc                              ;skip ahead if not start bit
		mov     rx_count_1,w            ;it is, so renew bit count
		clc
		bank    uart_1_tx
		mov	w,>>divisor_1
		add	w,divisor_1
		bank    uart_rx
		mov     rx_divide_1,w		;ready 1.5 bit periods
:rxbit          decsz   rx_divide_1		;middle of next bit?
IFDEF INLINE_VP
		jmp	:done
ELSE
		retp				; Return to the interrupt handler
ENDIF
		bank    uart_1_tx
		mov	w,divisor_1		;yes, reload 1 bit period
		bank    uart_rx
		mov	rx_divide_1,w
		dec     rx_count_1              ;last bit?
		sz                              ;if not
		rr      rx_byte_1               ;  then save bit
		sz                             	;and skip to end
IFDEF INLINE_VP
		jmp	:done
ELSE
		retp				; Return to the interrupt handler
ENDIF
		mov	w,rx_ring_cnt_1		; Is the receive buffer already full?
		inc	wreg
		xor	w,#rx_ring_size_1	; Compare with the buffer size
		sz
		jmp	:rx_ok			; Not full
		clrb	CTS_1			; CTS = false - not ready to receive data
		clrb	DSR_1			; DSR = false - not ready to receive data
		setb	buffer_oe_1		; Signal receive buffer overflow
IFDEF INLINE_VP
		jmp	:done
ELSE
		retp				; Return to the interrupt handler
ENDIF
:rx_ok		setb	CTS_1			; CTS = true - ready to receive data
		setb	DSR_1			; DSR = true - ready to receive data
		mov	w,rx_byte_1		; Save the received byte in global
		mov	isr_temp,w
		mov	w,rx_ring_ip_1		; Store character in receive buffer
		mov	fsr,w			; Set indirect address
		mov	w,isr_temp			; temp must be global
		mov	indf,w			; Store the received byte
		bank	uart_rx			; Restore the bank

		ringadv	rx_ring_ip_1,rx_ring_1,rx_ring_size_1

		inc	rx_ring_cnt_1		; Increment the ring buffer count
:done
endm

	;*********************************************************************************
	; VP: uart_2_transmit_fast
	; 57.6K - 115.2Kbaud UART with RTS/CTS handshake
	; ** Part of the Interrupt Service Routine **
	;*********************************************************************************
UART2_TRANSMIT_FAST	macro
		bank    uart_2_tx		;switch to serial register bank
		decsz	tx_divide_2		;ready to transmit bit?
		jmp	uart_2_rx_fast
		mov	tx_divide_2,divisor_2	;reload baud period
		test    tx_count_2              ;are we sending?
		sz
		jmp	:txbit			;yes, send next bit
		mov	w,tx_ring_cnt_2		;is tx ring empty?
		snz
		jmp	uart_2_rx_fast		;yes, go to :transmit_out
	;	jnb	DTR_2,uart_2_rx_fast
	;	jnb	RTS_2,uart_2_rx_fast
:txring		mov	w,tx_ring_op_2		;move one character from the ring to the
		mov	fsr,w			; transmitter using indirect addressing
		mov	w,indf
		bank	uart_2_tx		;switch back to the uart bank
		not     w                       ;ready bits (inverse logic)
		mov     tx_high_2,w             ; store data byte
		setb    tx_low_2.7              ; set up start bit
		mov     tx_count_2,#10          ;1 start + 8 data + 1 stop bit
		dec	tx_ring_cnt_2		;decrement tx ring byte count

		ringadv	tx_ring_op_2,tx_ring_2,tx_ring_size_2	;advance ring pointer
	
:txbit		clc                             ;ready stop bit
		rr      tx_high_2               ; and shift to next bit
		rr      tx_low_2                ;
		dec     tx_count_2		;decrement bit counter
		movb    tx_pin_2,/tx_low_2.6    ;output next bit

uart_2_rx_fast	bank    uart_rx			;switch to serial register bank
	        movb    c,rx_pin_2              ;get current rx bit
		test    rx_count_2              ;currently receiving byte?
		sz
		jmp     :rxbit                  ;if so, jump ahead
		mov     w,#9                    ;in case start, ready 9 bits
		sc                              ;skip ahead if not start bit
		mov     rx_count_2,w            ;it is, so renew bit count
		clc
		bank    uart_2_tx
		mov	w,>>divisor_2
		add	w,divisor_2
		bank    uart_rx
		mov     rx_divide_2,w		;ready 1.5 bit periods
:rxbit          decsz   rx_divide_2		;middle of next bit?
IFDEF INLINE_VP
		jmp	:done
ELSE
		retp				; Return to the interrupt handler
ENDIF
		bank    uart_2_tx
		mov	w,divisor_2		;yes, reload 1 bit period
		bank    uart_rx
		mov	rx_divide_2,w
		dec     rx_count_2              ;last bit?
		sz                              ;if not
		rr      rx_byte_2               ;  then save bit
		sz                             	;and skip to end
IFDEF INLINE_VP
		jmp	:done
ELSE
		retp				; Return to the interrupt handler
ENDIF
		mov	w,rx_ring_cnt_2		; Is the receive buffer already full?
		inc	wreg
		xor	w,#rx_ring_size_2	; Compare with the buffer size
		sz
		jmp	:rx_ok			; Not full
		clrb	CTS_2			; CTS = false - not ready to receive data
		clrb	DSR_2			; DSR = false - not ready to receive data
		setb	buffer_oe_2		; Signal receive buffer overflow
IFDEF INLINE_VP
		jmp	:done
ELSE
		retp				; Return to the interrupt handler
ENDIF
:rx_ok		setb	CTS_2			; CTS = true - ready to receive data
		setb	DSR_2			; DSR = true - ready to receive data
		mov	w,rx_byte_2		; Save the received byte in global
		mov	isr_temp,w
		mov	w,rx_ring_ip_2		; Store character in receive buffer
		mov	fsr,w			; Set indirect address
		mov	w,isr_temp			; temp must be global
		mov	indf,w			; Store the received byte
		bank	uart_rx			; Restore the bank

		ringadv	rx_ring_ip_2,rx_ring_2,rx_ring_size_2

		inc	rx_ring_cnt_2		; Increment the ring buffer count
:done
endm

;*****************************************************************************************
; Interrupt Service Routine
;*****************************************************************************************
	       ORG     0                       ;interrupt starts at 0h
interrupt

IFDEF AUTO_BAUD
		call @uart_1_autobaud
  IF SX_TYPE=1
		call @uart_2_autobaud
  ENDIF
ENDIF


	;*********************************************************************************
	; Virtual Peripheral: Universal Asynchronous Receiver Transmitter (UART) 
	;
	;   This routine sends and receives 2 channels of RS232C serial data, 
	; currently configured (though modifications can be made) for the popular
	; "No parity-checking, 8 data bit, 1 stop bit" (N,8,1) data format.
	;
	; RECEIVING:
	;   Whenever a valid byte of data has been received, it is put into
	; the rx_ring FIFO buffer. The address pointer rx_ring_ip and FIFO byte count  
	; rx_ring_cnt are also incremented with every new byte. A receive buffer overflow
	; is signaled by the uart_rx_oflow flag.
	;
	; TRANSMITTING:
	;   The transmit routine requires the data to be inverted and loaded (tx_high+tx_low)
	; register pair (with the inverted 8 data bits stored in tx_high and tx_low bit 7 set 
	; high to act as a start bit). Then the number of bits ready for transmission
	; (10=1 start + 8 data + 1 stop) must be loaded into the tx_count register.
	; As soon as this latter is done, the transmit routine immediately begins sending the data.
	;   The handshaking signals RTS and CTS have also been implemented. 
	; The CTS output is used to indicate that the DCE (SX) is ready to receive data. 
	; The RTS input is used to indicate that the DTE (terminal) is ready for receiving. 
	; Data is only sent if the RTS line is active.
	;
	; This routine has a varying execution rate and therefore should always be
	; placed after any timing-critical virtual peripherals such as timers,
	; adcs, pwms, etc.
	; Note: The transmit and receive routines are independent and either may be
	;	removed, if not needed, to reduce execution time and memory usage,
	;	as long as the initial "BANK serial" (common) instruction is kept.
	;
	;       Input variable(s) : tx_low (only high bit used), tx_high, tx_count
	;       Output variable(s) : rx_flag, rx_byte
	;       Variable(s) affected : tx_divide, rx_divide, rx_count
	;       Flag(s) affected : rx_flag
	;	Size : Transmit - 34 bytes + 1 byte shared with receive code
	;		Receive - 41 bytes + 1 byte shared with transmit code
	;       Timing (turbo) : 
	;	       Transmit - (a) [multiplexing] 2 cycles
	;			  (b) [not sending] 5 cycles
	;			  (c) [sending] 18 cycles
	;                         (d) [buffer empty] 13 cycles
	;		          (e) [start sending] 34 cycles
	;			  + 1 cycle shared with RX code ("bank" instr.)
	;		Receive - (a) [multiplexing] 2 cycles
	;			  (b) [not receiving] 9 cycles
	;			  (c) [start receiving] 15 cycles
	;			  (d) [receiving, awaiting bit] 13 cycles
	;			  (e) [receiving, bit ready] 20 cycles
	;		          (f) [receiving, last bit] 37 cycles
	;                         (g) [receiving, buffer full] 25 cycles
	;
	;
	;*********************************************************************************

IFDEF INLINE_VP		; we are inlining the VP functions, to save 7 cycles per UART
UART1		jnb	uart1_en,UART2		;execute UART channel 1 only when enabled
		jb	speed_1,uart_1_transmit_fast
		UART1_TRANSMIT
		jmp	UART2
uart_1_transmit_fast
		UART1_TRANSMIT_FAST
UART2

IF SX_TYPE = 1		;only compile if target is SX52

		jnb	uart2_en,uarts_done	;execute UART channel 2 only when enabled
		jb	speed_2,uart_2_transmit_fast
		UART2_TRANSMIT
		jmp	uarts_done
uart_2_transmit_fast
		UART2_TRANSMIT_FAST
ENDIF
ELSE			; we are calling the VP functions, not inlining
UART1		jnb	uart1_en,UART2		;execute UART channel 1 only when enabled
		jb	speed_1,:fast
		call	@uart_1_transmit
		jmp	UART2
:fast
		call	@uart_1_transmit_fast
UART2

IF SX_TYPE = 1		;only compile if target is SX52

		jnb	uart2_en,uarts_done	;execute UART channel 2 only when enabled
		jb	speed_2,:fast
		call	@uart_2_transmit
		jmp	uarts_done
:fast
		call	@uart_2_transmit_fast
ENDIF
ENDIF

uarts_done

;*****************************************************************************************
; End of Interrupt Service Routine
;*****************************************************************************************
isr_end
	;*********************************************************************************
	; Set Interrupt Rate
	;*********************************************************************************
		mov     w,#-int_period          ;interrupt every 'int_period' clocks
		retiw                           ;exit interrupt

;*****************************************************************************************
; RESET VECTOR 
;*****************************************************************************************

	;*********************************************************************************
	; Program execution begins here on power-up or after a reset
	;*********************************************************************************
reset_entry
IF SX_TYPE = 0	;SX28
		mov	m,#$0f			;point mode to port I/O's
		mov      ra,#RA_init		;initialize port RA
		mov     !ra,#RA_IO		;Set RA in/out directions
		mov      rb,#RB_init		;initialize port RB
		mov     !rb,#RB_IO		;Set RB in/out directions
		mov      rc,#RC_init		;initialize port RC
		mov     !rc,#RC_IO		;Set RC in/out directions

ELSE		;SX52
		mov	m,#$1f			;point mode to port I/O's
		mov      ra,#RA_init		;initialize port RA
		mov     !ra,#RA_IO		;Set RA in/out directions
		mov      rb,#RB_init		;initialize port RB
		mov     !rb,#RB_IO		;Set RB in/out directions
		mov      rc,#RC_init		;initialize port RC
		mov     !rc,#RC_IO		;Set RC in/out directions
		mov      rd,#RD_init		;initialize port RD
		mov     !rd,#RD_IO		;Set RD in/out directions
		mov      re,#RE_init		;initialize port RE
		mov     !re,#RE_IO		;Set RE in/out directions
ENDIF

	;*********************************************************************************
	; Clear all Data RAM locations
	;*********************************************************************************
IF SX_TYPE = 0	;SX28
		clr	fsr			;reset all ram banks
:zero_ram	sb	fsr.4			;are we on low half of bank?
		setb	fsr.3			;If so, don't touch regs 0-7
		clr	ind			;clear using indirect addressing
		incsz	fsr			;repeat until done
		jmp	:zero_ram

ELSE		;SX52
		mov	w,#$0a			;reset all ram starting at 08h
		mov	fsr,w
:zero_ram	clr	ind			;clear using indirect addressing
		incsz	fsr			;repeat until done
		jmp	:zero_ram

		clrb	fsr.7
ENDIF

		call	@uarts_init		;initialise the uarts

IF RTCC_prescalar = 1
		mov     !option,#%10011111      ;enable rtcc interrupt
ENDIF
IF RTCC_prescalar = 2
		mov     !option,#%10010000      ;enable rtcc interrupt
ENDIF

IFNDEF	AUTO_BAUD
		mov	w,#B115200
		call	@set_bps_rate_1
IF SX_TYPE = 1		;only compile if target is SX48/52
		mov	w,#B115200
		call	@set_bps_rate_2
ENDIF
ENDIF

		jmp	@main

;*****************************************************************************************
; MAIN PROGRAM CODE 
;*****************************************************************************************
		org	$200
main

	;*********************************************************************************
	; Main Program Loop
	;*********************************************************************************
IF SX_TYPE = 0		;only compile if target is SX28

		mov	w,#_hello		;send hello string to channel 1
		call	@send_string_1

:prompt		mov	w,#_prompt		;send prompt to channel 1
		call	@send_string_1

main_loop
		call	@get_byte_1		;get byte via UART channel 1
		sz
		call	@send_byte_1		;echo byte back to sender on channel 1

		jmp	main_loop		;return

ELSE

		mov	w,#_hello		;send hello string to channel 2
		call	@send_string_2
		mov	w,#_ch2			;send channel string to channel 2
		call	@send_string_2
		mov	w,#_prompt		;send prompt to channel 2
		call	@send_string_2
		mov	w,#_hello		;send hello string to channel 1
		call	@send_string_1
		mov	w,#_ch1			;send channel string to channel 1
		call	@send_string_1
		mov	w,#_prompt		;send prompt to channel 1
		call	@send_string_1

main_loop
		call	@get_byte_1		;get byte via UART channel 1
		sz
		call	@send_byte_1		;echo byte back to sender on channel 1
		call	@get_byte_2		;get byte via UART channel 2
		sz
		call	@send_byte_2		;echo byte back to sender on channel 2

		jmp	main_loop		;return

; this stuff was left in, to show how to create a 16550 device
cs_loop		
		snb	cs			;wait for chip select active(low)
		jmp	cs_loop			;return

		sb	read			;check for read active(low)
		jmp	:cont			; it's low, must be a request
		sb	write			;check for write active(low)
		jmp	:cont			; it's low, must be a request
		jmp	main_loop		;return
		
:cont		mov	temp,re			;keep only the lower 3 address bits
		and	temp,#$07
		sb	chsl			;if chsl = 0, 
		jmp	channel_2		; then channel 2 is being addressed

channel_1
		snz				;if lower 3 address bits = 0,
		jmp	tx_rx_buffer_1		; then tx/rx buffer is being addressed

		mov	w,#1			;is current address = $01?
		mov	w,temp-w
		snz
		jmp	int_enable_1

		mov	w,#2			;is current address = $02?
		mov	w,temp-w
		snz
		jmp	int_id_1

		mov	w,#3			;is current address = $03?
		mov	w,temp-w
		snz
		jmp	fifo_control_1

		mov	w,#4			;is current address = $04?
		mov	w,temp-w
		snz
		jmp	modem_control_1

		mov	w,#5			;is current address = $05?
		mov	w,temp-w
		snz
		jmp	line_status_1

		mov	w,#6			;is current address = $06?
		mov	w,temp-w
		snz
		jmp	modem_status_1
		jmp	main_loop		;return

tx_rx_buffer_1					;$00 -> Receive Buffer/Transmit Holding Register
		sb	read			;if read bit = 0,
		jmp	:read			; then receive buffer is being addressed
		sb	write			;if write bit = 0,
		jmp	:write			; then transmit holding buffer is being addressed
		jmp	main_loop		;return
:read
		mov	w,#$00			;change data bus to output
		mov	!rb,w	
		call	@get_byte_1		;get byte
		mov	rb,w		;load byte in w on data bus
		jnb	read,$
		mov	w,#$ff			;change data bus to input(hi-Z)
		mov	!rb,w	
		jmp	main_loop		;return
:write
		mov	w,rb			;load byte on data bus in w
		call	@send_byte_1		;send byte
		jnb	write,$
		jmp	main_loop		;return

int_enable_1					;$01 -> Interrupt Enable
		jmp	main_loop		;return

int_id_1					;$02 -> Interrupt ID
		jmp	main_loop		;return

fifo_control_1					;$03 -> FIFO Control
		jmp	main_loop		;return

modem_control_1					;$04 -> MODEM Control
		jmp	main_loop		;return

line_stat_1					;$05 -> Line Status
		snb	read			;if read bit = 1(not active),
		jmp	:write			; then return(not meant to be written to)
:read						; else line status register is being read
		mov	w,#$00			;change data bus to output
		mov	!data_bus,w	
		mov	w,line_status_1		;get line_status
		mov	data_bus,w		;load byte in w on data bus
		mov	w,#$ff			;change data bus to input(hi-Z)
		mov	!data_bus,w	
:write		jmp	main_loop		;return

modem_status_1					;$06 -> MODEM Status
		jmp	main_loop		;return

scratch_1					;$07 -> Scratch
		jmp	main_loop		;return

channel_2
		snz				;if lower 3 address bits = 0,
		jmp	tx_rx_buffer_2		; then tx/rx buffer is being addressed

		mov	w,#1			;is current address = $01?
		mov	w,temp-w
		snz
		jmp	int_enable_2

		mov	w,#2			;is current address = $02?
		mov	w,temp-w
		snz
		jmp	int_id_2

		mov	w,#3			;is current address = $03?
		mov	w,temp-w
		snz
		jmp	fifo_control_2

		mov	w,#4			;is current address = $04?
		mov	w,temp-w
		snz
		jmp	modem_control_2

		mov	w,#5			;is current address = $05?
		mov	w,temp-w
		snz
		jmp	line_status_2

		mov	w,#6			;is current address = $06?
		mov	w,temp-w
		snz
		jmp	modem_status_2
		jmp	main_loop		;return

tx_rx_buffer_2					;$00 -> Receive Buffer/Transmit Holding Register
		sb	read			;if read bit = 0,
		jmp	:read			; then receive buffer is being addressed
		sb	write			;if write bit = 0,
		jmp	:write			; then transmit holding buffer is being addressed
		jmp	main_loop		;return
:read
		mov	w,#$00			;change data bus to output
		mov	!rb,w	
		call	@get_byte_2		;get byte
		mov	rb,w			;load byte in w on data bus
		jnb	read,$
		mov	w,#$ff			;change data bus to input(hi-Z)
		mov	!rb,w	
		jmp	main_loop		;return
:write
		mov	w,rb			;load byte on data bus in w
		call	@send_byte_2		;send byte
		jnb	write,$
		jmp	main_loop		;return

int_enable_2					;$01 -> Interrupt Enable
		jmp	main_loop		;return

int_id_2					;$02 -> Interrupt ID
		jmp	main_loop		;return

fifo_control_2					;$03 -> FIFO Control
		jmp	main_loop		;return

modem_control_2					;$04 -> MODEM Control
		jmp	main_loop		;return

line_stat_2					;$05 -> Line Status
		snb	read			;if read bit = 1(not active),
		jmp	:write			; then return(not meant to be written to)
:read						; else line status register is being read
		mov	w,#$00			;change data bus to output
		mov	!data_bus,w	
		mov	w,line_status_2		;get line_status
		mov	data_bus,w		;load byte in w on data bus
		mov	w,#$ff			;change data bus to input(hi-Z)
		mov	!data_bus,w	
:write		jmp	main_loop		;return

modem_status_2					;$06 -> MODEM Status
		jmp	main_loop		;return

scratch_2					;$07 -> Scratch
		jmp	main_loop		;return

ENDIF

;*****************************************************************************************
; PROGRAM DATA 
;*****************************************************************************************
		org	$400			;must be lower half of page (for calls)
datapage

; String data for user interface (must be in lower half of memory page)
;
_hello		dw	13,10,13,10,'High speed SX DUART Demo 11/01/2004',0
_ch1		dw	13,10,13,10,'Channel 1',0
_ch2		dw	13,10,13,10,'Channel 2',0
_prompt		dw	13,10,'>',0
			; 75  110  134  150  300  600 1200 2400 4800 9600 19200 38400 57600 115200 230400
IF xtal=50000000
;50MHz @ 217
_divisors	dw	3072,2095,1719,1536, 768, 384, 192,  96,  48,  24,   12,    6,    4,     2,     0
ENDIF
IF xtal=75000000
  IF RTCC_prescalar=2
;75MHz, half speed @ 163
_divisors	dw	3067,2091,1717,1534, 767, 383, 192,  96,  48,  24,   12,    6,    4,     2,     0
  ELSE
;75Mhz, @ 217
_divisors       dw         0,3142,2579,2304,1152, 576, 288, 144,  72,  36,   18,    9,    6      3,     0
  ENDIF
ENDIF
;_divisors	dw	0,2304,1536,1048,856,768,384,192,96,94,48,32,24,16,12,6,4,2

;*****************************************************************************************
; SUBROUTINES 
;*****************************************************************************************

	;*********************************************************************************
	; Function: set_bps_rate_1
	; Set the baud rate, based on an index
	; 0 = 75bps
	; 1 = 110bps
	; 2 = 134bps
   	; 3 = 150bps
	; 4 = 300bps
	; 5 = 600bps
	; 6 = 1200bps
	; 7 = 2400bps
	; 8 = 4800bps
	; 9 = 9600bps
	; 10 = 19200bps
	; 11 = 38400bps
	; 12 = 57600bps
	; 13 = 115200bps
	;
	;*********************************************************************************
set_bps_rate_1
		mov	m,#4;datapage/100h	; with indirect addressing*
		mov	temp,w
		mov	w,#_divisors
		add	w,temp			;store divisors address
		iread 				; using the mode register
		snz				; if W is zero, invalid bps rate
		retp
		bank	uart_1_tx
		setb	uart1_en
		mov	divisor_1,w
		mov	w,m
		and	w, #$0f
		jnz	:lo_speed
		setb	speed_1
		retp
:lo_speed
		mov	divisor_1+1,w
		clrb	speed_1
		retp
		
IF SX_TYPE = 1		;only compile if target is SX52
	;*********************************************************************************
	; Function: set_bps_rate_2
	; Set the baud rate, based on an index
	;*********************************************************************************
set_bps_rate_2
		mov	m,#4;datapage/100h	; with indirect addressing*
		mov	temp,w
		mov	w,#_divisors
		add	w,temp			;store divisors address
		iread 				; using the mode register
		snz				; if W is zero, invalid bps rate
		retp
		bank	uart_2_tx
		setb	uart2_en
		mov	divisor_2,w
		mov	w,m
		and	w, #$0f
		jnz	:lo_speed
		setb	speed_2
		retp
:lo_speed
		mov	divisor_2+1,w
		clrb	speed_2
		retp
ENDIF

	;*********************************************************************************
	; Function: uarts_init
	; Initialise the UARTs
	;*********************************************************************************
uarts_init	
		jmp	_uarts_init

	;*********************************************************************************
	; Function: get_byte_1
	; Get byte via serial port
	; Byte received is returned in W
	;*********************************************************************************
get_byte_1	bank	uart_rx			; Select the bank
:wait		mov	w,rx_ring_cnt_1		; Get the number of bytes in the rx ring
		snz				; Is the receive ring empty?
		jmp	:return			; Yes, block until not empty
		mov	w,rx_ring_op_1		; Load the ring out pointer
		mov	fsr,w
		mov	w,indf			; Get character from buffer
		mov	temp,w			; Save character in temp
		bank	uart_rx			; Restore the bank

		ringadv	rx_ring_op_1,rx_ring_1,rx_ring_size_1	; Advance ring pointer

		dec	rx_ring_cnt_1		; Decrement rx char count
		sb	CTS_1			; If CTS is set, clear it
		setb	CTS_1			; CTS = true - ready to receive data
		sb	DSR_1			; If DSR is set, clear it
		setb	DSR_1			; DSR = true - ready to receive data
		mov	w,temp			; Return byte in W
		setb	rx_flag_1
		retp
:return		;clr	w			; Return 0 in W
		retp
	;*********************************************************************************
	; Function: get_byte_2
	; Get byte via serial port
	; Byte received is returned in W
	;*********************************************************************************
IF SX_TYPE = 1		;only compile if target is SX52

get_byte_2	bank	uart_rx			; Select the bank
:wait		mov	w,rx_ring_cnt_2		; Get the number of bytes in the rx ring
		snz				; Is the receive ring empty?
		jmp	:return			; Yes, block until not empty
		mov	w,rx_ring_op_2		; Load the ring out pointer
		mov	fsr,w
		mov	w,indf			; Get character from buffer
		mov	temp,w			; Save character in temp
		bank	uart_rx			; Restore the bank

		ringadv	rx_ring_op_2,rx_ring_2,rx_ring_size_2	; Advance ring pointer

		dec	rx_ring_cnt_2		; Decrement rx char count
		sb	CTS_2			; If CTS is set, clear it
		setb	CTS_2			; CTS = true - ready to receive data
		sb	DSR_2			; If DSR is set, clear it
		setb	DSR_2			; DSR = true - ready to receive data
		mov	w,temp			; Return byte in W
		setb	rx_flag_2
		retp
:return		;clr	w			; Return 0 in W
		retp

ENDIF
	;*********************************************************************************
	; Function: send_byte_1
	; Send byte via serial port
	; Byte to be sent should be in W
	;*********************************************************************************
send_byte_1	bank	uart_1_tx		; Select the bank
		mov	temp,w

:wait		mov	w,tx_ring_cnt_1		; Load the current ring contents
		xor	w,#tx_ring_size_1	; Compare to the ring size
		snb	status.2		; Is there room for a character?
		jmp	:wait			; No, block until there is room

		mov	w,tx_ring_ip_1		; Get buffer pointer
		mov	fsr,w
		mov	w,temp	
		mov	indf,w			; Save temp in the ring
		bank	uart_1_tx		; Restore the bank

		ringadv	tx_ring_ip_1,tx_ring_1,tx_ring_size_1	; Advance ring pointer

		inc	tx_ring_cnt_1		; Increment tx char count
		retp                            ;leave and fix page bits

	;*********************************************************************************
	; Function: send_byte_2
	; Send byte via serial port
	; Byte to be sent should be in W
	;*********************************************************************************
IF SX_TYPE = 1		;only compile if target is SX52

send_byte_2	bank	uart_2_tx		; Select the bank
		mov	temp,w

:wait		mov	w,tx_ring_cnt_2		; Load the current ring contents
		xor	w,#tx_ring_size_2	; Compare to the ring size
		snb	status.2		; Is there room for a character?
		jmp	:wait			; No, block until there is room

		mov	w,tx_ring_ip_2		; Get buffer pointer
		mov	fsr,w
		mov	w,temp	
		mov	indf,w			; Save temp in the ring
		bank	uart_2_tx		; Restore the bank

		ringadv	tx_ring_ip_2,tx_ring_2,tx_ring_size_2	; Advance ring pointer

		inc	tx_ring_cnt_2		; Increment tx char count
:return		retp				;leave and fix page bits

ENDIF
	;*********************************************************************************
	; Function: bytes_avail_1
	; Get number of bytes waiting in receive buffer
	; Number of bytes in buffer will be returned in W
	;*********************************************************************************
bytes_avail_1	bank	uart_rx			; Ensure ring variables
		mov	w,rx_ring_cnt_1		; Get receive ring count	
		retp

	;*********************************************************************************
	; Function: bytes_avail_2
	; Get number of bytes waiting in receive buffer
	; Number of bytes in buffer will be returned in W
	;*********************************************************************************
IF SX_TYPE = 1		;only compile if target is SX52

bytes_avail_2	bank	uart_rx			; Ensure ring variables
		mov	w,rx_ring_cnt_2		; Get receive ring count	
		retp

ENDIF
	;*********************************************************************************
	; Function: send_string_1
	; Send string pointed to by address in W register
	; *Note: High nibble into mode register denotes page address of data
	;*********************************************************************************
send_string_1	bank	uart_1_tx
		mov	string1,w		;store string address
	;	jnb	CTS_1,$			;wait for CTS_1 = true - ready to receive data
:loop		mov	w,string1		;read next string character
		mov	m,#4;datapage/100h		; with indirect addressing*
		iread 				; using the mode register
IF SX_TYPE = 0	;SX28
		mov	m,#$0f			;point mode to port I/O's
ELSE		;SX52
		mov	m,#$1f			;point mode to port I/O's
ENDIF
		test	w 			;are we at the last char?
		snz				;if not=0, skip ahead
		retp 				;yes, leave & fix page bits
						;not 0, so send character
		mov	temp,w
:wait		mov	w,tx_ring_cnt_1		; Load the current ring contents
		xor	w,#tx_ring_size_1	; Compare to the ring size
		snb	status.2		; Is there room for a character?
		jmp	:wait			; No, block until there is room

		mov	w,tx_ring_ip_1		; Get buffer pointer
		mov	fsr,w
		mov	w,temp	
		mov	indf,w			; Save temp in the ring
		bank	uart_1_tx		; Restore the bank
		ringadv	tx_ring_ip_1,tx_ring_1,tx_ring_size_1	; Advance ring pointer
		inc	tx_ring_cnt_1		; Increment tx char count

		inc	string1			;point to next character
		jmp	:loop			;loop until done

	;*********************************************************************************
	; Function: send_string_2
	; Send string pointed to by address in W register
	; *Note: High nibble into mode register denotes page address of data
	;*********************************************************************************
IF SX_TYPE = 1		;only compile if target is SX52

send_string_2	bank	uart_2_tx
		mov	string2,w		;store string address
	;	jnb	CTS_2,$			;wait for CTS_2 = true - ready to receive data
:loop		mov	w,string2		;read next string character
		mov	m,#4;datapage/100h		; with indirect addressing*
		iread 				; using the mode register
		mov	m,#$1f			;point mode to port I/O's
		test	w 			;are we at the last char?
		snz				;if not=0, skip ahead
		retp 				;yes, leave & fix page bits
						;not 0, so send character
		mov	temp,w
:wait		mov	w,tx_ring_cnt_2		; Load the current ring contents
		xor	w,#tx_ring_size_2	; Compare to the ring size
		snb	status.2		; Is there room for a character?
		jmp	:wait			; No, block until there is room

		mov	w,tx_ring_ip_2		; Get buffer pointer
		mov	fsr,w
		mov	w,temp	
		mov	indf,w			; Save temp in the ring
		bank	uart_2_tx		; Restore the bank
		ringadv	tx_ring_ip_2,tx_ring_2,tx_ring_size_2	; Advance ring pointer
		inc	tx_ring_cnt_2		; Increment tx char count

		inc	string2			;point to next character
		jmp	:loop			;loop until done
		
ENDIF

_uarts_init	bank	uart_rx			; Select the bank
		clr	rx_ring_cnt_1		; The receive ring is empty
		mov	w,#rx_ring_1
		mov	rx_ring_ip_1,w		; Set the in and out pointers to the start of
		mov	rx_ring_op_1,w		; the receive ring

IF SX_TYPE = 1		;only compile if target is SX52
		clr	rx_ring_cnt_2		; The receive ring is empty
		mov	w,#rx_ring_2
		mov	rx_ring_ip_2,w		; Set the in and out pointers to the start of
		mov	rx_ring_op_2,w		; the receive ring
ENDIF
		bank	uart_1_tx	
		clr	tx_ring_cnt_1		; The transmit ring is empty
		mov	w,#tx_ring_1
		mov	tx_ring_ip_1,w		; Set the in and out pointers to the start of
		mov	tx_ring_op_1,w		; the transmit ring

IF SX_TYPE = 1		;only compile if target is SX52
		bank	uart_2_tx	
		clr	tx_ring_cnt_2		; The transmit ring is empty
		mov	w,#tx_ring_2
		mov	tx_ring_ip_2,w		; Set the in and out pointers to the start of
		mov	tx_ring_op_2,w		; the transmit ring
		setb	uart2_en		;enable UART channel 2
ENDIF
		retp

;*****************************************************************************************
; Jump Table for Page 2 
;*****************************************************************************************
		org	$600
IFDEF AUTO_BAUD
	;*********************************************************************************
	; Virtual Peripheral: UART Autobaud (channel 1)
	;
	; This routine adds Autobaud capability to the UARTs by incrementing an 8-bit count
	; while the rx line is low. If a known character (the 'a' or 'A', $61 or $41 in this case)
	; is input, the count will be proportional to the bit rate of the received datastream.
	;*********************************************************************************
uart_1_autobaud	
		snb	uart1_en		;Don't run if UART channel 1 is already running
		jmp	:autobaud1_done

	;	jnb	RTS_1,:autobaud1_done	; if RTS = false, so DTE has nothing to send
	;	jnb	DTR_1,:autobaud1_done	; if DTR = false, so DTE is not ready to receive
		setb	CTS_1			; CTS = true - ready to receive data
		setb	DSR_1			; DSR = true - ready to receive data
		bank	uart_1_tx
		snb	lo_detected_1		;low detected on previous pass, start count
		jmp	:start_count
		snb	rx_pin_1		;wait for rx line go low
		jmp	:autobaud1_done		;rx line is high, exit
		setb	lo_detected_1		;start counting during low pulse
:start_count	snb	rx_pin_1		;increment count only when rx line is low
		jmp	:test_result		;rising edge after low pulse, test result
:inc_low	inc	divisor_1		;increment count while low
		snz
		inc	divisor_1+1
		jmp	:autobaud1_done

:test_result
		mov	w,divisor_1		;divisor_1(16-bit) contains baudrate divisor
		mov	tx_divide_1,w
		mov	w,divisor_1+1
		mov	tx_divide_1+1,w
		setb	uart1_en		;enable uarts

		mov	w,#2^$ff		;Check to see if baud > 57.6Kbps
		add	w,tx_divide_1
		snc				;skip if less than 2
		jmp	:comp			;jump if greater than 2

:no_comp	setb	speed_1			; then set speed_1 to indicate fast UART rate
		jmp	:setup_rx_115

:comp		dec	divisor_1		;trim divisor_1
		dec	tx_divide_1		;trim tx_divide_1
		not	tx_divide_1		;compliment tx_divide_1
		not	tx_divide_1+1		;compliment tx_divide_1+1

:setup_rx
		bank	uart_1_tx
		clc
		mov	w,>>divisor_1+1		;reload 1/2 bit period divisor_1(16-bit)
		bank    uart_rx	
		mov	rx_divide_1+1,w
		bank	uart_1_tx
		mov	w,>>divisor_1
		bank    uart_rx	
		mov	rx_divide_1,w
		mov     w,#9                    ;ready 9 bits
		mov     rx_count_1,w            ;renew bit count
		not	rx_divide_1		;compliment rx_divide_1
		not	rx_divide_1+1		;compliment rx_divide_1+1
		jmp	:autobaud1_done

:setup_rx_115
		bank    uart_rx	
		mov     w,#9                    ;ready 9 bits
		mov     rx_count_1,w            ;renew bit count
		mov	rx_divide_1,#1
:autobaud1_done
		retp

	;*********************************************************************************
	; Virtual Peripheral: UART Autobaud (channel 2)
	;
	; This routine adds Autobaud capability to the UARTs by incrementing an 8-bit count
	; while the rx line is low. If a known character (the 'a' or 'A', $61 or $41 in this case)
	; is input, the count will be proportional to the bit rate of the received datastream.
	;*********************************************************************************
IF SX_TYPE=1
uart_2_autobaud
		snb	uart2_en		;Don't run if UART channel 1 is already running
		jmp	:autobaud2_done

	;	jnb	RTS_2,:autobaud2_done	; if RTS = false, so DTE has nothing to send
	;	jnb	DTR_2,:autobaud2_done	; if DTR = false, so DTE is not ready to receive
		setb	CTS_2			; CTS = true - ready to receive data
		setb	DSR_2			; DSR = true - ready to receive data
		bank	uart_2_tx
		snb	lo_detected_2		;low detected on previous pass, start count
		jmp	:start_count
		snb	rx_pin_2		;wait for rx line go low
		jmp	:autobaud2_done		;rx line is high, exit
		setb	lo_detected_2		;start counting during low pulse
:start_count	snb	rx_pin_2		;increment count only when rx line is low
		jmp	:test_result		;rising edge after low pulse, test result
:inc_low	inc	divisor_2		;increment count while low
		snz
		inc	divisor_2+1
		jmp	:autobaud2_done

:test_result
		mov	w,divisor_2		;divisor_1(16-bit) contains baudrate divisor
		mov	tx_divide_2,w
		mov	w,divisor_2+1
		mov	tx_divide_2+1,w
		setb	uart2_en		;enable uarts

		mov	w,#2^$ff		;Check to see if baud > 57.6Kbps
		add	w,tx_divide_2
		snc				;skip if less than 2
		jmp	:comp			;jump if greater than 2

:no_comp	setb	speed_2			; then set speed_1 to indicate fast UART rate
		jmp	:setup_rx_115

:comp		dec	divisor_2		;trim divisor_1
		dec	tx_divide_2		;trim tx_divide_1
		not	tx_divide_2		;compliment tx_divide_1
		not	tx_divide_2+1		;compliment tx_divide_1+1

:setup_rx
		bank	uart_2_tx
		clc
		mov	w,>>divisor_2+1		;reload 1/2 bit period divisor_1(16-bit)
		bank    uart_rx	
		mov	rx_divide_2+1,w
		bank	uart_2_tx
		mov	w,>>divisor_2
		bank    uart_rx	
		mov	rx_divide_2,w
		mov     w,#9                    ;ready 9 bits
		mov     rx_count_2,w            ;renew bit count
		not	rx_divide_2		;compliment rx_divide_1
		not	rx_divide_2+1		;compliment rx_divide_1+1
		jmp	:autobaud2_done

:setup_rx_115
		bank    uart_rx	
		mov     w,#9                    ;ready 9 bits
		mov     rx_count_2,w            ;renew bit count
		mov	rx_divide_2,#1
:autobaud2_done
		retp
ENDIF
ENDIF

IFNDEF INLINE_VP
uart_1_transmit_fast
		jmp	_uart_1_transmit_fast
IF SX_TYPE = 1
uart_2_transmit_fast
		jmp	_uart_2_transmit_fast
ENDIF

	;*********************************************************************************
	; VP: uart_1_transmit
	; 110 - 57.6Kbaud UART with RTS/CTS handshake
	; ** Part of the Interrupt Service Routine **
	;*********************************************************************************
uart_1_transmit
		UART1_TRANSMIT
		retp				; Return to the interrupt handler

	;*********************************************************************************
	; VP: uart_2_transmit
	; 110 - 57.6Kbaud UART with RTS/CTS handshake
	; ** Part of the Interrupt Service Routine **
	;*********************************************************************************
IF SX_TYPE = 1		;only compile if target is SX52

uart_2_transmit
		UART2_TRANSMIT
		retp				; Return to the interrupt handler

ENDIF
	;*********************************************************************************
	; VP: uart_1_transmit_fast
	; 57.6K - 115.2Kbaud UART with RTS/CTS handshake
	; ** Part of the Interrupt Service Routine **
	;*********************************************************************************
_uart_1_transmit_fast
		UART1_TRANSMIT_FAST
		retp				; Return to the interrupt handler

IF SX_TYPE=1
	;*********************************************************************************
	; VP: uart_2_transmit_fast
	; 57.6K - 115.2Kbaud UART with RTS/CTS handshake
	; ** Part of the Interrupt Service Routine **
	;*********************************************************************************
_uart_2_transmit_fast
		UART2_TRANSMIT_FAST
		retp				; Return to the interrupt handler
ENDIF
ENDIF

;*****************************************************************************************
END		;End of program code
;*****************************************************************************************

