;
;==================================================================================================
; CENTRONICS (LPT) INTERFACE DRIVER
;==================================================================================================
;
; CENTRONICS-STYLE PARALLEL PRINTER DRIVER.
;
; IMPLEMENTED AS A ROMWBW CHARACTER DEVICE.  CURRENTLY HANDLES OUPUT
; ONLY.
;
;==================================================================================================
;
;  IBM PC STANDARD PARALLEL PORT (SPP):
;  - NHYODYNE PRINT MODULE
;
;  PORT 0 (OUTPUT):
;
;	D7	D6	D5	D4	D3	D2	D1	D0
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;     | PD7   | PD6   | PD5   | PD4   | PD3   | PD2   | PD1   | PD0   |
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;
;  PORT 1 (INPUT):
;
;	D7	D6	D5	D4	D3	D2	D1	D0
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;     | /BUSY | /ACK  | POUT  | SEL   | /ERR  | 0     | 0     | 0     |
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;
;  PORT 2 (OUTPUT):
;
;	D7	D6	D5	D4	D3	D2	D1	D0
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;     | STAT1 | STAT0 | ENBL  | PINT  | SEL   | RES   | LF    | STB   |
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;
;==================================================================================================
;
;  MG014 STYLE INTERFACE:
;  - RCBUS MG014 MODULE
;
;  PORT 0 (OUTPUT):
;
;	D7	D6	D5	D4	D3	D2	D1	D0
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;     | PD7   | PD6   | PD5   | PD4   | PD3   | PD2   | PD1   | PD0   |
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;
;  PORT 1 (INPUT):
;
;	D7	D6	D5	D4	D3	D2	D1	D0
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;     |       |       |       | /ERR  | SEL   | POUT  | BUSY  | /ACK  |
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;
;  PORT 2 (OUTPUT):
;
;	D7	D6	D5	D4	D3	D2	D1	D0
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;     | LED   |	      |	      |	      | /SEL  | /RES  | /LF   | /STB  |
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;
;==================================================================================================
;
;  S100 STYLE INTERFACE:
;  - S100 FPGA Z80
;
;  BASE I/O PORT (OUTPUT):
;
;	D7	D6	D5	D4	D3	D2	D1	D0
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;     | PD7   | PD6   | PD5   | PD4   | PD3   | PD2   | PD1   | PD0   |
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;
;  STATUS PORT (INPUT, BASE I/O - 1):
;
;	D7	D6	D5	D4	D3	D2	D1	D0
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;     |       |       |       |       |       |       | BUSY  | /ACK  |
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;
;  CONTROL PORT (OUTPUT, BASE I/O - 1):
;
;	D7	D6	D5	D4	D3	D2	D1	D0
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;     |       |       |       |       |       |       |       | /STB  |
;     +-------+-------+-------+-------+-------+-------+-------+-------+
;
;==================================================================================================
;
LPT_INIT:
	LD	B,LPT_CFGCNT		; LOOP CONTROL
	XOR	A			; ZERO TO ACCUM
	LD	(LPT_DEV),A		; CURRENT DEVICE NUMBER
	LD	IY,LPT_CFG		; POINT TO START OF CFG TABLE
LPT_INIT0:
	PUSH	BC			; SAVE LOOP CONTROL
	CALL	LPT_INITUNIT		; HAND OFF TO UNIT INIT CODE
	POP	BC			; RESTORE LOOP CONTROL
;
	LD	A,(IY+1)		; GET THE LPT TYPE DETECTED
	OR	A			; SET FLAGS
	JR	Z,LPT_INIT2		; SKIP IT IF NOTHING FOUND
;
	PUSH	BC			; SAVE LOOP CONTROL
	PUSH	IY			; CFG ENTRY ADDRESS
	POP	DE			; ... TO DE
	LD	BC,LPT_FNTBL		; BC := FUNCTION TABLE ADDRESS
	CALL	NZ,CIO_ADDENT		; ADD ENTRY IF LPT FOUND, BC:DE
	CALL	LPT_PRTCFG		; PRINT IF NOT ZERO
	POP	BC			; RESTORE LOOP CONTROL
