WIP 40x4 LCD: support in bios and emulator, minimal test rom, schematics

This commit is contained in:
2025-02-26 08:42:33 +01:00
parent bf91ff680d
commit f7afa69f66
9 changed files with 4934 additions and 39 deletions

View File

@ -0,0 +1,102 @@
; TM404A 40x4 characters LCD display (based on SN76489 chip) driver
; @author Daniele Verducci
; @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/>.
;
; --------------------------------------------------------------------------
;
; This is meant to be used with a 40x4 character display (containing a dual hd44780-compatible controller) like the TM404A.
; This kind of display uses two controllers to overcome the hd44780 controllers limitation of 80 addressable characters.
; It is seen as two separate displays, and thus have two EN pins. A finer implementation would apply some deconding logic to
; an address bit to use a single port, but here we will use two different IO ports to save an IC.
;
; LCD config (IO port 0)
LCD4004_TOP2LINES_PORT: EQU IO_2
LCD4004_BOTTOM2LINES_PORT: EQU IO_3
; PIN CONNECTIONS
;
; PIN DESCRIPTION TO PAT80 BUS PIN INFO
; ---------------------------------------------------------------------------------------------------
; 18 DB7DB0 DATA BUS
; 9 E1 (chip en 1) IOEN on LCD_TOP2LINES_PORT Chip enable for top 2 lines
; 10 R/W IOWR
; 11 RS A4 0: Command, 1: Data
; 12 V0 - Power supply for contrast (approx. +0.5V)
; 13 Vss - Ground
; 14 VDD - Power Supply Supply voltage for logic (+5.0V)
; 15 E2 (chip en 2) IOEN on LCD_BOTTOM2LINES_PORT Chip enable for bottom 2 lines
; 16 NC No Connect
;
; The 40x4 LCD memory map is the following:
;
; LINE CHIP EN PIN START ADDR END ADDR
; ----------------------------------------------------
; 1 9 0x00 (0) 0x27 (39)
; 2 9 0x40 (64) 0x67 (103)
; 3 15 0x00 (0) 0x27 (39)
; 4 15 0x40 (64) 0x67 (103)
;
; When we reach 0x40 on the first controller, we switch to the second.
; When we reach 0x40 on the second controller, we must shift
; all the lines up by one and position the cursor at 0x40.
; variables
LCD4004_VAR_SPACE: EQU DRV_IO_2_VAR_SPACE
LCD4004_CURSOR_POSITION_CONTROLLER: EQU DRV_IO_2_VAR_SPACE ; In which LCD controller is the cursor (0 or 1)
LCD4004_CURSOR_POSITION_CONTROLLER_MEMORY: EQU DRV_IO_2_VAR_SPACE + 1 ; Memory position of the cursor inside the controller's memory
; functions
; Initializes the driver and the LCD screen
LCD4004_Initialize:
; The following are documented as in the datasheet: RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
; Function set: 0 0 0 0 1 DL N F 0 0 (DL: 8-bit/4-bit, N: number of display lines 2/1, F: Font type 11dots/8dots)
; 0000111000 (8 bit, 2 lines)
; Display ON/OFF Control: 0 0 0 0 0 0 1 D C B (D: display on/off, C: cursor on/off, B: blinking cursor on/off)
; 0000001111 (display on, blinking cursor)
; Clear display
; 0000000001
; Move cursor (set DDRAM address in address counter)
LCD4004_MoveCursor:
; 0 0 1 AC6 AC5 AC4 AC3 AC2 AC1 AC0 (ACx: Address)
; Sends string
; @param BC Pointer to a null-terminated string first character
LCD4004_print:
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 (TERM_DATA_REG),a ; output char
inc bc ; increment bc to move to next char
jp LCD4004_print
; Writes a single character
; @param A Value of character to print
LCD4004_printc:
out (TERM_DATA_REG),a
ret

View File

@ -23,7 +23,7 @@ TERM_DATA_REG: EQU IO_0
TERM_DATA_AVAIL_REG: EQU IO_0 + 1
; variables
TERM_VAR_SPACE: EQU DRV_VAR_SPACE + 128
TERM_VAR_SPACE: EQU DRV_IO_0_VAR_SPACE
incoming_string: EQU TERM_VAR_SPACE
; functions

View File

