; PIO driver sets up the parallel port as a subtype of Serial/Char device.
;
;
; HBIOS initializes driver by:
;
; 1) Calling Pre-initialization
;
;    This involves setting up all the data structures describing the devices.
;    If possible, do a hardware test to verify it is available for adding to available devices.
;
; 2) Calling device initialization.
;
;    Hardware initialization.
;    Configure to initial state or to a new state.
;
; Implementation limitations:
;
; The fully functionality of the Z80 PIO can only be realized by using Z80 interrupt mode 2.
; Registers cannot be interrogated for interrupts status and the originating interrupt
; device cannot be determine.
;
; Full implementation of IM2 functionality for an ECB-ZP and ECB-4P board would require the 
; allocation of an interrupt handler for each chip channel. Thus, 12 interrupt handlers
; would be required to support this configuration. As the HBIOS only has an allocation of
; 16, a full implmentation is impractical.
;
; The compromise solution is to allow 4 interrupts for the PIO driver. All remaining PIO's
; are limited to Bit mode or blind read and write to the input/output ports.
;
; Zilog PIO reset state:
;
;	Both port mask registers are reset to inhibit All port data bits.
;	Port data bus lines are set to a high-impedance state and the Ready "handshake"
;	Mode 1 (output) is automatically selected.
;	The vector address registers are not reset.
;	Both port interrupt enable flip-flops are reset.
;	Both port output registers are reset.
;
; Register addressing example for ECB-ZP and ECB-4P assuming base address 90h and 88h respectively.
;
; PIO ----ZP---- ----4P----
;  0  DATA 0 90h DATA 0 B8h
;  0  DATA 1 91h DATA 1 B9h
;  0  CMD  0 92h CMD  0 BAh
;  0  CMD  1 93h CMD  1 BBh
;  1  DATA 0 94h DATA 0 BCh
;  1  DATA 1 95h DATA 1 BDh
;  1  CMD  0 96h CMD  0 BEh
;  1  CMD  1 97h CMD  1 BFh
;  2             DATA 0 C0h
;  2             DATA 1 C1h
;  2             CMD  0 C2h
;  2             CMD  1 C3h
;  3             DATA 0 C4h
;  3             DATA 1 C5h
;  3             CMD  0 C6h
;  3             CMD  1 C7h
;
PIODEBUG	.EQU	1
;
M_Output	.EQU	$00 << 6
M_Input		.EQU	$01 << 6
M_Bidir		.EQU	$02 << 6
M_BitCtrl	.EQU	$03 << 6
M_BitAllIn	.EQU	$FF
M_BitAllOut	.EQU	$00
;
PIO_NONE	.EQU	0
PIO_ZPIO	.EQU	1
PIO_8255	.EQU	2
PIO_PORT	.EQU	3

; SET MAXIMUM NUMBER OF INTERRUPTS AVAILABLE FOR ALL
; ENSURE INTERRUPTS ARE NOT TURNED ON IF IM2 IS NOT SET.

INT_ALLOC	.DB	0
INT_N		.EQU	00000000B
#IF	(INTMODE == 2)
INT_Y		.EQU	00000100B
INT_ALLOW	.EQU	4
#ELSE
INT_Y		.EQU	INT_N
INT_ALLOW	.EQU	0
#ENDIF
;
INT0		.EQU	00000000B
INT1		.EQU	00000001B
INT2		.EQU	00000010B
INT3		.EQU	00000011B

;
; SETUP THE DISPATCH TABLE ENTRIES
;
; PIO_CNT HOLDS THE NUMBER OF DEVICED CALCULATED FROM THE NUMBER OF DEFPIO MACROS
; PIO_CNT SHOULD INCREASE BY 2 FOR EVERY PIO CHIP ADDED.
;
; PIO_PREINIT WILL READ THROUGH ALL PIOCFG TABLES AND CONFIGURE EACH TABLE.
; IT WITH THEN CALL PIO_INITUNIT TO INITIALIZE EACH DEVICE TO ITS DEFAULT STATE
; 
; EXPECTS NOTHING ON ENTRY
;
PIO_PREINIT:
	CALL	NEWLINE		;D
	LD	B,PIO_CNT		; LOOP CONTROL
	LD	C,0			; PHYSICAL UNIT INDEX
	XOR	A			; ZERO TO ACCUM
;	LD	(PIO_DEV),A		; CURRENT DEVICE NUMBER
	LD	(INT_ALLOC),A		; START WITH NO INTERRUPTS ALLOCATED
PIO_PREINIT0:	
	PUSH	BC			; SAVE LOOP CONTROL
;	LD	A,C			; INITIALIZE THE UNIT

;	PUSH	AF		;D
;	LD	A,'u'		;D
;	CALL	COUT		;D
;	POP	AF		;D
;	CALL	PRTHEXBYTE	;D	UNIT
;	CALL	PC_SPACE	;D

