	TITLE	'CHARACTER I/O HANDLER FOR CP/M 3.0'

	; DEFINE LOGICAL VALUES:
TRUE		EQU	-1
FALSE		EQU	NOT TRUE

	; DETERMINE IF FOR BANK SELECT OR NOT:
BANKED		EQU	FALSE	;<------------------- BANKED VERSION

	; DEFINE PUBLIC LABELS:
	PUBLIC	?CINIT,?CI,?CO,?CIST,?COST
	PUBLIC	@CTBL

	; DEFINE EXTERNAL LABELS AND ENTRY POINTS:
	IF	BANKED
	EXTRN	@CBNK
	EXTRN	?BNKSL
	ENDIF
	EXTRN	OUT$BLOCKS	;BLOCK OUTPUT ROUTINE TO I/O PORTS
	EXTRN	?PMSG



	; INCLUDE Z-80 MACROS:
	MACLIB	Z80

	; EQUATES FOR MODE BYTE BIT FIELDS

MB$INPUT	EQU 0000$0001B	; DEVICE MAY DO INPUT
MB$OUTPUT	EQU 0000$0010B	; DEVICE MAY DO OUTPUT
MB$IN$OUT	EQU MB$INPUT+MB$OUTPUT

MB$SOFT$BAUD	EQU 0000$0100B	; SOFTWARE SELECTABLE BAUD RATES

MB$SERIAL	EQU 0000$1000B	; DEVICE MAY USE PROTOCOL
MB$XON$XOFF	EQU 0001$0000B	; XON/XOFF PROTOCOL ENABLED

BAUD$NONE	EQU 0		; NO BAUD RATE ASSOCIATED WITH THIS DEVICE
BAUD$50		EQU 1		; 50 BAUD
BAUD$75		EQU 2		; 75 BAUD
BAUD$110	EQU 3		; 110 BAUD
BAUD$134	EQU 4		; 134.5 BAUD
BAUD$150	EQU 5		; 150 BAUD
BAUD$300	EQU 6		; 300 BAUD
BAUD$600	EQU 7		; 600 BAUD
BAUD$1200	EQU 8		; 1200 BAUD
BAUD$1800	EQU 9		; 1800 BAUD
BAUD$2400	EQU 10		; 2400 BAUD
BAUD$3600	EQU 11		; 3600 BAUD
BAUD$4800	EQU 12		; 4800 BAUD
BAUD$7200	EQU 13		; 7200 BAUD
BAUD$9600	EQU 14		; 9600 BAUD
BAUD$19200	EQU 15		; 19.2K BAUD


	; MISCELLANEOUS EQUATES:

CRTSTAT	EQU	0H		;STATUS PORT TO CHECK CRT OUTPUT STATUS
IOBYTE	EQU	0EFH
KEYSTAT	EQU	0H
KEYIN	EQU	01H		;<<<<<<<<<<<< MAIN CONSOLE I/O PORTS >>>>>>>>>>>>>
KEYOUT	EQU	01H


CENTSTROBE EQU	4		;For Printer
CENTOUT	EQU	5
CENTSTAT EQU	5


	; WILL START OFF IN COMMON MEMORY FOR BANKED OR NON-BANKED SYSTEMS:
	CSEG


	IF	BANKED
	; WE PROVIDE ALTERNATE DEFINITIONS OF THE ROUTINE ENTRY POINTS IF
	;  WE ARE RUNNING A BANKED SYSTEM VERSUS A NON-BANKED SYSTEM:

	;;;;; ?CINIT
	; ENTER HERE FOR BANKED SYSTEMS FOR DEVICE INITIALIZATIONS:
?CINIT:
	LXI	H,BCINIT	;POINT TO BANKED ROUTINE ADDRESS
	JR	BANKIO		;GO TO DISPATCHER

	;;;;; ?CI
	; ENTER HERE FOR BANKED SYSTEM DEVICE INPUT:
?CI:	LXI	H,BCI		;POINT TO BANKED ROUTINE ADDRESS
	JR	BANKIO		;GO TO DISPATCHER

	;;;;; ?CO
	; ENTER HERE FOR BANKED SYSTEM DEVICE OUTPUT:
?CO:	LXI	H,BCO		;POINT TO BANKED ROUTINE ADDRESS
	JR	BANKIO		;GO TO DISPATCHER

	;;;;; ?CIST
	; ENTER HERE FOR BANKED SYSTEM DEVICE INPUT STATUS:
