list	p=16f88
        include <p16f88.inc>
        include <coff.inc>

        __CONFIG  _CONFIG1, _CP_OFF & _WDT_OFF &  _INTRC_IO & _PWRTE_ON & _LVP_OFF & _BODEN_OFF & _MCLR_OFF
        __CONFIG    _CONFIG2, _IESO_OFF & _FCMEN_OFF

	;; The purpose of this program is to test gpsim's ability to simulate a pic 16F88.
	;; Specifically, I2C

        errorlevel -302 

; Printf Command
.command macro x
  .direct "C", x
  endm

_ClkIn          equ     8000000        ; Input Clock Frequency

_ClkOut         equ     (_ClkIn >> 2)

;
; Compute the delay constants for setup & hold times
;
_40uS_Delay     set     (_ClkOut/250000)
_47uS_Delay     set     (_ClkOut/212766)
_50uS_Delay     set     (_ClkOut/200000)


TRUE    equ     1
FALSE   equ     0

LSB     equ     0
MSB     equ     7

#define SCL_PIN	2
#define SDA_PIN 3
#define I2CPORT	PORTB
#define _SCL_D	I2CPORT,SCL_PIN
#define _SCL	TRISB,SCL_PIN
#define _SDA	TRISB,SDA_PIN

#define T0IE	TMR0IE
#define T0IF	TMR0IF

#define _ENABLE_BUS_FREE_TIME   TRUE
#define _CLOCK_STRETCH_CHECK    TRUE
#define _OPTION_INIT (0xC0 | 0x02) ; Prescaler to TMR0 for Appox 1 mSec timeout

;*****************************************************************************
;                       I2C Bus Status Reg Bit Definitions
;*****************************************************************************

#define _Bus_Busy       Bus_Status,0
#define _Abort          Bus_Status,1
#define _Txmt_Progress  Bus_Status,2
#define _Rcv_Progress   Bus_Status,3

#define _Txmt_Success   Bus_Status,4
#define _Rcv_Success    Bus_Status,5
#define _Fatal_Error    Bus_Status,6
#define _ACK_Error      Bus_Status,7

;*****************************************************************************
;                       I2C Bus Contro Register
;*****************************************************************************
#define _10BitAddr      Bus_Control,0
#define _Slave_RW       Bus_Control,1
#define _Last_Byte_Rcv  Bus_Control,2

#define _SlaveActive    Bus_Control,6
#define _TIME_OUT_      Bus_Control,7



RELEASE_BUS     MACRO
                        bsf     STATUS,RP0              ; select page 1
                        bsf     _SDA            ; tristate SDA
                        bsf     _SCL            ; tristate SCL
;                       bcf     _Bus_Busy       ; Bus Not Busy, TEMP ????, set/clear on Start & Stop
                ENDM


;****************************************************************************
;  A MACRO To Load 8 OR 10 Bit Address To The Address Registers
;
;  SLAVE_ADDRESS is a constant and is loaded into the SlaveAddress Register(s)
;      depending  on 8 or 10 bit addressing modes
;****************************************************************************

LOAD_ADDR_10    MACRO   SLAVE_ADDRESS

	bsf     _10BitAddr      ; Slave has 10 bit address
	movlw   (SLAVE_ADDRESS & 0xff)
	movwf   SlaveAddr               ; load low byte of address
        movlw   (((SLAVE_ADDRESS >> 7) & 0x06) | 0xF0)  ; 10 bit addr 11110AA0
	movwf   SlaveAddr+1     ; hi order  address

	ENDM

LOAD_ADDR_8     MACRO   SLAVE_ADDRESS

	bcf     _10BitAddr      ; Set for 8 Bit Address Mode
	movlw   (SLAVE_ADDRESS & 0xff)
	movwf   SlaveAddr

	ENDM

