;*****************************************************************************************
; Copyright © [01/26/1999] Scenix Semiconductor, Inc. All rights reserved.
;
; Scenix Semiconductor, Inc. assumes no responsibility or liability for
; the use of this [product, application, software, any of these products.
; Scenix Semiconductor 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. Scenix Semiconductor makes no representation
; or warranties with respect to the accuracy or use of these information,
; or infringement of patents arising from such use or otherwise.
;*****************************************************************************************
; SX Interrupt-Driven Dual I2C Multi-Master demo.
;                   
; Filename:	I2C_UART demo1.src
;
; Author:	Bruce Wilson
;		Applications Engineer
;		Scenix Semiconductor Inc.
;
; Revision:	2.1
;
; Part:		SX28AC datecode 9929AA
; Freq:		50Mhz
; Compiled using Parallax SXKey28L
;
; Date Written: Aug 23,1999
;
; Last Revised: Nov 24,1999
;
; Introduction:
;	This program is one of the two peices of demo code written for the
;	Scenix UART/I2C Demo Board. This code is for SX 1 which is the top
;	SX28.
;
;	Demo Settings:
;		-UART: 19200,N,8,1
;	Jumpers Required:
;		- JP1, JP2, JP3, JP4
;		- 8/50, MM, MSTR, SL, 8/50
;		- JP13, JP14, JP15, JP16 each across pins 1-2
;		
;	To test this software follow the Quickstart Guide for the I2C/UART demo.
;
; Program Description:
;
;	This program demonstrates the use of a Scenix SX microcontroller as
;	an I2C device processor.  Each I2C interface is implemented in
;	software, as a Virtual Peripheral.  Because it is a Virtual Peripheral,
;	most of the interface to the peripheral is hardware-like.  This means
;	that the processing for the I2C operations is hidden from the main
;	program logic, and all it needs to do is call an access subroutine, and
;	it can proceed to perform other tasks while the I2C masters and slaves
;	do their work.  When finished, the I2C devices will set flags to indicate
;	whether or not the communications were successful.
;
;	This program is used to connect the two SX-Demo microcontrollers through 
;	an I2C bus.  When the code begins, it polls all addresses from 1 to 256 
;	(skipping the odd numbers) to find any slaves present on the bus.  Because
;	the on-chip slaves are also present on the bus, the SX will also find it's 
;	own slaves (The addresses for the slaves are set in the defines, later on 
;	in this program.)  The slave addresses are dynamic, meaning they can change 
; 	at any time while the program is running.
;
;	Once the program has looped through all 256 addresses, it falls through to
;	a command mode, where the user can choose to read or write a string to any 
;	detected slave device on the bus by pressing 'R' or 'S'. This corresponding to 
;	'R'ead from slave and 'S'ave string to slave.
;
;	After choosing the address of the slave and receiving back an acknowledge the
;	saved string will either be displayed or the user may enter a string to save.
;
;	In order to comunicate to this SX the user must conenct a serial cable to UART 1
;	or UART 2 depending on which I2C bus they wish to communicate on.
;	Use the terminal settings mentioned above.
;
; Revision History:
;	1.0 Core I2C master code and core I2C slave code with some test code.
;	1.11 Many changes, updates to get the code to work properly.
;	1.12 Dual Multi-Masters created.  Demo-routine written.  Terminal mode
;	     added, etc. (04/13/99)
;	1.13 Updated documentation
;	1.20 Modified for use with the Scenix UART/I2C demo board as a single multi-master,
;	     master or slave I2C processor with UART.
;	1.21 Added jumper selection of 8 or 50MHz operation
;	1.22 Changed functionality to run either in slave or master modes and read and write to the 
;		EEPROM on the demo board (IC6) 
;	1.23 Added arbitration so that it runs within the ISR to avoid cluttering up the mainline
;		and making it easier to use for an end user
;	1.24 multi-master mainline code was changed to be non-blocking which allows checking for
;		to see if the other master is talking to this slave.
;	1.25 converted code to run on IC1 single master
;	1.26 Modified code for dual UART
;	1.27 Modified code for dual master.
;	2.0  First release of I2C/Demo source code.
;	2.1  Updated code to run under SASM and also with new arbitration technique
;
;*****************************************************************************************
; TARGET SX
; Uncomment one of the following lines to choose the SX18AC, SX20AC, SX28AC, SX48BD/ES,
; SX48BD, SX52BD/ES or SX52BD. For SX48BD/ES and SX52BD/ES, uncomment both defines,
; SX48_52 and SX48_52_ES.
;*****************************************************************************************
;SX18_20
SX28
;SX48_52
;SX48_52_ES

;*****************************************************************************************
; ASSEMBLER
; Uncomment the following line if using the Parallax SX-Key assembler. SASM assembler
; enabled by default.
;*****************************************************************************************
;SX_Key					; Using the Parrallax SX-Key

;*****************************************************************************************
; PROGRAM DEFINES
;*****************************************************************************************
		ID	'I2CUART1'		; code identification
		reset	reset_entry		; set reset vector

;*****************************************************************************************
; Assembler directives:
;	high speed external osc, turbo mode, 8-level stack, and extended option reg.
;
;	SX18/20/28 - 4 pages of program memory and 8 banks of RAM enabled by default.
;	SX48/52 - 8 pages of program memory and 16 banks of RAM enabled by default.
;                
;*****************************************************************************************

IFDEF SX_Key 				;SX-Key Directives
  IFDEF SX18_20				;SX18AC or SX20AC device directives for SX-Key
		device	SX18L,oschs2,turbo,stackx_optionx
  ENDIF
  IFDEF SX28				;SX28AC device directives for SX-Key		
		device	SX28L,oschs2,turbo,stackx_optionx
  ENDIF
  IFDEF SX48_52_ES			;SX48BD/ES or SX52BD/ES device directives for SX-Key
		device	oschs,turbo,stackx,optionx
  ELSE
    IFDEF SX48_52				;SX48/52/BD device directives for SX-Key
		device	oschs2
    ENDIF
  ENDIF
		freq	50_000_000
ELSE  					;SASM Directives
  IFDEF SX18_20				;SX18AC or SX20AC device directives for SASM
		device	SX18,oschs2,turbo,stackx,optionx
  ENDIF
  IFDEF SX28				;SX28AC device directives for SASM
		device	SX28,oschs2,turbo,stackx,optionx
  ENDIF
  IFDEF SX48_52_ES			;SX48BD/ES or SX52BD/ES device directives for SASM
		device	SX52,oschs,turbo,stackx,optionx
  ELSE
    IFDEF SX48_52			;SX48BD or SX52BD device directives for SASM
		device	SX52,oschs2  
    ENDIF
  ENDIF
ENDIF


;*****************************************************************************************
; Macros
;*****************************************************************************************

	;*********************************************************************************
	; Macro: _bank
	; Sets the bank appropriately for all revisions of SX.
	;
	; This is required since the bank instruction has only a 3-bit operand, it cannot
	; be used to access all 16 banks of the SX48/52. For this reason FSR.4 (for SX48/52BD/ES)
	; or FSR.7 (SX48/52bd production release) needs to be set appropriately, depending
	; on the bank address being accessed. This macro fixes this.
	;
	; So, instead of using the bank instruction to switch between banks, use _bank instead.
	; 
	;*********************************************************************************
_bank	macro	1
	bank	\1

	IFDEF SX48_52
	  IFDEF SX48_52_ES
	    IF \1 & %00010000		;SX48BD/ES and SX52BD/ES (engineering sample) bank instruction
		setb	fsr.4		;modifies FSR bits 5,6 and 7. FSR.4 needs to be set by software.
	    ENDIF
	  ELSE
	    IF \1 & %10000000		;SX48BD and SX52BD (production release) bank instruction 
		setb	fsr.7		;modifies FSR bits 4,5 and 6. FSR.7 needs to be set by software.
	    ELSE
		clrb	fsr.7
	    ENDIF
	  ENDIF
	ENDIF
	endm

;*****************************************************************************************
; Constants
;*****************************************************************************************

int_period	equ	163		; This sets the I2C bus frequency.  (25kHz)
					; Also used as seed for UART baud rate.

strt_delay	=	4		; This start delay is critical in the operation
					; of the multi-master system. The value of strt_delay
IFNDEF	multi_master			; specifies the number of ISR's that the scl line must
  strt_delay	=	1		; be held high for before a start bit will be sent.
ENDIF

	;*********************************************************************************
	; I2C Constants
	;*********************************************************************************
write		=	1			; The "write" state starts at 1
						; in the jump table.
read		=	13			; The "read" state starts
						; at 13 in the jump table.
slave_address	=	$50			; Address of the slave (read)

	;*********************************************************************************
	; UART Constants
	; The following three values determine the UART baud rate.
	; The value of baud_rate and int_period affect the baud rate as follows:
	;  Baud rate = 50MHz/(baud_rate * int_period * RTCC_prescaler)
	;
	; Note: *int_period must <256 and longer than the length of the slowest possible
	; interrupt sequence in instruction cycles. Changing the value of int_period will
	; affect the rest of the virtual peripherals due to timing issues. The start delay
	; value must be set equal to (baud_rate)*1.5 + 1
	;*********************************************************************************

; *** 2400 baud (for slower baud rates, increase the RTCC prescaler)
baud_rate_24	=	48			;for 2400 baud rate generation
start_del_24	=	baud_rate_24+(baud_rate_24/2)+1

; *** 9600 baud
baud_rate_96	=	12			;for 9600 baud rate generation
start_delay_96	= 	baud_rate_96+(baud_rate_96/2)+1

; *** 19200 baud
baud_rate_192	=	4 			;for 19200 baud rate generation
start_delay_192	= 	baud_rate_192+(baud_rate_192/2)+1

; *** 38400 baud
baud_rate_384	=	3			;for 38400 baud rate generation
start_delay_384	= 	baud_rate_384+(baud_rate_384/2)+1


_cr		equ	13	; ASCII value for carridge return
_line_feed	equ	10	; ASCII value for a line feed


;*****************************************************************************************
; Port Assignment
;   RX_io 	: sets direction registers
;   RX_init	: sets the ports initial value
;*****************************************************************************************

RA_init		equ	%11110000		;SX18/20/28/48/52 port A latch init
RA_io		equ	%11110111		;SX18/20/28/48/52 port A DDIR value
RB_init		equ	%11111111		;SX18/20/28/48/52 port B latch init
RB_io		equ	%01110111		;SX18/20/28/48/52 port B DDIR value
RC_init		equ	%11111000		;SX18/20/28/48/52 port C latch init
RC_io		equ	%11110000		;SX18/20/28/48/52 port C DDIR value

IFDEF SX48_52
RD_init		equ	%11111111		;SX48/52 port D latch init
RD_io		equ	%11111111		;SX48/52 port D DDIR value
RE_init		equ	%11111111		;SX48/52 port E latch init
RE_io		equ	%11111111		;SX48/52 port E DDIR value
ENDIF

;*****************************************************************************************
; Pin Definitions 
;*****************************************************************************************
i2c_port	equ	ra
	scl		equ	0
  	sda		equ	1
	scl_pin1       	equ     i2c_port.scl	; I2C 1 clock
	sda_pin1       	equ     i2c_port.sda	; I2C 1 data I/O
	scl_pin2       	equ     i2c_port.2	; I2C 2 clock 
	sda_pin2       	equ     i2c_port.3	; I2C 2 data I/O 
	

uart_port	equ	rb
	rx_pin2         equ     uart_port.2	;UART1 receive input
	tx_pin2         equ     uart_port.3	;UART1 transmit output
	rx_pin1         equ     uart_port.6	;UART2 receive input
	tx_pin1         equ     uart_port.7	;UART2 transmit output

; Optional LEDs for debugging purposes...
led_port	equ	rc
green_led	equ	led_port.2	
red_led2	equ	led_port.1
red_led1	equ	led_port.0

c8_50_sel_pin	equ	rc.6	; crystal 8/50Mhz jumper select pin

;*****************************************************************************************
; Code Locations
;*****************************************************************************************
INTERRUPT_ORG		equ	$0   ; -> 08Ah 	This must always be 0 as this is the interrupt vector
I2CS_ISR_FUNC_ORG	equ	$200 ; -> 2BFh  I2C Master state machine executed from ISR
SEND_STRINGS_ORG	equ	$300 ; -> 3DEh  Strings to send to terminal MUST be stored here
I2CM_ISR_FUNC_ORG	equ	$400 ; -> 4EAh  I2C Master state machine executed from ISR
I2CM_SUBS_ORG2		equ	$500 ; -> 52Eh	I2C Subroutines called from mainline
UART_SUBS_ORG		equ	$600 ; -> 660h  UART subroutines stored here. These must be in first half of page
OTHER_SUBS_ORG		equ	$700 ; -> 739h	EEPROM read/write subroutines calles from mainline

;*****************************************************************************************
; Data Memory address definitions
; These definitions ensure the proper address is used for banks 0 - 7 for 2K SX devices
; (SX18/20/28) and 4K SX devices (SX48/52). 
;*****************************************************************************************
IFDEF SX48_52

global_org	=	$0A
bank0_org	=	$00
bank1_org	=	$10
bank2_org	=	$20
bank3_org	=	$30
bank4_org	=	$40
bank5_org	=	$50
bank6_org	=	$60
bank7_org	=	$70

ELSE

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

ENDIF
;*****************************************************************************************
; Global Register definitions
; NOTE: Global data memory starts at $0A on SX48/52 and $08 on SX18/20/28.
;*****************************************************************************************
		org     global_org

main_temp	equ	global_org+1
isr_temp	equ	global_org+2		; Temporary variable used by the ISR.
flags		equ	global_org+3
	uart_rx_flag	equ	flags.0		; flag set when data received by UART
	timer_flag	equ	flags.1		; Indicates a 16-bit timer rollover
	active_uart_flag 	equ	flags.4 ; used by mainline to switch between uart1 and uart2.
	active_master_flag	equ	flags.6
	eewrite_flag		equ	flags.7 ; true if user wants to write data to the EEPROM
I2C_flags	equ	global_org+4		; Flags various I2C things
	I2CM_event_flag	equ	I2C_flags.0
	I2CS_event_flag	equ	I2C_flags.1
	I2C_stop_found	equ	I2C_flags.2
	I2CS_rx_flag	equ	I2C_flags.3	; This flag indicates when data has
						; been received by an I2C master.
MASTER_BANK	equ	global_org+4		; used to remember which master bank is active
UART_BANK	equ	global_org+5		; used to remeber which serial bank is avtive

;*****************************************************************************************
; RAM Bank Register definitions
;*****************************************************************************************

	;*********************************************************************************
	; Bank 0 
	;*********************************************************************************
		org     bank0_org
BANK0		=	$

	;*********************************************************************************
	; Bank 1 - I2C Master variables ram bank 1
	;*********************************************************************************
		org     bank1_org
I2CM1		=	$	; I2CM bank
I2CM_state	ds	1	; This indicates the state that the I2CM master is currently in.
I2CM_sub_state	ds	1	; This indicates the substate that the I2C master is currently in.
I2CM_port_buf	ds	1	; This buffer holds the current state of the I2C port direction reg's
I2CM_bit_count	ds	1	; Indicates the number of bits left to process in read/write
I2CM_byte	ds	1	; The byte currently being written/read by the I2C master
I2CM_flags	ds	1
	I2CM_nack	equ	I2CM_flags.0	; This bit is set if the I2C master has received a NACK from the slave
	I2CM_rx_flag	equ	I2CM_flags.1 	; Indicates that the number of bytes requested have been received
	I2CM_lost_arb	equ	I2CM_flags.2	; indicates that this master has lost arbitration
	I2CM_doing_write equ	I2CM_flags.4	; Used to remember whether doing a read or write when recovering from loss of arbitration
	I2CM_in_control	equ	I2CM_flags.5	; Indicates that this master thinks it is in control of the I2C bus
	I2CM_ID		equ	I2CM_flags.6	; Used to distinguish which master bank is active
I2CM_index 	ds	1	; The index into the I2CM buffer, used for writing	
I2CM_num_bytes 	ds	1	; The index into the I2CM buffer, used for reading
I2CM_strt_ctr	ds	1	; Counts the number of SCL high cycles before sending a start bit onto the i2c-bus

I2CM_NACK_ctr	ds	1	; Used when finding slaves, counts number of retries before giving up finding an ACK
I2CM_addr_ctr	ds	1	; Used when finding slaves, keeps a record of the slave address currently looking for

I2CM1_buffer	=	$	; The buffer uses the last 7 registers of this bank (pre-increments, so put I2CM buffer here.)
I2CM_address	ds	1	; The address to read/write to.
I2CM_data_0 	ds	1	; Data buffer 
I2CM_data_1	ds	1	; Data buffer
I2CM_data_2	ds	1	; Data buffer
I2CM_data_3	ds	1	; Data buffer
; These data registers could easily be placed into another bank if a more are required

	;*********************************************************************************
	; Bank 2 - Serial bank 1
	;*********************************************************************************
		org     bank2_org
BANK2		=	$
SERIAL1		=       $	;UART bank 1
tx_high		ds      1	;hi byte to transmit
tx_low		ds      1	;low byte to transmit
tx_count	ds      1	;number of bits sent
tx_divide	ds      1	;xmit timing (/16) counter
rx_count	ds      1	;number of bits received
rx_divide	ds      1	;receive timing counter
rx_byte		ds      1	;buffer for incoming byte
string		ds	1	;used by send_string to store the address in memory
byte		ds	1	;used by serial routines
hex		ds	1

TIMERS		=	$
timer_low	ds	1	; 16-bit timer
timer_high	ds	1

EEPROM		=	$	; EERPOM bank
EE_bank_adr	ds	1	; current address in bank
EE_char		ds	1	; read byte and byte to write location

ARB
ARB_return	ds	1	; return vector for when retrying I2C operation

	;*********************************************************************************
	; Bank 3 - Timer and other variable ram bank
	;*********************************************************************************
		org     bank3_org

BANK3		=	$
SERIAL2		ds	10	; UART bank 2

	;*********************************************************************************
	; Bank 4 - Serial Bank 2 and some other variables
	;*********************************************************************************
		org     bank4_org
bank4		=	$
I2CM2		ds	11	; I2CM bank 2
; Once again I2CM2 will match I2CM1 but used for the other I2C master. The onl difference is 
; a different buffer equate is required as the FSR is used to access this.
I2CM2_buffer	=	$	; The buffer uses the last 8 registers of this bank (pre-increments, so put I2CM buffer here.)
	;*********************************************************************************
	; Bank 5 - Slave String stored in this bank
	;*********************************************************************************
		org     bank5_org
bank5		=	$
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_address	ds	1	; This byte holds the address which the I2C slave will respond to.
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 master
I2CS_data_in	ds	1	; The saved data byte received from the master.
I2CS_data_out	ds	1	; A byte of data to be read by the master.
I2CS_past_pres	ds	1	; The last_state of the I2C port (to be compared with the present state)
	I2CS_pres_scl	equ	I2CS_past_pres.0 ; The present state of the SCL line
	I2CS_pres_sda	equ	I2CS_past_pres.1 ; The present state of the SDA line
	I2CS_past_scl	equ	I2CS_past_pres.2 ; The state of the SCL line on last interrupt
	I2CS_past_sda	equ	I2CS_past_pres.3 ; The state of the SDA line on last interrupt
I2CS_flags	ds	1
	I2CS_data_valid equ	I2CS_flags.0	; Lets the I2C slave know that there
						; is valid data stored in I2CS_data_out
	I2CS_data_needed equ	I2CS_flags.1	; This flag is set if the I2C master has tried
						; to do a multiple read on this slave and there
						; is no valid data present in the I2CS_data_out
						; register.  The slave will hold the clock line
						; low until this the I2CS_data_valid flag is
						; set, indicating valid data.

	I2CS_being_read_flag equ I2CS_flags.2	; true when command received from master is to send
						; data. When clear slave will be receiving

	;*********************************************************************************
	; Bank 6 - I2
	;*********************************************************************************
		org     bank6_org
bank6		=	$
slave_string1	=	$	; where a string can be stored

	;*********************************************************************************
	; Bank 7
	;*********************************************************************************
		org     bank7_org

BANK7		=	$

IFDEF SX48_52
	;*********************************************************************************
	; Bank 8	; The following banks are only available on SX48/52
	;*********************************************************************************
		org	$80	;bank 8 address on SX52

bank8		=	$
	;*********************************************************************************
	; Bank 9
	;*********************************************************************************
		org	$90	;bank 9 address on SX52

bank9		=	$
	;*********************************************************************************
	; Bank A
	;*********************************************************************************
		org	$A0	;bank A address on SX52

bankA		=	$
	;*********************************************************************************
	; Bank B
	;*********************************************************************************
		org	$B0	;bank B address on SX52

bankB		=	$
	;*********************************************************************************
	; Bank C
	;*********************************************************************************
		org	$C0	;bank C address on SX52

bankC		=	$
	;*********************************************************************************
	; Bank D
	;*********************************************************************************
		org	$D0	;bank D address on SX52

bankD		=	$
	;*********************************************************************************
	; Bank E
	;*********************************************************************************
		org	$E0	;bank E address on SX52

bankE		=	$
	;*********************************************************************************
	; Bank F
	;*********************************************************************************
		org	$F0	;bank F address on SX52

bankF		=	$

ENDIF

;*****************************************************************************************
; Interrupt Service Routine
;*****************************************************************************************
; Note: The interrupt code must always originate at address $0.
;
; Interrupt Frequency = (Cycle Frequency / -(retiw value))  For example:
; With a retiw value of -217 and an oscillator frequency of 50MHz, this
; code runs every 4.34us.
;*****************************************************************************************
		org     $0
interrupt					;3
	;*********************************************************************************
	; Keep running 16bit timer...  execute every ISR
	;*********************************************************************************
		_bank	TIMERS			;1 
		inc	timer_low		;1 
		snz				;1
		inc	timer_high		;1
						;=4

	;*********************************************************************************
	; Virtual Peripheral Multitasker:  4 tasks
	; Do some simple ISR execution management
	; Every four ISR the following will execute:
	;   1 - I2C master 1 VP + UART 1 TX + TIMER + ARBITRATION  (ISR 1)
	;   2 - I2C slave VP	+ UART 2 TX + TIMER + ARBITRATION  (ISR 2)
	;   3 - I2C master 2 VP + UART 1 RX + TIMER + ARBITRATION  (ISR 3)
	;   4 - I2C slave VP	+ UART 2 RX + TIMER + ARBITRATION  (ISR 4)
	;
	;	Input variable(s): isr_multiplex: variable used to choose threads
	;	Output variable(s): 		None, executes the next thread
	;	Variable(s) affected: 		isr_multiplex
	;	Flag(s) affected: 		None
	;	Program Cycles:			10 cycles (turbo mode)
	;*********************************************************************************
		mov	w,timer_low		;1
		and	w,#$03			;1
; The code between the tableBegin and tableEnd statements MUST be 
; completely within the first half of a page.  The routines 
; it is jumping to must be in the same page as this table.
tableStart				; Start all tables with this macro.
		jmp	pc+w			; 3
		jmp	ISR_1			;
		jmp	ISR_2			;
		jmp	ISR_3			;
		jmp	ISR_4			;
tableEnd				; End all tables with this macro.
;-----------------------------------------------------------------------------------------

	;*********************************************************************************
	; Virtual Peripheral: Universal Asynchronous Receiver Transmitter (UART) 
	; These routines send and receive RS232 serial data, and are currently
	; configured (though modifications can be made) for the popular
	; "No parity-checking, 8 data bit, 1 stop bit" (N,8,1) data format.
	; RECEIVING: The rx_flag is set high whenever a valid byte of data has been
	; received and it is the calling routine's responsibility to reset this flag
	; once the incoming data has been collected.
	; TRANSMITTING: The transmit routine requires the data to be inverted
	; and loaded (tx_high+tx_low) register pair (with the inverted 8 data bits
	; stored in tx_high and tx_low bit 7 set high to act as a start bit). Then
	; the number of bits ready for transmission (10=1 start + 8 data + 1 stop)
	; must be loaded into the tx_count register. As soon as this latter is done,
	; the transmit routine immediately begins sending the data.
	; This routine has a varying execution rate and therefore should always be
	; placed after any timing-critical virtual peripherals such as timers,
	; adcs, pwms, etc.
	; Note: The transmit and receive routines are independent and either may be
	;	removed, if not needed, to reduce execution time and memory usage,
	;	as long as the initial "BANK serial" (common) instruction is kept.
	;
	;       Input variable(s) : tx_low (only high bit used), tx_high, tx_count
	;       Output variable(s) : rx_flag, rx_byte
	;       Variable(s) affected : tx_divide, rx_divide, rx_count
	;       Flag(s) affected : rx_flag
	;	Size : Transmit - 15 bytes + 1 byte shared with receive code
	;		Receive - 20 bytes + 1 byte shared with transmit code
	;       Timing For Single UART pass (turbo) : 
	;	       Transmit - (a) [not sending] 9 cycles
	;			  (b) [sending] 19 cycles
	;			  + 1 cycle shared with RX code ("bank" instr.)
	;		Receive - (a) [not receiving] 9 cycles
	;			  (b) [start receiving] 16 cycles
	;			  (c) [receiving, awaiting bit] 13 cycles
	;			  (d) [receiving, bit ready] 17 cycles
	;
	; SERIAL bank 1 and SERIAL bank2 are used to switch between the active UART
	;*********************************************************************************

ISR_1
	;*********************************************************************************
	; UART1 Transmit VP
	;*********************************************************************************
do_UART1_TX	_bank	SERIAL1			;switch to serial register bank
:transmit	decsz	tx_divide		;only execute the transmit routine
		jmp	:transmit_done		; 
		mov	w,#baud_rate_192	;load UART baud rate (50MHz)
		mov	tx_divide,w		; 
		test    tx_count		;are we sending?
		snz				; 
		jmp	:transmit_done		; 
		clc                             ;yes, ready stop bit
		rr      tx_high			; and shift to next bit
		rr      tx_low			; 
		dec     tx_count		;decrement bit counter
		snb	tx_low.6		;output next bit
		clrb	tx_pin1			; 
		sb	tx_low.6		; 
		setb	tx_pin1			; 
:transmit_done

	;*********************************************************************************
	; I2C Master 1 Virtual Peripheral. 
	; Executes every 4 ISR
	;*********************************************************************************
:do_master1	_bank	I2CM1			;1
		mov	w,#I2CM1_buffer		;1 ; Switch to I2CM bank and load FSR for
		mov	fsr,w			;1 ; buffer look-up, if needed.
		call	@I2CM_ISR		;3 + 12/24
						;=18/30
		jmp	ISR_out			; exit this ISR thread
;-----------------------------------------------------------------------------------------
ISR_2		
	;*********************************************************************************
	; UART2 Transmit VP
	;*********************************************************************************
do_UART2_TX	_bank	SERIAL2			;switch to serial register bank
:transmit	decsz	tx_divide		;only execute the transmit routine
		jmp	:transmit_done		; 
		mov	w,#baud_rate_192	;load UART baud rate (50MHz)
		mov	tx_divide,w		; 
		test    tx_count		;are we sending?
		snz				; 
		jmp	:transmit_done		; 
		clc                             ;yes, ready stop bit
		rr      tx_high			; and shift to next bit
		rr      tx_low			; 
		dec     tx_count		;decrement bit counter
		snb	tx_low.6		;output next bit
		clrb	tx_pin2			; 
		sb	tx_low.6		; 
		setb	tx_pin2			; 
:transmit_done

	;*********************************************************************************
	; I2C Slave VP... Runs every second ISR to enable higher sampling rate than sx masters
	;*********************************************************************************
do_I2CS		bank	I2CS			;1
		clc				;1 ;Save the current state and past state of the I2C bus
		snb	sda_pin1		;1
		stc				;1
		rl	I2CS_past_pres		;1
		clc				;1
		snb	scl_pin1		;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

		jmp	ISR_out			; exit this ISR thread
;-----------------------------------------------------------------------------------------
ISR_3
	;*********************************************************************************
	; UART2 Receive VP
	;*********************************************************************************
do_UART2_RX	_bank	SERIAL2			;switch to serial register bank
:receive        sb	rx_pin2			;get current rx bit
	        clc				; 
	        snb	rx_pin2			; 
	        stc				; 
		test    rx_count                ;currently receiving byte?
		sz				; 
		jmp	:rxbit                  ;if so, jump ahead
		mov     w,#9                    ;in case start, ready 9 bits
		sc				;skip ahead if not start bit
		mov     rx_count,w              ;it is, so renew bit count
		mov     w,#start_delay_192	;ready 1.5 bit periods (50MHz)
		mov     rx_divide,w		; 
:rxbit          decsz	rx_divide		;middle of next bit?
          	jmp	:rxdone			;
		mov	w,#baud_rate_192	;yes, ready 1 bit period (50MHz)
		mov	rx_divide,w		; 

		dec     rx_count                ;last bit?
		sz                              ;if not
		rr      rx_byte                 ; then save bit
		snz                             ;if so,
		setb    uart_rx_flag            ; then set flag
:rxdone						; else, exit

	;*********************************************************************************
	; I2C Master 1 Virtual Peripheral. 
	; Executes every 4 ISR
	;*********************************************************************************
:do_master2	bank	I2CM2			;1
		mov	w,#I2CM2_buffer		;1 ; Switch to I2CM bank and load FSR for
		mov	fsr,w			;1 ; buffer look-up, if needed.
		call	@I2CM_ISR		;3 + 12/24
						;=18/30
		jmp	ISR_out			; exit this ISR thread
;-----------------------------------------------------------------------------------------
ISR_4
	;*********************************************************************************
	; UART1 Receive VP
	;*********************************************************************************
		_bank	SERIAL1			;switch to serial register bank
:receive        sb	rx_pin1			;get current rx bit
	        clc				; 
	        snb	rx_pin1			; 
	        stc				; 
		test    rx_count                ;currently receiving byte?
		sz				; 
		jmp	:rxbit                  ;if so, jump ahead
		mov     w,#9                    ;in case start, ready 9 bits
		sc				;skip ahead if not start bit
		mov     rx_count,w              ;it is, so renew bit count
		mov     w,#start_delay_192	;ready 1.5 bit periods (50MHz)
		mov     rx_divide,w		; 
:rxbit          decsz	rx_divide		;middle of next bit?
          	jmp	:rxdone			;
		mov	w,#baud_rate_192	;yes, ready 1 bit period (50MHz)
		mov	rx_divide,w		; 

		dec     rx_count                ;last bit?
		sz                              ;if not
		rr      rx_byte                 ; then save bit
		snz                             ;if so,
		setb    uart_rx_flag            ; then set flag
:rxdone						; else, exit
		jmp	do_I2CS
;-----------------------------------------------------------------------------------------

ISR_out
	;*********************************************************************************
	; Update I2C port with buffered port data
	; This may need to be changed if your application has different pin configuration
	; to the circuit shown at the top of the source file.
	;*********************************************************************************
		_bank	I2CM2			;1 ; get master 2 line status and
		mov	w,I2CM_port_buf		;1 ;  write master 2 I2C lines to W
		stc				;1 
		rl	wreg			;1 ; shift over sda and scl lines
		stc				;1 ; ensure 1's are left on other lines for AND
		rl	wreg			;1 
		bank	I2CM1			;1 ; Select Master 1
		and	w,I2CM_port_buf		;1 
		bank	I2CS			;1 ; Select Slave
		and	w,I2CS_port_buf		;1
		mov	!i2c_port,w		;1 ; Update the I2C port with the buffered port data
						;=11

	;*********************************************************************************
	; I2C_arbitration
	; This code checks to see that the data written by I2CM ONE is the same
	; as the data present on the bus.  If not, this master has lost arbitration.
	;*********************************************************************************
I2C_arbitration
		_bank	I2CM1			 ;1	
	; --- Check if arbitration required ---
		jb	I2CM_lost_arb,arb_done	 ;2/4 ; No need to check if lost arb. already lost
		jnb	I2CM_in_control,arb_done ;2/4 ; Only check arbitration when master is in control
  	; --- Check if data on bus matches data written by master ---
		jb	sda_pin1,arb_done	 ;2/4 ; No master can have lost arb. when sda is high
		jnb	I2CM_port_buf.sda,arb_done ;2/4 check if buffer wants data high, if it does master loses arb.
 	; --- Arbitration has been lost. The master must now wait a 'random' time before trying again --- 
:arb_just_lost	setb	I2CM_lost_arb		 ;1 ; signal a loss of arbitration 
		clr	I2CM_state		 ;1 ; Put state machine into IDLE state
		clr	I2CM_sub_state		 ;1
arb_done					 ;=5/12

	;*********************************************************************************
	; Set Interrupt Rate
	;*********************************************************************************
isr_end
		mov	w,#-int_period		;1 ; refresh RTCC on return
		retiw				;3 ; return from the interrupt
				
;*****************************************************************************************
; End of the Interrupt Service Routine
;*****************************************************************************************

;*****************************************************************************************
; Jump table to second half of page
;*****************************************************************************************
do_slave	jmp	do_slave_

;*****************************************************************************************
; RESET VECTOR 
;*****************************************************************************************

	;*********************************************************************************
	; Program execution begins here on power-up or after a reset
	;*********************************************************************************

reset_entry		
	;*********************************************************************************
	; Initialise all port configuration
	;*********************************************************************************

IFDEF SX48_52
		mov	w,#$1f			; point MODE to write DDIR (SX48/52)
ELSE
		mov	w,#$0f			; point MODE to DDIR (SX18/20/28)
ENDIF
		mov	m,w			; Load m through w to ensure 5-bits written
						; in SX48/52
		mov     w,#RA_init            	; Initialize data latches for
		mov     ra,w			; port A.
		mov	w,#RA_io		; Initialize data direction register for		
		mov	!ra,w			; port A.
		mov     w,#RB_init          	; Initialize data latches for
		mov     rb,w			; port B.
		mov	w,#RB_io		; Initialize data direction register for
		mov	!rb,w			; port B.
		mov     w,#RC_init          	; Initialize data latches for
		mov     rc,w			; port C.
		mov	w,#RC_io		; Initialize data direction register for
		mov	!rc,w			; port C.

IFDEF SX48_52
		mov     w,#RD_init          	; Initialize data latches for
		mov     rd,w			; port D.
		mov	w,#RD_io		; Initialize data direction register for
		mov	!rd,w			; port D.
		mov     w,#RE_init          	; Initialize data latches for
		mov     re,w			; port E.
		mov	w,#RE_io		; Initialize data direction register for
		mov	!re,w			; port E.
ENDIF
	;*********************************************************************************
	; Clear all Data RAM locations
	;*********************************************************************************
IFDEF SX48_52   				;SX48/52 RAM clear routine
		mov	w,#$0a			;reset all ram starting at $0A
		mov	fsr,w
:zero_ram	clr	ind			;clear using indirect addressing
		incsz	fsr			;repeat until done
		jmp	:zero_ram

		_bank	bank0			;clear bank 0 registers
		clr	$10
		clr	$11
		clr	$12
		clr	$13
		clr	$14
		clr	$15
		clr	$16
		clr	$17
		clr	$18
		clr	$19
		clr	$1a
		clr	$1b
		clr	$1c
		clr	$1d
		clr	$1e
		clr	$1f

		bank	TIMER
		mov	w,timer_low

ELSE     					;SX18/20/28 RAM clear routine
		clr	fsr			;reset all ram banks
:zero_ram	sb	fsr.4			;are we on low half of bank?
		setb	fsr.3			;If so, don't touch regs 0-7
		clr	ind			;clear using indirect addressing
		incsz	fsr			;repeat until done
		jmp	:zero_ram
ENDIF

	;*********************************************************************************
	; Load Timer high with random value
	;*********************************************************************************

		bank	TIMERS
		mov	timer_high,w		; random seed
		test	timer_high		
		snz				
		inc	timer_high		; just to make sure timer is not zero

	;*********************************************************************************
	; Set default slave string
	;*********************************************************************************
		bank	slave_string1
		mov	slave_string1,	#'S'
		mov	slave_string1+1,#'X'
		mov	slave_string1+2,#'1'
		mov	slave_string1+3,#' '
		mov	slave_string1+4,#'S'
		mov	slave_string1+5,#'L'
		mov	slave_string1+6,#'A'
		mov	slave_string1+7,#'V'
		mov	slave_string1+8,#'E'

	;*********************************************************************************
	; Initialize I2C program registers
	;*********************************************************************************

		_bank	I2CM1
		call	@I2CM_init
		clrb	I2CM_ID
		_bank	I2CM2
		setb	I2CM_ID

		call	@I2CS_init

	;*********************************************************************************
	; 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 PROGRAM CODE
; This can be changed as you like to suit your own application
; At present data is read from an EEPROM device and the data stored into the last RAM bank
;*****************************************************************************************

main
	;*********************************************************************************
	; Main - Send Prompts to both UART1 and UART2
	;*********************************************************************************
		_bank	SERIAL1				; Select UART1
		clrb	active_uart_flag		; active uart is uart1
		skip
:prompt_send	setb	active_uart_flag
		mov	w,#_hello			; send title string
		call	@send_string
		mov	w,#_sxm				; send master x string
		call	@send_string
		sb	active_uart_flag
		mov	w,#'1'				
		snb	active_uart_flag
		mov	w,#'2'				
		call	@send_byte			; send master number
		mov	w,#_finding_devices		; send "finding devices"
		call	@send_string
		_bank	SERIAL2				; Select UART2
		jnb	active_uart_flag,:prompt_send	; send to UART2 as well


	;*********************************************************************************
	; Find I2C slaves on both bus, and displays there addresses. 
	; Tries each address twice before giving up. This retry value is easily changed however.
	; Both masters search for their slaves simultaniously
	; This routine works by sending out the address of a slave and waiting for an ACK
	; If an ACK is received then the current master knows there is a slave at that address
	;*********************************************************************************
		bank	ARB				; load return vector incase bus is busy
		mov	ARB_return,#_loopstart		;  and arbitration is needed
		bank	I2CM1				; initialize the address register to
		clr	I2CM_addr_ctr			; start looking for slaves from address 0
		_bank	I2CM2
		clr	I2CM_addr_ctr			; start looking for slaves from address 0
		mov	MASTER_BANK,#I2CM1		; select master 1 to start
		mov	UART_BANK,#SERIAL1		; select uart 1 to start also
		clrb	active_master_flag		; set master 1 as active

_loopstart	call	@sel_MASTER_BANK		; clear the number of data bytes (we are 
		clr	I2CM_NACK_ctr			; sending only the address, no data, and looking
		clr	I2CM_num_bytes			; for an ACK response.)
_find_loop
:nack		call	@sel_MASTER_BANK		; switch to I2CM bank.  
		mov	I2CM_address,I2CM_addr_ctr	; move the address counter into the I2CM address register
							;  because this is the next slave address to look for.
		call	@I2CM_send_bytes		; send this address onto the I2C bus.
		call	@I2CM_wait_not_busy		; wait until the transfer is complete.
		jb	I2CM_lost_arb,Ive_lost_arb
		call	@sel_MASTER_BANK
		and	w,#$ff				; check w for a successful transmission.  If transmission
							;   was unsuccessful, w will be non-zero.
		jz	:device_found			; if W was zero, this was successful.  Output the address found.
		decsz	I2CM_NACK_ctr			;   
		jmp	:nack				;  Keep trying until number of NACKs = 2
:next_device
		mov	I2CM_NACK_ctr,#2		; Try twice to get an ACK if a nack was received.
		setb	I2CM_addr_ctr.0			; increment by two's.
		inc	I2CM_addr_ctr
		jnb	active_master_flag,:m2		; if master one is active, do master 2 slave find
		jz	device_search_done		; device search is done when the address counter rolls over on m2.
		jmp	:m1				;  else, continue.
:device_found
		mov	I2CM_NACK_ctr,#2		; try each address 2 times when a NACK is received.
		mov	w,I2CM_addr_ctr			
		call	@sel_UART_BANK			; Select active UART 
		call	@send_hex			; send the address of the device which was found.
		mov	w,#' '				;   and seperate it from the last with a space.
		call	@send_byte
		call	@sel_MASTER_BANK
		setb	I2CM_addr_ctr.0			; increment by two's
		inc	I2CM_addr_ctr			; 
		jnb	active_master_flag,:m2		; if master one is active, do master 2 slave find
		jz	device_search_done		; If address counter rolls over, we are finished
:toggle_masters	
:m1		clrb	active_master_flag
		mov	MASTER_BANK,#I2CM1		; select master 1 for bank switches
		mov	UART_BANK,#SERIAL1		; select uart 1 for bank swithces
		jmp	_find_loop			; keep looking for slaves
:m2
		setb	active_master_flag
		mov	MASTER_BANK,#I2CM2		; select master 2 for bank switches
		mov	UART_BANK,#SERIAL2		; select uart 2 for bank switches
		jmp	_find_loop			; keep looking for slaves

device_search_done					; Done looking for devices on the bus.

	;*********************************************************************************
	; Main Program Loop
	; The main prpgram loop is broken into a number of stages. This is to allow
	; the slave rx_flag to be checked within each of the loops and respond at any time 
	; within the mainline code
	;*********************************************************************************
main_loop
		bank	ARB				; load return vector incase bus is busy
		mov	ARB_return,#main		;  and arbitration is needed

		; SEND INSTRUCTIONS
		bank	SERIAL1				; Select UART1
		mov	w,#_instructions		; send the instructions.
		call	@send_string
		bank	SERIAL2				; Select UART2
		mov	w,#_instructions		; send the instructions.
		call	@send_string
	
:stage1_loop	; GET A CHARACTER FROM A TERMAINAL AND SELECT WHICH MASTER TO RESPOND
		snb	I2CS_rx_flag			; Check if slave has received data
		call	do_slave			; process data received by the slave

		bank	SERIAL1				; Select UART1
		clrb	active_master_flag		; set master 1 as active
		jb	uart_rx_flag,:check_byte	; wait till a byte has been recvd by UART before calling get_byte

		bank	SERIAL2				; Select UART2
		setb	active_master_flag		; Set Master 2 as active 
		jb	uart_rx_flag,:check_byte	; wait till a byte has been recvd by UART before calling get_byte

		jmp	:stage1_loop			; keep looping until a byte is ready to receive

:check_byte	; CHECK IF CHAR IS EITHER A 'R' OR A 'S'
		clrb	eewrite_flag			; set EEPROM write flag to default (read)
		call	@get_byte			; get the key pressed
		call	@uppercase			; set to uppercase
		cje	byte,#'R',:stage2		; user wants to read some data from a slave
		cjne	byte,#'S',:stage1_loop		
		setb	eewrite_flag			; user wants to write to eeprom

	;*********************************************************************************
	; Load the bank select registers with the active master/uart addresses
	; Display a pause message on the inactive termainal master while the other master
	; is busy. This is only due to the mainline complications of running them at the
	; the same time. It is possible to run both masters simultaniously as seen when 
	; both masters do the slave device find.
	;*********************************************************************************
:stage2		jb	active_master_flag,:m2	
:m1
		bank	SERIAL2				; Display master 2 waiting message
		mov	w,#_master_wait
		call	@send_string
		mov	MASTER_BANK,#I2CM1		; select master 1 for bank switches
		mov	UART_BANK,#SERIAL1		; select uart 1 for bank switches
		jmp	:m_done
:m2
		bank	SERIAL1				; diaplay master 1 waiting message
		mov	w,#_master_wait
		call	@send_string
		mov	MASTER_BANK,#I2CM2		; select master 2 for bank switches
		mov	UART_BANK,#SERIAL2		; select uart 2 for bank switches
:m_done
		call	@sel_UART_BANK			; Select UART
		mov	w,#_enter_adr			; Display prompt for user
		call	@send_string

	;*********************************************************************************
	; Get the address of the device to read/write to from the user
	;*********************************************************************************
:stage2_loop
		snb	I2CS_rx_flag			; Check if slave has received data
		call	do_slave			; process data received by the slave
		call	@sel_UART_BANK			; Select UART
		sb	uart_rx_flag			; wait until a byte is waiting before calling get_hex
		jmp	:stage2_loop

		call	@sel_UART_BANK			; Select UART
		call	@get_hex			; get the address of the slave in hex from user

	;*********************************************************************************
	; Check for device present at that address
	;*********************************************************************************
:stage3		call	@sel_MASTER_BANK		; switch to active Master's RAM bank.
		mov	I2CM_address,w
		clr	I2CM_num_bytes			; check for the device's presence.
		call	@I2CM_send_bytes
		call	@I2CM_wait_not_busy		; wait for master to be idle and return ack or nack
		and	w,#$ff
		snz
		jmp	:device_present			; If we received an ACK, then send ACK message
:NACK		call	@sel_UART_BANK			; Select UART
		mov	w,#_NACK			; else send NACK and return to command mode.
		call	@send_string
		jmp	main				; no ack. was received. restart

:device_present
		call	@sel_UART_BANK			; Select UART
		mov	w,#_ACK				; send ACK message.
		call	@send_string			; and return to command mode.
	;*********************************************************************************
	; Do read or write data to the SLAVE depending on user selection
	;*********************************************************************************
:stage4		bank	EEPROM
		clr	EE_bank_adr
		jb	eewrite_flag,eewrite		; either read or write to EEPROM
	;*********************************************************************************
	; Read data from the slave
	;*********************************************************************************
		call	@sel_UART_BANK			; Select UART
		mov	w,#_ee_read
		call	@send_string			; display retreaving data string to user
eeread		call	@EE_read_char			; read a byte from the eeprom to EE_byte
		test	w				; check for null character read
		jz	main				; null char, end of string...restart
		call	@sel_UART_BANK			; Select UART
		call	@send_byte			; send received character to terminal (in w)
		bank	I2CS
		snb	I2CS_rx_flag			; Check if data was read from on board slave
		call	do_slave			; Do slave routine to check byte read ok
		bank	EEPROM
		inc	EE_bank_adr			; set to read next address
		jz	main				; finished reading entire bank...restart
		jmp	eeread				; read another character from eeprom

	;*********************************************************************************
	; Write a sting to the current EEPROM bank. 
	;*********************************************************************************
eewrite		call	@sel_UART_BANK			; Select UART
		mov	w,#_ee_write_pmpt		; prompt user to enter a string
		call 	@send_string
:get_char
:stage4_loop	
		snb	I2CS_rx_flag			; Check if the other master has sent data to this slave
		call	do_slave			; process data sent to this slave
		call	@sel_UART_BANK			; Select UART
		sb	uart_rx_flag			; wait till a byte is waiting before calling get_byte
		jmp	:stage4_loop

		call	@get_byte
		cje	byte,#13,ee_done_string		; stop when enter is pressed
		mov	w,byte
		bank 	EEPROM
		mov	EE_char,w			; load send buffer with character
		call	@EE_write_char
		bank	EEPROM
		mov	w,EE_char
		call	@sel_UART_BANK			; Select UART
		call	@send_byte			; echo character received to terminal
		bank 	EEPROM
		inc	EE_bank_adr			; check if 256 chars received already
		test	EE_bank_adr
		sz
		jmp	:get_char
		call	@sel_UART_BANK
		mov	w,#_too_long			; Send string is longer that 256 chars msg.
		call	@send_string
		jmp	main				; no need for null char since bank full
ee_done_string	
		bank 	EEPROM
		clr	EE_char				; end string with NULL
		call	@EE_write_char
		jmp 	main				; finished writing the string


	;*********************************************************************************
	; SLAVE SUBROUTINE
	; Simulates an EEPROM slave device. (READ ONLY) 
	; This subroutine can be improved by allowing allowing both reads and writes but this should
	; be done with the intended application in mind. This routine simply allows the same protocol
	; for an EEPROM read to work on the SX.
	;*********************************************************************************
do_slave_
		_bank 	I2CS
		clrb	I2CS_rx_flag			; clear the data waiting flag
		jb	I2CS_being_read_flag,:being_read	; Check if slave is to send or receive
	; --- Load data_out with data in case master is trying to read from slave 
	; --- The data will be read out next time through being_read 
:being_written
		mov	w,#slave_string1		; set location of stored string
		add	w,I2CS_data_in
		mov	fsr,w				; set location of stored byte(offset)
		mov	w,indf				; get stored byte
		_bank	I2CS
		mov	I2CS_data_out,w			; load data out register with stored byte
		setb	I2CS_data_valid			; set data valid flag so data is sent
		retp
:being_read						; loaded data will be sent in ISR... do nothing here
		retp

	;*********************************************************************************
	; This Master will wait here when arbitration is lost
	;*********************************************************************************
Ive_lost_arb	clrb	green_led
		clrb	red_led2
		setb	red_led1
wait_here	_bank	TIMERS				; Wait a 'random' time before trying again
		test	timer_high
		sz
		jmp	wait_here
		_bank	I2CM1
		clrb	I2CM_lost_arb
		_bank	ARB				; return to arbitration reset vector.
		mov	w,ARB_return			;  this is set to a location to retry 
		clrb	red_led1
		jmp	w				;  I2C operations from

	;*********************************************************************************
	; ERROR CONDITIONS:
	;*********************************************************************************
got_NACK	setb	green_led			; flash all LEDs
		setb	red_led1
		setb	red_led2
		jmp	main				; Retry I2C operation

org	I2CS_ISR_FUNC_ORG
;*****************************************************************************************
; 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
		mov	w,I2CS_past_pres	;1
		and	w,#$0f			;1
		xor	w,#%00001101		;1 ;Compare with a start condition.
		snz				;1
		jmp	:start			;3
		xor	w,#%00001010		;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	I2C_stop_found		;1
		retp				;3 = 12

:start		mov	w,#1			;1 ;If start, then start receiving the address.
		mov	I2CS_state,w		;1
		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_address
	;	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_address
	;	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_read_data
	;	This state prepares I2CS_read_byte to receive 8 bits of data.  It increments
	;	the state before exiting.
	;
	;  I2CS_make_idle
	;	This state puts the I2C slave back into idle mode.
	;
	;  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_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 try to send another byte of data from the I2CS_data_out register.  If no
	;	ACK is received, the slave will be put back into its idle state.
	;*********************************************************************************
I2CS_isr	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_address	;3 ;Get the address
		jmp	I2CS_read_byte		;3 
		jmp	I2CS_process_address	;3 ;Check to see if this device was addressed, and set
						   ;up for read or write if it was.  If there is no valid
		;*************************************************************************
		; 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_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.
		jmp	I2CS_waiting_for_data	;3 ;If there is no valid data, wait for it.
	
		;*************************************************************************
		; 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
		jmp	I2CS_read_data		;3 
		jmp	I2CS_read_byte		;3 
		jmp	I2CS_process_data	;3 
		jmp	I2CS_send_ack		;3 
		jmp	I2CS_make_idle		;3 

	;*********************************************************************************
	; 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_address
	;	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_address
		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_address
	;	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_address
		mov	w,I2CS_byte		;1 ;Get the received I2C byte
		xor	w,I2CS_address		;1 ;Compare with the address of this slave
		and	w,#$FE			;1 ;And out the R/!W bit
		snz				;1
		jmp	:slave_addressed	;3 ;If it is equal, send an ACK
		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
		clrb	I2CS_being_read_flag	;1 clear the slave being read flag
		retp				;3 = 14 + 7 = 21

:being_read	sb	I2CS_data_valid		;1
		jmp	I2CS_make_idle		;3 = 16
		mov	w,#(I2CS_being_read-I2CS_idle_loc);1
		mov	I2CS_state,w		;1
		setb	I2CS_being_read_flag	;1 set the slave being read flag
		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
		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_make_idle	
	;	This state puts the I2C slave back into idle mode.
	;*********************************************************************************
I2CS_make_idle
		clr	I2CS_state		;1
		clr	I2CS_sub_state		;1
		setb	I2CS_port_buf.scl	;1
		setb	I2CS_port_buf.sda	;1
		retp				;3 = 7/23(from I2CS_process_address) + 7 = 14/30

	;*********************************************************************************
	; 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
		mov	w,I2CS_byte		;1
		mov	I2CS_data_in,w		;1
		setb	I2CS_rx_flag		;1
		setb	I2CS_event_flag		;1 ;Indicate that a byte has been received with
						   ;the global event flag.
		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
		inc	I2CS_state		;1
		clr	I2CS_sub_state		;1
		clrb	I2CS_data_valid		;1
		mov	w,I2CS_data_out		;1
		mov	I2CS_byte,w		;1
		mov	w,#8			;1
		mov	I2CS_bit_count,w	;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 try to send another byte of data from the I2CS_data_out register.  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
		inc	I2CS_state		;1
		retp				;3 = 14 + 7 = 21

	;*********************************************************************************
	; State: I2CS_waiting_for_data
	;	This state waits for the mainline routine to put some valid data in the
	;	I2C_data_out 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_valid		;1
		jmp	:data_valid		;3
		setb	I2CS_data_needed	;1
		setb	I2CS_event_flag		;1
		retp				;3 = 13 + 7 = 20

:data_valid	mov	w,#(I2CS_being_read_again-I2CS_idle_loc);1
		mov	I2CS_state,w		;1
		retp				;3 = 16 + 7 = 23 

;*****************************************************************************************
; End of I2C Slave state machine
;*****************************************************************************************	

;*****************************************************************************************
; I2C Slave subroutines
;*****************************************************************************************
	; 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,#%10111111		;Set RB 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
		mov	w,#slave_address
		mov	I2CS_address,w
		retp

;*****************************************************************************************
; I2C Master Interrupt Service Routines
;*****************************************************************************************
; I2C Virtual Peripheral...
org	I2CM_ISR_FUNC_ORG

	;*********************************************************************************
	; Function: I2CM_ISR
	; I2C Master Interrupt-Driven State Machine
	; -------------------------------------------
	;	This is the I2C Master Interrupt Service Routine.  It is an interrupt-
	;	driven state machine which allows all of the actions of the I2C Master
	;	controller to be carried out, virtual peripheral style, with virtually
	;	no interaction from the mainline program.  
	;  I2CM_idle
	; 	This is the state that the I2C Master is usually in when it is not in use.
	; 	It just ensures that the I2C_port_buf SCL and SDA are both set high
	;  I2CM_start
	; 	When any mainline program wants to use the I2C master it puts the master into
	; 	start mode.  This mode creates a start condition on the I2C bus.  A start 
	;	condition is created when SDA goes from high to low while SCL stays high.
	;  I2CM_start_write
	;	This state performs some pre-processing which allows the I2CM_write state to
	;	do its work.  It sets up the bit count, gets the next piece of data from the
	;	buffer and prepares to send it.
	;  I2CM_write
	;	This state writes the data in I2CM_byte to the I2C bus
	;  I2CM_get_ack
	;	This state gets an ACK from the slave device.  If no ACK is received, the I2C
	;	Master state machine puts a stop condition on the bus and the I2CM_flags 
	;	register is loaded to indicate that a NACK has occurred.
	;  I2CM_write_repeat
	;	This state determines, after one byte of data is sent, whether or not
	; 	there is another byte to be sent.  If so, this state goes back to I2CM_start_write
	;	and sends the next byte.
	;  I2CM_stop
	;	This state puts a stop condition on the I2C bus and resets the state machine back
	;	to its idle state.  A stop condition is when SDA goes from low to high while SCL
	;	is high.
	;  I2CM_start_read
	;	This state simply loads the contents of the I2CM_address register into the I2CM_byte
	;	register and sets up the I2CM_write state to output the address of the slave to read.
	;  I2CM_read_data	
	; 	This state prepares the I2CM read routine so it can read from the slave device.  It
	;	initializes the bit count, etc.
	;  I2CM_read
	; 	This state read 8 bits of data from the slave device.
	;  I2CM_store_byte
	; 	This state stores the byte just read into the buffer.
	;  I2CM_send_ack
	; 	This state sends an ack if there is data left to write, and a NACK if there is
	;	no data left to write.
	;*********************************************************************************
I2CM_ISR
		mov	w,I2CM_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 Master while idle
		;*************************************************************************
	I2CM_idle_loc		=	$
		jmp	I2CM_idle		;3 ;If I2C_state = 0, I2C is idle

		;*************************************************************************
		; States for I2C Master while writing
		;*************************************************************************
	I2CM_write_loc	=	$
		jmp	I2CM_start		;3 ;Make SDA go low while SCL is high
		jmp	I2CM_start_write	;3 ;Load a byte from the buffer and prepare to send. 
		jmp	I2CM_write		;3 ;Write it.
		jmp	I2CM_get_ack		;3 ;Get an ACK signal
		jmp	I2CM_write_repeat	;3 ;Check to see if we have finished sending 
						   ;(if buffer read index = buffer write index)
	I2CM_stop_loc	=	$
		jmp	I2CM_stop		;3 ;If write_repeat determines we're finished,
						   ;then send stop

		;*************************************************************************
		; States for I2C Master while reading
		;*************************************************************************
	I2CM_read_loc	=	$		   ;Load this state into state machine if we
						   ;are starting to read
		jmp	I2CM_start		;3 ;
		jmp	I2CM_start_read		;3 ;Write address used for reading
		jmp	I2CM_write 		;3 ;(writing address)
		jmp	I2CM_get_ack		;3 ;
	I2CM_read_rpt_loc =	$
		jmp	I2CM_read_data		;3 ;
		jmp	I2CM_read		;3 ;Keep doing this until done
		jmp	I2CM_store_byte		;3 ;
		jmp	I2CM_send_ack		;3 ;
		jmp	I2CM_stop		;3 ;
						
	;*********************************************************************************
	; State: I2CM_idle
	; 	This is the state that the I2C Master is usually in when it is not in use.
	; 	It just ensures that the I2C_port_buf SCL and SDA are both set high
	;*********************************************************************************
I2CM_idle	setb	I2CM_port_buf.sda	;1
		setb	I2CM_port_buf.scl	;1
		retp				;3 = 6 + 7 = 13

	;*********************************************************************************
	; State: I2CM_start
	; 	When any mainline program wants to use the I2C master it puts the master into
	; 	start mode.  This mode creates a start condition on the I2C bus.  A start 
	;	condition is created when SDA goes from high to low while SCL stays high.
	;*********************************************************************************
I2CM_start	mov	w,I2CM_sub_state	;1
		add	pc,w			;3
		jmp	:state1			;3
		jmp	:state2			;3
		jmp	:state3			;3
		jmp	:state4			;3
		jmp	:state5			;3

:state1	 	setb	I2CM_port_buf.scl	;1
		setb	I2CM_in_control		;1
		setb	I2CM_port_buf.sda	;1
		inc	I2CM_sub_state		;1
		mov	I2CM_strt_ctr,#strt_delay ;2 
		retp				;3 = 9 + 7 = 16

;	--- WAITING FOR STOP/START OR IDLE SCL ---
:state2		jb	I2CM_ID,:m2		;check which master is active
:m1		sb	scl_pin1		;1 
		clr	I2CM_sub_state		;1 
		jmp	:m_done
:m2		sb	scl_pin2		;1 
		clr	I2CM_sub_state		;1 
:m_done		decsz	I2CM_strt_ctr		;1/2
		retp				;3
		inc	I2CM_sub_state		;1
		retp				;3 = 6/8 + 7 = 13/15
;	--- FINISHED CLOCK SYNCRONIZATION PERIOD ---

:state3		clrb	I2CM_port_buf.sda	;1
		inc	I2CM_sub_state		;1
		retp				;3 = 5 + 7 = 12

:state4		clrb	I2CM_port_buf.scl	;1
		inc	I2CM_sub_state		;1
		retp				;3 = 5 + 7 = 12

:state5		setb	I2CM_port_buf.sda	;1
		clr	I2CM_sub_state		;1
		inc	I2CM_state		;1
		retp				;3 = 6 + 7 = 13

	;*********************************************************************************
	; State: I2CM_start_write	
	;	This state performs some pre-processing which allows the I2CM_write state to
	;	do its work.  It sets up the bit count, gets the next piece of data from the
	;	buffer and prepares to send it.
	;*********************************************************************************
I2CM_start_write
		inc	I2CM_index		;1
		mov	w,I2CM_index		;1
		add	fsr,w			;1
		mov	w,indf			;1

		mov	I2CM_byte,w		;1
		mov	w,#8			;1
		mov	I2CM_bit_count,w	;1
		inc	I2CM_state		;1
		retp				;3 = 12 + 7 = 19

	;*********************************************************************************
	; State: I2CM_write
	;	This state writes the data in I2CM_byte to the I2C bus
	;*********************************************************************************
I2CM_write	mov	w,I2CM_sub_state	;1
		add	pc,w			;3
		jmp	:state1			;3
		jmp	:state2			;3
		jmp	:state3			;3

:state1		setb	I2CM_in_control
		setb	I2CM_port_buf.sda	;1
		rl	I2CM_byte		;1
		sc				;1
		clrb	I2CM_port_buf.sda	;1
		inc	I2CM_sub_state		;1
		retp				;3 = 15 + 7 = 22

:state2		setb	I2CM_port_buf.scl	;1
		inc	I2CM_sub_state		;1
		retp				;3 = 12 + 7 = 19

:state3		clrb	I2CM_port_buf.scl	;1
		dec	I2CM_bit_count		;1
		snz				;1
		jmp	:done			;3
		clr	I2CM_sub_state		;1
		retp				;3 = 14 + 7 = 21

:done		clr	I2CM_sub_state		;1
		inc	I2CM_state		;1
		clrb	I2CM_in_control
		retp				;3 = 18 + 7 = 25

	;*********************************************************************************
	; State: I2CM_get_ack
	;	This state gets an ACK from the slave device.  If no ACK is received, the I2C
	;	Master state machine puts a stop condition on the bus and the I2CM_flags 
	;	register is loaded to indicate that a NACK has occurred.
	;*********************************************************************************
I2CM_get_ack	mov	w,I2CM_sub_state	;1
		add	pc,w			;3
		jmp	:state1			;3
		jmp	:state2			;3
		jmp	:state3			;3
		jmp	:state4			;3

:state1		setb	I2CM_port_buf.sda	;1
		inc	I2CM_sub_state		;1
		retp				;3 = 13 + 7 = 20

:state2		setb	I2CM_port_buf.scl	;1
		inc	I2CM_sub_state		;1
		retp				;3 = 12 + 7 = 19

:state3		jb	I2CM_ID,:m2
:m1		jb	sda_pin1,:nack		;1
		inc	I2CM_sub_state		;1
		retp				;3 = 13 + 7 = 20 ;if no ack,
:m2		jb	sda_pin2,:nack		;1
		inc	I2CM_sub_state		;1
		retp				;3 = 13 + 7 = 20 ;if no ack,
:nack		mov	w,#(I2CM_stop_loc-I2CM_idle_loc);1
		mov	I2CM_state,w		;1
		clr	I2CM_sub_state		;1 ;send a stop and indicate that this didn't work.
		setb	I2CM_NACK		;1 ;set I2CM_NACK to show that this did not go through.
		retp				;3 = 17 + 7 = 24

:state4		clrb	I2CM_port_buf.scl	;1
		clr	I2CM_sub_state		;1
		inc	I2CM_state		;1 ;move on to next state
		retp				;3 = 13 + 7 = 20


	;*********************************************************************************
	; State: I2CM_write_repeat
	;	This state determines, after one byte of data is sent, whether or not
	; 	there is another byte to be sent.  If so, this state goes back to 
	;	I2CM_start_write and sends the next byte.
	;*********************************************************************************
I2CM_write_repeat
		mov	w,I2CM_num_bytes	;1 ;test the read index to see if it is = to write index.
		xor	w,I2CM_index		;1 ;if it is, then we have finished writing the buffer via. I2C.
		snz				;1
		jmp	:I2CM_write_done	;3
		dec	I2CM_state		;1 ;back to get_ack
		dec	I2CM_state		;1 ;back to write
		dec	I2CM_state		;1 ;back to write_data
		retp				;3 = 16 + 7 =23

:I2CM_write_done
		inc	I2CM_state		;1 ;move on to next state (stop)
		retp				;3 = 17 + 7 = 24;and start the stop bit	

	;*********************************************************************************
	; State: I2CM_stop
	;	This state puts a stop condition on the I2C bus and resets the state machine back
	;	to its idle state.  A stop condition is when SDA goes from low to high while SCL
	;	is high.
	;*********************************************************************************
I2CM_stop	mov	w,I2CM_sub_state	;1
		add	pc,w			;3
		jmp	:state1			;3
		jmp	:state2			;3
		jmp	:state3			;3

:state1		setb	I2CM_in_control
		clrb	I2CM_port_buf.sda	;1
		inc	I2CM_sub_state		;1
		retp				;3 = 13 + 7 = 20

:state2		setb	I2CM_port_buf.scl	;1
		inc	I2CM_sub_state		;1
		retp				;3 = 13 + 7 = 20

:state3		setb	I2CM_port_buf.sda	;1
		clr	I2CM_sub_state		;1 ;put the state machine in Idle
		clr	I2CM_state		;1
		clr	I2CM_num_bytes		;1
		mov	w,#$ff			;1
		mov	I2CM_index,w		;1
		clrb	I2CM_in_control
		retp				;3 = 16 + 7 = 23 

;*****************************************************************************************
; End of I2C Master state machine
;*****************************************************************************************


;*****************************************************************************************
; Jump table
;*****************************************************************************************
I2CM_send_byte		jmp	I2CM_send_byte_		;3
I2CM_send_bytes		jmp	I2CM_send_bytes_	;3
I2CM_get_byte		jmp	I2CM_get_byte_		;3
I2CM_get_bytes		jmp	I2CM_get_bytes_		;3
I2CM_wait_not_busy	jmp	I2CM_wait_not_busy_	;3
I2CM_init		jmp	I2CM_init_		;3
;*****************************************************************************************
; I2C Master Subroutines
;*****************************************************************************************

	;*********************************************************************************
	; Function: I2CM_start_read
	;	This state simply loads the contents of the I2CM_address register into the 
	;	I2CM_byte register and sets up the I2CM_write state to output the address 
	;	of the slave to read.
	;*********************************************************************************
I2CM_start_read
		mov	w,I2CM_address
		mov	I2CM_byte,w
		inc	I2CM_index
		mov	w,#8
		mov	I2CM_bit_count,w
		inc	I2CM_state
		retp

	;*********************************************************************************
	; Function: I2CM_read_data	
	; 	This function prepares the I2CM read routine so it can read from the slave 
	;	device. It initializes the bit count, etc.
	;*********************************************************************************
I2CM_read_data
		mov	w,#8
		mov	I2CM_bit_count,w
		inc	I2CM_state
		;*************************************************************************
		; Read 8 bits of data from the slave device.
		;*************************************************************************
I2CM_read	mov	w,I2CM_sub_state
		add	pc,w
		jmp	:state1
		jmp	:state2
		jmp	:state3

:state1		clrb	I2CM_in_control
		setb	I2CM_port_buf.scl
		inc	I2CM_sub_state
		retp

:state2 ;	sb	scl_pin1
	;	retp

		stc
		jb	I2CM_ID,:m2
:m1		sb	sda_pin1
		clc
		jmp	:m_done
:m2		sb	sda_pin2
		clc
:m_done		rl	I2CM_byte
		inc	I2CM_sub_state
		retp

:state3		clrb	I2CM_port_buf.scl
		clr	I2CM_sub_state
		dec	I2CM_bit_count
		sz	
		retp
		inc	I2CM_state
		retp

	;*********************************************************************************
	; Function: I2CM_store_byte
	; 	This function stores the byte just read into the buffer.
	;*********************************************************************************
I2CM_store_byte
		mov	w,I2CM_byte
		mov	isr_temp,w
		inc	I2CM_index
		mov	w,I2CM_index
		add	fsr,w
		mov	w,isr_temp
		mov	indf,w
		inc	I2CM_state
		setb	I2CM_rx_flag
		retp

	;*********************************************************************************
	; Function: I2CM_send_ack
	; 	This function sends an ack if there is data left to write, and a NACK if 
	;	there is no data left to write.
	;*********************************************************************************
I2CM_send_ack
		mov	w,I2CM_sub_state
		add	pc,w
		jmp	:state1
		jmp	:state2
		jmp	:state3
		jmp	:state4	

:state1		setb	I2CM_in_control
		clrb	I2CM_port_buf.sda
		mov	w,I2CM_index
		xor	w,I2CM_num_bytes
		snz
		setb	I2CM_port_buf.sda
		inc	I2CM_sub_state
		retp

:state2		setb	I2CM_port_buf.scl	;Pulse the clock
		inc	I2CM_sub_state
		retp

:state3		
;		jb	I2CM_ID,:m2
;:m1		sb	scl_pin1
;		retp
;		jmp	:m_done
;:m2		sb	scl_pin2
;		retp
:m_done		clrb	I2CM_port_buf.scl
		inc	I2CM_sub_state
		retp

:state4		setb	I2CM_port_buf.sda
		clr	I2CM_sub_state
		mov	w,I2CM_index
		xor	w,I2CM_num_bytes
		snz
		jmp	:done
		mov	w,#(I2CM_read_rpt_loc - I2CM_idle_loc) ;back to read 
		mov	I2CM_state,w
		retp

:done		inc	I2CM_state		;If this was the last byte to be read, send a stop
		retp

org	I2CM_SUBS_ORG2
;*****************************************************************************************
; I2C Master Subroutines:  Mainline Access routines (call from the mainline)
;*****************************************************************************************

	;*********************************************************************************
	; Function: I2CM_send_byte
	; 	This routine sets up the I2CM state machine to write the byte of data in
	; 	the I2CM_data_0 register.  Before entering this routine, make sure that
	;	the I2CM state machine is in its idle state (use the I2CM_wait_not_busy 
	;	subroutine) and that the I2CM_address is loaded with the address of the
	;	slave that this byte is going to, and that I2CM_data_0 is loaded with the
	;	data to send.
	;*********************************************************************************
I2CM_send_byte_
		mov	w,#1			;There is one byte of data in the buffer
		mov	I2CM_num_bytes,w
	;*********************************************************************************
	; Function: I2CM_send_bytes
	; 	This routine sets up the I2CM state machine to write the bytes of data in
	; 	the I2CM_data_0 - I2CM_data_n registers.  Before entering this routine, 
	;	make sure that the I2CM state machine is in its idle state (use the 
	;	I2CM_wait_not_busy subroutine) and that I2CM_address is loaded with the 
	;	address of the slave that this byte is going to, that I2CM_data_0 - I2CM_data_n
	;	are loaded loaded with the data to send, and that the I2CM_num_bytes register
	;	is loaded with the number of data bytes to send.
	;*********************************************************************************
I2CM_send_bytes_
		setb	I2CM_doing_write
		mov	w,#%11111110
		and	I2CM_address,w
		clrb	I2CM_nack
		mov	w,#(I2CM_write_loc-I2CM_idle_loc)
		mov	I2CM_state,w
		retp
	;*********************************************************************************
	; Function: I2CM_wait_not_busy
	; 	This routine polls the I2CM_state register until it is not busy.  It returns 
	;	when the I2C master state machine becomes idle.  It returns a (0) in the w
	;	register if the transfer appeared successful (ie.  The slave returned an ACK
	;	when addressed), and a (1) in the w register if the slave did not return
	;	an ack when addressed/written.
	;*********************************************************************************
I2CM_wait_not_busy_
:wait_loop	test	I2CM_state
		sz			; wait until I2C is idle
		jmp	:wait_loop
		mov	w,#1		; return (1) if we are idle because a NACK was
		snb	I2CM_nack	; received.  Return (0) if we are idle because
		retp			; this was successful
		clr	w		
		retp

	;*********************************************************************************
	; Function: I2CM_get_byte
	;	This routine gets one byte of data from the slave at address I2CM_address.
	;	before calling this routine, ensure that the I2C Master State Machine is
	;	in its idle state (use the I2CM_wait_not_busy subroutine) and that the
	;	I2CM_address register is loaded with a valid address.  The routine returns
	;	with the byte received in the w register and in the I2CM_data_0 register.
	;*********************************************************************************
I2CM_get_byte_
		clrb	I2CM_doing_write
:nack		mov	w,#%00000001
		or	I2CM_address,w
		mov	w,#1
		mov	I2CM_num_bytes,w
		clrb	I2CM_nack
		mov	w,#(I2CM_read_loc-I2CM_idle_loc)
		mov	I2CM_state,w
		call	I2CM_wait_not_busy
		and	w,$ff
		sz
		jmp	:nack
		clrb	I2CM_rx_flag
		mov	w,I2CM_data_0
		retp

	;*********************************************************************************
	; Function: I2CM_get_bytes
	;	This routine gets I2CM_num_bytes of data from the slave at address
	;	I2CM_address.  Before calling this routine, ensure that the I2CM state
	;	machine is idle by using the I2CM_wait_not_busy subroutine, that I2CM_address
	;	register contains the address of the slave to be read from, and that the
	;	I2CM_num_bytes register is loaded with the number of bytes of data to receive.
	;	The received bytes will be contained in registers I2CM_data_0 to I2CM_data_n.
	;*********************************************************************************
I2CM_get_bytes_
		clrb	I2CM_doing_write
		mov	w,#%00000001
		or	I2CM_address,w
		clrb	I2CM_nack
		clrb	I2CM_rx_flag
		mov	w,#(I2CM_read_loc-I2CM_idle_loc)
		mov	I2CM_state,w
		retp

	;*********************************************************************************
	; Function: I2CM_init
	;	This subroutine should be called on startup.  It initializes the registers
	;	which are critical to the operation of the I2CM state machine.
	;*********************************************************************************
I2CM_init_
		mov     w,#%10111111		;Set RB in/out directions 
		mov     I2CM_port_buf,w
		mov	w,#$ff
		mov	I2CM_num_bytes,w
		mov	w,#$ff
		mov	I2CM_index,w
		retp

	org	SEND_STRINGS_ORG
;*****************************************************************************************
; String data (for RS-232 output) and tables needs to be in page $300
;*****************************************************************************************
_hello          dw      13,10,10,'SX I2C/UART Demo1',0
_sxm		dw      13,10,'SX Master ',0 
_master_wait	dw	13,10,'Paused...',0
_finding_devices dw	13,10,'Devices Found at : ',0
_instructions	dw	13,10,'R - Read Saved Sentence',13,10,'S - Save a Sentence',13,10,'>',0,0
_enter_adr	dw	13,10,'Enter Address of Device: ',0
_ee_read	dw	13,10,'Saved Sentence : ',0
_ee_write_pmpt	dw	13,10,'Enter a Sentence to Save: ',0
_too_long	dw	13,10,'String Too Long!',0
_ACK		dw	13,10,'ACK.',0
_NACK		dw	13,10,'NACK.',0

org	UART_SUBS_ORG
;*****************************************************************************************
; Jump table for second half of page
;*****************************************************************************************
EE_write_char	jmp	EE_write_char_		;3
EE_read_char	jmp	EE_read_char_		;3
sel_UART_BANK	jmp	sel_UART_BANK_		;3
sel_MASTER_BANK jmp	sel_MASTER_BANK_	;3

;*****************************************************************************************
; UART Subroutines
;*****************************************************************************************

	;*********************************************************************************
	; Function: get_byte
	; Get byte via serial port and echo it back to the serial port
	; INPUTS:
	;	-NONE
	; OUTPUTS:
	;	-received byte in rx_byte
	;*********************************************************************************
get_byte	sb	uart_rx_flag		;wait till byte is received
		jmp	get_byte
		clrb    uart_rx_flag		;reset the receive flag
		mov     w,rx_byte		;store byte (copy using W)
		mov     byte,w			; & fall through to echo char back
		retp

	;*********************************************************************************
	; Function: send_byte
	; Send byte via serial port
	; INPUTS:
	;	w 	-	The byte to be sent via RS-232
	; OUTPUTS:
	; 	outputs the byte via RS-232
	;*********************************************************************************
send_byte 
:wait        	test    tx_count                ;wait for not busy
		sz
		jmp	:wait                   ;

		not     w                       ;ready bits (inverse logic)
		mov     tx_high,w               ; store data byte
		setb    tx_low.7                ; set up start bit
		mov     w,#10			;1 start + 8 data + 1 stop bit
		mov     tx_count,w
		retp                            ;leave and fix page bits

	;*********************************************************************************
	; Function: send_string
	; Send string pointed to by address in W register
	; INPUTS:
	;	w	-	The address of a null-terminated string in program
	;			memory
	; OUTPUTS:
	; 	outputs the string via RS-232
	;*********************************************************************************
send_string	
 		mov     string,w                ;store string address
:loop        	mov     w,string                ;read next string character
		mov     m,#3                    ;with indirect addressing (set to $300)
		iread                           ;using the mode register
		test    w                       ;are we at the last char?
		snz                             ;if not=0, skip ahead
		jmp	:exit			;yes, leave & fix page bits
		mov     m,#$0f                  ;reset the mode register to RX directions
		call    send_byte               ;not 0, so send character
		inc     string                  ;point to next character
		jmp     :loop                   ;loop until done
:exit		mov     m,#$0f                   ;reset the mode register
		retp
	;*********************************************************************************
	; Function: uppercase
	; Convert byte to uppercase.
	; INPUTS:
	;	byte - the byte to be converted
	; OUTPUTS:
	; 	byte - converted byte
	;*********************************************************************************
uppercase    	mov	w,#'a'            	;if byte is lowercase, then skip ahead
	    	mov	w,byte-w
	    	sc
		retp
		mov	w,#'a'-'A'		;change byte to uppercase
		sub	byte,w
		retp				;leave and fix page bits

	;*********************************************************************************
	; Function: send_hex
	; Output a hex number
	; INPUTS:
	;	w	-	The byte to be output
	; OUTPUTS:
	; 	outputs the hex byte via RS-232
	;*********************************************************************************
send_hex	mov	main_temp,w
		swap	wreg
		and	w,#$0f
		call	hex_table
		call	send_byte
		mov	w,main_temp
		and	w,#$0f
		call	hex_table
		call	send_byte
		retp

	;*********************************************************************************
	; Function: get_hex
	;	This routine returns with an 8-bit value in the W and in the hex 
	;	register.  It accepts a hex number from the terminal screen and
	;	returns. Remember to write a prompt to the screen before calling get_hex
	; OUTPUTS:
	;	-received byte in rx_byte
	;*********************************************************************************
get_hex
		call	:get_valid_hex
		mov	w,byte			; send the received (good) byte
		call	send_byte
		swap	main_temp		; put the nybble in the upper nybble of
		mov	w,main_temp
		mov	hex,w			; hex register

		call	:get_valid_hex
		mov	w,byte			; send the second received byte
		call	send_byte
		mov	w,main_temp			
		and	w,#$0f
		or	w,hex
		mov	hex,w
		retp

:get_valid_hex
:gh1		clr	main_temp
		jnb	uart_rx_flag,$		; get a byte from the terminal
		clrb	uart_rx_flag
		mov	byte,rx_byte
		call	uppercase		; uppercase it.

:loop		mov	w,main_temp		; get the value at temp (index)
		call	hex_table
		xor	w,byte
		snz				; compare it to the received byte
		ret
		inc	main_temp		; if they are equal, we have the
		jb	main_temp.4,:gh1	; upper nybble.  Continue if not.
		jmp	:loop
		ret

hex_table	add	pc,w
		retw	'0'
		retw	'1'
		retw	'2'
		retw	'3'
		retw	'4'
		retw	'5'
		retw	'6'
		retw	'7'
		retw	'8'
		retw	'9'
		retw	'A'
		retw	'B'
		retw	'C'
		retw	'D'
		retw	'E'
		retw	'F'

org	OTHER_SUBS_ORG
;*****************************************************************************************
; EERPOM Read/Write Subroutines
;*****************************************************************************************

	;*********************************************************************************
	; Function: EE_write_byte
	;	This routine writes EE_byte to eeprom.
	;	Ensure I2CM_address is loaded with the correct address	
	; INPUTS:
	;	-EE_bank_adr(the word address), EE_char(the char to write),
	;	-I2CM_address(command byte A0->AE)
	;*********************************************************************************

EE_write_char_
		BANK	EEPROM			
		mov	w,EE_bank_adr			; load first byte to send with E2 bank address
		call	@sel_MASTER_BANK		; switch to active Master's RAM bank.
		mov	I2CM_data_0,w
		BANK	EEPROM			
		mov	w,EE_char			; load second byte with actual data
		call	@sel_MASTER_BANK		; switch to active Master's RAM bank.
		mov	I2CM_data_1,w

		mov	I2CM_num_bytes,#2		; two bytes to send
		call	@I2CM_wait_not_busy
		call	@I2CM_send_bytes		; send the bytes
		retp

	;*********************************************************************************
	; Function: EE_read_byte
	;	This routine returns a byte from eeprom 
	;	Ensure I2CM_address is loaded with the correct address
	; INPUTS:
	;	-EE_bank_adr, I2CM_address(command byte)
	; OUTPUTS:
	;	-received byte in w
	;*********************************************************************************
	
EE_read_char_
		BANK	EEPROM			
		mov	w,EE_bank_adr			; load first byte to send with E2 bank address
		call	@sel_MASTER_BANK		; switch to active Master's RAM bank.
		mov	I2CM_data_0,w			; load send register with eeprom bank address		
		call	@I2CM_wait_not_busy
		call	@I2CM_send_byte			; send random access code and bank addess to EEPROM
		call	@I2CM_wait_not_busy
		snb	I2CS_rx_flag			; Check if just sent data to this slave
		call	@do_slave			; process data sent to this slave
		call	@sel_MASTER_BANK		; switch to active Master's RAM bank.
		call	@I2CM_get_byte			; returns char in w
		sb	I2CM_NACK
		retp
:NACK_read	mov	w,#_NACK			; send NACK
		call	@send_string
		mov	w,#0				; abort read
		retp

	;*********************************************************************************
	; function: sel_UART_BANK
	;	This function works exactly the same as the bank instruction but allows 
	; 	a register to point to the bank to be swithed to.
	;	in this case the register is UART_BANK
	;	Ensure that UART_BANK is loaded with either SERIAL1 or SERIAL2 depending which
	;	is the currently active master
	;	This routine uses the temp variable to save w so that w is not affected 
	;	during a bank switch
	; 	This routine is the same as saying:
	;		bank 	#UART_BANK
	;*********************************************************************************

sel_UART_BANK_
		mov	main_temp,w			; save w
		and	fsr,#$1f			; blank first 3 bits of fsr
		mov	w,UART_BANK			; get BankPtr
		and	w,#$e0				; blank all but first 3 bits of BankPtr
		or	fsr,w				; load fsr with OR of both
		mov	w,main_temp			; get w back 
		retp

	;*********************************************************************************
	; function: sel_MASTER_BANK
	;	This function works exactly the same as the bank instruction but allows 
	; 	a register to point to the bank to be swithed to.
	;	in this case the register is MASTER_BANK.
	;	Ensure that MASTER_BANK is loaded with either I2CM1 or I2CM2 depending which
	;	is the currently active master
	;	This routine uses the temp variable to save w so that w is not affected 
	;	during a bank switch
	; 	This routine is the same as saying:
	;		bank 	#MASTER_BANK
	;*********************************************************************************

sel_MASTER_BANK_
		mov	main_temp,w			; save w
		and	fsr,#$1f			; blank first 3 bits of fsr
		mov	w,MASTER_BANK			; get BankPtr
		and	w,#$e0				; blank all but first 3 bits of BankPtr
		or	fsr,w				; load fsr with OR of both
		mov	w,main_temp			; get w back
		retp

;*****************************************************************************************
END		;End of program code
;*****************************************************************************************