;file vplib.src

;uncomment following 3 lines if SX48/52
;DEV_48
;device			SX48,oschs1
;id			'DEV_SX48'

;uncomment following 3 lines if SX28
DEV_28
device			SX28,oschs1,turbo,stackx,optionx
id			'DEV_SX28'

;uncomment following 3 lines if SX18
;DEV_18
;device			SX18,oschs1,turbo,stackx,optionx
;id			'DEV_SX18'

irc_cal			IRC_SLOW
reset			reset_entry
freq			20_000_000
RESONATOR		equ	20	; set to freq/1_000_000

; application start up register values, set as needed

RA_latch	equ	%00000101	;port A output latch init 1=high 0=low
RA_tris		equ	%11111010	;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_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
RB_wked		equ	%11111111	;port B wake up edge register 1=neg 0=pos
RB_wkpnd	equ	%00000000	;port B MIWI pending register 1=detected 0=not detected
RB_cmp		equ	%11111111	;port B comparator enable register 1=disabled 0=enabled

ifndef DEV_18
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
endif

;ifdef DEV_48
RD_latch	equ	%00000000	;port D output latch 1=high 0=low
RD_tris		equ	%11111111	;port D direction register 1=input 0=output
RD_plp		equ	%11111111	;port D pull up register 1=off 0=on
RD_lvl		equ	%11111111	;port D level register 1=ttl 0=cmos
RD_st		equ	%11111111	;port D schmitt trigger register 1=off 0=on

RE_latch	equ	%00000000	;port E output latch 1=high 0=low
RE_tris		equ	%11111111	;port E direction register 1=input 0=output
RE_plp		equ	%11111111	;port E pull up register 1=off 0=on
RE_lvl		equ	%11111111	;port E level register 1=ttl 0=cmos
RE_st		equ	%11111111	;port E schmitt trigger register 1=off 0=on
;endif

PORTPIN_RA0	equ	0x50
PORTPIN_RA1	equ	0x51
PORTPIN_RA2	equ	0x52
PORTPIN_RA3	equ	0x53

PORTPIN_RB0	equ	0x60
PORTPIN_RB1	equ	0x61
PORTPIN_RB2	equ	0x62
PORTPIN_RB3	equ	0x63
PORTPIN_RB4	equ	0x64
PORTPIN_RB5	equ	0x65
PORTPIN_RB6	equ	0x66
PORTPIN_RB7	equ	0x67

ifndef DEV_18
PORTPIN_RC0	equ	0x70
PORTPIN_RC1	equ	0x71
PORTPIN_RC2	equ	0x72
PORTPIN_RC3	equ	0x73
PORTPIN_RC4	equ	0x74
PORTPIN_RC5	equ	0x75
PORTPIN_RC6	equ	0x76
PORTPIN_RC7	equ	0x77
endif

ifdef DEV_48
PORTPIN_RD0	equ	0x80
PORTPIN_RD1	equ	0x81
PORTPIN_RD2	equ	0x82
PORTPIN_RD3	equ	0x83
PORTPIN_RD4	equ	0x84
PORTPIN_RD5	equ	0x85
PORTPIN_RD6	equ	0x86
PORTPIN_RD7	equ	0x87

PORTPIN_RE0	equ	0x90
PORTPIN_RE1	equ	0x91
PORTPIN_RE2	equ	0x92
PORTPIN_RE3	equ	0x93
PORTPIN_RE4	equ	0x94
PORTPIN_RE5	equ	0x95
PORTPIN_RE6	equ	0x96
PORTPIN_RE7	equ	0x97
endif

ifdef DEV_18
global_org	equ	$07
endif
ifdef DEV_28
global_org	equ	$08
endif
ifdef DEV_48
global_org	equ	$0A
endif

ifdef DEV_48
bank0_org	equ	$00
bank1_org	equ	$10
bank2_org	equ	$20
bank3_org	equ	$30
bank4_org	equ	$40
bank5_org	equ	$50
bank6_org	equ	$60
bank7_org	equ	$70
bank8_org	equ	$80
bank9_org	equ	$90
bankA_org	equ	$A0
bankB_org	equ	$B0
bankC_org	equ	$C0
bankD_org	equ	$D0
bankE_org	equ	$E0
bankF_org	equ	$F0
else
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
endif

page0_org	equ	$0
page1_org	equ	$200
page2_org	equ	$400
page3_org	equ	$600
ifdef DEV_48
page4_org	equ	$800
page5_org	equ	$A00
page6_org	equ	$C00
page7_org	equ	$E00
endif

;power up register values

RA_tris_PU	equ	%11111111	;port A direction register 1=input 0=output
RA_plp_PU	equ	%11111111	;port A pull up register 1=off 0=on
RA_lvl_PU	equ	%11111111	;port A level register 1=ttl 0=cmos