?CIST:	LXI	H,BCIST		;POINT TO BANKED ROUTINE ADDRESS
	JR	BANKIO		;GO TO DISPATCHER

	;;;;; ?COST
	; ENTER HERE FOR BANKED SYSTEM DEVICE OUTPUT STATUS:
?COST:	LXI	H,BCOST		;POINT TO BANKED ROUTINE ADDRESS


	;;;;; BANKIO
	; ROUTINE DISPATCHES TO BANKED PORTION OF CHARACTER I/O ROUTINES:
BANKIO:
	SSPD	SPSAVE		;SAVE CURRENT STACK POINTER
	LXI	SP,IOSP		; AND USE LOCAL STACK FOR I/O
	LDA	@CBNK		;GET CURRENT BANK
	PUSH	PSW		;SAVE ON LOCAL STACK
	XRA	A		;WE WILL SELECT BANK 0 (OP SYS)
	CALL	?BNKSL
	LXI	D,BIORET	;RETURN ADDRESS IN [DE]
	PUSH	D		;PUT IT ON STACK FOR RETURN
	PCHL			;DISPATCH TO BANKED PART OF ROUTINE

	; ARRIVE HERE AFTER DEVICE HANDLER FINISHED:
BIORET:
	POP	D		;GET PREVIOUS CURRENT BANK TO [D]
	PUSH	PSW		;SAVE HANDLER RETURNED RESULT (IF ANY)
	MOV	A,D		;RESELECT PREVIOUS CURRENT BANK
	CALL	?BNKSL
	POP	PSW		;GET BACK RESULT CODE TO [A]
	LSPD	SPSAVE		;RESTORE PREVIOUS STACK
	RET			;AND RETURN...
	ENDIF


	;;;;;
	;;;;; ACTUAL DEVICE HANDLERS
	;;;;;


	;;;;; ?CINIT (BCINIT FOR BANKED)
	; PHYSICAL CODE FOR DEVICE INITIALIZATION:
	IF	BANKED
	DSEG			;CAN PUT IN BANKED SEGMENT IF BANKED
BCINIT:
	ELSE
?CINIT:
	ENDIF
	MOV	B,C		;ON ENTRY DEVICE # IS IN [C] BUT WE NEED
				; IT IN [B]
	CALL	DEV$DISPATCH	;GO TO CORRECT INIT ROUTINE
	DW	CINIT0		;INIT FOR DEVICE 0
	DW	CINIT1		;INIT FOR DEVICE 1
	DW	NULL$INIT	;INIT FOR DEVICE 2
	DW	NULL$INIT	;INIT FOR DEVICE 3
	DW	NULL$INIT	;INIT FOR DEVICE 4
	DW	NULL$INIT	;INIT FOR DEVICE 5
	DW	NULL$INIT	;INIT FOR DEVICE 6
	DW	NULL$INIT	;INIT FOR DEVICE 7
	DW	NULL$INIT	;INIT FOR DEVICE 8
	DW	NULL$INIT	;INIT FOR DEVICE 9
	DW	NULL$INIT	;INIT FOR DEVICE 10
	DW	NULL$INIT	;INIT FOR DEVICE 11
	DW	NULL$INIT	;INIT FOR DEVICE 12
	DW	NULL$INIT	;INIT FOR DEVICE 13
	DW	NULL$INIT	;INIT FOR DEVICE 14
	DW	NULL$INIT	;INIT FOR DEVICE 15


	;;;;; ?CI (BCI FOR BANKED)
	; PHYSICAL CODE FOR DEVICE INPUT:
	IF	BANKED
BCI:
	ELSE
?CI:
	ENDIF
	CALL	DEV$DISPATCH
	DW	CI0		;DEVICE 0 INPUT
	DW	CI1		;DEVICE 1 INPUT
	DW	NULL$CI		;DEVICE 2 INPUT
	DW	NULL$CI		;DEVICE 3 INPUT
	DW	NULL$CI		;DEVICE 4 INPUT
	DW	NULL$CI		;DEVICE 5 INPUT
	DW	NULL$CI		;DEVICE 6 INPUT
	DW	NULL$CI		;DEVICE 7 INPUT
	DW	NULL$CI		;DEVICE 8 INPUT
	DW	NULL$CI		;DEVICE 9 INPUT
	DW	NULL$CI		;DEVICE 10 INPUT
	DW	NULL$CI		;DEVICE 11 INPUT
	DW	NULL$CI		;DEVICE 12 INPUT
	DW	NULL$CI		;DEVICE 13 INPUT
	DW	NULL$CI		;DEVICE 14 INPUT
	DW	NULL$CI		;DEVICE 15 INPUT


	;;;;; ?CO (BCO FOR BANKED)
	; PHYSICAL CODE FOR DEVICE OUTPUT:
	IF	BANKED
