;*****************************************************************************************
; Copyright © [3/29/2000] 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.
;*****************************************************************************************
; 
; Filename:	i2cs.src
;
; Author:	Chris Fogelklou and Bruce Wilson
;		Applications Engineer
;		Scenix Semiconductor Inc.
;
; Revision:	1.04a (preliminary, for review)
;
; Part:		XXXXXXXXXX
; Freq:		50MHz
;
; Compiled using: XXXXXXXXXXXXX
;
;
; Date Written: Jan 15, 2000
;
; Last Revised: March 29, 2000
;
; Introduction:
;   	By using the I2C slave Virtual Peripheral™, provides any SX device with the interface
; 	required to operate as an I2C slave.  The way in which this peripheral has been written, 
; 	was with the intent to give the user simple access subroutines to call and need not
; 	worry about the inner workings of the peripheral code.  
; 	To implement the I2C slave Virtual Peripheral™ requires: 
;	216 bytes of program memory 
;	12 bytes of RAM.
;	2 I/O pins for the I2C bus (SDA and SCL)
;	Timer interrupt running every 2.6us for 100kHz-bus speed
;
; Program Description:
;   	The mainline code of this VP has been written to enable this slave to interface to the 
; 	SX I2C master VP (I2CM.src).  It also gives a a starting point for other programs.  
; 	The mainline code enables a string to to be read out of memory by a master I2C device
; 	just as data would be read out of an I2C EEPROM device.  
; 	The I2C slave Virtual Peripheral™ uses a state machine to change between all the required
; 	states within any I2C operation.  This state machine operates solely within the timer
; 	interrupt service routine.  It is possible to place the function I2CS_ISR in any page of 
; 	program memory.  Since the state machine can be executed asynchronously, it is also 
; 	possible to run the code from the mainline if required. If this is done no calls from the
; 	interrupt service routine are required leaving it free for other operations.
; 	The state machine will execute automatically when a master addresses this slave 
; 	set by slave_address.  Flags will be set so that the mainline code can check when data has
;	 been received or when data has been sent out from the slave. By checking these flags at
; 	strategic points within your code will enable the SX to run as a very efficient I2C slave
; 	device.               
;   	In order to test this program simply connect two SX devices as shown below configuring
; 	the two I2C lines to the I/O pins of your choice.
;
;		 		    4.7k			
;			    VCC	x--/\/\/\--------x 	   
;	 	 		    4.7k	 |
;			    VCC	x--/\/\/\---x	 |
;					    |    |
;					    |	 | SCL	  	    
;		 MASTER     RA0	------------x--------------------------- RB0    SLAVE
;		  SX 				 | SDA 	        	        SX 2
;	      (I2CM.SRC)   RA1	-----------------x---------------------- RB1  (I2CS.SRC)
;						            			
; 	The pins chosen above are default and allow this code to run directly on the Scenix I2C/UART
; 	demo board with no modification.  Run the master SX in debug mode and you will see the ASCII
; 	values for the string 'I2C SLAVE' stored in bank 7. This is the data which was read from this
; 	slave device.
;
; Interface Pins:
;
;		Put hardware interface pins here.
;	
; Revision History:
;   1.0	 	Core I2C state machine implemented by Chris Fogelklou
;   1.01 	Checked current I2C slave code and released slave VP with code that enables it to
;	 	be read by the master I2C VP at 100kHz.
;	 	Documentation and code revised by Bruce Wilson.	
;   1.02 	Code updated to run on SASM and SX52
;   1.03 	Code tested on Scenix Eval and I2C/UART boards on both SX52 and SX28 parts.;
;   1.04a 	Rewritten according to VP guide 1.02, incompabillity with 48/52 fixed (portdirection register) 
;		Selectable Scl/Sda pins (RA)
;   
;*****************************************************************************************
;*****************************************************************************************
; Target SX
; Uncomment one of the following lines to choose the SX18AC, SX20AC, SX28AC, SX48BD,
; or SX52BD. 
;*****************************************************************************************
;SX18_20
SX28
;SX48_52

;*****************************************************************************************
; Assembler Used
; Uncomment the following line if using the Parallax SX-Key assembler. SASM assembler
; enabled by default.
;*****************************************************************************************
;Sx_Key			; Uncomment this line to assemble this source code using the
			; Parallax Assembler

	;*********************************************************************************
	; 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				;SX48/52/BD device directives for SX-Key
		device	oschs2
  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				;SX48BD or SX52BD device directives for SASM
		device	SX52,oschs2  
  ENDIF
ENDIF
		id	'I2CM'		;
		reset	resetEntry	; set reset vector



;*****************************************************************************************
; Macros
;*****************************************************************************************
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;
	;   	To support compatibility between source code written for the SX28 and the SX52,
	;	use 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.7 needs to be
	; set appropriately, depending on the bank address being accessed. Use of this macro
	; switches banks correctly, regardless of the part being compiled for.
	;
	; Instead of using the bank instruction to switch between banks, use _bank instead.
	; 
	;*********************************************************************************

