2020-10-27 20:08:07 +01:00
; HD44780 20x4 characters LCD display driver
; @author Daniele Verducci
2021-07-11 09:49:42 +02:00
; @language: Z80 ASM
;
;
; This file is part of Pat80 Memory Monitor.
;
; Pat80 Memory Monitor is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; Pat80 Memory Monitor is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with Pat80 Memory Monitor. If not, see <http://www.gnu.org/licenses/>.
;
2020-11-24 21:34:51 +01:00
;
; USAGE:
; STR: DB "Hello world!",0 <-- null terminated string
; call Lcd_init <-- inits hd44780 controller
; ld bc, STR <-- load pointer to string's first char in reg BC
; call Lcd_print <-- this function will print the string
2020-11-22 23:01:22 +01:00
; LCD config (IO port 0)
LCD_INSTR_REG: EQU IO_0
LCD_DATA_REG: EQU IO_0 + 1
2020-10-26 22:05:03 +01:00
2020-11-01 12:25:51 +01:00
; constants
LCD_LINES_LEFT: DB 0x80 , 0xA8 , 0x94 , 0xBC ;array defining lcd command codes for the first char of every line
2020-10-27 22:27:39 +01:00
; variables
2020-11-23 08:47:23 +01:00
LCD_VAR_SPACE: EQU DRV_VAR_SPACE
lcd_cur_x: EQU LCD_VAR_SPACE
2020-11-01 12:25:51 +01:00
lcd_cur_y: EQU lcd_cur_x + 1
2020-10-24 23:30:50 +02:00
2020-10-27 20:08:07 +01:00
; functions
2020-10-24 23:30:50 +02:00
2020-11-01 12:25:51 +01:00
; Inits the lcd display
Lcd_init:
2020-10-24 23:30:50 +02:00
;reset procedure
2020-10-26 22:05:03 +01:00
ld a , 0x38
2020-10-24 23:30:50 +02:00
out ( LCD_INSTR_REG ), a
2020-10-26 22:05:03 +01:00
ld a , 0x08
2020-10-24 23:30:50 +02:00
out ( LCD_INSTR_REG ), a
2020-10-26 22:05:03 +01:00
ld a , 0x01
2020-10-24 23:30:50 +02:00
out ( LCD_INSTR_REG ), a
;init procedure
2020-10-26 22:05:03 +01:00
ld a , 0x38
2020-10-24 23:30:50 +02:00
out ( LCD_INSTR_REG ), a
2020-10-26 22:05:03 +01:00
ld a , 0x0F
2020-10-24 23:30:50 +02:00
out ( LCD_INSTR_REG ), a
ret
2020-10-26 22:05:03 +01:00
; Writes text starting from current cursor position
; @param BC Pointer to a null-terminated string first character
2020-11-01 12:25:51 +01:00
Lcd_print:
2020-10-24 23:30:50 +02:00
ld a , ( bc ) ; bc is the pointer to passed string's first char
cp 0 ; compare A content with 0 (subtract 0 from value and set zero flag Z if result is 0)
ret z ; if prev compare is true (Z flag set), string is finished, return
out ( LCD_DATA_REG ), a ; output char
inc bc ; increment bc to move to next char
2020-11-01 12:25:51 +01:00
; increment x position
ld a , ( lcd_cur_x )
inc a
ld ( lcd_cur_x ), a
; if x position > 19, increment y position and set x to 0
ld a , ( lcd_cur_x )
cp 20
jp nz , Lcd_print ; if x position is not 20, continue cycle
ld a , 0
ld ( lcd_cur_x ), a ; else set x to 0
; and increment y
ld a , ( lcd_cur_y )
inc a
ld ( lcd_cur_y ), a
; if y > 3 set y to 0
cp 4 ; a still contains lcd_cur_y: compare with 4
jp nz , Lcd_print ; if y position is not 4, continue cycle
ld a , 0
ld ( lcd_cur_x ), a ; else set y pos to 0
jp Lcd_print
2020-10-24 23:30:50 +02:00
2020-11-01 21:11:42 +01:00
; Writes a single character at current cursror position
; @param A Value of character to print
Lcd_printc:
out ( LCD_DATA_REG ), a
ret
2020-10-26 22:05:03 +01:00
; Set cursor position
; @param B X-axis position (0 to 19)
; @param C Y-axis position (0 to 3)
2020-11-01 12:25:51 +01:00
Lcd_locate:
ld a , b
ld ( lcd_cur_x ), a
ld a , c
ld ( lcd_cur_y ), a
call lcd_locate
ret
; private
; The cursor position can seem like black magic but it makes much more sense once you know that the HD44780 is designed to control a 40 character 4-line display. So if you have a 16× 2 then you will only see the first 16 characters of the top two lines. Simple enough once you get used to taking these character positions into account. For example, in a 16× 2 display, the first line is position 0-15. So 0x80 is the first position, 0x80 + 12 = 0x8C is the 13th (remember, they are zero indexed). The second line is a little tricky since it shows positions 64-79. Just add the position number (in decimal) to 0x80 (in hex) to get the hex address of the cursor position. The address is the command to move the cursor to that location. So, to move to the 13th position of the top line in a 16× 2 Sparkfun Display, I would send “0xFE 0x8C”. The first byte warns the onboard microcontroller that a command is coming, and the second byte is the command.
2020-10-26 22:05:03 +01:00
lcd_locate:
2020-11-01 12:25:51 +01:00
; warns the lcd microcontroller that a command is coming
ld a , 0xFE
out ( LCD_INSTR_REG ), a
; get line start command code from array
ld hl , LCD_LINES_LEFT ; load array pointer
ld bc , lcd_cur_y ; load line number
ld b , 0 ; since line number is only 8 bit, clean garbage on the upper bit
add hl , bc ; sum first array element to line number to access correct array element. Now hl contains array pointer
; now sum x offset to the start line code to obtain lcd controller complete position code
ld a , ( lcd_cur_x ) ; load cursor x position
add a , ( hl ) ; sum cursor x pos to line start code. Result is in a
out ( LCD_INSTR_REG ), a ; send cursor position to lcd controller
2020-10-26 22:05:03 +01:00
ret
; Clears the screen
2020-11-01 12:25:51 +01:00
Lcd_cls:
2020-10-26 22:05:03 +01:00
ld a , 0x01
out ( LCD_INSTR_REG ), a ; clear display
ld a , 0x02
out ( LCD_INSTR_REG ), a ; cursor to home (top left)
ret