;
LPT_INIT2:
	LD	DE,LPT_CFGSIZ		; SIZE OF CFG ENTRY
	ADD	IY,DE			; BUMP IY TO NEXT ENTRY
	DJNZ	LPT_INIT0		; LOOP UNTIL DONE
;
LPT_INIT3:
	XOR	A			; SIGNAL SUCCESS
	RET				; AND RETURN
;
; LPT INITIALIZATION ROUTINE
;
LPT_INITUNIT:
	CALL	LPT_DETECT		; DETERMINE LPT TYPE
	LD	(IY+1),A		; SAVE IN CONFIG TABLE
	OR	A			; SET FLAGS
	RET	Z			; ABORT IF NOTHING THERE
;
	; UPDATE WORKING LPT DEVICE NUM
	LD	HL,LPT_DEV		; POINT TO CURRENT DEVICE NUM
	LD	A,(HL)			; PUT IN ACCUM
	INC	(HL)			; INCREMENT IT (FOR NEXT LOOP)
	LD	(IY),A			; UPDATE UNIT NUM
;
	; SET DEFAULT CONFIG
	LD	DE,-1			; LEAVE CONFIG ALONE
	; CALL INITDEV TO IMPLEMENT CONFIG, BUT NOTE THAT WE CALL
	; THE INITDEV ENTRY POINT THAT DOES NOT ENABLE/DISABLE INTS!
	JP	LPT_INITDEVX		; IMPLEMENT IT AND RETURN
;
; DRIVER FUNCTION TABLE
;
LPT_FNTBL:
	.DW	LPT_IN
	.DW	LPT_OUT
	.DW	LPT_IST
	.DW	LPT_OST
	.DW	LPT_INITDEV
	.DW	LPT_QUERY
	.DW	LPT_DEVICE
#IF (($ - LPT_FNTBL) != (CIO_FNCNT * 2))
	.ECHO	"*** INVALID LPT FUNCTION TABLE ***\n"
	!!!	; FORCE AN ASSEMBLY ERROR
#ENDIF
;
; BYTE INTPUT
;
LPT_IN:
	; INPUT NOT SUPPORTED - RETURN NULL BYTE
	LD	E,0			; NULL BYTE
	XOR	A			; SIGNAL SUCCESS
	RET
;
; BYTE OUTPUT
;
LPT_OUT:
	CALL	LPT_OST			; READY TO SEND?
	JR	Z,LPT_OUT		; LOOP IF NOT
	LD	C,(IY+3)		; PORT 0 (DATA)
	EZ80_IO
	OUT	(C),E			; OUTPUT DATA TO PORT
#IF (LPTMODE == LPTMODE_SPP)
	LD	A,%00001101		; SELECT & STROBE, LEDS OFF
#ENDIF
#IF (LPTMODE == LPTMODE_MG014)
	LD	A,%00000100		; SELECT & STROBE, LED OFF
#ENDIF
#IF (LPTMODE == LPTMODE_S100)
	LD	A,%00000000		; STROBE
#ENDIF
#IF ((LPTMODE == LPTMODE_SPP) | (LPTMODE == LPTMODE_MG014))
	INC	C			; PUT CONTROL PORT IN C
	INC	C
#ENDIF
#IF (LPTMODE == LPTMODE_S100)
	DEC	C			; PUT CONTROL PORT IN C
#ENDIF
	EZ80_IO
	OUT	(C),A			; OUTPUT DATA TO PORT
	CALL	DELAY
#IF (LPTMODE == LPTMODE_SPP)
	LD	A,%00001100		; SELECT, LEDS OFF
#ENDIF
#IF (LPTMODE == LPTMODE_MG014)
	LD	A,%00000101		; SELECT, LED OFF
#ENDIF
#IF (LPTMODE == LPTMODE_S100)
	LD	A,%11111111		; STROBE
#ENDIF
	EZ80_IO
	OUT	(C),A			; OUTPUT DATA TO PORT
	CALL	DELAY
	XOR	A			; SIGNAL SUCCESS
	RET
