; *****************************************************************************************
; 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
;
;*****************************************************************************************
; 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.
;
;*****************************************************************************************
; SX28 Definitions
; DEVICE pins28,pages4,banks8,oschs
; DEVICE turbo,stackx,optionx
; ID '28DUART' ;program ID label
SX_TYPE = 1 ;0=SX28, 1=SX48/52
; SX52 Definitions
DEVICE sx52,oschs,turbo,stackx,optionx,sync
ID '52DUART' ;program ID label
RESET reset_entry ;set reset/boot address
; FREQ 50000000 ;oscillator frequency
;*****************************************************************************************
; 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
;*****************************************************************************************
; 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
RTCC_prescaler = 1 ;to change: see OPTION command later on
xtal = 50000000
;*****************************************************************************************
; 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
;*****************************************************************************************
; 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
lo_detected_1 equ flags.4
speed_1 equ flags.5
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
;*****************************************************************************************
; Odd RAM Bank Register definitions
;*****************************************************************************************
;*********************************************************************************
; Bank 1
;*********************************************************************************
org $10
;*********************************************************************************
; Bank 3
;*********************************************************************************
org $30
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 5
;*********************************************************************************
org $50
uart_2_tx = $ ;UART 2 TX bank
;divisor_2 ds 1
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 1 ;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 7
;*********************************************************************************
org $70
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 1 ;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 9
;*********************************************************************************
org $90
uart_tx_ring_1 = $ ;UART channel 1 TX ring buffers
tx_ring_1 ds tx_ring_size_1
;*********************************************************************************
; Bank B
;*********************************************************************************
org $b0
uart_rx_ring_1 = $ ;UART channel 1 RX ring buffers
rx_ring_1 ds rx_ring_size_1
;*********************************************************************************
; Bank D
;*********************************************************************************
org $d0
uart_tx_ring_2 = $ ;UART channel 2 TX ring buffers
tx_ring_2 ds tx_ring_size_2
;*********************************************************************************
; Bank F
;*********************************************************************************
org $f0
uart_rx_ring_2 = $ ;UART channel 2 RX ring buffers
rx_ring_2 ds rx_ring_size_2
;*****************************************************************************************
; Even RAM Bank Register definitions
;*****************************************************************************************
;*********************************************************************************
; Bank 2
;*********************************************************************************
org $20
;*********************************************************************************
; Bank 4
;*********************************************************************************
org $40
;*********************************************************************************
; Bank 6
;*********************************************************************************
org $60
;*********************************************************************************
; Bank 8
;*********************************************************************************
org $80
;*********************************************************************************
; Bank A
;*********************************************************************************
org $a0
;*********************************************************************************
; Bank C
;*********************************************************************************
org $c0
;*********************************************************************************
; Bank E
;*********************************************************************************
org $e0
;*****************************************************************************************
; 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
;*****************************************************************************************
; Interrupt Service Routine
;*****************************************************************************************
ORG 0 ;interrupt starts at 0h
interrupt
;*********************************************************************************
; 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.
;*********************************************************************************
autobaud1 snb uart1_en ;Don't run if UART channel 1 is already running
jmp :autobaud_done
; jnb RTS_1,:autobaud_done ; if RTS = false, so DTE has nothing to send
; jnb DTR_1,:autobaud_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 :autobaud_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 :autobaud_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 :autobaud_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
:autobaud_done
;*********************************************************************************
; 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
;
;
;*********************************************************************************
UART1 jnb uart1_en,UART2 ;execute UART channel 1 only when enabled
jb speed_1,:fast_1
call @uart_1_transmit
skip
:fast_1 call @uart_1_transmit_115
UART2
IF SX_TYPE = 1 ;only compile if target is SX52
jnb uart2_en,uarts_done ;execute UART channel 2 only when enabled
call @uart_2_transmit
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
mov w,#%00010000 ;point FSR to odd banks
mov fsr,w
ENDIF
call @uarts_init ;initialise the uarts
mov !option,#%10011111 ;enable rtcc interrupt
jmp @main
;*****************************************************************************************
; MAIN PROGRAM CODE
;*****************************************************************************************
org $200
main
IF SX_TYPE = 0 ;SX28
jmp main28
ELSE ;SX52
jmp main52
ENDIF
;*********************************************************************************
; Main Program Loop
;*********************************************************************************
IF SX_TYPE = 0 ;only compile if target is SX28
main28 jnb uart1_en,$
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
call @send_byte_1 ;echo byte back to sender on channel 1
jmp main_loop ;return
ELSE
main52 jnb uart1_en,$ ;wait for autobaud on UART channel 1
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
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
main_loop
snb cs ;wait for chip select active(low)
jmp main_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,'SX DUART Demo 04/12/99',0
_ch1 dw 13,10,13,10,'Channel 1',0
_ch2 dw 13,10,13,10,'Channel 2',0
_cr dw 13,10,0
_prompt dw 13,10,'>',0
_hi dw 13,10,'Hi!',0
_space dw ' ',0
;*****************************************************************************************
; SUBROUTINES
;*****************************************************************************************
; Function: uarts_init
; Initialise the UARTs
;*********************************************************************************
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
;*********************************************************************************
; 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
;*****************************************************************************************
; Jump Table for Page 2
;*****************************************************************************************
org $600
uart_1_transmit_115
jmp uart_1_transmit_115_
;*********************************************************************************
; VP: uart_1_transmit
; 110 - 57.6Kbaud UART with RTS/CTS handshake
; ** Part of the Interrupt Service Routine **
;*********************************************************************************
uart_1_transmit
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
retp ; Return to the interrupt handler
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
retp ; Return to the interrupt handler
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
retp ; Return to the interrupt handler
: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
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
bank uart_2_tx ;switch to serial register bank
decsz tx_divide_2 ;ready to transmit bit?
jmp uart_2_receive ;else, exit
mov w,#24 ;reload 9600bps period constant
mov tx_divide_2,w
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,#36
mov rx_divide_2,w
:rxbit decsz rx_divide_2 ;ready to transmit bit?
retp ; Return to the interrupt handler
mov w,#24 ;reload 1 bit period constant
mov rx_divide_2,w
: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
retp ; Return to the interrupt handler
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
retp ; Return to the interrupt handler
: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
retp ; Return to the interrupt handler
ENDIF
;*********************************************************************************
; VP: uart_1_transmit_115
; 57.6K - 115.2Kbaud UART with RTS/CTS handshake
; ** Part of the Interrupt Service Routine **
;*********************************************************************************
uart_1_transmit_115_
bank uart_1_tx ;switch to serial register bank
decsz tx_divide_1 ;ready to transmit bit?
jmp uart_1_rx_115
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_115 ;yes, go to :transmit_out
; jnb DTR_1,uart_1_rx_115
; jnb RTS_1,uart_1_rx_115
: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_115 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?
retp ; Return to the interrupt handler
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
retp ; Return to the interrupt handler
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
retp ; Return to the interrupt handler
: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
retp ; Return to the interrupt handler
;*****************************************************************************************
END ;End of program code
;*****************************************************************************************
file: /Techref/scenix/lib/io/osi2/serial/Duart_0412.SRC, 48KB, , updated: 2002/1/15 15:43, local time: 2025/1/14 04:01,
|
| ©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.SRC"> scenix lib io osi2 serial Duart_0412</A> |
Did you find what you needed?
|