RB_tris_PU	equ	%11111111	;port B direction register 1=input 0=output
RB_plp_PU	equ	%11111111	;port B pull up register 1=off 0=on
RB_lvl_PU	equ	%11111111	;port B level register 1=ttl 0=cmos
RB_st_PU	equ	%11111111	;port B schmitt trigger register 1=off 0=on
RB_wken_PU	equ	%11111111	;port B wake up enable register 1=off 0=on
RB_wked_PU	equ	%11111111	;port B wake up edge register 1=neg 0=pos
RB_wkpnd_PU	equ	%11111111	;port B MIWI pending register 1=detected 0=not detected
RB_cmp_PU	equ	%11111111	;port B comparator enable register 1=disabled 0=enabled

ifndef DEV_18
RC_tris_PU	equ	%11111111	;port C direction register 1=input 0=output
RC_plp_PU	equ	%11111111	;port C pull up register 1=off 0=on
RC_lvl_PU	equ	%11111111	;port C level register 1=ttl 0=cmos
RC_st_PU	equ	%11111111	;port C schmitt trigger register 1=off 0=on
endif

;ifdef DEV_48
RD_tris_PU	equ	%11111111	;port D direction register 1=input 0=output
RD_plp_PU	equ	%11111111	;port D pull up register 1=off 0=on
RD_lvl_PU	equ	%11111111	;port D level register 1=ttl 0=cmos
RD_st_PU	equ	%11111111	;port D schmitt trigger register 1=off 0=on

RE_tris_PU	equ	%11111111	;port E direction register 1=input 0=output
RE_plp_PU	equ	%11111111	;port E pull up register 1=off 0=on
RE_lvl_PU	equ	%11111111	;port E level register 1=ttl 0=cmos
RE_st_PU	equ	%11111111	;port E schmitt trigger register 1=off 0=on
;endif

;-----------------------------------------------------------------------------------------
; uart constants
;-----------------------------------------------------------------------------------------

if RESONATOR == 50
;192*1200=96*2400=48*4800=24*9600=12*19200=6*38400
;uartfs=192*1200=230400
;int_period=50000000/230400=217
uartfs		equ	230400		; uart's basic frequency 192*1200 = 230400
int_period	equ	217		; used as seed for uart baud rate.
endif

if RESONATOR == 20
;96*1200=48*2400=24*4800=12*9600=6*19200
;uartfs=96*1200=115200
;int_period=20000000/115200=174
uartfs		equ	115200		; uart's basic frequency 96*1200 = 115200
int_period	equ	174		; used as seed for uart baud rate.
endif

if RESONATOR == 10
;48*1200=24*2400=12*4800=6*9600
;uartfs=48*1200=57600
;int_period=10000000/57600=174
uartfs		equ	57600		; uart's basic frequency 48*1200 = 57600
int_period	equ	174		; used as seed for uart baud rate.
endif

if RESONATOR == 4
;24*1200=12*2400=6*4800
;uartfs=24*1200=28800
;int_period=4000000/28800=139
uartfs		equ	28800		; uart's basic frequency 24*1200 = 28800
int_period	equ	139		; used as seed for uart baud rate.
endif

THREADS		equ	3

mscount		equ	((2*1000*RESONATOR/int_period)+1)/2	;isr cycles for 1 msec
			;50MHz -> 230, 20MHz -> 115, 10MHz -> 57, 4MHz -> 29
uscount		equ	((2*250*RESONATOR/int_period)+1)/2	;isr cycles for 250 usec
			;50MHz -> 58, 20MHz -> 29, 10MHz -> 14, 4MHz -> 7

;The following baudrates are available

baud0		equ	1200
baud1		equ	2400
baud2		equ	4800
baud3		equ	9600
baud4		equ	19200
baud5		equ	38400
							; 50MHz  20MHz  10MHz  4MHz
divide0		equ	uartfs / (baud0 * THREADS)	;   64     32    16      8
StDelay0 	equ	divide0 + (divide0 / 2)		;   96     48    24     12
BAUD1200	equ	divide0				;load into rx_baud or tx_baud

divide1		equ	uartfs / (baud1 * THREADS)	;   32     16     8      4
StDelay1 	equ	divide1 + (divide1 / 2)		;   48     24    12      6
BAUD2400	equ	divide1				;load into rx_baud or tx_baud

divide2		equ	uartfs / (baud2 * THREADS)	;   16      8     4      2
StDelay2 	equ	divide2 + (divide2 / 2)		;   24     12     6      3
BAUD4800	equ	divide2				;load into rx_baud or tx_baud

divide3		equ	uartfs / (baud3 * THREADS)	;    8      4     2      -
StDelay3 	equ	divide3 + (divide3 / 2)		;   12      6     3      -
BAUD9600	equ	divide3				;load into rx_baud or tx_baud

divide4		equ	uartfs / (baud4 * THREADS)	;    4      2     -      -
StDelay4 	equ	divide4 + (divide4 / 2)		;    6      3     -      -
BAUD19200	equ	divide4				;load into rx_baud or tx_baud