;	RLCA				; MULTIPLY BY CFG TABLE ENTRY SIZE (32 BYTES)
;	RLCA				; ...
;	RLCA				; ... TO GET OFFSET INTO CFG TABLE
;	RLCA
;;	RLCA
;	LD	HL,PIO_CFG		; POINT TO START OF CFG TABLE
;	PUSH	AF
;	CALL	ADDHLA			; HL := ENTRY ADDRESS
;	POP	AF
;	CALL	ADDHLA			; HL := ENTRY ADDRESS
;	PUSH	HL			; SAVE IT
;	POP	IY			; ... TO IY

	CALL	IDXCFG			

	LD	(HL),C

	PUSH	AF		;D
	LD	A,'c'		;D
	CALL	COUT		;D
	POP	AF		;D
	PUSH	BC		;D
	PUSH	HL		;D
	POP	BC		;D
	CALL	PRTHEXWORD	;D	CONFIG TABLE
	CALL	PC_SPACE	;D
	POP	BC		;D

	LD	A,(IY+1)		; GET THE PIO TYPE DETECTED
	CP	PIO_PORT		; SET FLAGS

	PUSH	AF		;D
	LD	A,'t'		;D
	CALL	COUT		;D
	POP	AF		;D
	CALL	PRTHEXBYTE	;D	TYPE
	CALL	PC_SPACE	;D

;	JR	Z,BADINIT

;	PUSH	BC			; SAVE LOOP CONTROL
;	LD	BC,PIO_FNTBL		; BC := FUNCTION TABLE ADDRESS	
;	DEC	A
;	JR	Z,TYPFND		; SKIP IT IF NOTHING FOUND
;	LD	BC,PPI_FNTBL		; BC := FUNCTION TABLE ADDRESS
;	DEC	A
;	JR	Z,TYPFND		; ADD ENTRY IF PIO FOUND, BC:DE
;	LD	BC,PRT_FNTBL
;	DEC	A
;	JR	Z,TYPFND
;	POP	BC
;	JR	BADINIT

	PUSH	HL
	LD	DE,-1			; INITIALIZE THIS DEVICE WITH
	CALL	PIO_INITDEV		; DEFAULT VALUES
	POP	HL

;	JR	NZ,SKPINIT

	; AT THIS POINT WE KNOW WE
	; HAVE A VALID DEVICE SO ADD IT
	
	LD	A,8			; CALCULATE THE FUNCTION TABLE 
	CALL	ADDHLA			; POSITION WHICH FOLLOWS THE
	PUSH	HL			; CONFIGURATION TABLE OF EACH
	POP	BC			; DEVICE

TYPFND:	PUSH	AF		;D
	LD	A,'f'		;D
	CALL	COUT		;D
	POP	AF		;D
	PUSH	BC		;D
	CALL	PRTHEXWORD	;D	FUNCTION TABLE
	POP	BC		;D
	CALL	NEWLINE		;D

	PUSH	IY			; ADD ENTRY IF PIO FOUND, BC:DE
	POP	DE			; BC: DRIVER FUNCTION TABLE
	CALL	CIO_ADDENT		; DE: ADDRESS OF UNIT INSTANCE DATA

BADINIT:POP	BC			; RESTORE LOOP CONTROL

	INC	C			; NEXT PHYSICAL UNIT
SKPINIT:DJNZ	PIO_PREINIT0		; LOOP UNTIL DONE

	PUSH	AF		;D
	PRTS("INTS=$")		;D
	LD	A,(INT_ALLOC)	;D
	CALL	PRTHEXBYTE	;D
	POP	AF		;D
	PUSH	DE		;D
	LD	DE,CIO_TBL-3	;D
	CALL	DUMP_BUFFER	;D
	POP	DE		;D
	XOR	A			; SIGNAL SUCCESS
	RET				; AND RETURN
;
; INDEX INTO THE CONFIG TABLE
; ON ENTRY C = UNIT NUMBER
; ON EXIT IY = CONFIG DATA POINTER
; ON EXIT DE = CONFIG TABLE START
;
; EACH CONFIG TABLE IS 24 BYTES LONG
;
CFG_SIZ		.EQU	24
;
IDXCFG:	LD	A,C
	RLCA				; X 2
	RLCA				; X 4
	RLCA				; X 8
	LD	H,0
	LD	L,A			; HL = X 8
	PUSH	HL
	ADD	HL,HL			; HL = X 16
	POP	DE
	ADD	HL,DE			; HL = X 24
	LD	DE,PIO_CFG
	ADD	HL,DE
	PUSH	HL			; COPY CFG DATA PTR
	POP	IY			; ... TO IY
	RET
	
; PIO_INITDEV - INITIALIZE DEVICE
;
; IF DE = FFFF THEN THE SETUP PARAMETER WORD WILL BE READ FROM THE DEVICE CONFIGURATION  
; TABLE POINTED TO BY IY AND THE PIO PORT WILL BE PROGRAMMED BASED ON THAT CONFIGURATION. 
;
; OTHERWISE THE PIO PORT WILL BE PROGRAMMED BY THE SETUP PARAMETER WORD IN DE AND THIS 
; WILL BE SAVED IN THE DEVICE CONFIGURATION TABLE POINTED TO BY IY.
;
; ALL OTHER CONFIGURATION OF THE DEVICE CONFIGURATION TABLE IS DONE UPSTEAM BY PIO_PREINIT 

PIO_INITDEV:
	; TEST FOR -1 (FFFF) WHICH MEANS USE CURRENT CONFIG (JUST REINIT)
	LD	A,D			; TEST DE FOR
	AND	E			; ... VALUE OF -1
	INC	A			; ... SO Z SET IF -1
	JR	NZ,PIO_INITDEV1		; IF DE == -1, REINIT CURRENT CONFIG
PIO_INITDEV0:
	; LOAD EXISTING CONFIG (DCW) TO REINIT
	LD	E,(IY+4)		; LOW BYTE
	LD	D,(IY+5)		; HIGH BYTE	
;
PIO_INITDEV1:				; WHICH DEVICE TYPE?
	LD	A,(IY+1)
	CP	PIO_ZPIO		
	JR	Z,SETPIO0
	CP	PIO_8255
	JP	Z,SET_8255
	CP	PIO_PORT
	JP	Z,SET_PORT
