;********************************************************************
;====================================================================
; Program: custom.asm
; programmer(s): Dincer Aydin
; function:Demonstrates how custom characters are defined and used
;====================================================================
;********************************************************************
; This code requires a 2*16 LCD connected to a 8255 with base address of 00h
; routines sendcomA & sendcom & sendcharA & sendchar have been tested on 
; >> 2*16 Hitachi LCD with HD44780 chip 
; >> Samsung 1*16 LCD with a KS0062F00 chip
; >> 2*16 Epson LCD marked P300020400 SANTIS 1 ,and 
; >> noname 1*16 HD44780 based LCD.
; The Z80 was clocked at 2 MHz and 4,9152 MHz for each display.This was done 
; because the routines mentioned above take many t states and in most cases 
; that will be longer that the time a HD44780 will need to execute a command. 
;
; Connections:
; LCD data bus(pins #14-#7) connected to Port A of a 8255 with 00h base address
; LCD Enable pin(#6) connected to Port C bit #7
; LCD R/W pin(#5) connected to Port C bit #6
; LCD RS pin(#4) connected to Port C bit #5


;8255 port address(base 00h):	
	paadr 		equ 00h		; Address of PortA
	pbadr 		equ 01h		; Address of PortB
	pcadr 		equ 02h		; Address of PortC
	cwadr 		equ 03h		; Address of Control Word
;stuff to be written into the control word of the 8255:
;Some of the change the state of the ports and some manipulate
;bits on port C
	allpsin 	equ 9bh		; set all ports to input mode
	paincout 	equ 90h		; A input,C output,B output
	pandcout	equ 80h		; set all ports to output mode
	pacoutbin	equ 82h		; A output,C output,B input
	
;bit set/reset commands.These bits are the control signals for the LCD
	enable 		equ 0fh		; set portC bit #6
	disable		equ 0eh		; reset portC bit #6
	read 		equ 0dh		; set portC bit #5
	write 		equ 0ch		; reset portC bit #5
	command 	equ 0ah		; reset portC bit #4
	data 		equ 0bh		; set portC bit #4
	
; Define number of commands and length of string data
	numofc 		equ 4h		; number of commands

	
; initialization:		
	ld 	sp,200h 	; Set stack pointer
	ld 	c,cwadr 
	ld 	a,pacoutbin 	; Ports A&C output,B input
	out	(c),a

; *********It all begins here**********:
; This part sends commands to initialize the LCD
				
	call 	delay	
	ld 	a,38h		; function set command
	ld 	b,4		
again:	ld 	c,cwadr		
	ld 	d,command	
	out	(c),d		; select the instruction register 
	ld 	d,write
	out	(c),d		; reset RW pin for writing to LCD
	out	(paadr),a	; place the command into portA
	ld 	d,enable	
	out	(c),d		; enable the LCD
	ld 	d,disable
	out	(c),d		; disable the LCD 
	call	delay		; wait for a while
	djnz	again		; loop till the command is sent 4 times
	
; send commands to set input mode, cursor,number of lines ,and etc.
	ld 	hl,combegadr 	; set HL to point the first command
	ld 	b,numofc	; put the number of commands to be sent in B
nextcom:
	call 	sendcom		; send (HL) as a command 
	inc 	hl		; point the next command
	djnz 	nextcom		; loop till all commands are sent

; The code below will modify CG RAM contents to define 8 custom characters

		
	ld   	hl,custom_data	; make (HL) point custom character data
	ld 	a,40h		 
	call 	sendcomA	; Set CG RAM address to 00h by sendind 40h as a command
	

	ld	b,40h		; use B to count number of bytes to be written to CG RAM
				; 8 characters * 8 bytes = 64 bytes (40h) in this example
next_byte:
	call	sendchar	; write (HL) to CG RAM
	inc	hl		; point the next byte to be written to CG RAM
	djnz	next_byte	; loop till all bytes are sent

; Now that custom characters are defined , let's see them on the LCD screen
; The code below will print characters 0-7 (the ones we defined) to LCD screen
	ld	b,8h		; use B to count the number of bytes to be written to DD RAM 
	ld	a,80h		 
	call	sendcomA	; Set DD RAM adres to 00h by sending 80h as a command
	xor	a		; clear a
next_char:
	call 	sendcharA	; print ASCII character corresponding to the value in A
	inc	a		
	djnz	next_char	; loop till ASCII characters 0-7 are sent to the LCD
	halt
	

; ====================================================================
; Subroutine name:sendcomA & sendcom & sendcharA & sendchar 
; programmer:Caner Buyuktuna & Dincer Aydin
; input:A or (HL)
; output:
; Registers altered:A 
; function:	sendcharA sends the data in A to the LCD
;  	  	sendchar sends the data in (HL) to the LCD
; 		sendcomA sends the command in A to the LCD
;  		sendcom sends the command in (HL) to the LCD	
; ====================================================================
sendchar:
	ld 	a,(hl)		; put the data to be sent to the LCD in A
sendcharA:	
	push 	bc		; save BC
	push 	de		; save DE
	ld 	e,data		;  
	jp 	common

sendcom:
	ld 	a,(hl)
sendcomA:	
	push 	bc		; save BC
	push 	de		; save DE   
	ld 	e,command	   

common:	
	call 	bfcheck    	; See if the LCD is busy. If it is busy wait,till it is not.
	out 	(c),e		; Set/reset RS accoring to the content of register E
	ld 	d,write		
	out	(c),d		; reset RW pin for writing to LCD
	out	(paadr),a	; place data/instrucrtion to be written into portA
	ld 	d,enable	
	out	(c),d		; enable the LCD
	ld 	d,disable	
	out 	(c),d		; disable the LCD 
	pop 	de		; restore DE
	pop 	bc		; restore BC
	ret			; return
; ====================================================================
; Subroutine name:bfcheck
; programmer:Dincer Aydin
; input:
; output:
; Registers altered:D
; function:Checks if the LCD is busy and waits until it is not busy
; ====================================================================

bfcheck:
	push 	af		; save AF
	ld 	c,cwadr
	ld 	d,paincout
	out 	(c),d		; make A input,C output,B output
	ld 	d,read		
	out 	(c),d		; set RW pin for reading from LCD
	ld 	d,command
	out	(c),d		; select the instruction register 
check_again:	
	ld 	d,enable	
	out	(c),d		; enable the LCD
	in 	a,(paadr)	; read from LCD
	ld 	d,disable
	out 	(c),d		; disable the LCD 
	rlca			; rotate A left through C flag to see if the busy flag is set
	jp 	c,check_again	; if busy check it again,else continue
	ld 	d,pandcout	
	out	(c),d		; set all ports to output mode
	pop 	af		; restore AF
	ret

;====================================================================
; Subroutine name:DELAY and PDELAY
; programmer(s):Dincer Aydin
; input:the value in HL (for PDELAY only)
; output:-
; Registers altered:H,L
; function:delay 
; for more delay you can add a few nops or or some junk commands using the index 
; registers
; pdelay uses what you put into HL before you called the PDELAY routine 
;====================================================================

delay:	
	ld 	hl,1010h
pdelay:	
	dec	l
	jp 	nz,pdelay
	dec	h
	jp 	nz,pdelay
	ret

;====================================================================
; commands and strings
combegadr:
	db 01h,80h,0ch,06h  	; clear display,set DD RAM adress,
				; turn on display with cursor hidden,set entry mode
custom_data:			
				; These are the values to create some custom characters
db 0,4,2,31,2,4,0,0 	     	; right arrow				
db 0,4,8,31,8,4,0,0 		; left arrow
db 4,14,21,4,4,0,0,0 		; up arrow
db 0,0,0,4,4,21,14,4		; down arrow
db 8,20,20,8,5,6,5,5		; single-character "OK"
db 14,17,17,31,27,27,31,0	; locked (padlock closed)
db 14,16,16,31,27,27,31,0	; unlocked (padlock open)
db 17,18,23,9,19,4,7,0		; single-character "1/2"

; The custom character data above is taken from "LCD Serial Backpack App Note No. 2" 
; from Scott Edwards Electronics Inc. www.seetron.com







1