divide5		equ	uartfs / (baud5 * THREADS)	;    2      -     -      -
StDelay5 	equ	divide5 + (divide5 / 2)		;    3      -     -      -
BAUD38400	equ	divide5				;load into rx_baud or tx_baud

;serial data formats
E71		equ	%00001110	; even parity, 7 databits, 1 stopbit , 1 startbit, 10 bits total
E72		equ	%00001111	; even parity, 7 databits, 2 stopbit , 1 startbit, 11 bits total
E81		equ	%00001011	; even parity, 8 databits, 1 stopbit , 1 startbit, 11 bits total
O71		equ	%00010110	; odd  parity, 7 databits, 1 stopbit , 1 startbit, 10 bits total
O72		equ	%00010111	; odd  parity, 7 databits, 2 stopbit , 1 startbit, 11 bits total
O81		equ	%00010011	; odd  parity, 8 databits, 1 stopbit , 1 startbit, 11 bits total
N71		equ	%00000101	; no   parity, 7 databits, 1 stopbit , 1 startbit,  9 bits total 
N72		equ	%00000110	; no   parity, 7 databits, 2 stopbit , 1 startbit, 10 bits total 
N81		equ	%00000010	; no   parity, 8 databits, 1 stopbit , 1 startbit, 10 bits total
N82		equ	%00000011	; no   parity, 8 databits, 2 stopbit , 1 startbit, 11 bits total
UART_ENABLE	equ	%01000000	; load [UART_ENABLE] + dataformat value into rx_format or tx_format

M_CMP			equ	$08		;values for mode register
M_WKPND			equ	$09
M_WKED			equ	$0A
M_WKEN			equ	$0B
M_ST			equ	$0C
M_LVL			equ	$0D
M_PLP			equ	$0E
M_TRIS			equ	$0F

			; option register constants
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)
RTCC_PS_000		equ	%00000000	;RTCC = 1:2, WDT = 1:1
RTCC_PS_001		equ	%00000001	;RTCC = 1:4, WDT = 1:2
RTCC_PS_010		equ	%00000010	;RTCC = 1:8, WDT = 1:4
RTCC_PS_011		equ	%00000011	;RTCC = 1:16, WDT = 1:8
RTCC_PS_100		equ	%00000100	;RTCC = 1:32, WDT = 1:16
RTCC_PS_101		equ	%00000101	;RTCC = 1:64, WDT = 1:32
RTCC_PS_110		equ	%00000110	;RTCC = 1:128, WDT = 1:64
RTCC_PS_111		equ	%00000111	;RTCC = 1:256, WDT = 1:128

;-----------------------------------------------------------------------------------------

			; secret instruction fifo
fifo			macro
			dw	$47
endm

			; secret instruction pushW
pushW			macro
			dw	$48
endm

			; secret instruction pushST
pushST			macro
			dw	$49
endm

			; secret instruction pushFSR
pushFSR			macro
			dw	$4A
endm

			; secret instruction pushPC
pushPC			macro
			dw	$4B
endm

			; secret instruction popW
popW			macro
			dw	$4C
endm

			; secret instruction popST
popST			macro
			dw	$4D
endm

			; secret instruction popFSR
popFSR			macro
			dw	$4E
endm

			; secret instruction popPC
popPC			macro
			dw	$4F
endm

;-----------------------------------------------------------------------------------------

i2cs_var		macro	prefix, isr
prefix??_??bank		equ	($ & $F0) | isr
prefix??_??state	ds	1	; This indicates the state that the I2C slave is currently in.
prefix??_??sub_state	ds	1	; This indicates the substate that the I2C slave is currently in.
prefix??_??bit_count	ds	1	; Indicates the number of bits left to process in read/write
prefix??_??byte		ds	1	; The byte currently being written/read by the I2C slave
prefix??_??past_pres	ds	1	; The last_state of the I2C port (to be compared with the present state)
  prefix??_??pres_sda	equ	prefix??_??past_pres.0 ; The present state of the SDA line
  prefix??_??pres_scl	equ	prefix??_??past_pres.1 ; The present state of the SCL line
  prefix??_??past_sda	equ	prefix??_??past_pres.2 ; The state of the SDA line on last interrupt
  prefix??_??past_scl	equ	prefix??_??past_pres.3 ; The state of the SCL line on last interrupt
prefix??_??flags	ds	1	; i2c flags
  prefix??_??stop_found	equ	prefix??_??flags.0	; set by stop and cleared by start condition
  prefix??_??rx_flag	equ	prefix??_??flags.1	; set when this slave has received data
  prefix??_??broadcast	equ	prefix??_??flags.2	; set when this slave is addressed by broadcast id
  prefix??_??written_again	equ	prefix??_??flags.3	; set when this slave receives 2nd or later databyte
  prefix??_??data_needed	equ	prefix??_??flags.4	; set when this slave requires data
  prefix??_??sda_buf	equ	prefix??_??flags.5 ; current direction for sda pin
  prefix??_??cls_buf	equ	prefix??_??flags.6 ; current direction for scl pin
