Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[PIC] Completed PIC16F -> HD47780 interface in ASM

Status
Not open for further replies.

Twisty

Newbie level 4
Joined
Oct 27, 2016
Messages
6
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
85
Interfacing HD77480 to PIC16f648A-I/P

Please help. I appreciate this is a very newbie post, however I have spent a long time trying to fix this myself and as I am now at the end of my tether and really would appreciate some guidance.

I've pasted the code below and would be thankful for any pointers on I would go about debugging my problem.

I am trying to interface a Displaytech 204A LCD (20x4, apparantly HD77480 compatable) with a PIC16f648A-I/P.
I'm running 8 bit mode with data lines connected to PortB. E to RA3, RW to RA5, RS to RA4
I'm using MPLAB v8.92, compiling with MPASM.
I am programming with an OLIMEX-PIC-MCP-USB (i.e. Picstart+) in the ZIF socket.

I am managing to compile code and program it to the chip.
I am getting some 'Register in operand not in bank 0' warnings, which just seem to be something the compiler spits out when it sees operations outside of bank 0, or maybe it is more significant than that.

Right now I'd be very happy to just initialise the display but I am getting nothing :(

I also installed XC8 and adapted/compiled some other C code, but I've got the same result. It seems that most C code is for the 18F so it looks like I should stick with assembly for the 16F?


Code:
;*****************************************************************************
; This program should interface to a Hitachi HD77480-based LC-Display
; Currently a 4 line * 20 characters display module.
;
; This program should assemble for an 8-bit data interface.
; See AN587 for 4-bit and 8-bit interfaces.
;
; Program	LCD.ASM
; 
; Original Author	Peter Ouwehand Last update	96-01-12
;*Edited for a PIC16F648A and LCD control lines pinout
;*****************************************************************************


;*	LIST P=16C84, F=INHX8M
;*	__CONFIG _CP_OFF & _WDT_OFF & _XT_OSC
;* include <c:\pic\asm\p16c84.inc> 

	list      p=16f648A           ; list directive to define processor
    #include <p16F648A.inc>       ; processor specific variable definitions
    __CONFIG   _CP_OFF & _CPD_OFF & _LVP_OFF & _BOREN_ON & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTOSC_OSC_NOCLKOUT 

	; errorlevel  -302              ; suppress message 302 from list file ; commented out at the moment

;*****************************************************************************
; Fosc		= 4MHz
; Cycle_time	= 1/Fosc / 4
;		= 1/(4*10^6) / 4
;		= 1uSec
;*****************************************************************************

;*****************************************************************************
; Equates, I/O, vars
;*****************************************************************************
RESET_V		EQU	0x0000		; Address of RESET Vector
ISR_V		EQU	0x0004		; Address of Interrupt Vector
OSC_FREQ	EQU	D'4000000'	; Oscillator Frequency is 4 MHz

LCD_DATA	EQU	PORTB		; LCD data lines interface
LCD_DATA_TRIS	EQU	TRISB
LCD_CTRL	EQU	PORTA		; LCD control lines interface

LCD_LINE0	EQU	0x000
LCD_LINE1	EQU	0x040
LCD_LINE2	EQU	0x014
LCD_LINE3	EQU	0x054

; PORTA bits
LCD_E		EQU	3		;* LCD Enable control line connected to RA3
LCD_RW		EQU	5		;* LCD Read/Write control line connected to RA5
LCD_RS		EQU	4		;* LCD Register-Select control line connected to RA4

; PORTB bits
DB7		EQU	7		; LCD dataline 7 (MSB)
DB6		EQU	6		; LCD dataline 6
DB5		EQU	5		; LCD dataline 5
DB4		EQU	4		; LCD dataline 4
DB3		EQU	3		; LCD dataline 3
DB2		EQU	2		; LCD dataline 2
DB1		EQU	1		; LCD dataline 1
DB0		EQU	0		; LCD dataline 0 (LSB)

; misc.
LCD_TEMP	EQU	0x020		; LCD subroutines internal use

TABLE_INDEX	EQU	0x021		; Index to table strings
COUNT		EQU	0x022		; A counter
DELAY		equ	0x023		; Used in DELAYxxx routines
X_DELAY		equ	0x024		; Used in X_DELAYxxx routines


;*****************************************************************************
; Program start
;*****************************************************************************
	ORG	RESET_V			; RESET vector location
RESET		GOTO	START


;*****************************************************************************
; This is the Periperal Interrupt routine. Should NOT get here
;*****************************************************************************
	ORG	ISR_V			; Interrupt vector location
INTERRUPT	BCF     STATUS, RP0	; Select bank 0
		GOTO    INTERRUPT

;*****************************************************************************
; Initialize processor registers
;*****************************************************************************
START					; POWER_ON Reset (Beginning of program)

		CLRF	STATUS		; Do initialization, Select bank 0
		CLRF	INTCON		; Clear int-flags, Disable interrupts
		CLRF	PCLATH		; Keep in lower 2KByte

;* I had originally edited the original assembly for initialising
;* Then I changed to bankssel with I was getting error code 302
;		BCF	STATUS, RP0	; Select bank 0
;		CLRF PORTA ;Initialize PORTA by setting output data latches
;		MOVLW 0x07 ;Turn comparators off and
;		MOVWF CMCON ;enable pins for I/O functions
;		CLRF	PORTB
;		BCF STATUS, RP1
;		BSF STATUS, RP0 ;Select Bank1
;		MOVLW b'11000111' ;Value used to initialize data direction
;		MOVWF TRISA ;Set RA<2:0> as inputs RA<5:3> as outputs RA<7:6> as inputs 
;		MOVLW	0x000		; RB7-0 outputs
;		MOVWF	TRISB
;		BSF	OPTION_REG, NOT_RBPU ; Disable PORTB pull-ups
;
;		BCF	STATUS, RP0	; Select bank 0

;* Using banksel to select relevant banks for initilising 
		banksel PORTA
		clrf PORTA
		MOVLW 0x07 ;Turn comparators off and
		movwf PORTA

		banksel PORTB
		clrf PORTB

		banksel TRISA
		MOVLW b'11000111' ;Value used to initialize data direction
		MOVWF TRISA ;<compile warning 302 Register in operand not in bank 0.  Ensure that bank bits are correct! 
;Set RA<2:0> as inputs RA<5:3> as outputs RA<7:6> as inputs
		banksel TRISB
		MOVLW	0x000		; RB7-0 outputs
		MOVWF	TRISB ;< compile warning 302 Register in operand not in bank 0.  Ensure that bank bits are correct.




		CALL	LCDINIT		; Initialize LCDisplay


;*****************************************************************************
; Display some lines, here
;	----------------------
;	|This is on line :  0|
;	|This is on line :  1|
;	|This is on line :  2|
;	|This is on line :  3|
;	----------------------
; OK
;*****************************************************************************
		MOVLW	0x030		; ASCII '0'
		MOVWF	COUNT
		MOVLW	LCD_LINE0
		CALL	LCDSDDA		; Position cursor leftmost on first line
		CALL	TABLE_MSG	; Display message
		MOVLW	LCD_LINE0 + 0x013
		CALL	LCDSDDA		; Position cursor
		MOVF	COUNT, W
		CALL	LCDPUTCHAR	; Display line number

		INCF	COUNT, F
		MOVLW	LCD_LINE1
		CALL	LCDSDDA
		CALL	TABLE_MSG
		MOVLW	LCD_LINE1 + 0x013
		CALL	LCDSDDA
		MOVF	COUNT, W
		CALL	LCDPUTCHAR

		INCF	COUNT, F
		MOVLW	LCD_LINE2
		CALL	LCDSDDA
		CALL	TABLE_MSG
		MOVLW	LCD_LINE2 + 0x013
		CALL	LCDSDDA
		MOVF	COUNT, W
		CALL	LCDPUTCHAR

		INCF	COUNT, F
		MOVLW	LCD_LINE3
		CALL	LCDSDDA
		CALL	TABLE_MSG
		MOVLW	LCD_LINE3 + 0x013
		CALL	LCDSDDA
		MOVF	COUNT, W
		CALL	LCDPUTCHAR


;*****************************************************************************
; Program ends here
; OK
;*****************************************************************************
LOOP
		GOTO	LOOP		; Stay here forever


;*****************************************************************************
; Send a message using a table to output a message
; OK
;*****************************************************************************
TABLE_MSG
		MOVLW	0		; Startindex of table message
DISP_MSG
		MOVWF	TABLE_INDEX	; Holds message address
		CALL	MSG1
		ANDLW	0x0FF		; Check if at end of message
		BTFSC	STATUS, Z	; (zero returned at end)
		GOTO	TABLE_MSG_END             
		CALL	LCDPUTCHAR	; Display character
		MOVF	TABLE_INDEX, W	; Point to next character
		ADDLW	1
		GOTO	DISP_MSG
TABLE_MSG_END	RETURN





;*****************************************************************************
; LCD Module Subroutines
;*****************************************************************************
;
;=============================================================================
; LCDINIT
; Initilize LC-Display Module
; Should be modified to your needs (i.e. display type, cursor on/off, etc.)
; OK
;=============================================================================
LCDINIT
					; Busy-flag is not yet valid
		CLRF	LCD_CTRL	; ALL PORT output should output Low.
					; power-up delay
		MOVLW	0x01E
		CALL	X_DELAY500	; 30 * 0.5mS = 15mS
					; Busy Flag should be valid from here
		MOVLW	0x038		; 8-bit-interface, 2-lines
		CALL	LCDPUTCMD
		MOVLW	0x000		; disp.off, curs.off, no-blink
		CALL	LCDDMODE
		CALL	LCDCLEAR
		MOVLW	0x004		; disp.on, curs.off
		CALL	LCDDMODE
		MOVLW	0x002		; auto-inc (shift-cursor)
		CALL	LCDEMODE
		RETURN
;=============================================================================
; LCD_ENABLE
; Pulses LCD enable pin
; OK
;=============================================================================
LCD_ENABLE
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		RETURN
;=============================================================================
; LCDBUSY
; Returns when LCD busy-flag is inactive
; OK
;=============================================================================
LCDBUSY
		BSF	STATUS,RP0	; Select Register page 1
		MOVLW	0x0FF		; Set PORTB for input
		MOVWF	LCD_DATA_TRIS ;< compile warning 302 Register in operand not in bank 0.  Ensure that bank bits are correct.
		BCF	STATUS, RP0	; Select Register page 0
		BCF	LCD_CTRL, LCD_RS; Set LCD for command mode
		BSF	LCD_CTRL, LCD_RW; Setup to read busy flag
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	LCD_DATA, W	; Read busy flag + DDram address
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		ANDLW	0x80		; Check Busy flag, High = Busy
		BTFSS	STATUS, Z
		GOTO	LCDBUSY
LCDNOTBUSY	BCF	LCD_CTRL, LCD_RW
		BSF	STATUS, RP0	; Select Register page 1
		MOVLW	0x000
		MOVWF	LCD_DATA_TRIS	; Set PORTB for output ;< compile warning 302 Register in operand not in bank 0.  Ensure that bank bits are correct.
		BCF	STATUS, RP0	; Select Register page 0
		RETURN
;=============================================================================
; LCDCLEAR
; Clears display and returns cursor to home position (upper-left corner).
;
;=============================================================================
LCDCLEAR
		MOVLW	0x001
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDHOME
; Returns cursor to home position.
; Returns display to original position (when shifted).
;
;=============================================================================
LCDHOME
		MOVLW	0x002
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDEMODE
; Sets entry mode of display.
; Required entry mode must be set in W
;  b0	: 0 = no display shift	1 = display shift
;  b1	: 0 = auto-decrement	1 = auto-increment
;  b2-7	: don't care
; OK
;=============================================================================
LCDEMODE
		ANDLW	0x003		; Strip upper bits
		IORLW	0x004		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDDMODE
; Sets display control.
; Required display mode must be set in W
;  b0	: 0 = cursor blink off	1 = cursor blink on
;  b1	: 0 = cursor off	1 = cursor on
;  b2	: 0 = display off	1 = display on (display data remains in DDRAM)
;  b3-7	: don't care
; OK
;=============================================================================
LCDDMODE
		ANDLW	0x007		; Strip upper bits
		IORLW	0x008		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDSCGA
; Sets Character-Generator-RAM address. CGRAM is read/written after
;  this setting.
; Required CGRAM address must be set in W
;  b0-5	: required CGRAM address
;  b6-7	: don't care
;
;=============================================================================
LCDSCGA
		ANDLW	0x03F		; Strip upper bits
		IORLW	0x040		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDSDDA
; Sets the Display-Data-RAM address. DDRAM data is read/written after
;  this setting.
; Required DDRAM address must be set in W
;  b0-6	: required DDRAM address
;  b7	: don't care
; OK
;=============================================================================
LCDSDDA
		IORLW	0x080		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDGADDR
; Returns address counter contents, used for both DDRAM and CGRAM.
; RAM address is returned in W
;
;=============================================================================
LCDGADDR
		BSF	STATUS,RP0	; Select Register page 1
		MOVLW	0x0FF		; Set PORTB for input
		MOVWF	LCD_DATA_TRIS ;< compile warning 302 Register in operand not in bank 0.  Ensure that bank bits are correct.
		BCF	STATUS, RP0	; Select Register page 0
		BCF	LCD_CTRL, LCD_RS; Set LCD for command mode
		BSF	LCD_CTRL, LCD_RW; Setup to read busy flag
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	LCD_DATA, W	; Read busy flag + RAM address
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		ANDLW	0x07F		; Strip upper bit
		BCF	LCD_CTRL, LCD_RW
		BSF	STATUS, RP0	; Select Register page 1
		MOVLW	0x000
		MOVWF	LCD_DATA_TRIS	; Set PORTB for output getting ;< compile warning 302 Register in operand not in bank 0.  Ensure that bank bits are correct.
		BCF	STATUS, RP0	; Select Register page 0
		RETURN
;=============================================================================
; LCDPUTCHAR
; Sends character to LCD
; Required character must be in W
; OK
;=============================================================================
LCDPUTCHAR
		MOVWF	LCD_TEMP	; Character to be sent is in W
		CALL	LCDBUSY		; Wait for LCD to be ready
		BCF	LCD_CTRL, LCD_RW; Set LCD in read mode
		BSF	LCD_CTRL, LCD_RS; Set LCD in data mode
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	LCD_TEMP, W
		MOVWF	LCD_DATA	; Send data to LCD
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		RETURN
;=============================================================================
; LCDPUTCMD
; Sends command to LCD
; Required command must be in W
; OK
;=============================================================================
LCDPUTCMD
		MOVWF	LCD_TEMP	; Command to be sent is in W
		CALL	LCDBUSY		; Wait for LCD to be ready
		BCF	LCD_CTRL, LCD_RW; Set LCD in read mode
		BCF	LCD_CTRL, LCD_RS; Set LCD in command mode
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	LCD_TEMP, W
		MOVWF	LCD_DATA	; Send data to LCD
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		RETURN






;*****************************************************************************
; Delay_time	= ((DELAY_value * 3) + 4) * Cycle_time
; DELAY_value	= (Delay_time - (4 * Cycle_time)) / (3 * Cycle_time)
;
; i.e. (@ 4MHz crystal)
; Delay_time	= ((32 * 3) + 4) * 1uSec
;		= 100uSec
; DELAY_value	= (500uSec - 4) / 3
;		= 165.33
;		= 165
;*****************************************************************************
DELAY500	MOVLW	D'240'		; +1		1 cycle * increased to 240 just in case
		MOVWF	DELAY		; +2		1 cycle
DELAY500_LOOP	DECFSZ	DELAY, F	; step 1	1 cycle
		GOTO	DELAY500_LOOP	; step 2	2 cycles
DELAY500_END	RETURN			; +3		2 cycles
;
;
X_DELAY500	MOVWF	X_DELAY		; +1		1 cycle
X_DELAY500_LOOP	CALL	DELAY500	; step1		wait 500uSec
		DECFSZ	X_DELAY, F	; step2		1 cycle
		GOTO	X_DELAY500_LOOP	; step3		2 cycles
X_DELAY500_END	RETURN			; +2		2 cycles






;=============================================================================
; Table message to display
;=============================================================================
MSG1
		addwf	PCL ,F		;Jump to char pointed to in W reg
		retlw	'T'
		retlw	'h'
		retlw	'i'
		retlw	's'
		retlw	' '
		retlw	'i'
		retlw	's'
		retlw	' '
		retlw	'o'
		retlw	'n'
		retlw	' '
		retlw	'l'
		retlw	'i'
		retlw	'n'
		retlw	'e'
		retlw	' '
		retlw	':'
		retlw	' '
		retlw	' '
		retlw	'#'
MSG1_END
		retlw	0
;
;	IF ( (MSG1 & 0x0FF) >= (MSG1_END & 0x0FF) )
;		MESSG   "Warning - User Definded: Table 'MSG1' crosses page boundry in computed jump"
;	ENDIF


	END				; End of program
 

Re: Interfacing HD77480 to PIC16f648A-I/P

Meaned HD44780?

I am getting some 'Register in operand not in bank 0' warnings
If you prefer assembler coding for any strange reason, why don't you use the bank macros selection that avoid possible bank mismatch?
 
  • Like
Reactions: Twisty

    Twisty

    Points: 2
    Helpful Answer Positive Rating
Re: Interfacing HD77480 to PIC16f648A-I/P

Hi,

Maybe not a software problem:
Power supply, contrast voltage, reset, signal levels.

To check if the comunication works you could write data into the disply and read them back to verify the write process.
Also check busy flag operation.

Klaus
 
  • Like
Reactions: Twisty

    Twisty

    Points: 2
    Helpful Answer Positive Rating
Re: Interfacing HD77480 to PIC16f648A-I/P

