;
; SYSLIB Module Name:  SFILEI
; Author:  Richard Conn
; SYSLIB Version Number:  2.0
; Module Version Number:  1.0
; Module Entry Points:
;	ADRCPY		F$GET		F$PUT		FI$CLOSE
;	FI$OPEN		FO$CLOSE	FO$OPEN
; Module External References:
;	F$MOPEN		F$OPEN		INITFCB
;

;
;  SFILEIO.MAC -- BYTE-ORIENTED FILE I/O FOR SYSLIB
;    INCLUDED ROUTINES ARE --  FILE INPUT OPEN, FILE OUTPUT OPEN,
;			FILE INPUT CLOSE, FILE OUTPUT CLOSE,
;			GET, PUT
;	Error Diagnostics are returned to the caller via the Zero Flag
;		(Zero Flag Set - Z - ALWAYS Means No Error Occurred)
;		and the A Register
;	If the Zero Flag is Set (Z), then the A Register either contains
;		a 0 or the required returned value if A is significant for
;		the particular routine.
;	If the Zero Flag is Clear (NZ), then the A Register contains the
;		error code.  No other returned register value in HL, DE, or BC
;		should be considered to be valid.  The Returned Error Codes
;		in A are:
;			Code	Meaning
;			  1	GET or PUT attempted on unopened file
;			  2	Disk Full (Ran out of space)
;			  3	Input File Not Found
;			  4	Attempt to Read Past the EOF
;			  5	Directory Full
;			  6	Error in Closing a File
;			  7	Attempt to Open a File which is already open
;

;
;  EXTERNAL LIBRARY FILE DEFINITIONS
;
	EXT	F$OPEN	; OPEN FILE
	EXT	F$MOPEN	; OPEN/CREATE FILE
	EXT	INITFCB	; INIT FCB

;
;  CP/M EQUATES AND ASCII CONSTANTS
;
CPM	EQU	0	; WARM BOOT FOR CP/M
TBUFF	EQU	80H	; TEMPORARY FILE I/O BUFFER
CR	EQU	0DH	; <CR>
LF	EQU	0DH	; <LF>

BDOS	EQU	5	; BDOS ENTRY POINT
B$CL	EQU	16	; CLOSE FILE
B$RD	EQU	20	; READ NEXT RECORD
B$WR	EQU	21	; WRITE NEXT RECORD
B$DMA	EQU	26	; SET DMA ADDRESS

;
;  SUPPORTING MACROS
;
PUTRG	MACRO		; SAVE BC, DE, HL
	PUSH	B
	PUSH	D
	PUSH	H
	ENDM
GETRG	MACRO		; RESTORE HL, DE, BC
	POP	H
	POP	D
	POP	B
	ENDM

;**********************************************************************
;  SUPPORTING ROUTINES
;

;
;  ADDRESS TABLE COPY ROUTINE
;
ADRCPY::
	LXI	H,I$FLG	; PT TO FIRST ENTRY
	MVI	B,20	; 20 BYTES (10 ADDRESSES) TO COPY
	JMP	SFCBL	; DO COPY

;
;  GET ONE-LEVEL INDIRECT ADDRESS
;	HL = ADDRESS CONTAINED AT MEMORY LOCATION PTED TO BY HL
;
GETADR:
	PUSH	D	; SAVE DE
	MOV	E,M	; GET LOW
	INX	H
	MOV	D,M	; GET HIGH
	MOV	H,D	; SET HL
	MOV	L,E
	POP	D	; GET DE
	RET

;
;  SAVEFCB UTILITY -- COPY FCB FROM DE TO HL
;
SAVEFCB:
	MVI	B,36	; 36 BYTES
SFCBL:
	LDAX	D	; GET BYTE
	MOV	M,A	; PUT BYTE
	INX	H	; PT TO NEXT
	INX	D
	DCR	B	; COUNT DOWN
	JNZ	SFCBL
	RET
;
;  F$ABORT -- FILE ERROR -- ABORT
;
F$ABORT:
	GETRG		; RESTORE REGISTERS
	ORA	A	; SET FLAGS
	RET
;
;  F$CLOS -- CLOSE FILE
;
F$CLOS:
	MVI	C,B$CL	; CLOSE FILE
	CALL	BDOS
	RET
