;
;==================================================================================================
; ESP32 DRIVER
;
; SUPPORTS DAN WERNER'S NHYODYNE (MBC) ESP32 MODULE
; https://github.com/danwerner21/nhyodyne/tree/main/Z80ESP
;==================================================================================================
;
; TODO:
; - CLEAR CONSOLE SCREEN AT INITIALIZATION
; - INITIALIZE BAUD/MODE OF SERIAL INTERFACES AT INITIALIZATION?
;
ESP_IOBASE	.EQU	$9C
ESP_0_IO	.EQU	ESP_IOBASE + 0
ESP_1_IO	.EQU	ESP_IOBASE + 1
ESP_STAT	.EQU	ESP_IOBASE + 2
;
; ESP   STATUS PORT
; MSB X X S S S S S S
;         | | | | | +- ESP0 READY OUTPUT
;         | | | | +--- ESP0 BUSY
;         | | | +----- ESP0 SPARE
;         | | +------- ESP1 READY OUTPUT
;         | +--------- ESP1 BUSY
;         +----------- ESP1 SPARE
;
ESP_0_RDY	.EQU	%00000001
ESP_0_BUSY	.EQU	%00000010
ESP_0_SPARE	.EQU	%00000100
ESP_1_RDY	.EQU	%00001000
ESP_1_BUSY	.EQU	%00010000
ESP_1_SPARE	.EQU	%00100000
;
; COMMAND OPCODES
;
ESP_CMD_NOP	.EQU	0		; NO OP/SYNC
ESP_CMD_SYNC	.EQU	0		; NO OP/SYNC
ESP_0_CMD_COUT	.EQU	1		; CHAR OUT
ESP_0_CMD_CSTR	.EQU	2		; STRING OUT
ESP_0_CMD_KIN	.EQU	3		; KEY IN
ESP_0_CMD_KST	.EQU	4		; KBD BUF STATUS
ESP_CMD_SBAUD	.EQU	6		; SET SERIAL BAUD RATE
ESP_CMD_SMODE	.EQU	7		; SET SERIAL LINE MODE
ESP_CMD_SOUT	.EQU	8		; SERIAL BYTE OUT
ESP_CMD_SIN	.EQU	10		; SERIAL BYTE IN
ESP_CMD_SST	.EQU	11		; SERIAL INPUT STATUS
ESP_CMD_DISC	.EQU	$FF		; DISCOVER
;
; ESP CONFIG TABLE ENTRY OFFSETS
;
ESP_CFG_DEV	.EQU	0		; DEVICE NUMBER
ESP_CFG_IO	.EQU	1		; ESP I/O PORT
ESP_CFG_ST	.EQU	2		; ESP STATUS PORT
ESP_CFG_RDYMSK	.EQU	3		; ESP READY MASK
ESP_CFG_BSYMSK	.EQU	4		; ESP BUSY MASK
;
	DEVECHO	"ESP: IO="
	DEVECHO	ESP_IOBASE
	DEVECHO	"\n"
;
; GLOBAL ESP INITIALIZATION
;
ESP_INIT:
;
	CALL	NEWLINE			; FORMATTING
	PRTS("ESP: IO=0x$")
	LD	A,ESP_IOBASE
	CALL	PRTHEXBYTE
;
	XOR	A			; ZERO ACCUM
	LD	(ESP_PRES),A		; CLEAR MODULE PRESENCE BITS
;
	; DETECT FIRST ESP32 MODULE
	PRTS(" A=$")
	LD	IY,ESPSER0_CFG
	CALL	ESP_DETECT
	JR	Z,ESP_INIT1		; FOUND
	LD	DE,ESP_STR_NOHW
	CALL	WRITESTR
	JR	ESP_INIT2
;
ESP_INIT1:
	CALL	ESP_PRTVER

	LD	A,(ESP_PRES)
	SET	0,A
	LD	(ESP_PRES),A

;
ESP_INIT2:
	; DETECT SECOND ESP32 MODULE
	PRTS(" B=$")
	LD	IY,ESPSER1_CFG
	CALL	ESP_DETECT
	JR	Z,ESP_INIT3		; FOUND
	LD	DE,ESP_STR_NOHW
	CALL	WRITESTR
	JR	ESP_INIT4
;
ESP_INIT3:
	CALL	ESP_PRTVER

	LD	A,(ESP_PRES)
	SET	1,A
	LD	(ESP_PRES),A

