;*****************************************************************************************
; 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 Single I2C Multi-Master demo.
;
; Filename:	i2cmm.src
;
; Author:	Chris Fogelklou and Bruce Wilson
;		Applications Engineers
;		Scenix Semiconductor Inc.
;
; Revision:	1.06b (preliminary, for review)
;
; Part:		SX28AC datecode 9929AA
; Freq:		50Mhz
; Compiled using	Parallax SXKey28L
;			Parallax SXKey52
;			SASM
;
; Date Written: Aug 23,1999
;
; Last Revised: June 26,2000
;
; Introduction:
;   The I2C multi-master Virtual Peripheral™ has been written to enable the user to simply and
; easily operate the SX as an I2C master in multi-master mode. A multi-master I2C bus is when
; more than one master may initiate an I2C transfer on the I2C-bus. Arbitration is used to 
; determine whether or not a master is able to use the bus at any given time. 
;
; Program Description:
;    This program is a very straight forward I2C multi-master demo, which uses the I2C multi-master
; VP as a foundation. This program relies on two aspects for correct multi-master operation. The 
; first is an arbitration algorithm which executes every ISR. This arbitration algorithm is used to
; detect when a master may communicate on the bus, or if it must first wait for another master to 
; finish. The second aspect which is important for correct operation of the I2C specification on 
; the SX, is keeping a defined duration of the start-bit between masters. This ensures that a start
; may always be detected and that each SX master on the bus may synchronize their I2C clocks before
; beginning a transmission.
;   
; To run this demo on the Scenix demo board all you need to do is ensure that the
; scl and sda pins, and that the slave address is set correctly. For pin and port setup, refer to
; the documentation for the demo board you are using (pdf-document)
; To read from the onboard EEPROM on the:
; The slave address for the EEPROM is $A0 for both the boards.
; 
; If you run the programs in debug mode you will see the data contained in the EEPROM being read 
; into the last RAM bank. You can also see the text read and the slave address beeing read from 
; in a watch window. Your setup should be as follows:
;
;	 		    4.7k			
;		    VCC	x--/\/\/\--------x 	   
;	 	 	    4.7k	 |
;		    VCC	x--/\/\/\---x	 |
;				    |    |
;				    |	 | SCL	  	    
;		    SCL	------------x------------------x-------- SCL
;	       SX 1			 | SDA 	       |	     SX 2
;		    SDA	-----------------x-----------------x---- SDA
;						       |   |			
;						       |   |
;						      SCL SDA	
;						    EEPROM (24LC16B)
;
;   The pin definitions on either SX can be easily changed. However, you are restricted to use pin
; 0 and 1 on the port you have assgned for I2C, but you are allowed to switch the SDA and SCL pins.
; It is also recommended that for testing purposes leds be connected to pins RC.0, RC.1 and RC.2.  These can
; be used to verify correct operation of the multi-master system.
;
; Once both SX devices have been programmed they will read whatever data is stored in the first bank of the EEPROM 
; until a null is found, this data will be written into bank 7 of the SX. Run in debug mode to see the data.
; Since each SX will be trying to do this at the same time, arbitration is used. The SX masters
; will wait until the bus is idle, or a start bit is being put onto the bus. In Either of these cases the master
; will begin transmission. Whichever master pulls the SDA line low first will now win the arbitration procedure.  
; The master which loses arbitration will now wait a random length of time
; and then try again. This time that the master waits may need to be adjusted to suit your application. Since
; the way arbitration is detected is by the setting of a flag within the ISR, this allows the user to choose what
; action to take once loss of arbitration is detected. At present the master simply waits up to 85 I2C clock 
; cycles before retrying the transmission. The length of this wait is totally dependant on the maximum length
; of any operation to take place on the I2C-bus. It would be unwise to have a timout period of as little as 
; 50 clocks, if a master requires a 255 char string read from the EEPROM. It would be also possible to add more 
; sophistication to the restart of a transmission whereby it may simply be continued from when arbitration was
; lost.
;   Depending on the speed you wish to operate the I2C-bus at it may be necissary to change the pull-up resistor
; values. It is suggested that you refer to the I2C-bus specification pg. 40. This specificaiton is freely
; available at the Philips web site and has some good information on implementing the I2C specification.
;
; I2C Multi-Master VP requirements:
;	- 1 RAM bank for I2C MultiMaster variables
;	- 1 RAM bank for I2C MultiMaster string read from slave
;	- 300 Bytes of Program memory
;	- Up to 86 instruction cycles every ISR
;
; Interface Pins:
;
;	i2cmmPort		ra
;	scl			1
;	sda			0
;
;	i2cmmGreenLed	equ	rc.2
;	i2cmmRedLed2	equ	rc.1
;	i2cmmRedLed1	equ	rc.0
;
;
; Revision History:
;   1.0		Core I2C state machine implemented by Chris Fogelklou
;   1.01 	Documentation and code revised and updated by Bruce Wilson
;   1.02	Improved multi-master functionality and tested on the Scenix I2C/UART board
;   1.03 	Modified code for SASM assembler and SX52 compatibility
;   1.04	Detection of NACK added to mainline code to avoid continuing a transmission even
;		when the slave did not respond. This also solved a known bug, whereby loss of 
;		arbitration may sometimes not be detected due to timing between masters.
;   1.05	Added IFDEFs for SX28/52 and I2C/UART demo boards
;   1.06a	Rewritten according to vp_guide_1_02.src, compabillity to SX48/52 and  
;		ISR cycle count. By Olav Gundersen
;   1.06b	Added comments in program description, subroutines extended page moved to
;		new page (PAGE4_ORG). By Olav Gundersen
;*****************************************************************************************
;*****************************************************************************************
; 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
	watch	i2cmmRecvString,16,fstr	; Watch for simplify the debugging
	watch	i2cmmAddress,8,uhex	; 
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
multiMaster			; Enable the arbitration algorithm
id		'I2CMM'		; code identification
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			;1,2
	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: RS232 Receive
	rs232RxFlag	equ	flags0.0 ;indicates the reception of a bit from the UART

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.
i2cRecvChar	equ	$0F		; received character from EEPROM
 
