; SEQUENTIAL FILE I/O LIBRARY ; FILERR SET 0000H ;REBOOT AFTER ERROR @BDOS EQU 0005H ;BDOS ENTRY POINT @TFCB EQU 005CH ;DEFAULT FILE CONTROL BLOCK @TBUF EQU 0080H ;DEFAULT BUFFER ADDRESS ; ; BDOS FUNCTIONS @MSG EQU 9 ;SEND MESSAGE @OPN EQU 15 ;FILE OPEN @CLS EQU 16 ;FILE CLOSE @DIR EQU 17 ;DIRECTORY SEARCH @DEL EQU 19 ;FILE DELETE @FRD EQU 20 ;FILE READ OPERATION @FWR EQU 21 ;FILE WRITE OPERATION @MAK EQU 22 ;FILE MAKE @REN EQU 23 ;FILE RENAME @DMA EQU 26 ;SET DMA ADDRESS ; @SECT EQU 128 ;SECTOR SIZE EOF EQU 1AH ;END OF FILE CR EQU 0DH ;CARRIAGE RETURN LF EQU 0AH ;LINE FEED TAB EQU 09H ;HORIZONTAL TAB ; @KEY EQU 1 ;KEYBOARD @CON EQU 2 ;CONSOLE DISPLAY @RDR EQU 3 ;READER @PUN EQU 4 ;PUNCH @LST EQU 5 ;LIST DEVICE ; ; KEYWORDS FOR "FILE" MACRO INFILE EQU 1 ;INPUT FILE OUTFILE EQU 2 ;OUTPUTFILE SETFILE EQU 3 ;SETUP NAME ONLY ; ; THE FOLLOWING MACROS DEFINE SIMPLE SEQUENTIAL ; FILE OPERATIONS: ; FILLNAM MACRO FC,C ;; FILL THE FILE NAME/TYPE GIVEN BY FC FOR C CHARACTERS @CNT SET C ;;MAX LENGTH IRPC ?FC,FC ;;FILL EACH CHARACTER ;; MAY BE END OF COUNT OR NUL NAME IF @CNT=0 OR NUL ?FC EXITM ENDIF DB '&?FC' ;;FILL ONE MORE @CNT SET @CNT-1 ;;DECREMENT MAX LENGTH ENDM ;;OF IRPC ?FC ;; ;; PAD REMAINDER REPT @CNT ;;@CNT IS REMAINDER DB ' ' ;;PAD ONE MORE BLANK ENDM ;;OF REPT ENDM ; FILLDEF MACRO FCB,?FL,?LN ;; FILL THE FILE NAME FROM THE DEFAULT FCB ;; FOR LENGTH ?LN (9 OR 12) LOCAL PSUB JMP PSUB ;;JUMP PAST THE SUBROUTINE @DEF: ;;THIS SUBROUTINE FILLS FROM THE TFCB (+16) MOV A,M ;;GET NEXT CHARACTER TO A STAX D ;;STORE TO FCB AREA INX H INX D DCR C ;;COUNT LENGTH DOWN TO 0 JNZ @DEF RET ;; END OF FILL SUBROUTINE PSUB: FILLDEF MACRO ?FCB,?F,?L LXI H,@TFCB+?F ;;EITHER @TFCB OR @TFCB+16 LXI D,?FCB MVI C,?L ;;LENGTH = 9,12 CALL @DEF ENDM FILLDEF FCB,?FL,?LN ENDM ; FILLNXT MACRO ;; INITIALIZE BUFFER AND DEVICE NUMBERS @NXTB SET 0 ;;NEXT BUFFER LOCATION @NXTD SET @LST+1 ;;NEXT DEVICE NUMBER FILLNXT MACRO ENDM ENDM ; FILLFCB MACRO FID,DN,FN,FT,BS,BA ;; FILL THE FILE CONTROL BLOCK WITH DISK NAME ;; FID IS AN INTERNAL NAME FOR THE FILE, ;; DN IS THE DRIVE NAME (A,B..), OR BLANK ;; FN IS THE FILE NAME, OR BLANK ;; FT IS THE FILE TYPE ;; BS IS THE BUFFER SIZE ;; BA IS THE BUFFER ADDRESS LOCAL PFCB ;; ;; SET UP THE FILE CONTROL BLOCK FOR THE FILE ;; LOOK FOR FILE NAME = 1 OR 2 @C SET 1 ;;ASSUME TRUE TO BEGIN WITH IRPC ?C,FN ;;LOOK THROUGH CHARACTERS OF NAME IF NOT ('&?C' = '1' OR '&?C' = '2') @C SET 0 ;;CLEAR IF NOT 1 OR 2 ENDM ;; @C IS TRUE IF FN = 1 OR 2 AT THIS POINT IF @C ;;THEN FN = 1 OR 2 ;; FILL FROM DEFAULT AREA IF NUL FT ;;TYPE SPECIFIED? @C SET 12 ;;BOTH NAME AND TYPE ELSE @C SET 9 ;;NAME ONLY ENDIF FILLDEF FCB&FID,(FN-1)*16,@C ;;TO SELECT THE FCB JMP PFCB ;;PAST FCB DEFINITION DS @C ;;SPACE FOR DRIVE/FILENAME/TYPE FILLNAM FT,12-@C ;;SERIES OF DB'S ELSE JMP PFCB ;;PAST INITIALIZED FCB IF NUL DN DB 0 ;;USE DEFAULT DRIVE IF NAME IS ZERO ELSE DB '&DN'-'A'+1 ;;USE SPECIFIED DRIVE ENDIF FILLNAM FN,8 ;;FILL FILE NAME ;; NOW GENERATE THE FILE TYPE WITH PADDED BLANKS FILLNAM FT,3 ;;AND THREE CHARACTER TYPE ENDIF FCB&FID EQU $-12 ;;BEGINNING OF THE FCB DB 0 ;;EXTENT FIELD 00 FOR SETFILE ;; NOW DEFINE THE 3 BYTE FIELD, AND DISK MAP DS 20 ;;X,X,RC,DM0...DM15,CR FIELDS ;; IF FID&TYP<=2 ;;IN/OUTFILE ;; GENERATE CONSTANTS FOR INFILE/OUTFILE FILLNXT ;;@NXTB=0 ON FIRST CALL IF BS+0<@SECT ;; BS NOT SUPPLIED, OR TOO SMALL @BS SET @SECT ;;DEFAULT TO ONE SECTOR ELSE ;; COMPUTE EVEN BUFFER ADDRESS @BS SET (BS/@SECT)*@SECT ENDIF ;; ;; NOW DEFINE BUFFER BASE ADDRESS IF NUL BA ;; USE NEXT ADDRESS AFTER @NXTB FID&BUF SET BUFFERS+@NXTB ;; COUNT PAST THIS BUFFER @NXTB SET @NXTB+@BS ELSE FID&BUF SET BA ENDIF ;; FID&BUF IS BUFFER ADDRESS FID&ADR: DW FID&BUF ;; FID&SIZ EQU @BS ;;LITERAL SIZE FID&LEN: DW @BS ;;BUFFER SIZE FID&PTR: DS 2 ;;SET IN INFILE/OUTFILE ;; SET DEVICE NUMBER @&FID SET @NXTD ;;NEXT DEVICE @NXTD SET @NXTD+1 ENDIF ;;OF FID&TYP<=2 TEST PFCB: ENDM ; FILE MACRO MD,FID,DN,FN,FT,BS,BA ;; CREATE FILE USING MODE MD: ;; INFILE = 1 INPUT FILE ;; OUTFILE = 2 OUTPUT FILE ;; SETFILE = 3 SETUP FCB ;; (SEE FILLFCB FOR REMAINING PARAMETERS) LOCAL PSUB,MSG,PMSG LOCAL PND,EOD,EOB,PNC ;; CONSTRUCT THE FILE CONTROL BLOCK ;; FID&TYP EQU MD ;;SET MODE FOR LATER REF'S FILLFCB FID,DN,FN,FT,BS,BA IF MD=3 ;;SETUP FCB ONLY, SO EXIT EXITM ENDIF ;; FILE CONTROL BLOCK AND RELATED PARAMETERS ;; ARE CREATED INLINE, NOW CREATE IO FUNCTION JMP PSUB ;;PAST INLINE SUBROUTINE IF MD=1 ;;INPUT FILE GET&FID: ELSE PUT&FID: PUSH PSW ;;SAVE OUTPUT CHARACTER ENDIF LHLD FID&LEN ;;LOAD CURRENT BUFFER LENGTH XCHG ;;DE IS LENGTH LHLD FID&PTR ;;LOAD NEXT TO GET/PUT TO HL MOV A,L ;;COMPUTE CUR-LEN SUB E MOV A,H SBB D ;;CARRY IF NEXT ;; SKIP ALL BUT OUTPUT FILES IF ?F&TYP=2 LOCAL EOB?,PEOF,MSG,PMSG ;; WRITE ALL PARTIALLY FILLED BUFFERS EOB?: ;;ARE WE AT THE END OF A BUFFER? LHLD ?F&PTR ;;NEXT TO FILL MOV A,L ;;ON BUFFER BOUNDARY? ANI (@SECT-1) AND 0FFH JNZ PEOF ;;PUT EOF IF NOT 00 IF @SECT>255 ;; CHECK HIGH ORDER BYTE ALSO MOV A,H ANI (@SECT-1) SHR 8 JNZ PEOF ;;PUT EOF IF NOT 00 ENDIF ;; ARRIVE HERE IF END OF BUFFER, SET LENGTH ;; AND WRITE ONE MORE BYTE TO CLEAR BUFFS SHLD ?F&LEN ;;SET TO SHORTER LENGTH PEOF: MVI A,EOF ;;WRITE ANOTHER EOF PUSH PSW ;;SAVE ZERO FLAG CALL PUT&?F POP PSW ;;RECALL ZERO FLAG JNZ EOB? ;;NON ZERO IF MORE ;; BUFFERS HAVE BEEN WRITTEN, CLOSE FILE MVI C,@CLS LXI D,FCB&?F ;;READY FOR CALL CALL @BDOS INR A ;;255 IF ERR BECOMES 00 JNZ PMSG ;; FILE CANNOT BE CLOSED MVI C,@MSG LXI D,MSG CALL @BDOS JMP PMSG ;;ERROR MESSAGE PRINTED MSG: DB CR,LF DB 'CANNOT CLOSE &?F' DB '$' PMSG: ENDIF ENDM ;;OF THE IRP ENDM ; ERASE MACRO FID ;; DELETE THE FILE(S) GIVEN BY FID IRP ?F, MVI C,@DEL LXI D,FCB&?F CALL @BDOS ENDM ;;OF THE IRP ENDM ; DIRECT MACRO FID ;; PERFORM DIRECTORY SEARCH FOR FILE ;; SETS ZERO FLAG IF NOT PRESENT LXI D,FCB&FID MVI C,@DIR CALL @BDOS INR A ;00 IF NOT PRESENT ENDM ; RENAME MACRO NEW,OLD ;; RENAME FILE GIVEN BY "OLD" TO "NEW" LOCAL PSUB,REN0 ;; INCLUDE THE RENAME SUBROUTINE ONCE JMP PSUB @RENS: ;;RENAME SUBROUTINE, HL IS ADDRESS OF ;;OLD FCB, DE IS ADDRESS OF NEW FCB PUSH H ;;SAVE FOR RENAME LXI B,16 ;;B=00,C=16 DAD B ;;HL = OLD FCB+16 REN0: LDAX D ;;NEW FCB NAME MOV M,A ;;TO OLD FCB+16 INX D ;;NEXT NEW CHAR INX H ;;NEXT FCB CHAR DCR C ;;COUNT DOWN FROM 16 JNZ REN0 ;; OLD NAME IN FIRST HALF, NEW IN SECOND HALF POP D ;;RECALL BASE OF OLD NAME MVI C,@REN ;;RENAME FUNCTION CALL @BDOS RET ;;RENAME COMPLETE PSUB: RENAME MACRO N,O ;;REDEFINE RENAME LXI H,FCB&O ;;OLD FCB ADDRESS LXI D,FCB&N ;;NEW FCB ADDRESS CALL @RENS ;;RENAME SUBROUTINE ENDM RENAME NEW,OLD ENDM ; GET MACRO DEV ;; READ CHARACTER FROM DEVICE IF @&DEV <= @LST ;; SIMPLE INPUT MVI C,@&DEV CALL @BDOS ELSE CALL GET&DEV ENDM ;