BCO:
	ELSE
?CO:
	ENDIF
	CALL	DEV$DISPATCH	;GO TO CORRECT DEVICE OUTPUT HANDLER
	DW	CO0		;DEVICE 0 OUTPUT
	DW	CO1		;DEVICE 1 OUTPUT
	DW	NULL$CO		;DEVICE 2 OUTPUT
	DW	NULL$CO		;DEVICE 3 OUTPUT
	DW	NULL$CO		;DEVICE 4 OUTPUT
	DW	NULL$CO		;DEVICE 5 OUTPUT
	DW	NULL$CO		;DEVICE 6 OUTPUT
	DW	NULL$CO		;DEVICE 7 OUTPUT
	DW	NULL$CO		;DEVICE 8 OUTPUT
	DW	NULL$CO		;DEVICE 9 OUTPUT
	DW	NULL$CO		;DEVICE 10 OUTPUT
	DW	NULL$CO		;DEVICE 11 OUTPUT
	DW	NULL$CO		;DEVICE 12 OUTPUT
	DW	NULL$CO		;DEVICE 13 OUTPUT
	DW	NULL$CO		;DEVICE 14 OUTPUT
	DW	NULL$CO		;DEVICE 15 OUTPUT


	;;;;; ?CIST (BCIST FOR BANKED)
	; PHYSICAL CODE FOR DEVICE INPUT STATUS:
	IF	BANKED
BCIST:
	ELSE
?CIST:
	ENDIF
	CALL	DEV$DISPATCH
	DW	CIST0		;DEVICE 0 INPUT STATUS
	DW	CIST1		;DEVICE 1 INPUT STATUS
	DW	NULL$CIST	;DEVICE 2 INPUT STATUS
	DW	NULL$CIST	;DEVICE 3 INPUT STATUS
	DW	NULL$CIST	;DEVICE 4 INPUT STATUS
	DW	NULL$CIST	;DEVICE 5 INPUT STATUS
	DW	NULL$CIST	;DEVICE 6 INPUT STATUS
	DW	NULL$CIST	;DEVICE 7 INPUT STATUS
	DW	NULL$CIST	;DEVICE 8 INPUT STATUS
	DW	NULL$CIST	;DEVICE 9 INPUT STATUS
	DW	NULL$CIST	;DEVICE 10 INPUT STATUS
	DW	NULL$CIST	;DEVICE 11 INPUT STATUS
	DW	NULL$CIST	;DEVICE 12 INPUT STATUS
	DW	NULL$CIST	;DEVICE 13 INPUT STATUS
	DW	NULL$CIST	;DEVICE 14 INPUT STATUS
	DW	NULL$CIST	;DEVICE 15 INPUT STATUS


	;;;;; ?COST (BCOST FOR BANKED)
	; PHYSICAL CODE FOR DEVICE OUTPUT STATUS:
	IF	BANKED
BCOST:
	ELSE
?COST:
	ENDIF
	CALL	DEV$DISPATCH	;GO TO CONSOLE OUTPUT STATUS HANDLER
	DW	COST0		;DEVICE 0 OUTPUT STATUS
	DW	COST1		;DEVICE 1 OUTPUT STATUS
	DW	NULL$COST	;DEVICE 2 OUTPUT STATUS
	DW	NULL$COST	;DEVICE 3 OUTPUT STATUS
	DW	NULL$COST	;DEVICE 4 OUTPUT STATUS
	DW	NULL$COST	;DEVICE 5 OUTPUT STATUS
	DW	NULL$COST	;DEVICE 6 OUTPUT STATUS
	DW	NULL$COST	;DEVICE 7 OUTPUT STATUS
	DW	NULL$COST	;DEVICE 8 OUTPUT STATUS
	DW	NULL$COST	;DEVICE 9 OUTPUT STATUS
	DW	NULL$COST	;DEVICE 10 OUTPUT STATUS
	DW	NULL$COST	;DEVICE 11 OUTPUT STATUS
	DW	NULL$COST	;DEVICE 12 OUTPUT STATUS
	DW	NULL$COST	;DEVICE 13 OUTPUT STATUS
	DW	NULL$COST	;DEVICE 14 OUTPUT STATUS
	DW	NULL$COST	;DEVICE 15 OUTPUT STATUS


	;;;;; DEV$DISPATCH
	; ROUTINE JUMPS TO CORRECT DEVICE HANDLER:
