page 60,132
;TITLE (C) Copyright CAPROCK SYSTEMS, INC. 1982
;SUBTTL small-c:PC PC-DOS RUN TIME LIBRARY
;
;
DATASEG	SEGMENT	BYTE PUBLIC 'code'
	DB	'(C) Copyright CAPROCK SYSTEMS, INC. 1982'
;
;
;
					;FOR I/O BUFFERS
FCBSIZE	EQU	40		;PCDOS FCB SIZE + 3
BUFFER	EQU	4		;SPACE NEEDED FOR NEXT PTR AND UNUSED CNT
BUFSIZ	EQU	512		;DISK BLOCK SIZE
NEXTP	EQU	0		;OFFSET IN BUFFER OF NEXT CHAR PTR
UNUSED	EQU	2		;OFFSET IN BUFFER OF REMAINING CHARS
FLAG	EQU	37		;OFFSET IN FCB OF FLAG TYPING AN FCB
NBUFS	EQU	4		;NUMBER OF IOBUFS (SEE BELOW)
FREEFLG	EQU	128		;FCB NOT ALLOCATED
EOFFLG	EQU	2		;EOF ENCOUNTERED ON FILE
WRTFLG	EQU	1		;FILE OPENED FOR WRITING
CTRLZ	EQU	26
EOL	EQU	13		;END OF LINE CHARACTER
LF	EQU	10		;LINE FEED
;
;PCDOS INT 21H FUNCTION CODES
;
GETCH	EQU	1
PUTCH	EQU	2
GETSTR	EQU	10
SELECT	EQU	14
DMA	EQU	26
OPEN	EQU	15
DELETE	EQU	19
CREATE	EQU	22
CLOSE	EQU	16
SEQREAD	EQU	20
SEQWRITE EQU	21
GETDFLT	EQU	25		;GET CURRENT LOGGED DRIVE
PARSE	EQU	41
;
;
; FCB STORAGE AND I/O BUFFERS
;
DFLTDSK	DB	0		;BYTE FOR DEFAULT DISK
IOBUFS	DB	FCBSIZE-3 DUP(?)
	DB	FREEFLG,0,0
	DB	BUFFER+BUFSIZ DUP(?)
;
	DB	FCBSIZE-3 DUP(?)
	DB	FREEFLG,0,0
	DB	BUFFER+BUFSIZ DUP(?)
;
	DB	FCBSIZE-3 DUP(?)
	DB	FREEFLG,0,0
	DB	BUFFER+BUFSIZ DUP(?)
;
	DB	FCBSIZE-3 DUP(?)
	DB	FREEFLG,0,0
	DB	BUFFER+BUFSIZ DUP(?)
DATASEG	ENDS
;
;
;
CSEG	SEGMENT	BYTE PUBLIC 'code'
	ASSUME	CS:CSEG,DS:DATASEG
;-------------------------------------------------
;
;	Small-C:PC Run Time Library for IBM PC-DOS
;
;	V1	As of June, 1982
;
;--------------------------------------------------
	db	'(C) Copyright CAPROCK SYSTEMS, INC. 1982'
;
;
; Fetch byte at offset BX in Stack Segment (SS) and sign extend into BX
;
	PUBLIC	CCGCHAR
CCGCHAR:
	MOV	BP,BX
	MOV	AL,[BP]
	CBW
	MOV	BX,AX
	RET
;
; Fetch a 16-bit integer at offset BX in SS into BX
;
	PUBLIC	CCGINT
CCGINT:
	MOV	BP,BX
	MOV	BX,[BP]
	RET
;
; Store BL into SS at offset in DX
;
	PUBLIC	CCPCHAR
CCPCHAR:
	MOV	BP,DX
	MOV	[BP],BL
	RET
;
; Store BX into SS at Offset in DX
;
	PUBLIC	CCPINT
CCPINT:
	MOV	BP,DX
	MOV	[BP],BX
	RET