prefix??_??sdapin	ds	1	;pin SDA
prefix??_??sclpin	ds	1	;pin SCL
endm

lib_i2cs = 0

i2cs			macro	nr, prefix
			org	data
lib_i2cs_state		=	$+0	; This indicates the state that the I2C slave is currently in.
lib_i2cs_sub_state	=	$+1	; This indicates the substate that the I2C slave is currently in.
lib_i2cs_bit_count	=	$+2	; Indicates the number of bits left to process in read/write
lib_i2cs_byte		=	$+3	; The byte currently being written/read by the I2C slave
lib_i2cs_past_pres	=	$+4	; The last_state of the I2C port (to be compared with the present state)
  lib_i2cs_pres_sda	=	lib_i2cs_past_pres.0 ; The present state of the SDA line
  lib_i2cs_pres_scl	=	lib_i2cs_past_pres.1 ; The present state of the SCL line
  lib_i2cs_past_sda	=	lib_i2cs_past_pres.2 ; The state of the SDA line on last interrupt
  lib_i2cs_past_scl	=	lib_i2cs_past_pres.3 ; The state of the SCL line on last interrupt
lib_i2cs_flags		=	$+5	; i2c flags
  lib_i2cs_stop_found	=	lib_i2cs_flags.0	; set by stop and cleared by start condition
  lib_i2cs_rx_flag	=	lib_i2cs_flags.1	; set when this slave has received data
  lib_i2cs_broadcast	=	lib_i2cs_flags.2	; set when this slave is addressed by broadcast id
  lib_i2cs_written_again	=	lib_i2cs_flags.3	; set when this slave receives 2nd or later databyte
  lib_i2cs_data_needed	=	lib_i2cs_flags.4	; set when this slave requires data
  lib_i2cs_sda_buf	=	lib_i2cs_flags.5 ; current direction for sda pin
  lib_i2cs_cls_buf	=	lib_i2cs_flags.6 ; current direction for scl pin
lib_i2cs_sdapin		=	$+6	;pin SDA
lib_i2cs_sclpin		=	$+7	;pin SCL
vp??nr??_bank		equ	($ & $F8)|nr
i2cs_var prefix,nr
data			=	$
if lib_i2cs == 0
lib_i2cs = 1
			org	code
vp??nr??_isr		equ	$

vp??nr??_main		equ	$

code			=	$
endif
endm

;-----------------------------------------------------------------------------------------

uart_var		macro	prefix, isr
prefix??_??bank		equ	($ & $F0) | isr
prefix??_??count	ds	1		;number of bits
prefix??_??divide	ds	1		;timing counter
prefix??_??low		ds	1		;shift register
prefix??_??high		ds	1		;value
prefix??_??format	ds	1
  prefix??_??bits_0	equ	prefix??_??format.0	;bits 1 and 0 specify number of total bits
  prefix??_??bits_1	equ	prefix??_??format.1	;01 -> 9bits, 10 -> 10bits, 11 -> 11bits (start+data+parity+stop)
  prefix??_??7bits	equ	prefix??_??format.2	;set for 7 databits (0 = 8 databits)
  prefix??_??even	equ	prefix??_??format.3	;set for even parity
  prefix??_??odd	equ	prefix??_??format.4	;set for odd parity
  prefix??_??data	equ	prefix??_??format.5	;set if valid value, cleared after value grabbed
  prefix??_??enable	equ	prefix??_??format.6	;set to enable uart
  prefix??_??parity	equ	prefix??_??format.7	;parity bit placeholder
prefix??_??baud		ds	1		;baudrate
prefix??_??datapin	ds	1		;high nib = port (5 to 9), low nib = pin (0 to 7)
  prefix??_??error	equ	prefix??_??datapin.3	;set if parity error
prefix??_??hspin	ds	1		;high nib = port (5 to 9), low nib = pin (0 to 7)
endm

even_parity_count = 0

uart_even_parity	macro
if even_parity_count == 0
even_parity_count = 1
	; calculate even parity for w
	; result in main_temp.0
	;---------------------------------------------------------------------------------
even_parity
			mov	main_temp,w

	;calculation code by John Payson, as found on sxlist.com
	;This routine will leave the parity of main_temp in main_temp.0
	;while blenderizing most of the rest of main_temp

			mov	w,<>main_temp	; DL = abcdefgh
			xor	main_temp,w
			mov	w,>>main_temp
			xor	main_temp,w

	; at this point, the parity for half the bits
	; (a, b, e, and f) is in bit 2 of main_temp, and the
	; parity for the other half (bits c, d, g, and h)
	; is in bit 0 of main_temp.

			snb	main_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	main_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
