; ;================================================================================================== ; HARDWARE SUPPORT FOR HITACHI HD44780 OR EQUIVALENT ;================================================================================================== ; ; CURRENTLY ASSUMES A 20X4 DISPLAY ; ; TYPICAL PORTS USED ON RCBUS ECOSYSTEM: ; ; PRIMARY ALT ; FUNCTION $DA $AA ; DATA $DB $AB ; LCD_FUNC .EQU LCDBASE + 0 ; WRITE LCD_STAT .EQU LCDBASE + 0 ; READ LCD_DATA .EQU LCDBASE + 1 ; READ/WRITE ; LCD_ROWS .EQU 4 LCD_COLS .EQU 20 ; LCD_FUNC_CLEAR .EQU $01 ; CLEAR DISPLAY LCD_FUNC_HOME .EQU $02 ; HOME CURSOR & REMOVE ALL SHIFTING LCD_FUNC_ENTRY .EQU $04 ; SET CUR DIR AND DISPLAY SHIFT LCD_FUNC_DISP .EQU $08 ; DISP, CUR, BLINK ON/OFF LCD_FUNC_SHIFT .EQU $10 ; MOVE CUR / SHIFT DISP LCD_FUNC_SET .EQU $20 ; SET INTERFACE PARAMS LCD_FUNC_CGADR .EQU $40 ; SET CGRAM ADRESS LCD_FUNC_DDADR .EQU $80 ; SET DDRAM ADDRESS ; DEVECHO "LCD: IO=" DEVECHO LCDBASE DEVECHO ", SIZE=" DEVECHO LCD_COLS DEVECHO "X" DEVECHO LCD_ROWS DEVECHO "\n" ; ;-------------------------------------------------------------------------------------------------- ; HBIOS MODULE HEADER ;-------------------------------------------------------------------------------------------------- ; ORG_LCD .EQU $ ; .DW SIZ_LCD ; MODULE SIZE .DW LCD_INITPHASE ; ADR OF INIT PHASE HANDLER ; LCD_INITPHASE: ; INIT PHASE HANDLER, A=PHASE CP HB_PHASE_PREINIT ; PREINIT PHASE? JP Z,LCD_PREINIT ; DO PREINIT CP HB_PHASE_INIT ; INIT PHASE? JP Z,LCD_INIT ; DO INIT RET ; DONE ; ; HARDWARE RESET PRIOR TO ROMWBW CONSOLE INITIALIZATION ; LCD_PREINIT: ; ; RESET LCD CONTROLLER, DELAYS ARE FIXED, BUSY FLAG ; CANNOT BE USED YET, CONTROLLER MAY NOT EXIST! LD A,LCD_FUNC_SET | %11000 EZ80_IO OUT (LCD_FUNC),A LD DE,50000/16 ; WAIT >40MS, WE USE 50MS CALL VDELAY ; DO IT LD A,LCD_FUNC_SET | %11000 EZ80_IO OUT (LCD_FUNC),A LD DE,5000/16 ; WAIT >4.1MS, WE USE 5MS CALL VDELAY ; DO IT LD A,LCD_FUNC_SET | %11000 EZ80_IO OUT (LCD_FUNC),A LD DE,5000/16 ; WAIT >4.1MS, WE USE 5MS CALL VDELAY ; DO IT ; ; TEST FOR PRESENCE... CALL LCD_DETECT ; PROBE FOR HARDWARE LD A,(LCD_PRESENT) ; GET PRESENCE FLAG OR A ; SET FLAGS RET Z ; BAIL OUT IF NOT PRESENT ; ; REGISTER DRIVER WITH HBIOS LD BC,LCD_DISPATCH CALL DSKY_SETDISP ; ; WE CAN NOW DO NORMAL I/O W/ BUSY FLAG LD DE,LCD_INIT_SEQ ; INIT SEQUENCE CALL LCD_OUTFS ; SEND IT ; ; PUT SOMETHING ON THE DISPLAY LD DE,LCD_STR_BAN CALL LCD_OUTDS ; ; SECOND LINE ; CPU TYPE LD HL,$0100 ; ROW 2, COL 0 CALL LCD_GOTORC LD HL,LCD_CPU LD A,(HB_CPUTYPE) ; GET CPU TYPE RLCA ; WORD OFFSET CALL ADDHLA ; ADD OFFSET LD E,(HL) ; GET LSB INC HL ; BUMP LD D,(HL) ; GET MSB CALL LCD_OUTDS LD DE,LCD_STR_XPU CALL LCD_OUTDS ; CALL LCD_EVT_CPUSPD ; ; THIRD LINE LD HL,$0200 ; ROW 2, COL 0 CALL LCD_GOTORC LD DE,LCD_STR_CFG CALL LCD_OUTDS ; XOR A ; SIGNAL SUCCESS RET ; DONE ; ; POST CONSOLE INITIALIZATION ; LCD_INIT: CALL NEWLINE ; FORMATTING PRTS("LCD: IO=0x$") LD A,LCDBASE CALL PRTHEXBYTE ; LD A,(LCD_PRESENT) ; GET PRESENCE FLAG OR A ; SET FLAGS JR Z,LCD_INIT1 ; HANDLE NOT PRESENT ; CALL PC_SPACE LD A,LCD_COLS CALL PRTDEC8 PRTS("X$") LD A,LCD_ROWS CALL PRTDEC8 XOR A ; SIGNAL SUCCESS RET ; DONE ; LCD_INIT1: PRTS(" NOT PRESENT$") OR $FF RET ; ; DSKY DEVICE FUNCTION DISPATCH ENTRY ; A: RESULT (OUT), 0=OK, Z=OK, NZ=ERR ; B: FUNCTION (IN) ; LCD_DISPATCH: LD A,B ; GET REQUESTED FUNCTION AND $0F ; ISOLATE SUB-FUNCTION JP Z,LCD_NULL ; RESET DSKY HARDWARE DEC A JP Z,LCD_NULL ; GET KEYPAD STATUS DEC A JP Z,LCD_NULL ; READ A KEY FROM THE KEYPAD DEC A JP Z,LCD_NULL ; DISPLAY A 32-BIT BINARY VALUE IN HEX DEC A JP Z,LCD_SHOWSEG ; DISPLAY SEGMENTS DEC A JP Z,LCD_NULL ; SET KEYPAD LEDS DEC A JP Z,LCD_NULL ; SET STATUS LED DEC A JP Z,LCD_NULL ; BEEP DSKY SPEAKER DEC A JP Z,LCD_DEVICE ; DEVICE INFO DEC A JP Z,LCD_MESSAGE ; HANDLE MESSAGE DEC A JP Z,LCD_EVENT ; HANDLE EVENT SYSCHKERR(ERR_NOFUNC) RET ; ; ; LCD_NULL: XOR A RET ; ; DRAW DISPLAY USING DEBUG ALPHABET ; HL = SOURCE BUFFER ; LCD_SHOWSEG: XOR A RET ; ; DEVICE INFORMATION ; LCD_DEVICE: LD D,DSKYDEV_LCD ; D := DEVICE TYPE LD E,0 ; E := PHYSICAL DEVICE NUMBER LD H,0 ; H := MODE LD L,LCDBASE ; L := BASE I/O ADDRESS XOR A ; SIGNAL SUCCESS RET ; ; MESSAGE HANDLER ; LCD_MESSAGE: LD A,C ; GET MESSAGE ID ADD A,A ; WORD OFFSET LD HL,LCD_MSGTBL ; START OF MESSAGE TABLE CALL ADDHLA ; ADD OFFSET LD A,(HL) ; SAVE LSB INC HL ; BUMP TO MSB LD H,(HL) ; GET MSB LD L,A ; GET LSB ; EX DE,HL LD HL,$0300 ; ROW 3, COL 0 CALL LCD_GOTORC CALL LCD_LINEOUT XOR A ; SIGNAL SUCCESS RET ; ; EVENT HANDLER ; LCD_EVENT: LD A,C ; EVENT ID OR A ; 0=CPUSPD JR Z,LCD_EVT_CPUSPD ; HANDLE CPU SPD CHANGE DEC A ; 1=DSKACT JR Z,LCD_EVT_DSKACT ; HANDLE DISK ACTIVITY XOR A RET ; ; CPU SPEED CHANGE ; LCD_EVT_CPUSPD: ; "12.345 MHz" RIGHT JUSTIFIED LD HL,$010A ; ROW 2, COL 10 CALL LCD_GOTORC LD HL,(CB_CPUKHZ) PUSH HL LD BC,10000 ; 10 MHZ SBC HL,BC ; SUBTRACT JR NC,LCD_PREINIT1 LD A,' ' ; EXTRA PAD CALL LCD_OUTD LCD_PREINIT1: POP HL CALL LCD_PRTD3M ; PRINT AS DECIMAL WITH 3 DIGIT MANTISSA LD DE,LCD_STR_MHZ CALL LCD_OUTDS ; RET ; ; FORMAT: "Disk #99 R:12345678" ; 01234567890123456789 ; LCD_EVT_DSKACT: ; LD HL,$0300 ; ROW 3, COL 0 CALL LCD_GOTORC ; SET DISPLAY ADDRESS ; LD DE,LCD_STR_IO ; PREFIX CALL LCD_OUTDS ; SEND TO DISPLAY (COLS 0-5) ; LD A,(HB_DSKUNIT) ; GET DISK UNIT NUM CALL LCD_EVT_DSKACT_BYTE ; SEND TO DISPLAY (COLS 6-7) HEX??? ; LD A,' ' ; SEPARATOR CALL LCD_OUTD ; SEND TO DISPLAY (COL 8) CALL LCD_OUTD ; SEND TO DISPLAY (COL 9) ; LD A,(HB_DSKFUNC) ; ACTIVE DISK FUNCTION CP BF_DIOWRITE ; WRITE? LD A,'W' ; ASSUME WRITE JR Z,LCD_EVT_DSKACT0 ; GO AHEAD LD A,'R' ; OTHERWISE READ LCD_EVT_DSKACT0: CALL LCD_OUTD ; SEND CHAR (COL 10) ; LD A,':' ; SEPARATOR CALL LCD_OUTD ; SEND TO DISPLAY (COL 11) ; LD HL,HB_DSKADR+3 ; END OF DWORD (MSB) LD B,4 ; DO 4 BYTES ; LCD_EVT_DSKACT1: LD A,(HL) ; GET BYTE CALL LCD_EVT_DSKACT_BYTE ; SEND TO DISPLAY (COLS 12-19) DEC HL ; DEC PTR DJNZ LCD_EVT_DSKACT1 ; DO ALL 4 BYTES RET ; LCD_EVT_DSKACT_BYTE: PUSH AF ; SAVE BYTE RRCA ; DO TOP NIBBLE FIRST RRCA RRCA RRCA CALL HEXCONV ; CONVERT NIBBLE TO ASCII CALL LCD_OUTD ; SEND TO DISPLAY POP AF ; RECOVER CURRENT BYTE CALL HEXCONV ; CONVERT NIBBLE TO ASCII CALL LCD_OUTD ; SEND TO DISPLAY RET ; DONE ; ; DETECT PRESENCE OF LCD CONTROLLER BY WRITING AND READING BACK ; TEST VALUES IN THE CONTROLLER RAM. ; WE DO NOT USE THE NORMAL READ/WRITE ROUTINES BECAUSE WE DO ; NOT WANT TO STALL WAITING ON THE BUSY FLAG IF THE CONTROLLER ; IS NOT PRESENT ; LCD_DETECT: ; FIRST PASS W/ TEST VALUE $AA LD C,$AA CALL LCD_DETECT_PASS JR NZ,LCD_DETECT1 ; ; SECOND PASS W/ TEST VALUE $55 LD C,$55 CALL LCD_DETECT_PASS JR NZ,LCD_DETECT1 ; ; LCD PRESENT OR $FF JR LCD_DETECT_Z ; LCD_DETECT1: ; LCD NOT PRESENT XOR A JR LCD_DETECT_Z ; LCD_DETECT_Z: LD (LCD_PRESENT),A RET ; ; WRITE AND READBACK VALUE IN C TO THE FIRST BYTE OF DDRAM ; RETURN WITH COMPARE RESULT ; LCD_DETECT_PASS: CALL LCD_DELAY ; WAIT LD A,LCD_FUNC_DDADR EZ80_IO OUT (LCD_FUNC),A ; POINT TO FIRST BYTE CALL LCD_DELAY ; WAIT LD A,C ; TEST VALUE EZ80_IO OUT (LCD_DATA),A ; WRITE IT CALL LCD_DELAY ; WAIT LD A,LCD_FUNC_DDADR EZ80_IO OUT (LCD_FUNC),A ; POINT TO FIRST BYTE CALL LCD_DELAY EZ80_IO IN A,(LCD_DATA) ; GET VALUE CP C ; AS WRITTEN? RET ; ; DELAY USED DURING DETECT, >37US ; LCD_DELAY: CALL DELAY ; 16US CALL DELAY ; 16US JP DELAY ; 16US, TOTAL 48US ; ; DELAY USED DURING NORMAL I/O ; REQUIRED FOR HIGH SPEED CPU OPERATION ; #DEFINE LCD_XDELAY EX (SP),HL \ EX (SP),HL ; ; SEND FUNCTION CODE IN A ; LCD_OUTF: PUSH AF ; SAVE CODE LCD_OUTF1: LCD_XDELAY EZ80_IO IN A,(LCD_STAT) ; GET STATUS AND $80 ; ISOLATE BUSY FLAG JR NZ,LCD_OUTF1 ; LOOP TILL NOT BUSY POP AF ; RECOVER CODE LCD_XDELAY EZ80_IO OUT (LCD_FUNC),A ; SEND IT RET ; DONE ; ; SEND FUNCTION STRING ; DE=STRING ADDRESS, NULL TERMINATED ; LCD_OUTFS: LD A,(DE) ; NEXT BYTE TO SEND OR A ; SET FLAGS RET Z ; DONE WHEN NULL REACHED INC DE ; BUMP POINTER CALL LCD_OUTF ; SEND IT JR LCD_OUTFS ; LOOP AS NEEDED ; ; SEND DATA BYTE IN A ; LCD_OUTD: PUSH AF ; SAVE BYTE LCD_OUTD1: LCD_XDELAY EZ80_IO IN A,(LCD_STAT) ; GET STATUS AND $80 ; ISOLATE BUSY FLAG JR NZ,LCD_OUTD1 ; LOOP TILL NOT BUSY POP AF ; RECOVER BYTE LCD_XDELAY EZ80_IO OUT (LCD_DATA),A ; SEND IT RET ; DONE ; ; SEND STRING, BLANK PAD TO END OF LINE ; ASSUMES WRITING TO FIRST COLUMN ; LCD_LINEOUT: LD B,LCD_COLS CALL LCD_OUTDS LCD_LINEOUT1: LD A,' ' CALL LCD_OUTD DJNZ LCD_LINEOUT1 RET ; ; SEND DATA STRING ; DE=STRING ADDRESS, NULL TERMINATED ; LCD_OUTDS: LD A,(DE) ; NEXT BYTE TO SEND OR A ; SET FLAGS RET Z ; DONE WHEN NULL REACHED INC DE ; BUMP POINTER DEC B ; PADDING COUNTER FOR ABOVE CALL LCD_OUTD ; SEND IT JR LCD_OUTDS ; LOOP AS NEEDED ; ; GET DATA BYTE INTO A ; LCD_IND: LCD_XDELAY EZ80_IO IN A,(LCD_STAT) ; GET STATUS AND $80 ; ISOLATE BUSY FLAG JR NZ,LCD_IND ; LOOP TILL NOT BUSY POP AF ; RECOVER BYTE LCD_XDELAY EZ80_IO IN A,(LCD_DATA) ; GET IT RET ; DONE ; ; GOTO ROW(H),COL(L) ; LCD_GOTORC: PUSH HL ; SAVE INCOMING LD A,H ; ROW # TO A LD HL,LCD_ROWSIDX ; POINT TO ROWS INDEX TABLE CALL ADDHLA ; INDEX TO ROW ENTRY LD A,(HL) ; GET RWO START POP HL ; RECOVER INCOMING ADD A,L ; ADD COLUMN ADD A,LCD_FUNC_DDADR ; APPLY FUNCTION BIT JR LCD_OUTF ; AND SEND IT ; ; PRINT VALUE OF HL AS THOUSANDTHS, IE. 0.000 ; LCD_PRTD3M: PUSH BC PUSH DE PUSH HL LD E,'0' LD BC,-10000 CALL LCD_PRTD3M1 LD E,0 LD BC,-1000 CALL LCD_PRTD3M1 LD A,'.' CALL LCD_OUTD LD BC,-100 CALL LCD_PRTD3M1 LD C,-10 CALL LCD_PRTD3M1 LD C,-1 CALL LCD_PRTD3M1 POP HL POP DE POP BC RET LCD_PRTD3M1: LD A,'0' - 1 LCD_PRTD3M2: INC A ADD HL,BC JR C,LCD_PRTD3M2 SBC HL,BC CP E JR Z,LCD_PRTD3M3 LD E,0 CALL LCD_OUTD LCD_PRTD3M3: RET ; ; DATA STORAGE ; LCD_PRESENT .DB 0 ; NON-ZERO WHEN HARDWARE DETECTED ; LCD_ROWSIDX .DB $00,$40,$14,$54 ; ROW START INDEX ; LCD_INIT_SEQ: .DB LCD_FUNC_SET | %11000 ; FUNCTION SET, 2 LINES, 5X8 FONT .DB LCD_FUNC_DISP ; DISPLAY OFF .DB LCD_FUNC_CLEAR ; CLEAR DISPLAY, HOME CURSOR .DB LCD_FUNC_ENTRY | $02 ; INCREMENT, NO SHIFT .DB LCD_FUNC_DISP | $04 ; DISPLAY ON, NO CURSOR, NO BLINK .DB $00 ; TERMINATOR ; LCD_STR_BAN .DB "RomWBW v", BIOSVER, 0 LCD_STR_CFG .DB CONFIG, 0 LCD_STR_IO .DB "Disk #", 0 LCD_STR_XPU .DB " CPU",0 LCD_STR_MHZ .DB " MHz",0 ; LCD_CPU .DW LCD_CPU_Z80 .DW LCD_CPU_Z180 .DW LCD_CPU_Z180K .DW LCD_CPU_Z180N .DW LCD_CPU_Z280 .DW LCD_CPU_EZ80 ; LCD_CPU_Z80 .DB "Z80",0 LCD_CPU_Z180 .DB "Z180",0 LCD_CPU_Z180K .DB "Z180K",0 LCD_CPU_Z180N .DB "Z180N",0 LCD_CPU_Z280 .DB "Z280",0 LCD_CPU_EZ80 .DB "eZ80",0 ; LCD_MSGTBL: .DW LCD_MSG_LDR_SEL .DW LCD_MSG_LDR_BOOT .DW LCD_MSG_LDR_LOAD .DW LCD_MSG_LDR_GO .DW LCD_MSG_MON_RDY .DW LCD_MSG_MON_BOOT ; LCD_MSG_LDR_SEL .DB "Ready",0 LCD_MSG_LDR_BOOT .DB "Boot...",0 LCD_MSG_LDR_LOAD .DB "Load...",0 LCD_MSG_LDR_GO .DB "Go...",0 LCD_MSG_MON_RDY .DB "-CPU UP-",0 LCD_MSG_MON_BOOT .DB "Boot!",0 ; ;-------------------------------------------------------------------------------------------------- ; HBIOS MODULE TRAILER ;-------------------------------------------------------------------------------------------------- ; END_LCD .EQU $ SIZ_LCD .EQU END_LCD - ORG_LCD ; MEMECHO "LCD occupies " MEMECHO SIZ_LCD MEMECHO " bytes.\n"