*****************************************************************
*								*
*  SYSIO -- Standard Set of Redirectable I/O Drivers		*
*	for ARIES-1 with ZCPR2 and CHBIOSZ			*
*								*
*****************************************************************
FALSE	EQU	0
TRUE	EQU	NOT FALSE
stksiz	equ	200	;Limit of Caller's Stack
cr	equ	0dh	;<CR>

	MACLIB	CBIOSHDR

biosdma	equ	scratch	;Location of DMA Address

*****************************************************************
*								*
*  Disk Serial, MPU Serial, Quad I/O, and Modem Equates		*
*								*
*****************************************************************

;  Disk Serial -- Serial Channel on Disk Controller Board (DCE)
;    Baud Rate is set at 19,200 Baud in Hardware (DIP Switches)
ustat	equ	0E3F9H	;USART Status Address
ostat	equ	8	;Output Status Bit (TBE)
istat	equ	4	;Input Status Bit (RDA)

;  MPU Serial -- Serial Channel on CCS Z80 MPU Board (DCE)
mpubase	equ	20H	;Base address of 8250 on CCS Z80 MPU Board
mpudata	equ	mpubase		;Data I/O Registers
mpudll	equ	mpubase		;Divisor Latch Low
mpudlh	equ	mpubase+1	;Divisor Latch High
mpuier	equ	mpubase+1	;Interrupt Enable Register
mpulcr	equ	mpubase+3	;Line Control Register
mpupcr	equ	mpubase+4	;Peripheral Control Register
mpustat	equ	mpubase+5	;Line Status Register
mpupsr	equ	mpubase+6	;Peripheral Status Register

;  MPU Serial RDA and TBE
mpurda	equ	1	; Data Available Bit (RDA)
mputbe	equ	40h	; Transmit Buffer Empty Bit (TBE)

;  MPU Serial Baud Rate Values
bm00050	equ	2304	;    50   Baud
bm00075	equ	1536	;    75   Baud
bm00110	equ	1047	;   110   Baud
bm00134	equ	857	;   134.5 Baud
bm00150	equ	768	;   150   Baud
bm00300	equ	384	;   300   Baud
bm00600	equ	192	;   600   Baud
bm01200	equ	96	;  1200   Baud
bm01800	equ	64	;  1800   Baud
bm02000	equ	58	;  2000   Baud
bm02400	equ	48	;  2400   Baud
bm03600	equ	32	;  3600   Baud
bm04800	equ	24	;  4800   Baud
bm07200	equ	16	;  7200   Baud
bm09600	equ	12	;  9600   Baud
bm19200	equ	6	; 19200   Baud
bm38400	equ	3	; 38400   Baud
bm56000	equ	2	; 56000   Baud

;  MPU Serial Channel Baud Rate
mpbrate	equ	bm19200	; 19,200 Baud

;  Quad I/O Ports
qbase	equ	80h	; Base address of Quad RS-232 I/O Board
q0data	equ	qbase		; USART 0 Data Port (DTE)
q0stat	equ	qbase+1		; USART 0 Status Port
q1data	equ	qbase+2		; USART 1 Data Port (DTE)
q1stat	equ	qbase+3		; USART 1 Status Port
q2data	equ	qbase+4		; USART 2 Data Port (DTE)
q2stat	equ	qbase+5		; USART 2 Status Port
q3data	equ	qbase+6		; USART 3 Data Port (DCE)
q3stat	equ	qbase+7		; USART 3 Status Port
q0baud	equ	qbase+8		; USART 0 Baud Rate Port
q1baud	equ	qbase+9		; USART 1 Baud Rate Port
q2baud	equ	qbase+10	; USART 2 Baud Rate Port
q3baud	equ	qbase+11	; USART 3 Baud Rate Port

;  Quad I/O RDA and TBE
qrda	equ	2	; Read Data Available Bit (RDA)
qtbe	equ	1	; Transmit Buffer Empty Bit (TBE)

*************************************
*  Equate Values for PMMI as Modem  *
*************************************
*  Modem Ports (Special -- 300 or 600 Baud for PMMI)
*mods	equ	0E0H	; Modem Status Byte
*modd	equ	0E1H	; Modem Data Byte
*
*  Modem RDA and TBE
*mrda	equ	2	; Read Data Available Bit (RDA)
*mtbe	equ	1	; Transmit Buffer Empty Bit (TBE)
*************************************

;  Modem Ports set to QUAD I/O Port 2
mods	equ	q2stat	; Modem Status Port
modd	equ	q2data	; Modem Data Port

;  Modem RDA and TBE
mrda	equ	qrda
mtbe	equ	qtbe

;  Baud Rate Values
b00050	equ	0	;    50   Baud
b00075	equ	1	;    75   Baud
b00110	equ	2	;   110   Baud
b00134	equ	3	;   134.5 Baud
b00150	equ	4	;   150   Baud
b00300	equ	5	;   300   Baud
b00600	equ	6	;   600   Baud
b01200	equ	7	;  1200   Baud
b01800	equ	8	;  1800   Baud
b02000	equ	9	;  2000   Baud
b02400	equ	10	;  2400   Baud
b03600	equ	11	;  3600   Baud
b04800	equ	12	;  4800   Baud
b07200	equ	13	;  7200   Baud
b09600	equ	14	;  9600   Baud
b19200	equ	15	; 19200   Baud


*****************************************************************
*								*
*  Baud Rates for Quad I/O Devices				*
*								*
*****************************************************************

