;*****************************************************************************************
; 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 Single I2C Multi-Master demo.
;                   
; Filename:	I2C_UART demo2.src
;
; Author:	Bruce Wilson
;		Applications Engineer
;		Scenix Semiconductor Inc.
;
; Revision:	2.0
;
; Part:		SX28AC datecode 9929AA
; Freq:		50Mhz
; Compiled using Parallax SXKey28L
;
; Date Written: Aug 23,1999
;
; Last Revised: Sept 20,1999
;
; Introduction:
;	This program is one of the two peices of demo code written for the
;	Scenix I2C/UART Demo Board. This code is for SX28 U2 which is the bottom 
;	SX28.
;
;	Terminal 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 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 read 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 3
;	and 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.
;	2.0  First release of I2C/Demo source code.
;*****************************************************************************************
	;*********************************************************************************
     	; DEFINES
	;*********************************************************************************

	SX28L_assembler					; using the SXKey28L compiler
	multi_master					; enable arbitration algorithm


	;*********************************************************************************
	; Assembler directives:
	;	SX28L, high speed external osc, turbo mode, 8-level stack,
	;	and extended option reg.
	;
	;	4 pages of program memory and 8 banks of RAM enabled by default.
	;           

	IFDEF SX28L_assembler
		device	SX28L,oscxt4			; 28-pin device, 4 pages, 8 banks of RAM
		device	turbo,stackx_optionx		; High speed oscillator, turbo mode,
							; option register extend, 8-level stack
	ELSE
		device	pins28,oschs2,turbo,		; using old revision device
			stackx,optionx			; high speed oscillator, turbo mode,
							; option register extend, 8-level stack
	ENDIF
	;*********************************************************************************

		ID	'i2c_uart'		; code identificatino

		reset	reset_entry		; set reset vector

;*****************************************************************************************
; Global Register definitions
;*****************************************************************************************
		org     $08			;start of program registers

temp		ds	1		; Temporary variable
isr_temp	ds	1		; Temporary variable used by the ISR.
flags		ds	1		; Flags to indicate various things..
	timer_flag	equ	flags.0	; Indicates a 16-bit timer rollover
	rx_flag		equ	flags.1	; Indicates the reception of a bit from the UART
	eewrite_flag	equ	flags.2 ; true if user wants to write data to the EEPROM

I2C_flags	ds	1		; 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.


NACK_counter	ds	1
addr_counter	ds	1

int_period	ds	1	; RTCC interrupt rate

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

	;*********************************************************************************
	; Bank 0
	;*********************************************************************************
		org     $10

I2CM		=	$	; 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
	I2CM_doing_write equ	I2CM_flags.4
	I2CM_in_control	equ	I2CM_flags.5
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_buffer	=	$	; The buffer uses the last 8 registers of this bank (pre-increments, so put I2CM buffer here.)
  I2CM_address	equ	$18	; The address to read/write to.
  I2CM_data_0 	equ	$19	; Data buffer 
  I2CM_data_1	equ	$1a	; Data buffer
  I2CM_data_2	equ	$1b	; Data buffer
  I2CM_data_3	equ	$1c	; Data buffer
  I2CM_data_4	equ	$1d	; Data buffer
  I2CM_data_5	equ	$1e	; Data buffer
  I2CM_data_6	equ	$1f	; Data buffer

	;*********************************************************************************
	; Bank 1
	;*********************************************************************************
		org     $30 

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 2
	;*********************************************************************************
		org     $50

SERIAL		=       $	;UART bank
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	; low byte of 16-bit timer
timer_high	ds	1	; hight byte of 16-bit timer

	;*********************************************************************************
	; Bank 3
	;*********************************************************************************
		org	$70
EEPROM		=	$	; EERPOM bank
EE_bank_adr	ds	1	; current address in bank
EE_char		ds	1	; read byte and byte to write location


	;*********************************************************************************
	; Bank 4
	;*********************************************************************************
		org	$90
slave_string1	=	$	; where a string can be stored

	;*********************************************************************************
	; Bank 5
	;*********************************************************************************
		org	$b0

	;*********************************************************************************
	; Bank 6
	;*********************************************************************************
		org	$d0

	;*********************************************************************************
	; Bank 7
	;*********************************************************************************
		org	$f0

	;*********************************************************************************
	; Pin Definitions
	;*********************************************************************************