endif
endm

lib_txuart = 0

txuart			macro	nr, prefix
			org	data
lib_tx_count		=	$+0		;number of bits
lib_tx_divide		=	$+1		;timing counter
lib_tx_low		=	$+2		;shift register
lib_tx_high		=	$+3		;value
lib_tx_format		=	$+4
  lib_tx_bits_0		=	lib_tx_format.0	;bits 1 and 0 specify number of total bits
  lib_tx_bits_1		=	lib_tx_format.1	;01 -> 9bits, 10 -> 10bits, 11 -> 11bits (start+data+parity+stop)
  lib_tx_7bits		=	lib_tx_format.2	;set for 7 databits (0 = 8 databits)
  lib_tx_even		=	lib_tx_format.3	;set for even parity
  lib_tx_odd		=	lib_tx_format.4	;set for odd parity
  lib_tx_data		=	lib_tx_format.5	;set if valid value, cleared after value grabbed
  lib_tx_enable		=	lib_tx_format.6	;set to enable uart
  lib_tx_parity		=	lib_tx_format.7	;parity bit placeholder
lib_tx_baud		=	$+5		;baudrate
lib_tx_datapin		=	$+6		;high nib = port (5 to 9), low nib = pin (0 to 7)
  lib_tx_error		=	lib_tx_datapin.3	;set if parity error
lib_tx_hspin		=	$+7		;high nib = port (5 to 9), low nib = pin (0 to 7)
vp??nr??_bank		equ	($ & $F8)|nr
uart_var prefix,nr
data			=	$
			org	code
uart_even_parity
if lib_txuart == 0
lib_txuart = 1
uart_transmit
			mov	fsr,vp_bank		;select target uart bank
			jnb	lib_tx_enable,:txdone
			decsz	lib_tx_divide		;time for next bit?
			jmp	:txdone			;no
			mov	w,lib_tx_baud		;renew delay time
			mov	lib_tx_divide,w
			test    lib_tx_count		;bits to send?
			snz
			jmp	:txdone			;no
			clc				;ready stop bit
			rr	lib_tx_high			;and shift to next bit
			rr	lib_tx_low
			mov	w,lib_tx_datapin		;pin number 0 to 7 in lownib
			call	pinmask			;get ORmask for pin
			stc
			snb	lib_tx_low.5		;clear carry and keep ORmask to make pin high
			not	w			;set carry and make ANDmask to make pin low
			sb	lib_tx_low.5
			clc
			mov	isr_temp,w
			mov	w,<>lib_tx_datapin		;port (ra to re) in lownib
			and	w,#$0F
			mov	fsr,w			;ports ra to re
			mov	w,isr_temp
			snc
			and	indf,w
			sc
			or	indf,w
			mov	w,vp_bank		;select target uart bank
			mov	fsr,w
			dec	lib_tx_count		;decrement bit counter
			jnz	:txdone
			call	pinmask			;set isr_flag
			or	isr_flags,w
:txdone
			retp
uart_output_task
			snb	lib_tx_7bits		;8 databits?
			clrb	lib_tx_high.7		;no, clear b7 for parity calculation
			mov	w,lib_tx_high
			call	even_parity
			snb	lib_tx_7bits		;8 databits?
			setb	lib_tx_high.7		;no, ready stop bit
			jnb	lib_tx_odd,:uot1
			movb	C,/main_temp.0
			jmp	:uot2
:uot1
			jnb	lib_tx_even,:uot3
			movb	C,main_temp.0
:uot2
			jnb	lib_tx_7bits,:uot4		;7 databits?
			movb	lib_tx_high.7,C		;yes, move parity into b7
:uot3
			stc				;ready stop bit
:uot4
			clrb	lib_tx_low.7		;setup start bit
			rr	lib_tx_high			;rotate in parity bit or stop bit
			rr	lib_tx_low
			not	lib_tx_high			;inverted mode
			not	lib_tx_low
			mov	w,lib_tx_format		;get total number of bits
			and	w,#$03			;1, 2 or 3
			or	w,#$08			;9, 10, or 11
			mov	lib_tx_count,w		;now uart vp starts to transmit
			clrb	lib_tx_data			;mark byte grabbed
			retp
endif
vp??nr??_isr
			mov	vp_bank,#vp??nr??_bank
			jmp	uart_transmit
vp??nr??_main
			mov	fsr,#vp??nr??_bank
			jb	lib_tx_data,uart_output_task
			retp
code			=	$
endm

lib_rxuart = 0

rxuart			macro	nr, prefix
			org	data
