 TITLE  "lcd8.asm"              	; working with an LCD in the 8-bit mode
 List P=16F684, R=DEC
 INCLUDE "p16f684.inc"

SCL	EQU	2			; Clock line setup
SDA	EQU 3				; Data line setup

; data segment
 CBLOCK 0x20                   
 del, temp				; local variables 
 dataIO, cnt
 ENDC

; code segment
 PAGE
 __CONFIG _FCMEN_OFF & _IESO_OFF & _BOD_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT

 ORG 0                      		; start program at the beginning of mem
	goto start

 ORG 4					; ISR
	bcf	PORTA, SCL
	movlw	0x85			; set FSR to poit to TRISA
	movwf	FSR
	bcf	INDF, SCL		; lock the clock line

	bcf	STATUS, C
	btfsc	PORTA, SDA		; check the data line
	 bsf	STATUS, C		; C = data bit 
	rlf	dataIO, f		; rotate the bit into dataIO

	incf	cnt, f			; update the bit counter
	btfss	cnt, 3			; last bit?
	  goto	ISR_end			; NO - back to foreground code

	clrf	cnt			; restore the bit counter

	btfsc	dataIO, 7		; character or command?
	 goto	command

character
	call	writeDATA		; send received byte to LCD
	goto 	ISR_end

command
	movf	dataIO, w		; send control code
	call	writeCMD
	
ISR_end
	bsf	INDF, SCL		; release the clock line
	goto	$+1			; short delay
	bcf	INTCON, INTF		; unmask the ext. interrupt
	retfie


start
	clrf	PORTA
	clrf	PORTC
	movlw	0x07
	movwf	CMCON0			; comparators OFF
  	bsf    	STATUS, RP0      	; change to BANK 1
	clrf	ANSEL ^ 0x80		; all inputs digital
	movlw	b'001000'
	movwf	TRISA ^ 0x80		; enable RA3:2 for input	
  	clrf    TRISC ^ 0x80      	; enable PORTC for output 
	movlw	b'11000000'		
	movwf	OPTION_REG^0x80		; rasing edge INT setup
  	bcf    	STATUS, RP0        	; back to BANK 0
	movlw	0x85			; setup FSR to point to TRISA
	movwf	FSR

	call 	initLCD             	; initialize LCD display

	bsf	STATUS, RP0		; switch to BANK 1
	bsf	PORTA, SCL		; release the clock line
	goto	$+1			; short delay
	bcf	INTCON, INTF		; unmask the ext. interrupt
	bsf	INTCON, INTE		; enable external interrupt 
	bsf	INTCON, GIE		; enable interrupt system
	bcf	STATUS, RP0		; back to BANK 0
	
	clrf	cnt			; initialize the bit counter

	goto 	$			; INT waiting loop


; procedures
initLCD					; initialize LCD right after power up
	clrf 	PORTA
	call 	delay5			; 15 msec delay after power is on
	call	delay5	
	call 	delay5	
	call	delay5
	
	movlw	0x30			; start LCD init process
	call 	writeByte
	call 	delay5			; wait for 5 msec

	movlw	0x30			; send the init code for the 2nd time
	call	writeCMD

	movlw	0x30			; send the init code for the 3rd time
	call	writeCMD

	movlw	0x38			; set 8-bit and 2-line mode
	call	writeCMD

	movlw	0x0E			; turn on LCD and enable cursor
	call 	writeCMD

	movlw	0x06			; cursor move after each char
	call 	writeCMD

	movlw	1
	call 	writeCMD		; clear display
	call 	delay5

	return

writeCMD				; write to LCD a 1-byte command in W
	bcf	PORTA, 1		; send a command
	call 	writeByte
	movlw	40			; wait for 200 usec
	sublw	1
	sublw	0
	btfss	STATUS, Z
	 goto	$-3
	return

writeDATA				; write to LCD a 1-byte data in W
	bsf	PORTA, 1		; send data
	movf	dataIO, w
	call 	writeByte
	call 	delay5
	return

writeByte
	movwf	temp
	bcf	PORTA, 5		; copy two LSB bits into RA4:5
	bcf	PORTA, 4
	rrf	temp, f
	btfsc	STATUS, C
	 bsf	PORTA, 5
	rrf	temp, f
	btfsc	STATUS, C
	 bsf	PORTA, 4

	movlw	6			; copy all other bits into RC5:0
	rrf	temp, f			; and reverse their order
	rlf	PORTC, f		; (because of PCB layout)
	addlw	-1
	btfss	STATUS, Z
	 goto	$-4

	bsf	PORTA, 0		; toggle the E bit
	bcf	PORTA, 0
	return

delay5					; a delay for 5 milliseconds
	movlw	5
	movwf	del
	movlw 	200
	
	sublw	1			; this loop takes 5us*200 = 1ms
	sublw	0			; for PIC16F684 @ 4 Mhz
	btfss	STATUS, Z
	goto 	$-3

	decfsz	del,f
	goto 	$-6
	return

 END