;
ESP_INIT4:
	; INITIALIZE FIRST MODULE CHILD DRIVERS
	LD	A,(ESP_PRES)
	BIT	0,A
	JR	Z,ESP_INIT5
	LD	IY,ESPSER0_CFG
	CALL	ESPSER_INIT		; SERIAL INITIALIZATION
	LD	IY,ESPCON0_CFG
	CALL	ESPCON_INIT		; CONSOLE INITIALIZATION
;
ESP_INIT5:
	; INITIALIZE SECOND MODULE CHILD DRIVERS
	LD	A,(ESP_PRES)
	BIT	1,A
	JR	Z,ESP_INIT6
	LD	IY,ESPSER1_CFG
	CALL	ESPSER_INIT		; SERIAL INITIALIZATION
;
ESP_INIT6:
	RET
;
;==================================================================================================
; ESP32 INTERFACE FUNCTIONS
;==================================================================================================
;
ESP_DETECT:
	; TRY TO DETECT IF PORT IS FLOATING AND
	; FAIL DETECTION IF SO
	LD	C,(IY+ESP_CFG_ST)	; ESP STATUS PORT
	IN	A,(C)			; READ IT
	;CALL	PC_SPACE		; *DEBUG*
	;CALL	PRTHEXBYTE		; *DEBUG*
	AND	%11100000		; ISOLATE TOP 3 BITS
	CP	$00			; ALWAYS ZERO IF PRESENT
	RET	NZ			; ABORT ON FAILURE
;
	; ESP32 PROCESSOR MAY TAKE A FEW SECONDS TO START UP, SO
	; HERE WE WAIT FOR BUSY TO CLEAR WITH ABOUT A 5 SECOND TIMEOUT
	LD	B,0			; LOOP UP TO 256 TIMES
	LD	C,(IY+ESP_CFG_ST)	; ESP STATUS PORT
ESP_DETECT1:
	IN	A,(C)			; GET STATUS
	AND	(IY+ESP_CFG_BSYMSK)	; IS ESP BUSY?
	JR	Z,ESP_DETECT2		; MOVE ALONG IF NOT BUSY
	LD	DE,1500			; 1500 * 16US = 24MS
	CALL	VDELAY			; DELAY
	DJNZ	ESP_DETECT1		; LOOP
	OR	$FF			; SIGNAL FAILURE
	RET				; DONE
;
ESP_DETECT2:
	CALL 	ESP_SYNC
	;LD	A,B			; *DEBUG*
	;CALL	PC_SPACE		; *DEBUG*
	;CALL	PRTHEXBYTE		; *DEBUG*
	CALL	ESP_CLR			; CLEAR ANY PENDING DATA
	LD	A,ESP_CMD_DISC		; DISCOVER COMMAND
	CALL	ESP_OUT			; SEND IT
;
	; LOOK FOR SIGNATURE STARTING WITH "ESP"
	CALL	ESP_INWAIT		; ATTEMPT TO GET CHAR
	;CALL	PC_SPACE		; *DEBUG*
	;CALL	PRTHEXBYTE		; *DEBUG*
	CP	'E'
	RET	NZ
	CALL	ESP_INWAIT		; ATTEMPT TO GET CHAR
	;CALL	PC_SPACE		; *DEBUG*
	;CALL	PRTHEXBYTE		; *DEBUG*
	CP	'S'
	RET	NZ
	CALL	ESP_INWAIT		; ATTEMPT TO GET CHAR
	;CALL	PC_SPACE		; *DEBUG*
	;CALL	PRTHEXBYTE		; *DEBUG*
	CP	'P'
	RET
;
; CLEAR ESP INPUT QUEUE
;
ESP_CLR:
;	LD	B,0			; MAX CHARS TO READ   /Removed DDW
;ESP_CLR0:   /Removed DDW
	CALL	ESP_WTNBSY		; WAIT TILL NOT BUSY
	RET	NZ			; BAIL OUT IF TIMEOUT
	IN	A,(ESP_STAT)		; GET STATUS
	AND	(IY+ESP_CFG_RDYMSK)	; IS THERE MORE DATA?
	RET	Z			; IF NOT, DONE
	LD	C,(IY+ESP_CFG_IO)	; ESP I/O PORT
	IN	A,(C)			; GET BYTE
	JP 	ESP_CLR