;****************************************************************************
;                            I2C_WRITE_SUB
;                               
;  Writes a message just like I2C_WRITE, except that the data is preceeded 
;  by a sub-address to a slave device.
;        Eg. : A serial EEPROM would need an address of memory location for 
;	       Random Writes
;
;  Parameters :
;        _BYTES_ #of bytes starting from RAM pointer _SourcePointer_ (constant)
;        _SourcePointer_     Data Start Buffer pointer in RAM (file Registers)
;        _Sub_Address_       Sub-address of Slave (constant)
;
;   Sequence :
;               S-SlvAW-A-SubA-A-D[0]-A.....A-D[N-1]-A-P
;
;  If an error occurs then the routine simply returns and user should check for
;       flags in Bus_Status Reg (for eg. _Txmt_Success flag
;
;       Returns :       WREG = 1 on success, else WREG = 0
;
;  NOTE : The address of the slave must be loaded into SlaveAddress Registers,
;         and 10 or 8 bit mode addressing must be set
;
;  COMMENTS :
;       I2C_WR may prove to be more efficient than this macro in most situations
;       Advantages will be found for Random Address Block Writes for Slaves with
;       Auto Increment Sub-Addresses (like Microchip's 24CXX series Serial 
;	    EEPROMS)
;
;****************************************************************************

I2C_WR_SUB      MACRO   _BYTES_, _SourcePointer_, _Sub_Address_

	movlw   (_BYTES_ + 1)
	movwf   tempCount

	movlw   (_SourcePointer_ - 1)
	movwf   FSR

	movf    INDF,W
	movwf   StoreTemp_1     ; temporarily store contents of (_SourcePointer_ -1)
	movlw   _Sub_Address_
	movwf   INDF           ; store temporarily the sub-address at (_SourcePointer_ -1)

	call    _i2c_block_write        ; write _BYTES_+1 block of data

	movf    StoreTemp_1,W
	movwf   (_SourcePointer_ - 1) ; restore contents of (_SourcePointer_ - 1)
	
;	call    TxmtStopBit     ; Issue a stop bit to end transmission

	ENDM
I2C_WR_SUB2      MACRO   _BYTES_, _SourcePointer_, _Sub_Address_, _Sub_Address2_

	movlw   (_BYTES_ + 2)
	movwf   tempCount

	movlw   (_SourcePointer_ - 2)
	movwf   FSR

	movf    INDF,W
	movwf   StoreTemp_1     ; temporarily store contents of (_SourcePointer_ -2)
	movlw   _Sub_Address_
	movwf   INDF           ; store temporarily the sub-address at (_SourcePointer_ -2)
	movlw   (_SourcePointer_ - 1)
	movwf   FSR
	movlw	_Sub_Address2_
	movwf	INDF
	movlw   (_SourcePointer_ - 2)
	movwf   FSR

	call    _i2c_block_write        ; write _BYTES_+1 block of data

	movf    StoreTemp_1,W
;	movwf   (_SourcePointer_ - 2) ; restore contents of (_SourcePointer_ - 1)
	
;	call    TxmtStopBit     ; Issue a stop bit to end transmission

	ENDM


;****************************************************************************
;                               I2C_WRITE
;
;  A basic macro for writing a block of data to a slave
;
;  Parameters :
;       _BYTES_            #of bytes starting from RAM pointer _SourcePointer_
;      _SourcePointer_     Data Start Buffer pointer in RAM (file Registers)
;
;   Sequence :
;               S-SlvAW-A-D[0]-A.....A-D[N-1]-A-P
;
;  If an error occurs then the routine simply returns and user should check for
;       flags in Bus_Status Reg (for eg. _Txmt_Success flag)
;
;  NOTE : The address of the slave must be loaded into SlaveAddress Registers, 
;        and 10 or 8 bit mode addressing must be set
;****************************************************************************


I2C_WR          MACRO   _BYTES_, _SourcePointer_

	movlw   _BYTES_
	movwf   tempCount
	movlw   _SourcePointer_
	movwf   FSR

	call    _i2c_block_write
	call    TxmtStopBit  ; Issue a stop bit for slave to end transmission
	
	ENDM

;*****************************************************************************
;
;                               I2C_READ
;
; The basic MACRO/procedure to read a block message from a slave device
;
;   Parameters :
;               _BYTES_         :  constant : #of bytes to receive
;               _DestPointer_   :  destination pointer of RAM (File Registers)
;
;   Sequence :
;               S-SlvAR-A-D[0]-A-.....-A-D[N-1]-N-P
;
;   If last byte, then Master will NOT Acknowledge (send NACK)
;
;  NOTE : The address of the slave must be loaded into SlaveAddress Registers, 
;     and 10 or 8 bit mode addressing must be set
;
;*****************************************************************************

I2C_READ        MACRO   _BYTES_, _DestPointer_


	movlw   (_BYTES_ -1)
	movwf   tempCount       ; -1 because, the last byte is used out of loop
	movlw   _DestPointer_   
	movwf   FSR             ; FIFO destination address pointer

	call    _i2c_block_read               

	ENDM

;***************************************************************************
;
;                               I2C_READ_SUB
;  This MACRO/Subroutine reads a message from a slave device preceeded by 
;  a write of the sub-address.
;  Between the sub-addrers write & the following reads, a STOP condition 
;  is not issued and a "REPEATED START" condition is used so that an other 
;  master will not take over the bus, and also that no other master will 
;  overwrite the sub-address of the same salve.
;
;   This function is very commonly used in accessing Random/Sequential reads 
;   from a memory device (e.g : 24Cxx serial of Serial EEPROMs from Microchip).
;
;  Parameters :
;               _BYTES_         # of bytes to read
;               _DestPointer_   The destination pointer of data to be received.
;               _BubAddress_    The sub-address of the slave
;
;  Sequence :
;		S-SlvAW-A-SubAddr-A-S-SlvAR-A-D[0]-A-.....-A-D[N-1]-N-P
;
;
;***************************************************************************

I2C_READ_SUB    MACRO   _BYTES_, _DestPointer_, _SubAddress_

	bcf     _Slave_RW       ; set for write operation
	call    TxmtStartBit    ; send START bit
	call    Txmt_Slave_Addr ; if successful, then _Txmt_Success bit is set


	movlw   _SubAddress_
	movwf   DataByte        ; START address of EEPROM(slave 1)
	call    SendData        ; write sub address
;
; do not send STOP after this, use REPEATED START condition
;

	I2C_READ _BYTES_, _DestPointer_

	ENDM
I2C_READ_SUB2    MACRO   _BYTES_, _DestPointer_, _SubAddress_, _SubAddress2_

	bcf     _Slave_RW       ; set for write operation
	call    TxmtStartBit    ; send START bit
	call    Txmt_Slave_Addr ; if successful, then _Txmt_Success bit is set


	movlw   _SubAddress_
	movwf   DataByte        ; START address of EEPROM(slave 1)
	call    SendData        ; write sub address
	movlw   _SubAddress2_
	movwf   DataByte        ; START address of EEPROM(slave 1)
	call    SendData        ; write sub address
;
; do not send STOP after this, use REPEATED START condition
;

	I2C_READ _BYTES_, _DestPointer_

	ENDM
;----------------------------------------------------------------------
;----------------------------------------------------------------------
INT_VAR        UDATA

IO_buf  RES 10
IN_buf  RES 10

GPR_DATA                UDATA_SHR

w_temp RES  1
status_temp RES  1
SlaveAddr RES 1              ; Slave Addr must be loader into this reg
SlaveAddrHi RES 1            ; for 10 bit addressing mode
DataByte RES 1               ; load this reg with the data to be transmitted
BitCount RES 1               ; The bit number (0:7) transmitted or received
Bus_Status RES 1             ; Status Reg of I2C Bus for both TXMT & RCVE
Bus_Control RES 1            ; control Register of I2C Bus
DelayCount RES 1
DataByteCopy RES 1           ; copy of DataByte for Left Shifts (destructive)

SubAddr RES 1                ; sub-address of slave (used in I2C_HIGH.ASM)
SrcPtr RES 1                 ; source pointer for data to be transmitted

tempCount RES 1              ; a temp variable for scratch RAM
StoreTemp_1 RES 1            ; a temp variable for scratch RAM, do not disturb contents

_End_I2C_Ram RES 1           ; unused, only for ref of end of RAM allocation



;----------------------------------------------------------------------
;   ********************* RESET VECTOR LOCATION  ********************
;----------------------------------------------------------------------
RESET_VECTOR  CODE    0x000              ; processor reset vector
        movlw  high  start               ; load upper byte of 'start' label
        movwf  PCLATH                    ; initialize PCLATH
        goto   start                     ; go to beginning of program

	;; 
	;; Interrupt
	;; 
	movwf	w_temp
	swapf	STATUS,W
	movwf	status_temp
	bcf	STATUS,RP0	;adcon0 is in bank 0

 if _CLOCK_STRETCH_CHECK                ; TMR0 Interrupts enabled only if Clock Stretching is Used
        btfss   INTCON,T0IF
        goto    check	                ; other Interrupts
        bsf     _TIME_OUT_              ; MUST set this Flag
        bcf     INTCON,T0IF
	goto	int_ret
 endif


check:
	btfss	PIR1,SSPIF
	goto	int_ret
	bcf	PIR1,SSPIF
	call	sspint
int_ret:
	swapf	status_temp,w
	movwf	STATUS
	swapf	w_temp,F
	swapf	w_temp,W
	retfie



;----------------------------------------------------------------------
;   ******************* MAIN CODE START LOCATION  ******************
;----------------------------------------------------------------------
MAIN    CODE
start:

  .sim "module lib libgpsim_modules"
   .sim "module load pu pu1"
   .sim "module load pu pu2"
   .sim "node n1"
   .sim "attach n1 portb2 pu1.pin portb4" ; ee.SCL"
   .sim "node n2"
   .sim "attach n2 portb3 pu2.pin portb1" ; ee.SDA"
   .sim "node n3"
   ;.sim "attach n3 porta0 ee.WP"
   .sim "node n4"
   ;.sim "attach n4 porta1 ee.A0"
   .sim "node n5"
   ;.sim "attach n5 porta2 ee.A1"
   .sim "node n6"
   ;.sim "attach n6 porta3 ee.A2"
   .sim "scope.ch0 = \"portb2\""
   .sim "scope.ch1 = \"portb3\""


    bsf  STATUS,RP0	; bank 1
    movlw	0xf6	; set internal RC to 8 Mhz
    movwf	OSCCON
    clrf        TRISA
    bsf		PIE1,SSPIE	; allow SSP interrupts
    bsf		INTCON,GIE	; allow interrupts
    bsf		INTCON,PEIE	; allow interrupts
    bcf  STATUS,RP0	; bank 0
;    bsf		PORTA,0	; Write protect
    bsf		PORTA,1	; A0
    movlw	0x10
    movwf	tempCount
    movlw	IO_buf
    movwf	FSR

Fill_loop:
    movf	FSR,W
    movwf	INDF
    incf	FSR,F
    decfsz	tempCount,F
    goto	Fill_loop

    movlw	0x36	
    movwf	SSPCON
    bsf  STATUS,RP0	; bank 1
    movlw	0xa2
    movwf	SSPADD
    bcf  STATUS,RP0	; bank 0
    
    call InitI2CBus_Master

    LOAD_ADDR_8 0xa2
    call IsSlaveActive
    btfss _SlaveActive
  .assert "\"Slave not active\""
    nop
    LOAD_ADDR_8 0xa2
;    I2C_WR_SUB 8, IO_buf, 0x0c
    I2C_WR_SUB2 8, IO_buf, 0x0c, 0x0c
    call    TxmtStopBit     ; Issue a stop bit to end transmission
    movf    Bus_Status,W
  .assert "W == 0x10, \"*** FAILED I2C write status\""
    nop

poll_ready:
    LOAD_ADDR_8 0xa2
    call IsSlaveActive
    btfss _SlaveActive
    goto poll_ready	; slave not active yet
    nop

;
;	write 0xa2 0x0c 0x0c to set an address
;	write RSTART 0xa3 to initiate read
;	read 8 bytes of data into ram starting at IN_buf
;
    LOAD_ADDR_8 0xa2
    I2C_READ_SUB2 8, IN_buf, 0x0c, 0x0c
    nop
    bcf  STATUS,RP0	; bank 0
    movf	IN_buf,W
  .assert "W == 0xf5, \"*** FAILED read data\""
    nop
    movf	Bus_Status,W
  .assert "W == 0x30, \"*** FAILED 8 bit read status\""
    nop

;
;	write 8 bytes of data in slave 10bit mode
;
    bcf  STATUS,RP0	; bank 0
    movlw	0x37	
    movwf	SSPCON
    bsf  STATUS,RP0	; bank 1
    movlw	0xf0
    movwf	SSPADD
    bcf  STATUS,RP0	; bank 0
    LOAD_ADDR_10 0x0c
    I2C_WR 8, IO_buf

  .assert "\"*** PASSED p16f88 I2C test\""
    nop
    
    goto $

IsSlaveActive
                bcf     _Slave_RW       ; set for write operation
                call    TxmtStartBit    ; send START bit
                call    Txmt_Slave_Addr ; if successful, then _Txmt_Success bit is set
;
                bcf     _SlaveActive
                btfss   _ACK_Error      ; skip if NACK, device is not present or not responding
                bsf     _SlaveActive    ; ACK received, device present & listening
                call    TxmtStopBit
                return

include "i2c_low.inc"


_i2c_block_write:
	call    TxmtStartBit    ; send START bit
	bcf     _Slave_RW       ; set for write operation
	call    Txmt_Slave_Addr ; if successful, then _Txmt_Success bit is set
;
_block_wr1_loop:
	btfss   _Txmt_Success
	return
	movf    INDF,W
	movwf   DataByte  ; start from the first byte starting at _DataPointer_
	incf    FSR, F            
	call    SendData  ; send next byte, bus is our's !
	decfsz  tempCount, F
	goto    _block_wr1_loop  ; loop until desired bytes of data 
				 ;  transmitted to slave
	return
;
;****************************************************************************

_i2c_block_read:                
	call    TxmtStartBit    ; send START bit
	bsf     _Slave_RW       ; set for read operation
	bcf     _Last_Byte_Rcv  ; not a last byte to rcv
	call    Txmt_Slave_Addr ; if successful, then _Txmt_Success bit is set
	btfsc   _Txmt_Success
	goto    _block_rd1_loop ; end
	call    TxmtStopBit     ; Issue a stop bit for slave to end transmission
	retlw   FALSE           ; Error : may be device not responding
;
_block_rd1_loop:
	call    GetData
	movf    DataByte,W
	movwf   INDF      ;start receiving data, starting at Destination Pointer
	incf    FSR, F
	decfsz  tempCount, F
	goto    _block_rd1_loop  ; loop until desired bytes of data transmitted to slave
	bsf     _Last_Byte_Rcv          ; last byte to rcv, so send NACK
	call    GetData
	movf    DataByte,W
	movwf   INDF
	call    TxmtStopBit   ; Issue a stop bit for slave to end transmission
	retlw   TRUE

sspint:
    	bsf  STATUS,RP0	; bank 1
	btfsc	SSPSTAT,R_W	; write test ?
	goto	sspint_wr
	btfsc	SSPSTAT,UA	; UA bit set
	goto	sspint_ua
	
    	bcf  STATUS,RP0	; bank 0
	movf	SSPBUF,W
        return

sspint_wr:
    	bcf  STATUS,RP0	; bank 0
	movlw	0xf5	; byte to send
	movwf	SSPBUF	
	bsf	SSPCON,CKP	; turn off clock stretch
	return

sspint_ua:
	movlw	0x0c	; second byte address
	movwf	SSPADD
    	bcf  STATUS,RP0	; bank 0
	movf	SSPBUF,W
	return

	end


syntax highlighted by Code2HTML, v. 0.9.1