;
; Multiply DX by BX and return result in BX
;
	PUBLIC	CCMULT
CCMULT:
	MOV	AX,DX
	IMUL	BX
	MOV	BX,AX
	RET
;
; Divide DX by BX, return quotient in BX, remainder in DX
;
	PUBLIC	CCDIV
CCDIV:
	MOV	AX,DX
	MOV	CX,1
	IMUL	CX
	IDIV	BX
	MOV	BX,AX
	RET
;
; Unsigned compare of DX to BX, carry set if DX < BX
;
	PUBLIC	CCUCMP
CCUCMP:
	CMP	DH,BH
	JNZ	CC@1
	CMP	DL,BL
CC@1:
	MOV	BX,1
	RET
;
; Test if DX >= BX (Unsigned)
;
	PUBLIC	CCUGE
CCUGE:
	CALL	CCUCMP
	JNC	CC@2
	DEC	BX
CC@2:
	RET
;
; Test if DX < BX (Unsigned)
;
	PUBLIC	CCULT
CCULT:
	CALL	CCUCMP
	JC	CC@3
	DEC	BX
CC@3:
	RET
;
; Test if DX > BX (Unsigned)
;
	PUBLIC	CCUGT
CCUGT:
	XCHG	BX,DX
	CALL	CCULT
	RET
;
; Test if DX <= BX (Unsigned)
;
	PUBLIC	CCULE
CCULE:
	XCHG	BX,DX
	CALL	CCUGE
	RET
;
; Signed Compare of DX and BX
;
; carry set if DX < BX, zero/non-zero set for equality
;
	PUBLIC	CCCMP
CCCMP:
	SUB	DL,BL
	SBB	DH,BH
	MOV	BX,1
	JS	CCCMP1
	OR	DH,DL
	RET
CCCMP1:
	OR	DH,DL
	STC
	RET
;
; Test if DX = BX and set BX=1 if true, else 0
;
	PUBLIC	CCEQ
CCEQ:
	CALL	CCCMP
	JZ	CC@4
	DEC	BX
CC@4:
	RET
;
;
; Test if DX != BX
;
	PUBLIC	CCNE
CCNE:
	CALL	CCCMP
	JNZ	CC@5
	DEC	BX
CC@5:
	RET
;
; Test if DX < BX
;
	PUBLIC	CCLT
CCLT:
	CALL	CCCMP
	JC	CC@6
	DEC	BX
CC@6:
	RET
;
; Test if DX > BX
;
	PUBLIC	CCGT
CCGT:
	XCHG	BX,DX
	CALL	CCLT
	RET
;
; Test if DX <= BX
;
	PUBLIC	CCLE
CCLE:
	CALL	CCCMP
	JC	CC@7
	JZ	CC@7
	DEC	BX
CC@7:
	RET
;
; Test if DX >= BX
;
	PUBLIC	CCGE
CCGE:
	XCHG	BX,DX
	CALL	CCLE
	RET
;
;-------------------------------------------------
;
; PC-DOS I/O MODULES
; (C) CAPROCK SYSTEMS, INC.
; P.O. BOX 13814
; ARLINGTON, TEXAS 76013
;
;-------------------------------------------------
;
;
;
;	pcdos(ah,dx)
;
	PUBLIC	QZPCDOS
QZPCDOS:
	POP	CX
	POP	DX
	POP	AX
	PUSH	AX
	PUSH	DX
	PUSH	CX
	MOV	AH,AL
	INT	21H
	CBW
	MOV	BX,AX
	RET
;
;
;	out808X(port,AL)
;
;
	PUBLIC	QZOUT808X
QZOUT808X:
	POP	CX
	POP	AX
	POP	DX
	PUSH	DX
	PUSH	AX
	OUT	DX,AL
	JMP	CX
;
;
;	int var = in808X(port)
;
;
	PUBLIC	QZIN808X
QZIN808X:
	POP	CX
	POP	DX
	PUSH	DX
	IN	AL,DX
	CBW
	MOV	BX,AX
	JMP	CX
