;------------------------------------------------------------------
;                    4 Bit HD44780 LCD driver
;------------------------------------------------------------------
;
;
 ifdef LCD8x2
;
; 8 character x 2 line display
; (may be physically arranged as 16x1)
;
#DEFINE	LCDLENGTH 8
#DEFINE	LCDLINE2  0x40
;
 else
;
; 16 character x 2 line display
;
#DEFINE LCDLENGTH 16
#DEFINE LCDLINE2  0x40
;
 endif
;
;
; 8 level voltage divider
;
;#DEFINE LCD8L
;

; LCD Port pin assignments
;
;  6 pins used (any 6 pins, any port(s), any order)
;  Change bit assigns to suit your wiring!
;
;#DEFINE LCD_D7 = PORTB,0
;#DEFINE LCD_D6 = PORTB,1
;#DEFINE LCD_D5 = PORTB,2
;#DEFINE LCD_D4 = PORTB,3
;#DEFINE LCD_E  = PORTB,4
;#DEFINE LCD_RS = PORTB,5

;
; Variables used by the LCD routines
;
	BYTE	lcd_char	; ASCII character to send
	BYTE	lcd_dat		; data/instruction byte to send
	BYTE	sd_loop		; short delay counter
	BYTE	ld_loop_in	; long delay inner counter
	BYTE	ld_loop_ex	; long delay outer counter
	BYTE	lcd_address	; cursor address in LCD RAM

;
; output high nybble of LCD data
;
lcd_hi:
	bsf	LCD_D7
	btfss	lcd_dat,7
	bcf	LCD_D7
	bsf	LCD_D6
	btfss	lcd_dat,6
	bcf	LCD_D6
	bsf	LCD_D5
	btfss	lcd_dat,5
	bcf	LCD_D5
	bsf	LCD_D4
	btfss	lcd_dat,4
	bcf	LCD_D4
	RETURN

;
; output low nybble of LCD data
;
lcd_lo:
	bsf	LCD_D7
	btfss	lcd_dat,3
	bcf	LCD_D7
	bsf	LCD_D6
	btfss	lcd_dat,2
	bcf	LCD_D6
	bsf	LCD_D5
	btfss	lcd_dat,1
	bcf	LCD_D5
	bsf	LCD_D4
	btfss	lcd_dat,0
	bcf	LCD_D4
	RETURN

;-------------------------------------------------------------------
;                   Initialise the LCD Display
;
;
LCDinit:
        call    set_4bit_mode
 	call 	display_on
	goto	Clear_Display

;-------------------------------------------------------------------
;               Set LCD Controller to 4 bit Mode
;
; The LCD controller starts in 8 bit mode. We have to set it to
; 4 bit mode using only 4 bits! The bits for "Display lines" and
; "Character Font" are the lower nybble which we cannot send in
; 8 bit mode, so the command has to be repeated in 4 bit mode.
;
set_4bit_mode:
        movlw   B'00101000'
        movwf   lcd_dat
        call	lcd_hi		; upper nybble only!
	bcf	LCD_RS
	bsf	LCD_E
	nop			; send "Function Set" in 8 bit mode
	bcf	LCD_E
        call    wait100		; now in 4 bit mode
        movlw   B'00101000'
        call	LCDinst         ; send "Function Set" in 4 bit mode
        movlw   B'10001000'
	goto	LCDinst		; send "Entry Mode Set"

;------------------------------------------------------------------
;           Send Instruction Byte to LCD
;
; Input: W = instruction byte
;
LCDinst:
	bcf	LCD_RS
	goto	LCD_write

;------------------------------------------------------------------
;              Send Data Byte to LCD
;
; Input: W = data byte
;
LCDdata:
	incf	lcd_address	; update cursor position
	bsf	LCD_RS
LCD_write:
	movwf	lcd_dat
	call	lcd_hi
	bsf	LCD_E
	nop
	bcf	LCD_E
	call	lcd_lo
	bsf	LCD_E
	nop
	bcf	LCD_E
	goto    wait100		; wait 100uS


;------------------------------------------------------------------
;                 Clear the Display
;
Clear_Display:
	clrf	lcd_address	; cursor home
        movlw   b'00000001'	; "Clear Display" command
	call	LCDinst
        goto    wait4000        ; Wait 4mS


;-------------------------------------------------------------------
;               Turn the Display ON
;
display_on:
        movlw   B'00001100'	; Display ON, cursor OFF, blink OFF
        goto	LCDinst

;-------------------------------------------------------------------
;          Position Cursor in Display RAM
;
; Input: W = address
;
Set_Cursor:
	movwf	lcd_address
        iorlw	b'10000000'
        goto	LCDinst


;---------------------------------------------------
;         Show an ASCII Character on the LCD
;
; Input: W = character
;
Print_Char:
	goto	LCDdata

;---------------------------------------------------
;	         Wait for 100uS
;---------------------------------------------------
wait100:
        movlw   (100-4)/4
;---------------------------------------------------
;                  Short Delay
;---------------------------------------------------
; Input:  W = delay
;
; Output: waits for (W*4)+4 cycles
;
waitx4:
        movwf   sd_loop
sd_loops:
        decf    sd_loop,F	;
        skpz			; 4 cycles
        goto    sd_loops	;
	RETURN



wait4000:
        movlw   (4000/250)
;----------------------------------------------------
;                 Long Delay
;----------------------------------------------------
; Input: W = delay
;
; Output: waits for (w*250)+4 cycles
;
waitx250:
        movwf   ld_loop_ex
ld_loopex:
	movlw	(250-4)/4
	movwf   ld_loop_in
ld_loopin:
        decf    ld_loop_in,F
        skpz
        goto    ld_loopin
	decf    ld_loop_ex,F
        skpz
        goto    ld_loopex
	RETURN


