;*****************************************************************************************
; SYNOP rev.D program for COM (socket U27)
; ----------------------------------------
; identification (major-minor) $02-$00
;
; version 1.0
; august 22, 2002
;
; I2C and uart code adapted from Ubicom VP sources
;
; Implements token bus, i2c slave, uart1, uart2, uart3
; Copyright © Bos Automatisering & Elektrotechniek BV. All rights reserved.
;
; Bos BV assumes no responsibility or liability for the use of this [product, application,
; software, any of these products. Bos BV conveys no license, implicitly or otherwise, under
; any intellectual property rights. Information contained in this publication regarding
; (e.g.: application, implementation) and the like is intended through suggestion only and may
; be superseded by updates. Bos BV makes no representation or warranties with respect to the
; accuracy. Use of these information, or infringement of patents arising from such use or otherwise.
;
; Author: Peter Verkaik
; Email: peterverkaik@boselectro.nl
; Website: http://www.boselectro.nl/synop.html
; Discussion group: http://groups.yahoo.com/group/synop/
;
;*****************************************************************************************
;
; SPECIAL NOTE !!!
;
; This program for the COM device differs slightly from the IOC devices
; regarding the tokenbus, I2C slave id and broadcast command behaviour.
; The differences are:
;
;   COM device						  IOC device
;
; - acts only as I2C slave				- may act as I2C slave or I2C multimaster
;
; - always uses I2C id $18				- may use dynamic or static I2C id
;   although the tokenbits A7-A5			  but can address the COM device
;   are retransmitted to the stamp			  in case of an IOC master device
;
; - the token is not sent to the stamp			- after the token is received and processed
;   until a handshake signal from the			  it is immediately sent out to the next
;   stamp is received					  device on the tokenbus
;
; - this device accepts a broadcast (id $10)		- this (slave) device accepts a broadcast command
;   command and generates the ACK signals		  without generating ACK signals but the
;   but the received command is simply ignored		  received command is executed
;
;-----------------------------------------------------------------------------------------
;
; Program Description:
;
;	This program provides 3 buffered serial channels to a basic stamp.
;	The channels are accessed via the I2C bus.
;
;	This program has mainline code with a mainloop that accepts
;	a command via the i2c slave, and performs the task as indicated by the
;	command. It further checks for incoming serial data and handles it.
;	Finally, any serial output data is handled.
;
;-----------------------------------------------------------------------------------------
;
; SYNOP rev.D board information
;
;	The SYNOP board basically consists of 12 microcontrollers that share an I2C bus and that
;	are also daisychained linked using a unidirectional serial bus referred to as tokenbus.
;	The 12 microcontrollers are: 1 main processor unit (MPU), 10 I/O control units (IOC) and
;	1 communication unit (COM). The MPU is a basic stamp (or microprocessor with compatible
;	footprint) that is programmed in basic. The basic stamp lacks buffered I/O and interrupts
;	but has very short program development time. The COM unit (SX18 or PIC) provides 3 buffered
;	TX/RX channels to the stamp (2x rs232, 1x rs485) and the IOC units (SX18 or PIC) provide 10
;	buffered 8 digital I/O pins. The IOC units can be programmed to whatever function you require
;	if it is within the microcontrollers capabilities. Besides the tokenbus connection there
;	is a second serial connection between the stamp and the COM unit, basically providing
;	a serial channel with handshake between stamp and COM.
;
;	The microcontrollers share information using the I2C bus. This code uses only
;	the 7-bit addressing mode for a 100 kbit/s I2C bus as defined by the I2C specifications.
;	The tokenbus is a proprietary design and is implemented using a unidirectional uart VP.
;	It is used only to initialize the onboard microcontrollers that are connected to the tokenbus,
;	let the stamp provide I2C busmastership information to them and to let the stamp receive service
;	information from them.
;
;	The stamp does not support a multimaster I2C bus and also can not act as a I2C slave.
;	By means of the token the stamp can let the other microcontrollers know to disengage the
;	I2C bus temporarily so it can be used by the stamp.
;
; Token format
;
;	byte2    byte1    byte0
;	AAAAAAA1 RBMSSSS1 SSSSSSS0
;	|||||||| |||||||| |||||||+-- defines first byte of token
;	|||||||| |||||||| +++++++--- service flags IOC7-IOC1, set by IOC and COM
;	|||||||| |||||||+----------- defines next byte of token
;	|||||||| |||++++------------ service flags COM and IOC10-IOC8, set by IOC and COMM
;	|||||||| ||+---------------- set by IOC and COM to claim singlemaster i2c bus
;	|||||||| |+----------------- i2c bus type, 0=singlemaster 1=multimaster, defined by stamp
;	|||||||| +------------------ set by stamp to request I2C bus
;	|||||||+-------------------- defines next byte of token
;	|||++++--------------------- dynamic address id bits A4-A1 for IOC and COM connected to tokenbus
;	+++------------------------- static address id bits A7-A5, set by Basic Stamp
;
;	bit0 of each byte determines a first byte (bit0=0) or a next byte (bit0=1)
;	bit7 of byte1 (R) when set tells this device to enter I2C slave mode until a token with R=0 is received
;	bit6 of byte1 (B) when set tells this device that multimaster I2C is enabled,
;	  otherwise only singlemaster mode and slave mode.
;	  So this device runs in four possible modes
;	  1. I2C disabled - powerup/reset until a token is received
;	  2. I2C slave mode - (R=1) OR (R=0 and B=0 and M=1)
;	  3. I2C singlemaster mode - (R=0 and B=0 and M=0) (this device sets M=1 to claim i2c bus)
;	  4. I2C multimaster mode - (R=0 and B=1)
;	the service flag for this device is set by this device when it requires service from the stamp
;
; Program options
;
;	A number of options are present to include/exclude some portions of code
;	For this device ALL options must be enabled
;
;	TOKEN_BUS		if present the tokenbus code is compiled
;				if omitted the ports RA2 and RA3 remain inputs and must not be used
;
;	I2C_SLAVE		if present the I2C slave code is compiled
;				if omitted the I2C slave code is not compiled
;
;	I2C_BROADCAST		if present the I2C slave will respond to static/dynamic and broadcast id
;				if omitted the I2C slave only responds to the static/dynamic id
;
;	if I2C_SLAVE is not selected, ports RA0 and RA1 remain inputs and must not be used
;	if TOKEN_BUS is selected, dynamic addressing is used, otherwise SLAVE_STATIC_ID
;	This device always uses id $18

;-----------------------------------------------------------------------------------------
; program modules
;-----------------------------------------------------------------------------------------
device		SX18L,oschs1,turbo,stackx,optionx	;sx device options
irc_cal		IRC_SLOW
id		'SYNOPCOM'				;SYNOP rev.D COM device
reset		reset_entry				;set reset vector
;select one of the following
;RESONATOR	equ	20				;20 MHz
RESONATOR	equ	50				;50 MHz
;RESONATOR	equ	75				;75 MHz
freq		RESONATOR * 1000000			;resonator frequency

; uncomment any of the following lines to enable the module

TOKEN_BUS			;must be enabled
I2C_SLAVE			;must be enabled
I2C_BROADCAST			;must be enabled

; set static slave id (used if TOKEN_BUS not selected
SLAVE_STATIC_ID	= $18		;special id for COM device	** DO NOT CHANGE

; broadcast id
BROADCAST_ID	= $10		;special SYNOP board i2c broadcast id	** DO NOT CHANGE

; set devicetype values for this device
SYNOPDEVICE_MAJOR	=	$02		;identifies COM device
SYNOPDEVICE_MINOR	=	$00		;identifies this specific code

;-----------------------------------------------------------------------------------------
; schematic connection stamp - COM
;-----------------------------------------------------------------------------------------
;
;   +------+                        +------------+
;   |      |                        |       rb.1 o-----> uart3_wr_pin
;   |      |           token output |       rb.2 o-----< uart3_rx_pin
;   |      o<-----------------------o ra.3  rb.3 o-----> uart3_tx_pin
;   |      |                        |            |
;   |      |                        |       rb.4 o-----< uart1_rx_pin
;   |      |                        |       rb.5 o-----> uart1_tx_pin
;   |      |           handshake in |            |
;   |      o----------------------->o rb.0  rb.6 o-----> uart2_tx_pin
;   |      |                        | ra.2  rb.7 o-----< uart2_rx_pin
;   +------+ stamp             comm +-o----------+
;                                     +----------------< tokenbus input

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

if RESONATOR=75
;uartfs=288*1200=345600
;int_period=75000000/345600=217
;288*1200=3*96*1200=3*48*2400=3*24*4800=3*12*9600=3*6*19200=3*3*38400=3*2*57600

uartfs			= 345600	; uart's basic frequency 288 * 1200 = 345600
int_period		= 217		; used as seed for uart baud rate.

endif

if RESONATOR=50
;uartfs=192*1200=230400
;int_period=50000000/230400=217
;192*1200=3*64*1200=3*32*2400=3*16*4800=3*8*9600=3*4*19200=3*2*38400

uartfs			= 230400	; uart's basic frequency 192 * 1200 = 230400
int_period		= 217		; used as seed for uart baud rate.

endif

if RESONATOR=20
;uartfs=96*1200=115600
;int_period=20000000/115600=174
;96*1200=3*32*1200=3*16*2400=3*8*4800=3*4*9600=3*2*19200

uartfs			= 115600	; uart's basic frequency 96 * 1200 = 115600
int_period		= 174		; used as seed for uart baud rate.

endif

THREADS			equ	3	; DO NOT CHANGE. Each thread is executed once every 3 interrupts

;Tokenbus baudrate - DO NOT CHANGE

	uart0_baud	= 9600

	uart0_divide	= uartfs / (uart0_baud * THREADS)
        uart0_StDelay 	= uart0_divide + (uart0_divide / 2)

;XON/XOFF character for xon/xoff handshaking

	XON		= 17		;XON character (ctrl-Q)
	XOFF		= 19		;XOFF character (ctrl-S)

;The following baudrates are available for uart1-uart3

	uartx_baud0	= 1200
	uartx_baud1	= 2400
	uartx_baud2	= 4800
	uartx_baud3	= 9600
	uartx_baud4	= 19200
	uartx_baud5	= 38400		;only for resonator=50 and resonator=75
	uartx_baud6	= 57600		;only for resonator=75

	uartx_divide0	= uartfs / (uartx_baud0 * THREADS)
        uartx_StDelay0 	= uartx_divide0 + (uartx_divide0 / 2)

	uartx_divide1	= uartfs / (uartx_baud1 * THREADS)
        uartx_StDelay1 	= uartx_divide1 + (uartx_divide1 / 2)

	uartx_divide2	= uartfs / (uartx_baud2 * THREADS)
        uartx_StDelay2 	= uartx_divide2 + (uartx_divide2 / 2)

	uartx_divide3	= uartfs / (uartx_baud3 * THREADS)
        uartx_StDelay3 	= uartx_divide3 + (uartx_divide3 / 2)

	uartx_divide4	= uartfs / (uartx_baud4 * THREADS)
        uartx_StDelay4 	= uartx_divide4 + (uartx_divide4 / 2)

	uartx_divide5	= uartfs / (uartx_baud5 * THREADS)
        uartx_StDelay5 	= uartx_divide5 + (uartx_divide5 / 2)

	uartx_divide6	= uartfs / (uartx_baud6 * THREADS)
        uartx_StDelay6 	= uartx_divide6 + (uartx_divide6 / 2)

;-----------------------------------------------------------------------------------------
; Pin definitions & Port assignments
;-----------------------------------------------------------------------------------------

; port A definitions/assignments *** DO NOT CHANGE ***

sda		equ	0
scl		equ	1
rx		equ	2
tx		equ	3
i2c_token_port	equ	ra
  i2c_scl_pin	equ	i2c_token_port.scl	; i2c clock
  i2c_sda_pin	equ	i2c_token_port.sda	; i2c data I/O
  uart0_rx_pin	equ	i2c_token_port.rx	; uart receive input (tokenbus input)
  uart0_tx_pin	equ	i2c_token_port.tx	; uart transmit output (tokenbus output)

; i2c and uart0 change 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

; port B definitions/assignments *** set as required by application ***

hs		equ	0
wr3		equ	1
rx3		equ	2
tx3		equ	3
rx1		equ	4
tx1		equ	5
tx2		equ	6
rx2		equ	7

user_port	equ	rb
  uart0_tx_ena	equ	user_port.hs	;uart0 transmit enable input (handshake input) 0=enable 1=disable
  uart3_wr_pin	equ	user_port.wr3	;uart3 rx/tx select	rs485, port3, 1=write, 0=read
  uart3_rx_pin	equ	user_port.rx3	;uart3 rx input		rs485, port3
  uart3_tx_pin	equ	user_port.tx3	;uart3 tx output	rs485, port3
  uart1_rx_pin	equ	user_port.rx1	;uart1 rx input		rs232, port1
  uart1_tx_pin	equ	user_port.tx1	;uart1 tx output	rs232, port1
  uart2_tx_pin	equ	user_port.tx2	;uart2 tx output	rs232, port2
  uart2_rx_pin	equ	user_port.rx2	;uart2 rx input		rs232, port2

RB_latch	equ	%00000010	;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	%11111111	;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

;-----------------------------------------------------------------------------------------
; Code Locations
;-----------------------------------------------------------------------------------------
CODEPAGE_0	equ	$0
CODEPAGE_1	equ	$200
CODEPAGE_2	equ	$400
CODEPAGE_3	equ	$600

;-----------------------------------------------------------------------------------------
; Data Memory address definitions
;-----------------------------------------------------------------------------------------
global_org	=	$08
bank0_org	=	$10
bank1_org	=	$30
bank2_org	=	$50
bank3_org	=	$70
bank4_org	=	$90
bank5_org	=	$B0
bank6_org	=	$D0
bank7_org	=	$F0

;-----------------------------------------------------------------------------------------
; Global Register definitions
;-----------------------------------------------------------------------------------------
		org     global_org

isr_temp		equ	global_org-1	; rc is available as general purpose register in SX18
w_save			equ	global_org+0	; to save w during ram bank switch

IFDEF TOKEN_BUS
token_flags		equ	global_org+1
  token_init		equ	token_flags.0	; set when first valid token received by uart0
  token_hold		equ	token_flags.1	; set if token received
  token_service		equ	token_flags.2	; set if this device requires service from stamp
  token_write		equ	token_flags.3	; set when sending token to next device
  tstate0		equ	token_flags.4	; states to receive/send token
  tstate1		equ	token_flags.5
  uart0_rx_flag		equ	token_flags.6	; set if byte received by uart0 (token byte)
ENDIF

IFDEF I2C_SLAVE
i2c_flags		equ	global_org+2	; global i2c flags
  i2cs_stop_found	equ	i2c_flags.0	; set by stop and cleared by start condition
  i2cs_rx_flag		equ	i2c_flags.1	; set when this slave has received data
  i2cs_broadcast	equ	i2c_flags.2	; set when this slave is addressed by broadcast id
  i2cs_written_again	equ	i2c_flags.3	; set when this slave receives 2nd or later databyte
  i2cs_data_needed	equ	i2c_flags.4	; set when this slave requires data
  uart1_tx_enable	equ	i2c_flags.5	; set if uart1 tx output enabled by xon/xoff
  uart2_tx_enable	equ	i2c_flags.6	; set if uart2 tx output enabled by xon/xoff
  uart1_hs_flag		equ	i2c_flags.7	; set if uart1 handshake on
ENDIF

uart_flags1		equ	global_org+3
  uartx_tx_isr0		equ	uart_flags1.0	;bits 0 and 1 dest for current transmission
  uartx_tx_isr1		equ	uart_flags1.1	;  (may be set to other uart for XON/XOFF)
  uartx_tx_dest0	equ	uart_flags1.2	;bits 2 and 3 dest for current command
  uartx_tx_dest1	equ	uart_flags1.3	;  (0=none, 1=uart1, 2=uart2, 3=uart3)
  uart2_hs_flag		equ	uart_flags1.4	;set if uart1 handshake on
  uart1_rx_flag		equ	uart_flags1.5	;set if byte received by uart1
  uart2_rx_flag		equ	uart_flags1.6	;set if byte received by uart2
  uart3_rx_flag		equ	uart_flags1.7	;set if byte received by uart3

uart_flags2		equ	global_org+4
  uartx_data		equ	uart_flags2.0	;set if uartx_outbuf holds data
  uart1_data		equ	uart_flags2.1	;set if uart1_inbuf holds data
  uart2_data		equ	uart_flags2.2	;set if uart2_inbuf holds data
  uart3_data		equ	uart_flags2.3	;set if uart3_inbuf holds data
  uartx_full		equ	uart_flags2.4	;set if uartx_outbuf is full
  uart1_overrun		equ	uart_flags2.5	;set if uart1_inbuf overruns
  uart2_overrun		equ	uart_flags2.6	;set if uart2_inbuf overruns
  uart3_overrun		equ	uart_flags2.7	;set if uart3_inbuf overruns

uart_flags3		equ	global_org+5	;uart1 and uart2 handshake flags
  uart1_tx_xonxoff	equ	uart_flags3.0	;set if Tx supports xon/xoff
  uart1_tx_hardwire	equ	uart_flags3.1	;set if Tx is used for handshake
  uart1_rx_xonxoff	equ	uart_flags3.2	;set if Rx supports xon/xoff
  uart1_rx_hardwire	equ	uart_flags3.3	;set if Rx is used for handshake
  uart2_tx_xonxoff	equ	uart_flags3.4	;set if Tx supports xon/xoff
  uart2_tx_hardwire	equ	uart_flags3.5	;set if Tx is used for handshake
  uart2_rx_xonxoff	equ	uart_flags3.6	;set if Rx supports xon/xoff
  uart2_rx_hardwire	equ	uart_flags3.7	;set if Rx is used for handshake

uartx_port_buf		equ	global_org+6	;buffered data for port B direction register

command			equ	global_org+7	;used by mainline command menu

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

	; Bank 0 - Token variables ram bank
	;---------------------------------------------------------------------------------
		org     bank0_org

bank0		=	$

IFDEF TOKEN_BUS
token		=	$	;token bank
uart0_tx_high	ds      1	;hi byte to transmit
uart0_tx_low	ds      1	;low byte to transmit
uart0_tx_count	ds      1	;number of bits sent
uart0_tx_divide	ds      1	;xmit timing counter
uart0_rx_count	ds      1	;number of bits received
uart0_rx_divide	ds      1	;receive timing counter
uart0_rx_byte	ds      1	;buffer for incoming byte
uart0_port_buf	ds	1	;this buffer holds the current state of the uart0 port direction reg's
token0		ds	1	;receive/transmit buffer for token
token1		ds	1
token2		ds	1
token_rx_byte	ds	1	;dtore incoming tokenbyte
id_index	ds	1	;dynamic id i2c slave index to update token service flag
ENDIF
free1		ds	1	;not used
free2		ds	1	;not used
free3		ds	1	;not used

	; Bank 1 - I2C Slave variables and timer ram bank
	;---------------------------------------------------------------------------------
		org     bank1_org

bank1		=	$

clock		=	$
timer		ds	1			;increments every ISR
IFDEF I2C_SLAVE
i2cs		=	$	; i2cs Bank (I2C slave)
i2cs_state	ds	1	; This indicates the state that the I2C slave is currently in.
i2cs_sub_state	ds	1	; This indicates the substate that the I2C slave is currently in.
i2cs_port_buf	ds	1	; This buffer holds the current state of the I2C port direction reg's
i2cs_bit_count	ds	1	; Indicates the number of bits left to process in read/write
i2cs_byte	ds	1	; The byte currently being written/read by the I2C slave
i2cs_past_pres	ds	1	; The last_state of the I2C port (to be compared with the present state)
  i2cs_pres_sda		equ	i2cs_past_pres.0 ; The present state of the SDA line
  i2cs_pres_scl		equ	i2cs_past_pres.1 ; The present state of the SCL line
  i2cs_past_sda		equ	i2cs_past_pres.2 ; The state of the SDA line on last interrupt
  i2cs_past_scl		equ	i2cs_past_pres.3 ; The state of the SCL line on last interrupt
i2cs_id		ds	1	; This byte holds the id which the I2C slave will respond to.
i2cs_in_index	ds	1	;argument count
i2cs_out_index	ds	1	;number of bytes in i2cs_outbuffer
i2cs_out_tail	ds	1	;index of next data to send from i2cs_outbuffer
i2cs_inbuf	ds	1	;one byte input buffer
i2cs_outbuffer	=	$
i2cs_outbuf0	ds	1	;four byte output buffer
i2cs_outbuf1	ds	1
i2cs_outbuf2	ds	1
i2cs_outbuf3	ds	1
ENDIF

	; Bank 2 - uart1,uart2,uart3 transmit/receive variables ram bank
	;---------------------------------------------------------------------------------
		org     bank2_org

bank2		=	$
uart_io		=	$

uart1_rx_count	ds      1	;number of bits received
uart1_rx_divide	ds      1	;receive timing counter
uart1_rx_byte	ds      1	;buffer for incoming byte
uart1_config	ds	1	;lownib=config uart1_tx, highnib=config uart1_rx
				;0bbb=baud, 10xx=low handshake, 11xx=high handshake
				;baud=1200,2400,4800,9600,19200[,38400,57600,115200]
  uart1_tx_hs	equ	uart1_config.3	;xon/xoff or handshake level to allow receiving
  uart1_rx_hs	equ	uart1_config.7	;xon/xoff or handshake level to allow transmitting
uart1_rx_store	ds	1	;last received byte

uart2_rx_count	ds      1	;number of bits received
uart2_rx_divide	ds      1	;receive timing counter
uart2_rx_byte	ds      1	;buffer for incoming byte
uart2_config	ds	1	;lownib=config uart2_tx, highnib=config uart2_rx
				;0bbb=baud, 10xx=low handshake, 11xx=high handshake
				;baud=1200,2400,4800,9600,19200[,38400,57600,115200]
  uart2_tx_hs	equ	uart2_config.3	;xon/xoff or handshake level to allow receiving
  uart2_rx_hs	equ	uart2_config.7	;xon/xoff or handshake level to allow transmitting
uart2_rx_store	ds	1	;last received byte

uart3_rx_count	ds      1	;number of bits received
uart3_rx_divide	ds      1	;receive timing counter
uart3_rx_byte	ds      1	;buffer for incoming byte
uart3_config	ds	1	;lownib=config uart3_tx, highnib=config uart3_rx
				;xbbb=baud
				;baud=1200,2400,4800,9600,19200[,38400,57600,115200]
uart3_rx_store	ds	1	;last received byte

free4		ds	1	;not used

	; Bank 3 - uartx transmit, uartx,uart1,uart2,uart3 buffer qeue variables ram bank
	;---------------------------------------------------------------------------------
		org     bank3_org

bank3		=	$
uart_qeue	=	$

uartx_head	ds	1	;index of next free entry in uartx_outbuf
uartx_tail	ds	1	;index of next byte to read from uartx_outbuf
uartx_num	ds	1	;number of bytes in uartx_outbuf

uart1_head	ds	1	;index of next free entry in uart1_inbuf
uart1_tail	ds	1	;index of next byte to read from uart1_inbuf
uart1_num	ds	1	;number of bytes in uart1_inbuf

uart2_head	ds	1	;index of next free entry in uart2_inbuf
uart2_tail	ds	1	;index of next byte to read from uart2_inbuf
uart2_num	ds	1	;number of bytes in uart2_inbuf

uart3_head	ds	1	;index of next free entry in uart3_inbuf
uart3_tail	ds	1	;index of next byte to read from uart3_inbuf
uart3_num	ds	1	;number of bytes in uart3_inbuf

uartx_tx_high	ds      1	;hi byte to transmit
uartx_tx_low	ds      1	;low byte to transmit
uartx_tx_count	ds      1	;number of bits sent
uartx_tx_divide	ds      1	;xmit timing counter

	; Bank 4 - uartx output buffer ram bank
	;---------------------------------------------------------------------------------
		org     bank4_org

bank4		=	$
uartx_outbuf	=	$
		ds	16	;uart output buffer		;for data send out by uarts
uartx_outbuf_end
uartx_bufsize	=	uartx_outbuf_end - uartx_outbuf

	; Bank 5 - uart1 input buffer ram bank
	;---------------------------------------------------------------------------------
		org     bank5_org

bank5		=	$
uart1_inbuf	=	$
		ds	16	;uart1 input buffer (port1)
uart1_inbuf_end
uart1_bufsize	=	uart1_inbuf_end - uart1_inbuf
uart1_hwoff	=	uart1_bufsize-3	;turn hardwire hs off if stored bytes >= uart1_hwoff
uart1_hwon	=	3		;turn hardwire hs on if stored bytes <= uart1_hwon
uart1_xxoff	=	uart1_bufsize-8	;turn xon/xoff hs off if stored bytes >= uart1_xxoff
uart1_xxon	=	4		;turn xon/xoff hs on if stored bytes <= uart1_xxon

	; Bank 6 - uart2 input buffer ram bank
	;---------------------------------------------------------------------------------
		org     bank6_org

bank6		=	$
uart2_inbuf	=	$
		ds	16	;uart2 input buffer (port2)
uart2_inbuf_end
uart2_bufsize	=	uart2_inbuf_end - uart2_inbuf
uart2_hwoff	=	uart2_bufsize-3	;turn hardwire hs off if stored bytes >= uart2_hwoff
uart2_hwon	=	3		;turn hardwire hs on if stored bytes <= uart2_hwon
uart2_xxoff	=	uart2_bufsize-8	;turn xon/xoff hs off if stored bytes >= uart2_xxoff
uart2_xxon	=	4		;turn xon/xoff hs on if stored bytes <= uart2_xxon

	; Bank 7 - uart3 input buffer ram bank
	;---------------------------------------------------------------------------------
		org     bank7_org

bank7		=	$
uart3_inbuf	=	$
		ds	16	;uart3 input buffer (port3)
uart3_inbuf_end
uart3_bufsize	=	uart3_inbuf_end - uart3_inbuf

;-------------- end of ram variables -----------------------------------------------------


;*****************************************************************************************
		org	CODEPAGE_0
;*****************************************************************************************


;-----------------------------------------------------------------------------------------
; Interrupt Service Routine
;-----------------------------------------------------------------------------------------
; Note: The interrupt code must always originate at address $0.
; Interrupt Frequency = (Cycle Frequency / -(retiw value)) 

interrupt				;3
		jmp	inc_timer

;-----------------------------------------------------------------------------------------
; Initialization routines for tokenbus, i2c slave and i2c master
;-----------------------------------------------------------------------------------------

IFDEF TOKEN_BUS

	; Function: uart_init
	;	This subroutine should be called on initialization of the program.  It
	;	initializes the variables which are critical to the operation of the 
	;	uart state machine.
	;---------------------------------------------------------------------------------
uart_init
		bank	TOKEN
		mov	w,#RA_tris		;Set uart0 port in/out directions
		mov	uart0_port_buf,w
		mov	w,#RB_tris
		mov	uartx_port_buf,w
		ret
ENDIF

IFDEF I2C_SLAVE

	; Function: i2cs_init
	;	This subroutine should be called on initialization of the program.  It
	;	initializes the variables which are critical to the operation of the 
	;	i2cs slave state machine.
	;---------------------------------------------------------------------------------
i2cs_init	bank	i2cs
		mov	w,#%11111111		;Set i2c port in/out directions
		mov	i2cs_port_buf,w
		mov	w,#$ff			;Let part know last states have all been highs
		mov	i2cs_past_pres,w
  IFNDEF TOKEN_BUS
		mov	w,#SLAVE_STATIC_ID
		mov	i2cs_id,w
  ENDIF
		ret

i2cs_input_task
		jmp	@i2cs_input_handler	;handles incoming data from i2c bus

i2cs_output_task
		jmp	@i2cs_output_handler	;handles outgoing data to i2c bus

ENDIF

uart1_input_task				;handle input from uart1
		jmp	@uart1_input_handler

uart2_input_task				;handle input from uart2
		jmp	@uart2_input_handler

uart3_input_task				;handle input from uart3
		jmp	@uart3_input_handler

uartx_output_task				;handle output to uart1,uart2,uart3
		jmp	@uartx_output_handler

uartx_divide_table
		and	w,#$07
		jmp	pc+w		;this clears carryflag: save databit in carryflag before call !!
		retw	uartx_divide0
		retw	uartx_divide1
		retw	uartx_divide2
		retw	uartx_divide3
		retw	uartx_divide4
		retw	uartx_divide5
		retw	uartx_divide6
		retw	0

uartx_Stdelay_table
		and	w,#$07
		jmp	pc+w
		retw	uartx_StDelay0
		retw	uartx_StDelay1
		retw	uartx_StDelay2
		retw	uartx_StDelay3
		retw	uartx_StDelay4
		retw	uartx_StDelay5
		retw	uartx_StDelay6
		retw	0

IFDEF TOKEN_BUS
	; token format
	;    byte2   bytee1    byte0
	; AAAAAAA1 RBMSSSS1 SSSSSSS0
	; A = i2c slave address bit previous device
	; R = I2C busrequest bit, set and cleared by stamp
	; B = I2C bustype bit, 0=singlemaster 1=multimaster, set and cleared by stamp
	; M = I2C singlemaster active, set and cleared by this device
	; S = service flag bit, id corresponding flag set and cleared by this device

token_input_task

	; at this point a complete tokenbyte is received
	;
	; tstate0 = 0	must receive 1st tokenbyte
	; tstate1 = X
	;
	; tstate0 = 1	must receive 2nd tokenbyte
	; tstate1 = 0
	;
	; tstate0 = 1	must receive 3rd tokenbyte
	; tstate1 = 1

		bank	token
		jb	token_hold,gettoken_done	;already holding token
		jb	tstate0,gettoken1		;if tstate0=1 then goto gettoken1
		jb	token_rx_byte.0,gettoken_done	;discard byte if not first tokenbyte
gettoken0
		mov	token0,token_rx_byte		;save token0
		setb	tstate0				;set 1st byte flag
		clrb	tstate1				;clr 2nd byte flag
		jmp	gettoken_done
gettoken1
		jb	tstate1,gettoken2		;if tstate1=1 then goto gettoken2
		jnb	token_rx_byte.0,gettoken0	;if bit0=0 then goto gettoken0
		mov	token1,token_rx_byte		;save token1
		setb	tstate1				;set 2nd byte flag
		jmp	gettoken_done
gettoken2
		jnb	token_rx_byte.0,gettoken0	;if bit0=0 then goto gettoken0
		mov	token2,token_rx_byte		;save token2
		clrb	tstate0				;clr 1st byte flag
		clrb	tstate1				;clr 2nd byte flag
		mov	w_save,token_rx_byte
		and	w_save,#$1E			;extract id bits A4-A1
		csae	w_save,#$18			;invalid id if >= $18, do not process
		setb	token_hold			;mark token valid and holding
gettoken_done
		clrb	uart0_rx_flag
		sb	token_hold
		ret

		;at this point a complete token is received

		snb	token_write
		ret
		and	token2,#$E0			;keep id bits A7-A5
		or	token2,#SLAVE_STATIC_ID		;token2 = xxx11000 for COM device
		jb	token_init,processtoken_1
		bank	i2cs
		mov	i2cs_id,#SLAVE_STATIC_ID	;make id equal to SLAVE_STATIC_ID
							;while token2 bits A7-A5 are preserved
		setb	token_init			;token initialization done
		bank	token
processtoken_1
		setb	token2.0			;keep bit0 set
		jnb	token_init,processtoken_end	;only update service flags if initialized
		mov	w,uart_flags2			;any received data?
		and	w,#$0E				;extract data receive flags for uart1,uart2 and uart3
		clrb	token_service			;no
		sz
		setb	token_service			;yes
		movb	token1.4,token_service		;set serviceflag for this device
processtoken_end
		setb	token_write			;now token is ready to be sent
		ret

token_output_task

	; at this point a complete token (3 bytes) is received
	;
	; tstate0 = 0	must send 1st tokenbyte
	; tstate1 = 0
	;
	; tstate0 = 0	release tokenHold
	; tstate1 = 1
	;
	; tstate0 = 1	must send 2nd tokenbyte
	; tstate1 = 0
	;
	; tstate0 = 1	must send 3rd tokenbyte
	; tstate1 = 1

sendtoken
		bank	token
		test	uart0_tx_count
		jnz	sendtoken_end		;wait for not busy
		jb	tstate0,sendtoken1
		jnb	tstate1,sendtoken0
		;at this point token has been sent
		clrb	token_hold		;not holding token anymore
		clrb	token_write		;wait for new token to arrive
		clrb	tstate1
		ret
sendtoken0
		jb	uart0_tx_ena,sendtoken_end	;wait for handshake
		mov	w,token0
		not     w                       ;ready bits (inverse logic)
		mov     uart0_tx_high,w         ; store data byte
		setb    uart0_tx_low.7          ; set up start bit
		mov     w,#10			;1 start + 8 data + 1 stop bit
		mov     uart0_tx_count,w
		setb	tstate0
		clrb	tstate1
		jmp	sendtoken_end
sendtoken1
		jb	tstate1,sendtoken2
		jb	uart0_tx_ena,sendtoken_end	;wait for handshake
		mov	w,token1
		not     w                       ;ready bits (inverse logic)
		mov     uart0_tx_high,w         ; store data byte
		setb    uart0_tx_low.7          ; set up start bit
		mov     w,#10			;1 start + 8 data + 1 stop bit
		mov     uart0_tx_count,w
		setb	tstate1
		jmp	sendtoken_end
sendtoken2
		jb	uart0_tx_ena,sendtoken_end	;wait for handshake
		mov	w,token2
		not     w                       ;ready bits (inverse logic)
		mov     uart0_tx_high,w         ; store data byte
		setb    uart0_tx_low.7          ; set up start bit
		mov     w,#10			;1 start + 8 data + 1 stop bit
		mov     uart0_tx_count,w
		clrb	tstate0
sendtoken_end
		ret
ENDIF

	; Keep running 8bit timer
	;---------------------------------------------------------------------------------
inc_timer
		bank	clock		;1 
		inc	timer		;1		increments every 174/20000000=8.7usec @20MHz
		jnb	timer.1,inc_timer_done
		jnb	timer.0,inc_timer_done
		inc	timer				;if xxxxxx11 then increment again -> 0,1,2,0,1,2 etc
inc_timer_done

IFDEF I2C_SLAVE

	; I2C Slave Stuff...	Runs every ISR to enable high sampling rate
	;---------------------------------------------------------------------------------
	IFDEF TOKEN_BUS
		jnb	token_init,I2C_done
	ENDIF

slave_i2c
		bank	i2cs			;1
		clc				;1 ;Save the current state and past state of the I2C bus
		snb	i2c_scl_pin			;1
		stc				;1
		rl	i2cs_past_pres		;1
		clc				;1
		snb	i2c_sda_pin			;1
		stc				;1
		rl	i2cs_past_pres		;1
		call	@i2cs_get_start_stop	;3 + 9/12 ;Check the new states to see if we've received a start or a stop
		call	@i2cs_isr		;3 + 11/30(I2C_isr length) = 37/59 ;Call I2C slave ISR
ENDIF
I2C_done

	; Update i2c_token_port and user_port with buffered port data
	;---------------------------------------------------------------------------------
		mov	isr_temp,m		;2 ; Save the m register.

		mov	m,#$0f			;1
		mov	w,#$ff

IFDEF I2C_SLAVE
		bank	i2cs			;1
		and	w,i2cs_port_buf		;1 ;update the i2c/uart0 port with the buffered port data
ENDIF

IFDEF TOKEN_BUS
		bank	TOKEN			;1
		and	w,uart0_port_buf	;1 ;update the i2c/uart0 port with the buffered port data
ENDIF
		mov	!i2c_token_port,w	;1
		mov	w,uartx_port_buf
		mov	!user_port,w		; update port B with the buffered port data
		mov	m,isr_temp		;2 ; Restore the m register.
						;=13

		bank	clock
		mov	w,timer		;calculate thread number
		and	w,#$03
		add	pc,w
		jmp	isr_thread_0	;token receive uart, uart1 receive
		jmp	isr_thread_1	;token send uart, uart2 receive
		jmp	isr_thread_2	;uart1 or uart2 or uart3 transmit, uart3 receive
		jmp	isr_thread_end	;should never occur, just failsafe

	; isr thread 0 - runs at 1/3 isr
	;---------------------------------------------------------------------------------
isr_thread_0

		mov	w,uart_flags1		;get destination for transmission
		and	w,#$03
		add	pc,w
		jmp	receive3		;none
		jmp	transmit1		;uart1
		jmp	transmit2		;uart2
		jmp	transmit3		;uart3

	;uart1 transmit

transmit1
		bank	uart_qeue
		decsz	uartx_tx_divide		;1 ;only execute the transmit routine
		jmp	txdone1			;1 
		bank	uart_io
		mov	w,uart1_config
		bank	uart_qeue
		call	uartx_divide_table	; load uart1 tx baud rate (50MHz)
		mov	uartx_tx_divide,w		;1 
		test    uartx_tx_count		;1 ;are we sending?
		snz				;1 
		jmp	txdone1			;3
		clc                             ;1 ;yes, ready stop bit
		rr      uartx_tx_high		;1 ; and shift to next bit
		rr      uartx_tx_low		;1 
		dec     uartx_tx_count		;1 ;decrement bit counter
		snb	uartx_tx_low.6		;1 ;output next bit
		clrb	uartx_port_buf.tx1	;1 ;update uartx_port_buf state
		sb	uartx_tx_low.6		;1 ;(pin change state occurs next interrupt)
		setb	uartx_port_buf.tx1	;1 
txdone1
		jmp	receive3

	;uart2 transmit

transmit2
		bank	uart_qeue
		decsz	uartx_tx_divide		;1 ;only execute the transmit routine
		jmp	txdone2			;1 
		bank	uart_io
		mov	w,uart2_config
		bank	uart_qeue
		call	uartx_divide_table	; load uart2 tx baud rate (50MHz)
		mov	uartx_tx_divide,w		;1 
		test    uartx_tx_count		;1 ;are we sending?
		snz				;1 
		jmp	txdone2			;3
		clc                             ;1 ;yes, ready stop bit
		rr      uartx_tx_high		;1 ; and shift to next bit
		rr      uartx_tx_low		;1 
		dec     uartx_tx_count		;1 ;decrement bit counter
		snb	uartx_tx_low.6		;1 ;output next bit
		clrb	uartx_port_buf.tx2	;1 ;update uartx_port_buf state
		sb	uartx_tx_low.6		;1 ;(pin change state occurs next interrupt)
		setb	uartx_port_buf.tx2	;1 
txdone2
		jmp	receive3

	;uart3 transmit

transmit3
		jb	uartx_port_buf.wr3,txdone3	;uart3 is in read mode
		bank	uart_qeue
		decsz	uartx_tx_divide		;1 ;only execute the transmit routine
		jmp	txdone3			;1 
		bank	uart_io
		mov	w,uart3_config
		bank	uart_qeue
		call	uartx_divide_table	; load uart3 tx baud rate (50MHz)
		mov	uartx_tx_divide,w		;1 
		test    uartx_tx_count		;1 ;are we sending?
		snz				;1 
		setb	uartx_port_buf.wr3	;set uart3 read mode after byte has been sent
		snz
		jmp	txdone3			;3
		clc                             ;1 ;yes, ready stop bit
		rr      uartx_tx_high		;1 ; and shift to next bit
		rr      uartx_tx_low		;1 
		dec     uartx_tx_count		;1 ;decrement bit counter
		snb	uartx_tx_low.6		;1 ;output next bit
		clrb	uartx_port_buf.tx3	;1 ;update uartx_port_buf state
		sb	uartx_tx_low.6		;1 ;(pin change state occurs next interrupt)
		setb	uartx_port_buf.tx3	;1 
txdone3

	;uart3 receive

receive3
		jnb	uartx_port_buf.wr3,rxdone3	;uart3 is in write mode
		bank	uart_io
	        sb	uart3_rx_pin		;1 ;get current rx bit
	        clc				;1 
	        snb	uart3_rx_pin		;1
	        stc				;1 
		test    uart3_rx_count          ;1 ;currently receiving byte?
		sz				;1 
		jmp	rxbit3                  ;3 ;if so, jump ahead
		mov     w,#9                    ;1 ;in case start, ready 9 bits
		sc				;1 ;skip ahead if not start bit
		mov     uart3_rx_count,w        ;1 ;it is, so renew bit count
		mov	w,<>uart3_config
		call	uartx_StDelay_table	; ready 1.5 bit periods (50MHz)
		mov     uart3_rx_divide,w	;1 
rxbit3
		decsz	uart3_rx_divide		;1 ;middle of next bit?
          	jmp	rxdone3			;3 = 25/34
		dec     uart3_rx_count          ;1 ;last bit?
		sz                              ;1 ;if not
		rr      uart3_rx_byte           ;1 ;  then save bit
		jnz	rxdone30		;1 ;  and exit
		mov	w,uart3_rx_byte
		mov	uart3_rx_store,w	;save received byte
		setb	uart3_rx_flag
rxdone30
		mov	w,<>uart3_config
		call	uartx_divide_table	; yes, ready 1 bit period (50MHz)
		mov	uart3_rx_divide,w	;1 
rxdone3
		jmp	isr_thread_end

	; isr thread 1 - runs at 1/3 isr
	;---------------------------------------------------------------------------------
isr_thread_1

IFDEF TOKEN_BUS

	;uart0 receive (token)

tokenreceive_uart
		jb	token_hold,rxdone0
		bank	TOKEN
	        sb	uart0_rx_pin		;1 ;get current rx bit
	        clc				;1 
	        snb	uart0_rx_pin		;1
	        stc				;1 
		test    uart0_rx_count          ;1 ;currently receiving byte?
		sz				;1 
		jmp	rxbit0                  ;3 ;if so, jump ahead
		mov     w,#9                    ;1 ;in case start, ready 9 bits
		sc				;1 ;skip ahead if not start bit
		mov     uart0_rx_count,w        ;1 ;it is, so renew bit count
		mov     w,#uart0_StDelay	;1 ;ready 1.5 bit periods (50MHz)
		mov     uart0_rx_divide,w	;1 
rxbit0
		decsz	uart0_rx_divide		;1 ;middle of next bit?
          	jmp	rxdone0			;3 = 25/34
		mov	w,#uart0_divide		;1 ;yes, ready 1 bit period (50MHz)
		mov	uart0_rx_divide,w	;1 
		dec     uart0_rx_count          ;1 ;last bit?
		sz                              ;1 ;if not
		rr      uart0_rx_byte           ;1 ;  then save bit
		jnz	rxdone0			;1 ;  and exit
		mov	token_rx_byte,uart0_rx_byte
		setb	uart0_rx_flag
rxdone0
ENDIF
	;uart1 receive

receive1
		bank	uart_io
	        sb	uart1_rx_pin		;1 ;get current rx bit
	        clc				;1 
	        snb	uart1_rx_pin		;1
	        stc				;1 
		test    uart1_rx_count          ;1 ;currently receiving byte?
		sz				;1 
		jmp	rxbit1                  ;3 ;if so, jump ahead
		mov     w,#9                    ;1 ;in case start, ready 9 bits
		sc				;1 ;skip ahead if not start bit
		mov     uart1_rx_count,w        ;1 ;it is, so renew bit count
		mov	w,<>uart1_config
		call	uartx_StDelay_table	; ready 1.5 bit periods (50MHz)
		mov     uart1_rx_divide,w	;1 
rxbit1
		decsz	uart1_rx_divide		;1 ;middle of next bit?
          	jmp	rxdone1			;3 = 25/34
		dec     uart1_rx_count          ;1 ;last bit?
		sz                              ;1 ;if not
		rr      uart1_rx_byte           ;1 ;  then save bit
		jnz	rxdone10		;1 ;  and exit
		mov	w,uart1_rx_byte
		mov	uart1_rx_store,w	;save received byte
		setb	uart1_rx_flag
rxdone10
		mov	w,<>uart1_config
		call	uartx_divide_table	; ready 1 bit period (50MHz)
		mov	uart1_rx_divide,w	;1 
rxdone1
		jmp	isr_thread_end

	; isr thread 2 - runs at 1/3 isr
	;---------------------------------------------------------------------------------
isr_thread_2

IFDEF TOKEN_BUS

	;uart0 transmit (token)

tokensend_uart
		jnb	token_write,txdone0
		bank	TOKEN			;1 ;switch to serial register bank
		decsz	uart0_tx_divide		;1 ;only execute the transmit routine
		jmp	txdone0			;1
		mov	w,#uart0_divide		;1 ;load uart baud rate (50MHz)
		mov	uart0_tx_divide,w	;1 
		test    uart0_tx_count		;1 ;are we sending?
		snz				;1 
		jmp	txdone0			;3
		clc                             ;1 ;yes, ready stop bit
		rr      uart0_tx_high		;1 ; and shift to next bit
		rr      uart0_tx_low		;1 
		dec     uart0_tx_count		;1 ;decrement bit counter
		snb	uart0_tx_low.6		;1 ;output next bit
		clrb	uart0_port_buf.tx	;1 ;update uart0_port_buf state
		sb	uart0_tx_low.6		;1 ;(pin change state occurs next interrupt)
		setb	uart0_port_buf.tx	;1 
txdone0
ENDIF
	;uart2 receive

receive2
		bank	uart_io
	        sb	uart2_rx_pin		;1 ;get current rx bit
	        clc				;1 
	        snb	uart2_rx_pin		;1
	        stc				;1 
		test    uart2_rx_count          ;1 ;currently receiving byte?
		sz				;1 
		jmp	rxbit2                  ;3 ;if so, jump ahead
		mov     w,#9                    ;1 ;in case start, ready 9 bits
		sc				;1 ;skip ahead if not start bit
		mov     uart2_rx_count,w        ;1 ;it is, so renew bit count
		mov	w,<>uart2_config
		call	uartx_StDelay_table	; ready 1.5 bit periods (50MHz)
		mov     uart2_rx_divide,w	;1 
rxbit2
		decsz	uart2_rx_divide		;1 ;middle of next bit?
          	jmp	rxdone2			;3 = 25/34
		dec     uart2_rx_count          ;1 ;last bit?
		sz                              ;1 ;if not
		rr      uart2_rx_byte           ;1 ;  then save bit
		jnz	rxdone20		;1 ;  and exit
		mov	w,uart2_rx_byte
		mov	uart2_rx_store,w	;save received byte
		setb	uart2_rx_flag
rxdone20
		mov	w,<>uart2_config
		call	uartx_divide_table	; ready 1 bit period (50MHz)
		mov	uart2_rx_divide,w	;1 
rxdone2

isr_thread_end

	; Set Interrupt Rate
	;---------------------------------------------------------------------------------
isr_end
		mov	w,#(-int_period)&255		;1 ; refresh RTCC on return
		retiw				;3 ; 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
	;---------------------------------------------------------------------------------
		mov	m,#$0A			; point mode to WKED
		mov	!rb,#RB_wked
		mov	m,#$0B			; point mode to WKEN
		mov	!rb,#RB_wken
		mov	m,#$0C			; point mode to ST
		mov	!rb,#RB_st
		mov	m,#$0D			; point mode to LVL
		mov	!rb,#RB_lvl
		mov	!ra,#RA_lvl
		mov	m,#$0E			; point mode to PLP
		mov	!rb,#RB_plp
		mov	!ra,#RA_plp
		mov	rb,#RB_latch		; init latches
		mov	ra,#RA_latch
		mov	m,#$0F			; point mode to TRIS
		mov	!rb,#RB_tris
		mov	!ra,#RA_tris

	; Clear all Data RAM locations
	;---------------------------------------------------------------------------------
		mov	fsr,#$07
: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 I2C program registers
	;---------------------------------------------------------------------------------

IFDEF TOKEN_BUS
		call	uart_init
ENDIF

IFDEF I2C_SLAVE
		call	i2cs_init
ENDIF

		setb	uart1_tx_enable		;enable uart outputs for xon/xoff
		setb	uart2_tx_enable

	; Setup option register. 
	;---------------------------------------------------------------------------------

RTCC_ON		=	%10000000	;Enables RTCC at address $01 (RTW hi)
					;*WREG at address $01 (RTW lo) by default
RTCC_ID		=	%01000000	;Disables RTCC edge interrupt (RTE_IE hi)
					;*RTCC edge interrupt (RTE_IE lo) enabled by default
RTCC_INC_EXT	=	%00100000	;Sets RTCC increment on RTCC pin transition (RTS hi)
					;*RTCC increment on internal instruction (RTS lo) is defalut
RTCC_FE		=	%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	=	%00000000	;Assigns prescaler to RTCC (PSA lo)
RTCC_PS_OFF	=	%00001000	;Assigns prescaler to RTCC (PSA lo)
PS_000		=	%00000000	;RTCC = 1:2, WDT = 1:1
PS_001		=	%00000001	;RTCC = 1:4, WDT = 1:2
PS_010		=	%00000010	;RTCC = 1:8, WDT = 1:4
PS_011		=	%00000011	;RTCC = 1:16, WDT = 1:8
PS_100		=	%00000100	;RTCC = 1:32, WDT = 1:16
PS_101		=	%00000101	;RTCC = 1:64, WDT = 1:32
PS_110		=	%00000110	;RTCC = 1:128, WDT = 1:64
PS_111		=	%00000111	;RTCC = 1:256, WDT = 1:128

		mov	w,#RTCC_PS_OFF	;setup option register
		mov	!option,w

	; Main
	;---------------------------------------------------------------------------------

main
IFDEF TOKEN_BUS
main1
		snb	uart0_rx_flag
		call	token_input_task		;process received token bytes
		snb	token_write
		call	token_output_task		;send out received (and updated) token
		jnb	token_init,main1		;wait for first token before using i2c
ENDIF
IFDEF I2C_SLAVE
		snb	i2cs_rx_flag
		call	i2cs_input_task			;handle data written to this device
		snb	i2cs_data_needed
		call	i2cs_output_task		;supply data read from this device
ENDIF
		snb	uart1_rx_flag
		call	uart1_input_task		;handle input from uart1
		snb	uart2_rx_flag
		call	uart2_input_task		;handle input from uart2
		snb	uart3_rx_flag
		call	uart3_input_task		;handle input from uart3
		snb	uartx_data
		call	uartx_output_task		;handle output to uart
		jmp	main

;-------------- codepage 0 end -----------------------------------------------------------


;*****************************************************************************************
		org	CODEPAGE_1
;*****************************************************************************************
		jmp	$	;generates error if code above crosses page boundary

IFDEF I2C_SLAVE
;-----------------------------------------------------------------------------------------
; I2C Slave Interrupt Service Routines
;-----------------------------------------------------------------------------------------

	; Function: i2cs_get_start_stop
	; This subroutine checks the i2cs_past_pres register for the presence of a start
	; or stop condition.  It takes the values present in the i2cs_past_pres register
	; and checks them against what would be in the register if a start or stop condition
	; had occured.  This subroutine should be called on every pass of the ISR, because
	; it runs independantly to the rest of the slave code and re-initializes the state
	; machine, regardless of its last state, on the receipt of these conditions.
	;---------------------------------------------------------------------------------
i2cs_get_start_stop
		bank	i2cs
		mov	w,i2cs_past_pres	;1
		and	w,#$0f			;1
		xor	w,#%00001110		;1 ;Compare with a start condition.
		snz				;1
		jmp	:start			;3
		xor	w,#%00000101		;1 ;Compare XOR of start and stop with
		sz				;1 ;what would be present if a stop was present.
		retp				;3 = 9

:stop		clr	i2cs_state		;1 ;If stop, then make the I2C slave idle
		clr	i2cs_sub_state		;1
		setb	i2cs_stop_found		;1
		retp				;3 = 12

:start		mov	w,#1			;1 ;If start, then start receiving the address.
		mov	i2cs_state,w		;1
		clr	i2cs_sub_state
		clrb	i2cs_stop_found
		clrb	i2cs_written_again
		retp				;3 = 12

	; Function: i2cs_isr
	; I2C Slave Interrupt-Driven State Machine
	;
	;	This is the main interrupt service routine for the I2C slave interrupt
	;	service routine.  It is a state machine, allowing semi-straightline 
	;	code to operate on successive calls.  The current state of this state
	;	machine is stored in the registers i2cs_state and i2cs_sub_state.  These
	;	are the discrete states of this state machine...
	;
	;  i2cs_idle
	;	i2cs_idle is the state used when no start condition has occurred
	;	and the device has not been addressed.  The I2C slave will sit in this
	;	state until the state is changed by the independant subroutine 
	;	i2cs_get_start_stop.  
	;
	;  i2cs_wait_for_scl_low
	;	This state simply waits until SCL goes low.  It is entered once
	;	i2cs_get_start_stop encounters a start condition.  Once SCL goes low,
	;	the state is incremented
	;
	;  i2cs_get_id
	;	This state prepares the i2cs_read_byte routine to get 8-bits of data. 
	;	It increments the state to i2cs_read_byte before exiting.
	;
	;  i2cs_read_byte
	;	This state reads a byte of data.  i2cs_bit_count needs to be loaded with
	;	#8 before entering this state, or it won't know to increment to the next
	;	state after 8 bits of data have been loaded.
	;
	;  i2cs_process_id
	;	This state simply performs a quick calculation to figure out if this slave
	;	was just addressed.  If it was not, then it changes the state back to 
	;	i2cs_idle.  If this slave was addressed, then this state prepares to either
	;	read data or write data, depending on the bit 1 of the first byte received.
	;
	;  i2cs_send_ack
	;	This state outputs an ACK pulse, to tell the master that data was received
	;	correctly.  It pulls SDA low while SCL is pulsed high and low by the master.
	;
	;  i2cs_waiting_for_data
	;	This state waits for data to be put in i2cs_byte.
	;
	;  i2cs_send_data
	;	This state prepares the i2cs_write_byte state to send the 8-bits of data
	;	in the i2cs_data_out register.
	;
	;  i2cs_write_byte
	;	This state outputs a byte of data, clocked out by the SCL pin.  It must be
	;	pre-prepared to send out a byte by having the i2cs_byte register loaded with
	;	valid data and having the i2cs_bit_count register loaded with #8.
	;
	;  i2cs_get_ack
	;	This state gets an ACK from the I2C master.  If an ACK is received, this state
	;	will return to waiting_for_data. If no ACK is received, the slave will be put
	;	back into its idle state.
	;
	;  i2cs_read_data
	;	This state prepares i2cs_read_byte to receive 8 bits of data.  It increments
	;	the state before exiting.
	;
	;  i2cs_read_again
	;	This state waits for more data to receive.
	;
	;  i2cs_process_data
	;	This state processes a byte of data which was just received.
	;	It sets the i2cs_rx_flag to indicate that a byte of data was received by the slave.
	;
	;---------------------------------------------------------------------------------
i2cs_isr
		bank	i2cs
		mov	w,i2cs_state	;1 
		add	PC,w		;3 ;Add the state to the program counter
					   ;and go to the state in the jump table.

		; States for I2C slave while idle
		;-------------------------------------------------------------------------
	i2cs_idle_loc		=	$
		jmp	i2cs_idle		;3 ;If I2C_state = 0, I2C is idle
		jmp	i2cs_wait_for_scl_low	;3 ;Wait for SCL line to go low after start condition
		jmp	i2cs_get_id		;3 ;Get the id
		jmp	i2cs_read_byte		;3 
		jmp	i2cs_process_id		;3 ;Check to see if this device was addressed, and set
						   ;up for read or write if it was.

		; States for I2C slave being read
		;-------------------------------------------------------------------------
	i2cs_being_read		=	$	; Go to this state if the slave is being read.
		jmp	i2cs_send_ack		;3 
	i2cs_being_read_again	=	$
		jmp	i2cs_request_for_data	;3 ;tell mainline code to supply data.
		jmp	i2cs_waiting_for_data	;3 ;wait for data.
		jmp	i2cs_send_data		;3 ;Set up to send out a byte.
		jmp	i2cs_write_byte		;3 ;Send the byte, clocked by SCL
		jmp	i2cs_get_ack		;3 ;If we receive an ACK, then we are being read again.
	
		; States for I2C slave being written
		;-------------------------------------------------------------------------
	i2cs_being_written	=	$	; Go to this state if the slave is being written
		jmp	i2cs_send_ack		;3 ;Send an ACK
	i2cs_being_written_again	=	$
		jmp	i2cs_read_data		;3 
		jmp	i2cs_read_byte		;3 
		jmp	i2cs_process_data	;3 
		jmp	i2cs_send_ack		;3 
		jmp	i2cs_read_again
ENDIF

IFDEF I2C_SLAVE

	; State: i2cs_idle
	;	i2cs_idle is the state used when no start condition has occurred
	;	and the device has not been addressed.  The I2C slave will sit in this
	;	state until the state is changed by the independant subroutine 
	;	i2cs_get_start_stop.  
	;---------------------------------------------------------------------------------
i2cs_idle	setb	i2cs_port_buf.scl	;1
		setb	i2cs_port_buf.sda	;1
		retp				;3 = 5 + 7 = 12

	; State: i2cs_wait_for_scl_low
	;	This state simply waits until SCL goes low.  It is entered once
	;	i2cs_get_start_stop encounters a start condition.  Once SCL goes low,
	;	the state is incremented
	;---------------------------------------------------------------------------------
i2cs_wait_for_scl_low
		snb	i2cs_pres_scl		;1
		retp				;3 = 4 + 7 = 11
		clrb	i2cs_port_buf.scl	;1
		inc	i2cs_state		;1
		retp				;3 = 6 + 7 = 13

	; State: i2cs_get_id
	;	This state prepares the i2cs_read_byte routine to get 8-bits of data. 
	;	It increments the state to i2cs_read_byte before exiting.
	;---------------------------------------------------------------------------------
i2cs_get_id
		mov	w,#8			;1
		mov	i2cs_bit_count,w	;1
		inc	i2cs_state		;1
		retp				;3 = 6 + 7 = 13

	; State: i2cs_read_byte
	;	This state reads a byte of data.  i2cs_bit_count needs to be loaded with
	;	#8 before entering this state, or it won't know to increment to the next
	;	state after 8 bits of data have been loaded.
	;---------------------------------------------------------------------------------
i2cs_read_byte
		mov	w,i2cs_sub_state	;1
		add	PC,w			;3
		skip	; goto state1		;1
		jmp	:state2			;3

:state1						; wait until SCL goes high
		setb	i2cs_port_buf.scl	;1
		sb	i2cs_pres_scl		;1
		retp				;3 = 10 + 7 = 17		
		inc	i2cs_sub_state		;1 ;If SCL is high, rotate in the bit
		clc				;1
		snb	i2cs_pres_sda		;1
		stc				;1
		rl	i2cs_byte		;1
		retp				;3 = 15/17 + 7 = 22/24
:state2						; wait until SCL goes low
		snb	i2cs_pres_scl		;1
		retp				;3 = 11 + 7 = 18
		clrb	i2cs_port_buf.scl	;1
		clr	i2cs_sub_state		;1
		dec	i2cs_bit_count		;1
		sz				;1
		retp				;3 = 15 + 7 = 22
		inc	i2cs_state		;1
		retp				;3 = 16 + 7 = 23
						; Stay in this i2cs_state until all 8 bits
						; are received	

	; State: i2cs_process_id
	;	This state simply performs a quick calculation to figure out if this slave
	;	was just addressed.  If it was not, then it changes the state back to 
	;	i2cs_idle.  If this slave was addressed, then this state prepares to either
	;	read data or write data, depending on the bit 1 of the first byte received.
	;---------------------------------------------------------------------------------
i2cs_process_id
		mov	w,i2cs_byte		;1 ;Get the received I2C byte
		xor	w,i2cs_id		;1 ;Compare with the id of this slave
		and	w,#$FE			;1 ;And out the R/!W bit
		clrb	i2cs_broadcast
		jz	:slave_addressed
  IFDEF I2C_BROADCAST
		mov	w,#BROADCAST_ID		;compare broadcast id
		xor	w,i2cs_byte
		and	w,#$FE
		setb	i2cs_broadcast		;identify broadcast
		jz	:slave_addressed
		clrb	i2cs_broadcast
  ENDIF
:broadcast_read					;if addressed for read using broadcast id or
		clr	i2cs_state		;1 if the device was not addressed, then
		clr	i2cs_sub_state		;1 go back to the idle state
		retp				;3 = 12 + 7 = 19
:slave_addressed
		clrb	i2cs_port_buf.scl	;1	
						; If bit zero of the incoming byte is a 0,
		snb	i2cs_byte.0		;1 ;then go to the being written state, otherwise
		jmp	:being_read		;3 ;go to the being read state
		mov	w,#(i2cs_being_written-i2cs_idle_loc) ;1
		mov	i2cs_state,w		;1
		retp				;3 = 14 + 7 = 21
:being_read
		jb	i2cs_broadcast,:broadcast_read
		mov	w,#(i2cs_being_read-i2cs_idle_loc);1
		mov	i2cs_state,w		;1
		retp				;3 = 18 + 7 = 25

	; State: i2cs_send_ack
	;	This state outputs an ACK pulse, to tell the master that data was received
	;	correctly.  It pulls SDA low while SCL is pulsed high and low by the master.
	;---------------------------------------------------------------------------------
i2cs_send_ack	
		mov	w,i2cs_sub_state	;1
		add	pc,w			;3
		jmp	:state1			;3
		jmp	:state2			;3
		jmp	:state3			;3

:state1
		;sb	i2cs_broadcast		;this COM device does generate ACK for broadcast
		clrb	i2cs_port_buf.sda	;1 ;Clear SDA to set up ACK
		inc	i2cs_sub_state		;1
		retp				;3 = 12 + 7 = 19
:state2					
		setb	i2cs_port_buf.scl	;1 ;Allow the clock to go high
		sb	i2cs_pres_scl		;1 ;Check to see if the clock is high.
		retp				;3 = 12 + 7 = 19
		inc	i2cs_sub_state		;1
		retp				;3 = 13 + 7 = 20
:state3
		snb	i2cs_pres_scl		;1
		retp				;3 = 11 + 7 = 18
		clrb	i2cs_port_buf.scl	;1
		setb	i2cs_port_buf.sda	;1
		clr	i2cs_sub_state		;1
		inc	i2cs_state		;1
		retp				;3 = 15 + 7 = 22

	; State: i2cs_read_data
	;	This state prepares i2cs_read_byte to receive 8 bits of data.  It increments
	;	the state before exiting.
	;---------------------------------------------------------------------------------
i2cs_read_data
		mov	w,#8			;1
		mov	i2cs_bit_count,w	;1
		inc	i2cs_state		;1
		retp				;3 = 13 + 7 = 20

	; State: i2cs_read_again
	;	This state prepares to re-enter i2cs_read_data to receive another 8 bits of data.
	;	It stays in this state until i2cs_rx_flag is cleared.
	;---------------------------------------------------------------------------------
i2cs_read_again
		snb	i2cs_rx_flag		;wait till mainline code has grabbed data
		retp
		setb	i2cs_written_again	; identifies 2nd or later databyte, cleared by stop condition
		mov	w,#(i2cs_being_written_again-i2cs_idle_loc) ;1
		mov	i2cs_state,w		;  ;we may receive another byte
		retp				;3 = 13 + 7 = 20

	; State: i2cs_process_data
	;	This state processes a byte of data which was just received.  It moves the 
	;	byte that was just received into the i2cs_data_in register, and sets the 
	;	i2cs_event_flag to indicate an i2cs event and sets the i2cs_rx_flag to 
	;	indicate that a byte of data was received by the slave.
	;---------------------------------------------------------------------------------
i2cs_process_data
		setb	i2cs_rx_flag		;1
		inc	i2cs_state		;1
		retp				;3 = 15 + 7 = 22

	; State: i2cs_send_data
	;	This state prepares the i2cs_write_byte state to send the 8-bits of data
	;	in the i2cs_data_out register.
	;---------------------------------------------------------------------------------
i2cs_send_data
		mov	w,#8			;1
		mov	i2cs_bit_count,w	;1
		inc	i2cs_state		;1
		clr	i2cs_sub_state		;1
		retp				;3 = 17 + 7 = 24

	; State: i2cs_write_byte
	;	This state outputs a byte of data, clocked out by the SCL pin.  It must be
	;	pre-prepared to send out a byte by having the i2cs_byte register loaded with
	;	valid data and having the i2cs_bit_count register loaded with #8.
	;---------------------------------------------------------------------------------
i2cs_write_byte
		mov	w,i2cs_sub_state	;1
		add	pc,w			;3
		jmp	:state1			;3
		jmp	:state2			;3
		jmp	:state3			;3
:state1
		rl	i2cs_byte		;1
		setb	i2cs_port_buf.sda	;1
		sc				;1
		clrb	i2cs_port_buf.sda	;1
		inc	i2cs_sub_state		;1
		retp				;3 = 15 + 7 = 22
:state2
		setb	i2cs_port_buf.scl	;1 ;allow scl to go high, indicating slave is
						; ready
		snb	i2cs_pres_scl		;1 ;If SCL is not yet high, don't proceed to next state.
		inc	i2cs_sub_state		;1
		retp				;3 = 13 + 7 = 20
:state3
		snb	i2cs_pres_scl		;1
		retp				;3 = 11 + 7 = 18
		clr	i2cs_sub_state		;1
		clrb	i2cs_port_buf.scl	;1 ;Put scl low again to indicate slave is processing
		setb	i2cs_port_buf.sda	;1 ;release sda line again
		dec	i2cs_bit_count		;1
		sz				;1 ;once we've done all 8 bits, go to next state	
		retp				;3 = 16 + 7 = 23
		inc	i2cs_state		;1
		retp				;3 = 17 + 7 = 24

	; State: i2cs_get_ack
	;	This state gets an ACK from the I2C master.  If an ACK is received, this state
	;	will return to send more data.  If no ACK is received, the slave will be put back
	;	into its idle state.
	;---------------------------------------------------------------------------------
i2cs_get_ack
		mov	w,i2cs_sub_state	;1
		add	pc,w			;3
		jmp	:state1			;3
		jmp	:state2			;3

:state1		setb	i2cs_port_buf.scl	;1
		sb	i2cs_pres_scl		;1
		retp				;3 = 12 + 7 = 19
		snb	i2cs_pres_sda		;1
		jmp	:NACK			;3 = 13 + 7 = 20
		inc	i2cs_sub_state		;1
		retp				;3 = 14 + 7 = 21

:NACK		clr	i2cs_sub_state		;1
		clr	i2cs_state		;1
		retp				;3 = 18 + 7 = 25

:state2		snb	i2cs_pres_scl		;1
		retp				;3 = 11 + 7 = 18
		clrb	i2cs_port_buf.scl	;1
		clr	i2cs_sub_state		;1
		mov	w,#(i2cs_being_read_again-i2cs_idle_loc);1
		mov	i2cs_state,w		;1
		retp				;3 = 14 + 7 = 21

	; State: i2cs_request_for_data
	;	This state tells the mainline code to supply data in the i2cs_byte register.
	;---------------------------------------------------------------------------------
i2cs_request_for_data
		setb	i2cs_data_needed	;1 mainline code must fill i2cs_byte and clear i2cs_data_needed
		inc	i2cs_state
		retp				;3 = 13 + 7 = 20

	; State: i2cs_waiting_for_data
	;	This state waits for the mainline routine to put some valid data in the
	;	i2cs_byte register.  This state is only reached if the master has
	;	indicated it wants more data by sending an ACK after the last byte.
	;---------------------------------------------------------------------------------
i2cs_waiting_for_data
		snb	i2cs_data_needed	;1 wait till mainline code has cleared i2cs_data_needed
		retp				;3 = 13 + 7 = 20
		inc	i2cs_state
		retp
ENDIF

;-------------- codepage 1 end -----------------------------------------------------------


;*****************************************************************************************
		org	CODEPAGE_2
;*****************************************************************************************
		jmp	$	;generates error if code above crosses page boundary

enqeue_uartx_outbuf	jmp	_enqeue_uartx_outbuf
enqeue_uart1_inbuf	jmp	_enqeue_uart1_inbuf
enqeue_uart2_inbuf	jmp	_enqeue_uart2_inbuf
enqeue_uart3_inbuf	jmp	_enqeue_uart3_inbuf
deqeue_uartx_outbuf	jmp	_deqeue_uartx_outbuf
deqeue_uart1_inbuf	jmp	_deqeue_uart1_inbuf
deqeue_uart2_inbuf	jmp	_deqeue_uart2_inbuf
deqeue_uart3_inbuf	jmp	_deqeue_uart3_inbuf

;uart1-uart3 configuration
;
;	RX section of uart config	TX section of uart config
;	b7 b6 b5 b4 baud		b3 b2 b1 b0 baud
;	*  0  0  0  1200		*  0  0  0  1200
;	*  0  0  1  2400		*  0  0  1  2400
;	*  0  1  0  4800		*  0  1  0  4800
;	*  0  1  1  9600		*  0  1  1  9600
;	*  1  0  0  19200		*  1  0  0  19200
;	*  1  0  1  38400		*  1  0  1  38400
;	*  1  1  0  57600		*  1  1  0  57600
;	0  1  1  1  rx = low handshake	0  1  1  1  tx = low handshake
;	1  1  1  1  rx = high handshake	1  1  1  1  tx = high handshake
;
;	* b7 set -> xon/xoff support	* b3 set -> xon/xoff support
;
;	RX xon/xoff enabled:
;	if char received is XOFF then disable transmitting
;	if char received is XON then enable transmitting
;
;	RX low/high handshake:
;	if rx input equals not b7 then disable transmitting
;	if rx input equals b7 then enable transmitting
;
;	TX xon/xoff enabled:
;	if input buffer nearly full then transmit XOFF
;	if input buffer nearly empty then transmit XON
;
;	TX low/high handshake:
;	if input buffer nearly full then set tx output to not b3
;	if input buffer nearly empty then set tx output to b3
;
;	Note: for uart3 (RS485 half duplex) handshake is done by software
;	so bits b7 and b3 don't care. Baudrates are 1200-57600.

	;copy bits 0,1 of w to bits 2,3 of w

enable_uart1_receive
		call	uart1_rts_on
		retp

enable_uart2_receive
		call	uart2_rts_on
		retp

uart1_rts_off
		sb	uart1_hs_flag			;hs already off?
		ret					;yes
		bank	uart_qeue
		jb	uart1_tx_xonxoff,rts1_xoff	;xonxoff handshake
		sb	uart1_tx_hardwire
		ret					;no handshake
		csae	uart1_num,#uart1_hwoff
		ret
		bank	uart_io
		movb	uartx_port_buf.tx1,/uart1_tx_hs	;turn off handshake
		clrb	uart1_hs_flag
		ret
rts1_xoff
		csae	uart1_num,#uart1_xxoff
		ret
		clrb	uart1_hs_flag
rts1_xoff1
		test	uartx_tx_count
		jnz	rts1_xoff1			;wait for not busy
		setb	uartx_tx_isr0			;set dest for isr to 1
		clrb	uartx_tx_isr1
		mov	w,#XOFF				;send XOFF
		not     w                       	;ready bits (inverse logic)
		mov     uartx_tx_high,w         	;store data byte
		setb    uartx_tx_low.7          	;set up start bit
		mov     w,#10				;1 start + 8 data + 1 stop bit
		mov     uartx_tx_count,w
		ret

uart1_rts_on
		snb	uart1_hs_flag			;hs already on?
		ret					;yes
		bank	uart_qeue
		jb	uart1_tx_xonxoff,rts1_xon	;xonxoff handshake
		sb	uart1_tx_hardwire
		ret					;no handshake
		csbe	uart1_num,#uart1_hwon
		ret
		bank	uart_io
		movb	uartx_port_buf.tx1,uart1_tx_hs	;turn on handshake
		setb	uart1_hs_flag
		ret
rts1_xon
		csbe	uart1_num,#uart1_xxon
		ret
		setb	uart1_hs_flag
rts1_xon1
		test	uartx_tx_count
		jnz	rts1_xon1			;wait for not busy
		setb	uartx_tx_isr0			;set dest for isr to 1
		clrb	uartx_tx_isr1
		mov	w,#XON				;send XOFF
		not     w                       	;ready bits (inverse logic)
		mov     uartx_tx_high,w         	;store data byte
		setb    uartx_tx_low.7          	;set up start bit
		mov     w,#10				;1 start + 8 data + 1 stop bit
		mov     uartx_tx_count,w
		ret

uart2_rts_off
		sb	uart2_hs_flag			;hs already off?
		ret					;yes
		bank	uart_qeue
		jb	uart2_tx_xonxoff,rts2_xoff	;xonxoff handshake
		sb	uart2_tx_hardwire
		ret					;no handshake
		csae	uart2_num,#uart2_hwoff
		ret
		bank	uart_io
		movb	uartx_port_buf.tx2,/uart2_tx_hs	;turn off handshake
		clrb	uart2_hs_flag
		ret
rts2_xoff
		csae	uart2_num,#uart2_xxoff
		ret
		clrb	uart2_hs_flag
rts2_xoff1
		test	uartx_tx_count
		jnz	rts2_xoff1			;wait for not busy
		clrb	uartx_tx_isr0			;set dest for isr to 2
		setb	uartx_tx_isr1
		mov	w,#XOFF				;send XOFF
		not     w                       	;ready bits (inverse logic)
		mov     uartx_tx_high,w         	;store data byte
		setb    uartx_tx_low.7          	;set up start bit
		mov     w,#10				;1 start + 8 data + 1 stop bit
		mov     uartx_tx_count,w
		ret

uart2_rts_on
		snb	uart2_hs_flag			;hs already on?
		ret					;yes
		bank	uart_qeue
		jb	uart2_tx_xonxoff,rts2_xon	;xonxoff handshake
		sb	uart2_tx_hardwire
		ret					;no handshake
		csbe	uart2_num,#uart2_hwon
		ret
		bank	uart_io
		movb	uartx_port_buf.tx2,uart2_tx_hs	;turn on handshake
		setb	uart2_hs_flag
		ret
rts2_xon
		csbe	uart2_num,#uart2_xxon
		ret
		setb	uart2_hs_flag
rts2_xon1
		test	uartx_tx_count
		jnz	rts2_xon1			;wait for not busy
		clrb	uartx_tx_isr0			;set dest for isr to 2
		setb	uartx_tx_isr1
		mov	w,#XON				;send XON
		not     w                       	;ready bits (inverse logic)
		mov     uartx_tx_high,w         	;store data byte
		setb    uartx_tx_low.7          	;set up start bit
		mov     w,#10				;1 start + 8 data + 1 stop bit
		mov     uartx_tx_count,w
		ret

	;handle output to uart1, uart2, uart3

uartx_output_handler
		bank	uart_qeue
		test	uartx_tx_count
		sz
		retp					;wait for not busy
		sb	uartx_data
		retp					;nothing to send
		movb	uartx_tx_isr0,uartx_tx_dest0	;set dest for isr
		movb	uartx_tx_isr1,uartx_tx_dest1
		mov	w,uart_flags1			;go to correct output handler
		and	w,#$03
		add	pc,w
		retp
		jmp	uart1_output_handler
		jmp	uart2_output_handler
		jmp	uart3_output_handler

	;put w into uartx_outbuf

_enqeue_uartx_outbuf
		snb	uartx_full
		retp
		mov	w_save,w
		bank	uart_qeue
		mov	w,uartx_head
		mov	fsr,w				;calculate buffer address
		mov	w,#uartx_outbuf
		add	fsr,w
		mov	w,w_save			;store received byte
		mov	indf,w
		bank	uart_qeue
		inc	uartx_num			;adjust byte count
		inc	uartx_head
		csne	uartx_head,#uartx_bufsize	;wrap around head
		clr	uartx_head
		setb	uartx_data			;mark holding data
		csne	uartx_num,#uartx_bufsize	;test if buffer full
		setb	uartx_full			;buffer full
		retp

	;put w into uart1_inbuf

_enqeue_uart1_inbuf
		mov	w_save,w
		bank	uart_qeue
		cjne	uart1_num,#uart1_bufsize,enq1_uart1	;test if buffer full
		setb	uart1_overrun			;buffer full
		retp					;so discard byte and exit
enq1_uart1
		mov	w,uart1_head
		mov	fsr,w				;calculate buffer address
		mov	w,#uart1_inbuf
		add	fsr,w
		mov	w,w_save			;store received byte
		mov	indf,w
		bank	uart_qeue
		inc	uart1_num			;adjust byte count
		inc	uart1_head
		csne	uart1_head,#uart1_bufsize	;wrap around head
		clr	uart1_head
		setb	uart1_data			;mark holding data
		call	uart1_rts_off			;turn off hs if required
		retp

	;put w into uart2_inbuf

_enqeue_uart2_inbuf
		mov	w_save,w
		bank	uart_qeue
		cjne	uart2_num,#uart2_bufsize,enq1_uart2	;test if buffer full
		setb	uart2_overrun			;buffer full
		retp					;so discard byte and exit
enq1_uart2
		mov	w,uart2_head
		mov	fsr,w				;calculate buffer address
		mov	w,#uart2_inbuf
		add	fsr,w
		mov	w,w_save			;store received byte
		mov	indf,w
		bank	uart_qeue
		inc	uart2_num			;adjust byte count
		inc	uart2_head
		csne	uart2_head,#uart2_bufsize	;wrap around head
		clr	uart2_head
		setb	uart2_data			;mark holding data
		call	uart2_rts_off			;turn off hs if required
		retp

	;put w into uart3_inbuf

_enqeue_uart3_inbuf
		mov	w_save,w
		bank	uart_qeue
		cjne	uart3_num,#uart3_bufsize,enq_uart3	;test if buffer full
		setb	uart3_overrun			;buffer full
		retp					;so discard byte and exit
enq_uart3
		mov	w,uart3_head
		mov	fsr,w				;calculate buffer address
		mov	w,#uart3_inbuf
		add	fsr,w
		mov	w,w_save			;store received byte
		mov	indf,w
		bank	uart_qeue
		inc	uart3_num			;adjust byte count
		inc	uart3_head
		csne	uart3_head,#uart3_bufsize	;wrap around head
		clr	uart3_head
		setb	uart3_data			;mark holding data
		retp

	;get w from uartx_outbuf

_deqeue_uartx_outbuf
		sb	uartx_data
		retp
		bank	uart_qeue
		mov	w,uartx_tail			;get next byte from buffer
		mov	fsr,w
		mov	w,#uartx_outbuf
		add	fsr,w
		mov	w,indf
		mov	w_save,w
		bank	uart_qeue
		dec	uartx_num			;adjust byte count
		snz
		clrb	uartx_data
		inc	uartx_tail
		csne	uartx_tail,#uartx_bufsize	;wrap around tail
		clr	uartx_tail
		mov	w,w_save
		clrb	uartx_full			;mark buffer not full
		retp

	;get w from uart1_inbuf

_deqeue_uart1_inbuf
		sb	uart1_data			;test if any byte in buffer
		retp					;if not then exit
		bank	uart_qeue
		mov	w,uart1_tail			;get next byte from buffer
		mov	fsr,w
		mov	w,#uart1_inbuf
		add	fsr,w
		mov	w,indf
		mov	w_save,w
		bank	uart_qeue
		dec	uart1_num			;adjust byte count
		snz
		clrb	uart1_data			;mark buffer empty
		inc	uart1_tail
		csne	uart1_tail,#uart1_bufsize	;wrap around tail
		clr	uart1_tail
		call	uart1_rts_on			;turn on hs if required
		mov	w,w_save
		retp

	;get w from uart2_inbuf

_deqeue_uart2_inbuf
		sb	uart2_data			;test if any byte in buffer
		retp					;no
		bank	uart_qeue
		mov	w,uart2_tail			;get next byte from buffer
		mov	fsr,w
		mov	w,#uart2_inbuf
		add	fsr,w
		mov	w,indf
		mov	w_save,w
		bank	uart_qeue
		dec	uart2_num			;adjust byte count
		snz
		clrb	uart2_data			;mark buffer empty
		inc	uart2_tail
		csne	uart2_tail,#uart2_bufsize	;wrap around tail
		clr	uart2_tail
		call	uart2_rts_on			;turn on hs if required
		mov	w,w_save
		retp

	;get w from uart3_inbuf

_deqeue_uart3_inbuf
		sb	uart3_data			;test if any byte in buffer
		retp					;no
		bank	uart_qeue
		mov	w,uart3_tail			;get next byte from buffer
		mov	fsr,w
		mov	w,#uart3_inbuf
		add	fsr,w
		mov	w,indf
		mov	w_save,w
		bank	uart_qeue
		dec	uart3_num			;adjust byte count
		snz
		clrb	uart3_data			;mark buffer empty
		inc	uart3_tail
		csne	uart3_tail,#uart3_bufsize	;wrap around tail
		clr	uart3_tail
		mov	w,w_save
		retp

	;handle output to uart1

uart1_output_handler
		snb	uart1_tx_hardwire		;test if tx used as handshake
		retp					;tx is handshake so do not send
		jb	uart1_rx_xonxoff,uart1_hs_soft
		jnb	uart1_rx_hardwire,do_uartx_output	;rx does no handshake
		bank	uart_io
		jb	uart1_rx_hs,uart1_hs_high
uart1_hs_low						;test if handshake input is low
		jnb	uart1_rx_pin,do_uartx_output	;if so then send
		retp
uart1_hs_high						;test if handshake input is high
		jb	uart1_rx_pin,do_uartx_output	;if so then send
		retp
uart1_hs_soft
		jb	uart1_tx_enable,do_uartx_output	;send only if enabled
		retp

	;handle output to uart2

uart2_output_handler
		snb	uart2_tx_hardwire		;test if tx used as handshake
		retp					;tx is handshake so do not send
		jb	uart2_rx_xonxoff,uart2_hs_soft
		jnb	uart2_rx_hardwire,do_uartx_output	;rx does no handshake
		bank	uart_io
		jb	uart2_rx_hs,uart2_hs_high
uart2_hs_low						;test if handshake input is low
		jnb	uart2_rx_pin,do_uartx_output	;if so then send
		retp
uart2_hs_high						;test if handshake input is high
		jb	uart2_rx_pin,do_uartx_output	;if so then send
		retp
uart2_hs_soft
		jb	uart2_tx_enable,do_uartx_output	;send only if enabled
		retp

	;handle output to uart3

uart3_output_handler
		clrb	uartx_port_buf.wr3		;set uart3 write mode
do_uartx_output
		call	deqeue_uartx_outbuf		;get byte to send
		bank	uart_qeue
		not     w                       	;ready bits (inverse logic)
		mov     uartx_tx_high,w         	;store data byte
		setb    uartx_tx_low.7          	;set up start bit
		mov     w,#10				;1 start + 8 data + 1 stop bit
		mov     uartx_tx_count,w
		retp

	;handle input from uart1

uart1_input_handler
		bank	uart_io
		mov	w,uart1_rx_store		;get received byte
		clrb	uart1_rx_flag			;clear receive flag
		snb	uart1_rx_hardwire		;test if rx is hardwire handshake
		retp					;yes, so do not receive
		jnb	uart1_rx_xonxoff,enqeue_uart1_inbuf
		mov	w_save,w			;test for xon/xoff
		xor	w,#XON				;test if char received is XON
		snz
		setb	uart1_tx_enable			;yes, so enable tx
		snz
		retp					;and exit
		mov	w,w_save
		xor	w,#XOFF				;test if char received is XOFF
		snz
		clrb	uart1_tx_enable			;yes, so disable tx
		snz
		retp					;and exit
		mov	w,w_save			;char received is not XON or XOFF
		jmp	enqeue_uart1_inbuf		;so put byte in qeue

	;handle input from uart2

uart2_input_handler
		bank	uart_io
		mov	w,uart2_rx_store		;get received byte
		clrb	uart2_rx_flag			;clear receive flag
		snb	uart2_rx_hardwire		;test if rx is hardwire handshake
		retp					;yes, so do not receive
		jnb	uart2_rx_xonxoff,enqeue_uart2_inbuf
		mov	w_save,w			;test for xon/xoff
		xor	w,#XON				;test if char received is XON
		snz
		setb	uart2_tx_enable			;yes, so enable tx
		snz
		retp					;and exit
		mov	w,w_save
		xor	w,#XOFF				;test if char received is XOFF
		snz
		clrb	uart2_tx_enable			;yes, so disable tx
		snz
		retp					;and exit
		mov	w,w_save			;char received is not XON or XOFF
		jmp	enqeue_uart2_inbuf		;so put byte in qeue

	;handle input from uart3

uart3_input_handler
		bank	uart_io
		mov	w,uart3_rx_store		;get received byte
		clrb	uart3_rx_flag			;clear receive flag
		jmp	enqeue_uart3_inbuf		;put byte in qeue

;-------------- codepage 2 end -----------------------------------------------------------


;*****************************************************************************************
		org	CODEPAGE_3
;*****************************************************************************************
		jmp	$	;generates error if code above crosses page boundary

;-----------------------------------------------------------------------------------------
; MAIN PROGRAM CODE
; This can be changed as you like to suit your own application
;-----------------------------------------------------------------------------------------

; This slave provides the following to the mainline code:
;
; i2cs_rx_flag			;set when a databyte has been received by this slave
; i2cs_broadcast		;set when this slave received a broadcast id
; i2cs_written_again		;set for 2nd or later databyte received by this slave
; i2cs_data_needed		;set when this slave needs data to send to the master
; i2cs_stop_found		;set when stop condition detected on i2c bus
; i2cs_byte			;databyte buffer for this slave

; This slave sets the flag i2cs_written_again for the 2nd or later databyte received.
; It is cleared by the stop condition.
;
; The flag i2cs_rx_flag is set for any received databyte. It must be cleared by the mainline code
; directly after the byte has been grabbed from i2cs_byte. When the i2cs_rx_flag is set while
; i2cs_written_again is cleared, then the received byte is the command. More bytes received by this
; slave will have the flag i2cs_written_again set. After receiving a byte, this slave sends
; an ack to the master.
;
; The flag i2cs_data_needed is set when this slave requires data to send to the master.
; When this flag is set, the mainline code must place a databyte into i2cs_byte and clear
; the i2cs_data_needed flag. When the slave needs more data later on, the flag will be set again.
; Do not place a byte in the buffer while i2cs_data_needed is cleared, the buffer may be in use.
;
; The flag i2cs_stop_found is set by the stop condition and cleared by any start condition.
; It can be used to detect the end of the current i2c transfer.


; The mainline code will accept the following commands that are received by the i2c slave:
;
; This device must be addressed as follows:
; I2COUT id,command,[outdata,...]
; I2CIN  id,command,[indata,....]
;
; command	    description				parameters / returns
;
; 0000 0xxx	-   reserved				not specified
; 0000 1xxx	-   read device type and i2c id		major,minor,id
;
; 0001 0xbb	-   set uart configuration		config
; 0001 1xbb	-   read uart status			flags or bytes in buffer
;
; 0010 0xbb	-   write data to uart			output data
; 0010 1xbb	-   read data from uart			input data
;
; other		-   reserved				not specified
;
; xxx = don't care
; xbb = uart number

	;Because the i2c bus operates in half duplex and the datastream is sequential,
	;incoming data can be processed directly. Just keep argument count.

i2cs_input_handler
		bank	i2cs
		mov	w,i2cs_byte
		clrb	i2cs_rx_flag
		snb	i2cs_broadcast			;broadcast command (using id $10)?
		retp					;yes, then simply ignore
		jb	i2cs_written_again,:i2cs_ih2
		mov	command,w
		clr	i2cs_in_index
		clr	i2cs_out_index
		clr	i2cs_out_tail
		jb	command.3,exec_read_command	;bit3 set -> read command
		jmp	exec_write_command		;else -> write command
:i2cs_ih2
		mov	i2cs_inbuf,w
		inc	i2cs_in_index			;increment argument count
		jmp	exec_write_command

i2cs_output_handler
		bank	i2cs
		mov	w,#$FF
		mov	i2cs_byte,w			;dummy databyte of known value
		cje	i2cs_out_tail,i2cs_out_index,exec_read_command	;read again if outbuf enpty
		mov	w,i2cs_out_tail
		mov	fsr,w
		mov	w,#i2cs_outbuffer
		add	fsr,w
		mov	w,indf
		bank	i2cs
		mov	i2cs_byte,w
		inc	i2cs_out_tail
		clrb	i2cs_data_needed
		retp

	; read command switch

exec_read_command
		clr	i2cs_out_tail			;reset outbuf index
		jb	command.7,rd_cmd_invalid
		jb	command.6,rd_cmd_invalid
		mov	w,<>command
		and	w,#$03
		add	pc,w
		jmp	rd_cmd_0
		jmp	rd_cmd_1
		jmp	rd_cmd_2
		jmp	rd_cmd_invalid			;jmp	rd_cmd_3

rd_cmd_0
; 0000 1xxx	-   read device type and i2c id		major,minor,id

		mov	w,#SYNOPDEVICE_MAJOR
		mov	i2cs_outbuf0,w
		mov	w,#SYNOPDEVICE_MINOR
		mov	i2cs_outbuf1,w
		mov	w,i2cs_id
		mov	i2cs_outbuf2,w
		mov	w,#3
		mov	i2cs_out_index,w
		retp

rd_cmd_1
; 0001 1xbb	-   read uart status			flags or bytes in buffer

		mov	w,command
		and	w,#$03
		add	pc,w
		jmp	:rc1_0
		jmp	:rc1_1
		jmp	:rc1_2
		jmp	:rc1_3

:rc1_0
		mov	w,uart_flags2			;return flags
		clrb	uart1_overrun
		clrb	uart2_overrun
		clrb	uart3_overrun
		jmp	i2cs_return_one_byte

:rc1_1
		bank	uart_qeue
		mov	w,uart1_num			;return bytes in uart1_inbuf
		jmp	i2cs_return_one_byte

:rc1_2
		bank	uart_qeue
		mov	w,uart2_num			;return bytes in uart2_inbuf
		jmp	i2cs_return_one_byte

:rc1_3
		bank	uart_qeue
		mov	w,uart3_num			;return bytes in uart3_inbuf
		jmp	i2cs_return_one_byte

rd_cmd_2
; 0010 1xbb	-   read data from uart			input data

		mov	w,command
		and	w,#$03
		add	pc,w
		retp
		jmp	:rc2_1
		jmp	:rc2_2
		jmp	:rc2_3

:rc2_1
		jnb	uart1_data,rd_cmd_invalid
		call	@deqeue_uart1_inbuf		;get byte from uart1_inbuf
		jmp	i2cs_return_one_byte

:rc2_2
		jnb	uart2_data,rd_cmd_invalid
		call	@deqeue_uart2_inbuf		;get byte from uart2_inbuf
		jmp	i2cs_return_one_byte

:rc2_3
		jnb	uart3_data,rd_cmd_invalid
		call	@deqeue_uart3_inbuf		;get byte from uart3_inbuf
		jmp	i2cs_return_one_byte

rd_cmd_invalid
		mov	w,#$FF
i2cs_return_one_byte
		bank	i2cs
		mov	i2cs_outbuf0,w
		mov	w,#1
		mov	i2cs_out_index,w
		retp

	;write command switch

exec_write_command
		snb	command.7
		retp
		snb	command.6
		retp
		mov	w,<>command
		and	w,#$03
		add	pc,w
		retp					;jmp	wr_cmd_0
		jmp	wr_cmd_1
		jmp	wr_cmd_2
		retp					;jmp	wr_cmd_3

wr_cmd_1
; 0001 0xbb	-   set uart configuration		config

		bank	uart_qeue
:wc1_00
		test	uartx_tx_count
		jnz	:wc1_00				;wait for not busy
		bank	i2cs
		cja	i2cs_in_index,#0,:wc1_0
		bank	uart_qeue
		clr	uartx_num			;clear output buffer
		clr	uartx_head
		clr	uartx_tail
		clrb	uartx_data
		clrb	uartx_full
		retp
:wc1_0
		mov	w,command
		and	w,#$03
		add	pc,w
		retp
		jmp	:wc1_1
		jmp	:wc1_2
		jmp	:wc1_3

:wc1_1
		mov	w,i2cs_inbuf
		bank	uart_io
		mov	uart1_config,w
		bank	uart_qeue
		clr	uart1_num			;clear uart1 input buffer
		clr	uart1_head
		clr	uart1_tail
		clrb	uart1_data
		clrb	uart1_overrun
		bank	uart_io
		and	uart_flags3,#$F0		;clear uart1 handshake flags
		mov	w,uart1_config			;test for tx hardwire
		and	w,#$07
		xor	w,#$07
		snz
		setb	uart1_tx_hardwire
		jz	:wc1_11				;so it is
		movb	uart1_tx_xonxoff,uart1_tx_hs	;set xon/xoff handshake
:wc1_11
		mov	w,uart1_config			;test for rx hardwire
		and	w,#$70
		xor	w,#$70
		snz
		setb	uart1_rx_hardwire
		jz	:wc1_12				;so it is
		movb	uart1_rx_xonxoff,uart1_rx_hs	;set xon/xoff handshake
:wc1_12
		setb	uart1_tx_enable			;enable output for xon/xoff
		clrb	uart1_hs_flag			;clear hs flag
		call	@enable_uart1_receive		;so hs will be turned on
		retp

:wc1_2
		mov	w,i2cs_inbuf
		bank	uart_io
		mov	uart2_config,w
		bank	uart_qeue
		clr	uart2_num			;clear uart2 input buffer
		clr	uart2_head
		clr	uart2_tail
		clrb	uart2_data
		clrb	uart2_overrun
		bank	uart_io
		and	uart_flags3,#$0F		;clear uart2 handshake flags
		mov	w,uart2_config			;test for tx hardwire
		and	w,#$07
		xor	w,#$07
		snz
		setb	uart2_tx_hardwire
		jz	:wc1_21				;so it is
		movb	uart2_tx_xonxoff,uart2_tx_hs	;set xon/xoff handshake
:wc1_21
		mov	w,uart2_config			;test for rx hardwire
		and	w,#$70
		xor	w,#$70
		snz
		setb	uart2_rx_hardwire
		jz	:wc1_22				;so it is
		movb	uart2_rx_xonxoff,uart2_rx_hs	;set xon/xoff handshake
:wc1_22
		setb	uart2_tx_enable			;enable output for xon/xoff
		clrb	uart2_hs_flag			;clear hs flag
		call	@enable_uart2_receive		;so hs will be turned on
		retp

:wc1_3
		mov	w,i2cs_inbuf
		bank	uart_io
		mov	uart3_config,w
		bank	uart_qeue
		clr	uart3_num			;clear uart3 input buffer
		clr	uart3_head
		clr	uart3_tail
		clrb	uart3_data
		clrb	uart3_overrun
		retp

wr_cmd_2
; 0010 0xbb	-   write data to uart			output data

		cja	i2cs_in_index,#0,:wc2_1
		movb	uartx_tx_dest0,command.0	;set new destination
		movb	uartx_tx_dest1,command.1
		retp
:wc2_1
		mov	w,i2cs_inbuf
		call	@enqeue_uartx_outbuf		;store data for transmission
		retp

;-------------- codepage 3 end ------------------------------------------------------