;	DJNZ	ESP_CLR0		; LOOP TILL DONE /Removed DDW
;	OR	$FF			; SIGNAL FAILURE /Removed DDW
;	RET /Removed DDW

;
; RE-SYNC ESP  (added DDW)
;
ESP_SYNC:
	LD	B,0				; sync count -- WORST CASE  PERFORMACE COULD BE IMPROVED BY REDUCING THIS VALUE
ESP_SYNC0:
	PUSH 	BC
	LD	A,ESP_CMD_SYNC		; Sync ESP
	CALL	ESP_OUT			; SEND CMD OPCODE
	POP 	BC
	DJNZ	ESP_SYNC0		; LOOP TILL DONE
	RET
;
; PRINT ESP VERSION STRING TO CONSOLE
;
ESP_PRTVER:
	CALL 	ESP_SYNC
	CALL	ESP_CLR			; CLEAR ANY PENDING DATA
	;LD	DE,1000			; SMALL DELAY HERE
	;CALL	VDELAY			; ... SEEMS TO HELP RELIABILITY
	LD	A,ESP_CMD_DISC		; DISCOVER COMMAND
	CALL	ESP_OUT			; SEND IT
	CALL	ESP_WTRDY		; WAIT FOR READY TO OUTPUT
ESP_PRTVER1:
	CALL	ESP_WTNBSY		; WAIT TILL NOT BUSY
	IN	A,(ESP_STAT)		; GET STATUS
	AND	(IY+ESP_CFG_RDYMSK)	; ISOLATE OUTPUT READY BIT
	RET	Z			; DONE IF NOTHING READY
	CALL	ESP_IN			; GET NEXT CHAR
	CALL	COUT			; PRINT CHAR
	JR	ESP_PRTVER1		; LOOP
;
; SEND BYTE TO ESP
;
ESP_OUT:
	PUSH	AF			; SAVE VALUE
	CALL	ESP_WTNBSY		; WAIT TILL NOT BUSY
	POP	AF			; POP VALUE
	LD	C,(IY+ESP_CFG_IO)	; ESP I/O PORT
	OUT	(C),A			; SEND BYTE
	JR	ESP_WTBSY		; RETURN VIA WTBSY
;
; GET BYTE FROM ESP (BLOCKING)
;
ESP_INWAIT:
	CALL	ESP_WTNBSY		; WAIT TILL NOT BUSY
	CALL	ESP_WTRDY		; WAIT FOR READY TO OUTPUT
	JP 		ESP_IN1			; Added DDW
;
; GET BYTE FROM ESP (NON BLOCKING)
;
ESP_IN:
	CALL	ESP_WTNBSY		; WAIT TILL NOT BUSY
ESP_IN1:
	LD	C,(IY+ESP_CFG_IO)	; ESP I/O PORT
	IN	A,(C)			; GET BYTE
	PUSH	AF			; SAVE VALUE
	CALL	ESP_WTBSY		; WAIT TILL BUSY
	POP	AF			; RESTORE VALUE
	RET				; AND RETURN
;
; WAIT FOR ESP TO BE NOT BUSY
;
ESP_WTNBSY:
	LD	B,0			; MAX TRIES
	;PUSH	HL			; SAVE HL
	;LD	HL,0			; MAX TRIES
ESP_WTNBSY1:
	LD	C,(IY+ESP_CFG_ST)	; ESP STATUS PORT
	IN	A,(C)			; GET STATUS
	AND	(IY+ESP_CFG_BSYMSK)	; IS ESP BUSY?
	RET	Z			; RETURN IF NOT BUSY
	;JR	Z,ESP_WTNBSY_Z		; RETURN IF NOT BUSY
	DJNZ	ESP_WTNBSY1		; ELSE LOOP
	;DEC	HL			; DEC LOOP COUNTER
	;LD	A,H			; CHECK FOR
	;OR	L			; ... TIMEOUT
	;JR	NZ,ESP_WTNBSY1		; LOOP AS NEEDED
	;CALL	PC_ASTERISK		; *DEBUG*
	OR	$FF			; SIGNAL TIMEOUT
;ESP_WTNBSY_Z:
	;POP	HL			; RECOVER HL
	RET				; AND RETURN
;
; WAIT FOR ESP TO BE BUSY
;
ESP_WTBSY:
	LD	B,3			; MAX TRIES