lib_rx_count		=	$+0		;number of bits
lib_rx_divide		=	$+1		;timing counter
lib_rx_low		=	$+2		;shift register
lib_rx_high		=	$+3		;value
lib_rx_format		=	$+4
  lib_rx_bits_0		=	lib_rx_format.0	;bits 1 and 0 specify number of total bits
  lib_rx_bits_1		=	lib_rx_format.1	;01 -> 9bits, 10 -> 10bits, 11 -> 11bits (start+data+parity+stop)
  lib_rx_7bits		=	lib_rx_format.2	;set for 7 databits (0 = 8 databits)
  lib_rx_even		=	lib_rx_format.3	;set for even parity
  lib_rx_odd		=	lib_rx_format.4	;set for odd parity
  lib_rx_data		=	lib_rx_format.5	;set if valid value, cleared after value grabbed
  lib_rx_enable		=	lib_rx_format.6	;set to enable uart
  lib_rx_parity		=	lib_rx_format.7	;parity bit placeholder
lib_rx_baud		=	$+5		;baudrate
lib_rx_datapin		=	$+6		;high nib = port (5 to 9), low nib = pin (0 to 7)
  lib_rx_error		=	lib_rx_datapin.3	;set if parity error
lib_rx_hspin		=	$+7		;high nib = port (5 to 9), low nib = pin (0 to 7)
vp??nr??_bank		equ	($ & $F8) | nr
uart_var prefix,nr
data			=	$
			org	code
uart_even_parity
if lib_rxuart == 0
lib_rxuart = 1
uart_receive
			mov	fsr,vp_bank		;select target uart rambank
			jnb	lib_rx_enable,:rxdone
			mov	w,<>lib_rx_datapin		;port (ra to re) in lownib
			and	w,#$0F
			mov	fsr,w			;ports ra to re
			mov	w,indf			;read all pins
			mov	isr_temp,w		;save pin states
			mov	fsr,vp_bank
			mov	w,lib_rx_datapin		;pin number 0 to 7 in lownib
			call	pinmask			;get mask for pin
			and	isr_temp,w		;extract bit for pin
			test	lib_rx_count		;currently receiving byte?
			sz				;no
			jmp	:rx1			;yes, jump ahead
			mov	w,--lib_rx_format		;ready bits for start
			and	w,#$03			;number of bits 1, 2 or 3
			or	w,#$08			;9, 10 or 11
			test	isr_temp		;start bit?
			snz				;no
			mov	lib_rx_count,w		;yes, renew bit count
			mov	w,>>lib_rx_baud		;half bittime delay
			add	w,lib_rx_baud		;plus bittime delay -> start delay
			mov	lib_rx_divide,w
:rx1
			clc
			test	isr_temp
			sz
			stc
			decsz	lib_rx_divide		;middle of next bit?
          		jmp	:rxdone			;no
			dec	lib_rx_count		;last bit?
			jz	:rx2			;yes
			rr	lib_rx_low			;no, rotate in new bit
			movb	lib_rx_parity,C		;save b0
			jmp	:rx3			;renew delay time
:rx2
			mov	w,lib_rx_low		;save byte to allow receive another byte
			mov	lib_rx_high,w
			setb	lib_rx_data			;mark byte received
:rx3
			mov	w,lib_rx_baud		;renew delay time
			mov	lib_rx_divide,w
:rxdone
			retp
uart_input_task
			movb	C,lib_rx_parity		;get last saved b0
			jb	lib_rx_format.1,:uit1
			clc				;adjust for 7 databits + no parity (total bits is 9)
			rr	lib_rx_high
			rr	lib_rx_high
:uit1
			snb	lib_rx_format.0
			rl	lib_rx_high			;adjust for 8 databits + parity (total bits is 11)
			jnb	lib_rx_7bits,:uit2
			movb	C,lib_rx_high.7		;save possible parity in case 7 databits
			clrb	lib_rx_high.7		;clear b7 in case 7 databits
:uit2
			movb	lib_rx_parity,C		;save possible parity bit
			clrb	lib_rx_error		;assume no error
			jb	lib_rx_odd,:uit3
			jb	lib_rx_even,:uit3
			jmp	:uit4
:uit3
			mov	w,lib_rx_high
			call	even_parity
			snb	lib_rx_odd
			not	main_temp			;invert calculated even parity bit for odd parity
			addb	main_temp,lib_rx_parity		;if parity matches, b0 will be cleared
			snb	main_temp.0
			setb	lib_rx_error		;parity error
:uit4
			clrb	lib_rx_data			;mark byte grabbed
			mov	w,fsr			;isr_flag to use in lownib
			call	pinmask
			or	isr_flags,w		;set isr flag
			retp
endif
vp??nr??_isr
			mov	vp_bank,#vp??nr??_bank
			jmp	uart_receive
vp??nr??_main
			mov	fsr,#vp??nr??_bank
			jb	lib_rx_data,uart_input_task
			retp
code			=	$
endm

;-----------------------------------------------------------------------------------------

defvp			macro	nr, name, prefix
name nr, prefix
endm

;-----------------------------------------------------------------------------------------