;
;
;	VIDEO I/O THRU ROM BIOS
;
;	int10(AH,AL,BH,BL,CH,CL,DH,DL);
;
;
	PUBLIC	QZINT10
QZINT10:
	POP	SI
	POP	DX
	POP	AX
	MOV	DH,AL
	POP	CX
	POP	AX
	MOV	CH,AL
	POP	BX
	POP	AX
	MOV	BH,AL
	MOV	DI,BX
	POP	AX
	POP	BX
	MOV	AH,BL
	MOV	BX,DI
	PUSH	AX
	PUSH	AX
	PUSH	BX
	PUSH	BX
	PUSH	CX
	PUSH	CX
	PUSH	DX
	PUSH	DX
	PUSH	SI
	INT	10H
	RET
;
;
;	ASYNC I/O THRU ROM BIOS
;
;	BX = INT14(AH,AL,DX)
;
	PUBLIC	QZINT14
QZINT14:
	POP	CX
	POP	DX
	POP	BX
	POP	AX
	PUSH	AX
	PUSH	BX
	PUSH	DX
	MOV	AH,AL
	MOV	AL,BL
	INT	14H
	MOV	BX,AX
	JMP	CX
;
;
;	copy program prefix to stack area
;
;	copyprefix(ptr);
;
	PUBLIC	QZCOPYPREF
QZCOPYPREF:
	POP	CX
	POP	DI		;OFFSET INTO STACK OF DESTINATION
	PUSH	DI
	PUSH	CX
	MOV	AX,SS
	PUSH	ES
	MOV	ES,AX		;PREPARE FOR MOVE
	MOV	SI,0
	CLD
	MOV	CX,128		;NUMBER WORDS TO MOVE
	REP	MOVSW
	POP	ES
	RET
;
;
;	SOUND BELL
;
;	bell()
;
	PUBLIC	QZBELL
QZBELL:
	MOV	AX,7		;BELL CHARACTER
	PUSH	AX
	CALL	QZPUTCHAR	;SOUND IT
	POP	AX
	RET
;
;
;	CLEAR SCREEN
;
;	clrscreen();
;
	PUBLIC	QZCLRSCREE
QZCLRSCREE:
	INT	11H		;EQUIPMENT
	MOV	SI,AX
	AND	SI,30H
	MOV	AX,0B800H	;COLOR CARD RAM
	MOV	CX,8192		;WORD COUNT
	CMP	SI,30H
	JNE	CC@8
	MOV	AX,0B000H	;BW CARD RAM
	MOV	CX,2048		;WORD COUNT
CC@8:
	MOV	ES,AX
	MOV	AH,15		;VIDEO STATE
	INT	10H
	CMP	AL,4		;GRAPHICS MODE?
	JC	CC@9
	CMP	SI,30H		;BW CARD?
	JE	CC@9
	XOR	AX,AX		;FILL WORD
	JMP	SHORT CC@10
CC@9:
	MOV	AX,' '+7*256	;FILL WORD
CC@10:
	XOR	DI,DI
	CLD
	REP	STOSW		;CLEAR VIDEO MEMORY
	MOV	AH,2		;MOVE CURSOR
	XOR	DX,DX		;0,0 (HOME)
	XOR	BH,BH		;ACTIVE PAGE
	INT	10H
	RET
;
;
;
;	gets(buff)
;
;
	PUBLIC	QZGETS
QZGETS:
	POP	BX
	POP	DX
	PUSH	DX
	PUSH	BX
	SUB	DX,2
	MOV	BP,DX
	MOV	CX,[BP]
	MOV	AX,004FH	;ASSUMED LENGTH = 80 CHARS - 1 FOR EOL
	MOV	[BP],AX		;SET UP BUFFER THE WAY PC-DOS WANTS IT
	PUSH	DS
	MOV	AX,SS
	MOV	DS,AX
	MOV	AH,GETSTR
	INT	21H
	POP	DS
	MOV	AX,[BP]		;LENGTH IN AH
	MOV	[BP],CX		;RESTORE SAVED BYTES
	ADD	BP,2
	MOV	BX,BP
	MOV	AL,AH
	CBW
	ADD	BP,AX
	XOR	AL,AL
	MOV	[BP],AL		;INSERT C:PC STRING TERMINATOR
	CALL	PUTLF
	RET