ESP_WTBSY1:
	LD	C,(IY+ESP_CFG_ST)	; ESP STATUS PORT
	IN	A,(C)			; GET STATUS
	XOR	$FF			; INVERT SO 0=BUSY
	AND	(IY+ESP_CFG_BSYMSK)	; IS ESP BUSY?
	RET	Z			; RETURN IF BUSY
	DJNZ	ESP_WTBSY1		; ELSE LOOP
	OR	$FF			; SIGNAL TIMEOUT
	RET				; AND RETURN
;;;;
;;;; WAIT FOR ESP TO BE READY TO OUTPUT
;;;;
;;;ESP_WTRDY:
;;;	PUSH	HL			; SAVE HL
;;;	LD	HL,0			; MAX TRIES
;;;ESP_WTRDY1:
;;;	LD	C,(IY+ESP_CFG_ST)	; ESP STATUS PORT
;;;	IN	A,(C)			; GET STATUS
;;;	XOR	$FF			; INVERT SO 0=READY
;;;	AND	(IY+ESP_CFG_RDYMSK)	; IS ESP READY TO OUTPUT
;;;	JR	Z,ESP_WTRDY_Z		; RETURN IF READY
;;;	DEC	HL			; DEC LOOP COUNTER
;;;	LD	A,H			; CHECK FOR
;;;	OR	L			; ... TIMEOUT
;;;	JR	NZ,ESP_WTRDY1		; LOOP AS NEEDED
;;;	CALL	PC_PERIOD		; *DEBUG*
;;;	OR	$FF			; SIGNAL TIMEOUT
;;;ESP_WTRDY_Z:
;;;	POP	HL			; RECOVER HL
;;;	RET				; AND RETURN
;
; WAIT FOR ESP TO BE READY TO OUTPUT
;
ESP_WTRDY:
	LD	B,0			; MAX TRIES
ESP_WTRDY1:
	LD	C,(IY+ESP_CFG_ST)	; ESP STATUS PORT
	IN	A,(C)			; GET STATUS
	XOR	$FF			; INVERT SO 0=READY
	AND	(IY+ESP_CFG_RDYMSK)	; IS ESP READY TO OUTPUT
	RET	Z			; RETURN IF READY
	DJNZ	ESP_WTRDY1		; ELSE LOOP
	;CALL	PC_PERIOD		; *DEBUG*
	CALL 	ESP_SYNC 		; SOMETHING WENT WRONG, ENSURE SYNC (Added DDW)
	OR	$FF			; SIGNAL TIMEOUT
	RET				; AND RETURN
;
;
;
ESP_PRES		.DB	0	; MODULE PRESENCE BITS
;
ESP_STR_NOHW		.TEXT	"NOT PRESENT$"
ESP_STR_UPGRADE		.TEXT	"!!!UPGRADE REQUIRED!!!$"
;
;==================================================================================================
; ESP32 CONSOLE DRIVER
;==================================================================================================
;
;
ESPCON_ROWS	.EQU	25		; VGA DISPLAY ROWS
ESPCON_COLS	.EQU	80		; VGA DISPLAY COLS
;
	DEVECHO	"ESPCON: ENABLED\n"
;
;
;
ESPCON_INIT:
	LD	A,(ESPCON_DEVCNT)	; GET ESPCON PHYSICAL DEVICE COUNT
	LD	(IY+ESP_CFG_DEV),A	; SAVE PHYSICAL UNIT NUMBER
	INC	A			; UPDATE COUNT
	LD	(ESPCON_DEVCNT),A	; SAVE COUNT
;
; ADD OURSELVES TO CIO DISPATCH TABLE
;
	LD	BC,ESPCON_FNTBL		; BC := FUNCTION TABLE ADDRESS
	PUSH	IY			; COPY CONFIG ENTRY PTR
	POP	DE			; ... TO DE
	CALL	CIO_ADDENT		; ADD ENTRY, A := UNIT ASSIGNED
	;;;LD	(HCB + HCB_CRTDEV),A	; SET OURSELVES AS THE CRT DEVICE
	CALL	CIO_SETCRT		; SET OURSELVES AS THE CRT DEVICE
;
; ANNOUNCE OURSLEVES
;
	CALL	NEWLINE			; FORMATTING
	PRTS("ESPCON$")			; NAME
	LD	A,(IY+ESP_CFG_DEV)	; GET PHYSICAL UNIT NUMBER
	CALL	PRTDECB			; UNIT STILL IN A FROM ABOVE
	CALL	PC_COLON		; FORMATTING