;
; INPUT STATUS
;
LPT_IST:
	; INPUT NOT SUPPORTED - RETURN NOT READY
	XOR	A			; ZERO BYTES AVAILABLE
	RET				; DONE
;
; OUTPUT STATUS
;
LPT_OST:
	LD	C,(IY+3)		; BASE PORT
#IF ((LPTMODE == LPTMODE_SPP) | (LPTMODE == LPTMODE_MG014))
	INC	C			; SELECT STATUS PORT
#ENDIF
#IF (LPTMODE == LPTMODE_S100)
	DEC	C			; SELECT STATUS PORT
#ENDIF
	EZ80_IO
	IN	A,(C)			; GET STATUS INFO
#IF (LPTMODE == LPTMODE_SPP)
	AND	%10000000		; ISOLATE /BUSY
#ENDIF
#IF (LPTMODE == LPTMODE_MG014)
	AND	%00000010		; ISOLATE BUSY
	XOR	%00000010		; INVERT TO READY
#ENDIF
	RET				; DONE
;
; INITIALIZE DEVICE
;
LPT_INITDEV:
	HB_DI				; AVOID CONFLICTS
	CALL	LPT_INITDEVX		; DO THE REAL WORK
	HB_EI				; INTS BACK ON
	RET				; DONE
;
; THIS ENTRY POINT BYPASSES DISABLING/ENABLING INTS WHICH IS REQUIRED BY
; PREINIT ABOVE.  PREINIT IS NOT ALLOWED TO ENABLE INTS!
;
LPT_INITDEVX:
;
#IF (LPTMODE == LPTMODE_SPP)
;
	LD	C,(IY+3)		; PORT 0 (DATA)
	XOR	A			; CLEAR ACCUM
	EZ80_IO
	OUT	(C),A			; SEND IT
	INC	C			; BUMP TO
	INC	C			; ... PORT 2
	LD	A,%00001000		; SELECT AND ASSERT RESET, LEDS OFF
	EZ80_IO
	OUT	(C),A			; SEND IT
	CALL	LDELAY			; HALF SECOND DELAY
	LD	A,%00001100		; SELECT AND DEASSERT RESET, LEDS OFF
	EZ80_IO
	OUT	(C),A			; SEND IT
	XOR	A			; SIGNAL SUCCESS
	RET				; RETURN
;
#ENDIF
;
#IF (LPTMODE == LPTMODE_MG014)
	LD	A,(IY+3)		; BASE PORT
	ADD	A,3			; BUMP TO CONTROL PORT
	LD	C,A			; MOVE TO C FOR I/O
	LD	A,$82			; CONFIG A OUT, B IN, C OUT
	EZ80_IO
	OUT	(C),A			; DO IT
	DEC	C			; OUTPUT PORT
	LD	A,$81			; STROBE OFF, SELECT ON, RES ON, LED ON
	EZ80_IO
	OUT	(C),A			; SEND IT
	CALL	LDELAY			; HALF SECOND DELAY
	LD	A,$05			; STROBE OFF, SELECT ON, RES OFF, LED OFF
	EZ80_IO
	OUT	(C),A			; SEND IT
	XOR	A			; SIGNAL SUCCESS
	RET				; RETURN
#ENDIF
;
#IF (LPTMODE == LPTMODE_S100)
	LD	C,(IY+3)		; BASE PORT
	DEC	C			; DEC TO CONTROL PORT
	LD	A,$FF			; INIT VALUE
	EZ80_IO
	OUT	(C),A			; DO IT
	RET				; RETURN
#ENDIF
;
;
;
LPT_QUERY:
	LD	E,(IY+4)		; FIRST CONFIG BYTE TO E
	LD	D,(IY+5)		; SECOND CONFIG BYTE TO D
	XOR	A			; SIGNAL SUCCESS
	RET				; DONE
;
;
;
LPT_DEVICE:
	LD	D,CIODEV_LPT		; D := DEVICE TYPE
	LD	E,(IY)			; E := PHYSICAL UNIT
	LD	C,$40			; C := DEVICE TYPE, 0x40 IS PIO
	LD	H,(IY+1)		; H := MODE
	LD	L,(IY+3)		; L := BASE I/O ADDRESS
	XOR	A			; SIGNAL SUCCESS
	RET