;
PUTLF:
	MOV	AH,PUTCH
	MOV	DL,LF
	INT	21H
	RET
;
;
;	getchar()
;
;
	PUBLIC	QZGETCHAR
QZGETCHAR:
	MOV	AH,GETCH
	INT	21H
	MOV	BL,AL
	XOR	BH,BH
	CMP	AL,CTRLZ
	JNZ	GETC1
	MOV	BX,-1
GETC1:
	CMP	AL,EOL
	JNZ	GC2
	CALL	PUTLF
GC2:
	RET
;
;
;	putchar(c)
;
;
	PUBLIC	QZPUTCHAR
QZPUTCHAR:
	POP	BX
	POP	DX
	PUSH	DX
	PUSH	BX
	MOV	AH,PUTCH
	INT	21H
	MOV	BL,DL
	CMP	DL,EOL
	JNZ	PUTC1
	CALL	PUTLF
PUTC1:
	XOR	BH,BH
	RET
;
;
;	puts(cp)
;
;
	PUBLIC	QZPUTS
QZPUTS:
	POP	CX
	POP	BP
	PUSH	BP
	PUSH	CX
	MOV	AH,PUTCH
PS1:
	MOV	DL,[BP]
	OR	DL,DL
	JNZ	PS2
	RET
PS2:
	INC	BP
	INT	21H
	JMP	PS1
;
;
;	Run Time Initialize
;
;
CCGO:
	MOV	CL,4		;SHIFT COUNT
	MOV	AX,STACK
	MOV	SS,AX
	MOV	BX,DS
	SUB	AX,BX
	SAL	AX,CL		;MAKE DIFF 16 BITS
	NEG	AX
	ADD	AX,DS:6
	MOV	SP,AX		;MAX STACK POINTER
	PUSH	DS		;SAVE PREFIX ADDR
	SUB	AX,AX
	PUSH	AX		;LONG EXIT ADDRESS IS NOW ON TOP
	MOV	AX,DATASEG
	PUSH	DS
	MOV	DS,AX
	MOV	AH,GETDFLT
	INT	21H
	INC	AL
	MOV	BX,OFFSET DFLTDSK
	MOV	[BX],AL
	POP	DS
	EXTRN	QZMAIN:NEAR
	CALL	QZMAIN	;EXECUTE USER'S small-c:PC PROGRAM
; FALL THRU TO EXIT CODE
;
;
;	exit()
;
	PUBLIC	QZEXIT
QZEXIT:
	MOV	CL,4
	MOV	AX,SS
	MOV	BX,DS
	SUB	AX,BX
	SAL	AX,CL
	NEG	AX
	ADD	AX,DS:6
	SUB	AX,4
	MOV	SP,AX		;PRUNE STACK
	MOV	AX,DATASEG
	PUSH	DS
	MOV	DS,AX
	MOV	BX,OFFSET DFLTDSK
	MOV	DL,[BX]
	DEC	DL
	MOV	AH,SELECT
	INT	21H
	POP	DS
	DB	0CBH		;LONG RETURN TO DOS
;
;
;	fopen(name,mode)
;
	PUBLIC	QZFOPEN
QZFOPEN:
	POP	AX
	POP	CX	;MODE
	POP	SI	;PREPARE FOR PARSE
	PUSH	SI
	PUSH	CX
	PUSH	AX
	PUSH	CX	;SAVE MODE
	CALL	GRABIO	;GET FCB
	POP	CX	;RESTORE MODE FOR LATER USE
	OR	BX,BX	;ANY LUCK IN GETTING AN FCB?
	JNZ	FO1
	RET		;NOPE