DEV$DISPATCH:
	MOV	A,B		;GET DEVICE # TO [A]
	STA	DEV$CODE	;SAVE FOR LATER USE
	ADD	A		;X2 FOR WORD OFFSET
	POP	H		;RETURN ADDRESS IS 1ST PARAMETER ADDRESS
	MOV	E,A		;SET UP OFFSET IN [DE]
	MVI	D,0
	DAD	D		;[HL] = PTR TO HANDLER ADDRESS
	MOV	E,M		;GET HANDLER ADDRESS TO [DE]
	INX	H
	MOV	D,M
	XCHG			;PUT IN [HL]
	PCHL			;AND DISPATCH TO IT...


	;;;;;
	;;;;; PHYSICAL DEVICE HANDLER CODE:
	;;;;;
;

	;;;;; DEVICE 0 HANDLERS (SD SYSTEMS VIDIO BOARD)
CINIT0:	; DEVICE 0 INITIALIZATION 
	RET
;
; 
;<<<<<<<<<<<<<<<<<<< MAIN CONSOL STATUS ROUTINE >>>>>>>>>>>>>>>>>>>>>>
CIST0:				; DEVICE 0 INPUT STATUS:
CSTS:	IN	KEYSTAT
	ANI	02H
	RZ			;RETURN WITH 0 IN [A] IF NOTHING THERE
	DCR	A
	RET			;RETURN WITH 0FFH IN [A] IF SOMETHING
;
;
;<<<<<<<<<<<<<<<<<<<< MAIN CONSOL INPUT ROUTINE >>>>>>>>>>>>>>>>>>>>
;
CI0:				;DEVICE 0 INPUT:
CI:	CALL	CSTS		;NEED CONSTAT TO CLEAN UP SHIFT KEYS ETC
	JRZ	CI
	IN	KEYIN
	ANI	7FH
	RET
;
;<<<<<<<<<<<<<<<<<<<<<< MAIN CONSOL OUTPUT ROUTINE >>>>>>>>>>>>>>>>>>>>>>>>>
;
COST0:	; DEVICE 0 OUTPUT STATUS:
	IN	CRTSTAT
	ANI	4H
	RZ
	XRA	A
	DCR	A
	RET
;
CO0:				; DEVICE 0 OUTPUT:
CO:	IN	IOBYTE
	BIT	0,A		;CHECK IF OUTPUT TO LIST IS ALSO REQ
	JZ	LOX
	BIT	4,A		;KILL LF'S IF THIS IS 0
	JRNZ	SDCONO
	MOV	A,C
	CPI	0AH		;CHECK FOR LF'S
	JRZ	SDCON5		;KILL LF'S
	PUSH	B		;ALL OTHERE CHARACTERS SEND EOL THEN CHAR
	MVI	C,']'-40H	;FOR CLEAR TO END OF LINE
	CALL	SDCONO		;BECAUSE EOL IS SENT FOR EACH CHARACTER THE
	POP	B		;TYPE RATE IS NICELY SLOWED DOWN TO ~ 60 BAUD
	JR	SDCONO		;AT NO FURTHER EXPENSE |
SDCON5:	MOV	A,C
	RET
;
LOX:	CALL	SDCONO		;OUTPUT TO BOTH PRINTER & CONSOLE
	CALL	LO
	RET
;
SDCONO:	IN	0H		;SD SYSTEMS VIDIO BOARD PORT
	ANI	4H
	JRZ	SDCONO
	MOV	A,C
	CPI	07H		;IS IT A BELL
	JRZ	BELL1
	CPI	0H		;SD BOARD CANNOT TAKE A NULL!
	RZ
	OUT	1
	IN	IOBYTE
	BIT	5,A		;SEE IF TIME DELAY REQ WITH CONSOL OUTPUT
	JRNZ	SDCON5	;MAKE SURE TO RETURN CHARACTER SENT IN [A]
	PUSH	PSW
	MVI	A,50
	CALL	DELAY
	POP	PSW
	MOV	A,C
	RET			;RETURN CHARACTER SENT IN [A]
