;
;==================================================================================================
; 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"
;
; 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
;
	; "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
;
	; 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:
	XOR	A
	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
;
; SEND FUNCTION CODE IN A
;
LCD_OUTF:
	PUSH	AF			; SAVE CODE
LCD_OUTF1:
	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
	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:
	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
	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:
	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
	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	"Build: ", CONFIG, 0
LCD_STR_IO	.DB	"Disk #", 0
LCD_STR_XPU	.DB	" CPU",0
;;;LCD_STR_SPD	.DB	"12.345",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	"Z180-K",0
LCD_CPU_Z180N	.DB	"Z180-N",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