;
;  F$READ -- READ ONE 128-BYTE BLOCK VIA CP/M
;
F$READ:
	MVI	C,B$RD	; READ BLOCK
	CALL	BDOS
	RET
;
;  F$WRIT -- WRITE ONE 128-BYTE BLOCK VIA CP/M
;
F$WRIT:
	MVI	C,B$WR	; WRITE BLOCK
	CALL	BDOS
	RET
;
;  READ$BLOCK -- READ BLOCK FROM INPUT FILE INTO INPUT BUFFER
;    ON RETURN, Z=OK AND NZ=NOT OK (PAST EOF)
;
READ$BLOCK:
	LHLD	I$FCB	; GET FCB ADDRESS
	XCHG		; ... IN DE
	CALL	F$READ	; READ BLOCK
	PUSH	PSW	; SAVE STATUS
	LHLD	I$BUF	; PT TO INPUT BUFFER
	LXI	D,TBUFF	; PT TO TEMP BUFFER
	MVI	B,128	; 128 BYTES
	CALL	SFCBL	; COPY FROM DE TO HL FOR B BYTES
	POP	PSW	; GET STATUS
	ORA	A	; SET ZERO FLAG IF OK
	RET
;
;  WRIT$BLOCK -- WRITE BLOCK TO OUTPUT FILE
;
WRIT$BLOCK:
	LHLD	O$BUF	; ADDRESS OF OUTPUT BUFFER
	XCHG		; ... IN DE
	LXI	H,TBUFF	; PT TO TEMP BUFFER
	MVI	B,128	; 128 BYTES
	CALL	SFCBL	; COPY FROM DE TO HL FOR B BYTES
	LHLD	O$FCB	; PT TO DEFAULT FCB
	XCHG		; ... IN DE
	CALL	F$WRIT	; WRITE BLOCK
	ORA	A	; OK?
	RZ		; OK IF ZERO
	MVI	A,2	; DISK FULL ERROR CODE
	RET
;
;  **** BASE ROUTINES ****
;
;  FI$OPEN -- OPEN FILE WHOSE FCB IS PTED TO BY DE FOR INPUT
;
FI$OPEN::
	PUTRG		; SAVE REGISTERS
	PUSH	D	; SAVE PTR
	LXI	D,TBUFF	; SET DMA ADDRESS
	MVI	C,B$DMA
	CALL	BDOS
	POP	D	; GET PTR
	LHLD	I$FLG	; ALREADY OPENED?
	MOV	A,M	; GET FLAG
	ORA	A	; 0=NO
	JZ	FI$OP0
	MVI	A,7	; FILE ALREADY OPEN ERROR
	JMP	F$ABORT
FI$OP0:
	LHLD	I$FCB	; PT TO DEFAULT OPEN FCB
	PUSH	H	; SAVE PTR TO FCB
	CALL	SAVEFCB	; CREATE NEW FCB
	POP	D	; GET PTR TO FCB
	CALL	INITFCB	; CLEAR FCB FIELDS
	CALL	F$OPEN	; OPEN FILE
	ORA	A	; ZERO MEANS OK
	JZ	FI$OP1
	MVI	A,3	; FILE NOT FOUND FOR INPUT
	JMP	F$ABORT
FI$OP1:
	CALL	READ$BLOCK	; READ FIRST BLOCK
	JZ	FI$OP2
	MVI	A,4	; READ PAST EOF ERROR
	JMP	F$ABORT
FI$OP2:
	LHLD	I$BUF	; PT TO BUFFER
	XCHG		; ... ADDRESS IN DE
	LHLD	I$PTR	; SAVE PTR
	MOV	M,E	; SAVE ADDRESS PTR
	INX	H
	MOV	M,D
	LHLD	I$CNT	; GET PTR TO COUNT
	MVI	M,128	; SET COUNT
	LHLD	I$FLG	; GET PTR TO FLAG
	MVI	M,0FFH	; SET FILE OPENED FLAG
	GETRG		; RESTORE REGISTERS
	XRA	A	; OK RETURN
	RET
