;*******************************************************************************
;===============================================================================
; Program: v_scroll.asm
; programmer(s): Dincer Aydin (dinceraydin@altavista.net)
; function: vertical scroll effect
; The effect is the same as the one you can see at the entrance of my LCD pages:
; http://www.geocities.com/SiliconValley/Circuit/8882/lcd/index.html
;===============================================================================
;*******************************************************************************
; 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

#define equ .equ
#define end .end

;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
	
; Initialization is complete.


; >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; This part sends strings to the LCD:
numofcts 	equ 6h		; number of characters to scroll
numofd_line1 	equ 0Fh		; define the number of string bytes to be sent to line #1
numofd_line2	equ 08h		; define the number of string bytes to be sent to line #2

replay:	
	ld 	a,01h		; 01h is the "Clear Screen" command
	call 	sendcomA	; command is sent 
	
	ld 	a,40h		; set CGRAM address to 00h command
	call 	sendcomA	; command is sent and the addrss counter is pointing 00h in CGRAM
	
	xor 	a		; clear A
	ld 	b,64		; 64 CGRAM locations
clear:
	call 	sendcharA	; 00h is written to a CG ram location (i.e it is cleared)
	djnz 	clear		; loop till all CGRAM is cleared
				; CGRAM is clear and any garbage is gone
	
	ld 	a,80h		; 80h is the "set DDRAM address to 00h" command
	call 	sendcomA	; command is sent and the addrss counter is pointing 00h in DDRAM
				; Now that DDRAM address is set, let's print the string data
	ld 	hl,first_line	; set HL to point the first string byte
	ld 	b,numofd_line1	; put the number of string bytes to be sent in B
nextdata:			
	call 	sendchar	; send (HL) as string data 
	call    delay
	inc 	hl		; point the next string byte
	djnz	nextdata	; loop till all string bytes are sent
	
	ld 	hl,scrolldat	; make HL point the  bitmap data of the characters to be scrolled
	call 	scroll		; call the routine that will make it scroll
	
	ld 	a,0C4h		; set DDRAM address so that the cursor moves to the 4th location of the second line
	call 	sendcomA	
				; Now that DDRAM address is set, let's print the string data
	ld 	hl,second_line	; set HL to point the first string byte
	ld 	b,numofd_line2	; put the number of string bytes to be sent in B
nextdata2:			
	call 	sendchar	; send (HL) as string data 
	inc 	hl		; point the next string byte
	djnz	nextdata2	; loop till all string bytes are sent
	
	ld 	hl,0FFFFh
	call    pdelay
	
	jp 	replay
;====================================================================
; Subroutine name:Scroll
; programmer: Dincer Aydin
; input 1: HL pointing the data of the string to scroll
; input 2: "numofcts" >>the number of the characters in the string to be scrolled
; output:
; Registers altered: many
; function:	
;====================================================================
scroll:
	push 	hl	;ld hl',hl
	exx 
	pop 	hl
	exx
	
	ld b,1		; B is used as line counter
	ld a,47h	; A holds the command to set CGRAM 
			; address to the bottom line 
			; of the first custom chatacter in CGRAM 
				
			; later in the program A will be increased by 8 to
			; create the command to set CGRAM address to the same
			; line of the next custom character in CGRAM
			; The value in B = the number of lines to be 
			; written to CGRAM for each character.
			 
bidaha:	call delay
	
	exx		;ld hl,hl'
	push	hl
	exx 
	pop 	hl

	push 	bc	; save bc
	push 	af	; save af
	call 	tkr	; write to CGRAM to display B lines of the bitmap data,
			; starting from the laction specified by the command in A
			; for the first custom character and  A+8n command for the following 
			; custom characters   (n being the number of the custom character)
			; Need it be said that A and B are Z80 registers,not the hex numbers A and B ?
	pop 	af	; restore bc
	pop 	bc	; restore af
	dec 	a	; derease A to create a new set CGRAM address command
	inc 	b	; increase the number of lines to be sent a time
	push 	af	; save af
	ld 	a,09h	; see if all the lines are done (i.e. see if the aanimation is completed)
	cp 	b
	ret 	z	; return if so
	pop 	af	; else restore af
	jp 	bidaha	; and go shift all custom characters one more line up
	

tkr:	ld 	d,numofcts	; d stores the number of custom characters to be scrolled
	
other:	push 	af	; save bc
	push 	bc	; save af
	call 	ilk	; send the specified number of bytes belonging to this custom character
	pop 	bc	; restore bc
	pop 	af	; restore af
	
	add 	a,8h	; a is increased by 8 to create the command to set CGRAM address to the same
			; line for the next custom character
			; add 8 to l so that (HL) points to the bitmap data that belongs to the new CGRAM address
	push 	af 	
	ld 	a,8
	add 	a,l
	ld 	l,a
	pop 	af
	dec 	d	; see if all the custom characters got their share
	jp 	nz,other 	; if not, go on with the next one
	ret		; else return

ilk:	push hl		; save HL
	call sendcomA	; send the commnad in A to set the CGRAM addres
next:	call sendchar	; send the byte in (HL) to that CGRAM location
	inc hl		;
	djnz next	; dec B to see is all the lines of this character are done. Loop till they are done.
	pop hl		; restore HL
	ret	        ; the specified number of bytes belonging to this custom character are sent.It is time to return.
	
; >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
	
; ====================================================================
; 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 
; programmer(s):Dincer Aydin
; input:-
; output:-
; Registers altered:-
; function:delay 
; for more delay you can add a few nops or or some junk commands using the index 
; registers
;====================================================================
pdelay:	push 	hl
	jp 	comdelay
delay:	push    hl
	ld 	hl,2222h
comdelay:	
	dec	l
	nop
	jp 	nz,comdelay
	dec	h
	jp 	nz,comdelay
	pop	hl
	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

first_line:
	.text "VERTICAL " 
	.db 00h,01h,02h,03h,04h,05h 		; first 5 custom characters
second_line:
	.text "Is Cool!"

scrolldat:
	.db 143,144,144,142,129,129,158,128 ; S
	.db 128,128,142,144,144,145,142,128 ; c
	.db 128,128,150,153,144,144,144,128 ; r
	.db 128,128,142,145,145,145,142,128 ; o
	.db 140,132,132,132,132,132,142,128 ; l
	.db 140,132,132,132,132,132,142,128 ; l

end