FO1:
	MOV	DX,BX		;SAVE OFFSET
	PUSH	DS	;SAVE CALLER'S DS
	MOV	AX,DATASEG
	MOV	DS,AX	;ADDRESS OUR DATA SEGMENT
	ADD	BX,FCBSIZE	;OFFSET OF BUFFER
	MOV	AX,BX
	ADD	AX,BUFFER	;IO AREA OFFSET
	MOV	[BX]+NEXTP,AX	;NEXT AVAILABLE CHAR
	PUSH	DS
	MOV	AX,DS
	MOV	ES,AX
	MOV	DI,DX		;ES:DI -> FCB
	MOV	AX,SS
	MOV	DS,AX		;DS:SI -> NAME
	MOV	AH,PARSE
	XOR	AL,AL		;PARSE WITHOUT SKIPPING ANYTHING
	INT	21H
	POP	DS		;RESTORE OUR DS
	OR	AL,AL		;ANY LUCK?
	JNZ	FORET		;JUMP IF NOT
	MOV	AL,[DI]+1	;SEE IF WE HAD A VALID FILENAME
	CMP	AL,20H		;BLANK?
	JNZ	FO2		;NOPE, SO IT MUST BE SOMETHING THERE
FORET:
	POP	DS		;RESTORE CALLER'S DS
	XOR	BX,BX		;SET RETURN STATUS
	RET			;LEAVE
FO2:
	MOV	BX,DX		;RESTORE OFFSET OF FCB
	MOV	AL,[BX]		;DRIVE
	PUSH	DX		;SAVE ACCROSS CALL
	PUSH	CX
	PUSH	AX
	CALL	PCDOSDSK	;SELECT IT
	POP	AX
	POP	BP		;MODE OFFSET INTO BP FOR STACK ACCESS
	POP	DX
	MOV	AL,[BP]		;GET MODE CHAR
	CMP	AL,72H		;MODE='r'
	JZ	FO3
	CMP	AL,52H		;MODE='R'
	JNZ	FO5
FO3:
	MOV	AH,OPEN
	INT	21H		;OPEN THE FILE
	OR	AL,AL
	JZ	FO4		;NO ERROR JUMP
FO3@1:
	PUSH	DX		;FCB OFFSET
	CALL	FREEIO		;GIVE IT UP
	POP	DX
	JMP	FORET
FO4:
	XOR	AX,AX
FO4@1:
	MOV	[BX]+FCBSIZE+UNUSED,AX	;SET UNUSED BUFFER BYTES
	XOR	AX,AX
	MOV	[BX]+32,AL		;INIT SOME FCB FIELDS
	MOV	[BX]+33,AX
	MOV	[BX]+35,AX
	MOV	AX,BUFSIZ
	MOV	[BX]+14,AX
	POP	DS			;RESTORE CALLER'S DS
	RET				;LEAVE WITH BX SET TO FCB OFFSET
FO5:
	CMP	AL,77H		;MODE='w'
	JZ	FO6
	CMP	AL,57H		;MODE='W'
	JNZ	FO3@1		;TAKE ERROR EXIT
FO6:
	MOV	AH,DELETE
	INT	21H		;DELETE FILE IF IT CURRENTLY EXISTS
	MOV	AH,CREATE
	INT	21H		;CREATE IT
	OR	AL,AL		;OK?
	JNZ	FO3@1		;TAKE ERROR EXIT IF NOT
	MOV	BX,DX
	MOV	AL,WRTFLG
	MOV	[BX]+FLAG,AL	;INDICATE HOW FILE IS OPENED
	MOV	AX,BUFSIZ	;NUMBER OF UNUSED BUFFER POSITIONS
	JMP	FO4@1
;
;
;
;	grabio()
;
;
GRABIO:
	PUSH	DS
	MOV	AX,DATASEG
	MOV	DS,AX
	MOV	BX,OFFSET IOBUFS+FLAG
	MOV	CX,NBUFS
	MOV	AL,FREEFLG