;
;  FO$OPEN -- OPEN FILE WHOSE FCB IS PTED TO BY DE FOR OUTPUT
;
FO$OPEN::
	PUTRG		; SAVE REGISTERS
	PUSH	D	; SAVE PTR
	LXI	D,TBUFF	; SET DMA ADDRESS
	MVI	C,B$DMA
	CALL	BDOS
	POP	D	; GET PTR
	LHLD	O$FLG	; CHECK FOR FILE ALREADY OPENED
	MOV	A,M	; GET FLAG
	ORA	A	; 0 MEANS NOT YET OPENED
	JZ	FO$OP0
	MVI	A,7	; FILE ALREADY OPENED ERROR
	JMP	F$ABORT
FO$OP0:
	LHLD	O$FCB	; PT TO DEFAULT OPEN FCB
	PUSH	H	; SAVE PTR TO FCB
	CALL	SAVEFCB	; CREATE NEW FCB
	POP	D	; GET PTR TO FCB
	CALL	INITFCB	; CLEAR FCB FIELDS
	CALL	F$MOPEN	; OPEN AND/OR CREATE FILE
	JZ	FO$OP1
	MVI	A,5	; DIRECTORY FULL
	JMP	F$ABORT
FO$OP1:
	LHLD	O$BUF	; PT TO BUFFER
	XCHG		; ... IN DE
	LHLD	O$PTR	; SAVE PTR
	MOV	M,E	; SAVE ADDRESS PTR
	INX	H
	MOV	M,D
	LHLD	O$CNT	; GET PTR TO COUNT
	MVI	M,128	; SET COUNT
	LHLD	O$FLG	; GET PTR TO FLAG
	MVI	M,0FFH	; SET FILE OPENED FLAG
	GETRG		; RESTORE REGISTERS
	XRA	A	; OK RETURN
	RET
;
;  F$GET -- GET BYTE FROM INPUT FILE; BYTE RETURNED IN REG A
;    ON RETURN, IF CARRY=0 (NC), THEN OK; IF CARRY=1 (C), THEN PAST EOF
;
F$GET::
	PUTRG		; SAVE REGISTERS
	LHLD	I$FLG	; PT TO FLAG
	MOV	A,M	; GET IT; INPUT OK?
	ORA	A	; ZERO MEANS NO
	JNZ	F$G1
	MVI	A,1	; FILE NOT YET OPENED
	JMP	F$ABORT
F$G1:
	LHLD	I$PTR	; PT TO NEXT BYTE
	CALL	GETADR	; GET ADDRESS OF BYTE PTED TO BY PTR
	MOV	A,H	; EOF WAS REACHED IF POINTER IS ZERO
	ORA	L
	JZ	F$GEOF
	MOV	A,M	; GET BYTE
	STA	BYTE	; SAVE BYTE FOR RETURN
	INX	H	; PT TO NEXT BYTE
	XCHG		; ... IN DE
	LHLD	I$PTR	; PT TO POINTER
	MOV	M,E	; SAVE ADDRESS PTR
	INX	H
	MOV	M,D
	LHLD	I$CNT	; PT TO COUNT
	DCR	M	; DECREMENT COUNT
	JNZ	F$GET1
;  READ IN NEXT BLOCK AND RESET POINTERS
	LHLD	I$BUF	; PT TO BUFFER
	XCHG		; ... IN DE
	LHLD	I$PTR
	MOV	M,E	; SAVE ADDRESS PTR
	INX	H
	MOV	M,D
	LHLD	I$CNT	; PT TO COUNT
	MVI	M,128	; SET COUNT
	CALL	READ$BLOCK
	JZ	F$GET1	; OK RETURN
	LHLD	I$PTR	; SET POINTER TO ZERO TO INDICATE EOF REACHED
	MVI	M,0	; STORE ZEROES
	INX	H
	MVI	M,0
;  NORMAL EXIT
F$GET1:
	GETRG		; RESTORE REGISTERS
	XRA	A	; ZERO MEANS OK
	LDA	BYTE	; GET BYTE VALUE TO RETURN
	RET
;  EOF EXIT
F$GEOF:
	MVI	A,4	; EOF
	JMP	F$ABORT