@ -30,8 +30,8 @@ jp Sysinit ; Startup vector: DO NOT MOVE! Must be the first instruction
; I/O MAP
; I/O 0 (0x00 - 0x1F) Parallel terminal (uses addr 0x00 and 0x01)
; I/O 1 (0x20 - 0x3F) Sound card (uses addr 0x20 only)
; I/O 2 (0x40 - 0x5F) PS2 Keyboard (uses 0x40 and 0x41)
; I/O 3 (0x60 - 0x7F)
; I/O 2 (0x40 - 0x5F) 40x4 LCD, first controller (lines 1 and 2)
; I/O 3 (0x60 - 0x7F) 40x4 LCD, second controller (lines 3 and 4)
; I/O 4 (0x80 - 0x9F)
; I/O 5 (0xA0 - 0xBF)
; I/O 6 (0xC0 - 0xDF)
@ -89,12 +89,20 @@ Sys_Beep:
; MEMORY CONFIGURATION
SYS_VAR_SPACE: EQU 0x8000
DRV_VAR_SPACE: EQU 0x9000
APP_SPACE: EQU 0xA000
SYS_VAR_SPACE: EQU 0x8000 ; OS may allocate here memory for its own use
DRV_VAR_SPACE: EQU 0x9000 ; IO devices drivers may allocate here memory, each has a chunk of 512 bytes
DRV_IO_0_VAR_SPACE: EQU DRV_VAR_SPACE
DRV_IO_1_VAR_SPACE: EQU DRV_IO_0_VAR_SPACE + 512
DRV_IO_2_VAR_SPACE: EQU DRV_IO_1_VAR_SPACE + 512
DRV_IO_3_VAR_SPACE: EQU DRV_IO_2_VAR_SPACE + 512
DRV_IO_4_VAR_SPACE: EQU DRV_IO_3_VAR_SPACE + 512
DRV_IO_5_VAR_SPACE: EQU DRV_IO_4_VAR_SPACE + 512
DRV_IO_6_VAR_SPACE: EQU DRV_IO_5_VAR_SPACE + 512
DRV_IO_7_VAR_SPACE: EQU DRV_IO_6_VAR_SPACE + 512
APP_SPACE: EQU 0xA000 ; App may only allocate between there and MEM_END
MEM_END: EQU 0xFFFF
; SYSTEM CONFIGURATION
; DEVICES ("cards" on the IO bus) I/O space
IO_0: EQU 0x00
IO_1: EQU 0x20
IO_2: EQU 0x40

View File

@ -0,0 +1,116 @@
jp main ; Startup vector: DO NOT MOVE! Must be the first instruction
; Tests TM404A, on ports 2 and 3
IO_2: EQU 0x40
IO_3: EQU 0x60
LCD_TOP_INSTR_REG: EQU IO_2
LCD_TOP_DATA_REG: EQU IO_2 + 1
LCD_BOTTOM_INSTR_REG: EQU IO_3
LCD_BOTTOM_DATA_REG: EQU IO_3 + 1
; Inits the lcd display
LCD4004_init:
; --- Init first controller ---
; The following are documented as in the datasheet: RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
; Function set: 0 0 0 0 1 DL N F 0 0 (DL: 8-bit/4-bit, N: number of display lines 2/1, F: Font type 11dots/8dots)
; 0000111000 (8 bit, 2 lines)
ld a,0x38
out (LCD_TOP_INSTR_REG),a
call LCD4004_wait_busy_clear
; Display ON/OFF Control: 0 0 0 0 0 0 1 D C B (D: display on/off, C: cursor on/off, B: blinking cursor on/off)
; 0000001111 (display on, blinking cursor)
ld a,0x0F
out (LCD_TOP_INSTR_REG),a
call LCD4004_wait_busy_clear
; Clear display
; 0000000001
ld a,0x01
out (LCD_TOP_INSTR_REG),a
call LCD4004_wait_busy_clear
; --- Init second controller ---
; The following are documented as in the datasheet: RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
; Function set: 0 0 0 0 1 DL N F 0 0 (DL: 8-bit/4-bit, N: number of display lines 2/1, F: Font type 11dots/8dots)
; 0000111000 (8 bit, 2 lines)
ld a,0x38
out (LCD_BOTTOM_INSTR_REG),a
call LCD4004_wait_busy_clear
; Display ON/OFF Control: 0 0 0 0 0 0 1 D C B (D: display on/off, C: cursor on/off, B: blinking cursor on/off)
; 0000001111 (display on, blinking cursor)
ld a,0x0F
out (LCD_BOTTOM_INSTR_REG),a
call LCD4004_wait_busy_clear
; Clear display
; 0000000001
ld a,0x01
out (LCD_BOTTOM_INSTR_REG),a
call LCD4004_wait_busy_clear
ret
; Prints string
; @param BC Pointer to a null-terminated string first character
LCD4004_TOP_print:
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_TOP_DATA_REG),a ; output char
call LCD4004_wait_busy_clear ; wait for the lcd to execute (busy signal check)
inc bc ; increment bc to move to next char
jp LCD4004_TOP_print
; Prints string
; @param BC Pointer to a null-terminated string first character
LCD4004_BOTTOM_print:
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_BOTTOM_DATA_REG),a ; output char
call LCD4004_wait_busy_clear ; wait for the lcd to execute (busy signal check)
inc bc ; increment bc to move to next char
jp LCD4004_BOTTOM_print
; Waits for the busy flag to be clear, indicating the LCD controllers to have finished their work
LCD4004_wait_busy_clear:
ret
in a, (LCD_TOP_INSTR_REG) ; reads the status
rla ; busy flag is DB7, so we shift it into carry to check it
jp c, LCD4004_wait_busy_clear ; if carry is set, lcd is busy
in a, (LCD_BOTTOM_INSTR_REG) ; reads the status
rla ; busy flag is DB7, so we shift it into carry to check it
jp c, LCD4004_wait_busy_clear ; if carry is set, lcd is busy
ret
; --- TEST CODE ---
main:
TEST_STR_80: DB "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam augue tortor sed.",0 ; null terminated string
TEST_STR_85: DB "2Lorem ipsum dolor sit amet, consectetur adipiscing elit praesent pellentesque nisi.",0 ; null terminated string
; Init display
call LCD4004_init
; Write string on first 2 lines
ld bc, TEST_STR_80
call LCD4004_TOP_print
; Write long string on last 2 lines (to see how it wraps)
ld bc, TEST_STR_85
call LCD4004_BOTTOM_print
halt