GI1:
	CMP	AL,[BX]
	JZ	GI2
	ADD	BX,FCBSIZE+BUFFER+BUFSIZ
	LOOP	GI1
	XOR	BX,BX
	JMP	GI3
GI2:
	XOR	AL,AL
	MOV	[BX],AL
	SUB	BX,FLAG
GI3:
	POP	DS
	RET
;
;
;
;	freeio(unit)
;
;
FREEIO:
	POP	CX
	POP	BX
	PUSH	BX
	MOV	AL,FREEFLG
	MOV	[BX]+FLAG,AL
	XOR	BX,BX
	JMP	CX
;
;
;
;	pcdosdsk(drive)
;
;
PCDOSDSK:
	POP	CX
	POP	DX
	PUSH	DX
	OR	DL,DL
	JNZ	PD1
; SELECT DEFAULT DRIVE
	PUSH	BX
	MOV	BX,OFFSET DFLTDSK
	MOV	DL,[BX]
	POP	BX
PD1:
	DEC	DL
	MOV	AH,SELECT
	INT	21H
	JMP	CX
;
;
;
;	fclose(unit)
;
;
	PUBLIC	QZFCLOSE
QZFCLOSE:
	POP	CX
	POP	BX		;FCB OFFSET
	PUSH	BX
	PUSH	CX
	MOV	SI,1		;DEFAULT RETURN CODE
	PUSH	DS		;SAVE CALLER'S DS
	MOV	AX,DATASEG
	MOV	DS,AX
	MOV	AL,WRTFLG
	AND	AL,[BX]+FLAG	;OPENED FOR WRITE?
	JZ	FC3		;JUMP IF NOT
	MOV	AX,CTRLZ
	PUSH	AX		;CHAR TO WRITE
	PUSH	BX		;UNIT TO GO TO
	CALL	QZPUTC		;CTRLZ AT FILE END
	POP	BX
	POP	AX
; FILL BUFFER WITH FILL CHAR
	MOV	DX,BX
	MOV	AX,[BX]+FCBSIZE+NEXTP		;OFFSET OF NEXT AVAILABLE CHAR
	MOV	CX,DX
	ADD	CX,FCBSIZE+BUFFER+BUFSIZ	;OFFSET PAST LAST IOAREA BYTE
	SUB	CX,AX		;NUMBER OF CHARS TO FILL
	PUSH	CX		;SAVE UNUSED BYTE COUNT
	JLE	FC0		;JUMP IF NONE
	MOV	DI,AX		;START ADDRESS ES:DI
	MOV	AX,DS
	MOV	ES,AX
	MOV	AL,0	;FILL CHAR
	CLD			;LEFT TO RIGHT
	REP	STOSB		;FILL BUFFER
FC0:
	MOV	AX,SEQWRITE
	PUSH	AX
	PUSH	DX		;FUNCTION AND UNIT NOW ON STACK
	CALL	PCDOSIO		;WRITE SECTOR
	MOV	AX,BX		;SAVE RETURN CODE
	POP	BX		;RESTORE UNIT
	POP	CX
	OR	AX,AX
	JNS	FC1
	MOV	SI,0
FC1:
	POP	CX		;RESTORE UNUSED COUNT
	NEG	CX		;PREPARE TO REDUCE DOS COUNT
	JNS	FC3		;NO SIGN IMPLIES  DOS COUNT IS OK
	MOV	AX,1		;PREPARE FOR OVERFLOW
	ADD	[BX]+16,CX	;NEW LOW PART
	JNO	FC3		;NO OVERFLOW => NO HIGH PART CHANGE
	ADD	[BX]+18,AX	;NEW HIGH PART
FC3:
	MOV	DX,BX
	MOV	AH,CLOSE
	INT	21H		;CLOSE FILE
	OR	AL,AL
	JNS	FC2
	MOV	SI,0