q0brate	equ	b09600	;  9600 Baud for Intersystem
q1brate	equ	b01200	;  1200 Baud for Clock
q2brate	equ	b00300	;   300 Baud for Smartmodem
q3brate	equ	b00300	;   300 Baud for Printer


*****************************************************************
*								*
*  Miscellaneous Constants					*
*								*
*****************************************************************
XON	equ	11h	;X-ON
XOFF	equ	13h	;X-OFF
djram	equ	0e400h	;Base of DJ RAM
djcin	equ	djram+3	;DJ Console Input
djcout	equ	djram+6	;DJ Console Output

*****************************************************************
*								*
* The following are the Z80 Macro Definitions which are used to	*
* define the Z80 Mnemonics used to implement the Z80 instruction*
* set extensions employed in CBIOSZ.				*
*								*
*****************************************************************
;
; MACROS TO PROVIDE Z80 EXTENSIONS
;   MACROS INCLUDE:
;
$-MACRO 		;FIRST TURN OFF THE EXPANSIONS
;
;	JR	- JUMP RELATIVE
;	JRC	- JUMP RELATIVE IF CARRY
;	JRNC	- JUMP RELATIVE IF NO CARRY
;	JRZ	- JUMP RELATIVE IF ZERO
;	JRNZ	- JUMP RELATIVE IF NO ZERO
;	DJNZ	- DECREMENT B AND JUMP RELATIVE IF NO ZERO
;	LDIR	- MOV @HL TO @DE FOR COUNT IN BC
;	LXXD	- LOAD DOUBLE REG DIRECT
;	SXXD	- STORE DOUBLE REG DIRECT
;
;
;
;	@GENDD MACRO USED FOR CHECKING AND GENERATING
;	8-BIT JUMP RELATIVE DISPLACEMENTS
;
@GENDD	MACRO	?DD	;;USED FOR CHECKING RANGE OF 8-BIT DISPLACEMENTS
	IF (?DD GT 7FH) AND (?DD LT 0FF80H)
	DB	100H	;Displacement Range Error on Jump Relative
	ELSE
	DB	?DD
	ENDIF
	ENDM
;
; Z80 MACRO EXTENSIONS
;
JR	MACRO	?N
	DB	18H
	@GENDD	?N-$-1
	ENDM
;
JRC	MACRO	?N
	DB	38H
	@GENDD	?N-$-1
	ENDM
;
JRNC	MACRO	?N
	DB	30H
	@GENDD	?N-$-1
	ENDM
;
JRZ	MACRO	?N
	DB	28H
	@GENDD	?N-$-1
	ENDM
;
JRNZ	MACRO	?N
	DB	20H
	@GENDD	?N-$-1
	ENDM
;
DJNZ	MACRO	?N
	DB	10H
	@GENDD	?N-$-1
	ENDM
;
LDIR	MACRO
	DB	0EDH,0B0H
	ENDM
;
LDED	MACRO	?N
	DB	0EDH,05BH
	DW	?N
	ENDM
;
LBCD	MACRO	?N
	DB	0EDH,4BH
	DW	?N
	ENDM
;
SDED	MACRO	?N
	DB	0EDH,53H
	DW	?N
	ENDM
;
SBCD	MACRO	?N
	DB	0EDH,43H
	DW	?N
	ENDM
;
; END OF Z80 MACRO EXTENSIONS
;


*****************************************************************
*								*
* Terminal driver routines. Iobyte is initialized by the cold	*
* boot routine, to modify, change the "intioby" equate.	The	*
* I/O routines that follow all work exactly the same way. Using	*
* iobyte, they obtain the address to jump to in order to execute*
* the desired function. There is a table with four entries for	*
* each of the possible assignments for each device. To modify	*
* the I/O routines for a different I/O configuration, just	*
* change the entries in the tables.				*
*								*
*****************************************************************

	org	iobase		;Base Address of I/O Drivers
offset	equ	100h-iobase	;Offset for load via DDT or ZSID

	jmp	status		;Internal Status Routine
	jmp	select		;Device Select Routine
	jmp	namer		;Device Name Routine

	jmp	tinit		;Initialize Terminal

	jmp	const		;Console Input Status
	jmp	conin		;Console Input Char
	jmp	conout		;Console Output Char

	jmp	list		;List Output Char

	jmp	punch		;Punch Output Char

	jmp	reader		;Reader Input Char

	jmp	listst		;List Output Status

	jmp	newio		;New I/O Driver Installation Routine

	jmp	copen		;Open CON: Disk File
	jmp	cclose		;Close CON: Disk File

	jmp	lopen		;Open LST: Disk File
	jmp	lclose		;Close LST: Disk File


*****************************************************************
*								*
*  I/O Driver Support Specification Tables			*
*								*
*****************************************************************

*
* Device Counts
*	First Byte is Number of Devices, 2nd Byte is Selected Device
*
cnttbl:
	db	5,(intioby AND 7)		;CON:
	db	2,(intioby AND 08h) SHR 3	;RDR:
	db	2,(intioby AND 10h) SHR 4	;PUN:
	db	6,(intioby AND 0E0h) SHR 5	;LST:

*
* Configuration Table
*	First Byte is Rotate Count, 2nd Byte is Mask
*
cfgtbl:
	db	0,111$1$1$000b	;No Rotate, Mask Out 3 LSB
	db	3,111$1$0$111b	;3 Rotates, Mask Out Bit 3
	db	4,111$0$1$111b	;4 Rotates, Mask Out Bit 4
	db	5,000$1$1$111b	;5 Rotates, Mask Out 3 MSB