;
	; DISPLAY CONSOLE DIMENSIONS
	CALL	PC_SPACE
	LD	A,ESPCON_COLS
	CALL	PRTDECB
	LD	A,'X'
	CALL	COUT
	LD	A,ESPCON_ROWS
	CALL	PRTDECB
	CALL	PRTSTRD
	.TEXT	" TEXT (ANSI)$"
;
	XOR	A			; SIGNAL SUCCESS
	RET
;
; DRIVER FUNCTION TABLE
;
ESPCON_FNTBL:
	.DW	ESPCON_IN
	.DW	ESPCON_OUT
	.DW	ESPCON_IST
	.DW	ESPCON_OST
	.DW	ESPCON_INITDEV
	.DW	ESPCON_QUERY
	.DW	ESPCON_DEVICE
#IF (($ - ESPCON_FNTBL) != (CIO_FNCNT * 2))
	.ECHO	"*** INVALID ESPCON FUNCTION TABLE ***\n"
#ENDIF
;
;
;
ESPCON_IN:
	CALL	ESPCON_IST
	JR	Z,ESPCON_IN
	LD	A,ESP_0_CMD_KIN		; KBD INPUT
	CALL	ESP_OUT			; SEND CMD OPCODE
	CALL	ESP_INWAIT		; GET KEY
	LD	E,A			; PUT IN E
	XOR	A			; SIGNAL SUCCES
	RET				; AND DONE
;
;
;
ESPCON_IST:
	CALL	ESP_CLR			; CLEAR ANY PENDING DATA (Added DDW)
	LD	A,ESP_0_CMD_KST		; KBD BUF STATUS
	CALL	ESP_OUT			; SEND CMD OPCODE
	CALL	ESP_INWAIT		; GET BUF SIZE
	OR	A			; SET FLAGS
	RET	Z			; AND DONE
	OR	A
	RET
;
;
;
ESPCON_OUT:
	PUSH	DE
	LD	A,ESP_0_CMD_COUT	; CHAR OUT OPCODE
	CALL	ESP_OUT
	POP	DE
	LD	A,E
	CALL	ESP_OUT			; SEND CHAR VALUE
	XOR	A			; SIGNAL SUCCESS
	RET
;
;
;
ESPCON_OST:
	XOR	A			; ZERO ACCUM
	INC	A			; ACCUM := 1 TO SIGNAL 1 BUFFER POSITION
	RET				; RETURN
;
;
;
ESPCON_INITDEV:
	CALL 	ESP_CLR
	CALL 	ESP_SYNC
;	SYSCHKERR(ERR_NOTIMPL)  Removed DDW
	RET
;
;
;
ESPCON_QUERY:
	LD	DE,0
	LD	HL,0
	XOR	A
	RET
;
;
;
ESPCON_DEVICE:
	LD	D,CIODEV_ESPCON		; D := DEVICE TYPE
	LD	E,(IY+ESP_CFG_DEV)	; E := DEVICE NUM
	LD	C,$BF			; C := DEVICE TYPE, 0xBF IS PROP TERM
	LD	H,0			; H := 0, DRIVER HAS NO MODES
	LD	L,(IY+ESP_CFG_IO)	; L := BASE I/O ADDRESS
	XOR	A			; SIGNAL SUCCESS
	RET
;
; ESPCON CONFIGURATION
;
ESPCON_CFG:
ESPCON0_CFG:
	.DB	0				; DEVICE NUMBER (UPDATED DURING INIT)
	.DB	ESP_0_IO			; ESP DATA PORT
	.DB	ESP_STAT			; ESP STATUS PORT
	.DB	ESP_0_RDY			; ESP READY BIT MASK
	.DB	ESP_0_BUSY			; ESP BUSY BIT MASK
;
;
;
ESPCON_DEVCNT	.DB	0			; ESPCON DEVICES COUNT
;
;==================================================================================================
; ESP32 SERIAL DRIVER
;==================================================================================================
;
ESPSER_LINECFG		.EQU	SER_115200_8N1
;
ESPSER_CFG_LINE		.EQU	5
;
;
;
ESPSER_INIT:
	LD	A,(ESPSER_DEVCNT)	; GET ESPSER PHYSICAL DEVICE COUNT
	LD	(IY+ESP_CFG_DEV),A	; SAVE PHYSICAL UNIT NUMBER
	INC	A			; UPDATE COUNT
	LD	(ESPSER_DEVCNT),A	; SAVE COUNT