Thanks for the prompt replies, I will look into the bank macros selection. I much prefer C but used assembly as there appears to be very little code around for PIC6F in C, is it fine compiling C code for the PIC16F?

Sounds like reading data back from the display would require me to write a bunch more code. Equipment currently available to me is quite basic, i.e. multimeter.

I've just fixed a few things that I managed to miss before, although still not working.
Found an embarrassing error, I had connected LCD Read/Write to RA5 which is the only port which is input only on this PIC. I've now wired this to RA2 and changed the code, I also worked out that I was not initialising the CMCON register correctly and sorted that too.
I've double checked the hardware and I am running contrast pots. I've got some electronics experience but I am starting from zero on knowledge of PICS and assembly code.

Basically when I turn it on, with or without a PIC I just get squares on lines 1 and 3 of the display.


Code:
;*****************************************************************************
; This program should interface to a Hitachi HD77480-based LC-Display
; Currently a 4 line * 20 characters display module.
;
; This program should assemble for an 8-bit data interface.
; See AN587 for 4-bit and 8-bit interfaces.
;
; Program	LCD.ASM
; 
; Original Author	Peter Ouwehand Last update	96-01-12
;*Edited for a PIC16F648A and LCD control lines pinout
;*****************************************************************************


;*	LIST P=16C84, F=INHX8M
;*	__CONFIG _CP_OFF & _WDT_OFF & _XT_OSC
;* include <c:\pic\asm\p16c84.inc> 

	list      p=16f648A           ; list directive to define processor
    #include <p16F648A.inc>       ; processor specific variable definitions
    __CONFIG   _CP_OFF & _CPD_OFF & _LVP_OFF & _BOREN_ON & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTOSC_OSC_NOCLKOUT 

	; errorlevel  -302              ; suppress message 302 from list file ; commented out at the moment

;*****************************************************************************
; Fosc		= 4MHz
; Cycle_time	= 1/Fosc / 4
;		= 1/(4*10^6) / 4
;		= 1uSec
;*****************************************************************************

;*****************************************************************************
; Equates, I/O, vars
;*****************************************************************************
RESET_V		EQU	0x0000		; Address of RESET Vector
ISR_V		EQU	0x0004		; Address of Interrupt Vector
OSC_FREQ	EQU	D'4000000'	; Oscillator Frequency is 4 MHz

LCD_DATA	EQU	PORTB		; LCD data lines interface
LCD_DATA_TRIS	EQU	TRISB
LCD_CTRL	EQU	PORTA		; LCD control lines interface

LCD_LINE0	EQU	0x000
LCD_LINE1	EQU	0x040
LCD_LINE2	EQU	0x014
LCD_LINE3	EQU	0x054

; PORTA bits
LCD_E		EQU	3		;* LCD Enable control line connected to RA3
LCD_RW		EQU	2		;* LCD Read/Write control line connected to RA2
LCD_RS		EQU	4		;* LCD Register-Select control line connected to RA4

; PORTB bits
DB7		EQU	7		; LCD dataline 7 (MSB)
DB6		EQU	6		; LCD dataline 6
DB5		EQU	5		; LCD dataline 5
DB4		EQU	4		; LCD dataline 4
DB3		EQU	3		; LCD dataline 3
DB2		EQU	2		; LCD dataline 2
DB1		EQU	1		; LCD dataline 1
DB0		EQU	0		; LCD dataline 0 (LSB)

; misc.
LCD_TEMP	EQU	0x020		; LCD subroutines internal use

TABLE_INDEX	EQU	0x021		; Index to table strings
COUNT		EQU	0x022		; A counter
DELAY		equ	0x023		; Used in DELAYxxx routines
X_DELAY		equ	0x024		; Used in X_DELAYxxx routines


;*****************************************************************************
; Program start
;*****************************************************************************
	ORG	RESET_V			; RESET vector location
RESET		GOTO	START


;*****************************************************************************
; This is the Periperal Interrupt routine. Should NOT get here
;*****************************************************************************
	ORG	ISR_V			; Interrupt vector location
INTERRUPT	BCF     STATUS, RP0	; Select bank 0
		GOTO    INTERRUPT

;*****************************************************************************
; Initialize processor registers
;*****************************************************************************
START					; POWER_ON Reset (Beginning of program)

		CLRF	STATUS		; Do initialization, Select bank 0
		CLRF	INTCON		; Clear int-flags, Disable interrupts
		CLRF	PCLATH		; Keep in lower 2KByte

		CLRF PORTA ;Initialize PORTA by setting output data latches
		CLRF PORTB

;* I had originally edited the original assembly for initialising
;* Then I changed to bankssel with I was getting error code 302
;* Now I've gone back to this method when banksel got same error and I realised I was doing CMCON wrong.
		BCF	STATUS, RP0	; Select bank 0
		MOVLW 0x07 ;Turn comparators off and
		MOVWF CMCON ;enable pins for I/O functions
		BCF STATUS, RP1
		BSF STATUS, RP0 ;Select Bank1
		MOVLW b'11100011' ;Value used to initialize data direction
		MOVWF TRISA ;Set RA<1:0> as inputs RA<4:2> as outputs RA<7:5> as inputs 
		MOVLW	0x000		; RB7-0 outputs
		MOVWF	TRISB

;		BSF	OPTION_REG, NOT_RBPU ; Disable PORTB pull-ups

;
		BCF	STATUS, RP0	; Select bank 0

;* Using banksel to select relevant banks for initilising 
;		banksel PORTA
;		clrf PORTA
;		MOVLW 0x07 ;Turn comparators off and
;		movwf CMCON
;
;		banksel PORTB
;		clrf PORTB
;
;		banksel TRISA
;		MOVLW b'11000111' ;Value used to initialize data direction
;		MOVWF TRISA ;<compile warning 302 Register in operand not in bank 0.  Ensure that bank bits are correct! 
;Set RA<2:0> as inputs RA<5:3> as outputs RA<7:6> as inputs
;		banksel TRISB
;		MOVLW	0x000		; RB7-0 outputs
;		MOVWF	TRISB ;< compile warning 302 Register in operand not in bank 0.  Ensure that bank bits are correct.

		CALL	LCDINIT		; Initialize LCDisplay

;*****************************************************************************
; Display some lines, here
;	----------------------
;	|This is on line :  0|
;	|This is on line :  1|
;	|This is on line :  2|
;	|This is on line :  3|
;	----------------------
; OK
;*****************************************************************************
		MOVLW	0x030		; ASCII '0'
		MOVWF	COUNT
		MOVLW	LCD_LINE0
		CALL	LCDSDDA		; Position cursor leftmost on first line
		CALL	TABLE_MSG	; Display message
		MOVLW	LCD_LINE0 + 0x013
		CALL	LCDSDDA		; Position cursor
		MOVF	COUNT, W
		CALL	LCDPUTCHAR	; Display line number

		INCF	COUNT, F
		MOVLW	LCD_LINE1
		CALL	LCDSDDA
		CALL	TABLE_MSG
		MOVLW	LCD_LINE1 + 0x013
		CALL	LCDSDDA
		MOVF	COUNT, W
		CALL	LCDPUTCHAR

		INCF	COUNT, F
		MOVLW	LCD_LINE2
		CALL	LCDSDDA
		CALL	TABLE_MSG
		MOVLW	LCD_LINE2 + 0x013
		CALL	LCDSDDA
		MOVF	COUNT, W
		CALL	LCDPUTCHAR

		INCF	COUNT, F
		MOVLW	LCD_LINE3
		CALL	LCDSDDA
		CALL	TABLE_MSG
		MOVLW	LCD_LINE3 + 0x013
		CALL	LCDSDDA
		MOVF	COUNT, W
		CALL	LCDPUTCHAR


;*****************************************************************************
; Program ends here
; OK
;*****************************************************************************
LOOP
		GOTO	LOOP		; Stay here forever


;*****************************************************************************
; Send a message using a table to output a message
; OK
;*****************************************************************************
TABLE_MSG
		MOVLW	0		; Startindex of table message
DISP_MSG
		MOVWF	TABLE_INDEX	; Holds message address
		CALL	MSG1
		ANDLW	0x0FF		; Check if at end of message
		BTFSC	STATUS, Z	; (zero returned at end)
		GOTO	TABLE_MSG_END             
		CALL	LCDPUTCHAR	; Display character
		MOVF	TABLE_INDEX, W	; Point to next character
		ADDLW	1
		GOTO	DISP_MSG
TABLE_MSG_END	RETURN





;*****************************************************************************
; LCD Module Subroutines
;*****************************************************************************
;
;=============================================================================
; LCDINIT
; Initilize LC-Display Module
; Should be modified to your needs (i.e. display type, cursor on/off, etc.)
; OK
;=============================================================================
LCDINIT
					; Busy-flag is not yet valid
		CLRF	LCD_CTRL	; ALL PORT output should output Low.
					; power-up delay
		MOVLW	0x01E
		CALL	X_DELAY500	; 30 * 0.5mS = 15mS
					; Busy Flag should be valid from here
		MOVLW	0x038		; 8-bit-interface, 2-lines
		CALL	LCDPUTCMD
		MOVLW	0x000		; disp.off, curs.off, no-blink
		CALL	LCDDMODE
		CALL	LCDCLEAR
		MOVLW	0x004		; disp.on, curs.off
		CALL	LCDDMODE
		MOVLW	0x002		; auto-inc (shift-cursor)
		CALL	LCDEMODE
		RETURN
;=============================================================================
; LCD_ENABLE
; Pulses LCD enable pin
; OK
;=============================================================================
LCD_ENABLE
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		RETURN
;=============================================================================
; LCDBUSY
; Returns when LCD busy-flag is inactive
; OK
;=============================================================================
LCDBUSY
		BSF	STATUS,RP0	; Select Register page 1
		MOVLW	0x0FF		; Set PORTB for input
		MOVWF	LCD_DATA_TRIS ;< compile warning 302 Register in operand not in bank 0.  Ensure that bank bits are correct.
		BCF	STATUS, RP0	; Select Register page 0
		BCF	LCD_CTRL, LCD_RS; Set LCD for command mode
		BSF	LCD_CTRL, LCD_RW; Setup to read busy flag
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	LCD_DATA, W	; Read busy flag + DDram address
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		ANDLW	0x80		; Check Busy flag, High = Busy
		BTFSS	STATUS, Z
		GOTO	LCDBUSY
LCDNOTBUSY	BCF	LCD_CTRL, LCD_RW
		BSF	STATUS, RP0	; Select Register page 1
		MOVLW	0x000
		MOVWF	LCD_DATA_TRIS	; Set PORTB for output ;< compile warning 302 Register in operand not in bank 0.  Ensure that bank bits are correct.
		BCF	STATUS, RP0	; Select Register page 0
		RETURN
;=============================================================================
; LCDCLEAR
; Clears display and returns cursor to home position (upper-left corner).
;
;=============================================================================
LCDCLEAR
		MOVLW	0x001
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDHOME
; Returns cursor to home position.
; Returns display to original position (when shifted).
;
;=============================================================================
LCDHOME
		MOVLW	0x002
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDEMODE
; Sets entry mode of display.
; Required entry mode must be set in W
;  b0	: 0 = no display shift	1 = display shift
;  b1	: 0 = auto-decrement	1 = auto-increment
;  b2-7	: don't care
; OK
;=============================================================================
LCDEMODE
		ANDLW	0x003		; Strip upper bits
		IORLW	0x004		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDDMODE
; Sets display control.
; Required display mode must be set in W
;  b0	: 0 = cursor blink off	1 = cursor blink on
;  b1	: 0 = cursor off	1 = cursor on
;  b2	: 0 = display off	1 = display on (display data remains in DDRAM)
;  b3-7	: don't care
; OK
;=============================================================================
LCDDMODE
		ANDLW	0x007		; Strip upper bits
		IORLW	0x008		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDSCGA
; Sets Character-Generator-RAM address. CGRAM is read/written after
;  this setting.
; Required CGRAM address must be set in W
;  b0-5	: required CGRAM address
;  b6-7	: don't care
;
;=============================================================================
LCDSCGA
		ANDLW	0x03F		; Strip upper bits
		IORLW	0x040		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDSDDA
; Sets the Display-Data-RAM address. DDRAM data is read/written after
;  this setting.
; Required DDRAM address must be set in W
;  b0-6	: required DDRAM address
;  b7	: don't care
; OK
;=============================================================================
LCDSDDA
		IORLW	0x080		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDGADDR
; Returns address counter contents, used for both DDRAM and CGRAM.
; RAM address is returned in W
;
;=============================================================================
LCDGADDR
		BSF	STATUS,RP0	; Select Register page 1
		MOVLW	0x0FF		; Set PORTB for input
		MOVWF	LCD_DATA_TRIS ;< compile warning 302 Register in operand not in bank 0.  Ensure that bank bits are correct.
		BCF	STATUS, RP0	; Select Register page 0
		BCF	LCD_CTRL, LCD_RS; Set LCD for command mode
		BSF	LCD_CTRL, LCD_RW; Setup to read busy flag
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	LCD_DATA, W	; Read busy flag + RAM address
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		ANDLW	0x07F		; Strip upper bit
		BCF	LCD_CTRL, LCD_RW
		BSF	STATUS, RP0	; Select Register page 1
		MOVLW	0x000
		MOVWF	LCD_DATA_TRIS	; Set PORTB for output getting ;< compile warning 302 Register in operand not in bank 0.  Ensure that bank bits are correct.
		BCF	STATUS, RP0	; Select Register page 0
		RETURN
;=============================================================================
; LCDPUTCHAR
; Sends character to LCD
; Required character must be in W
; OK
;=============================================================================
LCDPUTCHAR
		MOVWF	LCD_TEMP	; Character to be sent is in W
		CALL	LCDBUSY		; Wait for LCD to be ready
		BCF	LCD_CTRL, LCD_RW; Set LCD in read mode
		BSF	LCD_CTRL, LCD_RS; Set LCD in data mode
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	LCD_TEMP, W
		MOVWF	LCD_DATA	; Send data to LCD
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		RETURN
;=============================================================================
; LCDPUTCMD
; Sends command to LCD
; Required command must be in W
; OK
;=============================================================================
LCDPUTCMD
		MOVWF	LCD_TEMP	; Command to be sent is in W
		CALL	LCDBUSY		; Wait for LCD to be ready
		BCF	LCD_CTRL, LCD_RW; Set LCD in read mode
		BCF	LCD_CTRL, LCD_RS; Set LCD in command mode
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	LCD_TEMP, W
		MOVWF	LCD_DATA	; Send data to LCD
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		RETURN






;*****************************************************************************
; Delay_time	= ((DELAY_value * 3) + 4) * Cycle_time
; DELAY_value	= (Delay_time - (4 * Cycle_time)) / (3 * Cycle_time)
;
; i.e. (@ 4MHz crystal)
; Delay_time	= ((32 * 3) + 4) * 1uSec
;		= 100uSec
; DELAY_value	= (500uSec - 4) / 3
;		= 165.33
;		= 165
;*****************************************************************************
DELAY500	MOVLW	D'240'		; +1		1 cycle * increased to 240 just in case
		MOVWF	DELAY		; +2		1 cycle
DELAY500_LOOP	DECFSZ	DELAY, F	; step 1	1 cycle
		GOTO	DELAY500_LOOP	; step 2	2 cycles
DELAY500_END	RETURN			; +3		2 cycles
;
;
X_DELAY500	MOVWF	X_DELAY		; +1		1 cycle
X_DELAY500_LOOP	CALL	DELAY500	; step1		wait 500uSec
		DECFSZ	X_DELAY, F	; step2		1 cycle
		GOTO	X_DELAY500_LOOP	; step3		2 cycles
X_DELAY500_END	RETURN			; +2		2 cycles






;=============================================================================
; Table message to display
;=============================================================================
MSG1
		addwf	PCL ,F		;Jump to char pointed to in W reg
		retlw	'T'
		retlw	'h'
		retlw	'i'
		retlw	's'
		retlw	' '
		retlw	'i'
		retlw	's'
		retlw	' '
		retlw	'o'
		retlw	'n'
		retlw	' '
		retlw	'l'
		retlw	'i'
		retlw	'n'
		retlw	'e'
		retlw	' '
		retlw	':'
		retlw	' '
		retlw	' '
		retlw	'#'
MSG1_END
		retlw	0
;
;	IF ( (MSG1 & 0x0FF) >= (MSG1_END & 0x0FF) )
;		MESSG   "Warning - User Definded: Table 'MSG1' crosses page boundry in computed jump"
;	ENDIF


	END				; End of program
 

Re: Interfacing HD77480 to PIC16f648A-I/P

Common mistake:
Do you are using a pullup resistor on RA4 pin (your LCD RS)?
This is an open collector output of the 16F648A !!
 
  • Like
Reactions: Twisty

    Twisty

    Points: 2
    Helpful Answer Positive Rating
Re: Interfacing HD77480 to PIC16f648A-I/P

Otherwise this is an excellent bit of work.

The '302' isn't an error, it's a warning from the assember that you are using a register that might need the bank switching, the assembler can't tell by itself. You can either ignore it or uncomment :
Code:
;errorlevel -302
which supresses the warning.

If this is to be used on any other PIC, it would be worthwhile looking at the 'cblock' directive so the variable addresses can be dynamically allocated.

Brian.
 
  • Like
Reactions: Twisty

    Twisty

    Points: 2
    Helpful Answer Positive Rating
Re: Interfacing HD77480 to PIC16f648A-I/P

Hi,

Basically when I turn it on, with or without a PIC I just get squares on lines 1 and 3 of the display.
This is typically when the controller is not initialized.
It seems your communication fails.

Klaus
 
  • Like
Reactions: Twisty

    Twisty

    Points: 2
    Helpful Answer Positive Rating