*****************************************************************
*								*
* status: return information on devices supported by this	*
*	I/O Package.  On exit, HL points to a logical device	*
*	table which is structured as follows:			*
*		Device	Count Byte  Current Assignment Byte	*
*		------	----------  -----------------------	*
*		 CON:	     0			1		*
*		 RDR:	     2			3		*
*		 PUN:	     4			5		*
*		 LST:	     6			7		*
*								*
*	If error or no I/O support, return with Zero Flag Set.	*
*	Also, if no error, A=Driver Module Number		*
*								*
*****************************************************************
status:
	lxi	h,cnttbl	;point to table
	mvi	a,81H		;Module 1 (SYSIO) with Disk Output
	ora	a		;Set Flags
	ret


*****************************************************************
*								*
* select: select devices indicated by B and C.  B is the number	*
*	of the logical device, where CON:=0, RDR:=1, PUN:=2,	*
*	LST:=3, and C is the desired device (range 0 to dev-1).	*
*	Return with Zero Flag Set if Error.			*
*								*
*****************************************************************
ranger:
	lxi	h,cnttbl-2	;check for error
	inr	b	;range of 1 to 4
	mov	a,b	;Value in A
	cpi	5	;B out of range?
	jnc	rangerr
	push	b	;save params
rang:
	inx	h	;pt to next
	inx	h
	djnz	rang
	mov	b,m	;get count in b
	mov	a,c	;get selected device number
	cmp	b	;compare (C must be less than B)
	pop	b	;get params
	jrnc	rangerr	;range error if C >= B
rangok:
	mvi	a,0ffh	;OK
	ora	a	;set flags
	ret
rangerr:
	xra	a	;not OK
	ret
select:
	call	ranger	;check for range error
	rz		;abort if error
	inx	h	;pt to current entry number
	mov	m,c	;save selected number there
	lxi	h,cfgtbl-2	;pt to configuration table
sel2:
	inx	h	;Pt to Entry in Configuration Table
	inx	h
	djnz	sel2
	mov	b,m	;Get Rotate Count
	inx	h	;Pt to Select Mask
	mov	d,m	;Get Select Mask
	mov	a,b	;Any Rotation to do?
	ora	a
	jz	sel4
	mov	a,c	;Get Selected Number
sel3:
	rlc		;Rotate Left 1 Bit
	djnz	sel3
	mov	c,a	;Place Bit Pattern Back in C
sel4:
	lda	iobyte	;get I/O byte
	ana	d	;mask out old selection
	ora	c	;mask in new selection
	sta	iobyte	;put I/O byte
	jr	rangok	;range OK

*****************************************************************
*								*
* namer: return text string of physical device.  Logical device	*
*	number is in B and physical selection is in C.		*
*	HL is returned pointing to the first character of the	*
*	string.  The strings are structured to begin with a	*
*	device name followed by a space and then a description	*
*	string which is terminated by a binary 0.		*
*								*
*	Return with Zero Flag Set if error.			*
*								*
*****************************************************************
namer:
	call	ranger	;check for range error
	rz		;return if so
	lxi	h,namptbl-2	;pt to name ptr table
	call	namsel	;select ptr table entry
	mov	b,c	;physical selection number in B now
	inr	b	;Add 1 for Initial Increment
	call	namsel	;point to string
	jr	rangok	;return with HL pointing and range OK
;
;  Select entry B in table pted to by HL; this entry is itself a pointer,
;  and return with it in HL
;
namsel:
	inx	h	;pt to next entry
	inx	h
	djnz	namsel
	mov	a,m	;get low
	inx	h
	mov	h,m	;get high
	mov	l,a	;HL now points to entry
	ret

*****************************************************************
*								*
* const: get the status for the currently assigned console.	*
*	 The I/O Byte is used to select the device.		*
*								*
*****************************************************************
const:
	lxi	h,cstble	;Beginning of jump table
conmask:
	lxi	d,cfgtbl	;Pt to First Entry in Config Table
	jr	seldev		;Select correct jump

*****************************************************************
*								*
* conin: input a character from the currently assigned console.	*
*	 The I/O Byte is used to select the device.		*
*								*
*****************************************************************
conin:
	lxi	h,citble	;Beginning of character input table
	jr	conmask		;Get Console Mask

*****************************************************************
*								*
* conout: output the character in C to the currently assigned	*
*	  console.  The I/O Byte is used to select the device.	*
*								*
*****************************************************************
conout:
	lxi	h,cotble	;Beginning of the character out table
	jr	conmask		;Get Console Mask

*****************************************************************
*								*
* csreader: get the status of the currently assigned reader.	*
*	    The I/O Byte is used to select the device.		*
*								*
*****************************************************************
csreadr:
	lxi	h,csrtble	;Beginning of reader status table
rdrmask:
	lxi	d,cfgtbl+2	;Pt to 2nd Entry in Config Table
	jr	seldev

*****************************************************************
*								*
* reader: input a character from the currently assigned reader.	*
*	  The I/O Byte is used to select the device.		*
*								*
*****************************************************************
reader:
	lxi	h,rtble		;Beginning of reader input table
	jr	rdrmask		;Get the Mask and Go