FC2:
	PUSH	BX
	CALL	FREEIO
	POP	BX
	POP	DS
	MOV	BX,SI
	RET
;
;
;
;	pcdosio(fn,unit)
;
;
PCDOSIO:
	POP	CX
	POP	DX		;UNIT= OFFSET IN DS OF FCB
	POP	BX		;FUNCTION WANTED
	PUSH	BX
	PUSH	DX
	PUSH	CX
	PUSH	DS		;SAVE CALLER'S DS
	MOV	AX,DATASEG
	MOV	DS,AX
	XOR	AH,AH
	XCHG	BX,DX
	MOV	AL,[BX]		;DRIVE NUMBER
	XCHG	BX,DX
	PUSH	DX
	PUSH	AX
	CALL	PCDOSDSK	;SELECT IT
	POP	AX
	POP	DX
	MOV	CX,DX		;SAVE UNIT
	ADD	DX,FCBSIZE+BUFFER	;IO AREA OFFSET
	MOV	AH,DMA
	INT	21H		;SET TRANSFER ADDRESS
	MOV	DX,CX		;DS:DX = FCB ADDRESS
	MOV	AH,BL		;FUNCTION CODE
	INT	21H
	MOV	CL,AL		;STATUS
	MOV	AH,DMA
	POP	DS		;RESET DS TO PREFIX
	MOV	DX,80H
	INT	21H		;DMA BACK TO DEFAULT
	MOV	AL,BL		;SAVE FUNCTION
	XOR	BX,BX
	OR	CL,CL
	JZ	PDI1
	CMP	AL,SEQREAD	;DID USER READ?
	JNZ	PDI0		;CONSIDER IT AN ERROR
	CMP	CL,03H		;PARTIALLY FULL BUFFER?
	JZ	PDI1		;THAT'S OK
PDI0:
	NOT	BX
PDI1:
	RET
;
;
;
;	getc (unit)
;
;
	PUBLIC	QZGETC
QZGETC:
	POP	CX
	POP	BX		;UNIT
	PUSH	BX
	PUSH	CX
	PUSH	BX
	CALL	CGET		;GET NEXT CHARACTER
	POP	DX		;UNIT
	CMP	BL,EOL		;END OF LINE
	JNZ	GC1		;GO ON BACK IF NOT
	PUSH	BX		;SAVE EOL ON STACK
	PUSH	DX		;UNIT
	CALL	CGET		;ABSORB LF
	POP	DX
	POP	BX		;RETURN EOL
GC1:
	RET
;
;
;
;	cget(unit)
;
;
CGET:
	POP	CX
	POP	BX
	MOV	DX,BX
	PUSH	BX
	PUSH	CX
	PUSH	DS
	MOV	AX,DATASEG
	MOV	DS,AX
	MOV	AL,EOFFLG
	AND	AL,[BX]+FLAG		;END OF FILE EXIST?
	JZ	CG1			;NOPE
CG0:
	POP	DS
	MOV	BX,-1			;ERROR RETURN
	RET
CG1:
	MOV	AX,[BX]+FCBSIZE+NEXTP	;BUFFER OFFSET OF NEXT CHARACTER
	MOV	CX,[BX]+FCBSIZE+UNUSED	;NUM CHARS REMAINING IN BUF
	OR	CX,CX
	JNZ	CG2			;SOME LEFT
	;READ NEW SECTOR
	MOV	AX,SEQREAD
	PUSH 	AX			;FUNCTION
	PUSH	DX			;UNIT
	CALL	PCDOSIO			;READ SECTOR
	POP	DX
	POP	AX
	OR	BH,BL			;OK?
	JNZ	CG0			;JUMP IF NOT
	MOV	BX,DX			;RESTORE UNIT
	MOV	CX,BUFSIZ		;NEW COUNT
	MOV	AX,DX
	ADD	AX,FCBSIZE+BUFFER	;NEW NEXT CHAR OFFSET