;
; ADD OURSELVES TO CIO DISPATCH TABLE
;
	LD	BC,ESPSER_FNTBL		; BC := FUNCTION TABLE ADDRESS
	PUSH	IY			; COPY CONFIG ENTRY PTR
	POP	DE			; ... TO DE
	CALL	CIO_ADDENT		; ADD ENTRY, A := UNIT ASSIGNED
;
; ANNOUNCE OURSLEVES
;
	CALL	NEWLINE			; FORMATTING
	PRTS("ESPSER$")			; NAME
	LD	A,(IY+ESP_CFG_DEV)	; GET PHYSICAL UNIT NUMBER
	CALL	PRTDECB			; UNIT STILL IN A FROM ABOVE
	CALL	PC_COLON		; FORMATTING
;
	PRTS(" MODE=$")				; FORMATTING
	LD	E,(IY+ESPSER_CFG_LINE+0)	; FIRST CONFIG BYTE TO E
	LD	D,(IY+ESPSER_CFG_LINE+1)	; SECOND CONFIG BYTE TO D
	CALL	PS_PRTSC0			; PRINT CONFIG
;
; TODO: PRINT SERIAL CONFIG
;
	XOR	A			; SIGNAL SUCCESS
	RET
;
; DRIVER FUNCTION TABLE
;
ESPSER_FNTBL:
	.DW	ESPSER_IN
	.DW	ESPSER_OUT
	.DW	ESPSER_IST
	.DW	ESPSER_OST
	.DW	ESPSER_INITDEV
	.DW	ESPSER_QUERY
	.DW	ESPSER_DEVICE
#IF (($ - ESPSER_FNTBL) != (CIO_FNCNT * 2))
	.ECHO	"*** INVALID ESPSER FUNCTION TABLE ***\n"
#ENDIF
;
;
;
ESPSER_IN:
	CALL	ESPSER_IST
	JR	Z,ESPSER_IN
	LD	A,ESP_CMD_SIN		; SERIAL INPUT
	CALL	ESP_OUT			; SEND CMD OPCODE
	CALL	ESP_INWAIT		; GET KEY
	LD	E,A			; PUT IN E
	XOR	A			; SIGNAL SUCCES
	RET				; AND DONE
;
;
;
ESPSER_IST:
	CALL	ESP_CLR			; CLEAR ANY PENDING DATA (Added DDW)
	LD	A,ESP_CMD_SST		; SERIAL STATUS
	CALL	ESP_OUT			; SEND CMD OPCODE
	CALL	ESP_INWAIT		; GET BUF SIZE
	OR	A			; SET FLAGS
	RET	Z			; AND DONE
	OR	A
	RET
;
;
;
ESPSER_OUT:
	PUSH	DE
	LD	A,ESP_CMD_SOUT		; CHAR OUT OPCODE
	CALL	ESP_OUT
	POP	DE
	LD	A,E
	CALL	ESP_OUT			; SEND CHAR VALUE
	XOR	A			; SIGNAL SUCCESS
	RET
;
;
;
ESPSER_OST:
	XOR	A			; ZERO ACCUM
	INC	A			; ACCUM := 1 TO SIGNAL 1 BUFFER POSITION
	RET				; RETURN
;
;
;
ESPSER_INITDEV:
	CALL 	ESP_CLR
	CALL 	ESP_SYNC
	PUSH	DE			; SAVE INCOMING CONFIG WORD
;
	; XLATE NEW LINE MODE INTO C
	LD	A,E
	AND	%00111111		; ISOLATE MODE BITS
	LD	C,0			; 8N1 = 0
	CP	SER_DATA8 | SER_PARNONE | SER_STOP1
	JR	Z,ESPSER_INITDEV1	; IF MATCH, DO IT
	INC	C			; 8E1 = 1
	CP	SER_DATA8 | SER_PAREVEN | SER_STOP1
	JR	Z,ESPSER_INITDEV1	; IF MATCH, DO IT
	INC	C			; 8O1 = 2
	CP	SER_DATA8 | SER_PARODD | SER_STOP1
	JR	Z,ESPSER_INITDEV1	; IF MATCH, DO IT
	INC	C			; 7N1 = 3
	CP	SER_DATA7 | SER_PARNONE | SER_STOP1
	JR	Z,ESPSER_INITDEV1	; IF MATCH, DO IT
	INC	C			; 7E1 = 4
	CP	SER_DATA7 | SER_PAREVEN | SER_STOP1
	JR	Z,ESPSER_INITDEV1	; IF MATCH, DO IT
	INC	C			; 7O1 = 5
	CP	SER_DATA7 | SER_PARODD | SER_STOP1
	JR	Z,ESPSER_INITDEV1	; IF MATCH, DO IT
	JR	NZ,ESPSER_INITDEV_ERR	; ELSE FAIL