Re: Interfacing HD77480 to PIC16f648A-I/P

Common mistake:
Do you are using a pullup resistor on RA4 pin (your LCD RS)?
This is an open collector output of the 16F648A !!

This was the missing link. I'm now connecting LCD RS to RA0 and it is actually working! I am so happy.


After tweaking a few things (I was generally losing the plot) my working ASM looks like the below.
The C code, however, is still not working I'll spend another hour trying to get that to work, if not then I guess I'll keep stabbing away in ASM.

Code:
;*****************************************************************************
; This program should interface to a Hitachi HD47780-based LC-Display
; Currently a 4 line * 20 characters display module.
;
; This program assembles for an 8-bit data interface.
;
; Program	LCD.ASM
; 
; Original Author	Peter Ouwehand Last update	96-01-12
;*Edited for a PIC16F648A and LCD control lines pinout 27 October 2016
;*****************************************************************************

	list      p=16f648A           ; list directive to define processor
    #include <p16F648A.inc>       ; processor specific variable definitions
    __CONFIG   _CP_OFF & _CPD_OFF & _LVP_OFF & _BOREN_ON & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTOSC_OSC_NOCLKOUT 

	 errorlevel  -302              ; suppress message 302 from list file compiler is too dumb to know if bank switching is correct or not

;*****************************************************************************
; Fosc		= 4MHz
; Cycle_time	= 1/Fosc / 4
;		= 1/(4*10^6) / 4
;		= 1uSec
;*****************************************************************************

;*****************************************************************************
; Equates, I/O, vars
;*****************************************************************************
RESET_V		EQU	0x0000		; Address of RESET Vector
ISR_V		EQU	0x0004		; Address of Interrupt Vector
OSC_FREQ	EQU	D'4000000'	; Oscillator Frequency is 4 MHz

LCD_DATA	EQU	PORTB		; LCD data lines interface
LCD_DATA_TRIS	EQU	TRISB
LCD_CTRL	EQU	PORTA		; LCD control lines interface

LCD_LINE0	EQU	0x000
LCD_LINE1	EQU	0x040
LCD_LINE2	EQU	0x014
LCD_LINE3	EQU	0x054

; PORTA bits
LCD_E		EQU	3		;* LCD Enable control line connected to RA3
LCD_RW		EQU	1		;* LCD Read/Write control line connected to RA1
LCD_RS		EQU	0		;* LCD Register-Select control line connected to RA0

; PORTB bits
DB7		EQU	7		; LCD dataline 7 (MSB)
DB6		EQU	6		; LCD dataline 6
DB5		EQU	5		; LCD dataline 5
DB4		EQU	4		; LCD dataline 4
DB3		EQU	3		; LCD dataline 3
DB2		EQU	2		; LCD dataline 2
DB1		EQU	1		; LCD dataline 1
DB0		EQU	0		; LCD dataline 0 (LSB)

; misc.
LCD_TEMP	EQU	0x020		; LCD subroutines internal use

TABLE_INDEX	EQU	0x021		; Index to table strings
COUNT		EQU	0x022		; A counter
DELAY		equ	0x023		; Used in DELAYxxx routines
X_DELAY		equ	0x024		; Used in X_DELAYxxx routines


;*****************************************************************************
; Program start
;*****************************************************************************
	ORG	RESET_V			; RESET vector location
RESET		GOTO	START


;*****************************************************************************
; This is the Periperal Interrupt routine. Should NOT get here
;*****************************************************************************
	ORG	ISR_V			; Interrupt vector location
INTERRUPT	BCF     STATUS, RP0	; Select bank 0
		GOTO    INTERRUPT

;*****************************************************************************
; Initialize processor registers
;*****************************************************************************
START					; POWER_ON Reset (Beginning of program)

		CLRF	STATUS		; Do initialization, Select bank 0
		CLRF	INTCON		; Clear int-flags, Disable interrupts
		CLRF	PCLATH		; Keep in lower 2KByte

		CLRF PORTA ;Initialize PORTA by setting output data latches
		CLRF PORTB

		BCF	STATUS, RP0	; Select bank 0
		MOVLW 0x07 ;Turn comparators off and
		MOVWF CMCON ;enable pins for I/O functions
		BCF STATUS, RP1
		BSF STATUS, RP0 ;Select Bank1
		MOVLW 0xF4 ; Value used to initialize data direction
		MOVWF TRISA ;Set RA<1:0> as outputs RA<2> as input RA<3> as input RA<7:4> as output 
		MOVLW	0x000		; RB7-0 all outputs
		MOVWF	TRISB
		BCF	STATUS, RP0	; Select bank 0

		CALL	LCDINIT		; Initialize LCDisplay

;*****************************************************************************
; Display some lines, here
;	----------------------
;	|This is on line :  0|
;	|This is on line :  1|
;	|This is on line :  2|
;	|This is on line :  3|
;	----------------------
; OK
;*****************************************************************************
		MOVLW	0x030		; ASCII '0'
		MOVWF	COUNT
		MOVLW	LCD_LINE0
		CALL	LCDSDDA		; Position cursor leftmost on first line
		CALL	TABLE_MSG	; Display message
		MOVLW	LCD_LINE0 + 0x013
		CALL	LCDSDDA		; Position cursor
		MOVF	COUNT, W
		CALL	LCDPUTCHAR	; Display line number

		INCF	COUNT, F
		MOVLW	LCD_LINE1
		CALL	LCDSDDA
		CALL	TABLE_MSG
		MOVLW	LCD_LINE1 + 0x013
		CALL	LCDSDDA
		MOVF	COUNT, W
		CALL	LCDPUTCHAR

		INCF	COUNT, F
		MOVLW	LCD_LINE2
		CALL	LCDSDDA
		CALL	TABLE_MSG
		MOVLW	LCD_LINE2 + 0x013
		CALL	LCDSDDA
		MOVF	COUNT, W
		CALL	LCDPUTCHAR

		INCF	COUNT, F
		MOVLW	LCD_LINE3
		CALL	LCDSDDA
		CALL	TABLE_MSG
		MOVLW	LCD_LINE3 + 0x013
		CALL	LCDSDDA
		MOVF	COUNT, W
		CALL	LCDPUTCHAR


;*****************************************************************************
; Program ends here
; OK
;*****************************************************************************
LOOP
		GOTO	LOOP		; Stay here forever


;*****************************************************************************
; Send a message using a table to output a message
; OK
;*****************************************************************************
TABLE_MSG
		MOVLW	0		; Startindex of table message
DISP_MSG
		MOVWF	TABLE_INDEX	; Holds message address
		CALL	MSG1
		ANDLW	0x0FF		; Check if at end of message
		BTFSC	STATUS, Z	; (zero returned at end)
		GOTO	TABLE_MSG_END             
		CALL	LCDPUTCHAR	; Display character
		MOVF	TABLE_INDEX, W	; Point to next character
		ADDLW	1
		GOTO	DISP_MSG
TABLE_MSG_END	RETURN





;*****************************************************************************
; LCD Module Subroutines
;*****************************************************************************
;
;=============================================================================
; LCDINIT
; Initilize LC-Display Module
; Should be modified to your needs (i.e. display type, cursor on/off, etc.)
; OK
;=============================================================================
LCDINIT
					; Busy-flag is not yet valid
		CLRF	LCD_CTRL	; ALL PORT output should output Low.
					; power-up delay
		MOVLW	0x01E
		CALL	X_DELAY500	; 30 * 0.5mS = 15mS
					; Busy Flag should be valid from here
		MOVLW	0x038		; 8-bit-interface, 2-lines
		CALL	LCDPUTCMD
		MOVLW	0x000		; disp.off, curs.off, no-blink
		CALL	LCDDMODE
		CALL	LCDCLEAR
		MOVLW	0x004		; disp.on, curs.off
		CALL	LCDDMODE
		MOVLW	0x002		; auto-inc (shift-cursor)
		CALL	LCDEMODE
		RETURN
;=============================================================================
; LCD_ENABLE
; Pulses LCD enable pin
; OK
;=============================================================================
LCD_ENABLE
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		RETURN
;=============================================================================
; LCDBUSY
; Returns when LCD busy-flag is inactive
; OK
;=============================================================================
LCDBUSY
		BSF	STATUS,RP0	; Select Register page 1
		MOVLW	0x0FF		; Set PORTB for input
		MOVWF	LCD_DATA_TRIS ;< compile warning 302 Register in operand not in bank 0.  Ensure that bank bits are correct.
		BCF	STATUS, RP0	; Select Register page 0
		BCF	LCD_CTRL, LCD_RS; Set LCD for command mode
		BSF	LCD_CTRL, LCD_RW; Setup to read busy flag
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	LCD_DATA, W	; Read busy flag + DDram address
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		ANDLW	0x80		; Check Busy flag, High = Busy
		BTFSS	STATUS, Z
		GOTO	LCDBUSY
LCDNOTBUSY	BCF	LCD_CTRL, LCD_RW
		BSF	STATUS, RP0	; Select Register page 1
		MOVLW	0x000
		MOVWF	LCD_DATA_TRIS	; Set PORTB for output ;< compile warning 302 Register in operand not in bank 0.  Ensure that bank bits are correct.
		BCF	STATUS, RP0	; Select Register page 0
		RETURN
;=============================================================================
; LCDCLEAR
; Clears display and returns cursor to home position (upper-left corner).
;
;=============================================================================
LCDCLEAR
		MOVLW	0x001
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDHOME
; Returns cursor to home position.
; Returns display to original position (when shifted).
;
;=============================================================================
LCDHOME
		MOVLW	0x002
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDEMODE
; Sets entry mode of display.
; Required entry mode must be set in W
;  b0	: 0 = no display shift	1 = display shift
;  b1	: 0 = auto-decrement	1 = auto-increment
;  b2-7	: don't care
; OK
;=============================================================================
LCDEMODE
		ANDLW	0x003		; Strip upper bits
		IORLW	0x004		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDDMODE
; Sets display control.
; Required display mode must be set in W
;  b0	: 0 = cursor blink off	1 = cursor blink on
;  b1	: 0 = cursor off	1 = cursor on
;  b2	: 0 = display off	1 = display on (display data remains in DDRAM)
;  b3-7	: don't care
; OK
;=============================================================================
LCDDMODE
		ANDLW	0x007		; Strip upper bits
		IORLW	0x008		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDSCGA
; Sets Character-Generator-RAM address. CGRAM is read/written after
;  this setting.
; Required CGRAM address must be set in W
;  b0-5	: required CGRAM address
;  b6-7	: don't care
;
;=============================================================================
LCDSCGA
		ANDLW	0x03F		; Strip upper bits
		IORLW	0x040		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDSDDA
; Sets the Display-Data-RAM address. DDRAM data is read/written after
;  this setting.
; Required DDRAM address must be set in W
;  b0-6	: required DDRAM address
;  b7	: don't care
; OK
;=============================================================================
LCDSDDA
		IORLW	0x080		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDGADDR
; Returns address counter contents, used for both DDRAM and CGRAM.
; RAM address is returned in W
;
;=============================================================================
LCDGADDR
		BSF	STATUS,RP0	; Select Register page 1
		MOVLW	0x0FF		; Set PORTB for input
		MOVWF	LCD_DATA_TRIS ;< compile warning 302 Register in operand not in bank 0.  Ensure that bank bits are correct.
		BCF	STATUS, RP0	; Select Register page 0
		BCF	LCD_CTRL, LCD_RS; Set LCD for command mode
		BSF	LCD_CTRL, LCD_RW; Setup to read busy flag
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	LCD_DATA, W	; Read busy flag + RAM address
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		ANDLW	0x07F		; Strip upper bit
		BCF	LCD_CTRL, LCD_RW
		BSF	STATUS, RP0	; Select Register page 1
		MOVLW	0x000
		MOVWF	LCD_DATA_TRIS	; Set PORTB for output getting ;< compile warning 302 Register in operand not in bank 0.  Ensure that bank bits are correct.
		BCF	STATUS, RP0	; Select Register page 0
		RETURN
;=============================================================================
; LCDPUTCHAR
; Sends character to LCD
; Required character must be in W
; OK
;=============================================================================
LCDPUTCHAR
		MOVWF	LCD_TEMP	; Character to be sent is in W
		CALL	LCDBUSY		; Wait for LCD to be ready
		BCF	LCD_CTRL, LCD_RW; Set LCD in read mode
		BSF	LCD_CTRL, LCD_RS; Set LCD in data mode
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	LCD_TEMP, W
		MOVWF	LCD_DATA	; Send data to LCD
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		RETURN
;=============================================================================
; LCDPUTCMD
; Sends command to LCD
; Required command must be in W
; OK
;=============================================================================
LCDPUTCMD
		MOVWF	LCD_TEMP	; Command to be sent is in W
		CALL	LCDBUSY		; Wait for LCD to be ready
		BCF	LCD_CTRL, LCD_RW; Set LCD in read mode
		BCF	LCD_CTRL, LCD_RS; Set LCD in command mode
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	LCD_TEMP, W
		MOVWF	LCD_DATA	; Send data to LCD
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		RETURN






;*****************************************************************************
; Delay_time	= ((DELAY_value * 3) + 4) * Cycle_time
; DELAY_value	= (Delay_time - (4 * Cycle_time)) / (3 * Cycle_time)
;
; i.e. (@ 4MHz crystal)
; Delay_time	= ((32 * 3) + 4) * 1uSec
;		= 100uSec
; DELAY_value	= (500uSec - 4) / 3
;		= 165.33
;		= 165
;*****************************************************************************
DELAY500	MOVLW	D'160'		; +1		1 cycle * increased to 240 just in case
		MOVWF	DELAY		; +2		1 cycle
DELAY500_LOOP	DECFSZ	DELAY, F	; step 1	1 cycle
		GOTO	DELAY500_LOOP	; step 2	2 cycles
DELAY500_END	RETURN			; +3		2 cycles
;
;
X_DELAY500	MOVWF	X_DELAY		; +1		1 cycle
X_DELAY500_LOOP	CALL	DELAY500	; step1		wait 500uSec
		DECFSZ	X_DELAY, F	; step2		1 cycle
		GOTO	X_DELAY500_LOOP	; step3		2 cycles
X_DELAY500_END	RETURN			; +2		2 cycles






;=============================================================================
; Table message to display
;=============================================================================
MSG1
		addwf	PCL ,F		;Jump to char pointed to in W reg
		retlw	'T'
		retlw	'h'
		retlw	'i'
		retlw	's'
		retlw	' '
		retlw	'i'
		retlw	's'
		retlw	' '
		retlw	'o'
		retlw	'n'
		retlw	' '
		retlw	'l'
		retlw	'i'
		retlw	'n'
		retlw	'e'
		retlw	' '
		retlw	':'
		retlw	' '
		retlw	' '
		retlw	'#'
MSG1_END
		retlw	0
;
;	IF ( (MSG1 & 0x0FF) >= (MSG1_END & 0x0FF) )
;		MESSG   "Warning - User Definded: Table 'MSG1' crosses page boundry in computed jump"
;	ENDIF


	END				; End of program
 

Re: Interfacing HD77480 to PIC16f648A-I/P

I don't see any comments addressing your questions about the use of C for this device.
Basically the C compiler used for all of the PIC12, PIC16 and PIC18 families of devices is the same and will generate the correct code when told the actual device in the IDE (you mention you are using the MAPLBx IDE and so I assume you also have the XC8 compiler available to you, even if not actually installed).
The main difference between devices is the register names which are defined (indirectly) by including the 'xc.h' header file - this uses the actual device name provided by the IDE to select the correct device specific header file. You will need to look at the data sheet to see the actual names of the registers but other than that, the use of the C compiler will make life a great deal easier for you. For example, you don't need to worry about the bank selections as the compiler will do this for you.
One comment I would make (regardless of whether you are using assembler or C) is to be careful of 'read-modify-write' conditions, especially with PORTA as you are using bits in that port to control the LCD chip. Check out Section 5.3 in the data sheet.
Susan
 
  • Like
Reactions: Twisty

    Twisty

    Points: 2
    Helpful Answer Positive Rating
Re: Interfacing HD77480 to PIC16f648A-I/P

I have one remaining little mystery relating to the snippet of code directly below. After doing 'MOVWF PCL' it appears that a 'RETURN' is being invoked and kicking the thread into the previous block. I bodged a fix by implementing the MOVWF using a dummy CALL but I would like to know if there is a cleaner way of getting a value from the PCL register.

Code:
	MOVF CHAR_INDEX_HIGH, W		 
	MOVWF PCLATH	 	
	MOVF CHAR_INDEX_LOW, W		;Recall char index into W
	MOVWF  PCL			;Look up the address and put char into W


I've got everything working though :) , it randomly selects and send any one of 25 four line messages to the LCD (100 strings in all)

So I needed a double lookup for getting the chars to send to the LCD and it took me a while working out how to do this within the 8bit offset limitation.

I've XC8 registered with MPLAB [1] and compiling but I ended up sticking with assembly for this project which was a learnding experience for me.

[1] v8.92 so it works with PICstart+ programmer, I had to manually run the .dll as admin to get it to register


*** And here is the full code. ***

Code:
;*****************************************************************************
; This program 'LCD.ASM' interfaces  with Hitachi HD47780-based LC-Display
;
; Assembles for: a PIC16F648A & 20 character x 4 line display
; 8-bit wide LCD dataline on Port B
; LCD control lines on RA 0,1,3
; Push button on RA2 to select messages 
;
; Original program adapted from Peter Ouwehand's Last update	96-01-12
; 
; Tom Webster 31 October 2016
;*****************************************************************************

	list      p=16f648A           ; list directive to define processor
    #include <p16F648A.inc>       ; processor specific variable definitions
    __CONFIG   _CP_OFF & _CPD_OFF & _LVP_OFF & _BOREN_ON & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTOSC_OSC_NOCLKOUT 

	 errorlevel  -302              ; suppress message 302 from list file compiler is too dumb to know if bank switching is correct or not

