Scenix Lib IO OSI2 I2C I2C_SLAVE.SRC

; *****************************************************************************************
; Copyright © [3/29/2000] Scenix Semiconductor, Inc. All rights reserved.
;
; Scenix Semiconductor, Inc. assumes no responsibility or liability for
; the use of this [product, application, software, any of these products].
; Scenix Semiconductor conveys no license, implicitly or otherwise, under
; any intellectual property rights.
; Information contained in this publication regarding (e.g.: application,
; implementation) and the like is intended through suggestion only and may
; be superseded by updates. Scenix Semiconductor makes no representation
; or warranties with respect to the accuracy or use of these information,
; or infringement of patents arising from such use or otherwise.
;*****************************************************************************************
; 
; Filename:	i2cs.src
;
; Author:	Chris Fogelklou and Bruce Wilson
;		Applications Engineer
;		Scenix Semiconductor Inc.
;
; Revision:	1.04a (preliminary, for review)
;
; Part:		XXXXXXXXXX
; Freq:		50MHz
;
; Compiled using: XXXXXXXXXXXXX
;
;
; Date Written: Jan 15, 2000
;
; Last Revised: March 29, 2000
;
; Introduction:
; 	By using the I2C slave Virtual Peripheral™, provides any SX device with the interface
; 	required to operate as an I2C slave. The way in which this peripheral has been written, 
; 	was with the intent to give the user simple access subroutines to call and need not
; 	worry about the inner workings of the peripheral code. 
; 	To implement the I2C slave Virtual Peripheral™ requires: 
;	216 bytes of program memory 
;	12 bytes of RAM.
;	2 I/O pins for the I2C bus (SDA and SCL)
;	Timer interrupt running every 2.6us for 100kHz-bus speed
;
; Program Description:
; 	The mainline code of this VP has been written to enable this slave to interface to the 
; 	SX I2C master VP (I2CM.src). It also gives a a starting point for other programs. 
; 	The mainline code enables a string to to be read out of memory by a master I2C device
; 	just as data would be read out of an I2C EEPROM device. 
; 	The I2C slave Virtual Peripheral™ uses a state machine to change between all the required
; 	states within any I2C operation. This state machine operates solely within the timer
; 	interrupt service routine. It is possible to place the function I2CS_ISR in any page of 
; 	program memory. Since the state machine can be executed asynchronously, it is also 
; 	possible to run the code from the mainline if required. If this is done no calls from the
; 	interrupt service routine are required leaving it free for other operations.
; 	The state machine will execute automatically when a master addresses this slave 
; 	set by slave_address. Flags will be set so that the mainline code can check when data has
;	 been received or when data has been sent out from the slave. By checking these flags at
; 	strategic points within your code will enable the SX to run as a very efficient I2C slave
; 	device. 
; 	In order to test this program simply connect two SX devices as shown below configuring
; 	the two I2C lines to the I/O pins of your choice.
;
;		 		 4.7k			
;			 VCC	x--/\/\/\--------x 	 
;	 	 		 4.7k	 |
;			 VCC	x--/\/\/\---x	 |
;					 | |
;					 |	 | SCL	 	 
;		 MASTER RA0	------------x--------------------------- RB0 SLAVE
;		 SX 				 | SDA 	 	 SX 2
;	 (I2CM.SRC) RA1	-----------------x---------------------- RB1 (I2CS.SRC)
;						 			
; 	The pins chosen above are default and allow this code to run directly on the Scenix I2C/UART
; 	demo board with no modification. Run the master SX in debug mode and you will see the ASCII
; 	values for the string 'I2C SLAVE' stored in bank 7. This is the data which was read from this
; 	slave device.
;
; Interface Pins:
;
;		Put hardware interface pins here.
;	
; Revision History:
; 1.0	 	Core I2C state machine implemented by Chris Fogelklou
; 1.01 	Checked current I2C slave code and released slave VP with code that enables it to
;	 	be read by the master I2C VP at 100kHz.
;	 	Documentation and code revised by Bruce Wilson.	
; 1.02 	Code updated to run on SASM and SX52
; 1.03 	Code tested on Scenix Eval and I2C/UART boards on both SX52 and SX28 parts.;
; 1.04a 	Rewritten according to VP guide 1.02, incompabillity with 48/52 fixed (portdirection register) 
;		Selectable Scl/Sda pins (RA)
; 
;*****************************************************************************************
;*****************************************************************************************
; Target SX
; Uncomment one of the following lines to choose the SX18AC, SX20AC, SX28AC, SX48BD,
; or SX52BD. 
;*****************************************************************************************
;SX18_20
SX28
;SX48_52
;*****************************************************************************************
; Assembler Used
; Uncomment the following line if using the Parallax SX-Key assembler. SASM assembler
; enabled by default.
;*****************************************************************************************
;Sx_Key			; Uncomment this line to assemble this source code using the
			; Parallax Assembler
	;*********************************************************************************
	; Assembler directives:
	;	high speed external osc, turbo mode, 8-level stack, and extended option reg.
	;
	;	SX18/20/28 - 4 pages of program memory and 8 banks of RAM enabled by default.
	;	SX48/52 - 8 pages of program memory and 16 banks of RAM enabled by default.
	; 
	;*********************************************************************************
IFDEF SX_Key 				;SX-Key Directives
 IFDEF SX18_20				;SX18AC or SX20AC device directives for SX-Key
		device	SX18L,oschs2,turbo,stackx_optionx
 ENDIF
 IFDEF SX28				;SX28AC device directives for SX-Key		
		device	SX28L,oschs2,turbo,stackx_optionx
 ENDIF
 IFDEF SX48_52				;SX48/52/BD device directives for SX-Key
		device	oschs2
 ENDIF
		freq	50_000_000
ELSE 					;SASM Directives
 IFDEF SX18_20				;SX18AC or SX20AC device directives for SASM
		device	SX18,oschs2,turbo,stackx,optionx
 ENDIF
 IFDEF SX28				;SX28AC device directives for SASM
		device	SX28,oschs2,turbo,stackx,optionx
 ENDIF
 IFDEF SX48_52				;SX48BD or SX52BD device directives for SASM
		device	SX52,oschs2 
 ENDIF
ENDIF
		id	'I2CM'		;
		reset	resetEntry	; set reset vector
;*****************************************************************************************
; Macros
;*****************************************************************************************
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;
	; 	To support compatibility between source code written for the SX28 and the SX52,
	;	use macros.
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	;*********************************************************************************
	; Macro: _bank
	; Sets the bank appropriately for all revisions of SX.
	;
	; This is required since the bank instruction has only a 3-bit operand, it cannot
	; be used to access all 16 banks of the SX48/52. For this reason FSR.7 needs to be
	; set appropriately, depending on the bank address being accessed. Use of this macro
	; switches banks correctly, regardless of the part being compiled for.
	;
	; Instead of using the bank instruction to switch between banks, use _bank instead.
	; 
	;*********************************************************************************