PORT_RA			equ	$05		;storage RA latch
PORT_RB			equ	$06		;storage RB latch
PORT_RC			equ	$07		;storage RC latch (ram if SX18)
PORT_RD			equ	$08		;storage RD latch (ram if SX18/28)
PORT_RE			equ	$09		;storage RE latch (ram if SX18/28)
isr_temp		equ	$0A
isr_flags		equ	$0B
  isr0_flag		equ	isr_flags.0	;system timer (VP0)
  isr1_flag		equ	isr_flags.1	;vp1
  isr2_flag		equ	isr_flags.2	;vp2
  isr3_flag		equ	isr_flags.3	;vp3
  isr4_flag		equ	isr_flags.4	;vp4
  isr5_flag		equ	isr_flags.5	;vp5
  isr6_flag		equ	isr_flags.6	;vp6
  isr7_flag		equ	isr_flags.7	;??
vp_bank			equ	$0C
main_temp		equ	$0F
DIR_RA			equ	$F5		;storage RA direction register
DIR_RB			equ	$F6		;storage RB direction register
DIR_RC			equ	$F7		;storage RC direction register
DIR_RD			equ	$F8		;storage RD direction register
DIR_RE			equ	$F9		;storage RE direction register

			org	page0_org
sxinterrupt		jmp	_sxinterrupt
reset_entry		jmp	init
do_vp1_main		jmp	vp1_main
do_vp1_isr		jmp	vp1_isr
do_vp2_main		jmp	vp2_main
do_vp2_isr		jmp	vp2_isr
do_vp3_main		jmp	vp3_main
do_vp3_isr		jmp	vp3_isr
do_vp4_main		jmp	vp4_main
do_vp4_isr		jmp	vp4_isr
do_vp5_main		jmp	vp5_main
do_vp5_isr		jmp	vp5_isr
do_vp6_main		jmp	vp6_main
do_vp6_isr		jmp	vp6_isr
code			=	$

;-----------------------------------------------------------------------------------------
; RAM Bank Register VP definitions
;-----------------------------------------------------------------------------------------

	; bank 0
	;---------------------------------------------------------------------------------
			org	bank0_org
data			=	$
defvp 1,rxuart,rx
defvp 2,txuart,tx

			org	code

pinmask
			and	w,#$07
			add	pc,w
			retw	1		;pin0
			retw	2		;pin1
			retw	4		;pin2
			retw	8		;pin3
			retw	16		;pin4
			retw	32		;pin5
			retw	64		;pin6
			retw	128		;pin7
code			=	$

	; bank 1
	;---------------------------------------------------------------------------------
			org	bank1_org
data			=	$
defvp 3,rxuart,rx1
defvp 4,txuart,tx1

	; bank 2
	;---------------------------------------------------------------------------------
			org	bank2_org
data			=	$
;timer VP
timer_bank		=	$
timer_isr		ds	1		;increments every isr
timer_thread		ds	1		;increments 4 every 3 isr

;-----------------------------------------------------------------------------------------

			org	code

	;clear all ram
clear_ram
			mov	fsr,#global_org
:zero_global
			clr	indf
			inc	fsr
			cjne	fsr,#$10,:zero_global
:zero_ram
			clr	indf
			inc	fsr
			snz
			retp
ifndef DEV_48
			setb	fsr.4
endif
			jmp	:zero_ram

_sxinterrupt
isr_timer
			bank	timer_bank
ifdef DEV_48
			clrb	fsr.7
endif
			inc	timer_isr
			inc	timer_thread
			sb	timer_thread.1	;skip if 2 or 3
			inc	timer_thread	;if 0 or 1, make 2
						;lowest 2 bits of timer_thread has repeated 01,10,11 sequence
			cjne	timer_isr,#mscount,:timer_done
			clr	timer_isr
			setb	isr0_flag	;main calls isr0()
:timer_done

update_dir
			mov	w,#$F0
			mov	fsr,w
			mov	m,#$0F		;access port direction registers
			mov	w,DIR_RA
			mov	!ra,w
			mov	w,DIR_RB
			mov	!rb,w
ifndef DEV_18
			mov	w,DIR_RC
			mov	!rc,w
endif
ifdef DEV_48
			mov	w,DIR_RD
			mov	!rd,w
			mov	w,DIR_RE
			mov	!re,w
endif

thread_start
			mov	w,#timer_bank
			mov	fsr,w
			mov	w,--timer_thread
			and	w,#$03		;w = 0, 1 or 2
			add	pc,w
			jmp	isr_thread_0
			jmp	isr_thread_1
isr_thread_2
			;insert thread_2 code here
ifdef vp6_bank
			call	do_vp6_isr
endif
ifdef vp5_bank
			call	do_vp5_isr
endif
			jmp	isr_thread_end
isr_thread_1
			;insert thread_1 code here
ifdef vp4_bank
			call	do_vp4_isr
endif
ifdef vp3_bank
			call	do_vp3_isr
endif
			jmp	isr_thread_end
