 TITLE  "lcd4.asm"              	; working with an LCD in the 4-bit mode
 List P=16F84a, R=DEC
 INCLUDE "p16F84a.inc"

; data segment
 CBLOCK 0x00C                   
 del, cmd				; variables for passing the parameters
 temp, i				; local variables 
 ENDC

; code segment
 PAGE
 __CONFIG _CP_OFF  & _PWRTE_ON  & _WDT_OFF & _HS_OSC

  org 0                      		; start program at the beginning of mem
	goto start

msg1					; first message to display
	addwf	PCL, f
	dt "  Welcome to", 0

msg2					; second message to display
	addwf	PCL, f
	dt "PIC programming", 0

start
  	bsf    	STATUS, RP0      	; change to BANK 1	
  	clrf    TRISB ^ 0x80      	; enable RB1-RB7 for output 
  	movlw  	0x7F              	; enable internal Pull-Ups
  	movwf  	OPTION_REG ^ 0x80
  	bcf    	STATUS, RP0        	; back to BANK 0

	call 	initLCD        		; initialize LCD display

; output of the top row (msg1)
	clrf 	i			
L1	movf	i, w
	call	msg1			; get the next char
	iorlw	0			; end of string?
	btfsc	STATUS, Z	
	goto 	L2			; YES - jump out of the loop
	call 	writeDATA		; NO - send it to LCD
	incf	i, f			; update the char index
	goto 	L1

L2	movlw	0xC0
	call 	writeCMD		; move the cursor to the second line
	clrf	i			; clear the index of the second string

; output the bottom row (msg2)
L3 	movf	i, w					
	call	msg2			; get the next char
	iorlw	0			; end of string?
	btfsc	STATUS, Z			
	goto 	L4			; YES - jump out of the loop
	call 	writeDATA		; NO - send it to LCD
	incf	i, f			; update the char index
	goto 	L3

L4  goto $				; endless loop


; procedures
initLCD					; initialize LCD right after power up
	movlw	15
	movwf	del
	call 	delay			; a 15 msec delay after power is on

	movlw	3
	movwf	PORTB			; start LCD init process
	movlw	5
	movwf	del	
	call 	delay			; wait for 5 msec	

	movlw	3
	movwf	PORTB			; start LCD init process
	bsf	PORTB, 4		; toggle the LCD Enable pin
	bcf	PORTB, 4
	movlw	5
	movwf	del	
	call 	delay			; wait for 5 msec

	bsf	PORTB, 4		; repeat the reset command (2nd time)
	bcf	PORTB, 4
	movlw	40			; wait for 200 usec
	sublw	1
	sublw	0
	btfss	STATUS, Z
	goto	$-3

	bsf	PORTB, 4		; repeat the reset command (3rd time)
	bcf	PORTB, 4
	movlw	40			; wait for 200 usec
	sublw	1
	sublw	0
	btfss	STATUS, Z
	goto	$-3

	movlw	2			; initialize LCD 4-bit mode
	movwf	PORTB
	bsf	PORTB, 4		; toggle the LCD Enable pin
	bcf	PORTB, 4

	movlw	0x28			; 2-line mode
	call	writeCMD

	movlw	1
	call 	writeCMD		; clear display

	movlw	5			; wait for 5 msec after clearing
	movwf	del
	call 	delay

	movlw	0x06			; cursor move after each char
	call 	writeCMD

	movlw	0x0E			; turn on LCD and enable cursor
	call 	writeCMD
	return

writeCMD				; write to LCD a 1-byte command in W
	movwf	temp
	swapf	temp, w
	andlw	0x0F			; leave only the high 4 bits in W
	movwf	PORTB
	bsf	PORTB, 4		; toggle the E bit
	bcf	PORTB, 4
	
	movfw	temp			; proceed with the low bit
	andlw	0x0F
	movwf	PORTB
	bsf	PORTB, 4		; toggle the  bit	
	bcf	PORTB, 4

	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
	movwf	temp
	swapf	temp, w
	andlw	0x0F			; leave only the lower 4 bits in W	
	movwf	PORTB
	bsf	PORTB, 5		; sending data
	bsf	PORTB, 4		; toggle the E bit
	bcf	PORTB, 4
	
	movfw	temp			; proceed with the low bit
	andlw	0x0F
	movwf	PORTB
	bsf	PORTB, 5		; sending data
	bsf	PORTB, 4		; toggle the  bit	
	bcf	PORTB, 4

	movlw	5			; wait for 5 msec
	movwf	del
	call 	delay
	return

delay					; a delay for del milliseconds
	movlw 	200
	
	sublw	1			; this loop takes 5us*200 = 1ms
	sublw	0			; for PIC16F84A @ 4 Mhz
	btfss	STATUS, Z
	goto 	$-3

	decfsz	del,f
	goto 	delay
	return

 end