;*****************************************************************************
; Fosc		= 4MHz
; Instruction_time	= 1/Fosc / 4
;		= 1/(4*10^6) / 4
;		= 1uSec
;*****************************************************************************

;*****************************************************************************
; Equates, I/O, vars
;*****************************************************************************
RESET_V			EQU	0x0000		; Address of RESET Vector
ISR_V			EQU	0x0004		; Address of Interrupt Vector
OSC_FREQ		EQU	D'4000000'	; Oscillator Frequency is 4 MHz

LCD_DATA		EQU	PORTB		; LCD data lines interface
LCD_DATA_TRIS	EQU	TRISB
LCD_CTRL		EQU	PORTA		; LCD control lines interface

; Constants 
LCD_LINE0	EQU	0x00		; Used to set cursor positions
LCD_LINE1	EQU	0x40		; change these if not using
LCD_LINE2	EQU	0x14		; a 20x4 display!
LCD_LINE3	EQU	0x54

NO_OF_MESSAGES_PLUS_ONE EQU 0x64

; PORTA bits
LCD_E		EQU	3		;* LCD Enable control line connected to RA3
LCD_RW		EQU	1		;* LCD Read/Write control line connected to RA1
LCD_RS		EQU	0		;* LCD Register-Select control line connected to RA0

; PORTB bits
DB7		EQU	7		; LCD dataline 7 (MSB)
DB6		EQU	6		; LCD dataline 6
DB5		EQU	5		; LCD dataline 5
DB4		EQU	4		; LCD dataline 4
DB3		EQU	3		; LCD dataline 3
DB2		EQU	2		; LCD dataline 2
DB1		EQU	1		; LCD dataline 1
DB0		EQU	0		; LCD dataline 0 (LSB)

; misc.

LCD_TEMP		EQU	0x020		; Temporarally holds LCD charachter values
MESSAGE_INDEX	EQU	0x021		; Used to pick message to display
DELAY			equ	0x022		; Used in DELAYxxx routine
X_DELAY			equ	0x023		; Used in X_DELAYxxx routine
CHAR_INDEX_HIGH equ	0x024		; Stores the MSBs of address of message string in table (passed to PCLATH for goto)
CHAR_INDEX_LOW	EQU	0x025		; Stores 8 LSB of address of message string in table (passed to W for goto)
;COUNTER	EQU	0x026		; A counter, currently not used

;*****************************************************************************
; Program start
;*****************************************************************************
	ORG	RESET_V			; RESET vector location
RESET		GOTO	START



;*****************************************************************************
; This is the Periperal Interrupt routine. Should NOT get here as they are disabled!
;*****************************************************************************
	ORG	ISR_V			; Interrupt vector location
INTERRUPT	BCF     STATUS, RP0	; Select bank 0
		GOTO    INTERRUPT

;*****************************************************************************
; Initialize processor registers, etc
;*****************************************************************************
START					; POWER_ON Reset (Beginning of program)

		CLRF	STATUS		; Do initialization, Select bank 0
		CLRF	INTCON		; Clear int-flags, Disable interrupts
		CLRF	PCLATH		; All the first finctions are with 2k however
							; lookups are at at 0x1xx and data table at 0x2xx+
							; these are dealt locally by passing values to pclath

		CLRF PORTA ;Initialize output data latches
		CLRF PORTB

		BCF	STATUS, RP0	; Select bank 0
		MOVLW 0x07 ;Turn comparators off and
		MOVWF CMCON ;enable pins for I/O functions
		BCF STATUS, RP1
		BSF STATUS, RP0 ;Select Bank1
		MOVLW 0xF4 ; Value used to initialize data direction
		MOVWF TRISA ;Set RA<1:0> as outputs RA<2> as input RA<3> as input RA<7:4> as output 
		MOVLW	0x000		; RB7-0 all outputs
		MOVWF	TRISB
		BCF	STATUS, RP0	; Select bank 0
		MOVLW	0		; INITIALISE message index to 0
		MOVWF	MESSAGE_INDEX
		;GOTO	MAINLOOP    ; for debugging
		GOTO	LCDINIT		; Initialize LCDisplay

;=============================================================================
; DISPLAY
; Sets the relevant constants for 
; blocks would be added or removed for a display  with more or less lines	
;=============================================================================

DISPLAY
		MOVLW	LCD_LINE0   ; Moves the literal value to W, acting as a constant
		CALL	LCDSDDA		; Position cursor leftmost on first line
		CALL	MESSAGE_LOOKUP	; Lookup char and send it to LCD
		INCF	MESSAGE_INDEX, F ; Increment Message Index ready for next line
;		MOVLW	LCD_LINE0 + 0x013	;These would set cursor to end of the line
;		CALL	LCDSDDA				
;		MOVF	COUNT, W			;stick count into W 
;		CALL	LCDPUTCHAR			;then send it to LCD. Useful for debugging 

		MOVLW	LCD_LINE1
		CALL	LCDSDDA
		CALL	MESSAGE_LOOKUP
		INCF	MESSAGE_INDEX, F

		MOVLW	LCD_LINE2
		CALL	LCDSDDA
		CALL	MESSAGE_LOOKUP
		INCF	MESSAGE_INDEX, F

		MOVLW	LCD_LINE3
		CALL	LCDSDDA
		CALL	MESSAGE_LOOKUP
		INCF	MESSAGE_INDEX, F


;=============================================================================
; MAINLOOP
; The Program sits here after displaying message
; It checks if user has pressed button before displaying next message
; then loop cycles through the number of the message block to display next
; so if user holds button down the it sequentually cycles through each message
; but if they wait between pressing the button then it is message is randomised
;=============================================================================

MAINLOOP

					;If Message Index has gone beyond max then set it to 0. 
		MOVLW  	NO_OF_MESSAGES_PLUS_ONE      ; put the value to compare with in W
 		SUBWF  	MESSAGE_INDEX,W   ; subtract W from the File Register
                             ;  and put result in W (this preserve file register)
		BTFSC  	STATUS,Z    ; SKIP next instruction Zero flag not set
		CLRF	MESSAGE_INDEX

		MOVLW	0x5				;provide a window for button to be pressed before decrement kicks in		
		CALL	X_DELAY500 		; Also helps with debouncing

		BTFSS   PORTA, 2        ; SKIP next instruction if SET (switch open)
		GOTO   	NEXT_MESSAGE	; IF SWITCH is closed then do this instruction
								; Otherwise increment the Message Index by 4 lines
		MOVLW 0x4          ;The W register has the value '4'
        ADDWF MESSAGE_INDEX,1 ;The value of W is added to MESSAGE_INDEX
	GOTO	MAINLOOP

NEXT_MESSAGE
		MOVLW	0xFF		;even more debounce required
		CALL	X_DELAY500 	;so call a delay counter
	GOTO	LCDINIT


;*****************************************************************************
; LCD Routines
;*****************************************************************************
;
;=============================================================================
; LCDINIT
; Initilize LC-Display Module
; Can modify to your needs (i.e. display type, cursor on/off, etc.)
; See the call headers for bit pattern descriptions
;=============================================================================
LCDINIT
					; Busy-flag is not yet valid
		CLRF	LCD_CTRL	; ALL PORT output should output Low.
					; power-up delay
		MOVLW	0x01E
		CALL	X_DELAY500	; 30 * 0.5mS = 15mS
					; Busy Flag should be valid from here
		MOVLW	0x38		; 8-bit-interface, 4-lines
		CALL	LCDPUTCMD
		MOVLW	0x00		; b2 disp.off, b1 curs.off, b0 blink.off
		CALL	LCDDMODE
		CALL	LCDCLEAR
		MOVLW	0x07		; disp.on, curs.off
		CALL	LCDDMODE
		MOVLW	0x02		; auto-inc (shift-cursor)
		CALL	LCDEMODE
		GOTO 	DISPLAY
;=============================================================================
; LCD_ENABLE
; Pulses LCD enable pin
; OK
;=============================================================================
LCD_ENABLE
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		RETURN
;=============================================================================
; LCDBUSY
; Returns when LCD busy-flag is inactive
; OK
;=============================================================================
LCDBUSY
		BSF	STATUS,RP0	; Select Register page 1
		MOVLW	0xFF		; Set PORTB for input
		MOVWF	LCD_DATA_TRIS 
		BCF	STATUS, RP0	; Select Register page 0
		BCF	LCD_CTRL, LCD_RS; Set LCD for command mode
		BSF	LCD_CTRL, LCD_RW; Setup to read busy flag
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	LCD_DATA, W	; Read busy flag + DDram address
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		ANDLW	0x80		; Check Busy flag, High = Busy
		BTFSS	STATUS, Z
		GOTO	LCDBUSY
LCDNOTBUSY	BCF	LCD_CTRL, LCD_RW
		BSF	STATUS, RP0	; Select Register page 1
		MOVLW	0x000
		MOVWF	LCD_DATA_TRIS	; Set PORTB for output ;< compile warning 302 Register in operand not in bank 0.  Ensure that bank bits are correct.
		BCF	STATUS, RP0	; Select Register page 0
		RETURN
;=============================================================================
; LCDCLEAR
; Clears display and returns cursor to home position (upper-left corner).
; OK
;=============================================================================
LCDCLEAR
		MOVLW	0x01
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDHOME
; Returns cursor to home position.
; Returns display to original position (when shifted).
; OK
;=============================================================================
LCDHOME
		MOVLW	0x02
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDEMODE
; Sets entry mode of display.
; Required entry mode must be set in W
;  b0	: 0 = no display shift	1 = display shift
;  b1	: 0 = auto-decrement	1 = auto-increment
;  b2-7	: don't care
; OK
;=============================================================================
LCDEMODE
		ANDLW	0x03		; Strip upper bits
		IORLW	0x04		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDDMODE
; Sets display control.
; Required display mode must be set in W
;  b0	: 0 = cursor blink off	1 = cursor blink on
;  b1	: 0 = cursor off	1 = cursor on
;  b2	: 0 = display off	1 = display on (display data remains in DDRAM)
;  b3-7	: don't care
; OK
;=============================================================================
LCDDMODE
		ANDLW	0x07		; Strip upper bits
		IORLW	0x08		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDSCGA
; Sets Character-Generator-RAM address. CGRAM is read/written after
;  this setting.
; Required CGRAM address must be set in W
;  b0-5	: required CGRAM address
;  b6-7	: don't care
; OK
;=============================================================================
LCDSCGA
		ANDLW	0x3F		; Strip upper bits
		IORLW	0x40		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDSDDA
; Sets the Display-Data-RAM address. DDRAM data is read/written after
;  this setting.
; Required DDRAM address must be set in W
;  b0-6	: required DDRAM address
;  b7	: don't care
; OK
;=============================================================================
LCDSDDA
		IORLW	0x80		; Function set
		CALL	LCDPUTCMD
		RETURN
;=============================================================================
; LCDGADDR
; Returns address counter contents, used for both DDRAM and CGRAM.
; RAM address is returned in W
; OK
;=============================================================================
LCDGADDR
		BSF	STATUS,RP0	; Select Register page 1
		MOVLW	0xFF		; Set PORTB for input
		MOVWF	LCD_DATA_TRIS 
		BCF	STATUS, RP0	; Select Register page 0
		BCF	LCD_CTRL, LCD_RS; Set LCD for command mode
		BSF	LCD_CTRL, LCD_RW; Setup to read busy flag
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	LCD_DATA, W	; Read busy flag + RAM address
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		ANDLW	0x7F		; Strip upper bit
		BCF	LCD_CTRL, LCD_RW
		BSF	STATUS, RP0	; Select Register page 1
		MOVLW	0x00
		MOVWF	LCD_DATA_TRIS	; Set PORTB for output getting 
		BCF	STATUS, RP0	; Select Register page 0
		RETURN
;=============================================================================
; LCDPUTCHAR
; Sends character to LCD
; Required character must be in W
; OK
;=============================================================================
LCDPUTCHAR
		MOVWF	LCD_TEMP	; Character to be sent is in W
		CALL	LCDBUSY		; Wait for LCD to be ready
		BCF	LCD_CTRL, LCD_RW; Set LCD in read mode
		BSF	LCD_CTRL, LCD_RS; Set LCD in data mode
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	LCD_TEMP, W
		MOVWF	LCD_DATA	; Send data to LCD
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		RETURN
;=============================================================================
; LCDPUTCMD
; Sends command to LCD
; Required command must be in W
; OK
;=============================================================================
LCDPUTCMD
		MOVWF	LCD_TEMP	; Command to be sent is in W
		CALL	LCDBUSY		; Wait for LCD to be ready
		BCF	LCD_CTRL, LCD_RW; Set LCD in read mode
		BCF	LCD_CTRL, LCD_RS; Set LCD in command mode
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	LCD_TEMP, W
		MOVWF	LCD_DATA	; Send data to LCD
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		RETURN


;*****************************************************************************
; Delay_time	= ((DELAY_value * 3) + 4) * Cycle_time
; DELAY_value	= (Delay_time - (4 * Cycle_time)) / (3 * Cycle_time)
;
; i.e. (@ 4MHz crystal)
; Delay_time	= ((32 * 3) + 4) * 1uSec
;		= 100uSec
; DELAY_value	= (500uSec - 4) / 3
;		= 165.33
;		= 165
;*****************************************************************************
DELAY500	MOVLW	D'240'		; +1		1 cycle * increased to 240 just in case
		MOVWF	DELAY		; +2		1 cycle
DELAY500_LOOP	DECFSZ	DELAY, F	; step 1	1 cycle
		GOTO	DELAY500_LOOP	; step 2	2 cycles
DELAY500_END	RETURN			; +3		2 cycles
;
;
X_DELAY500	MOVWF	X_DELAY		; +1		1 cycle
X_DELAY500_LOOP	CALL	DELAY500	; step1		wait 500uSec
		DECFSZ	X_DELAY, F	; step2		1 cycle
		GOTO	X_DELAY500_LOOP	; step3		2 cycles
X_DELAY500_END	RETURN			; +2		2 cycles



;*****************************************************************************
; Message table routines
;*****************************************************************************
;=============================================================================
; MESSAGE_LOOKUP
; Looks up the literal address of the first char 
; for the message correlating to the value of MESSAGE_INDEX 
;=============================================================================
MESSAGE_LOOKUP
	MOVLW 0x01		 
	MOVWF PCLATH	 
	MOVF MESSAGE_INDEX, W ;Put Message Index into W
	CALL GET_MSG_ADDRESS_HIGH ;start double lookup
	MOVWF CHAR_INDEX_HIGH
	MOVF MESSAGE_INDEX, W ;Put Message Index into W
	CALL GET_MSG_ADDRESS_LOW
	MOVWF CHAR_INDEX_LOW	; This is the base index for the defined string

;=============================================================================
; MESSAGE_LOOKUP
; Sequentually gets each charachter for the relevant message (1 line) 
; and send it to the screen
;=============================================================================
MESSAGE_OUTPUT
	MOVLW	0x3F		;delay for slow charachter display effect
	CALL	X_DELAY500 	; not required
	CALL MESSAGE_OUTPUT_CHAR	;for some reason calling movwf pcl invokes a return
							;so calling it in a subroutine to get around this!
	CLRF	PCLATH			; clear pclath for upcoming calls
	ADDLW 0					;If reached'0x000' at end of message 
	BTFSC STATUS, Z			;Then return back to message display funtion
	GOTO MESSAGE_OUTPUT_END
	CALL LCDPUTCHAR			;Otherwise send char to LCD
	INCFSZ CHAR_INDEX_LOW,f	;then increment the char index (INCSNZ not availible)
		GOTO MESSAGE_OUTPUT		;do it all over again
	incf CHAR_INDEX_HIGH,F ;If Index low is 0 then it has just rolled over so increment the high index
	GOTO MESSAGE_OUTPUT		;do it all over again
	
MESSAGE_OUTPUT_CHAR
	MOVF CHAR_INDEX_HIGH, W		 
	MOVWF PCLATH	 	
	MOVF CHAR_INDEX_LOW, W		;Recall char index into W
	MOVWF  PCL			;Look up the address and put char into W
	RETURN	

MESSAGE_OUTPUT_END
	RETURN

;=============================================================================
; GET_MSG_ADDRESS_HIGH & LOW
; Lookup tables list of strings, one for each message
; returns the literal address
; W is only 8 bits but address is 10bits hence split into HIGH and LOW.
; I've started it on a new chunk to make
; it easy to deal with PCLATH for calling these functions
;=============================================================================
	org 0x100