BAD_SET:OR	$FF			; UNKNOWN DEVICE
	RET

SETPIO0:LD	A,E			; GET MODE 
	AND	11000000B		; BITS (B7B6)
	CP	10000000B		; IS IT BIDIR?
	JR	NZ,SETPIO1
	LD	A,(IY+2)		; GET CHANNEL
	OR	A
	JR	NZ,BAD_SET		; CAN'T DO ON CH1

	; VALIDATE INTERRUPT REQUEST
	; GRANT INTERRUPT IF THERE IS A FREE INTERRUPT
	; GRANT INTERRUPT IF AN INTERRUPT IS ALREADY ALLOCATED TO THIS UNIT

SETPIO1:PUSH	AF		;D
	LD	A,'['		;D
	CALL	COUT		;D
	LD	A,(IY)		;D
	CALL	PRTHEXBYTE	;D
	LD	A,']'		;D
	CALL	COUT		;D
	POP	AF		;D

	BIT	2,E			; SKIP IF WE ARE NOT REQUESTING 
	JP	Z,SETPIO2		; AN INTERRUPT

	PRTS("[INTREQ]$")	;D

;	LD	A,(IY+4)		; GET CURRENT INTERRUPT SETTING
;	BIT	2,A			; SKIP IF IT IS ALREADY
;	JP	NZ,SETPIO2		; ALLOCATED TO THIS UNIT

	LD	A,(INT_ALLOC)		; WE NEED TO ALLOCATE AN
	CP	INT_ALLOW		; INTERRUPT. DO WE HAVE 
	JR	NC,BAD_SET		; ONE FREE?

	PRTS("[ALLOCINT]$")	;D

	; WHICH INTERRUPT IS FREE ?
	; SCAN THROUGH THE CFG TABLES
	; AND FIND A FREE ONE

	PUSH	AF			; NESTED LOOP
	LD	DE,CFG_SIZ		; OUTSIDE LOOP IS INTERRUPT
	LD	B,INT_ALLOW		; INSIDE LOOP IS DEVICE
SETPIOP: LD	C,B 
	 DEC	C
	 PUSH	BC
	 LD	B,PIO_CNT
	 LD	HL,PIO_CFG+4
SETPIOX: LD	A,(HL)
	 BIT	2,A			; JUMP TO NEXT DEVICE
	 JR	Z,SETPIOY		; IF NO INTERRUPT ON 
	 AND	00000011B		; THIS DEVICE

	 CP	C			; IF WE MATCH AN INTERRUPT HERE THEN IT IS NOT FREE.
	 JR	NZ,SETPIOY		; SO EXIT INSIDE LOOP AND TRY NEXT INTERRUPT

	 XOR	A			; WE MATCH INT 0 - IF WE ARE CHECKING FOR IT THEN
	 OR	C			; WE REGARD IS AS FREE.
	 JR	NZ,SETPIOZ

SETPIOY: ADD	HL,DE			
	 DJNZ	SETPIOX

	 JR	SETPIOQ			; WE GET HERE IF THE CURRENT INTERRUPT
					; WAS NOT MATCHED SO IT IS FREE
SETPIOZ: POP	BC
	DJNZ	SETPIOP
	POP	AF

	PRTS("[NONEFREE]$")
	RET

SETPIOQ:PUSH	AF			; AVAILABLE INTERRUPT IS IN C
	PRTS("[FREE]=$")
	LD	A,C
	CALL	PRTHEXBYTE
	POP	AF

	POP	AF			
	POP	AF

SETPIOR:LD	HL,INT_ALLOC		; INCREASE THE COUNT
	INC	(HL)			; OF USED INTERRUPTS
	LD	A,(HL)

;	LD	A,(IY)			; IS THIS UNIT 
;	INC	A			; UNITIALIZED?
;	JR	Z,SETPIO6

	LD	A,(IY+4)		; IT IS UNITIALIZED SO
	OR	C			; SAVE THE ALLOCATES 
	LD	(IY+4),A		; INTERRUPT
;
; FOR THIS DEVICE AND INTERRUPT, UPDATE THE CONFIG TABLE FOR THIS DEVICE.
; PIO_IN, PIO_OUT, PIO_IST, PIO_OST ENTRIES NEED TO BE REDIRECTED.
; INTERRUPT VECTOR NEEDS TO BE UPDATED
;
	LD	A,(IY+0)
	LD	HL,0
	; SETUP PIO INTERRUPT VECTOR IN IVT
	LD	HL,HBX_IV09+1

;	CALL	SPK_BEEP
;
SETPIO6:RET	

	; EXIT WITH FREE INTERRUPT IN C

	LD	A,C
	LD	(INT_ALLOC),A

	LD	A,E
	AND	11000000B
	OR	00000100B
	OR	C
	LD	E,A
	LD	(IY+5),A
;
	; TODO: DEALLOCATE AN INTERRUPT
;
;	LD	A,(INT_ALLOC)
;	DEC	A
;	LD	(INT_ALLOC),A
;
SETPIO2:

;	DE CONTAINS THE MODE IF INTERRUPT ROUTINE SKIPPED

	PRTS("[NOINTREQ]$")	;D

