; *****************************************************************************************
; 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
;*****************************************************************************************
file: /Techref/scenix/lib/io/osi2/serial/DUART_0412_JB.SRC, 60KB, , updated: 2004/11/19 15:12, local time: 2025/1/27 17:22,
|
| ©2025 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://linistepper.com/techref/scenix/lib/io/osi2/serial/DUART_0412_JB.SRC"> scenix lib io osi2 serial DUART_0412_JB</A> |
Did you find what you needed?
|