;
;
BELL1:	MVI	A,06H		;SEND A BELL
	OUT	1H
	MVI	A,0FH
	CALL	DELAY
	MVI	A,07H
	OUT	1H
	JR	SDCON5
;
DELAY:	DCR	A		;GENERAL COUNT DOWN TIME DELAY
	RZ			;LENGTH SET IN [A]
	PUSH	PSW
	MVI	A,05H
MORE:	DCR	A
	PUSH	PSW
	XRA	A
MORE2:	DCR	A
	JRNZ	MORE2
	POP	PSW
	JRNZ	MORE
	POP	PSW
	JR	DELAY
;
;
	;;;;; DEVICE 1 HANDLERS: (Parallel port to printers).
CINIT1:	; DEVICE 1 INITIALIZATIONS 
	MVI	A,0FFH		;NEEDED TO CLEAR PRINTER STROBE COMES UP
	OUT	CENTSTROBE	;AFTER A RESET LOW
	RET		


CIST1:	; DEVICE 1 INPUT STATUS:
	XRA	A
	RET

COST1:	; DEVICE 1 OUTPUT STATUS:
LSTAT:	IN	CENTSTAT	;FIRST FIND WHICH PRINTER IS SELECTED
	BIT	1,A
	JRNZ	CENSTAT
	BIT	5,A
	JRNZ	TRANSTAT
	XRA	A		;NONE SELECTED
	DCR	A
	RET
CENSTAT:ANI	00001111B	;XXXX0110 IS READY (BIT 3=PAPER BIT 2=FAULT
	CPI	00000110B	;BIT 1=SELECT  BIT 0=BUSY
	JRZ	LSTAT1
	XRA	A
	RET
TRANSTAT:ANI	11110000B	;0110XXX IS READY (BIT 7=ALERT BIT 6=FAULT
	CPI	01100000B	;BIT 5=SELECT BIT 4=BUSY
	JRZ	LSTAT1
	XRA	A
	RET
LSTAT1:	XRA	A		;PUT 0FFH IN [A] IF READY & NO ZERO FLAG
	DCR	A
	RET
;
CI1:	; DEVICE 1 INPUT:
	MVI	A,1AH
	RET

CO1:	; DEVICE 1 OUTPUT:
;
;THIS IS THE MAIN DRIVER FOR PRINTER OUTPUT. 
;
LO:	CALL	LSTAT
	JRZ	LO
	MVI	A,0FFH
	OUT	CENTSTROBE
	MOV	A,C
	OUT	CENTOUT
	IN	CENTSTAT
	BIT	1,A
	JRNZ	LCENT
	BIT	5,A
	JRNZ	LTRANS
	RET				;NO STROBE SINCE NOT SELECTED
;
LCENT:	MVI	A,11111110B		;STROBE FOR CENTRONICS
	JR	OVERLS
LTRANS:	MVI	A,11111101B
OVERLS:	OUT	CENTSTROBE
	MVI	A,0FFH
	OUT	CENTSTROBE
	RET
;
;
	;;;;; NULL ROUTINES:
NULL$CIST:
NULL$COST:
	XRA	A		;RETURN A FALSE STATUS RESULT
	JR	NULL$RET
NULL$CI:
	MVI	A,1AH		;FOR INPUT RETURN A CNTL-Z (EOF)
NULL$INIT:
NULL$CO:
NULL$RET:
	RET			;HARMLESS RETURN


	; STORAGE FOR DEVICE CODE -- CAN RESIDE IN SAME SEGMENT AS THE BULK
	;  OF CHARACTER I/O ROUTINES:
DEV$CODE:	DS	1

	;;;;; CHRTBL
	; CHARACTER DEVICE TABLE
	CSEG			;MUST RESIDE IN COMMON MEMORY
@CTBL:
	DB	'CRT   '		;CONSOLE (DEVICE 0)
	DB	MB$IN$OUT
	DB	BAUD$NONE

	DB	'LPT   '		;PRINTER (DEVICE 1)
	DB	MB$OUTPUT
	DB	BAUD$NONE

MAX$DEVICES	EQU	($-@CTBL)/8	;# DEVICES IN TABLE
	DB	0			;TABLE TERMINATOR


	; OTHER DATA AREAS:
	DS	24		;CHARACTER I/O LOCAL STACK
IOSP	EQU	$
SPSAVE	DS	2

	END