;	LD	A,(IY+4)
	LD	A,E			; GET MODE AND CREATE COMMAND
	AND	11000000B		; $B0
	OR	00001111B		; $0F	

	LD	C,(IY+3)		; GET DATA PORT
	INC	C			; POINT TO CMD
	INC	C			; PORT	
	OUT	(C),A			; SET MODE
	CP	(M_BitCtrl | $0F)	; IF MODE 3
	JR	NZ,SETPIO3		
	LD	A,(IY+5)		; SET I/O DIRECTION
	OUT	(C),A			; FOR MODE 3

SETPIO3:; INTERUPT HANDLING

	JP	SETPIO4

	; SETUP THE INTERRUPT VECTOR

	LD	A,E
	AND	00000011B
;	DEC	A			; INDEX INTO THE
	ADD	A,A			; THE VECTOR TABLE
	ADD	A,A			; 
	LD	C,A
	LD	B,0
	LD	HL,HBX_IV09+1
	ADD	HL,BC			; GET THE ADDRESS OF
	PUSH	DE
	LD	D,(HL)			; THAT INTERRUPT
	INC	HL			; HANDLER
	LD	E,(HL)
	LD	HL,0	;HBX_IVT+IVT_PIO0	; POPULATE THE
	LD	A,L			; GET LOW BYTE OF IVT ADDRESS
	ADD	HL,BC			; INTERRUPT TABLE
	LD	(HL),D			; WITH THE INTERRUPT
	INC	HL			; HANDLER ADDRESS FOR
	LD	(HL),E			; THIS UNIT
	POP	DE
	LD	HL,INT_ALLOC
	LD	C,(HL)
	LD	B,0
	LD	HL,PRTTAB-1		; SAVE THE DATA	
	ADD	HL,BC			; PORT FOR EACH INTERRUPT
	LD	C,(IY+3)		
	LD	(HL),C

	INC	C			; POINT TO CMD PORT
	INC	C
	DI				; SET THE VECTOR ADDRESS
	OUT	(C),A

;	LD	A,10000011B		; ENABLE INTERRUPTS ON
	OUT	(C),A			; THIS UNIT
	EI	
;	JR	GUD_SET
;
SETPIO4:LD	A,00000111B		; $07 
	OUT	(C),A			; NO INTERRUPTS
;
;	SUCCESSFULL SO SAVE DEVICE CONFIGURATION WORD (DCW)
;
GUD_SET:LD	(IY+4),E		; LOW BYTE
	LD	(IY+5),D		; HIGH BYTE	
;
;	UPDATE THE DEVICE TABLE WITH THE ADDRESSES FOR THE CORRECT ROUTINE.
;
	LD	A,E
	AND	00000111B
	LD	HL,INTMATRIX		; POINT TO EITHER THE INTERRUPT
	JR	NZ, USEIM
	LD	HL,POLMATRIX		; MATRIX OR THE POLLED MATRIX
USEIM:	PUSH	HL	

	PUSH	IY			; CALCULATE THE DESTINATION
	POP	HL			; ADDRESS IN THE PIO_CFG TABLE
	LD	BC,8			; FOR THE FOUR ADDESSES TO BE 
	ADD	HL,BC			; COPIED TO 

;	LD	B,0			; 00000000 CALCULATE THE SOURCE ADDRESS
	LD	C,E			; XX?????? FROM THE MATRIX. EACH ENTRY 
	SRL	C			; 0XX????? IN THE MATRIX IS 8 BYTES SO
	SRL	C			; 00XX???? SOURCE = MATRIX BASE + (8 * MODE)
	SRL	C			; 000XX???
	POP	DE			; LOAD THE MATRIX BASE
;	LD	DE,POLMATRIX
	EX	DE,HL
	ADD	HL,BC			; HL = SOURCE

	LD	C,8			; COPY 8 BYTES
	LDIR

;	PUSH	IY
;	POP	DE
;	CALL	DUMP_BUFFER

	XOR	A
	RET

PRTTAB:	.DB	0
	.DB	0
	.DB	0
	.DB	0