CG2:
	DEC	CX			;REDUCE UNUSED
	MOV	[BX]+FCBSIZE+UNUSED,CX
	MOV	CX,AX
	INC	CX			;BUMP UP NEXT CHAR POINTER
	MOV	[BX]+FCBSIZE+NEXTP,CX
	MOV	BX,AX			;OFFSET OF CHAR TO RETURN
	MOV	AL,[BX]
	CMP	AL,CTRLZ		;IS IT EOF MARKER?
	JNZ	CG3			;JUMP IF NOT
	MOV	BX,DX			;UNIT
	MOV	AH,EOFFLG
	OR	[BX]+FLAG,AH		;SET END OF FILE IN FCB
	JMP	CG0
CG3:
	MOV	BL,AL			;CHAR TO RETURN
	XOR	BH,BH
	POP	DS
	RET
;
;
;
;	putc(char,unit)
;
;
;
	PUBLIC	QZPUTC
QZPUTC:
	POP	CX
	POP	BX		;UNIT
	POP	AX		;CHAR
	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSH	AX		;CHAR
	PUSH	BX		;UNIT
	CALL	CPUT		;PUT OUT CHAR
	POP	DX		;UNIT
				;LEAVE CHAR ON STACK AS RETURN VALUE
	OR	BH,BH		;ERROR?
	JS	PC2		;JUMP IF SO
	CMP	BL,EOL		;DID EOL GO OUT?
	JNZ	PC1		;JUMP IF NOT
	MOV	AX,LF		;PUT OUT LF ALSO
	PUSH 	AX
	PUSH	DX
	CALL	CPUT
	POP	DX
	POP	AX
	OR	BH,BH		;ERROR?
	JS	PC2		;JUMP IF SO
PC1:
	POP	BX		;RETURN CHAR PASSED IN
	RET
PC2:
	POP	CX		;CLEAR CHAR OFF STACK
	MOV	BX,-1		;ERROR RETURN
	RET
;
;
;
;	cput(c,unit)
;
;
CPUT:
	POP	CX
	POP	BX		;UNIT
	POP	SI		;CHAR
	PUSH	SI
	PUSH	BX
	PUSH	CX
	PUSH	DS
	MOV	AX,DATASEG
	MOV	DS,AX
	MOV	DX,BX
	MOV	AX,[BX]+FCBSIZE+NEXTP	;NEXT CHAR OFFSET
	MOV	CX,[BX]+FCBSIZE+UNUSED	;UNUSED CHAR COUNT
	OR	CX,CX
	JNZ	CP2			;JUMP IF ROOM AVAILABLE
	MOV	AX,SEQWRITE
	PUSH	AX		;FUNCTION
	PUSH	DX		;UNIT
	CALL	PCDOSIO		;SECTOR WRITE
	POP	DX
	POP	AX
	OR	BH,BL
	JZ	CP1		;JUMP IF OK
CP0:
	POP	DS
	MOV	BX,-1		;ERROR RETURN
	RET
CP1:
	MOV	CX,BUFSIZ
	MOV	AX,DX
	ADD	AX,FCBSIZE+BUFFER
	MOV	BX,DX
CP2:
	DEC	CX		;REDUCE UNUSED
	MOV	[BX]+FCBSIZE+UNUSED,CX
	MOV	CX,AX
	INC	CX
	MOV	[BX]+FCBSIZE+NEXTP,CX	;NEW NEXT CHAR OFFSET
	MOV	BX,AX
	MOV	AX,SI		;CHAR TO PUT
	MOV	[BX],AL		;BUFFER CHARACTER
	MOV	BX,AX
	POP	DS
	RET
;
CSEG	ENDS
STACK	SEGMENT BYTE PUBLIC 'stack'
STACK	ENDS
DUMMY	SEGMENT BYTE STACK 'dummy'
	DB	128 DUP(?)
DUMMY	ENDS
	END	CCGO
                                                                                                           