home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
cpm
/
utils
/
asmutl
/
hd64180a.lbr
/
IOLCD440.AZM
/
IOLCD440.ASM
Wrap
Assembly Source File
|
1991-08-04
|
15KB
|
652 lines
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 --
;