isr_thread_0
			;insert thread_0 code here
ifdef vp2_bank
			call	do_vp2_isr
endif
ifdef vp1_bank
			call	do_vp1_isr
endif
isr_thread_end
			;insert code here
isr_end
			mov	w,#(-int_period)&255
			retiw

;-------------- end of the Interrupt Service Routines ------------------------------------

	; program initialization
init
			call	clear_ram
portinit
if RB_wked != RB_wked_PU
			mov	m,#M_WKED	; point mode to WKED
			mov	!rb,#RB_wked
endif
if RB_wken != RB_wken_PU
			mov	m,#M_WKEN	; point mode to WKEN
			mov	!rb,#RB_wken
endif
if RB_wkpnd != RB_wkpnd_PU
			mov	m,#M_WKPND	 ;point mode to WKPND
			mov	w,#RB_WKPND
			mov	!rb,w
endif
if RB_cmp != RB_cmp_PU
			mov	m,#M_CMP	; point mode to CMP
			mov	w,#RB_cmp
			mov	!rb,w
endif
			mov	m,#M_ST		; point mode to ST
ifndef DEV_18
if RC_st != RC_st_PU
			mov	!rc,#RC_st
endif
endif
if RB_st != RB_st_PU
			mov	!rb,#RB_st
endif
			mov	m,#M_LVL	; point mode to LVL
ifdef DEV_48
if RE_lvl != RE_lvl_PU
			mov	!re,#RE_lvl
endif
if RD_lvl != RD_lvl_PU
			mov	!rd,#RD_lvl
endif
endif
ifndef DEV_18
if RC_lvl != RC_lvl_PU
			mov	!rc,#RC_lvl
endif
endif
if RB_lvl != RB_lvl_PU
			mov	!rb,#RB_lvl
endif
if RA_lvl != RA_lvl_PU
			mov	!ra,#RA_lvl
endif
			mov	m,#M_PLP	; point mode to PLP
ifdef DEV_48
if RE_plp != RE_plp_PU
			mov	!re,#RE_plp
endif
if RD_plp != RD_plp_PU
			mov	!rd,#RD_plp
endif
endif
ifndef DEV_18
if RC_plp != RC_plp_PU
			mov	!rc,#RC_plp
endif
endif
if RB_plp != RB_plp_PU
			mov	!rb,#RB_plp
endif
if RA_plp != RA_plp_PU
			mov	!ra,#RA_plp
endif
ifdef DEV_48
			mov	rc,#RE_latch
			mov	rb,#RD_latch
endif
ifndef DEV_18
			mov	rc,#RC_latch	; init latches
endif
			mov	rb,#RB_latch
			mov	ra,#RA_latch
			mov	m,#M_TRIS	; point mode to TRIS
			mov	w,#$F0
			mov	fsr,w
ifdef DEV_48
			mov	w,#RE_tris
if RE_tris != RE_tris_PU
			mov	!re,w
endif
			mov	DIR_RE,w
			mov	w,#RD_tris
if RD_tris != RD_tris_PU
			mov	!rd,w
endif
			mov	DIR_RD,w
endif
ifndef DEV_18
			mov	w,#RC_tris
if RC_tris != RC_tris_PU
			mov	!rc,w
endif
			mov	DIR_RC,w
endif
			mov	w,#RB_tris
if RB_tris != RB_tris_PU
			mov	!rb,w
endif
			mov	DIR_RB,w
			mov	w,#RA_tris
if RA_tris != RA_tris_PU
			mov	!ra,w
endif
			mov	DIR_RA,w

	;setup option register
			mov	w,#RTCC_PS_OFF+RTCC_ON	;RTCC prescaler 1:1 and RTCC at $01
			mov	!option,w
	;insert vp initialization code (set baudrates, parity etc)
main
ifdef vp1_bank
			call	do_vp1_main
endif
ifdef vp2_bank
			call	do_vp2_main
endif
ifdef vp3_bank
			call	do_vp3_main
endif
ifdef vp4_bank
			call	do_vp4_main
endif
ifdef vp5_bank
			call	do_vp5_main
endif
ifdef vp6_bank
			call	do_vp6_main
endif
	;insert main loop code
			test	isr_flags
			jz	main			;no isr_flags interrupts
	;insert code to handle isr_flags interrupts
			jmp	main
code			=	$

			org	code
ifndef vp1_bank
vp1_main		=	$
vp1_isr			=	$
endif
ifndef vp2_bank
vp2_main		=	$
vp2_isr			=	$
endif
ifndef vp3_bank
vp3_main		=	$
vp3_isr			=	$
endif
ifndef vp4_bank
vp4_main		=	$
vp4_isr			=	$
endif
ifndef vp5_bank
vp5_main		=	$
vp5_isr			=	$
endif
ifndef vp6_bank
vp6_main		=	$
vp6_isr			=	$
endif
			retp

;end of file vplib.src