;
;-----------------------------------------------------------------------------
;
; INPUT INTERRUPT VECTOR MACRO AND DEFINITION FOR FOUR PORTS
;
#DEFINE	PIOMIVT(PIOIN,PIOIST,PIOPRT) 		\
#DEFCONT ;\
#DEFCONT ;	RETURN WITH ERROR IF THERE IS	\
#DEFCONT ;	ALREADY	A CHARACTER IN BUFFER	\
#DEFCONT ;\
#DEFCONT ;	OTHERWISE CHANGE THE STATUS TO	\
#DEFCONT ;	SHOW THERE IS ONE CHARACTER IN	\
#DEFCONT ;	THE BUFFER AND READ IT IN AND	\
#DEFCONT ;	AND STORE IT.RETURN GOOD STATUS.\
#DEFCONT ;\
#DEFCONT \	LD	A,(_CIST)
#DEFCONT \	OR	A
#DEFCONT \	JR	NZ,_OVFL
#DEFCONT \	LD	A,(PIOPRT)
#DEFCONT \	LD	C,A
#DEFCONT \	LD	A,1
#DEFCONT \	LD	(_CIST),A
#DEFCONT \	IN	A,(C)
#DEFCONT \	LD	(_CICH),A
#DEFCONT \	OR	$FF
#DEFCONT \	RET
#DEFCONT \_OVFL:XOR	A
#DEFCONT \	RET
#DEFCONT ;\
#DEFCONT ;\
#DEFCONT ;\
#DEFCONT ;\
#DEFCONT ;\
#DEFCONT ;\
#DEFCONT \PIOIN:CALL	PIOIST
#DEFCONT \	JR	Z,PIOIN
#DEFCONT \	LD	A,(_CICH)
#DEFCONT \	LD	E,A
#DEFCONT \	XOR	A
#DEFCONT \	LD	(_CIST),A
#DEFCONT \	RET
#DEFCONT ;\
#DEFCONT ;	If THERE A CHARACTER		\
#DEFCONT ;	AVAILABLE? RETURN NUMBER	\
#DEFCONT ;	IN A - 0 OR 1			\
#DEFCONT ;\
#DEFCONT \PIOIST:LD	A,(_CIST)
#DEFCONT \	AND	00000001B
#DEFCONT \	RET
#DEFCONT ;\
#DEFCONT ;	CIST :	01 = CHARACTER READY ELSE NOT READY	\
#DEFCONT ;	CISH :	CHARACTER STORED BY INTERRUPT		\
#DEFCONT ;\
#DEFCONT \_CIST	.DB	00
#DEFCONT \_CICH	.DB	00
;
PIOIVT0:.MODULE	PIOIVT0
PIOMIVT(PIO0IN,PI0_IST,PRTTAB+0)
PIOIVT1:.MODULE PIOIVT1
PIOMIVT(PIO1IN,PI1_IST,PRTTAB+1)
PIOIVT2:.MODULE PIOIVT2
PIOMIVT(PIO2IN,PI2_IST,PRTTAB+2)
PIOIVT3:.MODULE PIOIVT3
PIOMIVT(PIO3IN,PI3_IST,PRTTAB+3)
;
;-----------------------------------------------------------------------------
;
; OUTPUT INTERRUPT VECTOR MACRO AND DEFINITION FOR FOUR PORTS
;
; AN INTERRUPT IS GENERATED WHEN THE RECEIVING DEVICE CAN ACCEPT A CHARACTER
;
#DEFINE	PIOMOVT(PIOOUT,PIOOST,PIOPRT) 		\
#DEFCONT ;\
#DEFCONT ;	RETURN IF WE ARE WAITING FOR A	\
#DEFCONT ;	CHARACTER (COST = 00)		\
#DEFCONT ;\
#DEFCONT ;	IF ZERO CHARACTERS READY 
#DEFCONT ;	(COST = 01) CHANGE STATUS TO	\
#DEFCONT ;	WAITING FOR CHARACTER (COST 00)	\
#DEFCONT ;\
#DEFCONT ;	IF A CHARACTER IS READY THEN	\
#DEFCONT ;	OUTPUT AND CHANGE STATUS TO	\
#DEFCONT ;	ZERO CHARACTERS READY		\
#DEFCONT ;\
#DEFCONT \	LD	A,(_COST)
#DEFCONT \ 	DEC	A
#DEFCONT \	RET	M
#DEFCONT \	JR	Z,_WFC
#DEFCONT \	LD	A,(_COCH)
#DEFCONT \	LD	E,A
#DEFCONT \_ONOW:LD	A,(PIOPRT)
#DEFCONT \	LD	C,A
#DEFCONT \	OUT	(C),E
#DEFCONT \ 	LD	A,1
#DEFCONT \_WFC:	LD	(_COST),A
#DEFCONT \	RET
#DEFCONT ;\
#DEFCONT ;	WAIT FOR SPACE FOR THE CHARACTER\
#DEFCONT ;	IF WE ARE WAITING FOR A		\
#DEFCONT ;	CHARACTER THEN OUTPUT IT NOW	\
#DEFCONT ;	OTHERWISE STORE IT UNTIL THE	\
#DEFCONT ;	INTERRUPT CALLS FOR IT		\
#DEFCONT ;\
#DEFCONT \PIOOUT:LD	A,(_COST)
#DEFCONT \	CP	2
#DEFCONT \	JR	C,_ONOW
#DEFCONT \	LD	A,E
#DEFCONT \	LD	(_COCH),A
#DEFCONT \	LD	A,2
#DEFCONT \	LD	(_COST),A
#DEFCONT \	JR	PIOOUT
#DEFCONT ;\
#DEFCONT ;	RETURN WITH NUMBER OF		\
#DEFCONT ;	CHARACTERS AVAILABLE 0 or 1	\
#DEFCONT ;\
#DEFCONT \PIOOST:LD	A,(_COST)
#DEFCONT \	DEC	A
#DEFCONT \	DEC	A
#DEFCONT \	RET	Z
#DEFCONT \	LD	A,1
#DEFCONT \	RET
#DEFCONT ;\
#DEFCONT ;	COST :	00 WAITING FOR CHARACTER\
#DEFCONT ;		01 ZERO CHARACTERS READY\
#DEFCONT ;		02 ONE CHARACTER READY	\
#DEFCONT ;	COCH :	CHARACTER TO OUTPUT	\
#DEFCONT ;\
#DEFCONT \_COST	.DB	01
#DEFCONT \_COCH	.DB	00
;
PIOOVT0:.MODULE	PIOOVT0
PIOMOVT(PIO0OUT,PI0_OST,PRTTAB+0)
PIOOVT1:.MODULE PIOOVT1
PIOMOVT(PIO1OUT,PI1_OST,PRTTAB+1)
PIOOVT2:.MODULE PIOOVT2
PIOMOVT(PIO2OUT,PI2_OST,PRTTAB+2)
PIOOVT3:.MODULE PIOOVT3
PIOMOVT(PIO3OUT,PI3_OST,PRTTAB+3)
;
;-----------------------------------------------------------------------------
;
; NON INTERRUPT OUTPUT ROUTINE - SHARED
;
; INPUT WILL ALWAYS RETURN ERROR, CHARACTER RETURNED IS UNDEFINED.
; OUTPUT WILL ALWAYS RETURN SUCCESS
; INPUT-STATUS WILL ALWAYS RETURN 0 CHARACTERS IN BUFFER.
; OUTPUT-STATUS WILL ALWAYS RETURN 1 CHARACTER SPACE IN BUFFER.