;*****************************************************************************************
; 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_end


;VP_begin I2C Multi-Master
i2cmmBank	=	$	; I2CM bank
i2cmmState	ds	1	; This indicates the state that the I2CM master is currently in.
i2cmmSubState	ds	1	; This indicates the substate that the I2C master is currently in.
i2cmmPortBuf	ds	1	; This buffer holds the current state of the I2C port direction reg's
i2cmmBitCount	ds	1	; Indicates the number of bits left to process in read/write
i2cmmByte	ds	1	; The byte currently being written/read by the I2C master
i2cmmFlags	ds	1
	i2cmmNack	equ	i2cmmFlags.0	; This bit is set if the I2C master has received a NACK from the slave
	i2cmmRxFlag	equ	i2cmmFlags.1 	; Indicates that the number of bytes requested have been received
	i2cmmLostArb	equ	i2cmmFlags.2	; indicates that this master has lost arbitration
	i2cmmDoingWrite equ	i2cmmFlags.4	; Used to remember whether doing a read or write when recovering from loss of arbitration
	i2cmmInControl	equ	i2cmmFlags.5	; Indicates that this master thinks it is in control of the I2C bus
i2cmmIndex 	ds	1	; The index into the I2CM buffer, used for writing	
i2cmmNumBytes 	ds	1	; The index into the I2CM buffer, used for reading
i2cmmStrtCtr	ds	1	; Counts the number of SCL high cycles before sending a start bit onto the i2c-bus
i2cmmBuffer	=	$	; The buffer uses the last 7 registers of this bank (pre-increments, so put I2CM buffer here.)
i2cmmAddress	ds	1	; The address to read/write to.
i2cmmDataBuf	ds	6	; Data buffer
; These data registers could easily be placed into another bank if more data memory is required

;VP_end			; End cut/paste for I2C MultiMaster

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

bank2		=	$

;VP_begin I2C Multi-Master
i2cmmTimer	=	$
i2cmmTimerLow	ds	1
i2cmmTimerHigh	ds	1

VARS		=	$
i2cmmWritePtr	ds	1		; points to where data can be written
i2cmmReadAdr	ds	1		; used to point to address of slave to read
;VP_end					; End cut/paste for I2C MultiMaster

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


;VP_begin I2C Multi-Master
i2cmmRecvString	=	$	; where a string can be stored
;VP_end				; End cut/paste for I2C MultiMaster

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	%11111111		;SX18/20/28/48/52 port A latch init
RA_DDIR		equ	%11111111		;SX18/20/28/48/52 port A DDIR value
RA_LVL		equ	%11111111		;SX18/20/28/48/52 port A LVL value, set to CMOS
RA_PLP		equ	%00000000		;SX18/20/28/48/52 port A PLP value, set to pull-up
	
	;VP_begin I2C Multi-Master
	i2cmmPort	equ	ra
	scl		equ	1
	sda		equ	0
	i2cmmSclPin	equ     i2cmmPort.scl	; I2C clock
	i2cmmSdaPin	equ     i2cmmPort.sda	; I2C data I/O
	;VP_end					; End cut/paste for I2C MultiMaster
	
	
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	%00000000		;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	%11111000		;SX18/20/28/48/52 port C DDIR value, set to out on RC.0-2
RC_ST		equ	%11111111		;SX18/20/28/48/52 port C ST value
RC_LVL		equ	%11111111		;SX18/20/28/48/52 port C LVL value
RC_PLP		equ	%00000000		;SX18/20/28/48/52 port C PLP value


	;VP_begin I2C Multi-Master
	i2cmmGreenLed	equ	rc.2
	i2cmmRedLed2	equ	rc.1
	i2cmmRedLed1	equ	rc.0
	;VP_end					; End cut/paste for I2C MultiMaster


	
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	%00000000		;SX48/52 port D PLP value

RE_latch	equ	%00000000		;SX48/52 port E latch init
RE_DDIR		equ	%11111111		;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	%00000000		;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 intPeriod = (osc. frequency / interrupt frequency)
	;				  		= (50MHz / 307.2kHz)
	;						= 162.7604
	;	- Round intPeriod to the nearest integer:
	;						= 163
	;	- Now calculate your actual interrupt rate:
	;						= osc. frequency / intPeriod
	;						= 50MHz / 163
	;						= 306.748kHz
	;	- This interrupt frequency will be the timebase for all of the Virtual 
	;	  Peripherals
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

intPeriod	=	86	; Gives an interrupt period at 50MHz of (86 * (1/50000000)s) = 1.76us
				; Which gives an interrupt frequency of (1/1.76us)Hz = 581.395kHz
				; This is the double of the I2C bus frequency.  
				; intPeriod = clk_speed/(2x3 x bus_speed)
				; e.g this intPeriod gives us a bus of: 
				; busSpeed= 50Mhz/(2 x 3 x intPeriod)= 96.9kHz


;VP_begin I2C Multi-Master
i2cmmSlaveAddr	= 	$a0
;VP_end			; End cut/paste for I2C MultiMaster			

	
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; 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
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

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



;VP_begin I2C Multi-Master

i2cmmStrtDelay	=	4		; This start delay is critical in the operation
					; of the multi-master system. The value of i2cmmStrtDelay
					; specifies the number of ISR's that the scl line must
					; be held high for before a start bit will be sent.