;
; LPT DETECTION ROUTINE
;
#IF (LPTMODE == LPTMODE_NONE)
;
LPT_DETECT:
	LD	A,LPTMODE_NONE		; NOTHING TO DETECT
	RET
;
#ENDIF
;
#IF (LPTMODE == LPTMODE_SPP)
;
LPT_DETECT:
	LD	C,(IY+3)		; BASE PORT ADDRESS
	CALL	LPT_DETECT2		; CHECK IT
	JR	Z,LPT_DETECT1		; FOUND IT, RECORD IT
	LD	A,LPTMODE_NONE		; NOTHING FOUND
	RET				; DONE
;
LPT_DETECT1:
	; LPT FOUND, RECORD IT
	LD	A,LPTMODE_SPP		; RETURN CHIP TYPE
	RET				; DONE
;
LPT_DETECT2:
	; LOOK FOR LPT AT BASE PORT ADDRESS IN C
	INC	C			; PORT C FOR I/O
	INC	C			; ...
	XOR	A			; DEFAULT VALUE (TRI-STATE OFF)
	EZ80_IO
	OUT	(C),A			; SEND IT
;
	;IN	A,(C)			; READ IT
	;AND	%11000000		; ISOLATE STATUS BITS
	;CP	%00000000		; CORRECT VALUE?
	;RET	NZ			; IF NOT, RETURN
	;LD	A,%11000000		; STATUS BITS ON (LEDS OFF)
	;OUT	(C),A			; SEND IT
	;IN	A,(C)			; READ IT
	;AND	%11000000		; ISOLATE STATUS BITS
	;CP	%11000000		; CORRECT VALUE?
;
	DEC	C			; BACK TO BASE PORT
	DEC	C			; ...
	LD	A,$A5			; TEST VALUE
	EZ80_IO
	OUT	(C),A			; SEND IT
	EZ80_IO
	IN	A,(C)			; READ IT BACK
	CP	$A5			; CORRECT?
	RET				; RETURN (ZF SET CORRECTLY)
;
#ENDIF
;
#IF (LPTMODE == LPTMODE_MG014)
LPT_DETECT:
;
	; TEST FOR PPI EXISTENCE
	; WE SETUP THE PPI TO WRITE, THEN WRITE A VALUE OF $A5
	; TO PORT A (DATALO), THEN READ IT BACK.  IF THE PPI IS THERE
	; THEN THE BUS HOLD CIRCUITRY WILL READ BACK THE $A5. SINCE
	; WE ARE IN WRITE MODE, AN IDE CONTROLLER WILL NOT BE ABLE TO
	; INTERFERE WITH THE VALUE BEING READ.
;
	LD	A,(IY+3)		; BASE IO ADDRESS
	ADD	A,3			; BUMP TO CONTROL PORT
	LD	C,A			; PUT IN C
	LD	A,$80			; SET PORT A TO WRITE
	EZ80_IO
	OUT	(C),A			; WRITE IT
;
	LD	C,(IY+3)		; PPI PORT A
	LD	A,$A5			; TEST VALUE
	EZ80_IO
	OUT	(C),A			; PUSH VALUE TO PORT
	EZ80_IO
	IN	A,(C)			; GET PORT VALUE
  #IF (LPTTRACE >= 3)
	CALL	PC_SPACE
	CALL	PRTHEXBYTE
  #ENDIF
	CP	$A5			; CHECK FOR TEST VALUE
	JR	Z,LPT_DETECT1		; FOUND IT
	LD	A,LPTMODE_NONE		; NOT FOUND
	RET
;
LPT_DETECT1:
	; LPT FOUND, RECORD IT
	LD	A,LPTMODE_MG014		; RETURN CHIP TYPE
	RET				; DONE
#ENDIF
;
#IF (LPTMODE == LPTMODE_S100)
LPT_DETECT:
	; PORT ALWAYS EXISTS ON FPGA
	LD	A,LPTMODE_S100		; RETURN CHIP TYPE
	RET				; DONE