*****************************************************************
*								*
* Entry at seldev will form an offset into the table pointed	*
* to by H&L and then pick up the address and jump there.	*
* The configuration of the physical device assignments is	*
* pointed to by D&E (cfgtbl entry).				*
*								*
*****************************************************************
seldev:
	push	b		;Save Possible Char in C
	ldax	d		;Get Rotate Count
	mov	b,a		;... in B
	inx	d		;Pt to Mask
	ldax	d		;Get Mask
	cma			;Flip Bits
	mov	c,a		;... in C
	lda	iobyte		;Get I/O Byte
	ana	c		;Mask Out Selection
	inr	b		;Increment Rotate Count
seld1:
	dcr	b		;Count down
	jrz	seld2
	rrc			;Rotate Right one Bit
	jr	seld1
seld2:
	rlc			;Double Number for Table Offset
	mvi	d,0		;Form offset
	mov	e,a
	dad	d		;Add offset
	mov	a,m		;Pick up low byte
	inx	h
	mov	h,m		;Pick up high byte
	mov	l,a		;Form address
	pop	b		;Get Possible Char in C
	pchl			;Go there !

*****************************************************************
*								*
* punch: output char in C to the currently assigned punch	*
*	 device.  The I/O Byte is used to select the device.	*
*								*
*****************************************************************
punch:
	lxi	h,ptble		;Beginning of punch table
	lxi	d,cfgtbl+4	;Get Mask
	jr	seldev		;Select Device and Go

*****************************************************************
*								*
* list: output char in C to the currently assigned list device.	*
*	The I/O Byte is used to select the device.		*
*								*
*****************************************************************
list:
	lxi	h,ltble		;Beginning of the list device routines
lstmask:
	lxi	d,cfgtbl+6	;Get Mask
	jr	seldev		;Select Device and Go

*****************************************************************
*								*
* Listst: get the output status of the currently assigned list	*
*	  device.  The I/O Byte is used to select the device.	*
*								*
*****************************************************************
listst:
	lxi	h,lstble	;Beginning of the list device status
	jr	lstmask		;Mask and Go

*****************************************************************
*								*
* If customizing I/O routines is being performed, the tables	*
* below should be modified to reflect the changes. All I/O	*
* devices are decoded out of iobyte and the jump is taken from	*
* the following tables.						*
*								*
*****************************************************************

*
* name text tables
*
namptbl:
	dw	conname-2	;CON:
	dw	rdrname-2	;RDR:
	dw	punname-2	;PUN:
	dw	lstname-2	;LST:

conname:
	dw	namtty	;TTY
	dw	namcrt	;CRT
	dw	namusr	;CRT and Modem in Parallel
	dw	namusr1	;CRT Input and CRT/Remote Computer Output
	dw	namdsk1	;CRT and Disk

lstname:
	dw	namtty	;TTY
	dw	namcrt	;CRT
	dw	namrem	;Remote Computer
	dw	nammod	;Modem
	dw	namdsk2	;TTY and Disk
	dw	namdsk3	;Disk

rdrname:
	dw	nammod	;Modem
	dw	namclk	;Clock

punname:
	dw	nammod	;Modem
	dw	namclk	;Clock

namdsk1:
	db	'CRTDISK Televideo 950 CRT with Disk File Output',0
namdsk2:
	db	'TTYDISK Model 43 Printing Terminal with Disk File Output',0
namdsk3:
	db	'DISK Disk File Output Only',0
namtty:
	db	'TTY Model 43 Printing Terminal',0
namcrt:
	db	'CRT Televideo 950 CRT',0
namusr:
	db	'CRTMOD Televideo 950 CRT and Modem in Parallel',0
namusr1:
	db	'CRTREM Televideo 950 CRT Input and CRT/Remote Output',0
namrem:
	db	'REMOTE Remote Computer',0
nammod:
	db	'MODEM DC Hayes Smartmodem',0
namclk:
	db	'CLOCK DC Hayes Chronograph',0

*
* console input table
*
citble:
	dw	citty		;Input from tty (000) 
	dw	cicrt		;Input from crt (001) 
	dw	ciusr		;Input from crt and modem (010) 
	dw	cicrt		;Input from crt (011)
	dw	cicrt		;Input from crt (100)

*
* console output table
*
cotble:
	dw	cotty		;Output to tty (000) 
	dw	cocrt		;Output to crt (001)
	dw	cousr		;Output to crt and modem (010)
	dw	cousr1		;Output to crt and remote system (011) 
	dw	codsk1		;Output to crt and disk file (100)

*
* list device table
*
ltble:
	dw	cotty		;Output to tty (000) 
	dw	cocrt		;Output to crt (001)
	dw	corem		;Output to remote system (010)
	dw	comod		;Output to modem (011)
	dw	codsk2		;Output to tty and disk file (100)
	dw	codsk3		;Output to disk file (101)

*
* punch device table
*

ptble:
	dw	comod		;Output to modem (0)
	dw	coclk		;Output to clock (1) 

*
* reader device table
*
rtble:
	dw	cimod		;Input from modem (0)
	dw	ciclk		;Input from clock (1)

*
* console status table
*
cstble:
	dw	cstty		;Status of tty (000)
	dw	cscrt		;Status from crt (001)
	dw	csusr		;Status from crt and modem (010)
	dw	cscrt		;Status from crt (011)
	dw	cscrt		;Status from crt (100)

