title 'CORE Board LCD Drivers - 4 by 40 Char version' ;---------------------------------------------------------------- ; LCD Drivers for CORE Board ; ; This module provides all the LCD Support for CORE-BOARD ; and is separate to the I/O driver main module because ; different LCDs' will definately require different ; I/O drivers. ; ; Written by Richard Holmes 17-12-86 ; Last Update by Richard Holmes 11-08-87 ;---------------------------------------------------------------- ; Routines in this module ; ; ini$lcd Initialize the LCD display ; clr$lcd Clear LCD and home cursor ; hom$lcd Home cursor to top L.H. corner ; cur$lcd Cursor address lcd. D = X, E = Y ; eol$lcd Clear LCD to end of line. ; put$lcd Write character in A to LCD. ; str$lcd Print string at DE to LCD till a null or $ ;---------------------------------------------------------------- ; This version suits the HanDok 4016H. It is a 40 character by 4 ; line display. ; PA 0..7 are data bits into the chip ; ; PB 0 = Enable 1. For controller 1 ; 1 = Enable 2. For controller 2 ; 1 = R/W-. 1 = write to LCD ; 2 = Register select 1 = Data, 0 = control register ;---------------------------------------------------------------- ; maclib z80 maclib core ; public ini$lcd,hom$lcd,clr$lcd public put$lcd,cur$lcd,eol$lcd public str$lcd ; ; ---- Smart LCD I/O ---- ; public xyi$lcd,xyp$lcd,pmu$lcd ; extrn coe extrn ori$led ; Restore LED status extrn clrwdt ; Stop watchdog ; ; Equates ; data equ 044h ; Data port of LCD cntl equ 045h ; Control port of LCD mode equ 047h ; Mode port ; idle equ 0 ; Idle display controllers wr$cmd2 equ 1 ; Write command 1 wr$cmd1 equ 2 ; Write command 2 ; wr$dat2 equ 9 ; Write data 1 wr$dat1 equ 0ah ; Write data 2 ; rd$st2 equ 5 ; Read status 1 rd$st1 equ 6 ; Read status 2 ; bs equ 8 ; Backspace ; ;---------------------------------------------------------------- ; Return LCD Status. This is tricky as it must re-initialize ; the LCD control port and read the LCD status flag BF. It ; will return a 00 if idling or an FF if busy. NOTE the ; need to re-load the CS2 line to the ram chip and clock ; and LED return when finished. ;---------------------------------------------------------------- ; lcd$status: push h ; Save register to use as a scratch pad ; ; Generate the LCD enable signals lda lcd$cmd ; Command to be used ani 0000$0011b ; Get the controller bits ori 0000$0100b ; Or in the status reading function mov l,a ; L = device image. ; di ; STOP interrupts when checking LCD status mvi a,090h ; Port A inputs out @ledmd ; LED mode port mvi a,0c0h out @ledd ; Re-assert ram as soon as possible ; nop nop nop nop ; Settling time ; ; OK, Port A inputs, B and C outputs. ; Note the need to set R/W pin high for a time before the E pin (bit 0). ; mvi a,0000$0100b ; Send the Read bit out. out cntl ; To the control port mov a,l out cntl ; Send "E" bits also ; ; This requires a 40uS delay as per the data sheet ; mvi a,15 ; Plenty of time st$dly: dcr a jnz st$dly ; in data ; Read the LCD data mov l,a ; Save here. Can't see stack can we.... ; ; Restore 8255 mode mvi a,080h ; ALL outputs out @ledmd ; LED mode port ; Re-load ram control signals mvi a,0C0h ; Bit 6 and 7 for ram and clock on out @ledd ; Sends to led port to turn on ram again. ; nop nop nop nop xra a out cntl ; Clear the port ; ei ; Restore interrupts after LCD write ; call ori$led ; Restore previous LED status ; mov a,l ; Restore data pop h ; Restore register ;Now. Determine basic state of 00 = idle or FF = busy. ani 1000$0000b ; Leave busy flag rz mvi a,0ffh ; Else load an FF for very busy still. ret ; ;------------------------------------------------ ; Latch the command in lcd$cmd and the ; data in lcd$dat into the LCD. ;------------------------------------------------ ; lch$lcd: push h push psw ; Save registers ; ; Now setup timeout counter in HL and look for LCD idle lxi h,400 ; Big delay lch$cmd$loop: call clrwdt call lcd$status ; Get LCD status ora a jrz lch$cmd$ok dcx h mov a,l ora h ; Done ? jrnz lch$cmd$loop ; Loop on till IDLE (busy flag = 0) ; ERROR here ; pop psw pop h stc ; Carry flag = error ret ; ; Restore registers and do job ; lch$cmd$ok: pop psw push psw ; out data lda lcd$cmd out cntl xra a out cntl ; pop psw pop h ret ; page ;---------------------------------------------------------------- ; Initialize the LCD. ; ; 1. Data path 8 bits, 2 line display ; 2. Enable cursor. Blink. Enable display ; 4. Clear and home cursor. ; 3. Display stationary, auto increment, right ;---------------------------------------------------------------- ; ini$lcd: push psw ; ; Select the first controller, command latching. mvi a,wr$cmd1 ; Command for controller 1 sta lcd$cmd ; Save in command port. call ini$cntrlr ; Initialize the controller ; mvi a,wr$cmd2 ; Controller 2 sta lcd$cmd call ini$cntrlr ; xra a sta lcd$x sta lcd$y ; Setup X and Y addresses call cof$lcd ; Turn off all cursors call con$lcd ; Turn cursor on. ; pop psw ret ; ;---------------------------------------------------------------- ; ---- Initialize the contoller. ---- ;---------------------------------------------------------------- ; ini$cntrlr: ; ; 1. Enable 8 bit bus, 2 line display mvi a,038h call lch$lcd ; Latch 8 bit, 2 line display ; ; 2. Enable display on, cursor on, blink mvi a,0eh call lch$lcd ; mvi a,08 call lch$lcd ; ; ; 3. Display stationary, Right cursor ; mvi a,2 call lch$lcd ; mvi a,1 call lch$lcd ; ; Done. ret ; ;---------------------------------------------------------------- ; Clear LCD and home cursor. ;---------------------------------------------------------------- ; clr$lcd: push psw ; Clear controller 1 mvi a,wr$cmd1 sta lcd$cmd ; Select a clear command controller 1 mvi a,1 ; Clear display totally call lch$lcd ; ; Clear controller 2 mvi a,wr$cmd2 sta lcd$cmd ; Select a clear command controller 1 mvi a,1 ; Clear display totally call lch$lcd ; xra a ; Clear cursor address sta lcd$x sta lcd$y call hom$lcd ; pop psw ret ; ;---------------------------------------------------------------- ; Home the cursor ;---------------------------------------------------------------- ; hom$lcd: push d push psw ; lxi d,0 ; Top left hand corner of screen call cur$lcd ; Home to the top lh lcd corner ; pop psw pop d ret ; ;---------------------------------------------------------------- ; Turn all cursors off. ;---------------------------------------------------------------- ; cof$lcd: push psw mvi a,wr$cmd1 sta lcd$cmd mvi a,0000$1100b ; Display on, cursor off call lch$lcd ; Latch the command and data in. ; mvi a,wr$cmd2 sta lcd$cmd mvi a,0000$1100b ; Display on, cursor off call lch$lcd ; Latch the command and data in. pop psw ret ; ;---------------------------------------------------------------- ; Turn the current cursor at lcd$x and lcd$y on. ; ; This is decided by the current cursor address of the lcd. ;---------------------------------------------------------------- ; con$lcd: push b push psw ; ; Select the controller and load the write command control word mvi c,wr$cmd1 ; Default to controller 1 lda lcd$y ; Line number cpi 2 jc con$do ; Line 0 and 1 user controller 1 ; mvi c,wr$cmd2 ; Controller 2 if line 2 or 3 con$do: mov a,c ; get sta lcd$cmd ; Load the command mvi a,0fh ; cursor on call lch$lcd ; And do it. ; pop psw pop b ret ; page ;---------------------------------------------------------------- ; Cursor address. ; ; On Entry D = X in the range 0..38 (characters per line) ; E = Y in the range 0..3 (lines) ; ; If out of range, no change is made. ; All registers preserved ;---------------------------------------------------------------- ; cur$lcd: push b push psw ; ; Check if X > possible range. mov a,d cpi 40 ; Error if > 16 jrnc cur$err ; Check Line number mov a,e cpi 4 jrnc cur$err ; Error if > 3 ; sta lcd$y ; ; Turn off cursor(s) call cof$lcd ; All cursors off. Sets up lcd$cmd also. ; ; Now generate the ram address for the LCD mvi c,080h ; Top bit = cursor address mov a,e ; Get the line number back ani 0000$0001b ; Do a modulus 2 jrz cur$not$1 ; Top row is address 80h..8fh mvi c,0C0h ; If second row the offset is 0C0h ; cur$not$1: mov a,c ; Address add d ; Add in the X address call lch$lcd ; Command in A built up ; Save the X address also mov a,d ; X sta lcd$x ; ; Turn on the cusor call con$lcd ; cur$err: pop psw pop b ret ; ;---------------------------------------------------------------- ; Clear LCD to end of line ;---------------------------------------------------------------- ; eol$lcd: push b push d push psw ; Get the current cursor address. lda lcd$y mov e,a lda lcd$x mov d,a ; Save X ; ; A = column number. Send spaces to end of line now. ; eol$loop: push psw ; Save current column number mvi a,' ' ; Erase with a space. call put$lcd ; Send the character to the LCD pop psw ; Restore column ; cpi 39 ; Did we just do the last column ? jrz eol$done inr a jr eol$loop ; ; eol$done: call cur$lcd ; Cursor address to previous row/col position ; pop psw pop d pop b ret ; page ;---------------------------------------------------------------- ; ; Send character in A to the LCD ; ; We must decide which controller by checking the lcd$y address ; and use Y = 0,1 for controller 1, and 2,3 for controller 2. ;---------------------------------------------------------------- ; put$lcd: push psw push b ; ; Carriage return ? cpi 0dh jz put$cr cpi lf jz put$lf cpi bs ; Back space ? jz put$bs ; ; None of these. Bump the column number. lda lcd$x inr a sta lcd$x cpi 40 ; End of line ? jc put$lcd1 ; If not, continue. xra a ; Start of next line sta lcd$x ; ; Bump the row now. We must also cusor position. ; This is used as the line feed entry point so that the ; line number is advaced without advancing any columns. ; lda lcd$y inr a sta lcd$y cpi 4 ; Only allowed rows 0..3 jc put$lcd$xy ; Not last row and all ok ; ; Past last row. Start at top again. xra a sta lcd$y ; put$lcd$xy: push d lda lcd$x mov d,a lda lcd$y mov e,a call cur$lcd ; Setup cursor at end of line to "wrap" pop d ; ; Now select the controller to be used (written to). ; put$lcd1: mvi c,wr$dat1 ; Default to controller 1 lda lcd$y cpi 2 jc put$lcd$do mvi c,wr$dat2 ; put$lcd$do: mov a,c sta lcd$cmd ; pop b pop psw jmp lch$lcd ; ; Carriage return = home cursor to left hand column ; put$cr: xra a sta lcd$x ; ; Cursor address now - Also used by the CR handler ; put$lf$cur: push d lda lcd$x mov d,a ; D = X lda lcd$y mov e,a ; E = Y call cur$lcd pop d ; ; put$end: pop b ; Saved at routine start. pop psw ret ; ; Process a line feed by incrementing the line number by 1 ; put$lf: lda lcd$y ; Line number inr a sta lcd$y cpi 4 jc put$lf$cur ; Carry = not past end line xra a sta lcd$y jmp put$lf$cur ; ; Send a backspace to the LCD by decrementing the current column by ; one. NOTE that the backspacing will stop at left hand margin. ; put$bs: lda lcd$x ora a ; At zero ? jrz put$end dcr a sta lcd$x ; Save the new decremented column jmp put$lf$cur ; Cursor address now. ; page ;---------------------------------------------------------------- ; Print the menu at the address in DE into the LCD. ;---------------------------------------------------------------- ; pmu$lcd: push d ; save all push psw ; pmenu2: call clrwdt ; Reset watchdog call xyp$lcd ; Print the string following to the lcd ldax d ; is the next character a dollar ? cpi 0ffh ; end of menu ?? jrnz pmenu2 ; pop psw pop d ret ; ;---------------------------------------------------------------- ; Print the following X-Y addressed message to the LCD. The return ; address has the text in it preceeded by an X-Y address. ;---------------------------------------------------------------- ; xyi$lcd: xthl ; HL -> string, old hl in stack xchg ; now DE --> string call setxy ; set it up xchg ; now hl --> string start again call print xthl ; hl = original value, stack = return address ret ; ;---------------------------------------------------------------- ; Print the string --> by DE. Use the two bytes at the start of it ; as a screen address. ;---------------------------------------------------------------- ; xyp$lcd: push h call setxy ; set up screen xchg ; now hl --> string start call print xchg ; Restore DE --> past end of string pop h ret ; ; ---- Utility to print a string till a $. ---- ; On return HL -> to next byte after the string (code maybe) ; print: push psw inx h inx h ; skip over cursor address print2: mov a,m inx h ; Point to next character ora a ; null is allowed to end a string jz print3 cpi '$' ; End of string ? jz print3 call put$lcd ; Print the character jr print2 print3: pop psw ret ; ;---------------------------------------------------------------- ; Set the cursor up according to two bytes in ram which contain ; the X and Y addresses in them. The bytes --> by DE. ;---------------------------------------------------------------- ; setxy: push d ; save push h xchg ; HL --> bytes mov d,m ; load X value inx h mov e,m call cur$lcd ; Set it up pop h ; restore all now pop d ret ; ; ;---------------------------------------------------------------- ; Print string till null or $ ;---------------------------------------------------------------- ; str$lcd: push psw str$loop: call clrwdt ldax d cpi '$' ; $ = end jrz str$end ora a ; 00 = end jrz str$end call put$lcd ; Send inx d ; -> next jr str$loop ; ; str$end: pop psw ret ; ; ----oooo---- ; dseg ; lcd$cmd: ds 1 ; Command to write to LCD ; ; lcd$x: ds 1 lcd$y: ds 1 end ; ; -- End of module -- ;