mm_sel_pin	equ	rc.7
master_sel_pin	equ	rc.6
slave_sel_pin	equ	rc.5

scl		equ	0
sda		equ	1
scl_pin         equ     rb.scl	;I2C clock
sda_pin         equ     rb.sda	;I2C data I/O


;*****************************************************************************************
; 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 timer...
	;*********************************************************************************

		bank	TIMERS			;1 ; walk through 2 I2C functions,
		inc	timer_low		;1 
		sz				;1
		inc	timer_high		;1
						;=4

master_i2c	snb	master_sel_pin		;2/4 check if in master mode
		jmp	slave_i2c		
						;=6/8
	;*********************************************************************************
	; I2C Master Stuff...	Runs every alternate ISR
	;*********************************************************************************
		jb	timer_low.0,slave_i2c	;2/4 only run MASTER every alternate ISR
		bank	I2CM			;1
		jb	I2CM_lost_arb,slave_i2c	; 2/4  if lost arbitration stop sending/receiving

		mov	w,#I2CM_buffer		;1 ; Switch to I2CM bank and load FSR for
		mov	fsr,w			;1 ; buffer look-up, if needed.
		
		sb	I2CM_port_buf.scl	;1 ; If the master is trying to drive the scl
		page	I2CM_ISR		;1 ; pin high, but the slave won't let it, pause.
		call	I2CM_ISR		;3 + 12/24
		snb	scl_pin			;1 ; (don't call I2C master isr)
		page	I2CM_ISR		;1 
		call	I2CM_ISR		;3 + 12/24

slave_i2c	snb	slave_sel_pin		;4/2
		jmp	I2C_done		

	;*********************************************************************************
	; I2C Slave Stuff...	Runs every ISR
	;*********************************************************************************
		bank	I2CS			;1
		clc				;1 ;Save the current state and past state of the I2C bus
		snb	sda_pin			;1
		stc				;1
		rl	I2CS_past_pres		;1
		clc				;1
		snb	scl_pin			;1
		stc				;1
		rl	I2CS_past_pres		;1
		page	I2CS_get_start_stop	;1 ;Check the new states to see if we've received a start or a stop
		call	I2CS_get_start_stop	;3 + 9/12
		page	I2CS_isr		;1 ;Call I2C slave ISR
		call	I2CS_isr		;3 + 11/30(I2C_isr length) = 37/59

I2C_done
		mov	w,m			;1 ;Save the m register.
		mov	isr_temp,w		;1

		mov	m,#$0f			;1
		mov	w,rb			;1
		and	w,#%11111100		;1 ;Clear the data latches for SCL and SDA
		mov	rb,w			;1

	;*********************************************************************************
	; Update I2C port with buffered port data and
	; Makes sure I2C masters don't interfere with slaves, vice-versa
	;*********************************************************************************
; mm, master or slave
		snb	mm_sel_pin		;2/4 Execute anti-interference code if in mm mode
		jmp	master_only

		bank	I2CM			;1
		mov	w,I2CM_port_buf		;1 ;Update the I2C port with the buffered port data
		bank	I2CS			;1
		and	w,I2CS_port_buf		;1
		mov	!rb,w			;1
		jmp	select_done

master_only	snb	master_sel_pin		;2/4
		jmp	slave_only

		bank	I2CM			;1
		mov	w,I2CM_port_buf		;1 ;Update the I2C port with the buffered port data
		mov	!rb,w			;1
		jmp	select_done

slave_only	bank	I2CS			;1
		mov	w,I2CS_port_buf		;1 ;Update the I2C port with the buffered port data
		mov	!rb,w			;1
select_done
		mov	w,isr_temp		;1 ;Restore the m register.
		mov	m,w			;1 = 13

uart
	;*********************************************************************************
	; Pin Definitions
	;*********************************************************************************
rts_pin		equ	ra.0	;UART 1 RTS input
cts_pin		equ	ra.1	;UART 1 CTS output
rx_pin          equ     ra.2	;UART receive input
tx_pin          equ     ra.3	;UART transmit output

	;*********************************************************************************
	; 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_8	=	16			;for 2400 baud rate generation
start_del_24_8	=	25

; *** 9600 baud
baud_rate_96_8 =	4			;for 9600 baud rate generation
start_delay_96_8 = 	7

; *** 19200 baud
baud_rate_192_8 =	2			;for 19200 baud rate generation
start_delay_192_8 = 	3

; *** 2400 baud (for slower baud rates, increase the RTCC prescaler)
baud_rate_24_50	=	96			;for 2400 baud rate generation
start_del_24_50	=	145

; *** 9600 baud
baud_rate_96_50 =	24			;for 9600 baud rate generation
start_delay_96_50 = 	37

; *** 19200 baud
baud_rate_192_50 =	12			;for 19200 baud rate generation
start_delay_192_50 = 	19

; *** 38400 baud
baud_rate_384_50 =	6			;for 38400 baud rate generation
start_delay_384_50 = 	10

; *** 57600 baud
baud_rate_576_50 =	4			;for 57600 baud rate generation
start_delay_576_50 =	6

; *** 115200 baud
baud_rate_1152_50 =	2			;for 115200 baud rate generation
start_delay_1152_50 =	4

	;*********************************************************************************
	; Virtual Peripheral: Universal Asynchronous Receiver Transmitter (UART) 
	; These routines send and receive RS232C 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 (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
	;
	;*********************************************************************************
		bank	SERIAL			;1 ;switch to serial register bank
:transmit	decsz	tx_divide		;1 ;only execute the transmit routine
		jmp	:receive		;1 
		mov	w,#baud_rate_192_8	;1 ;load UART 1 baud rate (8MHz)
		sb	rc.4			;1 rc.4 = 1 -> 8MHz, rc.4 = 0 -> 50MHz
		mov	w,#baud_rate_192_50	;1 ;load UART 1 baud rate (50MHz)
		mov	tx_divide,w		;1 
		test    tx_count		;1 ;are we sending?
		snz				;1 
		jmp	:receive		;3
		clc                             ;1 ;yes, ready stop bit
		rr      tx_high			;1 ; and shift to next bit
		rr      tx_low			;1 
		dec     tx_count		;1 ;decrement bit counter
		snb	tx_low.6		;1 ;output next bit
		clrb	tx_pin			;1 
		sb	tx_low.6		;1 
		setb	tx_pin			;1 
:receive        sb	rx_pin			;1 ;get current rx bit
	        clc				;1 
	        snb	rx_pin			;1 
	        stc				;1 
		test    rx_count                ;1 ;currently receiving byte?
		sz				;1 
		jmp	:rxbit                  ;3 ;if so, jump ahead
		mov     w,#9                    ;1 ;in case start, ready 9 bits
		sc				;1 ;skip ahead if not start bit
		mov     rx_count,w              ;1 ;it is, so renew bit count
		mov     w,#start_delay_192_8	;1 ;ready 1.5 bit periods (8MHz)
		sb	rc.4			;1 rc.4 = 1 -> 8MHz, rc.4 = 0 -> 50MHz
		mov     w,#start_delay_192_50	;1 ;ready 1.5 bit periods (50MHz)
		mov     rx_divide,w		;1 
:rxbit          decsz	rx_divide		;1 ;middle of next bit?
          	jmp	:rxdone			;3 = 25/34
		mov	w,#baud_rate_192_8	;1 ;yes, ready 1 bit period (8MHz)
		sb	rc.4			;1 rc.4 = 1 -> 8MHz, rc.4 = 0 -> 50MHz
		mov	w,#baud_rate_192_50	;1 ;yes, ready 1 bit period (50MHz)
		mov	rx_divide,w		;1 

		dec     rx_count                ;1 ;last bit?
		sz                              ;1 ;if not
		rr      rx_byte                 ;1 ;  then save bit
		snz                             ;1 ;if so,
		setb    rx_flag                 ;1 = 29/40; then set flag
:rxdone						   ;else, exit

IFDEF		multi_master

	;*********************************************************************************
	; Function: I2C_arbitration
	; This code checks to see that the data written by the 2 I2C master is the same
	; as the data present on the bus.  If not, this master has lost arbitration.
	;*********************************************************************************
		jb	mm_sel_pin,arb_done	;2/4 Check if arbitration required or not 
		bank	I2CM			;1	
I2C_arbitration
		jb	I2CM_lost_arb,:arb_lost	 ;2/4 go straight to lost arb. routine, no need to check if lost arb.
		jnb	I2CM_in_control,arb_done ;2/4 Only do arbitration when master is in control
		
; --- Check if data on bus matches data written by master ---
		jb	sda_pin,arb_done	; 2/4 This master can't loose 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 (stops I2CM_isr)
		setb	I2CM_port_buf.sda	; 1 set signal lines to idle states
		setb	I2CM_port_buf.scl	; 1
:arb_lost	bank	TIMERS			; 1
		test	timer_high 		; 1 chack if timer has timerd out (random seed)
		jnz	arb_done		; 2 if it has start current operation before losing arb. again
:arb_random_timedout
		bank	I2CM			; 1
		clr	I2CM_sub_state		; 1	
		clrb	I2CM_lost_arb	 	; 1	
		jb	I2CM_doing_write,:arb_reset_write ;2/4
:arb_reset_read
		mov	w,#(I2CM_read_loc-I2CM_idle_loc) ;1	
		mov	I2CM_state,w		; 1
		jmp 	arb_done		; 3
:arb_reset_write
		mov	w,#(I2CM_write_loc-I2CM_idle_loc) ;1
		mov	I2CM_state,w		; 1
arb_done			
	;*********************************************************************************
	; IMPORTANT NOTE ABOUT ARBITRATION ALGORITHM
	;   This arbitration algorithm has been written for and only tested with the I2C/UART
	; demo board. It was written with no mainline code to check the status of the the 
	; I2CM_lost_arb flag. It is more of a black box type approach that works by attempting 
	; to restart the I2C operation that encountered a loss of arbitration. This approach 
	; has one main draw back, if the user is execuiting several I2C operations in a synchronous
	; fashion from the mainline these operations may be interrupted by another master on
	; the bus if it wins arbitration. The way around this problem is to do checking of the
	; I2CM_lost_arb flag from within the mainline, or possibly within the I2CM_wait_not_busy
	; function and act as best suits your application when it is detected that this master has
	; lost arbitration. Updates and different approaches of this algorithm may be found at
	; the scenix web site under the I2C master virtual peripheral source code. (I2CM.src)
	; The advantage of the approach used here is that no mainline code or checking is
	; required at any stage within the program. This technique could be extended depending
	; on the applicaton it is intended.
	;*********************************************************************************

ENDIF

	;*********************************************************************************
	; Set Interrupt Rate
	;*********************************************************************************

		mov	w,int_period		;1 ; refresh RTCC on return
		retiw				;3 ; return from the interrupt
						   ; = 1/(int_period*RTCC prescaler*1/50MHz)
						   ; = 1/(217*1*20ns) = 4.34us
						;Total # cycles = 88(best case)/141(worst case)
;*****************************************************************************************
; 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		

;*****************************************************************************************
; Port Assignment
;*****************************************************************************************
RA_init		equ	%1000
RA_io		equ	%0111
RB_init		equ	%11111100
RB_io		equ	%10111111
RC_init		equ	%00000000
RC_io		equ	%11111111

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

		mov	m,#$0e			; Initialize pullups on port B
		mov	w,#%11111100		; for I2C
		mov	!rb,w
		mov	m,#$0f
		mov     w,#RA_init            	; Initialize data latches for
		mov     ra,w
		mov	w,#RA_io		; port A.
		mov	!ra,w
		mov     w,#RB_init          	; Initialize data latches for
		mov     rb,w
		mov	w,#RB_io		; port B.
		mov	!rb,w
		mov     w,#RC_init          	; Initialize data latches for
		mov     rc,w
		mov	w,#RC_io		; port C.
		mov	!rc,w

		bank	TIMERS
		mov	w,timer_low		; save a random seed

	;*********************************************************************************
	; Clear all Data RAM locations
	;*********************************************************************************
		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

	;*********************************************************************************
	; 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,#'2'
		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	I2CM
		page	I2CM_init
		call	I2CM_init
		page	I2CS_init
		call	I2CS_init

	;*********************************************************************************
	; Setup and enable RTCC interrupt, WREG register
	;*********************************************************************************
		mov	w,#-208			; Set RTCC to interrupt every 208 cycles
		sb	rc.4			; rc.4 = 1 -> 8MHz, rc.4 = 0 -> 50MHz
		mov	w,#-217			; Set RTCC to interrupt every 217 cycles
		mov	int_period,w

		mov     !option,#%00011111      ;enable rtcc interrupt and wreg

;*****************************************************************************************
; MAIN PROGRAM CODE 
;*****************************************************************************************

main
	;*********************************************************************************
	; Main - Send Prompts
	;*********************************************************************************
		mov	w,#_hello			; send title string
		call	@send_string
		mov	w,#_mm				; send multi-master mode string
		jnb	mm_sel_pin,main_master
		mov	w,#_master			; send master mode string
		jnb	master_sel_pin,main_master
		mov	w,#_slave			; send slave mode string
		call	@send_string
		jmp	main_slave
	;*********************************************************************************
	; Master main program code...
	; Find I2C slaves on bus, and display.  Try each address 4 times and look for ACK.
	;*********************************************************************************
main_master
		call	@send_string
		mov	w,#_finding_devices		; send "finding devices"
		call	@send_string
_start

		bank	I2CM				; initialize the address register to
		clr	addr_counter			; start looking for slaves from address 0
_loopstart	clr	NACK_counter
		bank	I2CM
		clr	I2CM_num_bytes			; clear the number of data bytes (we are 
							; sending only the address, no data, and looking
							; for an ACK response.)
_find_loop
:nack		bank	I2CM				; switch to I2CM bank.  We are only using this
							;  master to find the slaves.
		mov	I2CM_address,addr_counter	; 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.
		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	NACK_counter			;   
		jmp	:nack				;   
:next_device
;		mov	w,#'.'				; Display dot when no device found
;		call	@send_byte

		mov	NACK_counter,#2			; Try twice to get an ACK if a nack was received.
		setb	addr_counter.0			; increment by two's.
		inc	addr_counter
		jz	device_search_done		; device search is done when the address counter rolls over.
		jmp	_find_loop			;  else, continue.
:device_found
		mov	NACK_counter,#2			; try each address twice when a NACK is received.
		mov	w,addr_counter			
		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
		setb	addr_counter.0			; increment by two's
		inc	addr_counter
		jz	device_search_done		; increment the address counter.  If it rolls over, we are 
							;  finished.
		jmp	_loopstart			; continue...
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 accordingly
	;*********************************************************************************
main_loop
		mov	w,#_instructions		; send the instructions.
		call	@send_string
:stage1_loop	
		snb	I2CS_rx_flag			; Check if slave has received data
		call	do_slave			; process data received by the slave
		sb	rx_flag				; wait till a byte has been recvd by UART before calling get_byte
		jmp	:stage1_loop
		clrb	eewrite_flag			; set EEPROM write flag to default
		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

	;*********************************************************************************
	; Get the address of the device from the user
	;*********************************************************************************
:stage2		mov	w,#_enter_adr			; Display prompt for user
		call	@send_string
:stage2_loop
		snb	I2CS_rx_flag			; Check if slave has received data
		call	do_slave			; process data received by the slave
		sb	rx_flag				; wait until a byte is waiting before calling get_hex
		jmp	:stage2_loop

		call	@get_hex			; get the address of the slave in hex from user
	;*********************************************************************************
	; Check for device present at that address
	;*********************************************************************************
:stage3		bank	I2CM				; switch to I2C Master #1's 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		mov	w,#_NACK			; else send NACK and return to command mode.
		call	@send_string
		jmp	main				; no ack. was received. restart

	;*********************************************************************************
	; Read or write data to the SLAVE depending on user selection
	;*********************************************************************************
:device_present
		mov	w,#_ACK				; send ACK message.
		call	@send_string			;
:stage4		bank	EEPROM
		clr	EE_bank_adr
		jb	eewrite_flag,eewrite		; either read or write to EEPROM
	;*********************************************************************************
	; Read data from the slave
	;*********************************************************************************
		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	@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		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
		sb	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	@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
;		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 MAIN PROGRAM CODE
	; Wait for a command from another maser on the bus...
	;*********************************************************************************
main_slave
		snb	I2CS_rx_flag			; Check if slave has received data
		call	do_slave			; process data received by the slave
		jmp 	main_slave

	;*********************************************************************************
	; SLAVE SUBROUTINE
	; Simulates an EEPROM slave device.
	;*********************************************************************************
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				

org	$200
;*****************************************************************************************
; 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 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	=	$40			; Address of the Slave (read)

;*****************************************************************************************
; 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

org	$300
;*****************************************************************************************
; String data (for RS-232 output) and tables needs to be in page $300
;*****************************************************************************************
_hello          dw      13,10,10,'SX I2C/UART Demo2',0
_mm		dw      13,10,'Multi-Master Mode',0 
_master		dw      13,10,'Master Mode',0 
_slave		dw      13,10,'Slave Mode',13,10,'Waiting For Command...',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
_ACK		dw	13,10,'ACK.',0
_NACK		dw	13,10,'NACK.',0

org	$400
;*****************************************************************************************
; I2C Master Interrupt Service Routines
;*****************************************************************************************

	;*********************************************************************************
	; 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
		clrb	I2CM_in_control		;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_in_control
		setb	I2CM_port_buf.sda	;1
		inc	I2CM_sub_state		;1
		retp				;3 = 6 + 7 = 13

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

: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		sb	sda_pin			;1
		inc	I2CM_sub_state		;1
		sb	sda_pin			;1
		retp				;3 = 13 + 7 = 20 ;if no ack,
		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
		jmp	:state4			;3
		jmp	:state5			;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		inc	I2CM_sub_state		;1 ;Delay so that another master can transmit, if needed.
		retp				;3 = 11 + 7 = 18

:state4		inc	I2CM_sub_state
		retp

:state5		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
		clrb	I2CM_in_control		
		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
		clrb	I2CM_in_control		
		;*************************************************************************
		; 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		setb	I2CM_port_buf.scl
		inc	I2CM_sub_state
		retp

:state2		stc
		sb	sda_pin
		clc
		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		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		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	$500
;*****************************************************************************************
; 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	$600
;*****************************************************************************************
; Jump table for second half of page
;*****************************************************************************************
EE_write_char	jmp	EE_write_char_		;3
EE_read_char	jmp	EE_read_char_		;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	rx_flag			;wait till byte is received
		jmp	get_byte
		clrb    rx_flag			;reset the receive flag
		bank	SERIAL
		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    	bank    SERIAL

: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	bank	SERIAL
 		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	temp,w
		swap	wreg
		and	w,#$0f
		call	hex_table
		call	send_byte
		mov	w,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		bank	SERIAL
;		mov	w,#_enter_hex
;		page	send_string
;		call	send_string
		call	:get_valid_hex
		mov	w,byte			; send the received (good) byte
		call	send_byte
		swap	temp			; put the nybble in the upper nybble of
		mov	w,temp
		mov	hex,w			; hex register

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

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

:loop		mov	w,temp			; get the value at temp (index)
		call	hex_table
		xor	w,byte
		snz				; compare it to the received byte
		ret
		inc	temp			; if they are equal, we have the
		jb	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	$700
;*****************************************************************************************
; 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,EE_char,I2CM_address(command byte)
	;*********************************************************************************

EE_write_char_
		BANK	EEPROM			
		mov	temp,EE_char		; load second byte with actual data
		mov	w,EE_bank_adr		; load first byte to send with E2 bank address
		BANK	I2CM
		mov	I2CM_data_0,w
		mov	I2CM_data_1,temp
		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 w
	;*********************************************************************************
	
EE_read_char_
		BANK	EEPROM			
		mov	w,EE_bank_adr		; load first byte to send with E2 bank address
		BANK	I2CM
		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
		bank	I2CM
		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

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