TITLE SYSLIB - Byte-Oriented I/O w User-Supplied Buffers, Append ; ; SYSLIB Module Name: SFXIO ; Author: Richard Conn ; SYSLIB Version Number: 3.6 ; Module Version Number: 1.3 public fxi$open,fx$get,fxi$close public fxo$open,fx$put,fxo$close public fxa$open ; Date: 10 Nov 86 ; Revised: Michael Bate ; Changes: - Added new entry point FXA$OPEN to open a file for ; appending. Called same as FXO$OPEN, except that ; If the file already exists, text will be appended ; to the text that is in it already. ; ; Date: 28 Apr 85 ; Revised: Al Dunsmuir ; Changes: - FX$GET and FX$PUT routines optimized for speed ; (PUTADR/GETADR not used if disk I/O not required) ; - Use direct DMA vs DMA to TBUFF and software move. ; Makes byte I/O a bit faster AND safer. ; - Some misc. code optimization also performed. ; ; Previous Version: 1.0 (16 Jan 84) ; ; SFXIO provides a group of routines which can perform byte-oriented ; file I/O with a user-defined buffer size. All of these routines work with ; an I/O Control Block which is structured as follows: ; ; Byte Length (Bytes) Function ; 0 1 Number of 128-byte pages in ; working buffer (set by user) ; 1 1 End of File Flag (set and used ; by SFXIO) ; 2 2 Byte Counter (set and used by SFXIO) ; 4 2 Next Byte Pointer (set and used by ; SFXIO) ; 6 2 Address of Working Buffer (set by user) ; 8 36 FCB of File (FN and FT Fields set by ; user, rest set by SFXIO) ; ; The following DB/DS structure can be used in the calling program ; to implement the I/O Control Block: ; ; IOCTL: DB 8 ; Use 8 128-byte pages (1K) ; DS 1 ; Filled by SFXIO ; DS 2 ; Filled by SFXIO ; DS 2 ; Filled by SFXIO ; DW WORKBF ; Address of Working Buffer ; ; IOCFCB: DB 0 ; Current Disk (Inited by SFXIO) ; DB 'MYFILE ' ; File Name ; DB 'TXT' ; File Type ; DS 24 ; Fill Out 36 Bytes ; ; WORKBF: DS 1024 ; Working Buffer ; ; All uses of the routines contain the address of IOCTL in DE. ; Note that if you use a buffer for input, DO NOT use it for output also! ; ; ; External SYSLIB References ; EXT F$OPEN ;Open File EXT F$MOPEN ;Open/Create file EXT INITFCB ;Init FCB EXT F$CLOSE ;Close file EXT SHFTRH ;Shift HL right one bit. EXT SHFTLH ;Shift HL left one bit. EXT f$exist ;File existence test EXT f$appl ;Append to a file ; CP/M Equates and ASCII Constants ; TBUFF EQU 80H ;Temporary File I/O buffer CTRLZ EQU 'Z'-'@' ;^Z 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 ; **** Macro Routines for FXIO **** ; PUTRG MACRO ;Save BC, DE, HL PUSH BC PUSH DE PUSH HL ENDM GETRG MACRO ;Restore BC, DE, HL POP HL POP DE POP BC ENDM ; **** Support Routines for FXIO **** ; ; ; FXIO0 - Routine to read next buffer-full from disk ; FXIO0: LD A,(EOF) ;Check for EOF OR A ;Abort if already at EOF RET NZ ;NZ is error code ; LD HL,(BUFADR) ;Get address of input buffer LD A,(BCNT) ;Get block count LD B,A ;... in B FXIO1: PUSH BC ;Save remaining block count. PUSH HL ;Save current Input buffer ptr EX DE,HL ;Get buffer addr in DE LD C,B$DMA ;Point BDOS DMA ptr to input buffer CALL BDOS ; LD HL,(FCB) ;Get FCB address EX DE,HL ;... IN DE LD C,B$RD ;Read block via BDOS CALL BDOS POP HL ;Restore input buffer ptr POP BC ;Restore remaining block count OR A ;Check Read return code. JR NZ,FXIO2 ;Br if End-of-File ; LD DE,128 ;Get block length ADD HL,DE ;Update buffer addr to point to next block ; djnz FXIO1 ;Loop until all blocks written. JR FXIO3 ;Done. FXIO2: LD A,0FFH ;Set "EOF" condition LD (EOF),A ;Set EOF flag ; FXIO3: LD HL,(BUFADR) ;Pt to first byte in buffer LD (BYTENXT),HL ; as "next byte" address ; LD A,(BCNT) ;Get block count SUB B ;Adjust by # empty blocks LD H,A ;Convert to bytes LD L,0 CALL SHFTRH LD (BYTECNT),HL ;Set byte count ; PUSH HL ;Save byte count LD DE,TBUFF ;Reset DMA address (for compatability) LD C,B$DMA CALL BDOS POP HL ;Get byte count ; LD A,H ;Set EOF return code based on byte count OR L JR Z,FXIO4 XOR A ;Set no error (data available) RET FXIO4: OR 0FFH ;Set error (no data available) RET ; ; FXOO0 - Routine to flush buffer to disk and set up for next write ; FXOO0: LD HL,(BYTECNT) ;Get # of bytes yet to go CALL SHFTLH ;Convert to blocks LD A,(BCNT) ;Get # of blocks in buffer SUB H ;Compute number to write LD B,A ;Get final block count in B LD HL,(BUFADR) ;Pt to first byte to write ; FXOO1: LD A,B ;Check if write complete OR A ;0=Done JR Z,FXOO2 ; DEC B ;Decrement block counter PUSH BC ;Save remaining block count PUSH HL ;Save current Output buffer ptr EX DE,HL ;Get buffer addr in DE LD C,B$DMA ;Point BDOS DMA ptr to output buffer CALL BDOS ; LD HL,(FCB) ;Get FCB address EX DE,HL ;... IN DE LD C,B$WR ;Write block via BDOS CALL BDOS POP HL ;Restore output buffer ptr POP BC ;Restore remaining block count ; LD DE,128 ;Get block length ADD HL,DE ;Update buffer addr to point to next block ; OR A ;Check Write return code. JR Z,FXOO1 ;If OK, Loop until all blocks have been written ; OR 0FFH ;Set error code RET ; FXOO2 - Routine to init buffers for next write ; FXOO2: LD HL,(BUFADR) ;Pt to first byte in buffer LD (BYTENXT),HL ; as "next byte" address ; XOR A ;SET NO EOF LD (EOF),A ; LD A,(BCNT) ;Get block count LD H,A ;Convert to bytes LD L,0 CALL SHFTRH LD (BYTECNT),HL ;Set byte count ; LD DE,TBUFF ;Reset DMA address (for compatability) LD C,B$DMA CALL BDOS ; XOR A ;No Error RET ; **** Base Routines for FXIO **** ; ; ; FXI$OPEN - Open file/buffers for Byte-Oriented Input (GET) ; on input, DE pts to I/O Control Block ; on output, A=0 and Zero flag set if error (File not found) ; FXI$OPEN: PUTRG ;Save registers CALL PUTADR ;Copy I/O Control Block data XOR A ;Set no EOF LD (EOF),A LD HL,(FCB) ;Get FCB address EX DE,HL ;... in DE CALL INITFCB ;Init FCB CALL F$OPEN ;Attempt to open file JR NZ,ERRET ;NZ = Error (File not found) ; CALL FXIO0 ;OK - Fill buffer with data ; ; Normal return ; OKRET: CALL GETADR ;Update I/O Control Block data GETRG ;Restore regs OR 0FFH ;Indicate success RET ; Error return ; ERRET: GETRG ;Restore regs XOR A ;Indicate Error RET ; FXO$OPEN - Open file/buffers for Byte-Oriented Output (PUT) ; on input, DE pts to I/O Control Block ; on output, A=0 and Zero flag set if error (No Directory space) ; FXO$OPEN: PUTRG ;Save registers CALL PUTADR ;Copy I/O Control Block data CALL FXOO2 ;Init buffers LD HL,(FCB) ;Get FCB address EX DE,HL ;... in DE CALL INITFCB ;Init FCB CALL F$MOPEN ;Open and/or Create file JR NZ,ERRET ;NZ = Error (No Directory Space) JR OKRET ;OK- Return and update I/O Control Block ; FXA$OPEN - Open file/buffers for Byte-Oriented Output (PUT) with Append ; on input, DE pts to I/O Control Block ; on output, A=0 and Zero flag set if error (No Directory space or ; File is full) ; FXA$OPEN: PUTRG ;Save registers CALL PUTADR ;Copy I/O Control Block data CALL FXOO2 ;Init buffers LD HL,(FCB) ;Get FCB address EX DE,HL ;... in DE CALL INITFCB ;Init FCB CALL F$EXIST ; See if file exists JR NZ,START CALL F$MOPEN ; File does NOT exist -- create it JR NZ,ERRET ; NZ = Error (No Directory Space) JR OKRET start: CALL F$APPL ; Open file for append JR Z,START1 CP 3 JR NZ,ERRET ; Error - file full JR OKRET ; take this jump if file is empty start1: LD DE,(BUFADR) ; Scan buffer for Ctl-Z LD HL,TBUFF PUSH DE LD BC,080h PUSH BC LDIR POP BC POP HL LD A,CTRLZ CPIR JR NZ,START2 ; ctl-Z not found DEC HL ; found ctl-Z in buffer INC BC start2: LD (BYTENXT),HL ; Set place to store next byte LD A,(BCNT) ; Get count of blocks minus 1 DEC A LD H,A ; Convert to bytes LD L,0 CALL SHFTRH ADD HL,BC ; Add # bytes from first sector LD (BYTECNT),HL ; Store in # available bytes JR OKRET ; ; FX$GET - Get next byte from buffer/file ; on input, DE pts to I/O Control Block ; on output, A=Char and Zero flag set if past EOF ; FX$GET: PUTRG ;Save registers ; ; Check if data byte is in buffer. ; EX DE,HL ;HL -> I/O Control Block data INC HL ;Get caller's BYTECNT in DE INC HL LD E,(HL) INC HL LD D,(HL) DEC HL ;Pt to caller's BYTECNT again. LD A,D ;Is the data byte in the buffer? OR E JR Z,FXGET1 ;No - Fill buffer and GET byte. ; ; It is. GET it and update BYTECNT, BYTENXT as quickly as possible. ; DEC DE ;Update byte count LD (HL),E ;Update caller's BYTECNT. INC HL LD (HL),D ; INC HL ;Get caller's BYTENXT. LD E,(HL) INC HL LD D,(HL) ; LD A,(DE) ;GET data byte from buffer LD (BYTE),A ;Save for return. ; INC DE ;Update caller's BYTENXT. LD (HL),D DEC HL LD (HL),E ; GETRG ;Restore regs OR 0FFH ;Indicate success LD A,(BYTE) ;Get data byte RET ; ; Data byte not in buffer - Fill buffer from file first ; FXGET1: EX DE,HL ;DE -> I/O Control Block data. DEC DE DEC DE CALL PUTADR ;Copy I/O Control Block data CALL FXIO0 ;Fill buffer from file JP NZ,ERRET ;NZ = Error (End of File) ; LD HL,(BYTENXT) ;Pt to first byte in buffer LD A,(HL) ;GET from buffer LD (BYTE),A ;Save it INC HL ;Pt to next byte LD (BYTENXT),HL ;Update next byte pointer LD HL,(BYTECNT) ;One less byte in buffer DEC HL LD (BYTECNT),HL ;Update byte count ; ; Normal return ; OKRET1: CALL GETADR ;Update I/O Control Block data GETRG ;Restore regs OR 0FFH ;Indicate success LD A,(BYTE) ;Get data byte RET ; FX$PUT - Put next byte into buffer/file ; on input, A=char and DE pts to I/O Control Block ; on output, A=Char and Zero flag set if write error ; FX$PUT: PUTRG ;Save registers LD (BYTE),A ;Save byte to output ; ; Check if data byte will fit in buffer. ; EX DE,HL ;HL -> I/O Control Block data INC HL ;Get caller's BYTECNT in DE INC HL LD E,(HL) INC HL LD D,(HL) DEC HL ;Pt to caller's BYTECNT again. LD A,D ;Will the data byte fit in the buffer? OR E JR Z,FXPUT1 ;No - Flush buffer to file and PUT byte. ; ; It is. PUT it and update BYTECNT, BYTENXT as quickly as possible. ; DEC DE ;Update byte count LD (HL),E ;Update caller's BYTECNT. INC HL LD (HL),D ; INC HL ;Get caller's BYTENXT. LD E,(HL) INC HL LD D,(HL) ; LD A,(BYTE) ;Get data byte LD (DE),A ;PUT data byte in buffer ; INC DE ;Update caller's BYTENXT. LD (HL),D DEC HL LD (HL),E ; GETRG ;Restore regs OR 0FFH ;Indicate success LD A,(BYTE) ;Get data byte RET ; ; Data byte will not fit in buffer - Flush buffer to file first. ; FXPUT1: EX DE,HL ;DE -> I/O Control Block data. DEC DE DEC DE CALL PUTADR ;Copy I/O Control Block data CALL FXOO0 ;NO - Flush buffer to file JP NZ,ERRET ;NZ = Error (Write error) ; LD HL,(BYTENXT) ;Pt to first byte in buffer LD A,(BYTE) ;Get next byte LD (HL),A ;PUT in buffer INC HL ;Pt to next byte LD (BYTENXT),HL ;Update next byte pointer LD HL,(BYTECNT) ;One less byte of free space in buffer DEC HL LD (BYTECNT),HL ;Update byte count JR OKRET1 ;OK-return with byte and updated control block ; FXI$CLOSE - Close file/buffers for Byte-Oriented Input (GET) ; on input, DE pts to I/O Control Block ; on output, A=0 and Zero flag set if error (Error in closing file) ; FXI$CLOSE: PUTRG ;Save registers CALL PUTADR ;Copy I/O Control Block data LD HL,(FCB) ;Get FCB address EX DE,HL ;... in DE CALL F$CLOSE ;Close file JP NZ,ERRET ;NZ = Error (Close file error) JP OKRET ;OK- Return and update I/O Control Block ; FXO$CLOSE - Close file/buffers for Byte-Oriented Output (PUT) ; on input, DE pts to I/O Control Block ; on output, A=0 and Zero flag set if error (Error in closing file) ; FXO$CLOSE: PUTRG ;Save registers CALL PUTADR ;Copy I/O Control Block data ; ; Fill last block with ^Z ; FXOCL1: LD HL,(BYTECNT) ;Get free space count. LD A,L ;If on page boundary, done AND 7FH JR Z,FXOCL2 DEC HL ;One less byte of free space in buffer LD (BYTECNT),HL ;Update byte count LD HL,(BYTENXT) ;Pt to next byte LD (HL),CTRLZ ;Store EOF char INC HL LD (BYTENXT),HL JR FXOCL1 ;Loop until last block is full ; ; Close file and exit ; FXOCL2: CALL FXOO0 ;Flush buffers to disk LD HL,(FCB) ;Get FCB address EX DE,HL ;... in DE CALL F$CLOSE ;Close file JP NZ,ERRET ;NZ = Error (Close file error) JP OKRET ;OK- Return and update I/O Control Block ; PUTADR - Copy I/O Control Block data to local storage ; PUTADR: EX DE,HL ;Get I/O Control Block addr in HL LD (BUFFER),HL ;Save I/O Control Block address ; LD A,(HL) ;Get block count LD (BCNT),A ; INC HL LD A,(HL) ;Get eof flag LD (EOF),A ; INC HL LD E,(HL) ;Get low count INC HL LD D,(HL) EX DE,HL LD (BYTECNT),HL ;Put byte count ; EX DE,HL INC HL LD E,(HL) ;Get low address INC HL LD D,(HL) EX DE,HL LD (BYTENXT),HL ;Put next byte ptr ; EX DE,HL INC HL LD E,(HL) ;Get low address INC HL LD D,(HL) EX DE,HL LD (BUFADR),HL ;Put buffer address ptr ; EX DE,HL INC HL LD (FCB),HL ;Save address of FCB RET ; GETADR - Update I/O Control Block data from local storage ; GETADR: LD HL,(BUFFER) ;Get I/O Control Block address ; INC HL ;Skip block count ; LD A,(EOF) ;Get EOF flag LD (HL),A ; EX DE,HL LD HL,(BYTECNT) ;Get byte count EX DE,HL INC HL LD (HL),E ;Set low count INC HL LD (HL),D EX DE,HL ; LD HL,(BYTENXT) ;Get next byte pointer EX DE,HL INC HL LD (HL),E ;Set low address INC HL LD (HL),D RET ; ; BUFFERS ; BYTE: DS 1 ;Data byte BUFFER: DS 2 ;Starting address of I/O control block ; The following mirrors the structure of the I/O Control Block ; BCNT: DS 1 ;Number of blocks in buffer EOF: DS 1 ;EOF flag (0=not at EOF, 0FFH=at EOF) BYTECNT: DS 2 ;Number of bytes to go yet BYTENXT: DS 2 ;Address of next byte to PUT/GET BUFADR: DS 2 ;Address of working buffer FCB: DS 2 ;Address of FCB END ress INC HL LD D,(HL) EX DE,HL LD (BUFADR),HL ;Put buffer address ptr ; EX DE,HL INC HL LD (FCB),HL ;S