Peter Verkaik says:
Unable to find an existing uart for 8 databits + parity I wrote one myself. I saw a few posts on the basic stamp forum about this so I share this. It is an assembly source.The program itself contains more than just the uart as it is written as a template to be extended for applications.
;*****************************************************************************************
; ELX1 board, co-procesor template
; ----------------------------------------------------------------------------------------
;
; version 0.9 beta
; december 9, 2004
; compile with SASM
;
; Author: Peter Verkaik
; Email: peterverkaik@boselectro.nl
;
; This program is a SX28 template providing a 2-wire host interface (halfduplex serial line +
; write enable line used for synchronization), and a 2-wire serial interface for terminal or
; other serial device. This terminal interface can be configured at runtime for:
; TX/RX with or without xon/xoff handshake, TX only with CTS handshake, RX only with RTS handshake
; four selectable baudrates (default 1200,2400,9600,19200)
; 7 or 8 databits
; parity odd, even or none
; powerup default is TX/RX without handshake, 1200 baud, 8 databits, no parity
; Port A is dedicated to the host and terminal.
; Ports B and C are not used and are available for the application.
; Code pages 2 and 3 are empty
; Ram banks 4, 5, 6 and 7 are empty
; 5 free global ram bank bytes
; four 8-byte buffers for terminal and host uarts
; Host command interface is expandable for up to 128 commands
;
;*****************************************************************************************
; Connection diagram. Both wires should have 4.7k-10k pullup resistor.
;
; MCU co-processor (this device)
; -------+ +------------------------+
; | | |
; pinWE o------o RA.1 = /WE |
; pinDQ o------o RA.0 = DQ |
; | | RC.0-RC.7 o-- application
; -------+ | RB.0-RB.7 o-- application
; | |
; | RA.2 o-- terminal TX (or RTS in case of RX/RTS)
; | RA.3 o-- terminal RX (or CTS in case of TX/CTS)
; +------------------------+
;
;*****************************************************************************************
; Communication protocol between this device and the host MCU.
;
; ------+ +-+ +------+-----
; pinWE S0 | S1 | S2 | | S3 | S4 | S5 | S0
; +-----+-------------------+ +-----+-----------------------+
; <--- pulled low by MCU ---> <- pulled low by this device ->
;
; ------+ +-+ +------+-----
; pinDQ <cmd> <param1>...<paramN> <rsp> <data1>...<dataN>
; - hiZ +- - - - - input - - - - -+-+ - - - - output - - - - - - -+ - - hiZ - -
;
; State S0: idle
; State S1: receive command byte
; State S2: receive parameter bytes
; State S3: transmit response byte
; State S4: transmit data bytes
; State S5: finalize
;
; Each command sequence starts at the H->L transition on pinWE.
; This may be follwed by <cmd> and <param> bytes.
; A L->H transition on pinWE (from MCU) is ALWAYS follewed by
; a H->L transition and <rsp>.
; <data> bytes are optional and determined by command or status.
; The command sequence stops after this device releases pinWE.
;
;-----------------------------------------------------------------------------------------
; program modules
;-----------------------------------------------------------------------------------------
device SX28,oschs1,turbo,stackx,optionx ;sx device options
irc_cal IRC_SLOW
id 'ELX1TMPL' ;Elx1 board template
reset reset_entry ;set reset vector
;select one of these freq
;freq 4_000_000
;freq 10_000_000
freq 20_000_000
;select according to freq
;RESONATOR equ 4
;RESONATOR equ 10
RESONATOR equ 20
ERROR p2w 'Assembled assuming a ' RESONATOR ' MHz oscillator'
; The host interface (pins ra.0 and ra.1) is used to accept commands and data
; from a host mcu and delivers status and data to that mcu.
; The terminal interface (pins ra.2 and ra.3) is used to accept key input from a terminal(program)
; and delivers display/control characters to that terminal(program).
;-----------------------------------------------------------------------------------------
; uart constants
;-----------------------------------------------------------------------------------------
IF RESONATOR = 20
;128*1200=64*2400=32*4800=16*9600=8*19200
;uartfs=128*1200=153600
;int_period=20000000/153600=130
uartfs equ 153600 ; uart's basic frequency 128*1200 = 153600
int_period equ 130 ; 6.5uS used as seed for uart baud rate.
THREADS equ 1
ENDIF
IF RESONATOR = 10
;64*1200=32*2400=16*4800=8*9600=4*19200
;uartfs=64*1200=76800
;int_period=10000000/76800=130
uartfs equ 76800 ; uart's basic frequency 64*1200 = 76800
int_period equ 130 ; 13uS used as seed for uart baud rate.
THREADS equ 1
ENDIF
IF RESONATOR = 4
;32*1200=16*2400=8*4800=4*9600=2*19200
;uartfs=32*1200=38400
;int_period=4000000/38400=104
uartfs equ 38400 ; uart's basic frequency 32*1200 = 38400
int_period equ 104 ; 26uS used as seed for uart baud rate.
THREADS equ 1
ENDIF
mscount equ ((2000*RESONATOR/int_period)+1)/2 ;number of int cycles for 1 msec
ERROR p2w '# of cycles per interrupt = ' mscount
XON equ 17 ;XON character (ctrl-Q)
XOFF equ 19 ;XOFF character (ctrl-S)
;The following baudrates are available
baud0 equ 1200
baud1 equ 2400
baud2 equ 4800
baud3 equ 9600
baud4 equ 19200
; 20MHz 10MHz 4MHz
divide0 equ uartfs / (baud0 * THREADS) ; 128 64 32
StDelay0 equ divide0 + (divide0 / 2) ; 192 96 48
divide1 equ uartfs / (baud1 * THREADS) ; 64 32 16
StDelay1 equ divide1 + (divide1 / 2) ; 96 48 24
divide2 equ uartfs / (baud2 * THREADS) ; 32 16 8
StDelay2 equ divide2 + (divide2 / 2) ; 48 24 12
divide3 equ uartfs / (baud3 * THREADS) ; 16 8 4
StDelay3 equ divide3 + (divide3 / 2) ; 24 12 6
divide4 equ uartfs / (baud4 * THREADS) ; 8 4 2
StDelay4 equ divide4 + (divide4 / 2) ; 12 6 3
hostDivide equ divide3 ;baud used by host
hostStDelay equ StDelay3
;-----------------------------------------------------------------------------------------
; Pin definitions & Port assignments
;-----------------------------------------------------------------------------------------
;host and term pin definitions
hostDQ equ 0
hostWE equ 1
pinDQ equ ra.hostDQ ;host uart transmit/receive
pinWE equ ra.hostWE ;host write enable input
termTX equ 2
termRX equ 3
pinTX equ ra.termTX ;term uart transmit
pinRX equ ra.termRX ;term uart receive
; ra changes output level by changing direction register!! Never drive output high!!
RA_latch equ %00000000 ;port A output latch init 1=high 0=low
RA_tris equ %11111111 ;port A direction register 1=input 0=output
RA_plp equ %00000000 ;port A pull up register 1=off 0=on
RA_lvl equ %11111111 ;port A level register 1=ttl 0=cmos
; rb changes output level by changing direction register!! Never drive output high!!
RB_latch equ %00000000 ;port B output latch 1=high 0=low
RB_tris equ %11111111 ;port B direction register 1=input 0=output
RB_plp equ %00000000 ;port B pull up register 1=off 0=on
RB_lvl equ %00000000 ;port B level register 1=ttl 0=cmos
RB_st equ %11111111 ;port B schmitt trigger register 1=off 0=on
RB_wken equ %11111111 ;port B wake up enable register 1=off 0=on ** KEEP OFF
RB_wked equ %11111111 ;port B wake up edge register 1=neg 0=pos
; rc changes output level by changing direction register!! Never drive output high!!
RC_latch equ %00000000 ;port C output latch 1=high 0=low
RC_tris equ %11111111 ;port C direction register 1=input 0=output
RC_plp equ %00000000 ;port C pull up register 1=off 0=on
RC_lvl equ %11111111 ;port C level register 1=ttl 0=cmos
RC_st equ %11111111 ;port C schmitt trigger register 1=off 0=on
;-----------------------------------------------------------------------------------------
; Code Locations
;-----------------------------------------------------------------------------------------
CODEPAGE_0 equ $0
CODEPAGE_1 equ $200
CODEPAGE_2 equ $400
CODEPAGE_3 equ $600
;-----------------------------------------------------------------------------------------
; Data Memory address definitions
;-----------------------------------------------------------------------------------------
global_org equ $08
bank0_org equ $10
bank1_org equ $30
bank2_org equ $50
bank3_org equ $70
bank4_org equ $90
bank5_org equ $B0
bank6_org equ $D0
bank7_org equ $F0
;-----------------------------------------------------------------------------------------
; Global Register definitions
;-----------------------------------------------------------------------------------------
org global_org
temp ds 1 ;for temporary use (buffers, calculation)
ra_dir_buf ds 1 ;holds value for ra direction register
status_flags ds 1
cmd_invalid equ status_flags.0 ;set if command invalid
cmd_parameter_missing equ status_flags.1 ;set if missing parameter
buffer_overrun equ status_flags.2 ;set if any buffer overrun
term_parity_error equ status_flags.3 ;set if parity error
free_global ds 5
;-----------------------------------------------------------------------------------------
; RAM Bank Register definitions
;-----------------------------------------------------------------------------------------
; Bank 0 - host uart bank, timer bank
;---------------------------------------------------------------------------------
org bank0_org
hu_bank = $ ;host uart bank
hu_rx_count ds 1 ;number of bits to receive
hu_rx_divide ds 1 ;receive timing counter
hu_rx_byte ds 1 ;received byte
hu_tx_count ds 1 ;number of bits to transmit
hu_tx_divide ds 1 ;transmit timing counter
hu_tx_low ds 1 ;low byte to transmit
hu_tx_high ds 1 ;high byte to transmit
hu_head ds 1 ;hinib=tx, lonib=rx: index of next free entry in buf
hu_tail ds 1 ;hinib=tx, lonib=rx: index of next byte to read from buf
hu_num ds 1 ;hinib=tx, lonib=rx: number of bytes in buf
hu_rx_full equ hu_num.3 ; set if hu_rx full (num=8)
hu_tx_full equ hu_num.7 ; set if hu_tx full (num=8)
hu_flags ds 1
hu_rx_data equ hu_flags.0 ;set if hu_rx not empty
hu_rx_overrun equ hu_flags.1 ;set if hu_rx overruns
hu_rx_enable equ hu_flags.2 ;set to enable host uart receive
hu_rx_flag equ hu_flags.3 ;set if byte in hu_rx_byte
hu_tx_data equ hu_flags.4 ;set if hu_tx not empty
hu_tx_overrun equ hu_flags.5 ;set if hu_tx overruns
hu_tx_enable equ hu_flags.6 ;set to enable host uart transmit
hu_tx_flag equ hu_flags.7 ;not used
hu_state ds 1 ;host state
; b2-b0: state of host_command_task routine
pinWE_present equ hu_state.6 ; current state of pinWE
pinWE_past equ hu_state.7 ; past state of pinWE
hu_command ds 1 ;received host command
cmd_done equ hu_command.7 ; set if command finished
timer_bank = $
timer_int ds 1 ;increments every isr
timer_1m ds 1 ;decrements every msec
free_bank0 ds 1
; Bank 1 - term uart bank
;---------------------------------------------------------------------------------
org bank1_org
tu_bank = $ ;term uart bank
tu_rx_count ds 1 ;number of bits to receive
tu_rx_divide ds 1 ;receive timing counter
tu_rx_byte ds 1 ;received byte
tu_tx_count ds 1 ;number of bits to transmit
tu_tx_divide ds 1 ;transmit timing counter
tu_tx_low ds 1 ;low byte to transmit
tu_tx_high ds 1 ;high byte to transmit
tu_head ds 1 ;hinib=tx, lonib=rx: index of next free entry in buf
tu_tail ds 1 ;hinib=tx, lonib=rx: index of next byte to read from buf
tu_num ds 1 ;hinib=tx, lonib=rx: number of bytes in buf
tu_rx_full equ tu_num.3 ; set if tu_rx full (num=8)
tu_tx_full equ tu_num.7 ; set if tu_tx full (num=8)
tu_flags ds 1
tu_rx_data equ tu_flags.0 ;set if tu_rx not empty
tu_rx_overrun equ tu_flags.1 ;set if tu_rx overruns
tu_rx_enable equ tu_flags.2 ;set if transmitted XON, cleared if transmitted XOFF
tu_rx_flag equ tu_flags.3 ;set if byte in tu_rx_byte
tu_tx_data equ tu_flags.4 ;set if tu_tx not empty
tu_tx_overrun equ tu_flags.5 ;set if tu_tx overruns
tu_tx_enable equ hu_flags.6 ;set if received XON, cleared if received XOFF
tu_rx_parity equ tu_flags.7 ;received parity bit
tu_state ds 1 ;term state (not implemented)
tu_command ds 1 ;received term command (not implemented)
tu_config ds 1 ;terminal configuration
; b1-b0 = baud (1200,2400,9600,19200)
tu_rts equ tu_config.2 ; 1 for rts handshake (TX used as RTS)
tu_cts equ tu_config.3 ; 1 for cts handshake (RX used as CTS)
tu_xonxoff equ tu_config.4 ; 1 for xon/xoff handshake (inter-character delay must be 1 msec at 9600 baud)
tu_7bits equ tu_config.5 ; 1 for 7 databits (0 = 8 databits)
tu_parity_even equ tu_config.6 ; 1 for even parity
tu_parity_odd equ tu_config.7 ; 1 for odd parity
tu_char ds 1 ;save received byte here
tu_bits ds 1 ;number of bits (startbit+databits+paritybit+stopbit)
; set by command TM_SET_CONFIG (bits is 9, 10 or 11)
; Bank 2 - host uart buffers
;---------------------------------------------------------------------------------
org bank2_org
hu_buf_bank = $
hu_rx_buf ds 8
hu_tx_buf ds 8
; Bank 3 - term uart buffers
;---------------------------------------------------------------------------------
org bank3_org
tu_buf_bank = $
tu_rx_buf ds 8
tu_tx_buf ds 8
; Bank 4 -
;---------------------------------------------------------------------------------
org bank4_org
free_bank4 ds 16
; Bank 5 -
;---------------------------------------------------------------------------------
org bank5_org
free_bank5 ds 16
; Bank 6 -
;---------------------------------------------------------------------------------
org bank6_org
free_bank6 ds 16
; Bank 7 -
;---------------------------------------------------------------------------------
org bank7_org
free_bank7 ds 16
;-------------- end of ram variables -----------------------------------------------------
;*****************************************************************************************
org CODEPAGE_0
;*****************************************************************************************
;-----------------------------------------------------------------------------------------
; Interrupt Service Routine
;-----------------------------------------------------------------------------------------
interrupt jmp _interrupt ;1
; hooks for application
;---------------------------------------------------------------------------------
app_isr retp ;change to jmp @myapp_isr if any
app_main retp ;change to jmp @myapp_main if any
; jump table for template
;---------------------------------------------------------------------------------
enqueue_hu_tx jmp _enqueue_hu_tx ;store byte for output to host
enqueue_hu_rx jmp _enqueue_hu_rx ;store byte received from host
dequeue_hu_tx jmp _dequeue_hu_tx ;retrieve byte for output to host
dequeue_hu_rx jmp _dequeue_hu_rx ;retrieve byte received from host
enqueue_tu_tx jmp _enqueue_tu_tx ;store byte for output to terminal
enqueue_tu_rx jmp _enqueue_tu_rx ;store byte received from terminal
dequeue_tu_tx jmp _dequeue_tu_tx ;retrieve byte for output to terminal
dequeue_tu_rx jmp _dequeue_tu_rx ;retrieve byte received from terminal
host_input_task jmp _host_input_task ;receives host bytes into hu_rx_buf
host_output_task jmp _host_output_task ;transmit host bytes from hu_tx_buf
; get term baud parameters as determined by tu_config.0 and tu_config.1
;---------------------------------------------------------------------------------
tu_divide
mov w,tu_config
and w,#$03
add pc,w
retw divide0 ;1200
retw divide1 ;2400
retw divide3 ;9600
retw divide4 ;19200
tu_StDelay
mov w,tu_config
and w,#$03
add pc,w
retw StDelay0 ;1200
retw StDelay1 ;2400
retw StDelay3 ;9600
retw StDelay4 ;19200
; host command task
; Wait for a command received from host MCU,
; execute command and transmit results to host MCU.
;---------------------------------------------------------------------------------
STATE_IDLE equ 0
STATE_RECEIVE_COMMAND equ 1
STATE_RECEIVE_PARAMETER equ 2
STATE_TRANSMIT_RESPONSE equ 3
STATE_TRANSMIT_DATA equ 4
STATE_FINALIZE equ 5
host_command_task
call @checkPinWE ;check for edges
bank hu_bank
mov w,hu_state
and w,#$07
add pc,w
jmp hostIdle ;0
jmp hostReceiveCommand ;1
jmp hostReceiveParameter ;2
jmp hostTransmitResponse ;3
jmp hostTransmitData ;4
jmp hostFinalize ;5
jmp hostIdle ;6 failsafe
jmp hostIdle ;7 failsafe
hostIdle
clrb hu_rx_enable
clrb hu_tx_enable
setb ra_dir_buf.hostDQ
setb ra_dir_buf.hostWE
retp
hostReceiveCommand
sb hu_rx_data ;any received data?
retp ;no
call dequeue_hu_rx
mov hu_command,w ;this is command
mov w,#$7F
snb cmd_done
mov hu_command,w ;make command $7F if b7 is set
clrb cmd_invalid
clrb term_parity_error
setb cmd_parameter_missing ;cleared by function
call @host_command_handler ;execute command
bank hu_bank
inc hu_state ;move to next state
retp
hostReceiveParameter
sb hu_rx_data ;any received data?
retp ;no
jmp @host_command_handler ;execute command
hostTransmitResponse
clrb ra_dir_buf.hostWE ;this device pulls pinWE low
mov w,hu_flags
and w,#$22 ;extract overrun flags
sz
setb buffer_overrun
bank tu_bank
mov w,tu_flags
and w,#$22 ;extract overrun flags
sz
setb buffer_overrun
mov w,status_flags ;transmit status flags
call enqueue_hu_tx
clrb hu_rx_overrun
clrb hu_tx_overrun
inc hu_state ;move to next state
bank tu_bank
clrb tu_rx_overrun
clrb tu_tx_overrun
clrb buffer_overrun
retp
hostTransmitData
snb hu_tx_data ;output buffer empty?
retp ;no
call @host_command_handler ;get next data
bank hu_bank
snb cmd_done ;all done?
inc hu_state ;yes, move to next state
retp
hostFinalize
snb hu_tx_data ;output buffer empty
retp ;no
test hu_tx_count ;transmitter ready?
sz
retp ;no
clrb hu_tx_enable ;disable transmitter
setb ra_dir_buf.hostWE ;release pinWE
and hu_state,#$C0 ;wait for new command
retp
;-------------- start of the Interrupt Service Routines ----------------------------------
_interrupt
; Update timers
;---------------------------------------------------------------------------------
inc_timer
bank timer_bank
inc timer_int ;increments every isr
cjne timer_int,#mscount,:it_done ; isr cycles for 1 msec
clr timer_int
dec timer_1m ;decrements every msec
:it_done
inc_timer_done
call app_isr ;call application isr routines
; Update ra with buffered data
;---------------------------------------------------------------------------------
mov w,ra_dir_buf
mov !ra,w
; Run uarts
;---------------------------------------------------------------------------------
hostVP
bank hu_bank
;host uart receive ;receive only when enabled
jnb hu_rx_enable,:hu_rx_done
:receive
sb pinDQ ; get current rx bit
clc
snb pinDQ
stc
test hu_rx_count ; currently receiving byte?
sz
jmp :hur1 ; if so, jump ahead
mov w,#9 ; in case start, ready 9 bits
sc ; skip ahead if not start bit
mov hu_rx_count,w ; it is, so renew bit count
mov w,#hostStDElay
mov hu_rx_divide,w
:hur1
decsz hu_rx_divide ; middle of next bit?
jmp :hu_rx_done
dec hu_rx_count ; last bit?
sz ; if not
rr hu_rx_byte ; then save bit
jnz :hur2 ; and exit
setb hu_rx_flag
:hur2
mov w,#hostDivide
mov hu_rx_divide,w
:hu_rx_done
;host uart transmit ;transmit only when enabled
jnb hu_tx_enable,:hu_tx_done
:transmit
decsz hu_tx_divide ; only execute the transmit routine
jmp :hu_tx_done
mov w,#hostDivide
mov hu_tx_divide,w
test hu_tx_count ; are we sending?
snz
jmp :hu_tx_done
clc ; yes, ready stop bit
rr hu_tx_high ; and shift to next bit
rr hu_tx_low
dec hu_tx_count ; decrement bit counter
snb hu_tx_low.6 ; output next bit
clrb ra_dir_buf.hostDQ ; update term_port_buf state
sb hu_tx_low.6 ; (pin change state occurs next interrupt)
setb ra_dir_buf.hostDQ
:hu_tx_done
termVP
bank tu_bank
;term uart receive
jb tu_cts,:term_rxdone ;only receive if rx is not cts
:receive
sb pinRX ; get current rx bit
clc
snb pinRX
stc
test tu_rx_count ; currently receiving byte?
sz
jmp :term_rx1 ; ;if so, jump ahead
mov w,--tu_bits ; in case start, ready bits
sc ; skip ahead if not start bit
mov tu_rx_count,w ; it is, so renew bit count
call tu_StDelay
mov tu_rx_divide,w
:term_rx1
decsz tu_rx_divide ; middle of next bit?
jmp :term_rxdone
dec tu_rx_count ; last bit?
jz :term_rx2 ; if not
rr tu_rx_byte ; then save bit
movb tu_rx_parity,C ; and save b0
jmp :term_rx3 ; and exit
:term_rx2
movb C,tu_rx_parity ;get last saved b0
movb tu_rx_parity,tu_rx_byte.7 ;save possible parity bit
sb tu_bits.1
rr tu_rx_byte ;adjust for 7 databits + no parity (tu_bits=9)
sb tu_bits.1
rr tu_rx_byte
snb tu_bits.0
rl tu_rx_byte ;adjust for 8 databits + parity (tu_bits=11)
snb tu_7bits
clrb tu_rx_byte.7 ;clear b7 for 7 databits
mov w,tu_rx_byte ;save byte to allow receive another byte
mov tu_char,w ; while processing this one
setb tu_rx_flag ;mark byte received
:term_rx3
call tu_divide
mov tu_rx_divide,w
:term_rxdone
;term uart transmit
jb tu_rts,:term_txdone ;only transmit if tx is not rts
:transmit
decsz tu_tx_divide ; only execute the transmit routine
jmp :term_txdone
call tu_divide
mov tu_tx_divide,w
test tu_tx_count ; are we sending?
snz
jmp :term_txdone
clc ; yes, ready stop bit
rr tu_tx_high ; and shift to next bit
rr tu_tx_low
dec tu_tx_count ; decrement bit counter
snb tu_tx_low.5 ; output next bit
clrb ra_dir_buf.termTX ; update term_port_buf state
sb tu_tx_low.5 ; (pin change state occurs next interrupt)
setb ra_dir_buf.termTX
:term_txdone
; Set Interrupt Rate
;---------------------------------------------------------------------------------
isr_end
mov w,#(-int_period)&$FF ; refresh RTCC on return
retiw ; return from the interrupt
;-------------- end of the Interrupt Service Routines ------------------------------------
;RESET VECTOR Program execution begins here on power-up or after a reset
;---------------------------------------------------------------------------------
reset_entry
; Initialise all port configuration
;---------------------------------------------------------------------------------
M_WKED equ $0A
M_WKEN equ $0B
M_ST equ $0C
M_LVL equ $0D
M_PLP equ $0E
M_TRIS equ $0F
mov m,#M_WKED ; point mode to WKED
mov !rb,#RB_wked
mov m,#M_WKEN ; point mode to WKEN
mov !rb,#RB_wken
mov m,#M_ST ; point mode to ST
mov !rc,#RC_st
mov !rb,#RB_st
mov m,#M_LVL ; point mode to LVL
mov !rc,#RC_lvl
mov !rb,#RB_lvl
mov !ra,#RA_lvl
mov m,#M_PLP ; point mode to PLP
mov !rc,#RC_plp
mov !rb,#RB_plp
mov !ra,#RA_plp
mov rc,#RC_latch ; init latches
mov rb,#RB_latch
mov ra,#RA_latch
mov m,#M_TRIS ; point mode to TRIS
mov !rc,#RC_tris
mov !rb,#RB_tris
mov !ra,#RA_tris
; Clear all Data RAM locations
;---------------------------------------------------------------------------------
mov fsr,#global_org
:zero_global
clr indf
inc fsr
cjne fsr,#$10,:zero_global
:zero_ram
clr indf
inc fsr
jz :zero_end
setb fsr.4
jmp :zero_ram
:zero_end
; Initialize registers
;---------------------------------------------------------------------------------
mov ra_dir_buf,#$FF
bank hu_bank
mov hu_state,#$C0 ;pin levels high
bank tu_bank
setb tu_tx_enable ;enable transmit
setb tu_rx_enable ;enable receive
mov tu_bits,#10 ;set correct bits for startup (8N1,1200)
; Setup option register.
;---------------------------------------------------------------------------------
RTCC_ON equ %10000000 ;Enables RTCC at address $01 (RTW hi)
;*WREG at address $01 (RTW lo) by default
RTCC_ID equ %01000000 ;Disables RTCC edge interrupt (RTE_IE hi)
;*RTCC edge interrupt (RTE_IE lo) enabled by default
RTCC_INC_EXT equ %00100000 ;Sets RTCC increment on RTCC pin transition (RTS hi)
;*RTCC increment on internal instruction (RTS lo) is defalut
RTCC_FE equ %00010000 ;Sets RTCC to increment on falling edge (RTE_ES hi)
;*RTCC to increment on rising edge (RTE_ES lo) is default
RTCC_PS_ON equ %00000000 ;Assigns prescaler to RTCC (PSA lo)
RTCC_PS_OFF equ %00001000 ;Assigns prescaler to WDT (PSA hi)
PS_000 equ %00000000 ;RTCC = 1:2, WDT = 1:1
PS_001 equ %00000001 ;RTCC = 1:4, WDT = 1:2
PS_010 equ %00000010 ;RTCC = 1:8, WDT = 1:4
PS_011 equ %00000011 ;RTCC = 1:16, WDT = 1:8
PS_100 equ %00000100 ;RTCC = 1:32, WDT = 1:16
PS_101 equ %00000101 ;RTCC = 1:64, WDT = 1:32
PS_110 equ %00000110 ;RTCC = 1:128, WDT = 1:64
PS_111 equ %00000111 ;RTCC = 1:256, WDT = 1:128
mov w,#RTCC_PS_OFF ;setup option register
mov !option,w
; Mainloop
;---------------------------------------------------------------------------------
mainloop
call host_input_task ;handle host data input
call host_output_task ;handle host data output
call host_command_task ;handle host command
call @term_input_task ;handle keypad input
call @term_output_task ;handle display output
call app_main ;call application mainloop
jmp mainloop
;put w into hu_tx_buf
;---------------------------------------------------------------------------------
_enqueue_hu_tx
bank hu_bank
snb hu_tx_full
setb hu_tx_overrun
snb hu_tx_full
retp
mov temp,w
mov w,<>hu_head
and w,#$0F
mov fsr,w ;calculate buffer address
mov w,#hu_tx_buf
add fsr,w
mov w,temp ;store received byte
mov indf,w
bank hu_bank
add hu_num,#16 ;adjust byte count
add hu_head,#16
clrb hu_head.7 ;wrap around head
setb hu_tx_data ;mark holding data
retp
;put w into hu_rx_buf
;---------------------------------------------------------------------------------
_enqueue_hu_rx
bank hu_bank
snb hu_rx_full
setb hu_rx_overrun
snb hu_rx_full
retp
mov temp,w
mov w,hu_head
and w,#$0F
mov fsr,w ;calculate buffer address
mov w,#hu_rx_buf
add fsr,w
mov w,temp ;store received byte
mov indf,w
bank hu_bank
add hu_num,#1 ;adjust byte count
add hu_head,#1
clrb hu_head.3 ;wrap around head
setb hu_rx_data ;mark holding data
retp
;get w from hu_tx_buf
;---------------------------------------------------------------------------------
_dequeue_hu_tx
bank hu_bank
sb hu_tx_data
retp
mov w,<>hu_tail ;get next byte from buffer
and w,#$0F
mov fsr,w
mov w,#hu_tx_buf
add fsr,w
mov w,indf
mov temp,w
bank hu_bank
sub hu_num,#16 ;adjust byte count
mov w,<>hu_num
and w,#$0F
snz
clrb hu_tx_data
add hu_tail,#16
clrb hu_tail.7 ;wrap around tail
mov w,temp
retp
;get w from hu_rx_buf
;---------------------------------------------------------------------------------
_dequeue_hu_rx
bank hu_bank
sb hu_rx_data ;test if any byte in buffer
retp ;if not then exit
mov w,hu_tail ;get next byte from buffer
and w,#$0F
mov fsr,w
mov w,#hu_rx_buf
add fsr,w
mov w,indf
mov temp,w
bank hu_bank
sub hu_num,#1 ;adjust byte count
mov w,hu_num
and w,#$0F
snz
clrb hu_rx_data ;mark buffer empty
add hu_tail,#1
clrb hu_tail.3 ;wrap around tail
mov w,temp
retp
;put w into tu_tx_buf
;---------------------------------------------------------------------------------
_enqueue_tu_tx
bank tu_bank
snb tu_tx_full
setb tu_tx_overrun
snb tu_tx_full
retp
mov temp,w
mov w,<>tu_head
and w,#$0F
mov fsr,w ;calculate buffer address
mov w,#tu_tx_buf
add fsr,w
mov w,temp ;store received byte
mov indf,w
bank tu_bank
add tu_num,#16 ;adjust byte count
add tu_head,#16
clrb tu_head.7 ;wrap around head
setb tu_tx_data ;mark holding data
retp
;put w into tu_rx_buf
;---------------------------------------------------------------------------------
_enqueue_tu_rx
bank tu_bank
snb tu_rx_full
setb tu_rx_overrun
snb tu_rx_full
retp
mov temp,w
mov w,tu_head
and w,#$0F
mov fsr,w ;calculate buffer address
mov w,#tu_rx_buf
add fsr,w
mov w,temp ;store received byte
mov indf,w
bank tu_bank
add tu_num,#1 ;adjust byte count
add tu_head,#1
clrb tu_head.3 ;wrap around head
setb tu_rx_data ;mark holding data
retp
;get w from tu_tx_buf
;---------------------------------------------------------------------------------
_dequeue_tu_tx
bank tu_bank
sb tu_tx_data
retp
mov w,<>tu_tail ;get next byte from buffer
and w,#$0F
mov fsr,w
mov w,#tu_tx_buf
add fsr,w
mov w,indf
mov temp,w
bank tu_bank
sub tu_num,#16 ;adjust byte count
mov w,<>tu_num
and w,#$0F
snz
clrb tu_tx_data
add tu_tail,#16
clrb tu_tail.7 ;wrap around tail
mov w,temp
retp
;get w from tu_rx_buf
;---------------------------------------------------------------------------------
_dequeue_tu_rx
bank tu_bank
sb tu_rx_data ;test if any byte in buffer
retp ;if not then exit
mov w,tu_tail ;get next byte from buffer
and w,#$0F
mov fsr,w
mov w,#tu_rx_buf
add fsr,w
mov w,indf
mov temp,w
bank tu_bank
sub tu_num,#1 ;adjust byte count
mov w,tu_num
and w,#$0F
snz
clrb tu_rx_data ;mark buffer empty
add tu_tail,#1
clrb tu_tail.3 ;wrap around tail
mov w,temp
retp
; host input task
;---------------------------------------------------------------------------------
_host_input_task
bank hu_bank
sb hu_rx_enable ;may we receive?
retp ;no
sb hu_rx_flag ;byte received?
retp ;no
mov w,hu_rx_byte
clrb hu_rx_flag
jmp enqueue_hu_rx ;store byte in buffer
; host output task
;---------------------------------------------------------------------------------
_host_output_task
bank hu_bank
sb hu_tx_enable ;may we transmit?
retp ;no
sb hu_tx_data ;byte to transmit?
retp ;no
test hu_tx_count ;transmitter ready?
sz
retp ;no
call dequeue_hu_tx ;get byte to transmit
not w ;ready bits (inverse logic)
mov hu_tx_high,w ;store data byte
setb hu_tx_low.7 ;set up start bit
mov w,#10 ;1 start + 8 data + 1 stop bit
mov hu_tx_count,w ;VP starts transmitting
retp
;-------------- codepage 0 end -----------------------------------------------------------
;*****************************************************************************************
org CODEPAGE_1
;*****************************************************************************************
jmp $ ;generates error if code above crosses page boundary
term_input_task jmp _term_input_task ;receives term bytes into tu_rx_buf
term_output_task jmp _term_output_task ;transmit term bytes from tu_tx_buf
; set parity and stopbits for byte in tu_tx_high (just about to transmit)
;---------------------------------------------------------------------------------
tu_set_parity
snb tu_7bits ;8 databits?
clrb tu_tx_high.7 ;no, ready stop bit
mov w,tu_tx_high
call even_parity
:odd
jnb tu_parity_odd,:even
movb C,/temp.0
jmp :parity
:even
jnb tu_parity_even,:exit
movb C,temp.0
:parity
sb tu_7bits ;7 databits?
retp ;no
movb tu_tx_high.7,C ;move parity into b7
:exit
clc ;ready stop bit
retp
; calculate even parity for w
; result in temp.0
;---------------------------------------------------------------------------------
even_parity
mov temp,w
;calculation code by John Payson, as found on sxlist.com
;This routine will leave the parity of temp in temp.0
;while blenderizing most of the rest of temp
mov w,<>temp ; temp = abcdefgh
xor temp,w
mov w,>>temp
xor temp,w
; at this point, the parity for half the bits
; (a, b, e, and f) is in bit 2 of temp, and the
; parity for the other half (bits c, d, g, and h)
; is in bit 0 of temp.
snb temp.2 ; if the parity of (a,b,e,f) is 0,
; then the parity of (a,b,c,d,e,f,g,h)
; is equal to the parity of (c,d,g,h)...
; which is already in bit 0, so skip ahead.
inc temp ; otherwise, the parity of (a,b,e,f) is 1,
; so the parity of (a,b,c,d,e,f,g,h) is
; NOT equal to the parity of (c,d,g,h).
; invert bit 0.
; at this point, bit 0 contains the parity of
; (a,b,c,d,e,f,g,h).
retp
; term output handshake off (inform remote deivce to stop transmitting)
; either RTS (on TX pin) or XON/XOFF
;---------------------------------------------------------------------------------
tu_hs_off
sb tu_rx_enable
retp ;hs already off
mov w,tu_config
and w,#$14 ;test if tu_xonxoff or tu_rts set
snz
retp ;no handshake
mov w,tu_num
and w,#$0F ;extract bytes in tu_rx_buf
jb tu_xonxoff,:tu_xoff ;xonxoff handshake
xor w,#$05 ;disable receive if 5 bytes in tu_rx_buf
sz
retp
clrb tu_rx_enable
setb ra_dir_buf.termTX ;turn off handshake
retp
:tu_xoff
xor w,#$02 ;disable receive if 2 byte in tu_rx_buf
sz
retp
:tu_xoff1
test tu_tx_count
jnz :tu_xoff1 ;wait for not busy
clrb tu_rx_enable
mov w,#XOFF ;send XOFF
jmp tu_output_w
; term output handshake on (inform remote deivce to start transmitting)
; either RTS (on TX pin) or XON/XOFF
;---------------------------------------------------------------------------------
tu_hs_on
snb tu_rx_enable
retp ;hs already on
mov w,tu_config
and w,#$14 ;test if tu_xonxoff or tu_rts set
snz
retp ;no handshake
mov w,tu_num
and w,#$0F ;extract bytes in tu_rx_buf
jb tu_xonxoff,:tu_xon ;xonxoff handshake
xor w,#$01 ;enable receive if only 1 byte in tu_rx_buf
sz
retp
setb tu_rx_enable
clrb ra_dir_buf.termTX ;turn on handshake
retp
:tu_xon
xor w,#$01 ;enable receive if only 1 byte in tu_rx_buf
sz
retp
setb tu_rx_enable
:tu_xon1
test tu_tx_count
jnz :tu_xon1 ;wait for not busy
mov w,#XON ;send XOFF
jmp tu_output_w
; term input task
;---------------------------------------------------------------------------------
_term_input_task
bank tu_bank
snb tu_cts ;test if rx is used as cts
retp ;yes, so do not receive
sb tu_rx_flag ;byte received?
retp ;no
clrb tu_rx_flag ;clear receive flag (byte in tu_char)
jnb tu_xonxoff,:tu_input ;jump ahead if no xon/xoff
mov w,tu_char
mov temp,w ;test for xon/xoff
xor w,#XON ;test if byte received is XON
snz
setb tu_tx_enable ;yes, so enable tx
snz
retp ;and exit (XON is not stored)
mov w,tu_char
xor w,#XOFF ;test if byte received is XOFF
snz
clrb tu_tx_enable ;yes, so disable tx
snz
retp ;and exit (XOFF is not stored)
:tu_input
jb tu_parity_odd,:parity
jb tu_parity_even,:parity
jmp :exit
:parity
mov w,tu_char
call even_parity
snb tu_parity_odd
not temp
and temp,#$01 ;extract calculated parity bit
addb temp,tu_rx_parity ;add received parity bit
snb temp.0 ;temp=0 or temp=2 if parity bits match
setb term_parity_error
:exit
mov w,tu_char
call @enqueue_tu_rx ;put byte in queue
jmp @tu_hs_off ;turn off handshake if required
; term output task
;---------------------------------------------------------------------------------
_term_output_task
bank tu_bank
snb tu_rts ;test if tx is used as rts
retp ;tx is handshake so do not send
sb tu_tx_data ;data to send?
retp ;no
test tu_tx_count ;transmitter busy?
sz
retp ;yes
jb tu_xonxoff,tu_hs_soft
jnb tu_cts,tu_output ;rx not used as handshake
tu_hs_cts ;test if handshake input on
jnb pinRX,tu_output ;if so then send
retp
tu_hs_soft
sb tu_tx_enable ;send only if enabled
retp
tu_output
call @dequeue_tu_tx
tu_output_w
not w ;ready bits (inverse logic)
mov tu_tx_high,w ;store data byte
setb tu_tx_low.7 ;set up start bit
call tu_set_parity
rr tu_tx_high ;shiftin parity bit or stop bit
rr tu_tx_low
mov w,tu_bits ;start + data + parity + stop
mov tu_tx_count,w
retp
; detect transition on pinWE
; actions when neg edge:
; - disable transmitter
; - clear buffers
; - initialize receive registers
; - enable receiver
; - move to state STATE_RECEIVE_COMMAND
; actions when pos edge:
; - disable receiver
; - initialize transmit registers
; - enable transmitter
; - move to state STATE_TRANSMIT_RESPONSE
;---------------------------------------------------------------------------------
checkPinWE
bank hu_bank
snb hu_tx_enable
retp
movb pinWE_past,pinWE_present
movb pinWE_present,pinWE
mov w,hu_state
and w,#$C0 ;extract pinWE states
xor w,#$40 ;L->H transition?
jz :init_transmit ; yes
xor w,#$C0 ;H->L transition
sz
retp ; no
:init_receive
clrb hu_tx_enable ;disable transmitter
clr hu_head ;clear buffers parameters (tx and rx)
clr hu_tail
clr hu_num
clrb hu_rx_data
clrb hu_rx_overrun
clrb hu_tx_data
clrb hu_tx_overrun
clr hu_rx_count ;initialize receive registers
clrb hu_rx_flag
setb hu_rx_enable ;enable receiver
and hu_state,#$C0 ;keep pin states
or hu_state,#STATE_RECEIVE_COMMAND ;move to state RECEIVE_COMMAND
retp
:init_transmit
clrb hu_rx_enable ;disable receiver
clr hu_tx_count ;initialize transmit registers
setb hu_tx_enable ;enable transmitter
and hu_state,#$C0 ;keep pin states
or hu_state,#STATE_TRANSMIT_RESPONSE ;move to state TRANSMIT_RESPONSE
retp
; Delay approx. 1 second
;--------------------------------------------------------------------
delay_1s00
mov w,#250
call delay_w
delay_0s75
mov w,#250
call delay_w
; Delay approx. 0.5 second
;--------------------------------------------------------------------
delay_0s50
mov w,#250
call delay_w
delay_0s25
mov w,#250
; Delay w milliseconds
;--------------------------------------------------------------------
delay_w
bank timer_bank
mov timer_1m,w
:wait
test timer_1m
jnz :wait
retp
; Host command handler.
; This is the host command dispatcher entry.
; It is always called when command or parameters are received.
;---------------------------------------------------------------------------------
HOST_COMMANDS equ $08 ;number of host commands
host_command_handler
bank hu_bank
snb cmd_done
jmp clear_input ;command already finished
csb hu_command,#HOST_COMMANDS
jmp invalid_host_command
mov w,hu_command
add pc,w
jmp _DV_GET_VERSION ;00 device get version
jmp _TM_SET_CONFIG ;01 terminal set configuration
jmp _TM_READ_DATA ;02 terminal read data
jmp _TM_WRITE_DATA ;03 terminal write data
jmp _IO_READ_PORTB ;04 i/o read port B
jmp _IO_WRITE_PORTB_DIR ;05 i/o write port B direction
jmp _IO_READ_PORTC ;06 i/o read port C
jmp _IO_WRITE_PORTC_DIR ;07 i/o write port C direction
;new commands here
; Invalid commands must remove input data to prevent hu_rx_buf overrun
;---------------------------------------------------------------------------------
invalid_host_command
setb cmd_invalid
clear_input
call @dequeue_hu_rx ;get rid of any input
; Mark command finished
;---------------------------------------------------------------------------------
host_command_exit
bank hu_bank
setb cmd_done
retp
; Command $00 - Get device version.
; Parameters - none
; Description - This command returns 8 bytes to the host
;---------------------------------------------------------------------------------
_DV_GET_VERSION
clrb cmd_parameter_missing
sb hu_tx_enable ;may we transmit?
retp ;no
and hu_head,#$0F ;reset hu_tx_buf parameters
and hu_tail,#$0F
and hu_num,#$0F
or hu_num,#$80
bank hu_buf_bank
mov hu_tx_buf+$0,#'T' ;co-processor identification
mov hu_tx_buf+$1,#'E'
mov hu_tx_buf+$2,#'M'
mov hu_tx_buf+$3,#'P'
mov hu_tx_buf+$4,#'L'
mov hu_tx_buf+$5,#'A'
mov hu_tx_buf+$6,#'T'
mov hu_tx_buf+$7,#'E'
bank hu_bank
setb hu_tx_data
jmp host_command_exit
; Command $01 - terminal set configuration
; Parameters - config (default value = 0)
; b7 b6 b5 b4 b3 b2 b1 b0
; | | | | | | +---+-- baud (0 to 3 -> see baud table)
; | | | | | +---------- 1 if TX used as RTS handshake
; | | | | +-------------- 1 if RX used as CTS handshake
; | | | +------------------ 1 if xon/xoff handshake
; | | +---------------------- 1 for 7 databits (default = 8 databits)
; | +-------------------------- 1 for even parity (default = no parity)
; +------------------------------ 1 for odd parity
; Description - set terminal baud, parity, handshake
;---------------------------------------------------------------------------------
_TM_SET_CONFIG
jb hu_tx_enable,:output ;prevent deadlock if not received byte
sb hu_rx_data
retp
clrb cmd_parameter_missing
call @dequeue_hu_rx
bank tu_bank
mov tu_config,w
mov tu_bits,#10 ;assume 8 databits and no parity
snb tu_parity_even
setb tu_bits.0 ;if parity set bits to 11
snb tu_parity_odd
setb tu_bits.0
snb tu_7bits
dec tu_bits ;if 7 databits decrement bits
:output
jmp host_command_exit
; Command $02 - read terminal data (one byte)
; Parameters - none
; Description - Return current terminal character, if any, else 0
;---------------------------------------------------------------------------------
_TM_READ_DATA
clrb cmd_parameter_missing
sb hu_tx_enable
retp
clr w
bank tu_bank
jnb tu_rx_data,:tm_rd
call @dequeue_tu_rx
:tm_rd
call @enqueue_hu_tx
bank tu_bank
call @tu_hs_on ;turn on handshake if required
jmp host_command_exit
; Command $03 - write terminal data
; Parameters - bytes to write
; Description - write one or more characters to term
;---------------------------------------------------------------------------------
_TM_WRITE_DATA
clrb cmd_parameter_missing
jb hu_tx_enable,:output
sb hu_rx_data
retp
call @dequeue_hu_rx
call @enqueue_tu_tx
retp
:output
jmp host_command_exit
; Command $04 - read port B
; Parameters - none
; Description - Return current port B input value to host
;---------------------------------------------------------------------------------
_IO_READ_PORTB
clrb cmd_parameter_missing
sb hu_tx_enable ;may we transmit
retp ;no
mov w,rb
call @enqueue_hu_tx
jmp host_command_exit
; Command $05 - write value to port B direction register
; Parameters - dirmask
; Description - Make port B pins low output (bit=0) or input (bit=1)
;---------------------------------------------------------------------------------
_IO_WRITE_PORTB_DIR
jb hu_tx_enable,:output ;prevent deadlock if not received byte
sb hu_rx_data
retp
clrb cmd_parameter_missing
call @dequeue_hu_rx
mov !rb,w
:output
jmp host_command_exit
; Command $06 - read port C
; Parameters - none
; Description - Return current port C input value to host
;---------------------------------------------------------------------------------
_IO_READ_PORTC
clrb cmd_parameter_missing
sb hu_tx_enable ;may we transmit
retp ;no
mov w,rc
call @enqueue_hu_tx
jmp host_command_exit
; Command $07 - write value to port C direction register
; Parameters - dirmask
; Description - Make port C pins low output (bit=0) or input (bit=1)
;---------------------------------------------------------------------------------
_IO_WRITE_PORTC_DIR
jb hu_tx_enable,:output ;prevent deadlock if not received byte
sb hu_rx_data
retp
clrb cmd_parameter_missing
call @dequeue_hu_rx
mov !rc,w
:output
jmp host_command_exit
;-------------- codepage 1 end -----------------------------------------------------------
;*****************************************************************************************
org CODEPAGE_2
;*****************************************************************************************
jmp $ ;generates error if code above crosses page boundary
;-------------- codepage 2 end -----------------------------------------------------------
;*****************************************************************************************
org CODEPAGE_3
;*****************************************************************************************
jmp $ ;generates error if code above crosses page boundary
;-------------- codepage 3 end ------------------------------------------------------