please dont rip this site

Scenix Lib IO OSI2 I2C I2C_DUAL_MULTI_MASTER.SRC

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





file: /Techref/scenix/lib/io/osi2/i2c/i2c_dual_multi_master.src, 84KB, , updated: 2001/10/18 10:39, local time: 2024/11/12 11:35,
TOP NEW HELP FIND: 
18.224.43.50:LOG IN
©2024 PLEASE DON'T RIP! THIS SITE CLOSES OCT 28, 2024 SO LONG AND THANKS FOR ALL THE FISH!

 ©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions?
Please DO link to this page! Digg it! / MAKE!

<A HREF="http://techref.massmind.org/techref/scenix/lib/io/osi2/i2c/i2c_dual_multi_master.src"> scenix lib io osi2 i2c i2c_dual_multi_master</A>

Did you find what you needed?

 

Welcome to massmind.org!

 
Quick, Easy and CHEAP! RCL-1 RS232 Level Converter in a DB9 backshell
Ashley Roll has put together a really nice little unit here. Leave off the MAX232 and keep these handy for the few times you need true RS232!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  .