*
* status from reader device
*
csrtble:
	dw	csmod		;Status from modem (0)
	dw	csclk		;Status from clock (1)

*
* Status from list device
*
lstble:
	dw	costty		;Status from tty (000)
	dw	coscrt		;Status from crt (001)
	dw	cosrem		;Status from remote system (010)
	dw	cosmod		;Status from modem (011)
	dw	costty		;Status from tty (100)
	dw	ready		;Status of disk file is always ready (101)


*****************************************************************
*								*
* Tinit can be modified for different I/O setups.		*
*								*
*****************************************************************
tinit:				;Initialize the terminal routine

;  Initialize I/O Byte
	mvi	a,intioby	;Initialize IOBYTE
	sta	iobyte

;  Initialize MPU Serial I/O Channel Characteristics and Baud Rate
	mvi	a,10$00$00$11b	;Access Divisor:
				;  10 -- Set divisor latch, clear break
				;  00 -- 0 parity bit, even parity (N/A)
				;  00 -- disable parity, 1 stop bit
				;  11 -- 8 Data Bits
	out	mpulcr		;To Line Control Register
	lxi	h,mpbrate	;HL = MPU Channel Baud Rate
	mov	a,l		;Set Low-Byte of Baud Rate
	out	mpudll		;To Divisor Latch Low
	mov	a,h		;Set High-Byte of Baud Rate
	out	mpudlh		;To Divisor Latch High
	mvi	a,00$00$01$11b	;Reset Divisor Access and Set Characteristics:
				;  00 -- Clear divisor latch, clear break
				;  00 -- 0 parity bit, even parity (N/A)
				;  01 -- disable parity, 2 stop bits
				;  11 -- 8 Data Bits
	out	mpulcr		;To Line Control Register
	xra	a		;A=0
	out	mpuier		;Disable All Interrupts in Interrupt Register
	out	mpustat		;Clear All Error Flags in Line Status Register
	mvi	a,0000$1111b	;3 Zeroes, No Loop, 1, Set RLSD, CTS, DSR
	out	mpupcr		;To Peripheral Control Register

;  Initialize Quad I/O Channel Characteristics
	mvi	a,10$11$01$11b	;General-Purpose Reset:
				;  10 -- 1 1/2 Stop Bits
				;  11 -- Even Parity, Enable Parity
				;  01 -- 6 Bits/Char
				;  11 -- 64x Baud Rate
	call	setquad		;Set All 4 Quad I/O Ports
	mvi	a,01$11$01$11b	;General-Purpose Reset:
				;  01 -- Disable Hunt, Internal Reset
				;  11 -- RTS High, Error Reset
				;  01 -- No Break, Enable RxRDY
				;  11 -- NOT DTR High, Enable TxEN
	call	setquad		;Set All 4 Quad I/O Ports
	mvi	a,11$00$11$10b	;Characteristics Set for All:
				;  11 -- 2 Stop Bits
				;  00 -- No Parity
				;  11 -- 8 Bits/Char
				;  10 -- 16x Baud Rate
	call	setquad		;Set All 4 Quad I/O Ports
	mvi	a,00$11$01$11b	;Characteristics Set for All:
				;  00 -- Disable Hunt, No Internal Reset
				;  11 -- RTS High, Error Reset
				;  01 -- No Break, Enable RxRDY
				;  11 -- NOT DTR High, Enable TxEN
	call	setquad		;Set All 4 Quad I/O Ports

;  Initialize Quad I/O Baud Rates
	mvi	a,q0brate	;Set USART 0 Baud Rate
	out	q0baud
	mvi	a,q1brate	;Set USART 1 Baud Rate
	out	q1baud
	mvi	a,q2brate	;Set USART 2 Baud Rate
	out	q2baud
	mvi	a,q3brate	;Set USART 3 Baud Rate
	out	q3baud

;  Clear Garbage Char from CRT
	call	cscrt		;Gobble up unwanted char
	ora	a		;A=0 if none
	cnz	cicrt		;Grab character
	ret

;  Set All Quad I/O Control Ports
setquad:
	out	q0stat		;USART 0
	out	q1stat		;USART 1
	out	q2stat		;USART 2
	out	q3stat		;USART 3
	xthl			;Long Delay
	xthl
	ret


*****************************************************************
*								*
*  NEWIO -- Set UC1: Device to the Device Drivers whose Jump	*
*	Table is Pointed to by HL				*
*								*
*  This Jump Table is structured as follows:			*
*	JMP ISTAT	<-- Input Status (0=No Char, 0FFH=Char)	*
*	JMP INPUT	<-- Input Character			*
*	JMP OUTPUT	<-- Output Character in C		*
*								*
*  The Base Address of this Jump Table (JBASE) is passed to	*
*	NEWIO in the HL Register Pair.				*
*								*
*****************************************************************
newio:
	shld	cstble+6	;Set UC1: Input Status
	lxi	d,3		;Prepare for offset to next jump
	dad	d		;HL points to next jump
	shld	citble+6	;Set UC1: Input Character
	dad	d		;HL points to next jump
	shld	cotble+6	;Set UC1: Output Character
	ret


*****************************************************************
*								*
*  Input Status, Input Character, and Output Character		*
*	Subroutines for CP/M					*
*								*
*****************************************************************
*								*
*  Input Status --						*
*	These routines return 0 in the A Register if no input	*
* data is available, 0FFH if input data is available.		*
*								*
*  Input Character --						*
*	These routines return the character (byte) in the A	*
* Register.  MSB is masked off.					*
*								*
*  Output Character --						*
*	These routines output the character (byte) in the C	*
* Register.							*
*								*
*****************************************************************

