; LIBLCD: LCD read/write library
; LCD is on port #0x
; A7-A4 = 0
; A3 selects top half (port #0A = write, port #09 = read, port #08 = ctrl)
; A2 selects bottom half (port #06 = write, port #05 = read, port #04 = ctrl)
; A1 is the Register Select bit
; A0 is Read/Write (1 = Read, 0 = Write)

; LCD commands
		DEFINE	Clear		%00000001
		DEFINE	InitMode	%00111000
		DEFINE	EntryModeSet	%00000110
		DEFINE	DisplayOn	%00001100

; LCD bit positions - not values, positions!
		DEFINE	bit_LCDRead	0
		DEFINE	bit_LCD_RS	1

; Various constants
		DEFINE	C_LCDTopHalf	#0A
		DEFINE	C_LCDBotHalf	#06
		; 2x40 should be 80, but the display half's memory map
		; is non-contiguous, so when you read the character position
		; on the second line, it doesn't start at 41...
		DEFINE	C_LCDLimit	103

;----------------------------------------------------------------------
; F_LCDinit: Initializes the LCD - clears the screen, sets the mode etc.
;
F_LCDinit
		push AF			; preserve registers
		push BC
		ld B, 2			; 2 iterations - one for each half
		ld C, #08		; Port 8
F_LCDinit.Loop
		ld A, InitMode
		out (C), A
		
		call F_LCDWait
		ld A, DisplayOn
		out (C), A
	
		call F_LCDWait
		ld A, EntryModeSet
		out (C), A

		call F_LCDWait
		ld A, Clear
		out (C), A

		call F_LCDWait

		ld C, #04		; Next half
		djnz F_LCDinit.Loop

		; Reset LCD variables
		ld A, C_LCDTopHalf
		ld (V_LCDWriteHalf), A

		; Done. Restore registers and return.
		pop BC
		pop AF
		ret
;-----------------------------------------------------------------------

;-----------------------------------------------------------------------
; F_LCDputchar: Writes a character to the current LCD line
; A = character to display
F_LCDputchar
		call F_LCDWait
		push BC
		push AF
		ld B, A		; preserve character we want to print
		ld A, (V_LCDWriteHalf)	; get the current display half
		ld C, A
		ld A, B		; restore character
		out (C), A

		; Flip display half? An earlier iteration will have
		; set the 'penultimate character' bit.
		ld A, (V_LCDPrepSwitch)
		cp 1
		jr z, F_LCDputchar.switch

		; Find out where we are on the display.
		ld A, C
		and %00001100	; mask out bits other than half select
		or 1		; set Read (i.e. the LSB)
		ld C, A
		in A, (C)
		and %01111111	; Mask out the busy flag.
		cp C_LCDLimit	; End of a display half?
		jr nz, F_LCDputchar.Exit	; nein!

		; Prepare to switch. Because the way the LCD works - if
		; we just write the last character on a half, we can no longer
		; tell that we are at the end, because the LCD byte counter
		; resets! So we have to remember we're at the penultimate
		; character.
		ld A, 1
		ld (V_LCDPrepSwitch), A
		jr F_LCDputchar.Exit

F_LCDputchar.switch
		; Switch halves
		bit 3, C	; Bit 3 = 1 for top half, 0 for bottom
		jr nz, F_LCDputchar.ToBottom
		ld A, C_LCDTopHalf
		jr F_LCDputchar.SaveHalf
F_LCDputchar.ToBottom
		ld A, C_LCDBotHalf
F_LCDputchar.SaveHalf
		ld (V_LCDWriteHalf), A
		ld A, 0
		ld (V_LCDPrepSwitch), A

F_LCDputchar.Exit
		pop AF
		pop BC
		ret

V_LCDWriteHalf	defb	C_LCDTopHalf
V_LCDPrepSwitch	defb	0
;-----------------------------------------------------------------------

;-----------------------------------------------------------------------
; F_LCDWait: Check the LCD's busy half, and wait until it clears.
; Parameters: None. All registers preserved.
F_LCDWait
		push AF
F_LCDWait.THWait
		in A, (#05)
		bit 7, A
		jr nz, F_LCDWait.THWait
F_LCDWait.BHWait
		in A, (#09)
		bit 7, A
		jr nz, F_LCDWait.BHWait

		pop AF
		ret
;-----------------------------------------------------------------------