;
;  F$PUT -- PUT BYTE IN REG A INTO OUTPUT FILE
;
F$PUT::
	PUTRG		; SAVE REGISTERS
	STA	BYTE	; SAVE BYTE TO OUTPUT
	LHLD	O$FLG	; GET FILE OPENED FLAG
	MOV	A,M	; GET FLAG
	ORA	A	; ZERO MEANS NO
	JNZ	F$P1
	POP	PSW	; CLEAR STACK
	MVI	A,1	; FILE NOT OPENED
	JMP	F$ABORT
F$P1:
	LHLD	O$PTR	; GET PTR TO NEXT BYTE
	CALL	GETADR	; PT TO BYTE PTED TO BY PTR
	LDA	BYTE	; GET BYTE TO OUTPUT
	MOV	M,A	; PUT BYTE
	INX	H	; PT TO NEXT
	XCHG		; ... IN DE
	LHLD	O$PTR	; PT TO PTR
	MOV	M,E	; SAVE ADDRESS
	INX	H
	MOV	M,D
	LHLD	O$CNT	; PT TO COUNT
	DCR	M	; COUNT DOWN
	JNZ	F$PUT1	; RETURN IF OK
;  BUFFER FULL -- WRITE IT TO DISK AND RESET POINTER AND COUNT
	LHLD	O$BUF	; RESET POINTER
	XCHG		; ADDR OF BUFFER IN DE
	LHLD	O$PTR
	MOV	M,E	; SAVE ADDRESS PTR
	INX	H
	MOV	M,D
	LHLD	O$CNT	; PT TO COUNT
	MVI	M,128	; RESET COUNT
	CALL	WRIT$BLOCK
	MVI	A,2	; ASSUME DISK FULL ERROR
	JNZ	F$ABORT	; ERROR IN WRITE
;  NORMAL EXIT
F$PUT1:
	GETRG		; RESTORE REGISTERS
	XRA	A	; Z FOR NO ERROR
	LDA	BYTE	; GET BYTE VALUE
	RET
;
;  FI$CLOS -- CLOSE FILE OPENED FOR INPUT
;
FI$CLOS::
	PUSH	H
	LHLD	I$FLG	; SET INPUT OPENED FLAG
	MVI	M,0	; INPUT NOT OPENED NOW
	POP	H
	RET
;
;  GENERAL-PURPOSE POINTERS FOR ALL GLOBAL ROUTINES
;
I$FLG:	DS	2	; INPUT FILE OPENED FLAG (0=NO)
O$FLG:	DS	2	; OUTPUT FILE OPENED FLAG (0=NO)
I$FCB:	DS	2	; INPUT FILE FCB
O$FCB:	DS	2	; OUTPUT FILE FCB
I$BUF:	DS	2	; INPUT BUFFER
O$BUF:	DS	2	; OUTPUT BUFFER
I$PTR:	DS	2	; INPUT CHAR PTR
O$PTR:	DS	2	; OUTPUT CHAR PTR
I$CNT:	DS	2	; INPUT CHAR COUNT
O$CNT:	DS	2	; OUTPUT CHAR COUNT


BYTE:	DS	1	; BYTE STORAGE

;
;  FO$CLOS -- CLOSE FILE OPENED FOR OUTPUT
;
FO$CLOS::
	PUTRG		; SAVE REGS
	STA	BYTE	; SAVE A
	MVI	A,1AH	; PUT CTRL-Z
	CALL	F$PUT
CLOSE1:
	LHLD	O$CNT	; PT TO COUNT
	MOV	A,M	; GET COUNT
	CPI	128	; CLOSE IF BLOCK JUST WRITTEN
	JZ	CLOSE2
	XRA	A	; PUT ZERO
	CALL	F$PUT
	JMP	CLOSE1
CLOSE2:
	LHLD	O$FLG	; SET OUTPUT OPENED FLAG
	MVI	M,0	; NOT OPENED
	LHLD	O$FCB	; PT TO OUTPUT FCB
	XCHG		; ... PT VIA DE
	CALL	F$CLOS	; CLOSE FILE
	CPI	0FFH	; ERROR?
	JNZ	CLOSE3
	MVI	A,6	; CLOSE ERROR
	JMP	F$ABORT
CLOSE3:
	GETRG		; RESTORE REGS
	XRA	A	; OK RETURN
	LDA	BYTE	; GET ORIGINAL A
	RET

	END