*****************************************************************
*								*
*  CRT Input Status, Input Character, and Output Character	*
*								*
*****************************************************************

cscrt	equ	$	;CRT Input Status
	lda	ustat	;Get Status
	cma		;Inverted Logic
	ani	istat	;Mask for input status and fall thru to 'STAT'
	jr	stat	;Set Flags

coscrt	equ	$	;CRT Output Status
	lda	ustat	;Get USART status
	cma		;Inverted Logic
	ani	ostat	;Mask for output status
	jr	stat	;Return

cicrt	equ	$	;CRT Input
	jmp	djcin	;Get char

cocrt	equ	$	;CRT Output
	jmp	djcout	;Put char

*****************************************************************
*								*
*  Modem Input Status, Input Character, and Output Character	*
*								*
*****************************************************************

csmod	equ	$	;Modem Input Status
	in	mods
	ani	mrda	;Data available?
	jr	stat

cosmod	equ	$	;Modem Output Status
	in	mods	;Get status
	ani	mtbe	;TBE?
	jr	stat

cimod	equ	$	;Modem Input Character
	call	csmod	;RDA? 
	jrz	cimod
	in	modd	;Get data
	ani	7fh	;Mask
	ret

comod	equ	$	;Modem Output
	call	cosmod	;TBE?
	jrz	comod
	mov	a,c	;Get char
	out	modd	;Put data
	ret

*****************************************************************
*								*
*  Clock Input Status, Input Character, and Output Character	*
*								*
*****************************************************************

csclk	equ	$	;TTY Input Status
	in	q1stat	;Get Status
	ani	qrda	;Data available?
	jr	stat

cosclk	equ	$	;TTY Output Status
	in	q1stat	;Get Status
	ani	qtbe	;TBE?
	jr	stat

ciclk	equ	$	;TTY Input Character
	call	csclk	;RDA?
	jrz	ciclk
	in	q1data	;Get data
	ani	7fh	;Mask
	ret

coclk	equ	$	;TTY Output Character
	call	cosclk	;TBE?
	jrz	coclk
	mov	a,c	;Get data
	out	q1data	;Put data
	ret

*****************************************************************
*								*
* This is a common return point to correctly set the return 	*
*    status flags; it is centrally located for the jump		*
*    relative instructions					*
*								*
*****************************************************************
stat:
	rz		;Nothing found
ready:
	mvi	a,0ffh	;Set A for negative status
	ret

*****************************************************************
*								*
*  TTY Input Status, Input Character, and Output Character	*
*								*
*****************************************************************

cstty	equ	$	;TTY Input Status
	in	q3stat	;Get Status
	ani	qrda	;Data available?
	jr	stat

costty	equ	$	;TTY Output Status
	in	q3stat	;Get Status
	ani	qtbe	;TBE?
	jr	stat

citty	equ	$	;TTY Input Character
	call	cstty	;RDA?
	jrz	citty
	in	q3data	;Get data
	ani	7fh	;Mask
	ret

cotty	equ	$	;TTY Output Character
	call	costty	;TBE?
	jrz	cotty
	mov	a,c	;Get data
	out	q3data	;Put data
	ret

*****************************************************************
*								*
*  Remote System Input Status, Input Character, and Output	*
*	Character						*
*								*
*****************************************************************

csrem	equ	$	;TTY Input Status
	in	q0stat	;Get Status
	ani	qrda	;Data available?
	jr	stat

cosrem	equ	$	;TTY Output Status
	in	q0stat	;Get Status
	ani	qtbe	;TBE?
	jr	stat

cirem	equ	$	;TTY Input Character
	call	csrem	;RDA?
	jrz	cirem
	in	q0data	;Get data
	ani	7fh	;Mask
	ret

corem	equ	$	;TTY Output Character
	call	coxoff	;Check for XOFF and process
	call	cosrem	;TBE?
	jrz	corem
	mov	a,c	;Get data
	out	q0data	;Put data
	ret
coxoff	equ	$	;Remote XOFF Check and Processing
	call	csrem	;Input Char from LST: Device?
	rz		;Zero if none
	call	cirem	;Get Char
	cpi	XOFF	;XOFF?
	rnz		;Return if not
	call	cirem	;Wait for Any Other Char
	ret

*****************************************************************
*								*
*  User-Defined (CRT and Modem) Input Status, Input Character,	*
*	and Output Character					*
*								*
*****************************************************************

csusr	equ	$	;User (CRT and Modem) Input Status
	call	cscrt	;Input from CRT?
	rnz		;Char found
	call	csmod	;Input from Modem?
	ret

cosusr	equ	cosmod	;Output status same as modem since modem is slower

ciusr	equ	$	;Modem/CRT Input Combination
	call	cscrt	;Input from CRT?
	jnz	cicrt	;Get char from CRT
	call	csmod	;Input from Modem?
	jnz	cimod	;Get char from Modem
	jr	ciusr	;Continue

cousr	equ	$	;Modem/CRT Output Combination
	call	comod	;Output to Modem
	jmp	cocrt	;Output to CRT

ciusr1	equ	$	;Modem/CRT Input w/CRT Output Combination
	call	ciusr	;Get char
	push	psw	;Save char in A
	mov	c,a	;Char in C
	call	cocrt	;Output to CRT
	pop	psw	;Restore char in A
	ret