GET_MSG_ADDRESS_HIGH ; first step of double lookup beginning of the selected message address and put into W register
	ADDWF PCL, F
	RETLW HIGH(MSG0)	
	RETLW HIGH(MSG1)
	RETLW HIGH(MSG2)
	RETLW HIGH(MSG3)
	RETLW HIGH(MSG4)
	RETLW HIGH(MSG5)
	RETLW HIGH(MSG6)
	RETLW HIGH(MSG7)
	RETLW HIGH(MSG8)
	RETLW HIGH(MSG9)

	RETLW HIGH(MSG10)
	RETLW HIGH(MSG11)
	RETLW HIGH(MSG12)
	RETLW HIGH(MSG13)
	RETLW HIGH(MSG14)
	RETLW HIGH(MSG15)
	RETLW HIGH(MSG16)
	RETLW HIGH(MSG17)
	RETLW HIGH(MSG18)
	RETLW HIGH(MSG19)

	RETLW HIGH(MSG20)
	RETLW HIGH(MSG21)
	RETLW HIGH(MSG22)
	RETLW HIGH(MSG23)
	RETLW HIGH(MSG24)
	RETLW HIGH(MSG25)
	RETLW HIGH(MSG26)
	RETLW HIGH(MSG27)
	RETLW HIGH(MSG28)
	RETLW HIGH(MSG29)

	RETLW HIGH(MSG30)
	RETLW HIGH(MSG31)
	RETLW HIGH(MSG32)
	RETLW HIGH(MSG33)
	RETLW HIGH(MSG34)
	RETLW HIGH(MSG35)
	RETLW HIGH(MSG36)
	RETLW HIGH(MSG37)
	RETLW HIGH(MSG38)
	RETLW HIGH(MSG39)

	RETLW HIGH(MSG40)
	RETLW HIGH(MSG41)
	RETLW HIGH(MSG42)
	RETLW HIGH(MSG43)
	RETLW HIGH(MSG44)
	RETLW HIGH(MSG45)
	RETLW HIGH(MSG46)
	RETLW HIGH(MSG47)
	RETLW HIGH(MSG48)
	RETLW HIGH(MSG49)

	RETLW HIGH(MSG50)
	RETLW HIGH(MSG51)
	RETLW HIGH(MSG52)
	RETLW HIGH(MSG53)
	RETLW HIGH(MSG54)
	RETLW HIGH(MSG55)
	RETLW HIGH(MSG56)
	RETLW HIGH(MSG57)
	RETLW HIGH(MSG58)
	RETLW HIGH(MSG59)

	RETLW HIGH(MSG60)
	RETLW HIGH(MSG61)
	RETLW HIGH(MSG62)
	RETLW HIGH(MSG63)
	RETLW HIGH(MSG64)
	RETLW HIGH(MSG65)
	RETLW HIGH(MSG66)
	RETLW HIGH(MSG67)
	RETLW HIGH(MSG68)
	RETLW HIGH(MSG69)

	RETLW HIGH(MSG70)
	RETLW HIGH(MSG71)
	RETLW HIGH(MSG72)
	RETLW HIGH(MSG73)
	RETLW HIGH(MSG74)
	RETLW HIGH(MSG75)
	RETLW HIGH(MSG76)
	RETLW HIGH(MSG77)
	RETLW HIGH(MSG78)
	RETLW HIGH(MSG79)

	RETLW HIGH(MSG80)
	RETLW HIGH(MSG81)
	RETLW HIGH(MSG82)
	RETLW HIGH(MSG83)
	RETLW HIGH(MSG84)
	RETLW HIGH(MSG85)
	RETLW HIGH(MSG86)
	RETLW HIGH(MSG87)
	RETLW HIGH(MSG88)
	RETLW HIGH(MSG89)

	RETLW HIGH(MSG90)
	RETLW HIGH(MSG91)
	RETLW HIGH(MSG92)
	RETLW HIGH(MSG93)
	RETLW HIGH(MSG94)
	RETLW HIGH(MSG95)
	RETLW HIGH(MSG96)
	RETLW HIGH(MSG97)
	RETLW HIGH(MSG98)
	RETLW HIGH(MSG99)



GET_MSG_ADDRESS_LOW ; first step of double lookup beginning of the selected message address and put into W register
	ADDWF PCL, F
	RETLW LOW(MSG0)	
	RETLW LOW(MSG1)
	RETLW LOW(MSG2)	
	RETLW LOW(MSG3)
	RETLW LOW(MSG4)
	RETLW LOW(MSG5)
	RETLW LOW(MSG6)
	RETLW LOW(MSG7)
	RETLW LOW(MSG8)
	RETLW LOW(MSG9)

	RETLW LOW(MSG10)
	RETLW LOW(MSG11)
	RETLW LOW(MSG12)
	RETLW LOW(MSG13)
	RETLW LOW(MSG14)
	RETLW LOW(MSG15)
	RETLW LOW(MSG16)
	RETLW LOW(MSG17)
	RETLW LOW(MSG18)
	RETLW LOW(MSG19)

	RETLW LOW(MSG20)
	RETLW LOW(MSG21)
	RETLW LOW(MSG22)
	RETLW LOW(MSG23)
	RETLW LOW(MSG24)
	RETLW LOW(MSG25)
	RETLW LOW(MSG26)
	RETLW LOW(MSG27)
	RETLW LOW(MSG28)
	RETLW LOW(MSG29)

	RETLW LOW(MSG30)
	RETLW LOW(MSG31)
	RETLW LOW(MSG32)
	RETLW LOW(MSG33)
	RETLW LOW(MSG34)
	RETLW LOW(MSG35)
	RETLW LOW(MSG36)
	RETLW LOW(MSG37)
	RETLW LOW(MSG38)
	RETLW LOW(MSG39)

	RETLW LOW(MSG40)
	RETLW LOW(MSG41)
	RETLW LOW(MSG42)
	RETLW LOW(MSG43)
	RETLW LOW(MSG44)
	RETLW LOW(MSG45)
	RETLW LOW(MSG46)
	RETLW LOW(MSG47)
	RETLW LOW(MSG48)
	RETLW LOW(MSG49)

	RETLW LOW(MSG50)
	RETLW LOW(MSG51)
	RETLW LOW(MSG52)
	RETLW LOW(MSG53)
	RETLW LOW(MSG54)
	RETLW LOW(MSG55)
	RETLW LOW(MSG56)
	RETLW LOW(MSG57)
	RETLW LOW(MSG58)
	RETLW LOW(MSG59)

	RETLW LOW(MSG60)
	RETLW LOW(MSG61)
	RETLW LOW(MSG62)
	RETLW LOW(MSG63)
	RETLW LOW(MSG64)
	RETLW LOW(MSG65)
	RETLW LOW(MSG66)
	RETLW LOW(MSG67)
	RETLW LOW(MSG68)
	RETLW LOW(MSG69)

	RETLW LOW(MSG70)
	RETLW LOW(MSG71)
	RETLW LOW(MSG72)
	RETLW LOW(MSG73)
	RETLW LOW(MSG74)
	RETLW LOW(MSG75)
	RETLW LOW(MSG76)
	RETLW LOW(MSG77)
	RETLW LOW(MSG78)
	RETLW LOW(MSG79)

	RETLW LOW(MSG80)
	RETLW LOW(MSG81)
	RETLW LOW(MSG82)
	RETLW LOW(MSG83)
	RETLW LOW(MSG84)
	RETLW LOW(MSG85)
	RETLW LOW(MSG86)
	RETLW LOW(MSG87)
	RETLW LOW(MSG88)
	RETLW LOW(MSG89)

	RETLW LOW(MSG90)
	RETLW LOW(MSG91)
	RETLW LOW(MSG92)
	RETLW LOW(MSG93)
	RETLW LOW(MSG94)
	RETLW LOW(MSG95)
	RETLW LOW(MSG96)
	RETLW LOW(MSG97)
	RETLW LOW(MSG98)
	RETLW LOW(MSG99)

;=============================================================================
; CHAR_TABLE
; Big list of strings, one for each message
; a zero at the end of each string so the loop that calls them
; can detect the end of the string
; I've started it on a new chunk so that the first char is at a 00 address
; this was useful for a previous version of the code, it isn't any longer
;=============================================================================
	org 0x200		;Start on a new 256 byte chunk

;20 ch max  12345678901234567890		
MSG0	DT "ÿ** Message  #00 **ÿ", 0 
MSG1	DT "ÿ** Message  #01 **ÿ", 0
MSG2	DT "ÿ** Message  #02 **ÿ", 0
MSG3	DT "ÿ** Message  #03 **ÿ", 0

MSG4	DT "ÿ** Message  #04 **ÿ", 0
MSG5	DT "ÿ** Message  #05 **ÿ", 0
MSG6	DT "ÿ** Message  #06 **ÿ", 0
MSG7	DT "ÿ** Message  #07 **ÿ", 0

MSG8	DT "ÿ** Message  #08 **ÿ", 0
MSG9	DT "ÿ** Message  #09 **ÿ", 0
MSG10	DT "ÿ** Message  #10 **ÿ", 0
MSG11	DT "ÿ** Message  #11 **ÿ", 0

MSG12	DT "ÿ** Message  #12 **ÿ", 0
MSG13	DT "ÿ** Message  #13 **ÿ", 0
MSG14	DT "ÿ** Message  #14 **ÿ", 0
MSG15	DT "ÿ** Message  #15 **ÿ", 0

MSG16	DT "ÿ** Message  #16 **ÿ", 0
MSG17	DT "ÿ** Message  #17 **ÿ", 0
MSG18	DT "ÿ** Message  #18 **ÿ", 0
MSG19	DT "ÿ** Message  #19 **ÿ", 0		

MSG20	DT "ÿ** Message  #20 **ÿ", 0
MSG21	DT "ÿ** Message  #21 **ÿ", 0
MSG22	DT "ÿ** Message  #22 **ÿ", 0
MSG23	DT "ÿ** Message  #23 **ÿ", 0		

MSG24	DT "ÿ** Message  #24 **ÿ", 0
MSG25	DT "ÿ** Message  #25 **ÿ", 0
MSG26	DT "ÿ** Message  #26 **ÿ", 0
MSG27	DT "ÿ** Message  #27 **ÿ", 0		

MSG28	DT "ÿ** Message  #28 **ÿ", 0
MSG29	DT "ÿ** Message  #29 **ÿ", 0
MSG30	DT "ÿ** Message  #30 **ÿ", 0
MSG31	DT "ÿ** Message  #31 **ÿ", 0		

MSG32	DT "ÿ** Message  #32 **ÿ", 0
MSG33	DT "ÿ** Message  #33 **ÿ", 0
MSG34	DT "ÿ** Message  #34 **ÿ", 0
MSG35	DT "ÿ** Message  #35 **ÿ", 0		

MSG36	DT "ÿ** Message  #36 **ÿ", 0
MSG37	DT "ÿ** Message  #37 **ÿ", 0
MSG38	DT "ÿ** Message  #38 **ÿ", 0
MSG39	DT "ÿ** Message  #39 **ÿ", 0		

MSG40	DT "ÿ** Message  #40 **ÿ", 0
MSG41	DT "ÿ** Message  #41 **ÿ", 0		
MSG42	DT "ÿ** Message  #42 **ÿ", 0
MSG43	DT "ÿ** Message  #43 **ÿ", 0

MSG44	DT "ÿ** Message  #44 **ÿ", 0
MSG45	DT "ÿ** Message  #45 **ÿ", 0		
MSG46	DT "ÿ** Message  #46 **ÿ", 0
MSG47	DT "ÿ** Message  #47 **ÿ", 0

MSG48	DT "ÿ** Message  #48 **ÿ", 0
MSG49	DT "ÿ** Message  #49 **ÿ", 0
MSG50	DT "ÿ** Message  #50 **ÿ", 0
MSG51	DT "ÿ** Message  #51 **ÿ", 0		

MSG52	DT "ÿ** Message  #52 **ÿ", 0
MSG53	DT "ÿ** Message  #53 **ÿ", 0
MSG54	DT "ÿ** Message  #54 **ÿ", 0
MSG55	DT "ÿ** Message  #55 **ÿ", 0		

MSG56	DT "ÿ** Message  #56 **ÿ", 0
MSG57	DT "ÿ** Message  #57 **ÿ", 0
MSG58	DT "ÿ** Message  #58 **ÿ", 0
MSG59	DT "ÿ** Message  #59 **ÿ", 0	

MSG60	DT "ÿ** Message  #60 **ÿ", 0
MSG61	DT "ÿ** Message  #61 **ÿ", 0		
MSG62	DT "ÿ** Message  #62 **ÿ", 0
MSG63	DT "ÿ** Message  #63 **ÿ", 0

MSG64	DT "ÿ** Message  #64 **ÿ", 0
MSG65	DT "ÿ** Message  #65 **ÿ", 0		
MSG66	DT "ÿ** Message  #66 **ÿ", 0
MSG67	DT "ÿ** Message  #67 **ÿ", 0

MSG68	DT "ÿ** Message  #68 **ÿ", 0
MSG69	DT "ÿ** Message  #69 **ÿ", 0
MSG70	DT "ÿ** Message  #70 **ÿ", 0
MSG71	DT "ÿ** Message  #71 **ÿ", 0		

MSG72	DT "ÿ** Message  #72 **ÿ", 0
MSG73	DT "ÿ** Message  #73 **ÿ", 0
MSG74	DT "ÿ** Message  #74 **ÿ", 0
MSG75	DT "ÿ** Message  #75 **ÿ", 0		

MSG76	DT "ÿ** Message  #76 **ÿ", 0
MSG77	DT "ÿ** Message  #77 **ÿ", 0
MSG78	DT "ÿ** Message  #78 **ÿ", 0
MSG79	DT "ÿ** Message  #79 **ÿ", 0

MSG80	DT "ÿ** Message  #80 **ÿ", 0
MSG81	DT "ÿ** Message  #81 **ÿ", 0		
MSG82	DT "ÿ** Message  #82 **ÿ", 0
MSG83	DT "ÿ** Message  #83 **ÿ", 0

MSG84	DT "ÿ** Message  #84 **ÿ", 0
MSG85	DT "ÿ** Message  #85 **ÿ", 0		
MSG86	DT "ÿ** Message  #86 **ÿ", 0
MSG87	DT "ÿ** Message  #87 **ÿ", 0

MSG88	DT "ÿ** Message  #88 **ÿ", 0
MSG89	DT "ÿ** Message  #89 **ÿ", 0
MSG90	DT "ÿ** Message  #90 **ÿ", 0
MSG91	DT "ÿ** Message  #91 **ÿ", 0

MSG92	DT "ÿ** Message  #92 **ÿ", 0
MSG93	DT "ÿ** Message  #93 **ÿ", 0
MSG94	DT "ÿ** Message  #94 **ÿ", 0
MSG95	DT "ÿ** Message  #95 **ÿ", 0		

MSG96	DT "ÿ** Message  #96 **ÿ", 0
MSG97	DT "ÿ** Message  #97 **ÿ", 0
MSG98	DT "ÿ** Message  #98 **ÿ", 0
MSG99	DT "ÿ** Message  #99 **ÿ", 0

	END				; End of program

; This compiler directive does not work with MPLAB!
;	IF ( (GET_CHAR & 0x0FF) >= (MSG39 & 0x0FF) )
;		MESSG   "Warning - User Definded: Table crosses page boundry in computed jump"
;	ENDIF
 

I've completed the coding for my little LCD project now so I am sharing the code back in case any of it is helpful for people in the future.

I've tried to squeeze as much as I could into the 4k program memory. It looks up groups of messages and CGRAM sets from tables, displays the messages in different ways and a few other things.

It's the first time I've coded in assembly and welcome any feedback on how I can improve.

Code:
;=================================================================================================
; 'LCD.ASM' 
;
; Assembles for: PIC16F648A & 20 characters x 4 line HD47780-based LC-Display
; Connected by: 8-bit LCD dataline on Port B
; LCD control lines on RA 0,1,3
; Push button input on RA2  
;
; FUNCTIONS:
; Sequential or random selection of messages from table via lookup
; Currently 42 x 4 line messages
;
; Different display modes for messages (currently 4) via lookup table
; Different Custom Character Graphics sets for each message via lookup table
; Option to periodically alternate CGRAM on displayed message
; 
; By Tom Webster 3 November 2016
;=================================================================================================


	list      p=16f648A           ; list directive to define processor
    #include <p16F648A.inc>       ; processor specific variable definitions
    __CONFIG   _CP_OFF & _CPD_OFF & _LVP_OFF & _BOREN_ON & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTOSC_OSC_NOCLKOUT 

	 errorlevel  -302 ; suppress message 302 from list file compiler is too dumb to know if bank switching is correct or not

;************************************
; Fosc		= 4MHz					*
; Instruction_time	= 1/Fosc / 4	*
;		= 1/(4*10^6) / 4			*
;		= 1uSec						*
;************************************


;*************************************************************************************************
; Equates, I/O, vars
RESET_V			EQU	0x0000		; Address of RESET Vector
ISR_V			EQU	0x0004		; Address of Interrupt Vector
OSC_FREQ		EQU	D'4000000'	; Oscillator Frequency is 4 MHz

LCD_DATA		EQU	PORTB		; LCD data lines interface
LCD_DATA_TRIS	EQU	TRISB
LCD_CTRL		EQU	PORTA		; LCD control lines interface

; Constants 
;LCD_LINE0		EQU 0x00	;Using constants for LCD starting positions for line 0 & 2
LCD_LINE2		EQU 0x14	;because they never change
NO_OF_MESSAGES 	EQU D'42'	;message index actually starts at 0 but that is good as we 
							;actually want to use no +1 in the calculations.
DELAY500ns		EQU D'165'
DELAY15ms		EQU	D'30'

; PORTA bits
LCD_E		EQU	3		;* LCD Enable control line connected to RA3
LCD_RW		EQU	1		;* LCD Read/Write control line connected to RA1
LCD_RS		EQU	0		;* LCD Register-Select control line connected to RA0

; PORTB bits
DB7		EQU	7		; LCD dataline 7 (MSB)
DB6		EQU	6		; LCD dataline 6
DB5		EQU	5		; LCD dataline 5
DB4		EQU	4		; LCD dataline 4
DB3		EQU	3		; LCD dataline 3
DB2		EQU	2		; LCD dataline 2
DB1		EQU	1		; LCD dataline 1
DB0		EQU	0		; LCD dataline 0 (LSB)

; misc.