; If all masters on the I2C bus are running at the same clock speed, simply leave the value set
; at 4. If for example one of the masters is running twice as fast as another then this value may
; need to be changed. A good rule of thumb is the slowest master must have a delay of at least 4 and the
; faster masters use multiples of that relative to their clock speeds. The principle of this is 
; simply to keep the length of the start bit the same no matter which master is using the bus. This
; makes it easy to detect and know when it is okay to use the bus. The other reason for changing this
; value is to change the relative amount of time that each master will be allocated.
; eg. run SX 1 with intPeriod = 166 (100kHz) and i2cmmStrtDelay = 4
; 	  SX 2 with intPeriod = 111 (150kHz) and i2cmmStrtDelay = 6
; Running at 100kHz with 50Mhz clock, a i2cmmStrtDelay of 3 gives a start+stop pulse of approx. 24us
; The higher the delay the less timing critical arbitration will be. For instance if the i2cmmStrtDelay is
; 3 this will work fine only as long as other masters are running at the same frequency. But if you
; increase the value to 4 you will find that operation will still continue fine even with a difference
; of 50kHz, as in the example settings above.
;   If you are not running this master in multi-master mode it is possible to decrease the start_delay
; to 1, this improves data throughput by reducing the width of the start+stop pulse to 17us.
IFNDEF	multiMaster
  i2cmmStrtDelay	=	1
ENDIF
;VP_end				; End cut/paste for I2C MultiMaster