cousr1	equ	$	;Remote System/CRT Output Combination
	call	corem	;Output to Remote System
	jmp	cocrt	;Output to CRT


*****************************************************************
*								*
*  Disk System Output Character					*
*	CODSK1 - CRT and Disk					*
*	CODSK2 - TTY and Disk					*
*								*
*****************************************************************

codsk1	equ	$	;CRT and Disk Output Character
	call	cocrt	;Send to CRT
	lxi	d,dsk1	;Disk 1 Parameters
	lda	clast	;Check last char for double <CR>
	cpi	cr
	jrz	codsk1a
	mov	a,c	;Set new last char
	sta	clast
	jr	codsk	;Output to Disk
codsk1a:
	mov	a,c	;Is this a <CR> also?
	cpi	cr
	rz
	sta	clast	;No, so set as new last and continue
	jr	codsk

codsk2	equ	$	;TTY and Disk Output Character
	call	cotty	;Send to TTY
	lxi	d,dsk2	;Disk 2 Parameters
	lda	tlast	;Check last char for double <CR>
	cpi	cr
	jrz	codsk2a
	mov	a,c	;Set new last char
	sta	tlast
	jr	codsk	;Output to Disk
codsk2a:
	mov	a,c	;Is this a <CR> also?
	cpi	cr
	rz
	sta	tlast	;No, so set as new last and continue
	jr	codsk

codsk3	equ	$	;Disk Output as Printer
	lxi	d,dsk2	;Disk 2 Parameters

codsk	equ	$	;Disk Output Character
	ldax	d	;Get Open Flag
	ora	a	;File Open?
	rz		;Done if Not
	lxi	h,0	;Save User Stack
	dad	sp
	shld	stack
	lxi	sp,stack	;Set Driver Stack
	push	b	;Save char in C
	push	d	;Save ptr to File Spec Block
	lxi	d,stksav	;Save User's Stack in case I'm in the BDOS
	lxi	b,stksiz	;Arbitrary
	ldir		;Copy
	pop	h	;Get HL as ptr to File Spec Block
	pop	b	;Get char in C
	push	h	;Save ptr to File Spec Block
	inx	h	;Pt to Buffer Address
	mov	e,m	;Get address
	inx	h
	mov	d,m
	xchg		;HL pts to next location
	mov	m,c	;Store Character
	inx	h	;Pt to following location
	xchg		;... in DE
	mov	m,d	;Store New Address
	dcx	h
	mov	m,e
	inx	h	;Pt to char count
	inx	h
	inr	m	;Increment char count
	mvi	a,128	;Buffer full?
	cmp	m	;It is if 128 characters are stored in it
	pop	h	;Get Ptr to File Spec Block
	jrnz	done	;Done if less than 128
	call	initfsb	;Init File Spec Block
	call	write	;Write Block to Disk
done:
	lhld	stack	;Restore User Stack
	sphl
	lxi	d,stksav	;Pt to User's Original Stack
	xchg		;HL is where user's stack once was
	lxi	b,stksiz	;Size of my Stack Save Area
	ldir		;Restore Original Stack
	ret
;
;  COPEN -- Open Console File for Output
;  LOPEN -- Open Printer File for Output
;
copen:
	lxi	d,fcb1	;Define Console File
	lxi	b,12	;12 bytes
	ldir		;Copy
	lxi	h,dsk1	;Pt to File Spec Block
	xra	a	;Set No Last Char
	sta	clast
	jr	open
lopen:
	lxi	d,fcb2	;Define Printer File
	lxi	b,12	;12 bytes
	ldir		;Copy
	lxi	h,dsk2	;Pt to File Spec Block
	xra	a	;Set No Last Char
	sta	tlast
;
;  Open Disk File for Output
;
open:
	push	h	;Save ptr to File Spec Block
	call	savud	;Save Current User and Disk as Home
	lda	disk	;Set Home Base User and Disk
	sta	hdisk
	lda	user
	sta	huser
	call	initfsb	;Init File Spec Block pted to by HL
	call	savenv	;Save Environment
	call	initfcb	;Clear FCB
	mvi	c,19	;Delete File
	push	d	;Save DE
	call	entry
	pop	d	;Get DE
	call	initfcb
	mvi	c,22	;Create File
	call	entry
	mvi	c,'M'	;Make error possible
	call	errchk	;Perform Error Check
	call	getenv	;Restore Environment
	pop	h	;Get Ptr to File Spec Block
	ret
;
;  Init File Spec Block pted to by HL
;	On input, HL pts to File Spec Block
;	On output, File Spec Block is initialized and HL pts to DMA Address
;
initfsb:
	mvi	m,0ffh	;Set File Open
	inx	h	;Skip Buffer Address
	mov	d,h	;DE=ptr to buffer address
	mov	e,l
	inx	h
	inx	h	;Pt to Byte Count
	mvi	m,0	;Init it to Zero
	inx	h	;Pt to DMA Address
	mov	a,m	;Set Buffer Address
	stax	d
	inx	h
	inx	d
	mov	a,m
	stax	d
	dcx	h	;Pt to DMA Address
	ret
;
;  Close Disk Files
;	CCLOSE -- CON file (DSK1)
;	LCLOSE -- LST file (DSK2)
;
cclose:
	lxi	h,dsk1	;Pt to File Spec Block
	jr	close