;LCD_LINE0			EQU	0x28		; Used to set cursor positions
LCD_LINE1			EQU	0x20		; change these if not using
;LCD_LINE2			EQU	0x30		; a 20x4 display!
LCD_LINE3			EQU	0x21		; set by 'DISPLAY_MODE' passed to 'DISPLAY' routine
MESSAGE_INDEX		EQU	0x22		; Used to pick message to display
CHAR_INDEX_HIGH		EQU	0x23		; Stores the MSBs of address (passed to PCLATH for goto)
CHAR_INDEX_LOW		EQU	0x24		; Stores 8 LSB of address (passed to W for goto)
CURSOR_MODE			EQU	0x25		; Set by 'DISPLAY_MODE' 
ENTRY_MODE			EQU	0x26		; Set by 'DISPLAY_MODE'
CG_MODE				equ	0x27		; Set by 'DISPLAY_MODE'
TEMP_DELAY500		EQU	0x28		; Used in DELAY500ns routine
TEMP_DELAY15		EQU	0x29		; Used in DELAY15ms routine passed to 
X_DELAY				EQU	0x30		; set by 'DISPLAY_MODE' then passed to X_DELAYxx routine
TEMP_0				EQU	0x31		; Used in 'LCDPUT*' ROUTINES and X_DELAYxx
TEMP_A				EQU	0x32		; Used in message_backwards routine AND cg_ram_set routine
TEMP_B				EQU	0x33		; used in message_backwards routine



;*************************************************************************************************
; Program start
	ORG	RESET_V			; RESET vector location
RESET		GOTO	START



;*************************************************************************************************
; This is the Peripheral Interrupt routine. Should NOT get here as they are disabled!
	ORG	ISR_V			; Interrupt vector location
INTERRUPT	BCF     STATUS, RP0	; Select bank 0
		GOTO    INTERRUPT

;*************************************************************************************************
; Initialize processor registers, etc
START					; POWER_ON Reset (Beginning of program)
		CLRF	STATUS		; Do initialization, Select bank 0
		CLRF	INTCON		; Clear int-flags, Disable interrupts
		CLRF	PCLATH		; All the first functions are with 2k however
							; lookups are at 0x1xx and data table at 0x2xx+
							; these are dealt locally by passing values to pclath

		CLRF PORTA ;Initialize output data latches

		BCF STATUS, RP1
		BSF STATUS, RP0 ;Select Bank1
		MOVLW 0xF4 ; Value used to initialize data direction
		MOVWF TRISA ;Set RA<1:0> as outputs RA<2> as input RA<3> as input RA<7:4> as output 
		MOVLW	0x0		; RB7-0 all outputs
		MOVWF	TRISB
		BCF	STATUS, RP0	; Select bank 0
		MOVLW 0x07 ;Turn comparators off and
		MOVWF CMCON ;enable pins for I/O functions

		MOVLW	0		; INITIALISE the vars
		MOVWF	MESSAGE_INDEX

;=================================================================================================
; LCDINIT
; Initialise LC-Display Module
; See the subroutine headers for bit pattern descriptions
;-------------------------------------------------------------------------------------------------
LCDINIT
					; Busy-flag is not yet valid
		CLRF	LCD_CTRL	; ALL PORT output should output Low.
					; power-up delay
	
		CALL	DELAY_15ms	; 30 * 0.5mS = 15mS
					; Busy Flag should be valid from here
		MOVLW	0x38		; 8-bit-interface, 4-lines
		CALL	LCDPUTCMD

;=================================================================================================
; DISPLAY
; Sets the relevant constants for 
; blocks would be added or removed for a display  with more or less lines	
;-------------------------------------------------------------------------------------------------

DISPLAY
		
					;LCDCLEAR WAS HERE
		MOVLW	HIGH(DISPLAY_MODE)	; Following functions start at org 0x100
		MOVWF	PCLATH
		movf	MESSAGE_INDEX,w	
		CALL 	DISPLAY_MODE ; sets the values for display of current message
		
DISPLAY_1
		CLRF	PCLATH		; following functions are back in first 2k.
		CALL	LCDDMODE
		CALL	LCDEMODE
		CALL	CG_SET
		CALL	LCDCLEAR	;Clear the display ready for next message
		CALL	MESSAGE_OUT	; Lookup char and send it to LCD

;		MOVLW	LCD_LINE0 + 0x013	;These would set cursor to end of the line
;		CALL	LCDSDDA				
;		MOVF	COUNT, W			;stick count into W 
;		CALL	LCDPUTCHAR			;then send it to LCD. Useful for debugging 
		 		
		CALL	DISPLAY_2
		movf	LCD_LINE1,w 
		CALL    DISPLAY_3

		CALL	DISPLAY_2
		movlw	LCD_LINE2
		CALL    DISPLAY_3

		CALL	DISPLAY_2
		movf	LCD_LINE3,w 
		CALL    DISPLAY_3

		INCF	MESSAGE_INDEX,f ; Increment Message Index ready for next line

								;do a delay so that if the user is holding the 
								;next message button down
		MOVLW 0x40				;they can read the message before
		CALL X_DELAY_15ms		;the next message is displayed
								;also debounces after a fast message
		CLRF TEMP_0
		MOVLW 0x50
		MOVWF X_DELAY
		

;=================================================================================================
; MAINLOOP
; The Program sits here after displaying message
; It checks if user has pressed button before displaying next message
; then loop cycles through the number of the message block to display next
; so if user holds button down the it sequentially cycles through each message
; but if they wait between pressing the button then it is message is randomised
;-------------------------------------------------------------------------------------------------

MAINLOOP

					;If Message Index has gone beyond max then set it to 0. 
		MOVLW  	NO_OF_MESSAGES      ; put the value to compare with in W
 		SUBWF  	MESSAGE_INDEX,w   ; subtract W from the File Register
                             ;  and put result in W (this preserve file register)
		BTFSC  	STATUS,Z    ; SKIP next instruction Zero flag not set
			CALL MAINLOOP_1
		BTFSS   PORTA, 2        ; SKIP next instruction if SET (switch open)
			GOTO   	DISPLAY	; IF SWITCH is closed then show next message
		INCF MESSAGE_INDEX,f ; Otherwise increment the Message Index 
	GOTO	MAINLOOP

MAINLOOP_1
		CLRF	MESSAGE_INDEX
		decfsz 	TEMP_0,F
	RETURN
			DECFSZ X_DELAY,F
	RETURN
			SWAPF 	CG_MODE,F
			CALL 	CG_SET
			MOVLW 0x50
			MOVWF X_DELAY
	RETURN



;=================================================================================================
; DISPLAY SUB-ROUTINES
; These are here so that the display thread flows into MAINLOOP
; Yes, I am pedantic enough to do this just to save a GOTO instruction. in a previous iteration 
; I optimised instructions where I could to get everything but the char table in the first 2k
;-------------------------------------------------------------------------------------------------
DISPLAY_2
		SWAPF 	ENTRY_MODE,f
		;movf	ENTRY_MODE,w ; put in w register	
		CALL	LCDEMODE
	RETURN

DISPLAY_3
		CALL	LCDSDDA
		CALL	MESSAGE_OUT_1
	RETURN	

;=================================================================================================
; LCD SUB ROUTINES
;=================================================================================================
; LCD_ENABLE
; Pulses LCD enable pin
; Currently not required
;-------------------------------------------------------------------------------------------------
;LCD_ENABLE
;		BSF	LCD_CTRL, LCD_E	; LCD E-line High
;		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
;		RETURN

;=================================================================================================
; LCDBUSY
; Returns when LCD busy-flag is inactive
;-------------------------------------------------------------------------------------------------
LCDBUSY
		BSF	STATUS,RP0	; Select Register page 1
		MOVLW	0xFF		; Set PORTB for input
		MOVWF	LCD_DATA_TRIS 
		BCF	STATUS, RP0	; Select Register page 0
		BCF	LCD_CTRL, LCD_RS; Set LCD for command mode
		BSF	LCD_CTRL, LCD_RW; Setup to read busy flag
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	LCD_DATA, W	; Read busy flag + DDram address
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		ANDLW	0x80		; Check Busy flag, High = Busy
		BTFSS	STATUS, Z
		GOTO	LCDBUSY
LCDNOTBUSY	BCF	LCD_CTRL, LCD_RW
		BSF	STATUS, RP0	; Select Register page 1
		MOVLW	0x00
		MOVWF	LCD_DATA_TRIS	; Set PORTB for output
		BCF	STATUS, RP0	; Select Register page 0
		RETURN

;=================================================================================================
; LCDCLEAR
; Clears display and returns cursor to home position (upper-left corner).
;-------------------------------------------------------------------------------------------------
LCDCLEAR
		MOVLW	0x01
		CALL	LCDPUTCMD
		RETURN

;=================================================================================================
; LCDHOME
; Returns cursor to home position.
; Returns display to original position (when shifted).
; Currently not required
;-------------------------------------------------------------------------------------------------
;LCDHOME
;		MOVLW	0x02
;		CALL	LCDPUTCMD
;		RETURN

;=================================================================================================
; LCDEMODE
; Sets entry mode of display.
; Required entry mode must be set in W
;  b0	: 0 = no display shift	1 = display shift
;  b1	: 0 = auto-decrement	1 = auto-increment
;  b2-7	: don't care
;-------------------------------------------------------------------------------------------------
LCDEMODE
		movf	ENTRY_MODE,w ; set entry mode
		ANDLW	0x03		 ; Strip upper bits
		IORLW	0x04		 ; Function set
		CALL	LCDPUTCMD
		RETURN

;=================================================================================================
; LCDDMODE
; Sets display control.
; Required display mode must be set in W
;  b0	: 0 = cursor blink off	1 = cursor blink on
;  b1	: 0 = cursor off	1 = cursor on
;  b2	: 0 = display off	1 = display on (display data remains in DDRAM)
;  b3-7	: don't care
;-------------------------------------------------------------------------------------------------
LCDDMODE
		movf	CURSOR_MODE,w
		ANDLW	0x07		; Strip upper bits
		IORLW	0x08		; Function set
		CALL	LCDPUTCMD
		RETURN

;=================================================================================================
; LCDSCGA
; Sets Character-Generator-RAM address. CGRAM is read/written after
;  this setting.
; Required CGRAM address must be set in W
;  b0-5	: required CGRAM address
;  b6-7	: don't care
; currently not required
;-------------------------------------------------------------------------------------------------
LCDSCGA
		ANDLW	0x3F		; Strip upper bits
		IORLW	0x40		; Function set
		CALL  LCDPUTCMD
		RETURN

;=================================================================================================
; LCDSDDA
; Sets the Display-Data-RAM address. DDRAM data is read/written after
;  this setting.
; Required DDRAM address must be set in W
;  b0-6	: required DDRAM address
;  b7	: don't care
;-------------------------------------------------------------------------------------------------
LCDSDDA
		IORLW	0x80		; Function set
		CALL	LCDPUTCMD
		RETURN

;=================================================================================================
; LCDGADDR
; Returns address counter contents, used for both DDRAM and CGRAM.
; RAM address is returned in W
; Currently not required
;-------------------------------------------------------------------------------------------------
;LCDGADDR
;		BSF	STATUS,RP0	; Select Register page 1
;		MOVLW	0xFF		; Set PORTB for input
;		MOVWF	LCD_DATA_TRIS 
;		BCF	STATUS, RP0	; Select Register page 0
;		BCF	LCD_CTRL, LCD_RS; Set LCD for command mode
;		BSF	LCD_CTRL, LCD_RW; Setup to read busy flag
;		BSF	LCD_CTRL, LCD_E	; LCD E-line High
;		MOVF	LCD_DATA, W	; Read busy flag + RAM address
;		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
;		ANDLW	0x7F		; Strip upper bit
;		BCF	LCD_CTRL, LCD_RW
;		BSF	STATUS, RP0	; Select Register page 1
;		MOVLW	0x00
;		MOVWF	LCD_DATA_TRIS	; Set PORTB for output getting 
;		BCF	STATUS, RP0	; Select Register page 0
;		RETURN

;=================================================================================================
; LCDPUTCHAR
; Sends character to LCD
; Required character must be in W
;-------------------------------------------------------------------------------------------------
LCDPUTCHAR
		MOVWF	TEMP_0	; Character to be sent is in W
		CALL	LCDBUSY		; Wait for LCD to be ready
		BCF	LCD_CTRL, LCD_RW; Set LCD in read mode
		BSF	LCD_CTRL, LCD_RS; Set LCD in data mode
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	TEMP_0, W
		MOVWF	LCD_DATA	; Send data to LCD
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		RETURN

;=================================================================================================
; LCDPUTCMD
; Sends command to LCD
; Required command must be in W
;-------------------------------------------------------------------------------------------------
LCDPUTCMD
		MOVWF	TEMP_0	; Command to be sent is in W
		CALL	LCDBUSY		; Wait for LCD to be ready
		BCF	LCD_CTRL, LCD_RW; Set LCD in read mode
		BCF	LCD_CTRL, LCD_RS; Set LCD in command mode
		BSF	LCD_CTRL, LCD_E	; LCD E-line High
		MOVF	TEMP_0, W
		MOVWF	LCD_DATA	; Send data to LCD
		BCF	LCD_CTRL, LCD_E	; LCD E-line Low
		RETURN

;=================================================================================================
; DELAY ROUTINES
; Delay_time	= ((DELAY_value * 3) + 4) * Cycle_time
; DELAY_value	= (Delay_time - (4 * Cycle_time)) / (3 * Cycle_time)
;
; i.e. (@ 4MHz crystal)
; Delay_time	= ((32 * 3) + 4) * 1uSec
;		= 100uSec
; DELAY_value	= (500uSec - 4) / 3
;		= 165.33
;		= 165
;-------------------------------------------------------------------------------------------------

DELAY500us
		MOVLW	DELAY500ns		; +1		1 cycle *
		MOVWF 	TEMP_DELAY500		; +2		1 cycle
DELAY500us_LOOP
		DECFSZ	TEMP_DELAY500, F	; step 1	1 cycle
		GOTO	DELAY500us_LOOP	; step 2	2 cycles
DELAY500us_END
	RETURN			; +3		2 cycles


DELAY_15ms
		MOVLW	DELAY15ms
		MOVWF	TEMP_DELAY15		; +1		1 cycle
DELAY_15ms_LOOP
		CALL	DELAY500us	; step1		wait 500uSec
		DECFSZ	TEMP_DELAY15, F	; step2		1 cycle
		GOTO	DELAY_15ms_LOOP	; step3		2 cycles
DELAY_15ms_END
	RETURN			; +2		2 cycles


X_DELAY_15ms
		MOVWF	TEMP_0		; +1		1 cycle
X_DELAY_15ms_LOOP
		CALL	DELAY_15ms	; step1	
		DECFSZ	TEMP_0, F	; step2		1 cycle
		GOTO	X_DELAY_15ms_LOOP	; step3		2 cycles
X_DELAY_15ms_END
	RETURN			; +2		2 cycles


;=================================================================================================
; MESSAGE_OUT
; Looks up the literal address of the first char 
; for the message correlating to the value of MESSAGE_INDEX 
; Then sequentially gets each character for the relevant message (1 line) and sends to the screen
;-------------------------------------------------------------------------------------------------
MESSAGE_OUT
		MOVLW	HIGH(GET_MSG_ADDRESS_HIGH)		 
		MOVWF PCLATH	 
		MOVF MESSAGE_INDEX, W ;Put Message Index into W
		CALL GET_MSG_ADDRESS_HIGH ;start double lookup
		MOVWF CHAR_INDEX_HIGH
		MOVF MESSAGE_INDEX, W ;Put Message Index into W
		CALL GET_MSG_ADDRESS_LOW
		MOVWF CHAR_INDEX_LOW	; This is the base index for the defined string

MESSAGE_OUT_1
	btfsc ENTRY_MODE, 1 ; if this bit is 0 then screen is running backwards 
		goto MESSAGE_OUT_FWD
	GOTO MESSAGE_OUT_BWD

MESSAGE_OUT_FWD
		CALL MESSAGE_GET_CHAR	; calling movwf pcl invokes a return
							;so calling it in a subroutine to get around this!
		CLRF	PCLATH		; following functions are back in first 2k.
		ADDLW 0					;If reached'0x00' at end of message 
		BTFSC STATUS, Z			;Then return back to message display function
			GOTO MESSAGE_OUT_END
		CALL LCDPUTCHAR			;Otherwise send char to LCD
		CALL CHAR_INDEX_INCREMENT
		MOVF	X_DELAY,W		;delay for slow character display effect
		BTFSS  	STATUS,Z
		CALL	X_DELAY_15ms	; creates a delay between the display of each character
	GOTO MESSAGE_OUT_FWD		;repeat

MESSAGE_OUT_BWD
		CALL	MESSAGE_FFWD      	;This function take the index to the 0 just after
		MOVF 	CHAR_INDEX_HIGH,W 	;the end of the current message
		MOVWF	TEMP_A				;this index is then stored in temp A & B
		MOVF	CHAR_INDEX_LOW,W   
		MOVWF	TEMP_B

MESSAGE_OUT_BWD_1
		DECF CHAR_INDEX_LOW,F
		MOVLW  	0XFF      ; put the value to compare with in W
 		SUBWF  	CHAR_INDEX_LOW,w   ; subtract W from the File Register
                             ;  and put result in W (this preserve file register)
		BTFSC  	STATUS,Z    ; SKIP next instruction Zero flag not set
			DECF CHAR_INDEX_HIGH,F  ;if low index just rolled under then need to decrement high index
		CALL MESSAGE_GET_CHAR
		ADDLW 0					;If reached'0x00' at end of message 
		BTFSC STATUS, Z			;Then return back to message display function
			GOTO MESSAGE_OUT_BWD_END
		CALL LCDPUTCHAR			;Otherwise send char to LCD
		MOVF	X_DELAY,W		;delay for slow character display effect
		BTFSS  	STATUS,Z
		CALL	X_DELAY_15ms	; creates a delay between the display of each character
		GOTO MESSAGE_OUT_BWD_1