PIOSHO_IN:
	LD	A,1
	RET
;
PIOSHO_OUT:	
	LD	C,(IY+3)
	OUT	(C),E
	XOR	A
	RET
;
PIOSHO_IST:	XOR	A
	RET
;
PIOSH_OST:	
	LD	A,1
	RET
;
;-----------------------------------------------------------------------------
;
; NON INTERRUPT INPUT ROUTINE - SHARED
;
; INPUT WILL ALWAYS A CHARACTER AND SUCCESS.
; OUTPUT WILL ALWAYS RETURN FAILURE
; INPUT STATUS WILL ALWAYS RETURN 1 CHARACTER IN BUFFER.
;OUTPUT-STATUS WILL ALWAYS RETURN 0 CHARACTER SPACE IN BUFFER.
;
PIOSHI_IN:
	LD	C,(IY+3)
	IN	A,(C)
	LD	E,A
	XOR	A
	RET
;
PIOSHI_OUT:
	LD	A,1
	RET
;
PIOSH_IST:
	LD	A,1
	RET
;
PIOSHI_OST:
	XOR	A
	RET
;
;-----------------------------------------------------------------------------
;
; ON ENTRY IY POINTS TO THE DEVICE RECORD. GET AND RETURN THE CONFIGURATION WORD IN DE
;
PIO_QUERY:
PPI_QUERY:
	LD	E,(IY+4)		; FIRST CONFIG BYTE TO E
	LD	D,(IY+5)		; SECOND CONFIG BYTE TO D
	XOR	A			; SIGNAL SUCCESS
	RET
;
;-----------------------------------------------------------------------------
;
; ON ENTRY IY POINTS TO THE DEVICE RECORD. FOR CHARACTER DEVICES BIT 6 OF ATTRIBUTE
; INDICATES PARALLEL PORT IF 1 SO WE SET IT. COMMON TO ALL PORTS
;
PIO_DEVICE:
PPI_DEVICE:
	LD	D,CIODEV_PIO		; D := DEVICE TYPE
	LD	E,(IY)			; E := PHYSICAL UNIT
	LD	C,$40			; C := ATTRIBUTE
	LD	H,0			; H := 0, DRIVER HAS NO MODES
	LD	L,(IY+3)		; L := BASE I/O ADDRESS
	XOR	A			; SIGNAL SUCCESS
	RET
;
INTMATRIX:
	.DW	PIO0IN,	PIO0OUT, PI0_IST, PI0_OST
	.DW	PIO1IN,	PIO1OUT, PI1_IST, PI1_OST
	.DW	PIO2IN,	PIO2OUT, PI2_IST, PI2_OST
	.DW	PIO3IN,	PIO3OUT, PI3_IST, PI3_OST
POLMATRIX:
	.DW	PIOSHO_IN, PIOSHO_OUT, PIOSHO_IST, PIOSH_OST	; OUTPUT
	.DW	PIOSHI_IN, PIOSHI_OUT, PIOSH_IST,  PIOSHI_OST	; INPUT
	.DW	0,0,0,0						; BIDIR	
	.DW	0,0,0,0						; BIT MODE

SET_8255:
	RET	
;
SET_BYE:
	XOR	A			; SIGNAL SUCCESS
	RET
;
; ------------------------------------
; i8255 FUNCTION TABLE ROUTINES
;-------------------------------------
	
PPI_IN:
	XOR	A			; SIGNAL SUCCESS
	RET	
;
PPI_OUT:
	XOR	A			; SIGNAL SUCCESS
	RET
;	
PPI_IST:
	RET	
;	
PPI_OST:
	RET
;
; PIO_INITDEV - Configure device.
; If DE = FFFF then extract the configuration information from the table of devices and program the device using those settings.
; Otherwise use the configuration information in DE to program those settings and save them in the device table

PPI_INITDEV:
	XOR	A			; SIGNAL SUCCESS
	RET	
PPI_INT:OR	$FF			; NZ SET TO INDICATE INT HANDLED
	RET
;	
PIO_PRTCFG:
	; ANNOUNCE PORT
	CALL	NEWLINE			; FORMATTING
	PRTS("PIO$")			; 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 PIO TYPE
	CALL	PC_SPACE		; FORMATTING
	LD	A,(IY+1)		; GET PIO TYPE BYTE
	LD	DE,PIO_TYPE_STR		; POINT HL TO TYPE MAP TABLE
	CALL	PRTIDXDEA

	; ALL DONE IF NO PIO WAS DETECTED
	LD	A,(IY+1)		; GET PIO TYPE BYTE
	OR	A			; SET FLAGS
	RET	Z			; IF ZERO, NOT PRESENT
;
	PRTS(" MODE=$")			; FORMATTING
	LD	E,(IY+4)		; LOAD CONFIG
	LD	D,(IY+5)		; ... WORD TO DE
	CALL	PS_PRTPC0		; PRINT CONFIG
;
	LD	A,(IY+4)		; PRINT
	BIT	2,A			; ALLOCATED
	JR	Z,NOINT			; INTERRUPT
	PRTS("/i$")
	LD	A,(IY+4)
	AND	00000011B
	CALL	PRTDECB