_bank	macro	1
	bank	1円
	IFDEF SX48_52
	 IF 1円 & %10000000		;SX48BD and SX52BD (production release) bank instruction 
		setb	fsr.7		;modifies FSR bits 4,5 and 6. FSR.7 needs to be set by software.
	 ELSE
		clrb	fsr.7
	 ENDIF
	ENDIF
	endm
	;*****************************************************************************************
	; Macros for SX28/52 Compatibility
	;*****************************************************************************************
	;*********************************************************************************
	; Macro: _mode
	; Sets the MODE register appropriately for all revisions of SX.
	;
	; This is required since the MODE (or MOV M,#) instruction has only a 4-bit operand. 
	; The SX18/20/28AC use only 4 bits of the MODE register, however the SX48/52BD have 
	; the added ability of reading or writing some of the MODE registers, and therefore use
	; 5-bits of the MODE register. The MOV M,W instruction modifies all 8-bits of the 
	; MODE register, so this instruction must be used on the SX48/52BD to make sure the MODE
	; register is written with the correct value. This macro fixes this.
	;
	; So, instead of using the MODE or MOV M,# instructions to load the M register, use
	; _mode instead.
	; 
	;*********************************************************************************
_mode	macro	1
	IFDEF SX48_52
 expand
		mov	w,#1円		;loads the M register correctly for the SX48BD and SX52BD
		mov	m,w
 noexpand
	ELSE
 expand
		mov	m,#1円		;loads the M register correctly for the SX18AC, SX20AC
 noexpand					;and SX28AC
	ENDIF
	endm
	;*****************************************************************************************
	; INCP/DECP macros for incrementing/decrementing pointers to RAM
	; used to compensate for incompatibilities between SX28 and SX52
	;*****************************************************************************************
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;
	; 	To support compatibility between source code written for the SX28 and the SX52,
	;	use macros. This macro compensates for the fact that RAM banks are contiguous in
	;	the SX52, but separated by 0x20 in the SX18/28.
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
INCP	macro	1
		inc	1円
	IFNDEF	SX48_52
		setb	1円.4		; If SX18 or SX28, keep bit 4 of the pointer = 1
	ENDIF				; to jump from 1ドルf 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ドル --> 1ドルf --> 1ドルf
	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	=	0ドルA
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 0ドルA on SX48/52 and 08ドル on SX18/20/28.
;*****************************************************************************************
		org global_org
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;
	; 	Use only these defined label types for global registers. If an extra temporary 
	;	register is required, adhere to these label types. For instance, if two temporary
	;	registers are required for the Interrupt Service Routine, use the label isrTemp1
	;	for it.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
flags0		equ	global_org + 0 ; stores bit-wise operators like flags 
						; and function-enabling bits (semaphores)
	;VP_begin I2C Slave
	i2csEventFlag	equ	flags0.1
	i2csStopFound	equ	flags0.2
	i2csRxFlag	equ	flags0.3
	;VP_end
flags1		equ	global_org + 1 ; stores bit-wise operators like flags 
						; and function-enabling bits (semaphores)
localTemp0	equ	global_org + 2	; temporary storage register
						; Used by first level of nesting
						; Never guaranteed to maintain data
localTemp1	equ	global_org + 3	; temporary storage register	
						; Used by second level of nesting
						; or when a routine needs more than one 
						; temporary global register.	
localTemp2	equ	global_org + 4	; temporary storage register
						; Used by third level of nesting or by
						; main loop routines that need a loop 
						; counter, etc.
isrTemp0	equ	global_org + 5	; Interrupt Service Routine's temp register. 
						; Don't use this register in the mainline.
;*****************************************************************************************
; RAM Bank Register definitions
;*****************************************************************************************
	;*********************************************************************************
	; Bank 0
	;*********************************************************************************
		org bank0_org
bank0		=	$
	
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	- Avoid using bank0 in programs written for SX48/52.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	;*********************************************************************************
	; Bank 1
	;*********************************************************************************
		org bank1_org
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;
	;	Tip 1:
	;	Indicate which Virtual Peripherals a portion of source code or declaration belongs
	;	to with a 
	;			";VP: VirtualPeripheralName"
	;	 comment.
	;
	;	Tip 2:
	;	All RAM location declaration names should be
	; 	- left justified
	;	- less than 2 tabs in length
	;	- written in hungarian notation
	;	- prefixed by a truncated version of the Virtual Peripheral's name
	;	
	;	Examples:
	;
	;	;VP: RS232 Transmit
	;
	;	rs232TxBank	= $ ;RS232 Transmit bank
	;
	; 	rs232TxHigh	ds 1 ;hi byte to transmit
	;	rs232TxLow	ds 1 ;low byte to transmit
	;	rs232TxCount	ds 1 ;number of bits sent
	;	rs232TxDivide	ds 1 ;xmit timing (/16) counter
	;	rs232TxString	ds	1			;the address of the string to be sent
	;	rs232TxByte	ds	1			;semi-temporary serial register
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;VP: ISR Multithreader
isrMultiplex	ds	1				; The isrMultiplex register is used to switch to a new
							; execution thread on each pass of the ISR.
;VP_begin I2C Slave
; Used by state machine
i2csBank		= 	$			; I2C Slave bank
i2csState		ds	1			; This indicates the state that the I2C slave is currently in
i2csSubState		ds	1			; This indicates the substate the I2C slave is currently in
i2csPortBuf		ds	1			; This buffer holds the current state of the I2C port direction reg's
i2csAddress		ds	1			; This byte holds the address which the I2C slave will respond to
i2csBitCount		ds	1			; Indicate sthe number of bits left to process in read/write
i2csByte		ds	1			; The byte currently being written/read by the I2C master
i2csPastPres		ds	1			; The last_state of the I2C port (to be compared with the present state)
i2csPresSCL		equ	i2csPastPres.0		; Present state of SCL
i2csPresSDA		equ	i2csPastPres.1		; Present state of SDA
i2csPastSCL		equ	i2csPastPres.2		; The state of SCL on last interrupt
i2csPastSDA		equ	i2csPastPres.3		; The state of SDA on last interrupt
; User Accessible
i2csDataIn		ds	1			; The saved data byte received from the master
i2csDataOut		ds	1			; A byte of data to be read by the master
i2csFlags		ds	1
i2csDataValid		equ	i2csFlags.0		; Lets the I2C slave know that there is valid data stored 
							; in the i2csDataOut
i2csDataNeeded		equ	i2csFlags.1		; This flag is set if the I2C master has tried to do a 
							; multiple read on this slave and there is no valid data
							; present in the i2csDataOut register.
							; The slave will hold the clock line low until the i2csDataValid 
							; flag is set, indicating valid data.
i2csBeingReadFlag	equ	i2csFlags.2		; True when command received from the master is to send data.
							; When clear slave will be receving
;VP_end
	;*********************************************************************************
	; Bank 2
	;*********************************************************************************
		org bank2_org
bank2		=	$
	;*********************************************************************************
	; Bank 3
	;*********************************************************************************
		org bank3_org
bank3		=	$
	;*********************************************************************************
	; Bank 4
	;*********************************************************************************
		org bank4_org
bank4		=	$
	;*********************************************************************************
	; Bank 5
	;*********************************************************************************
		org bank5_org
bank5		=	$
	;*********************************************************************************
	; Bank 6
	;*********************************************************************************
		org bank6_org
bank6		=	$
	;*********************************************************************************
	; Bank 7
	;*********************************************************************************
		org bank7_org
bank7		=	$
i2csStringBank	= 	$
IFDEF SX48_52
	;*********************************************************************************
	; Bank 8
	;*********************************************************************************
		org	80ドル	;bank 8 address on SX52
bank8		=	$
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	- This extra memory is not available in the SX18/28, so don't use it for Virtual
	;	 Peripherals written for both platforms.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	;*********************************************************************************
	; Bank 9
	;*********************************************************************************
		org	90ドル	;bank 9 address on SX52
bank9		=	$
	;*********************************************************************************
	; Bank A
	;*********************************************************************************
		org	$A0	;bank A address on SX52
bankA		=	$
	;*********************************************************************************
	; Bank B
	;*********************************************************************************
		org	$B0	;bank B address on SX52
bankB		=	$
	;*********************************************************************************
	; Bank C
	;*********************************************************************************
		org	$C0	;bank C address on SX52
bankC		=	$
	;*********************************************************************************
	; Bank D
	;*********************************************************************************
		org	$D0	;bank D address on SX52
bankD		=	$
	;*********************************************************************************
	; Bank E
	;*********************************************************************************
		org	$E0	;bank E address on SX52
bankE		=	$
	;*********************************************************************************
	; Bank F
	;*********************************************************************************
		org	$F0	;bank F address on SX52
bankF		=	$
ENDIF
		
	;*********************************************************************************
	; Pin Definitions: 
	;*********************************************************************************
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	- Store all initialization constants for the I/O in the same area, so
	;	 pins can be easily moved around.
	;	- Pin definitions should follow the same format guidelines as RAM definitions
	;		- Left justified
	;		- Hungarian Notation
	;		- Less that 2 tabs in length
	;		- Indicate the Virtual Peripheral the pin is used for
	;	- Only use symbolic names to access a pin/port in the source code.
	;	- Example:
	;		; VP: RS232 Transmit
	;			rs232TxPin	equ	ra.3
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
RA_latch	equ	%00001011		;SX18/20/28/48/52 port A latch init
RA_DDIR		equ	%11110111		;SX18/20/28/48/52 port A DDIR value
RA_LVL		equ	%11111111		;SX18/20/28/48/52 port A LVL value
RA_PLP		equ	%11111111		;SX18/20/28/48/52 port A PLP value
	;VP_begin I2C Slave
	i2csPort	equ	ra
	i2csScl		equ	0
	i2csSda		equ	1			; 
	i2csSclPin	equ	i2csPort.i2csScl	; SCL
	i2csSdaPin	equ	i2csPort.i2csSda	; SDA
	;VP_end
	
	
RB_latch	equ	%11111111		;SX18/20/28/48/52 port B latch init
RB_DDIR		equ	%11111111		;SX18/20/28/48/52 port B DDIR value
RB_ST		equ	%11111111		;SX18/20/28/48/52 port B ST value
RB_LVL		equ	%00000000		;SX18/20/28/48/52 port B LVL value
RB_PLP		equ	%11111111		;SX18/20/28/48/52 port B PLP value
RC_latch	equ	%11111111		;SX18/20/28/48/52 port C latch init
RC_DDIR		equ	%01111111		;SX18/20/28/48/52 port C DDIR value
RC_ST		equ	%11111111		;SX18/20/28/48/52 port C ST value
RC_LVL		equ	%00000000		;SX18/20/28/48/52 port C LVL value
RC_PLP		equ	%01111111		;SX18/20/28/48/52 port C PLP value
IFDEF SX48_52	;SX48BD/52BD Port initialization values
RD_latch	equ	%00000000		;SX48/52 port D latch init
RD_DDIR		equ	%11111111		;SX48/52 port D DDIR value
RD_ST		equ	%11111111		;SX48/52 port D ST value
RD_LVL		equ	%00000000		;SX48/52 port D LVL value
RD_PLP		equ	%11111111		;SX48/52 port D PLP value
RE_latch	equ	%00000000		;SX48/52 port E latch init
RE_DDIR		equ	%01001111		;SX48/52 port E DDIR value
RE_ST		equ	%11111111		;SX48/52 port E ST value
RE_LVL		equ	%00000000		;SX48/52 port E LVL value
RE_PLP		equ	%11111111		;SX48/52 port E PLP value
ENDIF
;*****************************************************************************************
; Program constants
;*****************************************************************************************
;-------------------------------------------------------------------------------------
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;	To calculate the interrupt period in cycles:
	;	- First, choose the desired interrupt frequency
	;		- Should be a multiple of each Virtual Peripherals sampling frequency.
	;		- Example: 19200kHz UART sampling rate * 16 = 307.200kHz
	;	- Next, choose the desired oscillator frequency.
	;		- 50MHz, for example.
	;	- Perform the calculation period = (osc. frequency / interrupt frequency)
	;				 		= (50MHz / 307.2kHz)
	;						= 162.7604
	;	- Round int_period to the nearest integer:
	;						= 163
	;	- Now calculate your actual interrupt rate:
	;						= osc. frequency / int_period
	;						= 50MHz / 163
	;						= 306.748kHz
	;	- This interrupt frequency will be the timebase for all of the Virtual 
	;	 Peripherals
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
int_period	=	125	; Gives an interrupt period at 50MHz of (125 * (1/50000000)s) = 2.5us
				; Which gives an interrupt frequency of (1/2.5us)Hz = 400kHz
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;	- Include all calculations for Virtual Peripheral constants for any sample 
	;	 rate.
	;	- Relate all Virtual Peripheral constants to the sample rate of the Virtual
	;	 Peripheral.
	;	- Example:
	;		; VP: 5ms Timer
	;		TIMER_DIV_CONST	equ 192	; This constant = timer sample rate/200Hz = 192
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;VP_begin I2C Slave
i2csSlaveAddress	equ	40ドル			; Address of this slave
;VP_end 
;-------------------------------------------------------------------------------------
IFDEF SX48_52
	;*********************************************************************************
	; SX48BD/52BD Mode addresses
	; *On SX48BD/52BD, most registers addressed via mode are read and write, with the
	; exception of CMP and WKPND which do an exchange with W.
	;*********************************************************************************
; Timer (read) addresses
TCPL_R		equ	00ドル		;Read Timer Capture register low byte
TCPH_R		equ	01ドル		;Read Timer Capture register high byte
TR2CML_R	equ	02ドル		;Read Timer R2 low byte
TR2CMH_R	equ	03ドル		;Read Timer R2 high byte
TR1CML_R	equ	04ドル		;Read Timer R1 low byte
TR1CMH_R	equ	05ドル 		;Read Timer R1 high byte
TCNTB_R		equ	06ドル		;Read Timer control register B
TCNTA_R		equ	07ドル		;Read Timer control register A
; Exchange addresses
CMP		equ	08ドル		;Exchange Comparator enable/status register with W
WKPND		equ	09ドル		;Exchange MIWU/RB Interrupts pending with W
; Port setup (read) addresses
WKED_R		equ	0ドルA		;Read MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_R		equ	0ドルB		;Read MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_R		equ	0ドルC		;Read Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_R		equ	0ドルD		;Read Port Level setup, 0 = CMOS, 1 = TTL
PLP_R		equ	0ドルE		;Read Port Weak Pullup setup, 0 = enabled, 1 = disabled
DDIR_R		equ	0ドルF		;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	1ドルA		;Write MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_W		equ	1ドルB		;Write MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_W		equ	1ドルC		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_W		equ	1ドルD		;Write Port Level setup, 0 = CMOS, 1 = TTL
PLP_W		equ	1ドルE		;Write Port Weak Pullup setup, 0 = enabled, 1 = disabled
DDIR_W		equ	1ドルF		;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	0ドルA		;Write MIWU/RB Interrupt edge setup, 0 = falling, 1 = rising
WKEN_W		equ	0ドルB		;Write MIWU/RB Interrupt edge setup, 0 = enabled, 1 = disabled
ST_W		equ	0ドルC		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
LVL_W		equ	0ドルD		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
PLP_W		equ	0ドルE		;Write Port Schmitt Trigger setup, 0 = enabled, 1 = disabled
DDIR_W		equ	0ドルF		;Write Port Direction
ENDIF
;*****************************************************************************************
; Program memory ORG defines
;*****************************************************************************************
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	- Place a table at the top of the source with the starting addresses of all of
	;	 the components of the program.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
INTERRUPT_ORG		equ	0ドル	; Interrupt must always start at location zero
RESET_ENTRY_ORG		equ	1ドルFB	; The program will jump here on reset.
SUBROUTINES_ORG		equ	200ドル	; The subroutines are in this location
STRINGS_ORG		equ	300ドル	; The strings are in location 300ドル
PAGE3_ORG		equ	400ドル	; Page 3 is empty
MAIN_PROGRAM_ORG	equ	600ドル	; The main program is in the last page of program memory.
;****************************** Beginning of program space *******************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
	org	INTERRUPT_ORG			; First location in program memory.
;*****************************************************************************************
;------------------------------------------------------------------------------
; Interrupt Service Routine
;------------------------------------------------------------------------------
; Note: The interrupt code must always originate at address 0ドル.
;
; Interrupt Frequency = (Cycle Frequency / -(retiw value)) For example:
; With a retiw value of -163 and an oscillator frequency of 50MHz, this
; code runs every 3.26us.
;------------------------------------------------------------------------------
ISR							;3	The interrupt service routine...
;VP_begin I2C Slave
		_bank	i2csBank			; 1
		clrb	c				; 1 ; Save the current state and past state of the I2C bus
		snb	i2csSdaPin			; 1
		setb	c					; 1
		rl	i2csPastPres			; 1	
		clrb	c					; 1
		snb	i2csSclPin			; 1
		setb	c					; 1
		rl	i2csPastPres			; 1
		page	i2csGetStartStop		; 1 ; Check the new state to see if we've received a start or stop
		call	i2csGetStartStop		; 3 = 13 + 10/14 = 23/27
		page	i2csISR				; 1 ; Call I2C Slave ISR
		call	i2csISR				; 3 + 12/27 (i2csISR length) = 39/58
i2csDone
		mov	w,m				; 1 ; Save the m register.
		mov	isrTemp0,w			; 1
		_mode	DDIR_W				; 1, 2 ; 2 cycles for SX48_52 (_mode macro)
		clrb	i2csSclPin			; 1 ; Clear the data latches for SCL and SDA
		clrb	i2csSdaPin 			; 1
		
		_bank	i2csBank			; 1
		mov	w,i2csPortBuf			; 1 ; Update the I2C port with the buffered port data		
		mov	!i2csPort,w			; 1
		mov	w,isrTemp0			; 1 ; Restore the m register
		mov	m,w				; 1 = 10 (11 for SX48/52)
		jmp	isrOut				;7 cycles until mainline program resumes execution
							;= 17/18 + 39/58 = 56/76
;VP_end
;------------------------------------------------------------------------------
isrOut
;------------------------------------------------------------------------------
	mov	w,#-int_period	;1	; return and add -int_period to the RTCC
	retiw			;3	; using the retiw instruction.
;------------------------------------------------------------------------------
;*****************************************************************************************
org	RESET_ENTRY_ORG
;*****************************************************************************************
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;	The main program operation should be easy to find, so place it at the end of the 
	;	program code. This means that if the first page is used for anything other than 
	;	main program source code, a reset_entry must be placed in the first page, along 
	;	with a 'page' instruction and a 'jump' instruction to the beginning of the 
	;	main program.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
;------------------------------------------------------------------------------
resetEntry					; Program starts here on power-up
	page	_resetEntry
	jmp	_resetEntry
;------------------------------------------------------------------------------
;*****************************************************************************************
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;	ORG statements should use predefined labels rather than literal values.
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
org	SUBROUTINES_ORG
;*****************************************************************************************
; Subroutines
;*****************************************************************************************
	;----------------------------------------------------------
	; VP: I2C Slave
	;
	; Function: i2csGetStartStop
	;
	; Input: i2csGetStartStop
	; Output: i2csState
	;
	; 	This subroutine checks the i2csPastPres register for the presence of a start
	; 	or stop condition. It takes the values present in the i2csPastPres register
	; 	and checks them against what would be in the register if a start or stop condition
	; 	had occured. This subroutine should be called on every pass of the ISR, because
	; 	it runs independantly to the rest of the slave code and re-initializes the state
	; 	machine, regardless of its last state, on the receipt of these conditions.
	;----------------------------------------------------------
i2csGetStartStop
		mov	w,i2csPastPres			; 1
		and	w,#0ドルf				; 1
		xor	w,#%00001101			; 1 ; Compare with a start condition
		
		snb	z				; 1
		jmp	:start				; 3
		xor	w,#%00001010			; 1 ; Compare XOR of start and stop with
		sb	z				; 1 ; what would be present if a stop was present
		
		retp					; 3 = 10
:stop		clr	i2csState			; 1 ; If stop, then make I2C slave idle
		clr	i2csSubState			; 1
		setb	i2csStopFound			; 1
		retp					; 3 = 14
:start		mov	w,#1				; 1 ; If start, then start receving the address
		mov	i2csState,w			; 1
		retp					; 3 = 12
	;----------------------------------------------------------
	; VP: I2C Slave
	;
	; Function: i2csISR
	;
	; Input: i2csState
	; Output
	;
	;
	; States:
	;
	; I2C Slave Interrupt-Driven State Machine
	;	 ----------------------------------------
	;
	;	This is the main interrupt service routine for the I2C slave interrupt
	;	service routine. It is a state machine, allowing semi-straightline 
	;	code to operate on successive calls. The current state of this state
	;	machine is stored in the registers i2csState and i2csSubState. These
	;	are the discrete states of this state machine...
	;
	; i2csIdle
	;	i2csIdle is the state used when no start condition has occurred
	;	and the device has not been addressed. The I2C slave will sit in this
	;	state until the state is changed by the independant subroutine 
	;	i2csGetStartStop. 
	;
	; i2csWaitForSclLow
	;	This state simply waits until SCL goes low. It is entered once
	;	i2csGetStartStop encounters a start condition. Once SCL goes low,
	;	the state is incremented
	;
	; i2csGetAddress
	;	This state prepares the i2csReadByte routine to get 8-bits of data. 
	;	It increments the state to i2csReadByte before exiting.
	;
	; i2csReadByte
	;	This state reads a byte of data. i2csBitCount needs to be loaded with
	;	#8 before entering this state, or it won't know to increment to the next
	;	state after 8 bits of data have been loaded.
	;
	; i2csProcessAddress
	;	This state simply performs a quick calculation to figure out if this slave
	;	was just addressed. If it was not, then it changes the state back to 
	;	i2csIdle. If this slave was addressed, then this state prepares to either
	;	read data or write data, depending on the bit 1 of the first byte received.
	;
	; i2csSendAck
	;	This state outputs an ACK pulse, to tell the master that data was received
	;	correctly. It pulls SDA low while SCL is pulsed high and low by the master.
	;
	; i2csReadData
	;	This state prepares i2csReadByte to receive 8 bits of data. It increments
	;	the state before exiting.
	;
	; i2csMakeIdle
	;	This state puts the I2C slave back into idle mode.
	;
	; i2csProcessData
	;	This state processes a byte of data which was just received. It moves the 
	;	byte that was just received into the i2csDataIn register, and sets the 
	;	I2CS_event_flag to indicate an I2CS event and sets the i2csRxFlag to 
	;	indicate that a byte of data was received by the slave.
	;
	; i2csSendData
	;	This state prepares the i2csWriteByte state to send the 8-bits of data
	;	in the i2csDataOut register.
	;
	; i2csWriteByte
	;	This state outputs a byte of data, clocked out by the SCL pin. It must be
	;	pre-prepared to send out a byte by having the I2CS_byte register loaded with
	;	valid data and having the i2csBitCount register loaded with #8.
	;
	; i2csGetAck
	;	This state gets an ACK from the I2C master. If an ACK is received, this state
	;	will try to send another byte of data from the i2csDataOut register. If no
	;	ACK is received, the slave will be put back into its idle state.
	;----------------------------------------------------------
i2csISR
		mov	w,i2csState			; 1
		add	PC,w				; 3 ; Add the state to the program counter
tableStart
							 ; and go to the state in the jump table.
; Idle states
i2csIdleLoc		= $
		jmp	i2csIdle			; 3 ; If i2csState = 0, I2C is idle
		jmp	i2csWaitForSclLow		; 3 ; Wait for SCL line to go low after start condition
		jmp	i2csGetAddress			; 3 ; Get the address
		jmp	i2csReadByte
		jmp	i2csProcessAddress		; 3 ; Check to see if the device was addressed.
		
; Read states
i2csBeingRead		= $
		jmp	i2csSendAck			; 3
i2csBeingReadAgain	= $
		jmp	i2csSendData			; 3 ; Set up to send a byte
		jmp	i2csWriteByte			; 3 ; Send the byte, clocked by SCL
		jmp	i2csGetAck			; 3 ; If we receive an Ack, then we are being read again.
		jmp	i2csWaitingForData		; 3 ; If there is no valid data, wait for it.
; Write states
i2csBeingWritten	= $
		jmp	i2csSendAck			; 3 ; Send an ACK
		jmp	i2csReadData			; 3
		jmp	i2csReadByte			; 3
		jmp	i2csProcessData			; 3
		jmp	i2csSendAck			; 3
		jmp	i2csMakeIdle			; 3 
tableEnd
	;----------------------------------------------------------
	; State: i2csIdle
	;	i2csIdle is the state used when no start condition has occurred
	;	and the device has not been addressed. The I2C slave will sit in this
	;	state until the state is changed by the independant subroutine 
	;	i2csGetStartStop.
	;----------------------------------------------------------
i2csIdle
		setb	i2csPortBuf.i2csScl		; 1
		setb	i2csPortBuf.i2csSda		; 1
		retp					; 3 = 5 + 7 = 12
	;----------------------------------------------------------
	; State: i2csWaitForSclLow
	;	This state simply waits until SCL goes low. It is entered once
	;	i2csGetStartStop encounters a start condition. Once SCL goes low,
	;	the state is incremented
	;----------------------------------------------------------
i2csWaitForSclLow
		snb	i2csPresScl			; 1
		retp					; 3 = 4 + 7 = 11
		clrb	i2csPortBuf.i2csScl		; 1
		inc	i2csState			; 1
		retp					; 3 = 7 + 7 = 14
	;----------------------------------------------------------
	; State: i2csGetAddress
	;	This state prepares the i2csReadByte routine to get 8-bits of data. 
	;	It increments the state to i2csReadByte before exiting.
	;----------------------------------------------------------
i2csGetAddress
		mov	w,#8				; 1
		mov	i2csBitCount,w			; 1
		inc	i2csState			; 1
		retp					; 3 = 6 + 7 = 13
	;----------------------------------------------------------
	; State: i2csReadByte
	;	This state reads a byte of data. i2csBitCount needs to be loaded with
	;	#8 before entering this state, or it won't know to increment to the next
	;	state after 8 bits of data have been loaded.
	;----------------------------------------------------------
i2csReadByte
		mov	w,i2csSubState			; 1
		add	pc,w				; 3
		skip					; 1 goto state 2 
		jmp	:state2				; 3
:state1							; 1 ; Wait until SCL goes high
		setb	i2csPortBuf.i2csScl		; 1
		sb	i2csPresScl			; 1
		retp					; 3 = 12 + 7 = 19
		inc	i2csSubState			; 1 ; If SCL is high,rotate in the bit
		clrb	c					; 1
		snb	i2csPresSda			; 1
		setb	c					; 1
		rl	i2csByte			; 1
		retp					; 3 = 18 + 7 = 25
:state2
		snb	i2csPresScl			; 1
		retp					; 3 = 11 + 7 = 18
		clrb	i2csPortBuf.i2csScl		; 1
		clr	i2csSubState			; 1
		dec	i2csBitCount			; 1
		sb	z					; 1
		retp					; 3 = 16 + 7 = 23
		inc	i2csState			; 1
		retp					; 3 = 18 + 7 = 25
							; stay in this i2csState until all 8 bits are received
	;----------------------------------------------------------
	; State: i2csProcessAddress
	;	This state simply performs a quick calculation to figure out if this slave
	;	was just addressed. If it was not, then it changes the state back to 
	;	I2CSIdle. If this slave was addressed, then this state prepares to either
	;	read data or write data, depending on the bit 1 of the first byte received.	
	;----------------------------------------------------------
i2csProcessAddress
		mov	w,i2csByte				; 1 ; Get the received I2C byte
		xor	w,i2csAddress				; 1 ; Compare it with the address of this slave
		and	w,#$FE					; 1 ; And out the R/!W bit
		snb	z						; 1
		jmp	:slave_addressed			; 3 ; If it is equal, send an ACK
		clr	i2csState				; 1 ; If the device wasn't addressed, then go idle
		retp						; 3 = 9 + 7 = 16
:slave_addressed						;=7
		clrb	i2csPortBuf.i2csScl		 	; 1 
		snb	i2csByte.0				; 1 ; If bit zero of the incoming byte is a 0, 
							 	 ; then go to the being written state
		jmp	:being_read				; 3 ; go to the being_read state
		mov	w,#(i2csBeingWritten-i2csIdleLoc) 	; 1
		mov	i2csState,w				; 1
		clrb	i2csBeingReadFlag			; 1 ; Clear the slave being read flag
		retp						; 3 = 16 + 7 = 23
:being_read	sb	i2csDataValid				; 1
		jmp	i2csMakeIdle				; 3 = 16
		mov	w,#(i2csBeingRead-i2csIdleLoc)		; 1
		mov	i2csState,w				; 1
		setb	i2csBeingReadFlag			; 1 ; Set the slave being read flag
		retp						; 3 = 20 + 7 = 27
	;----------------------------------------------------------
	; State: i2csSendAck
	;	This state outputs an ACK pulse, to tell the master that data was received
	;	correctly. It pulls SDA low while SCL is pulsed high and low by the master.	
	;----------------------------------------------------------
i2csSendAck
		mov	w,i2csSubState			; 1
		add	PC,w				; 3
		jmp	:state1				; 3
		jmp	:state2				; 3
		jmp	:state3				; 3
:state1							
		clrb	i2csPortBuf.i2csSda		; 1 ; Clear SDA to set up ACK
		inc	i2csSubState			; 1
		retp					; 3 = 12 + 7 = 19
:state2
		setb	i2csPortBuf.i2csScl		; 1 ; Allow the clock to go high
		sb	i2csPresScl			; 1 ; Check to see if the clock is high
		retp					; 3 = 12 + 7 = 19
		inc	i2csSubState			; 1
		retp					; 3 = 14 + 7 = 21
:state3
		snb	i2csPresScl			; 1
		retp					; 3 = 11 + 7 = 18
		clrb	i2csPortBuf.i2csScl		; 1
		setb	i2csPortBuf.i2csSda		; 1
		clr	i2csSubState			; 1
		inc	i2csState			; 1
		retp					; 3 = 16 + 7 = 23
	;----------------------------------------------------------
	; State: i2csReadData
	;	This state prepares I2CS_read_byte to receive 8 bits of data. It increments
	;	the state before exiting.
	;----------------------------------------------------------
i2csReadData
		mov	w,#8				; 1
		mov	i2csBitCount,w			; 1
		inc	i2csState			; 1
		retp					; 3 = 6 + 7 = 13
	;----------------------------------------------------------
	; State: i2csMakeIdle
	;	This state puts the I2C slave back into idle mode.
	;----------------------------------------------------------
i2csMakeIdle
		clr	i2csState			; 1
		clr	i2csSubState			; 1
		setb	i2csPortBuf.i2csScl		; 1
		setb	i2csPortBuf.i2csSda		; 1
		retp					; 3 = 7 + 7/16 = 14/23 (ISR/i2csProcessAddress) 
	;----------------------------------------------------------
	; State: i2csProcessData
	;	This state processes a byte of data which was just received. It moves the 
	;	byte that was just received into the I2CS_data_in register, and sets the 
	;	I2CS_event_flag to indicate an I2CS event and sets the I2CS_rx_flag to 
	;	indicate that a byte of data was received by the slave.
	;----------------------------------------------------------
i2csProcessData
		mov	w,i2csByte			; 1
		mov	i2csDataIn,w			; 1
		setb	i2csRxFlag			; 1
		setb	i2csEventFlag			; 1 ; Indicate that a byte has been received with the global event flag
		inc	i2csState			; 1
		retp					; 3 = 8 + 7 = 15
	
	;----------------------------------------------------------
	; State: i2csSendData
	;	This state prepares the i2csWriteByte state to send the 8-bits of data
	;	in the i2csDataOut register.
	;----------------------------------------------------------
i2csSendData
		inc	i2csState			; 1
		clr	i2csSubState			; 1
		clrb	i2csDataValid			; 1
		mov	w,i2csDataOut			; 1
		mov	i2csByte,w			; 1
		mov	w,#8				; 1
		mov	i2csBitCount,w			; 1
		retp					; 3 = 10 + 7 = 17
	;----------------------------------------------------------
	; State: i2csWriteByte
	;	This state outputs a byte of data, clocked out by the SCL pin. It must be
	;	pre-prepared to send out a byte by having the i2csByte register loaded with
	;	valid data and having the i2csBitCount register loaded with #8.
	;----------------------------------------------------------
i2csWriteByte
		mov	w,i2csSubState			; 1
		add	PC,w				; 3
		jmp	:state1				; 3
		jmp	:state2				; 3
		jmp	:state3				; 3
:state1
		rl	i2csByte			; 1
		setb	i2csPortBuf.i2csSda		; 1
		sb	c				; 1
		clrb	i2csPortBuf.i2csSda		; 1
		inc	i2csSubState			; 1
		retp					; 3 = 15 + 7 = 22
:state2
		setb	i2csPortBuf.i2csScl		; 1 allow SCL to go high, indicating slave is ready
		snb	i2csPresScl			; 1 ; If SCL is not yet high, don't proceed to next state
		inc	i2csSubState			; 1
		retp					; 3 = 13 + 7 = 20
:state3
		snb	i2csPresScl			; 1
		retp					; 3 = 11 + 7 = 18
		clr	i2csSubState			; 1
		clrb	i2csPortBuf.i2csScl		; 1 ; Drag SCL low again to indicate slave is processing
		setb	i2csPortBuf.i2csSda		; 1 ; release SDA line again
		dec 	i2csBitCount			; 1
		sb	z				; 1 ; once we hav done all 8 bits, go to next state
		retp					; 3 = 17 + 7 = 24
		inc	i2csState			; 1
		retp					; 3 = 19 + 7 = 26
	;----------------------------------------------------------
	; State: i2csGetAck
	;	This state gets an ACK from the I2C master. If an ACK is received, this state
	;	will try to send another byte of data from the I2CSDataOut register. If no
	;	ACK is received, the slave will be put back into its idle state.
	;----------------------------------------------------------
i2csGetAck
		mov	w,i2csSubState			; 1
		add	PC,w				; 3
		jmp	:state1				; 3
		jmp	:state2				; 3
:state1
		setb	i2csPortBuf.i2csScl		; 1
		sb	i2csPresScl			; 1
		retp					; 3 = 12 + 7 = 19
		snb	i2csPresSda			; 1
		jmp	:NACK				; 3
		inc	i2csSubState			; 1
		retp					; 3 = 16 + 7 = 23
:NACK
		clr	i2csSubState			; 1
		clr	i2csState			; 1
		retp					; 3 = 19 + 7 = 26
:state2		
		snb	i2csPresScl			; 1
		retp					; 3 = 11 + 7 = 18
		clrb	i2csPortBuf.i2csScl		; 1
		clr	i2csSubState			; 1
		inc	i2csState			; 1
		retp					; 3 = 15 + 7 = 22
	;----------------------------------------------------------
	; State: i2csWaitingForData
	;	This state waits for the mainline routine to put some valid data in the
	;	I2CDataOut register. This state is only reached if the master has 
	;	indicated it wants more data by sending an ACK after the last byte.
	;----------------------------------------------------------
i2csWaitingForData
		snb	i2csDataValid				; 1
		jmp	:data_valid				; 3
		setb	i2csDataNeeded				; 1
		setb	i2csEventFlag				; 1
		retp						; 3 = 7 + 7 = 14
:data_valid
		mov	w,#(i2csBeingReadAgain-i2csIdleLoc)	; 1
		mov	i2csState,w				; 1
		retp						; 3 = 9 + 7 = 16
	;----------------------------------------------------------
	; VP: I2C Slave
	;
	; Function: i2csInit
	;
	; 	This subroutine should be called on initialization of the program. It
	; 	initializes the variables which are critical to the operation of the 
	; 	I2CS slave state machine. 
	;
	; Memory Location Dependency:
	;	This subroutine will only work as long as i2cString lies within the 
	;	same page as the STRINGS_ORG label. In this VP, that range happens 
	;	to be a half a page in size (300ドル - 3ドルFF) due to the fact that 
	;	STRINGS_ORG is equated to 300ドル. 	
	;
	; localTemp registers destroyed by this subroutine:
	; 	localTemp0, localTemp1
	;----------------------------------------------------------
i2csInit
		_bank	i2csBank
		mov	w,#%10111111				; Set RB in/out directions
		mov	i2csPortBuf,w
		mov	w,#$FF					; Let part know last states have all been high
		mov	i2csPastPres,w
		mov	w,#i2csSlaveAddress
		mov	i2csAddress,w
		; Copy string into RAM
		
		_bank	i2csStringBank
		clr	localTemp0				; Clear string offset counter			
:loop
		_mode	STRINGS_ORG>>8				; String base src address
		mov	w,#i2csString				; String offset address
		add	w,localTemp0				
		
		iread						; Read Program memory
		or	w,#00ドル					; String is 0 terminated
		snb	z
		jmp	:out
		
		mov	localTemp1,w				; Save string char temporary
		mov	w,#i2csStringBank
		add	w,localTemp0				; String offset address
		mov	fsr,w
		mov	w,localTemp1				
		mov	indf,w					; Write char to RAM
		inc	localTemp0				; Increment string offset counter
		jmp	:loop
		
:out
		retp
;*****************************************************************************************
org	STRINGS_ORG		; This label defines where strings are kept in program space.
;*****************************************************************************************
;------------------------------------------------------------------------------
; Put String Data Here
;------------------------------------------------------------------------------
; Example:
;_hello dw 13,10,'UART Demo',0
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	; 	- Routines that use location-dependant data, such as in example below, should
	;	 use a LABEL rather than a literal value as their input. Example:
	;	 instead of
	;		mov m,#3		 ; move upper nybble of address of strings into m
	;	 use
	;		mov	m,#STRINGS_ORG>>8; move upper nybble of address of strings into m
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; VP: I2C Slave
i2csString		dw	'I2C SLAVE',0 ;This string must lie within the same page of 
						;program memory as the STRINGS_ORG label in order
						;for the function i2csInit to operate correctly.
;*****************************************************************************************
org	PAGE3_ORG
;*****************************************************************************************
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;	To ensure that several Virtual Peripherals, when pasted together, do not cross 
	;	a page boundary without the integrator's knowledge, put an ORG statement and one 
	;	instruction at every page boundary. This will generate an error if a pasted 
	;	subroutine moves another subroutine to a page boundary. 
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	jmp	$	; This instruction will cause an assembler error if the source code before
			; the org statement inadvertantly crosses a page boundary.
;*****************************************************************************************
org	MAIN_PROGRAM_ORG
;*****************************************************************************************
;------------------------------------------------------------------------------
; RESET VECTOR 
;------------------------------------------------------------------------------
	;------------------------------------------------------------------------------
	; Program execution begins here on power-up or after a reset
	;------------------------------------------------------------------------------
_resetEntry		
	;------------------------------------------------------------------------------
	; Initialize all port configuration
	;------------------------------------------------------------------------------
		_mode	ST_W			;point MODE to write ST register
		mov w,#RB_ST 	;Setup RB Schmitt Trigger, 0 = enabled, 1 = disabled
		mov	!rb,w		
		mov w,#RC_ST 	;Setup RC Schmitt Trigger, 0 = enabled, 1 = disabled
		mov	!rc,w	
IFDEF SX48_52
		mov w,#RD_ST 	;Setup RD Schmitt Trigger, 0 = enabled, 1 = disabled
		mov	!rd,w		
		mov w,#RE_ST 	;Setup RE Schmitt Trigger, 0 = enabled, 1 = disabled
		mov	!re,w		
ENDIF
		_mode	LVL_W			;point MODE to write LVL register
		mov w,#RA_LVL 	;Setup RA CMOS or TTL levels, 0 = TTL, 1 = CMOS
		mov	!ra,w		 
		mov w,#RB_LVL 	;Setup RB CMOS or TTL levels, 0 = TTL, 1 = CMOS
		mov	!rb,w		
		mov w,#RC_LVL 	;Setup RC CMOS or TTL levels, 0 = TTL, 1 = CMOS
		mov	!rc,w	
IFDEF SX48_52
		mov w,#RD_LVL 	;Setup RD CMOS or TTL levels, 0 = TTL, 1 = CMOS
		mov	!rd,w		
		mov w,#RE_LVL 	;Setup RE CMOS or TTL levels, 0 = TTL, 1 = CMOS
		mov	!re,w		
ENDIF
		_mode	PLP_W			;point MODE to write PLP register
		mov w,#RA_PLP 	;Setup RA Weak Pull-up, 0 = enabled, 1 = disabled
		mov	!ra,w		 
		mov w,#RB_PLP 	;Setup RB Weak Pull-up, 0 = enabled, 1 = disabled
		mov	!rb,w		
		mov w,#RC_PLP 	;Setup RC Weak Pull-up, 0 = enabled, 1 = disabled
		mov	!rc,w	
IFDEF SX48_52
		mov w,#RD_PLP 	;Setup RD Weak Pull-up, 0 = enabled, 1 = disabled
		mov	!rd,w		
		mov w,#RE_PLP 	;Setup RE Weak Pull-up, 0 = enabled, 1 = disabled
		mov	!re,w		
ENDIF
		_mode	DDIR_W			;point MODE to write DDIR register
		mov	w,#RA_DDIR		;Setup RA Direction register, 0 = output, 1 = input		
		mov	!ra,w	
		mov	w,#RB_DDIR		;Setup RB Direction register, 0 = output, 1 = input
		mov	!rb,w			
		mov	w,#RC_DDIR		;Setup RC Direction register, 0 = output, 1 = input
		mov	!rc,w			
IFDEF SX48_52
		mov	w,#RD_DDIR		;Setup RD Direction register, 0 = output, 1 = input
		mov	!rd,w			
		mov	w,#RE_DDIR		;Setup RE Direction register, 0 = output, 1 = input
		mov	!re,w			
ENDIF
		mov w,#RA_latch 	;Initialize RA data latch
		mov ra,w		
		mov w,#RB_latch 	;Initialize RB data latch
		mov rb,w		
		mov w,#RC_latch 	;Initialize RC data latch
		mov rc,w		
IFDEF SX48_52
		mov w,#RD_latch 	;Initialize RD data latch
		mov rd,w			
		mov w,#RE_latch 	;Initialize RE data latch
		mov re,w			
ENDIF
	;------------------------------------------------------------------------------
	; Clear all Data RAM locations
	;------------------------------------------------------------------------------
zeroRam
IFDEF SX48_52 				;SX48/52 RAM clear routine
		mov	w,#0ドルa			;reset all ram starting at 0ドルA
		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	1ドルa
		clr	1ドルb
		clr	1ドルc
		clr	1ドルd
		clr	1ドルe
		clr	1ドルf
ELSE 					;SX18/20/28 RAM clear routine
		clr	fsr			;reset all ram banks
:zeroRam	sb	fsr.4			;are we on low half of bank?
		setb	fsr.3			;If so, don't touch regs 0-7
		clr	ind			;clear using indirect addressing
		incsz	fsr			;repeat until done
		jmp	:zeroRam
ENDIF
	;------------------------------------------------------------------------------
	; Initialize program/VP registers
	;------------------------------------------------------------------------------
	;VP_begin I2C Slave
	call	@i2csInit
	;VP_end
	;------------------------------------------------------------------------------
	; Setup and enable RTCC interrupt, WREG register, RTCC/WDT prescaler
	;------------------------------------------------------------------------------
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
	; Virtual Peripheral Guidelines Tip:
	;
	;	The suggested default values for the option register are:
	;	- Bit 7 set to 0: location 01ドル addresses the W register (WREG)
	;	- Bit 3 set to 1: Prescaler assigned to WatchDog Timer
	;
	;	If a routine must change the value of the option register (for example, to access
	;	the RTCC register directly), then it should restore the default value for the 
	;	option register before exiting.
	;
	;?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?
RTCC_ON		=	%10000000	;Enables RTCC at address 01ドル (RTW hi)
					;*WREG at address 01ドル (RTW lo) by default
RTCC_ID		=	%01000000	;Disables RTCC edge interrupt (RTE_IE hi)
					;*RTCC edge interrupt (RTE_IE lo) enabled by default
RTCC_INC_EXT	=	%00100000	;Sets RTCC increment on RTCC pin transition (RTS hi)
					;*RTCC increment on internal instruction (RTS lo) is default
RTCC_FE		=	%00010000	;Sets RTCC to increment on falling edge (RTE_ES hi)
					;*RTCC to increment on rising edge (RTE_ES lo) is default
RTCC_PS_ON	=	%00000000	;Assigns prescaler to RTCC (PSA lo)
RTCC_PS_OFF	=	%00001000	;Assigns prescaler to WDT (PSA hi)
PS_000		=	%00000000	;RTCC = 1:2, WDT = 1:1
PS_001		=	%00000001	;RTCC = 1:4, WDT = 1:2
PS_010		=	%00000010	;RTCC = 1:8, WDT = 1:4
PS_011		=	%00000011	;RTCC = 1:16, WDT = 1:8
PS_100		=	%00000100	;RTCC = 1:32, WDT = 1:16
PS_101		=	%00000101	;RTCC = 1:64, WDT = 1:32
PS_110		=	%00000110	;RTCC = 1:128, WDT = 1:64
PS_111		=	%00000111	;RTCC = 1:256, WDT = 1:128
OPTIONSETUP	equ	RTCC_ON | RTCC_PS_OFF	; the default option setup for this program.
		mov	w,#OPTIONSETUP		; setup option register for RTCC interrupts enabled 
		mov	!option,w		; and no prescaler.
		jmp	@mainLoop
;------------------------------------------------------------------------------
; MAIN PROGRAM CODE 
;------------------------------------------------------------------------------
mainLoop
		sb	i2csRxFlag			; Check if slave has received data		
		jmp	mainLoop
		_bank	i2csBank
		snb	i2csBeingReadFlag		; Check if slave is to send or receive
		jmp	:being_read
		
:being_written						; he data will be read out next time through being_read
		mov	w,#i2csStringBank		; Set location of stored string
		add	w,i2csDataIn
		mov	fsr,w				; set location of stored byte (offset)
		mov	w,indf				; get stored byte
		_bank	i2csBank
		mov	i2csDataOut,w			; load data out register
		setb	i2csDataValid			; set data valid flag so data is sent
		clrb	i2csRxFlag			; Clear the data waiting flag
		jmp	mainLoop
:being_read						; loaded data will be sent in ISR... do nothing here
		clrb	i2csRxFlag			; Clear the data waiting flag
		jmp	mainLoop
;*****************************************************************************************
END		;End of program code
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************
;*****************************************************************************************

file: /Techref/scenix/lib/io/osi2/i2c/i2c_slave.src, 61KB, , updated: 2001年10月18日 11:39, local time: 2025年9月2日 11:33,
40.74.122.252:LOG IN

©2025 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_slave.src"> scenix lib io osi2 i2c i2c_slave</A>

Did you find what you needed?

Welcome to massmind.org!

Welcome to techref.massmind.org!

.

AltStyle によって変換されたページ (->オリジナル) /