_bank	macro	1
	bank	\1

	IFDEF SX48_52
	    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
	endm


	;*****************************************************************************************
	; Macros for SX28/52 Compatibility
	;*****************************************************************************************
	;*********************************************************************************
	; Macro: _mode
	; Sets the MODE register appropriately for all revisions of SX.
	;
	; This is required since the MODE (or MOV M,#) instruction has only a 4-bit operand. 
	; The SX18/20/28AC use only 4 bits of the MODE register, however the SX48/52BD have 
	; the added ability of reading or writing some of the MODE registers, and therefore use
	; 5-bits of the MODE register. The  MOV M,W instruction modifies all 8-bits of the 
	; MODE register, so this instruction must be used on the SX48/52BD to make sure the MODE
	; register is written with the correct value. This macro fixes this.
	;
	; So, instead of using the MODE or MOV M,# instructions to load the M register, use
	;  _mode instead.
	; 
	;*********************************************************************************
_mode	macro	1
	IFDEF SX48_52
  expand
		mov	w,#\1		;loads the M register correctly for the SX48BD and SX52BD
		mov	m,w
  noexpand
	ELSE
  expand
		mov	m,#\1		;loads the M register correctly for the SX18AC, SX20AC
  noexpand					;and SX28AC
	ENDIF
	endm

	;*****************************************************************************************
	; INCP/DECP macros for incrementing/decrementing pointers to RAM
	; used to compensate for incompatibilities between SX28 and SX52
	;*****************************************************************************************

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;
	;   	To support compatibility between source code written for the SX28 and the SX52,
	;	use macros.  This macro compensates for the fact that RAM banks are contiguous in
	;	the SX52, but separated by 0x20 in the SX18/28.
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

INCP	macro	1
		inc	\1
	IFNDEF	SX48_52
		setb	\1.4		; If SX18 or SX28, keep bit 4 of the pointer = 1
	ENDIF				; to jump from $1f to $30, etc.
endm
		
DECP	macro	1
	IFDEF	SX48_52
		dec	\1
	ELSE
		clrb	\1.4		; If SX18 or SX28, forces rollover to next bank
		dec	\1		; if it rolls over.  (Skips banks with bit 4 = 0)
		setb	\1.4		; Eg:  $30 --> $20 --> $1f --> $1f
	ENDIF				; AND: $31 --> $21 --> $20 --> $30
endm

	;*****************************************************************************************
	; Error generating macros
	; Used to generate an error message if the label is unintentionally moved into the 
	; second half of a page.  Use for lookup tables.
	;*****************************************************************************************

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;
	;   	Surround lookup tables with the tableStart and tableEnd macros.  An error will
	;	be generated on assembly if the table crosses a page boundary.
	;	
	;	Example:
	;		lookupTable1
	;			add	pc,w
	;		tableStart
	;			retw	0
	;			retw	20
	;			retw	-20
	;			retw	-40
	;		tableEnd
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

tableStart	macro 0			; Generates an error message if code that MUST be in
					; the first half of a page is moved into the second half.
	if $ & $100
		ERROR  'Must be located in the first half of a page.'
	endif
endm

tableEnd	macro 0			; Generates an error message if code that MUST be in
					; the first half of a page is moved into the second half.
	if $ & $100
		ERROR  'Must be located in the first half of a page.'
	endif
endm

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

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;
	;   	Use only these defined label types for global registers.  If an extra temporary 
	;	register is required, adhere to these label types.  For instance, if two temporary
	;	registers are required for the Interrupt Service Routine, use the label isrTemp1
	;	for it.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

flags0		equ	global_org + 0  ; stores bit-wise operators like flags 
						; and function-enabling bits (semaphores)

	;VP_begin I2C Slave
	i2csEventFlag	equ	flags0.1
	i2csStopFound	equ	flags0.2
	i2csRxFlag	equ	flags0.3
	;VP_end

flags1		equ	global_org + 1  ; stores bit-wise operators like flags 
						; and function-enabling bits (semaphores)
localTemp0	equ	global_org + 2	; temporary storage register
						; Used by first level of nesting
						; Never guaranteed to maintain data
localTemp1	equ	global_org + 3	; temporary storage register	
						; Used by second level of nesting
						; or when a routine needs more than one 
						; temporary global register.	
localTemp2	equ	global_org + 4	; temporary storage register
						; Used by third level of nesting or by
						; main loop routines that need a loop 
						; counter, etc.
isrTemp0	equ	global_org + 5	; Interrupt Service Routine's temp register.  
						; Don't use this register in the mainline.

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

	;*********************************************************************************
	; Bank 0
	;*********************************************************************************
		org     bank0_org


bank0		=	$
	
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	- Avoid using bank0 in programs written for SX48/52.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

	;*********************************************************************************
	; Bank 1
	;*********************************************************************************
		org     bank1_org

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;
	;	Tip 1:
	;	Indicate which Virtual Peripherals a portion of source code or declaration belongs
	;	to with a 
	;			";VP: VirtualPeripheralName"
	;	 comment.
	;
	;	Tip 2:
	;	All RAM location declaration names should be
	;   	- left justified
	;	- less than 2 tabs in length
	;	- written in hungarian notation
	;	- prefixed by a truncated version of the Virtual Peripheral's name
	;	
	;	Examples:
	;
	;	;VP: RS232 Transmit
	;
	;	rs232TxBank	=       $                       ;RS232 Transmit bank
	;
	; 	rs232TxHigh	ds      1                       ;hi byte to transmit
	;	rs232TxLow	ds      1                       ;low byte to transmit
	;	rs232TxCount	ds      1                       ;number of bits sent
	;	rs232TxDivide	ds      1                       ;xmit timing (/16) counter
	;	rs232TxString	ds	1			;the address of the string to be sent
	;	rs232TxByte	ds	1			;semi-temporary serial register
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

;VP: ISR Multithreader

isrMultiplex	ds	1				; The isrMultiplex register is used to switch to a new
							; execution thread on each pass of the ISR.


;VP_begin I2C Slave

; Used by state machine
i2csBank		= 	$			; I2C Slave bank
i2csState		ds	1			; This indicates the state that the I2C slave is currently in
i2csSubState		ds	1			; This indicates the substate the I2C slave is currently in
i2csPortBuf		ds	1			; This buffer holds the current state of the I2C port direction reg's
i2csAddress		ds	1			; This byte holds the address which the I2C slave will respond to
i2csBitCount		ds	1			; Indicate sthe number of bits left to process in read/write
i2csByte		ds	1			; The byte currently being written/read by the I2C master
i2csPastPres		ds	1			; The last_state of the I2C port (to be compared with the present state)
i2csPresSCL		equ	i2csPastPres.0		; Present state of SCL
i2csPresSDA		equ	i2csPastPres.1		; Present state of SDA
i2csPastSCL		equ	i2csPastPres.2		; The state of SCL on last interrupt
i2csPastSDA		equ	i2csPastPres.3		; The state of SDA on last interrupt

; User Accessible
i2csDataIn		ds	1			; The saved data byte received from the master
i2csDataOut		ds	1			; A byte of data to be read by the master
i2csFlags		ds	1
i2csDataValid		equ	i2csFlags.0		; Lets the I2C slave know that there is valid data stored 
							; in the i2csDataOut
i2csDataNeeded		equ	i2csFlags.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 i2csDataOut register.
							; The slave will hold the clock line low until the i2csDataValid 
							; flag is set, indicating valid data.
i2csBeingReadFlag	equ	i2csFlags.2		; True when command received from the master is to send data.
							; When clear slave will be receving

;VP_end






	;*********************************************************************************
	; Bank 2
	;*********************************************************************************
		org     bank2_org

bank2		=	$

	;*********************************************************************************
	; Bank 3
	;*********************************************************************************
		org     bank3_org

bank3		=	$

	;*********************************************************************************
	; Bank 4
	;*********************************************************************************
		org     bank4_org

bank4		=	$

	;*********************************************************************************
	; Bank 5
	;*********************************************************************************
		org     bank5_org

bank5		=	$

	;*********************************************************************************
	; Bank 6
	;*********************************************************************************
		org     bank6_org

bank6		=	$

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

bank7		=	$

i2csStringBank	= 	$




IFDEF SX48_52
	;*********************************************************************************
	; Bank 8
	;*********************************************************************************
		org	$80	;bank 8 address on SX52

bank8		=	$

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	- This extra memory is not available in the SX18/28, so don't use it for Virtual
	;	  Peripherals written for both platforms.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

	;*********************************************************************************
	; 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
		
	;*********************************************************************************
	; Pin Definitions:  
	;*********************************************************************************

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	- Store all initialization constants for the I/O in the same area, so
	;	  pins can be easily moved around.
	;	- Pin definitions should follow the same format guidelines as RAM definitions
	;		- Left justified
	;		- Hungarian Notation
	;		- Less that 2 tabs in length
	;		- Indicate the Virtual Peripheral the pin is used for
	;	- Only use symbolic names to access a pin/port in the source code.
	;	- Example:
	;		; VP: RS232 Transmit
	;			rs232TxPin	equ	ra.3
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?



RA_latch	equ	%00001011		;SX18/20/28/48/52 port A latch init
RA_DDIR		equ	%11110111		;SX18/20/28/48/52 port A DDIR value
RA_LVL		equ	%11111111		;SX18/20/28/48/52 port A LVL value
RA_PLP		equ	%11111111		;SX18/20/28/48/52 port A PLP value

	;VP_begin I2C Slave
	i2csPort	equ	ra
	i2csScl		equ	0
	i2csSda		equ	1			; 
	i2csSclPin	equ	i2csPort.i2csScl	; SCL
	i2csSdaPin	equ	i2csPort.i2csSda	; SDA
	;VP_end
	
	
RB_latch	equ	%11111111		;SX18/20/28/48/52 port B latch init
RB_DDIR		equ	%11111111		;SX18/20/28/48/52 port B DDIR value
RB_ST		equ	%11111111		;SX18/20/28/48/52 port B ST value
RB_LVL		equ	%00000000		;SX18/20/28/48/52 port B LVL value
RB_PLP		equ	%11111111		;SX18/20/28/48/52 port B PLP value


RC_latch	equ	%11111111		;SX18/20/28/48/52 port C latch init
RC_DDIR		equ	%01111111		;SX18/20/28/48/52 port C DDIR value
RC_ST		equ	%11111111		;SX18/20/28/48/52 port C ST value
RC_LVL		equ	%00000000		;SX18/20/28/48/52 port C LVL value
RC_PLP		equ	%01111111		;SX18/20/28/48/52 port C PLP value

IFDEF SX48_52	;SX48BD/52BD Port initialization values
RD_latch	equ	%00000000		;SX48/52 port D latch init
RD_DDIR		equ	%11111111		;SX48/52 port D DDIR value
RD_ST		equ	%11111111		;SX48/52 port D ST value
RD_LVL		equ	%00000000		;SX48/52 port D LVL value
RD_PLP		equ	%11111111		;SX48/52 port D PLP value

RE_latch	equ	%00000000		;SX48/52 port E latch init
RE_DDIR		equ	%01001111		;SX48/52 port E DDIR value
RE_ST		equ	%11111111		;SX48/52 port E ST value
RE_LVL		equ	%00000000		;SX48/52 port E LVL value
RE_PLP		equ	%11111111		;SX48/52 port E PLP value
ENDIF






;*****************************************************************************************
; Program constants
;*****************************************************************************************

;-------------------------------------------------------------------------------------
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;	To calculate the interrupt period in cycles:
	;	- First, choose the desired interrupt frequency
	;		- Should be a multiple of each Virtual Peripherals sampling frequency.
	;		- Example:  19200kHz UART sampling rate * 16 = 307.200kHz
	;	- Next, choose the desired oscillator frequency.
	;		- 50MHz, for example.
	;	- Perform the calculation period = (osc. frequency / interrupt frequency)
	;				  		= (50MHz / 307.2kHz)
	;						= 162.7604
	;	- Round int_period to the nearest integer:
	;						= 163
	;	- Now calculate your actual interrupt rate:
	;						= osc. frequency / int_period
	;						= 50MHz / 163
	;						= 306.748kHz
	;	- This interrupt frequency will be the timebase for all of the Virtual 
	;	  Peripherals
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

int_period	=	125	; Gives an interrupt period at 50MHz of (125 * (1/50000000)s) = 2.5us
				; Which gives an interrupt frequency of (1/2.5us)Hz = 400kHz

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;	- Include all calculations for Virtual Peripheral constants for any sample 
	;	  rate.
	;	- Relate all Virtual Peripheral constants to the sample rate of the Virtual
	;	  Peripheral.
	;	- Example:
	;		; VP: 5ms Timer
	;		TIMER_DIV_CONST	equ 192	; This constant = timer sample rate/200Hz = 192
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?



;VP_begin I2C Slave
i2csSlaveAddress	equ	$40			; Address of this slave
;VP_end 



;-------------------------------------------------------------------------------------
IFDEF SX48_52
	;*********************************************************************************
	; SX48BD/52BD Mode addresses
	; *On SX48BD/52BD, most registers addressed via mode are read and write, with the
	; exception of CMP and WKPND which do an exchange with W.
	;*********************************************************************************
; Timer (read) addresses
TCPL_R		equ	$00		;Read Timer Capture register low byte
TCPH_R		equ	$01		;Read Timer Capture register high byte
TR2CML_R	equ	$02		;Read Timer R2 low byte
TR2CMH_R	equ	$03		;Read Timer R2 high byte
TR1CML_R	equ	$04		;Read Timer R1 low byte
TR1CMH_R	equ	$05 		;Read Timer R1 high byte
TCNTB_R		equ	$06		;Read Timer control register B
TCNTA_R		equ	$07		;Read Timer control register A

; Exchange addresses
CMP		equ	$08		;Exchange Comparator enable/status register with W
WKPND		equ	$09		;Exchange MIWU/RB Interrupts pending with W

; Port setup (read) addresses
WKED_R		equ	$0A		;Read MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_R		equ	$0B		;Read MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_R		equ	$0C		;Read Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_R		equ	$0D		;Read Port Level setup, 0 = CMOS, 1 = TTL
PLP_R		equ	$0E		;Read Port Weak Pullup setup, 0 = enabled, 1 = disabled
DDIR_R		equ	$0F		;Read Port Direction

; Timer (write) addresses
TR2CML_W	equ	$12		;Write Timer R2 low byte
TR2CMH_W	equ	$13		;Write Timer R2 high byte
TR1CML_W	equ	$14		;Write Timer R1 low byte
TR1CMH_W	equ	$15 		;Write Timer R1 high byte
TCNTB_W		equ	$16		;Write Timer control register B
TCNTA_W		equ	$17		;Write Timer control register A

; Port setup (write) addresses
WKED_W		equ	$1A		;Write MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_W		equ	$1B		;Write MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_W		equ	$1C		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_W		equ	$1D		;Write Port Level setup, 0 = CMOS, 1 = TTL
PLP_W		equ	$1E		;Write Port Weak Pullup setup, 0 = enabled, 1 = disabled
DDIR_W		equ	$1F		;Write Port Direction

ELSE

	;*********************************************************************************
	; SX18AC/20AC/28AC Mode addresses
	; *On SX18/20/28, all registers addressed via mode are write only, with the exception of
	; CMP and WKPND which do an exchange with W.
	;*********************************************************************************
; Exchange addresses
CMP		equ	$08		;Exchange Comparator enable/status register with W
WKPND		equ	$09		;Exchange MIWU/RB Interrupts pending with W

; Port setup (read) addresses
WKED_W		equ	$0A		;Write MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_W		equ	$0B		;Write MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_W		equ	$0C		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_W		equ	$0D		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
PLP_W		equ	$0E		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
DDIR_W		equ	$0F		;Write Port Direction
ENDIF


;*****************************************************************************************
; Program memory ORG defines
;*****************************************************************************************

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	- Place a table at the top of the source with the starting addresses of all of
	;	  the components of the program.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

INTERRUPT_ORG		equ	$0	; Interrupt must always start at location zero
RESET_ENTRY_ORG		equ	$1FB	; The program will jump here on reset.
SUBROUTINES_ORG		equ	$200	; The subroutines are in this location
STRINGS_ORG		equ	$300	; The strings are in location $300
PAGE3_ORG		equ	$400	; Page 3 is empty
MAIN_PROGRAM_ORG	equ	$600	; The main program is in the last page of program memory.













;****************************** Beginning of program space *******************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
	org	INTERRUPT_ORG			; First location in program memory.
;*****************************************************************************************
;------------------------------------------------------------------------------
; 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 -163 and an oscillator frequency of 50MHz, this
; code runs every 3.26us.
;------------------------------------------------------------------------------
ISR							;3	The interrupt service routine...




;VP_begin I2C Slave

		_bank	i2csBank			; 1
		clrb	c				; 1 ; Save the current state and past state of the I2C bus
		snb	i2csSdaPin			; 1
		setb	c					; 1
		rl	i2csPastPres			; 1	
		clrb	c					; 1
		snb	i2csSclPin			; 1
		setb	c					; 1
		rl	i2csPastPres			; 1
		page	i2csGetStartStop		; 1 ; Check the new state to see if we've received a start or stop
		call	i2csGetStartStop		; 3 = 13 + 10/14 = 23/27
		page	i2csISR				; 1 ; Call I2C Slave ISR
		call	i2csISR				; 3 + 12/27 (i2csISR length) = 39/58

i2csDone
		mov	w,m				; 1 ; Save the m register.
		mov	isrTemp0,w			; 1

		_mode	DDIR_W				; 1, 2 ; 2 cycles for SX48_52 (_mode macro)

		clrb	i2csSclPin			; 1 ; Clear the data latches for SCL and SDA
		clrb	i2csSdaPin 			; 1

		
		_bank	i2csBank			; 1
		mov	w,i2csPortBuf			; 1 ; Update the I2C port with the buffered port data		
		mov	!i2csPort,w			; 1

		mov	w,isrTemp0			; 1 ; Restore the m register
		mov	m,w				; 1 = 10 (11 for SX48/52)

		jmp	isrOut				;7 cycles until mainline program resumes execution
							;= 17/18 + 39/58 = 56/76
;VP_end




;------------------------------------------------------------------------------
isrOut
;------------------------------------------------------------------------------
	mov	w,#-int_period	;1	; return and add -int_period to the RTCC
	retiw			;3	; using the retiw instruction.

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


;*****************************************************************************************
org	RESET_ENTRY_ORG
;*****************************************************************************************
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;	The main program operation should be easy to find, so place it at the end of the 
	;	program code.  This means that if the first page is used for anything other than 
	;	main program source code, a reset_entry must be placed in the first page, along 
	;	with a 'page' instruction and a 'jump' instruction to the beginning of the 
	;	main program.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;------------------------------------------------------------------------------
resetEntry					; Program starts here on power-up
	page	_resetEntry
	jmp	_resetEntry
;------------------------------------------------------------------------------







;*****************************************************************************************
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;	ORG statements should use predefined labels rather than literal values.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?




org	SUBROUTINES_ORG
;*****************************************************************************************
; Subroutines
;*****************************************************************************************



	;----------------------------------------------------------
	; VP: I2C Slave
	;
	; Function: i2csGetStartStop
	;
	; Input:  i2csGetStartStop
	; Output: i2csState
	;
	; 	This subroutine checks the i2csPastPres register for the presence of a start
	; 	or stop condition.  It takes the values present in the i2csPastPres 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.
	;----------------------------------------------------------

i2csGetStartStop
		mov	w,i2csPastPres			; 1
		and	w,#$0f				; 1
		xor	w,#%00001101			; 1 ; Compare with a start condition
		
		snb	z				; 1
		jmp	:start				; 3
		xor	w,#%00001010			; 1 ; Compare XOR of start and stop with
		sb	z				; 1 ; what would be present if a stop was present
		
		retp					; 3 = 10

:stop		clr	i2csState			; 1 ; If stop, then make I2C slave idle
		clr	i2csSubState			; 1
		setb	i2csStopFound			; 1
		retp					; 3 = 14

:start		mov	w,#1				; 1 ; If start, then start receving the address
		mov	i2csState,w			; 1
		retp					; 3 = 12





	;----------------------------------------------------------
	; VP: I2C Slave
	;
	; Function: i2csISR
	;
	; Input: i2csState
	; Output
	;
	;
	; States:
	;
	; 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 i2csState and i2csSubState.  These
	;	are the discrete states of this state machine...
	;
	;  i2csIdle
	;	i2csIdle 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 
	;	i2csGetStartStop.  
	;
	;  i2csWaitForSclLow
	;	This state simply waits until SCL goes low.  It is entered once
	;	i2csGetStartStop encounters a start condition.  Once SCL goes low,
	;	the state is incremented
	;
	;  i2csGetAddress
	;	This state prepares the i2csReadByte routine to get 8-bits of data. 
	;	It increments the state to i2csReadByte before exiting.
	;
	;  i2csReadByte
	;	This state reads a byte of data.  i2csBitCount 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.
	;
	;  i2csProcessAddress
	;	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 
	;	i2csIdle.  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.
	;
	;  i2csSendAck
	;	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.
	;
	;  i2csReadData
	;	This state prepares i2csReadByte to receive 8 bits of data.  It increments
	;	the state before exiting.
	;
	;  i2csMakeIdle
	;	This state puts the I2C slave back into idle mode.
	;
	;  i2csProcessData
	;	This state processes a byte of data which was just received.  It moves the 
	;	byte that was just received into the i2csDataIn register, and sets the 
	;	I2CS_event_flag to indicate an I2CS event and sets the i2csRxFlag to 
	;	indicate that a byte of data was received by the slave.
	;
	;  i2csSendData
	;	This state prepares the i2csWriteByte state to send the 8-bits of data
	;	in the i2csDataOut register.
	;
	;  i2csWriteByte
	;	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 i2csBitCount register loaded with #8.
	;
	;  i2csGetAck
	;	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 i2csDataOut register.  If no
	;	ACK is received, the slave will be put back into its idle state.
	;----------------------------------------------------------

i2csISR
		mov	w,i2csState			; 1
		add	PC,w				; 3 ; Add the state to the program counter


tableStart
							    ; and go to the state in the jump table.
; Idle states
i2csIdleLoc		= $
		jmp	i2csIdle			; 3 ; If i2csState = 0, I2C is idle
		jmp	i2csWaitForSclLow		; 3 ; Wait for SCL line to go low after start condition
		jmp	i2csGetAddress			; 3 ; Get the address
		jmp	i2csReadByte
		jmp	i2csProcessAddress		; 3 ; Check to see if the device was addressed.

		
; Read states
i2csBeingRead		= $
		jmp	i2csSendAck			; 3
i2csBeingReadAgain	= $
		jmp	i2csSendData			; 3 ; Set up to send a byte
		jmp	i2csWriteByte			; 3 ; Send the byte, clocked by SCL
		jmp	i2csGetAck			; 3 ; If we receive an Ack, then we are being read again.
		jmp	i2csWaitingForData		; 3 ; If there is no valid data, wait for it.


; Write states
i2csBeingWritten	= $
		jmp	i2csSendAck			; 3 ; Send an ACK
		jmp	i2csReadData			; 3
		jmp	i2csReadByte			; 3
		jmp	i2csProcessData			; 3
		jmp	i2csSendAck			; 3
		jmp	i2csMakeIdle			; 3 

tableEnd



	;----------------------------------------------------------
	; State: i2csIdle
	;	i2csIdle 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 
	;	i2csGetStartStop.
	;----------------------------------------------------------

i2csIdle
		setb	i2csPortBuf.i2csScl		; 1
		setb	i2csPortBuf.i2csSda		; 1
		retp					; 3 = 5 + 7 = 12



	;----------------------------------------------------------
	; State: i2csWaitForSclLow
	;	This state simply waits until SCL goes low.  It is entered once
	;	i2csGetStartStop encounters a start condition.  Once SCL goes low,
	;	the state is incremented
	;----------------------------------------------------------

i2csWaitForSclLow
		snb	i2csPresScl			; 1
		retp					; 3 = 4 + 7 = 11
		clrb	i2csPortBuf.i2csScl		; 1
		inc	i2csState			; 1
		retp					; 3 = 7 + 7 = 14



	;----------------------------------------------------------
	; State: i2csGetAddress
	;	This state prepares the i2csReadByte routine to get 8-bits of data. 
	;	It increments the state to i2csReadByte before exiting.
	;----------------------------------------------------------

i2csGetAddress
		mov	w,#8				; 1
		mov	i2csBitCount,w			; 1
		inc	i2csState			; 1
		retp					; 3 = 6 + 7 = 13



	;----------------------------------------------------------
	; State: i2csReadByte
	;	This state reads a byte of data.  i2csBitCount 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.
	;----------------------------------------------------------

i2csReadByte
		mov	w,i2csSubState			; 1
		add	pc,w				; 3
		skip					; 1 goto state 2 
		jmp	:state2				; 3

:state1							; 1 ; Wait until SCL goes high
		setb	i2csPortBuf.i2csScl		; 1
		sb	i2csPresScl			; 1
		retp					; 3 = 12 + 7 = 19
		inc	i2csSubState			; 1 ; If SCL is high,rotate in the bit

		clrb	c					; 1
		snb	i2csPresSda			; 1
		setb	c					; 1
		rl	i2csByte			; 1
		retp					; 3 = 18 + 7 = 25

:state2
		snb	i2csPresScl			; 1
		retp					; 3 = 11 + 7 = 18
		clrb	i2csPortBuf.i2csScl		; 1
		clr	i2csSubState			; 1
		dec	i2csBitCount			; 1
		sb	z					; 1
		retp					; 3 = 16 + 7 = 23
		inc	i2csState			; 1
		retp					; 3 = 18 + 7 = 25
							; stay in this i2csState until all 8 bits are received


	;----------------------------------------------------------
	; State: i2csProcessAddress
	;	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 
	;	I2CSIdle.  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.	
	;----------------------------------------------------------

i2csProcessAddress
		mov	w,i2csByte				; 1 ; Get the received I2C byte
		xor	w,i2csAddress				; 1 ; Compare it with the address of this slave

		and	w,#$FE					; 1 ; And out the R/!W bit
		snb	z						; 1
		jmp	:slave_addressed			; 3 ; If it is equal, send an ACK
		clr	i2csState				; 1 ; If the device wasn't addressed, then go idle
		retp						; 3 = 9 + 7 = 16

:slave_addressed						;=7
		clrb	i2csPortBuf.i2csScl		   	; 1 
		snb	i2csByte.0				; 1 ; If bit zero of the incoming byte is a 0, 
							    	    ; then go to the being written state
		jmp	:being_read				; 3 ; go to the being_read state
		mov	w,#(i2csBeingWritten-i2csIdleLoc) 	; 1
		mov	i2csState,w				; 1
		clrb	i2csBeingReadFlag			; 1 ; Clear the slave being read flag
		retp						; 3 = 16 + 7 = 23

:being_read	sb	i2csDataValid				; 1
		jmp	i2csMakeIdle				; 3 = 16
		mov	w,#(i2csBeingRead-i2csIdleLoc)		; 1
		mov	i2csState,w				; 1
		setb	i2csBeingReadFlag			; 1 ; Set the slave being read flag
		retp						; 3 = 20 + 7 = 27



	;----------------------------------------------------------
	; State: i2csSendAck
	;	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.	
	;----------------------------------------------------------

i2csSendAck
		mov	w,i2csSubState			; 1
		add	PC,w				; 3
		jmp	:state1				; 3
		jmp	:state2				; 3
		jmp	:state3				; 3

:state1							
		clrb	i2csPortBuf.i2csSda		; 1 ; Clear SDA to set up ACK
		inc	i2csSubState			; 1
		retp					; 3 = 12 + 7 = 19

:state2
		setb	i2csPortBuf.i2csScl		; 1 ; Allow the clock to go high
		sb	i2csPresScl			; 1 ; Check to see if the clock is high
		retp					; 3 = 12 + 7 = 19
		inc	i2csSubState			; 1
		retp					; 3 = 14 + 7 = 21

:state3
		snb	i2csPresScl			; 1
		retp					; 3 = 11 + 7 = 18
		clrb	i2csPortBuf.i2csScl		; 1
		setb	i2csPortBuf.i2csSda		; 1
		clr	i2csSubState			; 1
		inc	i2csState			; 1
		retp					; 3 = 16 + 7 = 23



	;----------------------------------------------------------
	; State: i2csReadData
	;	This state prepares I2CS_read_byte to receive 8 bits of data.  It increments
	;	the state before exiting.
	;----------------------------------------------------------

i2csReadData
		mov	w,#8				; 1
		mov	i2csBitCount,w			; 1
		inc	i2csState			; 1
		retp					; 3 = 6 + 7 = 13



	;----------------------------------------------------------
	; State: i2csMakeIdle
	;	This state puts the I2C slave back into idle mode.
	;----------------------------------------------------------

i2csMakeIdle
		clr	i2csState			; 1
		clr	i2csSubState			; 1
		setb	i2csPortBuf.i2csScl		; 1
		setb	i2csPortBuf.i2csSda		; 1
		retp					; 3 = 7 + 7/16 = 14/23 (ISR/i2csProcessAddress) 




	;----------------------------------------------------------
	; State: i2csProcessData
	;	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.
	;----------------------------------------------------------

i2csProcessData
		mov	w,i2csByte			; 1
		mov	i2csDataIn,w			; 1
		setb	i2csRxFlag			; 1
		setb	i2csEventFlag			; 1 ; Indicate that a byte has been received with the global event flag
		inc	i2csState			; 1
		retp					; 3 = 8 + 7 = 15
	


	;----------------------------------------------------------
	; State: i2csSendData
	;	This state prepares the i2csWriteByte state to send the 8-bits of data
	;	in the i2csDataOut register.
	;----------------------------------------------------------

i2csSendData
		inc	i2csState			; 1
		clr	i2csSubState			; 1
		clrb	i2csDataValid			; 1
		mov	w,i2csDataOut			; 1
		mov	i2csByte,w			; 1
		mov	w,#8				; 1
		mov	i2csBitCount,w			; 1
		retp					; 3 = 10 + 7 = 17



	;----------------------------------------------------------
	; State: i2csWriteByte
	;	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 i2csByte register loaded with
	;	valid data and having the i2csBitCount register loaded with #8.
	;----------------------------------------------------------

i2csWriteByte
		mov	w,i2csSubState			; 1
		add	PC,w				; 3
		jmp	:state1				; 3
		jmp	:state2				; 3
		jmp	:state3				; 3

:state1
		rl	i2csByte			; 1
		setb	i2csPortBuf.i2csSda		; 1
		sb	c				; 1
		clrb	i2csPortBuf.i2csSda		; 1
		inc	i2csSubState			; 1
		retp					; 3 = 15 + 7 = 22

:state2
		setb	i2csPortBuf.i2csScl		; 1 allow SCL to go high, indicating slave is ready
		snb	i2csPresScl			; 1 ; If SCL is not yet high, don't proceed to next state
		inc	i2csSubState			; 1
		retp					; 3 = 13 + 7 = 20

:state3
		snb	i2csPresScl			; 1
		retp					; 3 = 11 + 7 = 18
		clr	i2csSubState			; 1
		clrb	i2csPortBuf.i2csScl		; 1 ; Drag SCL low again to indicate slave is processing
		setb	i2csPortBuf.i2csSda		; 1 ; release SDA line again
		dec 	i2csBitCount			; 1
		sb	z				; 1 ; once we hav done all 8 bits, go to next state
		retp					; 3 = 17 + 7 = 24
		inc	i2csState			; 1
		retp					; 3 = 19 + 7 = 26



	;----------------------------------------------------------
	; State: i2csGetAck
	;	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 I2CSDataOut register.  If no
	;	ACK is received, the slave will be put back into its idle state.
	;----------------------------------------------------------

i2csGetAck
		mov	w,i2csSubState			; 1
		add	PC,w				; 3
		jmp	:state1				; 3
		jmp	:state2				; 3

:state1
		setb	i2csPortBuf.i2csScl		; 1
		sb	i2csPresScl			; 1
		retp					; 3 = 12 + 7 = 19
		snb	i2csPresSda			; 1
		jmp	:NACK				; 3
		inc	i2csSubState			; 1
		retp					; 3 = 16 + 7 = 23

:NACK
		clr	i2csSubState			; 1
		clr	i2csState			; 1
		retp					; 3 = 19 + 7 = 26

:state2		
		snb	i2csPresScl			; 1
		retp					; 3 = 11 + 7 = 18
		clrb	i2csPortBuf.i2csScl		; 1
		clr	i2csSubState			; 1
		inc	i2csState			; 1
		retp					; 3 = 15 + 7 = 22



	;----------------------------------------------------------
	; State: i2csWaitingForData
	;	This state waits for the mainline routine to put some valid data in the
	;	I2CDataOut register.  This state is only reached if the master has 
	;	indicated it wants more data by sending an ACK after the last byte.
	;----------------------------------------------------------

i2csWaitingForData
		snb	i2csDataValid				; 1
		jmp	:data_valid				; 3
		setb	i2csDataNeeded				; 1
		setb	i2csEventFlag				; 1
		retp						; 3 = 7 + 7 = 14

:data_valid
		mov	w,#(i2csBeingReadAgain-i2csIdleLoc)	; 1
		mov	i2csState,w				; 1
		retp						; 3 = 9 + 7 = 16









	;----------------------------------------------------------
	; VP: I2C Slave
	;
	; Function: i2csInit
	;
	; 	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.  
	;
	; Memory Location Dependency:
	;	This subroutine will only work as long as i2cString lies within the 
	;	same page as the STRINGS_ORG label.  In this VP, that range happens 
	;	to be a half a page in size ($300 - $3FF) due to the fact that 
	;	STRINGS_ORG is equated to $300.  	
	;
	; localTemp registers destroyed by this subroutine:
	; 	localTemp0, localTemp1
	;----------------------------------------------------------

i2csInit
		_bank	i2csBank
		mov	w,#%10111111				; Set RB in/out directions
		mov	i2csPortBuf,w
		mov	w,#$FF					; Let part know last states have all been high
		mov	i2csPastPres,w
		mov	w,#i2csSlaveAddress
		mov	i2csAddress,w


		; Copy string into RAM
		
		_bank	i2csStringBank
		clr	localTemp0				; Clear string offset counter			

:loop
		_mode	STRINGS_ORG>>8				; String base src address
		mov	w,#i2csString				; String offset address
		add	w,localTemp0				
		
		iread						; Read Program memory
		or	w,#$00					; String is 0 terminated
		snb	z
		jmp	:out
		
		mov	localTemp1,w				; Save string char temporary
		mov	w,#i2csStringBank
		add	w,localTemp0				; String offset address
		mov	fsr,w
		mov	w,localTemp1				
		mov	indf,w					; Write char to RAM


		inc	localTemp0				; Increment string offset counter
		jmp	:loop
		

:out
		retp







;*****************************************************************************************
org	STRINGS_ORG		; This label defines where strings are kept in program space.
;*****************************************************************************************
;------------------------------------------------------------------------------
; Put String Data Here
;------------------------------------------------------------------------------

; Example:
;_hello          dw      13,10,'UART Demo',0

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	- Routines that use location-dependant data, such as in example below, should
	;	  use a LABEL rather than a literal value as their input.  Example:
	;	  instead of
	;		mov     m,#3		 ; move upper nybble of address of strings into m
	;	  use
	;		mov	m,#STRINGS_ORG>>8; move upper nybble of address of strings into m
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?


	; VP: I2C Slave
i2csString		dw	'I2C SLAVE',0   ;This string must lie within the same page of 
						;program memory as the STRINGS_ORG label in order
						;for the function i2csInit to operate correctly.






;*****************************************************************************************
org	PAGE3_ORG
;*****************************************************************************************
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;	To ensure that several Virtual Peripherals, when pasted together, do not cross 
	;	a page boundary without the integrator's knowledge, put an ORG statement and one 
	;	instruction at every page boundary.  This will generate an error if a pasted 
	;	subroutine moves another subroutine to a page boundary.  
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

	jmp	$	; This instruction will cause an assembler error if the source code before
			; the org statement inadvertantly crosses a page boundary.





;*****************************************************************************************
org	MAIN_PROGRAM_ORG
;*****************************************************************************************



;------------------------------------------------------------------------------
; RESET VECTOR 
;------------------------------------------------------------------------------

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

_resetEntry		
	;------------------------------------------------------------------------------
	; Initialize all port configuration
	;------------------------------------------------------------------------------

		_mode	ST_W			;point MODE to write ST register
		mov     w,#RB_ST            	;Setup RB Schmitt Trigger, 0 = enabled, 1 = disabled
		mov	!rb,w		
		mov     w,#RC_ST            	;Setup RC Schmitt Trigger, 0 = enabled, 1 = disabled
		mov	!rc,w	
IFDEF SX48_52
		mov     w,#RD_ST            	;Setup RD Schmitt Trigger, 0 = enabled, 1 = disabled
		mov	!rd,w		
		mov     w,#RE_ST            	;Setup RE Schmitt Trigger, 0 = enabled, 1 = disabled
		mov	!re,w		
ENDIF
		_mode	LVL_W			;point MODE to write LVL register
		mov     w,#RA_LVL            	;Setup RA CMOS or TTL levels, 0 = TTL, 1 = CMOS
		mov	!ra,w		 
		mov     w,#RB_LVL            	;Setup RB CMOS or TTL levels, 0 = TTL, 1 = CMOS
		mov	!rb,w		
		mov     w,#RC_LVL            	;Setup RC CMOS or TTL levels, 0 = TTL, 1 = CMOS
		mov	!rc,w	
IFDEF SX48_52
		mov     w,#RD_LVL            	;Setup RD CMOS or TTL levels, 0 = TTL, 1 = CMOS
		mov	!rd,w		
		mov     w,#RE_LVL            	;Setup RE CMOS or TTL levels, 0 = TTL, 1 = CMOS
		mov	!re,w		
ENDIF
		_mode	PLP_W			;point MODE to write PLP register
		mov     w,#RA_PLP            	;Setup RA Weak Pull-up, 0 = enabled, 1 = disabled
		mov	!ra,w		 
		mov     w,#RB_PLP            	;Setup RB Weak Pull-up, 0 = enabled, 1 = disabled
		mov	!rb,w		
		mov     w,#RC_PLP            	;Setup RC Weak Pull-up, 0 = enabled, 1 = disabled
		mov	!rc,w	
IFDEF SX48_52
		mov     w,#RD_PLP            	;Setup RD Weak Pull-up, 0 = enabled, 1 = disabled
		mov	!rd,w		
		mov     w,#RE_PLP            	;Setup RE Weak Pull-up, 0 = enabled, 1 = disabled
		mov	!re,w		
ENDIF
		_mode	DDIR_W			;point MODE to write DDIR register
		mov	w,#RA_DDIR		;Setup RA Direction register, 0 = output, 1 = input		
		mov	!ra,w	
		mov	w,#RB_DDIR		;Setup RB Direction register, 0 = output, 1 = input
		mov	!rb,w			
		mov	w,#RC_DDIR		;Setup RC Direction register, 0 = output, 1 = input
		mov	!rc,w			
IFDEF SX48_52
		mov	w,#RD_DDIR		;Setup RD Direction register, 0 = output, 1 = input
		mov	!rd,w			
		mov	w,#RE_DDIR		;Setup RE Direction register, 0 = output, 1 = input
		mov	!re,w			
ENDIF
		mov     w,#RA_latch          	;Initialize RA data latch
		mov     ra,w		
		mov     w,#RB_latch         	;Initialize RB data latch
		mov     rb,w		
		mov     w,#RC_latch          	;Initialize RC data latch
		mov     rc,w		
IFDEF SX48_52
		mov     w,#RD_latch         	;Initialize RD data latch
		mov     rd,w			
		mov     w,#RE_latch         	;Initialize RE data latch
		mov     re,w			
ENDIF


	;------------------------------------------------------------------------------
	; Clear all Data RAM locations
	;------------------------------------------------------------------------------

zeroRam
IFDEF SX48_52   				;SX48/52 RAM clear routine
		mov	w,#$0a			;reset all ram starting at $0A
		mov	fsr,w
:zeroRam	clr	ind			;clear using indirect addressing
		incsz	fsr			;repeat until done
		jmp	:zeroRam

		_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

ELSE     					;SX18/20/28 RAM clear routine
		clr	fsr			;reset all ram banks
:zeroRam	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	:zeroRam
ENDIF
	;------------------------------------------------------------------------------
	; Initialize program/VP registers
	;------------------------------------------------------------------------------



	;VP_begin I2C Slave
	call	@i2csInit
	;VP_end


	;------------------------------------------------------------------------------
	; Setup and enable RTCC interrupt, WREG register, RTCC/WDT prescaler
	;------------------------------------------------------------------------------

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;
	;	The suggested default values for the option register are:
	;	- Bit 7 set to 0: location $01 addresses the W register (WREG)
	;	- Bit 3 set to 1: Prescaler assigned to WatchDog Timer
	;
	;	If a routine must change the value of the option register (for example, to access
	;	the RTCC register directly), then it should restore the default value for the 
	;	option register before exiting.
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?


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 default
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 WDT (PSA hi)
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

OPTIONSETUP	equ	RTCC_ON | RTCC_PS_OFF	; the default option setup for this program.
		mov	w,#OPTIONSETUP		; setup option register for RTCC interrupts enabled 
		mov	!option,w		; and no prescaler.
		jmp	@mainLoop

;------------------------------------------------------------------------------
; MAIN PROGRAM CODE 
;------------------------------------------------------------------------------

mainLoop
		sb	i2csRxFlag			; Check if slave has received data		
		jmp	mainLoop

		_bank	i2csBank
		snb	i2csBeingReadFlag		; Check if slave is to send or receive
		jmp	:being_read
		
:being_written						; he data will be read out next time through being_read
		mov	w,#i2csStringBank		; Set location of stored string
		add	w,i2csDataIn
		mov	fsr,w				; set location of stored byte (offset)
		mov	w,indf				; get stored byte
		_bank	i2csBank
		mov	i2csDataOut,w			; load data out register
		setb	i2csDataValid			; set data valid flag so data is sent
		clrb	i2csRxFlag			; Clear the data waiting flag
		jmp	mainLoop

:being_read						; loaded data will be sent in ISR... do nothing here
		clrb	i2csRxFlag			; Clear the data waiting flag


		jmp	mainLoop




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