#ENDIF
;
;
;
LPT_PRTCFG:
	; ANNOUNCE PORT
	CALL	NEWLINE			; FORMATTING
	PRTS("LPT$")			; FORMATTING
	LD	A,(IY)			; DEVICE NUM
	CALL	PRTDECB			; PRINT DEVICE NUM
	PRTS(": IO=0x$")		; FORMATTING
	LD	A,(IY+3)		; GET BASE PORT
	CALL	PRTHEXBYTE		; PRINT BASE PORT

	; PRINT THE LPT TYPE
	PRTS(" MODE=$")			; FORMATTING
	LD	A,(IY+1)		; GET LPT TYPE BYTE
	RLCA				; MAKE IT A WORD OFFSET
	LD	HL,LPT_TYPE_MAP		; POINT HL TO TYPE MAP TABLE
	CALL	ADDHLA			; HL := ENTRY
	LD	E,(HL)			; DEREFERENCE
	INC	HL			; ...
	LD	D,(HL)			; ... TO GET STRING POINTER
	CALL	WRITESTR		; PRINT IT
;
	; ALL DONE IF NO LPT WAS DETECTED
	LD	A,(IY+1)		; GET LPT TYPE BYTE
	OR	A			; SET FLAGS
	RET	Z			; IF ZERO, NOT PRESENT
;
	; *** ADD MORE DEVICE INFO??? ***
;
	XOR	A
	RET
;
;
;
LPT_TYPE_MAP:
		.DW	LPT_STR_NONE
		.DW	LPT_STR_SPP
		.DW	LPT_STR_MG014
		.DW	LPT_STR_S100
;
LPT_STR_NONE	.DB	"<NOT PRESENT>$"
LPT_STR_SPP	.DB	"SPP$"
LPT_STR_MG014	.DB	"MG014$"
LPT_STR_S100	.DB	"S100$"
;
; WORKING VARIABLES
;
LPT_DEV		.DB	0		; DEVICE NUM USED DURING INIT
;
; LPT DEVICE CONFIGURATION TABLE
;
LPT_CFG:
;
LPT0_CFG:
	; LPT MODULE A CONFIG
	.DB	0			; DEVICE NUMBER (SET DURING INIT)
	.DB	0			; LPT TYPE (SET DURING INIT)
	.DB	0			; MODULE ID
	.DB	LPT0BASE		; BASE PORT
	.DW	0			; LINE CONFIGURATION
;
	DEVECHO	"LPT: MODE="
  #IF (LPTMODE == LPTMODE_SPP)
	DEVECHO	"SPP"
  #ENDIF
  #IF (LPTMODE == LPTMODE_MG014)
	DEVECHO	"MG014"
  #ENDIF
  #IF (LPTMODE == LPTMODE_S100)
	DEVECHO	"S100"
  #ENDIF
	DEVECHO	", IO="
	DEVECHO	LPT0BASE
	DEVECHO	"\n"
;
LPT_CFGSIZ	.EQU	$ - LPT_CFG	; SIZE OF ONE CFG TABLE ENTRY
;
#IF (LPTCNT >= 2)
;
LPT1_CFG:
	; LPT MODULE B CONFIG
	.DB	0			; DEVICE NUMBER (SET DURING INIT)
	.DB	0			; LPT TYPE (SET DURING INIT)
	.DB	1			; MODULE ID
	.DB	LPT1BASE		; BASE PORT
	.DW	0			; LINE CONFIGURATION
;
	DEVECHO	"LPT: MODE="
  #IF (LPTMODE == LPTMODE_SPP)
	DEVECHO	"SPP"
  #ENDIF
  #IF (LPTMODE == LPTMODE_MG014)
	DEVECHO	"MG014"
  #ENDIF
  #IF (LPTMODE == LPTMODE_S100)
	DEVECHO	"S100"
  #ENDIF
	DEVECHO	", IO="
	DEVECHO	LPT1BASE
	DEVECHO	"\n"
;
#ENDIF
;
LPT_CFGCNT	.EQU	($ - LPT_CFG) / LPT_CFGSIZ