;
ESPSER_INITDEV1:
	; DECODE NEW BAUD RATE INTO DE:HL
	LD	H,0
	LD	A,D
	AND	%00011111
	LD	L,A
	LD	DE,75
	PUSH	BC			; SAVE NEW LINE MODE
	CALL	DECODE			; DE:HL IS DECODED BAUD RATE
	POP	BC			; RESTORE NEW LINE MODE
	JR	NZ,ESPSER_INITDEV_ERR	; IF ERROR, FAIL
;
	; PROGRAM NEW LINE MODE
	PUSH	BC
	LD	A,ESP_CMD_SMODE
	CALL	ESP_OUT
	POP	BC
	LD	A,C
	;CALL	PRTHEXBYTE
	;CALL	LDELAY
	CALL	ESP_OUT
;
	; PROGRAM NEW BAUD RATE
	LD	A,ESP_CMD_SBAUD
	CALL	ESP_OUT
	LD	A,L
	CALL	ESP_OUT
	LD	A,H
	CALL	ESP_OUT
	LD	A,E
	CALL	ESP_OUT
	LD	A,D
	CALL	ESP_OUT
;
	; SAVE NEW LINE CONFIG WORD
	POP	DE				; RESTORE CONFIG WORD
	LD	(IY+ESPSER_CFG_LINE+0),E	; FIRST CONFIG BYTE
	LD	(IY+ESPSER_CFG_LINE+1),D	; SECOND CONFIG BYTE
;
	XOR	A
	RET
;
ESPSER_INITDEV_ERR:
	POP	DE			; THROW AWAY CONFIG WORD ON STACK
	OR	$FF
	RET
;
;
;
ESPSER_QUERY:
	LD	E,(IY+ESPSER_CFG_LINE+0)	; FIRST CONFIG BYTE TO E
	LD	D,(IY+ESPSER_CFG_LINE+1)	; SECOND CONFIG BYTE TO D
	LD	HL,0
	XOR	A
	RET
;
;
;
ESPSER_DEVICE:
	LD	D,CIODEV_ESPSER		; D := DEVICE TYPE
	LD	E,(IY+ESP_CFG_DEV)	; E := DEVICE NUM
	LD	C,$00			; C := DEVICE TYPE, 0x00 IS RS-232
	LD	H,0			; H := 0, DRIVER HAS NO MODES
	LD	L,(IY+ESP_CFG_IO)	; L := BASE I/O ADDRESS
	XOR	A			; SIGNAL SUCCESS
	RET
;
; ESPSER CONFIGURATION
;
ESPSER_CFG:
ESPSER0_CFG:
	.DB	0				; DEVICE NUMBER (UPDATED DURING INIT)
	.DB	ESP_0_IO			; ESP DATA PORT
	.DB	ESP_STAT			; ESP STATUS PORT
	.DB	ESP_0_RDY			; ESP READY BIT MASK
	.DB	ESP_0_BUSY			; ESP BUSY BIT MASK
	.DW	ESPSER_LINECFG			; LINE CONFIGURATION
;
	DEVECHO	"ESPSER: DEVICE=0\n"
;
ESPSER1_CFG:
	.DB	0				; DEVICE NUMBER (UPDATED DURING INIT)
	.DB	ESP_1_IO			; ESP DATA PORT
	.DB	ESP_STAT			; ESP STATUS PORT
	.DB	ESP_1_RDY			; ESP READY BIT MASK
	.DB	ESP_1_BUSY			; ESP BUSY BIT MASK
	.DW	ESPSER_LINECFG			; LINE CONFIGURATION
;
	DEVECHO	"ESPSER: DEVICE=1\n"
;
;
;
ESPSER_DEVCNT	.DB	0			; ESPSER DEVICES COUNT