lclose:
	lxi	h,dsk2	;Pt to File Spec Block
close:
	push	h	;Save ptr
	inx	h	;Pt to Buffer Address
	mov	e,m	;Get it
	inx	h
	mov	d,m
	inx	h	;Pt to Byte Count
	mov	b,m	;Get it
	xchg		;HL pts to Buffer
close1:
	mvi	m,1AH	;EOF Char
	inx	h
	inr	b	;Buffer full?
	mvi	a,128	;Check
	cmp	b
	jrnz	close1
	pop	h	;Get ptr
	mvi	m,0	;Set Not Open
	inx	h	;Pt to DMA Address
	inx	h
	inx	h
	inx	h
	push	h	;Save ptr
	call	write	;Write Last Block
	pop	h	;Get ptr
	call	savenv	;Save User Environment
	mvi	c,16	;Close Disk File
	call	entry
	mvi	c,'C'	;Close error possible
	call	errchk	;Perform error check
	jmp	getenv	;Restore User Environment

;
;  Init FCB Pted to by DE and do not affect DE
;
initfcb:
	push	d	;Save Ptr
	xchg		;Ptr in HL
	mvi	m,0	;Clear Disk
	lxi	d,12	;Pt to EX Field
	dad	d
	mvi	b,24	;24 more bytes
ifcb:
	mvi	m,0	;Zero Fill
	inx	h	;Pt to Next
	djnz	ifcb
	pop	d	;Get Ptr
	ret

;
;  Write Block to File whose DMA Address is pointed to by HL
;
write:
	call	savenv	;Save Environment
	mvi	c,21	;Write Block
	call	entry
	mvi	c,'W'	;Write error possible
	call	errchk	;Perform error check
;
;  Get User Environment
;	This routine restores the User's User/Disk Flag and DMA Address
;
getenv:
	call	getud	;Reset User/Disk
	lhld	usrdma	;Reset User's DMA Address
	xchg
	mvi	c,26	;Set DMA
	call	entry
	ret
;
;  Save User Environment
;	This routine saves his DMA Address and User/Disk Flag
;	This routine sets the Disk I/O User/Disk Flag and DMA Address
;	   and returns with DE containing the address of the FCB
;	On input, HL pts to DMA Address entry in File Spec Block
;
savenv:
	call	savdma	;Save User's DMA Address
	call	savud	;Save Current User/Disk
	call	setfud	;Set User/Disk for Output File
	mov	e,m
	inx	h
	mov	d,m
	inx	h	;Pt to FCB
	push	h	;Save ptr
	mvi	c,26	;Set DMA
	call	entry
	pop	h	;Get ptr
	mov	e,m	;Get FCB Address in DE
	inx	h
	mov	d,m
	ret
;
;  Save Current User/Disk
;
savud:
	push	h	;Save Regs
	push	d
	push	b
	mvi	c,25	;Get Disk
	call	entry
	sta	disk
	mvi	c,32	;Get User
	mvi	e,0ffh
	call	entry
	sta	user
rexit:
	pop	b	;Restore Regs
	pop	d
	pop	h
	ret
;
;  Restore Current User/Disk
;
getud:
	push	h	;Save Regs
	push	d
	push	b
	lda	disk	;Select Disk
	mov	e,a
	mvi	c,14
	call	entry
	lda	user	;Select User
	mov	e,a
	mvi	c,32
	call	entry
	jr	rexit	;Exit and Restore Regs
;
;  Set User/Disk for File Output
;
setfud:
	push	h	;Save Regs
	push	d
	push	b
	lda	hdisk	;Select Home Disk
	mov	e,a
	mvi	c,14
	call	entry
	lda	huser	;Select Home User
	mov	e,a
	mvi	c,32
	call	entry
	jr	rexit	;Exit and Restore Regs
;
;  Save Current DMA Address
;
savdma:
	push	h
	lhld	biosdma	;Get Current DMA
	shld	usrdma	;Set User DMA
	pop	h
	ret
;
;  Check for A=0FFH and print error code in C if so
;
errchk:
	cpi	0ffh	;Error?
	rnz
err1:
	call	djcout	;Output Error Code
	mvi	c,7	;Ring Bell
	jmp	djcout
;
;  Buffers
;
dsk1:	db	0	;Not Open
	dw	dma1	;Buffer Address Set by Software
	db	0	;No Bytes Stored
	dw	dma1	;DMA Address
	dw	fcb1	;FCB Address

dsk2:	db	0	;Not Open
	dw	dma2	;Buffer Address Set by Software
	db	0	;No Bytes Stored
	dw	dma2	;DMA Address
	dw	fcb2	;FCB Address

	db	'End of SYSIO',0

	org	bufbase	;Scratch Area for Buffers

dma1:	ds	128	;DMA for Console
dma2:	ds	128	;DMA for Printer

disk:	ds	1	;Current Disk
user:	ds	1	;Current User
hdisk:	ds	1	;Home Disk
huser:	ds	1	;Home User
usrdma:	ds	2	;User DMA Address
fsbptr:	ds	2	;Ptr to File Spec Block
clast:	ds	1	;Last Char to CRT
tlast:	ds	1	;Last Char to PRT
fcb1:	ds	36	;CRT Disk Output FCB
fcb2:	ds	36	;TTY Disk Output FCB

	ds	100	;50-Element Local Stack
stack:	ds	2	;User Stack Ptr

stksav:	ds	stksiz	;User's Original Stack


	end