MESSAGE_OUT_BWD_END
		MOVF 	TEMP_A,W
		MOVWF	CHAR_INDEX_HIGH
		MOVF	TEMP_B,W
		MOVWF	CHAR_INDEX_LOW
MESSAGE_OUT_END
		CALL CHAR_INDEX_INCREMENT
	RETURN


MESSAGE_FFWD
		CALL MESSAGE_GET_CHAR
		ADDLW 0					;If reached'0x00' at end of message 
		BTFSC STATUS, Z			;Then return back to message display function
			RETURN
		CALL CHAR_INDEX_INCREMENT
		GOTO MESSAGE_FFWD

CHAR_INDEX_INCREMENT
		INCFSZ CHAR_INDEX_LOW,f	;increment the char index (16F INCSNZ not available)
			RETURN		
		incf CHAR_INDEX_HIGH,F ;If Index low is 0 then it has just rolled over 
			RETURN				;so increment the high index before returning

MESSAGE_GET_CHAR
		MOVF CHAR_INDEX_HIGH, W		 
		MOVWF PCLATH	 	
		MOVF CHAR_INDEX_LOW, W		;Recall char index into W
		MOVWF  PCL			;Look up the address and put char into W
							;RETURNS AUTOMATICALLY

;=================================================================================================
; CG_SET
; Sequentially sends each line pattern for the relevant Custom Graphics Character
; There are 8 lines of 5 pix per character. (curious 5 lines of 8px would have been more efficient!)
; First the address of the line (range 0-3f) is sent by LCDSCGA (which adds 0x40 before sending
; Then the Byte for the bit pattern of the line is sent by LCDPUTCHAR (dc,dc,dc,px1,px2,px3,px4,px5)
; Originally I was sending the address and patterns in reverse
; however it appears that the LCD always fills each graphic from top-to-bottom whether the address
; for each char is decremented or incremented, so make sure you always read the patterns forwards
; or your characters will be upside down!
;-------------------------------------------------------------------------------------------------
CG_SET
		MOVLW 0x00				;this is the CGRAM address +1
		MOVWF TEMP_A	; the plus one is so the counter works correctly
		MOVF CG_MODE, W ;Put Message Index into W
		ANDLW 0x0F ; Strip upper bits
		CALL GET_CG_SET_ADDRESS_HIGH ;start double lookup
		MOVWF CHAR_INDEX_HIGH
		MOVF CG_MODE, W ;Put Message Index into W
		ANDLW 0x0F ; Strip upper bits
		CALL GET_CG_SET_ADDRESS_LOW
		MOVWF CHAR_INDEX_LOW	; This is the base index for the defined string

CG_SET_OUTPUT
		MOVf TEMP_A,w		
		CALL LCDSCGA			;then CGRAM address to LCD
		CALL MESSAGE_GET_CHAR   ;shared with Message output routine!
		CLRF	PCLATH		; following functions are back in first 2k.
		CALL LCDPUTCHAR			;send line pattern to LCD
		incf TEMP_A,f			;increment the CGRAM address
		movlw 0x40			;if it's gone beyond the last address (3f)
		subwf TEMP_A,w		;then we're all done
		BTFSC  	STATUS,Z    ; SKIP next instruction Zero flag not set
			return			; exit this routine.
		CALL	CHAR_INDEX_INCREMENT ;otherwise increase the index
	GOTO CG_SET_OUTPUT		;and repeat

GET_CG_SET_ADDRESS_HIGH
	ADDWF PCL, F
	RETLW HIGH(CG_SET0)
	RETLW HIGH(CG_SET1)
	RETLW HIGH(CG_SET2)	
	RETLW HIGH(CG_SET3)
;	RETLW HIGH(CG_SET4)
;	RETLW HIGH(CG_SET5)
;	RETLW HIGH(CG_SET6)
;	RETLW HIGH(CG_SET7)
;	RETLW HIGH(CG_SET8)
;	RETLW HIGH(CG_SET9)
;	RETLW HIGH(CG_SET10)
;	RETLW HIGH(CG_SET11)
;	RETLW HIGH(CG_SET12)
;	RETLW HIGH(CG_SET13)
;	RETLW HIGH(CG_SET14)
;	RETLW HIGH(CG_SET15)

GET_CG_SET_ADDRESS_LOW
	ADDWF PCL, F
	RETLW LOW(CG_SET0)
	RETLW LOW(CG_SET1)
	RETLW LOW(CG_SET2)	
	RETLW LOW(CG_SET3)
;	RETLW LOW(CG_SET4)
;	RETLW LOW(CG_SET5)
;	RETLW LOW(CG_SET6)
;	RETLW LOW(CG_SET7)
;	RETLW LOW(CG_SET8)
;	RETLW LOW(CG_SET9)
;	RETLW LOW(CG_SET10)
;	RETLW LOW(CG_SET11)
;	RETLW LOW(CG_SET12)
;	RETLW LOW(CG_SET13)
;	RETLW LOW(CG_SET14)
;	RETLW LOW(CG_SET15)

;*****************
; Start on a new chunk so we know which calls 
; require PCLATH to be set for 
	org 0x100


;=================================================================================================
; GET_MSG_ADDRESS_HIGH & LOW
; Lookup tables list of strings, one for each message
; returns the literal address
; W is only 8 bits but address is 10bits hence split into HIGH and LOW.
;-------------------------------------------------------------------------------------------------
GET_MSG_ADDRESS_HIGH 	;first step of double lookup beginning of the selected 
	ADDWF PCL, F		;message address and put into W register
	RETLW HIGH(MSG0)	
	RETLW HIGH(MSG1)
	RETLW HIGH(MSG2)
	RETLW HIGH(MSG3)
	RETLW HIGH(MSG4)
	RETLW HIGH(MSG5)
	RETLW HIGH(MSG6)
	RETLW HIGH(MSG7)
	RETLW HIGH(MSG8)
	RETLW HIGH(MSG9)

	RETLW HIGH(MSG10)
	RETLW HIGH(MSG11)
	RETLW HIGH(MSG12)
	RETLW HIGH(MSG13)
	RETLW HIGH(MSG14)
	RETLW HIGH(MSG15)
	RETLW HIGH(MSG16)
	RETLW HIGH(MSG17)
	RETLW HIGH(MSG18)
	RETLW HIGH(MSG19)

	RETLW HIGH(MSG20)
	RETLW HIGH(MSG21)
	RETLW HIGH(MSG22)
	RETLW HIGH(MSG23)
	RETLW HIGH(MSG24)
	RETLW HIGH(MSG25)
	RETLW HIGH(MSG26)
	RETLW HIGH(MSG27)
	RETLW HIGH(MSG28)
	RETLW HIGH(MSG29)

	RETLW HIGH(MSG30)
	RETLW HIGH(MSG31)
	RETLW HIGH(MSG32)
	RETLW HIGH(MSG33)
	RETLW HIGH(MSG34)
	RETLW HIGH(MSG35)
	RETLW HIGH(MSG36)
	RETLW HIGH(MSG37)
	RETLW HIGH(MSG38)
	RETLW HIGH(MSG39)

	RETLW HIGH(MSG40)
	RETLW HIGH(MSG41)
;	RETLW HIGH(MSG42)
;	RETLW HIGH(MSG43)
;	RETLW HIGH(MSG44)
;	RETLW HIGH(MSG45)
;	RETLW HIGH(MSG46)
;	RETLW HIGH(MSG47)
;	RETLW HIGH(MSG48)
;	RETLW HIGH(MSG49)



GET_MSG_ADDRESS_LOW 
	ADDWF PCL, F
	RETLW LOW(MSG0)	
	RETLW LOW(MSG1)
	RETLW LOW(MSG2)	
	RETLW LOW(MSG3)
	RETLW LOW(MSG4)
	RETLW LOW(MSG5)
	RETLW LOW(MSG6)
	RETLW LOW(MSG7)
	RETLW LOW(MSG8)
	RETLW LOW(MSG9)

	RETLW LOW(MSG10)
	RETLW LOW(MSG11)
	RETLW LOW(MSG12)
	RETLW LOW(MSG13)
	RETLW LOW(MSG14)
	RETLW LOW(MSG15)
	RETLW LOW(MSG16)
	RETLW LOW(MSG17)
	RETLW LOW(MSG18)
	RETLW LOW(MSG19)

	RETLW LOW(MSG20)
	RETLW LOW(MSG21)
	RETLW LOW(MSG22)
	RETLW LOW(MSG23)
	RETLW LOW(MSG24)
	RETLW LOW(MSG25)
	RETLW LOW(MSG26)
	RETLW LOW(MSG27)
	RETLW LOW(MSG28)
	RETLW LOW(MSG29)

	RETLW LOW(MSG30)
	RETLW LOW(MSG31)
	RETLW LOW(MSG32)
	RETLW LOW(MSG33)
	RETLW LOW(MSG34)
	RETLW LOW(MSG35)
	RETLW LOW(MSG36)
	RETLW LOW(MSG37)
	RETLW LOW(MSG38)
	RETLW LOW(MSG39)

	RETLW LOW(MSG40)
	RETLW LOW(MSG41)
;	RETLW LOW(MSG42)
;	RETLW LOW(MSG43)
;	RETLW LOW(MSG44)
;	RETLW LOW(MSG45)
;	RETLW LOW(MSG46)
;	RETLW LOW(MSG47)
;	RETLW LOW(MSG48)
;	RETLW LOW(MSG49)


;=================================================================================================
; DISPLAY MODE
; Lookup table selects how you want to display each message based on message index
; the following functions then set up the corresponding values that are used 
; in the 'DISPLAY' function to set up the display accordingly.
;-------------------------------------------------------------------------------------------------

DISPLAY_MODE ; SELECTS MODE OF DISPLAY
	ADDWF PCL, F
	GOTO DISPLAY_FAST	; MSG0	
	GOTO DISPLAY_FANCY 	; MSG1
	GOTO DISPLAY_SCROLL	; MSG2
	GOTO DISPLAY_BONKERS; MSG3
	GOTO DISPLAY_FANCY	; MSG4
	GOTO DISPLAY_SCROLL	; MSG5
	GOTO DISPLAY_FAST 	; MSG6
	GOTO DISPLAY_FANCY	; MSG7
	GOTO DISPLAY_FAST 	; MSG8
	GOTO DISPLAY_FANCY	; MSG9

	GOTO DISPLAY_FAST 	; MSG10
	GOTO DISPLAY_FANCY	; MSG11
	GOTO DISPLAY_SCROLL	; MSG12
	GOTO DISPLAY_FAST 	; MSG13
	GOTO DISPLAY_FANCY	; MSG14
	GOTO DISPLAY_SCROLL	; MSG15
	GOTO DISPLAY_FAST 	; MSG16
	GOTO DISPLAY_FANCY	; MSG17
	GOTO DISPLAY_SCROLL	; MSG18
	GOTO DISPLAY_FAST 	; MSG19

	GOTO DISPLAY_FANCY	; MSG20
	GOTO DISPLAY_SCROLL	; MSG21
	GOTO DISPLAY_FAST 	; MSG22
	GOTO DISPLAY_SCROLL	; MSG23
	GOTO DISPLAY_SCROLL	; MSG24
	GOTO DISPLAY_SCROLL	; MSG25
	GOTO DISPLAY_SCROLL	; MSG26
	GOTO DISPLAY_SCROLL	; MSG27
	GOTO DISPLAY_SCROLL	; MSG28
	GOTO DISPLAY_SCROLL	; MSG29

	GOTO DISPLAY_SCROLL	; MSG30
	GOTO DISPLAY_SCROLL	; MSG31
	GOTO DISPLAY_SCROLL	; MSG32
	GOTO DISPLAY_SCROLL	; MSG33
	GOTO DISPLAY_SCROLL	; MSG34
	GOTO DISPLAY_SCROLL	; MSG35
	GOTO DISPLAY_SCROLL	; MSG36
	GOTO DISPLAY_SCROLL	; MSG37
	GOTO DISPLAY_SCROLL	; MSG38
	GOTO DISPLAY_SCROLL	; MSG39

	GOTO DISPLAY_SCROLL	; MSG40
	GOTO DISPLAY_SCROLL	; MSG41
	GOTO DISPLAY_SCROLL	; MSG42
	GOTO DISPLAY_SCROLL	; MSG43
	GOTO DISPLAY_SCROLL	; MSG44
	GOTO DISPLAY_SCROLL	; MSG45
	GOTO DISPLAY_SCROLL	; MSG46
	GOTO DISPLAY_SCROLL	; MSG47
	GOTO DISPLAY_SCROLL	; MSG48
	GOTO DISPLAY_SCROLL	; MSG49
	

DISPLAY_FAST ;delay.0 ,increment.forwards, blink.off cursor.off,
		;MOVLW 0			;using a literal constant of 0 for line 0&2 as they do not change
		;MOVWF LCD_LINE0	; Used to set cursor positions

		MOVLW 0x40			; change these if not using
		MOVWF LCD_LINE1		; a 20x4 display!
		;MOVLW 0x14
		;MOVWF LCD_LINE2

		MOVLW 0x54
		MOVWF LCD_LINE3	

		movlw 0
		movwf X_DELAY

		MOVLW 0x30
		movwf CG_MODE

		movlw b'00000100'	; b3-7 don't care, b2 display on(1)/off(0), b1 cursor on(1)/off(0), b0 cursor blink on(1)/off) 
		movwf CURSOR_MODE	;  

		MOVLW b'00100010'	;  b2-7	: don't care, b1	decrement(0)/increment(1), b0	display shift on(1)/off(0)
		MOVWF ENTRY_MODE	; note: The display function swaps the upper/lower nibbles between each line
							; hence setting bits 4&5 as well as bits 0&1
		RETURN	   

DISPLAY_FANCY ;delay.moderate, increment.flips, blink.off cursor.off,
		;MOVLW 0			;using a literal constant of 0 for line 0&2 as they do not change
		;MOVWF LCD_LINE0	; Used to set cursor positions

		MOVLW 0x53			; change these if not using
		MOVWF LCD_LINE1		; a 20x4 display!
		;MOVLW 0x14
		;MOVWF LCD_LINE2

		MOVLW 0x67
		MOVWF LCD_LINE3	

		movlw 0x04
		movwf X_DELAY

		MOVLW 0x12
		movwf CG_MODE

		movlw b'00000100'	; b3-7 don't care, b2 display on(1)/off(0), b1 cursor on(1)/off(0), b0 cursor blink on(1)/off) 
		movwf CURSOR_MODE	;  

		MOVLW b'00000010'	;  b2-7	: don't care, b1	decrement(0)/increment(1), b0	display shift on(1)/off(0)
		MOVWF ENTRY_MODE	; note: The display function swaps the upper/lower nibbles between each line
	RETURN					; hence setting bits 4&5 as well as bits 0&1	   

DISPLAY_SCROLL ;delay.large, increment.forwards, blink.on cursor.on,
		;MOVLW 0			;using a literal constant of 0 for line 0&2 as they do not change
		;MOVWF LCD_LINE0	; Used to set cursor positions

		MOVLW 0x40			; change these if not using
		MOVWF LCD_LINE1		; a 20x4 display!
		;MOVLW 0x14
		;MOVWF LCD_LINE2

		MOVLW 0x54
		MOVWF LCD_LINE3	

		movlw 0x0A
		movwf X_DELAY

		MOVLW 0x10
		movwf CG_MODE
	
		movlw b'00000111'	; b3-7 don't care, b2 display on(1)/off(0), b1 cursor on(1)/off(0), b0 cursor blink on(1)/off) 
		movwf CURSOR_MODE	;  

		MOVLW b'00100010'	;  b2-7	: don't care, b1	decrement(0)/increment(1), b0	display shift on(1)/off(0)
		MOVWF ENTRY_MODE	; note: The display function swaps the upper/lower nibbles between each line
	RETURN 					; hence setting bits 4&5 as well as bits 0&1

DISPLAY_BONKERS ;delay.moderate, increment.forwards, blink.on cursor.on,
		;MOVLW 0			;using a literal constant of 0 for line 0&2 as they do not change
		;MOVWF LCD_LINE0	; Used to set cursor positions

		MOVLW 0x53			; change these if not using
		MOVWF LCD_LINE1		; a 20x4 display!
		;MOVLW 0x14
		;MOVWF LCD_LINE2

		MOVLW 0x67
		MOVWF LCD_LINE3	

		movlw 0x05
		movwf X_DELAY

		MOVLW 0x23
		movwf CG_MODE

		movlw b'00000100'	; b3-7 don't care, b2 display on(1)/off(0), b1 cursor on(1)/off(0), b0 cursor blink on(1)/off) 
		movwf CURSOR_MODE	;  

		MOVLW b'00010011'	;  b2-7	: don't care, b1	decrement(0)/increment(1), b0	display shift on(1)/off(0)
		MOVWF ENTRY_MODE	; note: The display function swaps the upper/lower nibbles between each line
	RETURN 					; hence setting bits 4&5 as well as bits 0&1	

;=================================================================================================
; CG_TABLE
; Big list of strings, one for Character Graphic 
; Must have 8 chars per string and 8 strings per set
;-------------------------------------------------------------------------------------------------

CG_SET0; General
		DT 0x0,0xa,0x1f,0x1f,0xe,0x4,0x0, 0	;heart 
	DT 0x0,0xa,0xa,0x0,0x11,0xe,0x0, 0	;smilie 	
	DT 0x0,0x1,0x3,0x7,0xf,0x1f,0x1f, 0	;UPSLOPE 
 	DT 0x0,0x10,0x18,0x1c,0x1e,0x1f,0x1f, 0	;DOWNSLOPE
 DT 0x1f,0x1f,0xf,0x7,0x3,0x1,0x0, 0	;DOWNCEILING
	DT 0x1f,0x1f,0x1e,0x1c,0x18,0x10,0x0, 0	;UPCEILING 
 		DT	0xe,0x11,0xe,0x4,0xe,0x4,0xa, 0	;man
	DT 0x1b,0x1b,0x0,0xe,0x4,0x15,0xe, 0	;dogface

CG_SET1; dog
 		DT 0x0,0x1,0x2,0x4,0x4,0x8,0x9, 0	;1
 		DT 0x10,0xb,0x4,0x8,0x12,0x10,0x3, 0;2
		DT 0x1,0x1a,0x4,0x2,0x9,0x1,0x18, 0	;3
		DT 0x0,0x10,0x8,0x4,0x4,0x2,0x12, 0	;4
		DT 0x5,0x2,0x0,0x0,0x0,0x0,0x1, 0	;5
		DT 0xc,0x11,0x8,0x4,0xb,0x12,0x4, 0	;6
		DT 0x6,0x11,0x2,0x4,0x1a,0x9,0x4, 0	;7
		DT 0x14,0x8,0x0,0x0,0x0,0x0,0x10, 0	;8

CG_SET2; BIG FONT
		DT   B'00111', B'01111', B'11111', B'11111', B'11111', B'11111', B'11111', B'11111'
		DT	 B'11111', B'11111', B'11111', B'00000', B'00000', B'00000', B'00000', B'00000'
		DT   B'11100', B'11110', B'11111', B'11111', B'11111', B'11111', B'11111', B'11111'
		DT	B'11111', B'11111',  B'11111',  B'11111', B'11111', B'11111', B'01111', B'00111'
		DT  B'00000', B'00000', B'00000', B'00000', B'00000', B'11111', B'11111', B'11111'
		DT  B'11111', B'11111', B'11111', B'11111', B'11111', B'11111', B'11110', B'11100'
		DT  B'11111', B'11111', B'11111', B'00000', B'00000', B'00000', B'11111', B'11111'
		DT  B'11111', B'00000', B'00000', B'00000', B'00000', B'11111', B'11111', B'11111'

CG_SET3 ; battery
empty	DT 0xe,0x1b,0x11,0x11,0x11,0x11,0x11,0x1f 	; empty
 		DT 0xe,0x1b,0x11,0x11,0x11,0x11,0x1f,0x1f 	;
		DT 0xe,0x1b,0x11,0x11,0x11,0x1f,0x1f,0x1f 	;
		DT 0xe,0x1b,0x11,0x11,0x1f,0x1f,0x1f,0x1f 	;
		DT 0xe,0x1b,0x11,0x1f,0x1f,0x1f,0x1f,0x1f 	;
 		DT 0xe,0x1b,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f 	;
full	DT 0xe,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f 	; full
		DT 0xe,0x1b,0x11,0x11,0x11,0x11,0x1f, 0 	;empty 5x7



;=================================================================================================
; CHAR_TABLE
; Big list of strings, one for each message
; a zero at the end of each string so the loop that calls them
; can detect the end of the string
;-------------------------------------------------------------------------------------------------

;	0000	 0001	0010	0011	0100	0101	0110	0111	1000	1001	1010	1011	1100	1101	1110	1111
; 0000	0	 16 ?	33 !	49 1	65 A	81 Q	97 a	113 q	129 ü	145 æ	161 í	177 ¦	193 -	209 -	225 ß	241 ±
; 0001	1 ?	 17 ?	34 “	50 2	66 B	82 R	98 b	114 r	130 é	146 Æ	162 ó	178 ¦	194 -	210 -	226 G	242 =
; 0010	2 ?	 18 ?	35 #	51 3	67 C	83 S	99 c	115 s	131 â	147 ô	163 ú	179 ¦	195 +	211 +	227 p	243 =
; 0011	3 ?	 19 ?	36 $	52 4	68 D	84 T	100 d	116 t	132 ä	148 ö	164 ñ	180 ¦	196 -	212 +	228 S	244 (
; 0100	4 ?	 20 ¶	37  	53 5	69 E	85 U	101 e	117 u	133 à	149 ò	165 Ñ	181 ¦	197 +	213 +	229 s	245 )
; 0101	5 ?	 21 §	38 %	54 6	70 F	86 V	102 f	118 v	134 å	150 û	166 ª	182 ¦	198 ¦	214 +	230 µ	246 ÷
; 0110	6 ?	 22 ?	39 ‘	55 7	71 G	87 W	103 g	119 w	135 ç	151 ù	167 º	183 +	199 ¦	215 +	231 t	247 ˜
; 0111	7 •	 23 ?	40 (	56 8	72 H	88 X	104 h	120 x	136 ê	152 ÿ	168 ¿	184 +	200 +	216 +	232 F	248 °
; 1000	8 ?	 24 ?	41 )	57 9	73 I	89 Y	105 i	121 y	137 ë	153 Ö	169 ¬	185 ¦	201 +	217 +	233 T	249 •
; 1001	9 ?	 25 ?	42 *	60 0	74 J	90 Z	106 j	122 z	138 è	154 Ü	170 ¬	186 ¦	202 -	218 +	234 O	250 •
; 1010	10 ? 26 ?	43 +	59 ;	75 K	91 [	107 k	123 {	139 ï	155 ¢	171 ½	187 +	203 -	219 ¦	235 d	251 v
; 1011	11 ? 27 ?	44 ,	60 <	76 L	92 \	108 l	124 |	140 î	156 £	172 ¼	188 +	204 ¦	220 _	236 8	252 n
; 1100	12 ? 28 ?	45 -	61 =	77 M	93 ]	109 m	125 }	141 ì	157 ¥	173 ¡	189 +	205 -	221 ¦	237 f	253 ²
; 1101	13 ? 29 ?	46 .	62 >	78 N	94 ^	110 n	126 ~	142 Ä	158 P	174 «	190 +	206 +	222 ¦	238 e	254 ¦
; 1110	14 ? 30 ?	47 /	63 ?	79 O	95 _	111 o	127 ¦	143 Å	159 ƒ	175 »	191 +	207 -	223 ¯	239 n	255 þ
; 1111	15 ¤ 31 ?	48 0	64 @	80 P	96 `	112 p	128 Ç	144 É	160 á	176 ¦	192 +	208 -	224 a	240 =	256 ÿ


;20 ch max  12345678901234567890		
MSG0	DT 0x8,0x9,0xA,0xB," Message 00 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 01 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 02 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 03 ", 0xC,0xD,0xE,0xF, 0

MSG1	DT 0x8,0x9,0xA,0xB," Message 04 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 05 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 06 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 07 ", 0xC,0xD,0xE,0xF, 0

MSG2	DT 0x8,0x9,0xA,0xB," Message 08 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 09 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 10 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 11 ", 0xC,0xD,0xE,0xF, 0

MSG3	DT 0x8,0x9,0xA,0xB," Message 12 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 13 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 14 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 15 ", 0xC,0xD,0xE,0xF, 0

MSG4	DT 0x8,0x9,0xA,0xB," Message 16 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 17 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 18 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 19 ", 0xC,0xD,0xE,0xF, 0		

MSG5	DT 0x8,0x9,0xA,0xB," Message 20 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 21 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 22 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 23 ", 0xC,0xD,0xE,0xF, 0		

MSG6	DT 0x8,0x9,0xA,0xB," Message 24 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 25 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 26 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 27 ", 0xC,0xD,0xE,0xF, 0		

MSG7	DT 0x8,0x9,0xA,0xB," Message 28 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 29 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 30 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 31 ", 0xC,0xD,0xE,0xF, 0		

MSG8	DT 0x8,0x9,0xA,0xB," Message 32 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 33 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 34 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 35 ", 0xC,0xD,0xE,0xF, 0		

MSG9	DT 0x8,0x9,0xA,0xB," Message 36 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 37 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 38 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 39 ", 0xC,0xD,0xE,0xF, 0		

MSG10	DT 0x8,0x9,0xA,0xB," Message 40 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 41 ", 0xC,0xD,0xE,0xF, 0		
		DT 0x8,0x9,0xA,0xB," Message 42 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 43 ", 0xC,0xD,0xE,0xF, 0

MSG11	DT 0x8,0x9,0xA,0xB," Message 44 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 45 ", 0xC,0xD,0xE,0xF, 0		
		DT 0x8,0x9,0xA,0xB," Message 46 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 47 ", 0xC,0xD,0xE,0xF, 0

MSG12	DT 0x8,0x9,0xA,0xB," Message 48 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 49 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 50 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 51 ", 0xC,0xD,0xE,0xF, 0		

MSG13	DT 0x8,0x9,0xA,0xB," Message 52 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 53 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 54 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 55 ", 0xC,0xD,0xE,0xF, 0		

MSG14	DT 0x8,0x9,0xA,0xB," Message 56 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 57 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 58 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 59 ", 0xC,0xD,0xE,0xF, 0	

MSG15	DT 0x8,0x9,0xA,0xB," Message 60 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 61 ", 0xC,0xD,0xE,0xF, 0		
		DT 0x8,0x9,0xA,0xB," Message 62 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 63 ", 0xC,0xD,0xE,0xF, 0

MSG16	DT 0x8,0x9,0xA,0xB," Message 64 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 65 ", 0xC,0xD,0xE,0xF, 0		
		DT 0x8,0x9,0xA,0xB," Message 66 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 67 ", 0xC,0xD,0xE,0xF, 0

MSG17	DT 0x8,0x9,0xA,0xB," Message 68 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 69 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 70 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 71 ", 0xC,0xD,0xE,0xF, 0		

MSG18	DT 0x8,0x9,0xA,0xB," Message 72 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 73 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 74 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 75 ", 0xC,0xD,0xE,0xF, 0		

MSG19	DT 0x8,0x9,0xA,0xB," Message 76 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 77 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 78 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 79 ", 0xC,0xD,0xE,0xF, 0

MSG20	DT 0x8,0x9,0xA,0xB," Message 80 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 81 ", 0xC,0xD,0xE,0xF, 0		
		DT 0x8,0x9,0xA,0xB," Message 82 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 83 ", 0xC,0xD,0xE,0xF, 0

MSG21	DT 0x8,0x9,0xA,0xB," Message 84 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 85 ", 0xC,0xD,0xE,0xF, 0		
		DT 0x8,0x9,0xA,0xB," Message 86 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 87 ", 0xC,0xD,0xE,0xF, 0

MSG22	DT 0x8,0x9,0xA,0xB," Message 88 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 89 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 90 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 91 ", 0xC,0xD,0xE,0xF, 0

MSG23	DT 0x8,0x9,0xA,0xB," Message 92 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 93 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 94 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 95 ", 0xC,0xD,0xE,0xF, 0		

MSG24	DT 0x8,0x9,0xA,0xB," Message 96 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 97 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 98 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 99 ", 0xC,0xD,0xE,0xF, 0

MSG25	DT 0x8,0x9,0xA,0xB," Message 60 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 61 ", 0xC,0xD,0xE,0xF, 0		
		DT 0x8,0x9,0xA,0xB," Message 62 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 63 ", 0xC,0xD,0xE,0xF, 0

MSG26	DT 0x8,0x9,0xA,0xB," Message 64 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 65 ", 0xC,0xD,0xE,0xF, 0		
		DT 0x8,0x9,0xA,0xB," Message 66 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 67 ", 0xC,0xD,0xE,0xF, 0

MSG27	DT 0x8,0x9,0xA,0xB," Message 68 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 69 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 70 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 71 ", 0xC,0xD,0xE,0xF, 0		

MSG28	DT 0x8,0x9,0xA,0xB," Message 72 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 73 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 74 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 75 ", 0xC,0xD,0xE,0xF, 0		

MSG29	DT 0x8,0x9,0xA,0xB," Message 76 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 77 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 78 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 79 ", 0xC,0xD,0xE,0xF, 0

MSG30	DT 0x8,0x9,0xA,0xB," Message 80 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 81 ", 0xC,0xD,0xE,0xF, 0		
		DT 0x8,0x9,0xA,0xB," Message 82 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 83 ", 0xC,0xD,0xE,0xF, 0

MSG31	DT 0x8,0x9,0xA,0xB," Message 84 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 85 ", 0xC,0xD,0xE,0xF, 0		
		DT 0x8,0x9,0xA,0xB," Message 86 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 87 ", 0xC,0xD,0xE,0xF, 0

MSG32	DT 0x8,0x9,0xA,0xB," Message 88 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 89 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 90 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 91 ", 0xC,0xD,0xE,0xF, 0

MSG33	DT 0x8,0x9,0xA,0xB," Message 92 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 93 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 94 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 95 ", 0xC,0xD,0xE,0xF, 0		

MSG34	DT 0x8,0x9,0xA,0xB," Message 96 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 97 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 98 ", 0xC,0xD,0xE,0xF, 0
		DT 0x8,0x9,0xA,0xB," Message 99 ", 0xC,0xD,0xE,0xF, 0

MSG35	DT "ÿ Message 100", 0xC,0xD,0xE,0xF, 0
		DT "Message 101", 0xC,0xD,0xE,0xF, 0		
		DT "Message 102", 0xC,0xD,0xE,0xF, 0
		DT "Message 103", 0xC,0xD,0xE,0xF, 0

MSG36	DT "Message 104", 0xC,0xD,0xE,0xF, 0
		DT "Message 105", 0xC,0xD,0xE,0xF, 0		
		DT "Message 106", 0xC,0xD,0xE,0xF, 0
		DT "Message 107", 0xC,0xD,0xE,0xF, 0

MSG37	DT "Message 108", 0xC,0xD,0xE,0xF, 0
		DT "Message 109", 0xC,0xD,0xE,0xF, 0
		DT "Message 110", 0xC,0xD,0xE,0xF, 0
		DT "Message 111", 0xC,0xD,0xE,0xF, 0		

MSG38	DT "Message 112", 0xC,0xD,0xE,0xF, 0
		DT "Message 113", 0xC,0xD,0xE,0xF, 0
		DT "Message 114", 0xC,0xD,0xE,0xF, 0
		DT "Message 115", 0xC,0xD,0xE,0xF, 0		

MSG39	DT "Message 116", 0xC,0xD,0xE,0xF, 0
		DT "Message 117", 0xC,0xD,0xE,0xF, 0
		DT "Message 118", 0xC,0xD,0xE,0xF, 0
		DT "Message 119", 0xC,0xD,0xE,0xF, 0

MSG40	DT "Message 120", 0xC,0xD,0xE,0xF, 0
		DT "Message 121", 0xC,0xD,0xE,0xF, 0		
		DT "Message 122", 0xC,0xD,0xE,0xF, 0
		DT "Message 123", 0xC,0xD,0xE,0xF, 0

MSG41	DT "Message 124", 0xC,0xD,0xE,0xF, 0
		DT "Message 125", 0xC,0xD,0xE,0xF, 0		
		DT "Message 126", 0xC,0xD,0xE,0xF, 0
		DT "Message 127", 0xC,0xD,0xE,0xF, 0

;MSG42	DT "Message 128", 0xC,0xD,0xE,0xF, 0
;		DT "Message 129", 0xC,0xD,0xE,0xF, 0
;		DT "Message 130", 0xC,0xD,0xE,0xF, 0
;		DT "Message 131", 0xC,0xD,0xE,0xF, 0

;MSG43	DT "Message 132", 0xC,0xD,0xE,0xF, 0
;		DT "Message 133", 0xC,0xD,0xE,0xF, 0
;		DT "Message 134", 0xC,0xD,0xE,0xF, 0
;		DT "Message 135", 0xC,0xD,0xE,0xF, 0		

;MSG44	DT "Message 136", 0xC,0xD,0xE,0xF, 0
;		DT "Message 137", 0xC,0xD,0xE,0xF, 0
;		DT "Message 138", 0xC,0xD,0xE,0xF, 0
;		DT "Message 139", 0xC,0xD,0xE,0xF, 0

;MSG45	DT "Message 140", 0xC,0xD,0xE,0xF, 0
;		DT "Message 141", 0xC,0xD,0xE,0xF, 0		
;		DT "Message 142", 0xC,0xD,0xE,0xF, 0
;		DT "Message 143", 0xC,0xD,0xE,0xF, 0

;MSG46	DT "Message 144", 0xC,0xD,0xE,0xF, 0
;		DT "Message 145", 0xC,0xD,0xE,0xF, 0		
;		DT "Message 146", 0xC,0xD,0xE,0xF, 0
;		DT "Message 147", 0xC,0xD,0xE,0xF, 0

;MSG47	DT "Message 148", 0xC,0xD,0xE,0xF, 0
;		DT "Message 149", 0xC,0xD,0xE,0xF, 0
;		DT "Message 150", 0xC,0xD,0xE,0xF, 0
;		DT "Message 151", 0xC,0xD,0xE,0xF, 0		

;MSG48	DT "Message 152", 0xC,0xD,0xE,0xF, 0
;		DT "Message 153", 0xC,0xD,0xE,0xF, 0
;		DT "Message 154", 0xC,0xD,0xE,0xF, 0
;		DT "Message 155", 0xC,0xD,0xE,0xF, 0		
;
;MSG49	DT "Message 156", 0xC,0xD,0xE,0xF, 0
;		DT "Message 157", 0xC,0xD,0xE,0xF, 0
;		DT "Message 158", 0xC,0xD,0xE,0xF, 0
;		DT "Message 159", 0xC,0xD,0xE,0xF, 0

	END				; End of program

; This compiler directive does not work with MPLAB!
;	IF ( (GET_CHAR & 0x0FF) >= (MSG39 & 0x0FF) )
;		MESSG   "Warning - User Definded: Table crosses page boundry in computed jump"
;	ENDIF
 

The initialization code is incomplete, the HD47780 registers can be in any random condition when power is applied, so you should call LCDPUTCMD with 38H three times as well as initializing all of the other registers.
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top