;*****************************************************************************************
; 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
PAGE4_ORG		equ	$500	; Additional subroutines for I2CMM
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: VP Multitasker

	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; - Multi-thread the Interrupt Service Routine
	;	- Produces a FAR smaller worst-case cycle time count, and enables a larger number
	;	  of VP's to run simultaneously.  Also produces "empty" slots that future VP's 
	;	  can be copied and pasted into easily.
	;	- Determine how often your tasks need to run.  (9600bps UART can run well at a 
	;	  sampling rate of only 38400Hz, so don't run it faster than this.)
	;	- Strategically place each "module" into the threads of the ISR.  If a module 
	;	  must be run more often, just call it's module at double the rate or quadruple
	; 	  the rate, etc.…
	;	- Split complicated Virtual Peripherals into several modules, keeping the 
	;	  high-speed portions of the Virtual Peripherals as small and quick as possible, 
	;	  and run the more complicated, slower processing part of the Virtual Peripheral 
	;	  at a lower rate.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	;------------------------------------------------------------------------------
	; Virtual Peripheral Multitasker:  up to 24 individual threads, each running at
	;				   the interrupt rate/24.  Change the number of
	;				   table entries for each thread to change the
	;				   sample rate of that thread.
	;
	;	Input variable(s): isrMultiplex: variable used to choose threads
	;	Output variable(s): 		None, executes the next thread
	;	Variable(s) affected: 		isrMultiplex
	;	Flag(s) affected: 		None
	;	Program Cycles:			9 cycles (turbo mode SX28)
	;------------------------------------------------------------------------------
	_bank	isrMultiplex		;1 (or 2 in SX52)
	inc	isrMultiplex		;1 ; toggle interrupt rates
	mov	w,isrMultiplex		;1
				
				; The code between the tableBegin and tableEnd statements MUST be 
				; completely within the first half of a page.  The routines 
				; it is jumping to must be in the same page as this table.
tableStart			; Start all tables with this macro.
	add	pc,w			;3
	jmp	isrThread1		;3 = 9/10 + 52/76 = 61/86 cycles
	jmp	isrThread2		;
	jmp	isrThread1		;
	jmp	isrThread3		;
	jmp	isrThread1		;
	jmp	isrThread4		;
	jmp	isrThread1		;
	jmp	isrThread5		;
	jmp	isrThread1		;
	jmp	isrThread2		;
	jmp	isrThread1		;
	jmp	isrThread6		;
	jmp	isrThread1		;
	jmp	isrThread7		;
	jmp	isrThread1		;
	jmp	isrThread3		;
	jmp	isrThread1		;
	jmp	isrThread2		;
	jmp	isrThread1		;
	jmp	isrThread8		;
	jmp	isrThread1		;
	jmp	isrThread9		;
	jmp	isrThread1		;
	jmp	isrThread16		;
tableEnd				; End all tables with this macro.


;------------------------------------------------------------------------------
;VP: VP Multitasker
; ISR TASKS
;------------------------------------------------------------------------------
isrThread1		; Serviced at ISR rate / 2
;------------------------------------------------------------------------------
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	The sample rate of this section of code is the isr rate / 2, because it is jumped
	;	to in every 2nd entry in the VP Multitaskers table.  To increase the
	;	sample rate, put more calls to this thread in the Multitasker's jump table.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?


	;VP_begin I2C Multi-Master
	;*********************************************************************************
	; Keep running 8bit timer... 
	; Uncomment the two lines below if a 16 bit timer is needed (be aware that this 
	; adds two cycles to this ISR thread).
	;*********************************************************************************
		_bank	i2cmmTimer		;1 (or 2 in SX48/52) 
		inc	i2cmmTimerLow		;1 
;		snz				;1
;		inc	i2cmmTimerHigh		;1 
						;= 2/3 + 9 = 11/12
	;*********************************************************************************
	; I2C Master Virtual Peripheral
	;*********************************************************************************
		_bank	i2cmmBank		;1 (or 2 in SX52)
		mov	w,#i2cmmBuffer		;1 ; Switch to I2CM bank and load FSR for
		mov	fsr,w			;1 ; buffer look-up, if needed.
		call	@i2cmmISR		;3 + i2cmmISR = 17/18 + 12/26 =
						;  = 29/44

	;*********************************************************************************
	; Update I2C port with buffered port data
	;*********************************************************************************
		mov	w,m			;1 ; Save the m register.
		mov	isrTemp0,w		;1

		_mode	DDIR_W			;1 (2 for 48/52		
		mov	w,i2cmmPort		;1
		and	w,#%11110000		;1 ; Clear the data latches for SCL and SDA
		mov	i2cmmPort,w		;1		 

		_bank	i2cmmBank		;1 (or 2 in SX52)
		mov	w,i2cmmPortBuf		;1
		mov	!i2cmmPort,w		;1 ; Update the I2C port with the buffered port data

		mov	w,isrTemp0		;1 ; Restore the m register.
		mov	m,w			;1
						;= 11/13 + 29/44 = 40/57
	;*********************************************************************************
	; i2cmmArbitration
	; This code checks to see that the data written by I2CM is the same
	; as the data present on the bus.  If not, this master has lost arbitration.
	;*********************************************************************************
		IFDEF	multiMaster
i2cmmArb
		_bank	i2cmmBank			;1 (or 2 in SX52)
	; --- Check if arbitration required ---
		snb	i2cmmLostArb			;1 ; No need to check if lost arb. already lost
		jmp	i2cmmArbDone			;3
		sb	i2cmmInControl 			;1 ; Only check arbitration when master is in control
		jmp	i2cmmArbDone 			;3
  	; --- Check if data on bus matches data written by master ---
		snb	i2cmmSdaPin	 		;1 ; No master can have lost arb. when sda is high
		jmp	i2cmmArbDone	 		;3
		sb	i2cmmPortBuf.sda 		;1 ; check if buffer wants data high, if it does master loses arb.
		jmp	i2cmmArbDone 			;3
 	; --- Arbitration has been lost. The master must now wait a 'random' time before trying again --- 
:i2cmmArbLost	setb	i2cmmLostArb			;1 ; signal a loss of arbitration (just lost)
		clr	i2cmmState		 	;1 ; Put state machine into IDLE state
		clr	i2cmmSubState		 	;1
i2cmmArbDone					 	;  = 5/12 + 40/57 = 45/69
		ENDIF	
	;VP_end				; End cut/paste for I2C MultiMaster

		jmp	isrOut		;7 cycles until mainline program resumes execution
					; = 7 + 45/69 = 52/76
;------------------------------------------------------------------------------
isrThread2		; Serviced at ISR rate / 8
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread3		; Serviced at ISR rate / 12
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread4		; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread5		; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread6		; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread7		; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread8		; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread9		; Serviced at ISR rate / 24
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread10		; Never serviced
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread11		; Never serviced
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread12		; Never serviced
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread13		; Never serviced
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread14		; Never serviced
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread15		; Never serviced
;------------------------------------------------------------------------------
	jmp	isrOut			;7 cycles until mainline program resumes execution

;------------------------------------------------------------------------------
isrThread16		; Serviced at ISR rate / 24
;			; This thread must reload the isrMultiplex register 
			; since it is the last one to run in a rotation.
;------------------------------------------------------------------------------
	_bank	isrMultiplex
	mov	w,#255			; Reload isrMultiplex so isrThread1 will be run
	mov	isrMultiplex,w		; on next interrupt.
	jmp	isrOut
;------------------------------------------------------------------------------





;------------------------------------------------------------------------------
isrOut
;------------------------------------------------------------------------------
	mov	w,#-intPeriod	;1	; return and add -intPeriod 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_begin I2C Multi-Master
;*****************************************************************************************
; I2C Master Interrupt Service Routines
;*****************************************************************************************
; I2C Virtual Peripheral...


	;*********************************************************************************
	; Function: i2cmmISR
	; 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.  
	;  i2cmmIdle
	; 	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
	;  i2cmmStart
	; 	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.
	;  i2cmmStartWrite
	;	This state performs some pre-processing which allows the i2cmmWrite 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.
	;  i2cmmWrite
	;	This state writes the data in i2cmmByte to the I2C bus
	;  i2cmmGetAck
	;	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 i2cmmFlags 
	;	register is loaded to indicate that a NACK has occurred.
	;  i2cmmWriteRepeat
	;	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 i2cmmStartWrite
	;	and sends the next byte.
	;  i2cmmStop
	;	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.
	;  i2cmmStartRead
	;	This state simply loads the contents of the i2cmmAddress register into the i2cmmByte
	;	register and sets up the i2cmmWrite state to output the address of the slave to read.
	;  i2cmmReadData	
	; 	This state prepares the I2CM read routine so it can read from the slave device.  It
	;	initializes the bit count, etc.
	;  i2cmmRead
	; 	This state read 8 bits of data from the slave device.
	;  i2cmmStoreByte
	; 	This state stores the byte just read into the buffer.
	;  i2cmmSendAck
	; 	This state sends an ack if there is data left to write, and a NACK if there is
	;	no data left to write.
	;*********************************************************************************
i2cmmISR

		mov	w,i2cmmState		;1

						; The code between the tableStart and tableEnd statements
						; MUST be completely within the first half of a page. The routines
	tableStart				; it is jumping to must be in the same page as this table. 
		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
		;*************************************************************************
	i2cmmIdleLoc	=	$
		jmp	i2cmmIdle		;3 ;If I2C_state = 0, I2C is idle

		;*************************************************************************
		; States for I2C Master while writing
		;*************************************************************************
	i2cmmWriteLoc	=	$
		jmp	i2cmmStart		;3 ;Make SDA go low while SCL is high
		jmp	i2cmmStartWrite		;3 ;Load a byte from the buffer and prepare to send. 
		jmp	i2cmmWrite		;3 ;Write it.
		jmp	i2cmmGetAck		;3 ;Get an ACK signal
		jmp	i2cmmWriteRepeat	;3 ;Check to see if we have finished sending 
						   ;(if buffer read index = buffer write index)
	i2cmmStopLoc	=	$
		jmp	i2cmmStop		;3 ;If write_repeat determines we're finished,
						   ;then send stop

		;*************************************************************************
		; States for I2C Master while reading
		;*************************************************************************
	i2cmmReadLoc	=	$		   ;Load this state into state machine if we
						   ;are starting to read
		jmp	i2cmmStart		;3 ;
		jmp	i2cmmStartRead		;3 ;Write address used for reading
		jmp	i2cmmWrite 		;3 ;(writing address)
		jmp	i2cmmGetAck		;3 ;
	i2cmmReadRptLoc =	$
		jmp	i2cmmReadData		;3 ;
		jmp	i2cmmRead		;3 ;Keep doing this until done
		jmp	i2cmmStoreByte		;3 ;
		jmp	i2cmmSendAck		;3 ;
		jmp	i2cmmStop		;3 ;
	tableEnd				; = 7 (from i2cmmISR to here)
	;*********************************************************************************
	; State: i2cmmIdle
	; 	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
	;*********************************************************************************
i2cmmIdle	setb	i2cmmPortBuf.sda	;1
		setb	i2cmmPortBuf.scl	;1
		retp				;3 = 5 + 7 = 12

	;*********************************************************************************
	; State: i2cmmStart
	; 	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.
	;*********************************************************************************
i2cmmStart	mov	w,i2cmmSubState		;1
						; The code between the tableStart and tableEnd statements
						; MUST be completely within the first half of a page. The routines
	tableStart				; it is jumping to must be in the same page as this table. 
		add	pc,w			;3
		jmp	:state1			;3
		jmp	:state2			;3
		jmp	:state3			;3
		jmp	:state4			;3
		jmp	:state5			;3 = 7 + 7 = 14
	tableEnd

:state1		setb	i2cmmPortBuf.scl	;1
		setb	i2cmmInControl		;1
		setb	i2cmmPortBuf.sda	;1
		inc	i2cmmSubState		;1
		mov	w,#i2cmmStrtDelay 	;1 
		mov	i2cmmStrtCtr,w 		;1 
		retp				;3 = 9 + 14 = 23

;	--- WAITING FOR STOP/START OR IDLE SCL ---
:state2		sb	i2cmmSclPin		;1 
		clr	i2cmmSubState		;1 
		decsz	i2cmmStrtCtr		;1
		retp				;3 = 6 + 14 = 20
		inc	i2cmmSubState		;1
		retp				;3 = 8 + 14 = 22
;	--- FINISHED CLOCK SYNCRONIZATION PERIOD ---

:state3		clrb	i2cmmPortBuf.sda	;1
		inc	i2cmmSubState		;1
		retp				;3 = 5 + 14 = 19

:state4		clrb	i2cmmPortBuf.scl	;1
		inc	i2cmmSubState		;1
		retp				;3 = 5 + 14 = 19

:state5		setb	i2cmmPortBuf.sda	;1
		clr	i2cmmSubState		;1
		inc	i2cmmState		;1
		retp				;3 = 6 + 14 = 20

	;*********************************************************************************
	; State: i2cmmStartWrite	
	;	This state performs some pre-processing which allows the i2cmmWrite 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.
	;*********************************************************************************
i2cmmStartWrite
		inc	i2cmmIndex		;1
		mov	w,i2cmmIndex		;1
		add	fsr,w			;1
		mov	w,indf			;1

		mov	i2cmmByte,w		;1
		mov	w,#8			;1
		mov	i2cmmBitCount,w		;1
		inc	i2cmmState		;1
		retp				;3 = 11 + 7 = 18

	;*********************************************************************************
	; State: i2cmmWrite
	;	This state writes the data in i2cmmByte to the I2C bus
	;*********************************************************************************
i2cmmWrite	mov	w,i2cmmSubState		;1

						; The code between the tableStart and tableEnd statements
						; MUST be completely within the first half of a page. The routines
		tableStart			; it is jumping to must be in the same page as this table. 
		add	pc,w			;3
		jmp	:state1			;3
		jmp	:state2			;3
		jmp	:state3			;3 = 7 + 7 = 14
		tableEnd

:state1		setb	i2cmmInControl		;1
		setb	i2cmmPortBuf.sda	;1
		rl	i2cmmByte		;1
		sb	c			;1
		clrb	i2cmmPortBuf.sda	;1
		inc	i2cmmSubState		;1
		retp				;3 = 9 + 14 = 23

:state2		setb	i2cmmPortBuf.scl	;1
		inc	i2cmmSubState		;1
		retp				;3 = 5 + 14 = 19

:state3		clrb	i2cmmPortBuf.scl	;1
		dec	i2cmmBitCount		;1
		snb	z			;1
		jmp	:done			;3
		clr	i2cmmSubState		;1
		retp				;3 = 8 + 14 = 22

:done		clr	i2cmmSubState		;1
		inc	i2cmmState		;1
		clrb	i2cmmInControl		;1
		retp				;3 = 12 + 14 = 26

	;*********************************************************************************
	; State: i2cmmGetAck
	;	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 i2cmmFlags 
	;	register is loaded to indicate that a NACK has occurred.
	;*********************************************************************************
i2cmmGetAck	mov	w,i2cmmSubState		;1
						; The code between the tableStart and tableEnd statements
						; MUST be completely within the first half of a page. The routines
	tableStart				; it is jumping to must be in the same page as this table. 
		add	pc,w			;3
		jmp	:state1			;3
		jmp	:state2			;3
		jmp	:state3			;3
		jmp	:state4			;3 = 7 + 7 = 14
	tableEnd

:state1		setb	i2cmmPortBuf.sda	;1
		inc	i2cmmSubState		;1
		retp				;3 = 5 + 14 = 19

:state2		setb	i2cmmPortBuf.scl	;1
		inc	i2cmmSubState		;1
		retp				;3 = 5 + 14 = 19

:state3		sb	i2cmmSdaPin		;1
		inc	i2cmmSubState		;1
		sb	i2cmmSdaPin		;1
		retp				;3 = 6 + 14 = 20 ;if ack,
		mov	w,#(i2cmmStopLoc-i2cmmIdleLoc);1 
		mov	i2cmmState,w		;1
		clr	i2cmmSubState		;1 ;send a stop and indicate that this didn't work.
		setb	i2cmmNack		;1 ;set i2cmmNack to show that this did not go through.
		retp				;3 = 11 + 14 = 25

:state4		clrb	i2cmmPortBuf.scl	;1
		clr	i2cmmSubState		;1
		inc	i2cmmState		;1 ;move on to next state
		retp				;3 = 6 + 14 = 20


	;*********************************************************************************
	; State: i2cmmWriteRepeat
	;	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 
	;	i2cmmStartWrite and sends the next byte.
	;*********************************************************************************
i2cmmWriteRepeat
		mov	w,i2cmmNumBytes		;1 ;test the read index to see if it is = to write index.
		xor	w,i2cmmIndex		;1 ;if it is, then we have finished writing the buffer via. I2C.
		snb	z			;1
		jmp	:i2cmmWriteDone		;3
		dec	i2cmmState		;1 ;back to GetAck
		dec	i2cmmState		;1 ;back to Write
		dec	i2cmmState		;1 ;back to StartWrite
		retp				;3 = 10 + 7 = 17

:i2cmmWriteDone
		inc	i2cmmState		;1 ;move on to next state (stop)
		retp				;3 = 10 + 7 = 17 ;and start the stop bit	

	;*********************************************************************************
	; State: i2cmmStop
	;	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.
	;*********************************************************************************
i2cmmStop	mov	w,i2cmmSubState		;1
						; The code between the tableStart and tableEnd statements
						; MUST be completely within the first half of a page. The routines
	tableStart				; it is jumping to must be in the same page as this table. 
					
		add	pc,w			;3
		jmp	:state1			;3
		jmp	:state2			;3
		jmp	:state3			;3 = 7 + 7 = 14
	tableEnd

:state1		setb	i2cmmInControl		;1
		clrb	i2cmmPortBuf.sda	;1
		inc	i2cmmSubState		;1
		retp				;3 = 6 + 14 = 20

:state2		setb	i2cmmPortBuf.scl	;1
		inc	i2cmmSubState		;1
		retp				;3 = 5 + 14 = 19

:state3		setb	i2cmmPortBuf.sda	;1
		clr	i2cmmSubState		;1 
		clr	i2cmmState		;1 ;put the state machine in Idle
		clr	i2cmmNumBytes		;1
		mov	w,#$ff			;1
		mov	i2cmmIndex,w		;1
		clrb	i2cmmInControl		;1
		retp				;3 = 10 + 14 = 24 

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


;*****************************************************************************************
; Jump table
;*****************************************************************************************
i2cmmSendByte		jmp	i2cmmSendByte_		;3
i2cmmSendBytes		jmp	i2cmmSendBytes_		;3
i2cmmGetByte		jmp	i2cmmGetByte_		;3
i2cmmGetBytes		jmp	i2cmmGetBytes_		;3
i2cmmWaitNotBusy	jmp	i2cmmWaitNotBusy_	;3
i2cmmInit		jmp	i2cmmInit_		;3
;*****************************************************************************************
; I2C Master Subroutines
;*****************************************************************************************

	;*********************************************************************************
	; Function: i2cmmStartRead
	;	This state simply loads the contents of the i2cmmAddress register into the 
	;	i2cmmByte register and sets up the i2cmmWrite state to output the address 
	;	of the slave to read.
	;*********************************************************************************
i2cmmStartRead
		mov	w,i2cmmAddress
		mov	i2cmmByte,w
		inc	i2cmmIndex
		mov	w,#8
		mov	i2cmmBitCount,w
		inc	i2cmmState
		retp

	;*********************************************************************************
	; Function: i2cmmReadData	
	; 	This function prepares the I2CM read routine so it can read from the slave 
	;	device. It initializes the bit count, etc.
	;*********************************************************************************
i2cmmReadData
		mov	w,#8
		mov	i2cmmBitCount,w
		inc	i2cmmState
	;*************************************************************************
	; Read 8 bits of data from the slave device.
	;*************************************************************************
i2cmmRead	mov	w,i2cmmSubState
						; The code between the tableStart and tableEnd statements
						; MUST be completely within the first half of a page. The routines
	tableStart				; it is jumping to must be in the same page as this table. 
		add	pc,w
		jmp	:state1
		jmp	:state2
		jmp	:state3
	tableEnd

:state1		clrb	i2cmmInControl
		setb	i2cmmPortBuf.scl
		inc	i2cmmSubState
		retp

:state2		sb	i2cmmSclPin
		retp
		stc
		sb	i2cmmSdaPin
		clc
		rl	i2cmmByte
		inc	i2cmmSubState
		retp

:state3		clrb	i2cmmPortBuf.scl
		clr	i2cmmSubState
		dec	i2cmmBitCount
		sb	z	
		retp
		inc	i2cmmState
		retp

	;*********************************************************************************
	; Function: i2cmmStoreByte
	; 	This function stores the byte just read into the buffer.
	;*********************************************************************************
i2cmmStoreByte
		mov	w,i2cmmByte
		mov	isrTemp0,w
		inc	i2cmmIndex
		mov	w,i2cmmIndex
		add	fsr,w
		mov	w,isrTemp0
		mov	indf,w
		inc	i2cmmState
		setb	i2cmmRxFlag
		retp

	;*********************************************************************************
	; Function: i2cmmSendAck
	; 	This function sends an ack if there is data left to write, and a NACK if 
	;	there is no data left to write.
	;*********************************************************************************
i2cmmSendAck
		mov	w,i2cmmSubState
						; The code between the tableStart and tableEnd statements
						; MUST be completely within the first half of a page. The routines
		tableStart			; it is jumping to must be in the same page as this table. 
		add	pc,w
		jmp	:state1
		jmp	:state2
		jmp	:state3
		jmp	:state4	
	tableEnd

:state1		setb	i2cmmInControl
		clrb	i2cmmPortBuf.sda
		mov	w,i2cmmIndex
		xor	w,i2cmmNumBytes
		snz
		setb	i2cmmPortBuf.sda
		inc	i2cmmSubState
		retp

:state2		setb	i2cmmPortBuf.scl	;Pulse the clock
		inc	i2cmmSubState
		retp

:state3		sb	i2cmmSclPin
		retp
		clrb	i2cmmPortBuf.scl
		inc	i2cmmSubState
		retp

:state4		setb	i2cmmPortBuf.sda
		clr	i2cmmSubState
		mov	w,i2cmmIndex
		xor	w,i2cmmNumBytes
		snz
		jmp	:done
		mov	w,#(i2cmmReadRptLoc - i2cmmIdleLoc) ;back to read 
		mov	i2cmmState,w
		retp

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

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

	;*********************************************************************************
	; Function: i2cmmSendByte
	; 	This routine sets up the I2CM state machine to write the byte of data in
	; 	the i2cmmDataBuf register.  Before entering this routine, make sure that
	;	the I2CM state machine is in its idle state (use the i2cmmWaitNotBusy 
	;	subroutine) and that the i2cmmAddress is loaded with the address of the
	;	slave that this byte is going to, and that i2cmmDataBuf is loaded with the
	;	data to send.
	;*********************************************************************************
i2cmmSendByte_
		mov	w,#1			;There is one byte of data in the buffer
		mov	i2cmmNumBytes,w
	;*********************************************************************************
	; Function: i2cmmSendBytes
	; 	This routine sets up the I2CM state machine to write the bytes of data in
	; 	the i2cmmDataBuf registers.  Before entering this routine, 
	;	make sure that the I2CM state machine is in its idle state (use the 
	;	i2cmmWaitNotBusy subroutine) and that i2cmmAddress is loaded with the 
	;	address of the slave that this byte is going to, that i2cmmDataBuf
	;	are loaded with the data to send, and that the i2cmmNumBytes register
	;	is loaded with the number of data bytes to send.
	;*********************************************************************************
i2cmmSendBytes_
		setb	i2cmmDoingWrite
		mov	w,#%11111110
		and	i2cmmAddress,w
		clrb	i2cmmNack
		mov	w,#(i2cmmWriteLoc-i2cmmIdleLoc)
		mov	i2cmmState,w
		retp
	;*********************************************************************************
	; Function: i2cmmWaitNotBusy
	; 	This routine polls the i2cmmState 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.
	;*********************************************************************************
i2cmmWaitNotBusy_
:wait_loop	test	i2cmmState
		sb	z		; wait until I2C is idle
		jmp	:wait_loop
		mov	w,#1		; return (1) if we are idle because a NACK was
		snb	i2cmmNack	; received.  Return (0) if we are idle because
		retp			; this was successful
		clr	w		
		retp

	;*********************************************************************************
	; Function: i2cmmGetByte
	;	This routine gets one byte of data from the slave at address i2cmmAddress.
	;	before calling this routine, ensure that the I2C Master State Machine is
	;	in its idle state (use the i2cmmWaitNotBusy subroutine) and that the
	;	i2cmmAddress register is loaded with a valid address.  The routine returns
	;	with the byte received in the w register and in the i2cmmDataBuf register.
	;*********************************************************************************
i2cmmGetByte_
		clrb	i2cmmDoingWrite
:nack		mov	w,#%00000001
		or	i2cmmAddress,w
		mov	w,#1
		mov	i2cmmNumBytes,w
		clrb	i2cmmNack
		mov	w,#(i2cmmReadLoc-i2cmmIdleLoc)
		mov	i2cmmState,w
		call	i2cmmWaitNotBusy
		and	w,#$ff
		sz
		jmp	:nack
		clrb	i2cmmRxFlag
		mov	w,i2cmmDataBuf
		retp

	;*********************************************************************************
	; Remaining functions (i2cmmGetBytes and i2cmmInit) is moved to PAGE4_ORG
	; because of insufficient space in this page...
	;*********************************************************************************


	;VP_end					; End cut/paste for I2C MultiMaster




;*****************************************************************************************
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
	jmp	$	; This instruction will cause an assembler error if the source code before
			; the org statement inadvertantly crosses a page boundary.


	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; 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
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?









;*****************************************************************************************
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	PAGE4_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.  
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?

	;VP_begin I2C Multi-Master
		
	;*********************************************************************************
	; FUNCTIONS moved from SUBROUTINES because of insufficient space
	;*********************************************************************************

	;*********************************************************************************
	; Function: i2cmmGetBytes
	;	This routine gets i2cmmNumBytes of data from the slave at address
	;	i2cmmAddress.  Before calling this routine, ensure that the I2CM state
	;	machine is idle by using the i2cmmWaitNotBusy subroutine, that i2cmmAddress
	;	register contains the address of the slave to be read from, and that the
	;	i2cmmNumBytes register is loaded with the number of bytes of data to receive.
	;	The received bytes will be contained in registers i2cmmDataBuf.
	;*********************************************************************************
i2cmmGetBytes_
		clrb	i2cmmDoingWrite
		mov	w,#%00000001
		or	i2cmmAddress,w
		clrb	i2cmmNack
		clrb	i2cmmRxFlag
		mov	w,#(i2cmmReadLoc-i2cmmIdleLoc)
		mov	i2cmmState,w
		retp

	;*********************************************************************************
	; Function: i2cmmInit
	;	This subroutine should be called on startup.  It initializes the registers
	;	which are critical to the operation of the I2CM state machine.
	;*********************************************************************************
i2cmmInit_
		mov     w,#%10111111		;Set RB in/out directions 
		mov     i2cmmPortBuf,w
		mov	w,#$ff
		mov	i2cmmNumBytes,w
		mov	w,#$ff
		mov	i2cmmIndex,w
		retp
	;VP_end			; End cut/paste for I2C MultiMaster








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

		_bank	i2cmmBank
		call	@i2cmmInit
		;VP_end			; End cut/paste for I2C MultiMaster
	;------------------------------------------------------------------------------
	; 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 prescaler assigned to WDT.
		jmp	@mainLoop



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

; MAIN PROGRAM CODE 

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



mainLoop



	;VP_begin I2C Multi-Master

;*****************************************************************************************
; MAIN PROGRAM CODE
; This can be changed as you like to suit your own application
; At present data is read from an EEPROM device and the data stored into the last RAM bank
;*****************************************************************************************

	;*********************************************************************************
	; Initialize variables...
	;*********************************************************************************
		_bank	i2cmmBank
		mov	w,#i2cmmSlaveAddr		; set address of slave to read. 
		mov	i2cmmAddress,w
		_bank	VARS				; clear read and write address pointers
		clr	i2cmmReadAdr
		clr	i2cmmWritePtr
	;*********************************************************************************
	; Read a character from EEPROM 'i2cmmAddress', at location 'i2cmmReadAdr'
	;*********************************************************************************
i2cRead		_bank	VARS
		mov	w,i2cmmReadAdr
		_bank	i2cmmBank
		mov	i2cmmDataBuf,w			; load send register with data address		
		call	@i2cmmWaitNotBusy		; wait for I2CM state machine to enter idle state
		call	@i2cmmSendByte			; send 'want to read' and bank addess to i2c slave
		call	@i2cmmWaitNotBusy		; wait for I2CM state machine to enter idle state
		snb	i2cmmLostArb			; if arbitration was lost then wait a while and try again
		jmp	iveLostArb
		snb	i2cmmNack			; Check for no acknowledge
		jmp	i2cmmGotNack			; Set lights for no Ack and try again

		call	@i2cmmGetByte			; Gets the byte from slave and returns it in w
		snb	i2cmmLostArb			; if arbitration was lost wait a while and then try again
		jmp	iveLostArb
		snb	i2cmmNack			; Check for no acknowledge
		jmp	i2cmmGotNack			; Set lights for no ack and try again
		or	w,#$00				; check for null character, indicating end of string
		snb	z				; null char, end of string...restart
		jmp	mainLoop
		mov	i2cRecvChar,w			; save received character
;		cjne	i2cRecvChar,#'A',i2cmmBadData	; Check data received matches data saved in EEPROM
							; for example store all 'A's in the EEPROM and confirm
							; read data is an 'A'.

	;*********************************************************************************
	; Store the characters received in bank 7...
	;*********************************************************************************
i2cmmStoreChar	clrb	i2cmmRedLed1			
		setb	i2cmmGreenLed			; indicate data received okay
		_bank	VARS
		mov	w,#i2cmmRecvString
		add	w,i2cmmWritePtr
		mov	fsr,w
		mov	w,i2cRecvChar			; save revcd data to address pointed to by fsr
		mov	indf,w
		_bank	VARS
		inc	i2cmmReadAdr			; set to read next address
		inc	i2cmmWritePtr			; increment offset of where to save data
		snz
		jmp	mainLoop				; finished reading entire EEPROM bank... Restart
		jmp	i2cRead			; read another character from eeprom

	;*********************************************************************************
	; This Master will wait here when arbitration is lost
	;*********************************************************************************
iveLostArb	clrb	i2cmmGreenLed
		snb	i2cmmRedLed2			; This master has lost arbitration
		jmp	i2cmmLedOn
i2cmmLedOff	setb	i2cmmRedLed2			; Toggle green LED
		jmp	i2cmmWaitHere
i2cmmLedOn	clrb	i2cmmRedLed2
i2cmmWaitHere	_bank	i2cmmTimer			; Wait a 'random' time before trying again
		test	i2cmmTimerLow
		sz
		jmp	i2cmmWaitHere
		_bank	i2cmmBank
		clrb	i2cmmLostArb
		jmp	mainLoop			; Retry I2C operation

	;*********************************************************************************
	; ERROR CONDITIONS:
	;*********************************************************************************
i2cmmGotNack	setb	i2cmmGreenLed
		setb	i2cmmRedLed1
		setb	i2cmmRedLed2
		jmp	mainLoop			; Did not get an acknowledge from slave
i2cmmBadData	setb	i2cmmRedLed1
		clrb	i2cmmRedLed2
		clrb	i2cmmGreenLed
		mov	w,i2cRecvChar			; Got incorrect data from EEPROM
		jmp	$				;  should never occur



	;VP_end						; End cut/paste for I2C MultiMaster


		jmp	mainLoop
;*****************************************************************************************
END		;End of program code
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************