NOINT:	XOR	A
	RET
;
; WORKING VARIABLES
;
PIO_DEV		.DB	0		; DEVICE NUM USED DURING INIT
;	
; DESCRIPTION OF DIFFERENT PORT TYPES
;
PIO_TYPE_STR:
		.TEXT	"<NOT PRESENT>$"	; IDX 0
		.TEXT	"Zilog PIO$"		; IDX 1
		.TEXT	"i8255 PPI$"		; IDX 2
		.TEXT	"IO Port$"		; IDX 3
;
; Z80 PIO PORT TABLE - EACH ENTRY IS FOR 1 CHIP I.E. TWO PORTS
;
;	32 BYTE DATA STRUCTURE FOR EACH PORT
;
;	.DB	0		; IY+0 CIO DEVICE NUMBER 			(SET DURING PRE-INIT, THEN FIXED)
;	.DB	0		; IY+1 PIO TYPE 				(SET AT ASSEMBLY, FIXED)
;	.DB	0		; IY+2 PIO CHANNEL				(SET AT ASSEMBLY, FIXED)
;	.DB	PIOBASE+2	; IY+3 BASE DATA PORT				(SET AT ASSEMBLY, FIXED)
;	.DB	0		; IY+4 SPW - MODE 3 I/O DIRECTION BYTE		(SET AT ASSEMBLE, SET WITH INIT)
;	.DB	0		; IY+5 SPW - MODE, INTERRUPT			(SET AT ASSEMBLY, SET WITH INIT)
;	.DW	0		; IY+6/7 FUNCTION TABLE				(SET AT ASSEMBLY, SET DURING PRE-INIT AND AT INIT)
;	.DW	PIO_IN		; IY+8  ADDR FOR DEVICE INPUT			(SET WITH INIT)
;	.DW	PIO_OUT		; IY+10 ADDR FOR DEVICE OUTPUT			(SET WITH INIT)
;	.DW	PIO_IST		; IY+12 ADDR FOR DEVICE INPUT STATUS		(SET WITH INIT)
;	.DW	PIO_OST		; IY+14 ADDR FOR DEVICE OUTPUT STATUS		(SET WITH INIT)
;	.DW	PIO_INITDEV	; IY+16 ADDR FOR INITIALIZE DEVICE ROUTINE	(SET AT ASSEMBLY, FIXED)
;	.DW	PIO_QUERY	; IY+18 ADDR FOR QUERY DEVICE RECORD ROUTINE	(SET AT ASSEMBLY, FIXED)
;	.DW	PIO_DEVICE	; IY+20 ADDR FOR DEVICE TYPE ROUTINE		(SET AT ASSEMBLY, FIXED)
;	.FILL	10
;
;	SETUP PARAMETER WORD:
;
;	+-------------------------------+ +-------+-----------+---+-------+
;	|         BIT CONTROL           | | MODE  |           | A |  INT  |
;	+-------------------------------+ --------------------+-----------+
;	 F   E   D   C   B   A   9   8     7   6   5   4   3   2   1   0
;	-- MSB (D REGISTER) --           -- LSB (E REGISTER) --
;
;
; 	MSB = BIT CONTROL MAP USE IN MODE 3 
;
; 	MODE B7 B6 =	00 Mode 0 Output
;			01 Mode 1 Input
;			10 Mode 2 Bidir
;			11 Mode 3 Bit Mode
;
;	INTERRUPT ALLOCATED B2	= 0 NOT ALLOCATED
;				= 1 IS ALLOCATED
;
;	WHICH IVT IS ALLOCATES B1 B0	00 IVT_PIO0
;					01 IVT_PIO1
;					10 IVT_PIO2
;					11 IVT_PIO3
;	
#DEFINE	DEFPIO(MPIOBASE,MPIOCH0,MPIOCH1,MPIOCH0X,MPIOCH1X,MPIOIN0,MPIOIN1) \
#DEFCONT \ .DB	0
#DEFCONT \ .DB	PIO_ZPIO
#DEFCONT \ .DB	0
#DEFCONT \ .DB	MPIOBASE
#DEFCONT \ .DB	(MPIOCH0|MPIOIN0)
#DEFCONT \ .DB	MPIOCH0X
#DEFCONT \ .DW	0
#DEFCONT \ .DW	0,0,0,0, PIO_INITDEV,PIO_QUERY,PIO_DEVICE
#DEFCONT \ .FILL 2	
#DEFCONT \ .DB	0
#DEFCONT \ .DB	PIO_ZPIO
#DEFCONT \ .DB	1
#DEFCONT \ .DB	MPIOBASE+1
#DEFCONT \ .DB	(MPIOCH1|MPIOIN1)
#DEFCONT \ .DB	MPIOCH1X
#DEFCONT \ .DW	0
#DEFCONT \ .DW	0,0,0,0, PIO_INITDEV,PIO_QUERY,PIO_DEVICE
#DEFCONT \ .FILL 2	
;
; i8255 PORT TABLE - EACH ENTRY IS FOR 1 CHIP I.E. THREE PORTS
;
#DEFINE	DEFPPI(MPPIBASE,MPPICH1,MPPICH2,MPPICH3,MPPICH1X,MPPICH2X,MPPICH3X) \
#DEFCONT \ .DB	0
#DEFCONT \ .DB	PIO_8255
#DEFCONT \ .DB	0
#DEFCONT \ .DB	MPPIBASE
#DEFCONT \ .DB	(MPPICH1|00001000B)
#DEFCONT \ .DB	MPPICH1X
#DEFCONT \ .DW	0
#DEFCONT \ .DW	PPI_IN,PPI_OUT,PPI_IST,PPI_OST,PPI_INITDEV,PPI_QUERY,PPI_DEVICE
#DEFCONT \ .FILL 2
#DEFCONT \ .DB	0
#DEFCONT \ .DB	PIO_8255
#DEFCONT \ .DB	1
#DEFCONT \ .DB	MPPIBASE+2
#DEFCONT \ .DB	(MPPICH2|00010000B)
#DEFCONT \ .DB	MPPICH2X
#DEFCONT \ .DW	0
#DEFCONT \ .DW	PPI_IN,PPI_OUT,PPI_IST,PPI_OST,PPI_INITDEV,PPI_QUERY,PPI_DEVICE
#DEFCONT \ .FILL 2
#DEFCONT \ .DB	0
#DEFCONT \ .DB	PIO_8255
#DEFCONT \ .DB	2
#DEFCONT \ .DB	MPPIBASE+4
#DEFCONT \ .DB	(MPPICH3|00100000B)
#DEFCONT \ .DB	MPPICH3X
#DEFCONT \ .DW	0
#DEFCONT \ .DW	PPI_IN,PPI_OUT,PPI_IST,PPI_OST,PPI_INITDEV,PPI_QUERY,PPI_DEVICE
#DEFCONT \ .FILL 2
;
; HERE WE ACTUALLY DEFINE THE HARDWARE THAT THE HBIOS CAN ACCESS
; THE INIT ROUTINES READ AND SET THE INITIAL MODES FROM THIS INFO
;
PIO_CFG:
;
#IF	PIO_4P
DEFPIO(PIO4BASE+0,M_Output,M_Input,M_BitAllOut,M_BitAllOut,INT_N,INT_N)
DEFPIO(PIO4BASE+4,M_Input,M_Input,M_BitAllOut,M_BitAllOut,INT_N,INT_N)
DEFPIO(PIO4BASE+8,M_Output,M_Output,M_BitAllOut,M_BitAllOut,INT_N,INT_N)
DEFPIO(PIO4BASE+12,M_Output,M_Output,M_BitAllOut,M_Output,INT_N,INT_N)
#ENDIF
#IF	PIO_ZP
DEFPIO(PIOZBASE+0,M_Input,M_Input,M_BitAllOut,M_BitAllOut,INT_N,INT_N)
DEFPIO(PIOZBASE+4,M_Output,M_Output,M_BitAllOut,M_BitAllOut,INT_N,INT_N)
#ENDIF
; PIO_SBC & (PLATFORM == PLT_SBC) & (PPIDEMODE != PPIDEMODE_SBC))

#IF 	PIO_SBC
DEFPPI(PIOSBASE,M_Output,M_Output,M_Output,M_BitAllOut,M_BitAllOut,M_BitAllOut)
#ENDIF
;
PIO_CNT	.EQU	($ - PIO_CFG) / CFG_SIZ 	
;
;-------------------------------------------------------------------
; WHEN WE GET HERE IY POINTS TO THE PIO_CFG TABLE WE ARE WORKING ON.
; C IS THE UNIT NUMBER
;-------------------------------------------------------------------
;
;PIO_INITUNIT:
;	LD	A,C			; SET THE UNIT NUMBER
;	LD	(IY),A	
;
;	LD	DE,-1			; LEAVE CONFIG ALONE
;	CALL	PIO_INITDEV		; IMPLEMENT IT AND RETURN
;	XOR	A			; SIGNAL SUCCESS
;	RET				; AND RETURN
;
PIO_INIT:
;	CALL	SPK_BEEP
	LD	B,PIO_CNT		; COUNT OF POSSIBLE PIO UNITS
	LD	C,0			; INDEX INTO PIO CONFIG TABLE
PIO_INIT1:
	PUSH	BC			; SAVE LOOP CONTROL
	
;	LD	A,C			; PHYSICAL UNIT TO A
;	RLCA				; MULTIPLY BY CFG TABLE ENTRY SIZE (32 BYTES)
;	RLCA				; ...
;	RLCA				; ... TO GET OFFSET INTO CFG TABLE
;	RLCA
;;	RLCA
;	LD	HL,PIO_CFG		; POINT TO START OF CFG TABLE
;	PUSH	AF
;	CALL	ADDHLA			; HL := ENTRY ADDRESS
;	POP	AF
;	CALL	ADDHLA			; HL := ENTRY ADDRESS
;	PUSH	HL			; COPY CFG DATA PTR
;	POP	IY			; ... TO IY
;	POP	IY			; ... TO IY

	CALL	IDXCFG

	LD	A,(IY+1)		; GET PIO TYPE
	OR	A			; SET FLAGS
	CALL	NZ,PIO_PRTCFG		; PRINT IF NOT ZERO
	
;	PUSH	DE	
;	LD	DE,$FFFF		; INITIALIZE DEVICE/CHANNEL
;	CALL	PIO_INITDEV		; BASED ON DPW
;	POP	DE

	POP	BC			; RESTORE LOOP CONTROL
	INC	C			; NEXT UNIT
	DJNZ	PIO_INIT1		; LOOP TILL DONE
;
	XOR	A			; SIGNAL SUCCESS
	RET				; DONE
;
SET_PORT:
	; DEVICE TYPE IS I/O PORT SO JUST WRITE $00 TO IT
	LD	C,(IY+3)
	OUT	(C),A
	XOR	A
	RET
