TITLE 'XDIR Ver 1.0 for CP/M-86' ; ; XDIR.A86 for CP/M-86, as of February 18, 1981: Kelly Smith ; FALSE EQU 0 TRUE EQU NOT FALSE ; ;USER OPTION SPECIFICATIONS ; AOPT EQU TRUE ;TRUE TO ALLOW SEARCHING ALL USER AREAS DOPT EQU TRUE ;TRUE TO ALLOW SEARCHING ALL DRIVES ON-LINE FOPT EQU TRUE ;TRUE TO ALLOW FILE OUTPUT OPTION NOPT EQU TRUE ;TRUE TO ALLOW DISABLING PAGE PAUSE OPTION PGPAWZ EQU TRUE ;TRUE FOR PAUSE AFTER EACH PAGE POPT EQU TRUE ;TRUE TO ALLOW PRINTER OPTION REPERR EQU TRUE ;TRUE TO REPORT COMMAND LINE OPTION ERRORS REPSIZ EQU TRUE ;TRUE TO REPORT FILE SIZES SOPT EQU TRUE ;TRUE TO ALLOW SYSTEM FILE OPTION UOPT EQU TRUE ;TRUE TO ALLOW USER NUMBER OPTION DELIM EQU 7CH ;FENCE (DELIMITER) CHARACTER (VERTICAL BAR) BIGCRT EQU TRUE ;TRUE = 24/80 DISPLAY, FALSE = 16/64 DISPLAY NPL EQU 4 ;# OF NAMES PER LINE (MAX OF 3 FOR 64X16) ; (MAX OF 4 FOR 80X24) LPS EQU 20 ;# OF LINES PER SCREEN (MAX OF 12 FOR 64X16) ; (MAX OF 20 FOR 80X24) ; ; BDOS EQUATES ; RDCHR EQU 1 ;READ CHAR FROM CONSOLE WRCHR EQU 2 ;WRITE CHAR TO CONSOLE CONST EQU 11 ;CHECK CONS STAT RESET EQU 13 ;RESET DISK SYSTEM SELDSK EQU 14 ;SELECT DISK OPEN EQU 15 ;0FFH=NOT FOUND CLOSE EQU 16 ; " " SEARCH EQU 17 ; " " NEXT EQU 18 ; " " READ EQU 20 ;NOT 0 = EOF WRITE EQU 21 ;NOT 0 = DISK FULL MAKE EQU 22 ;0FFH = DIRECTORY FULL CURDSK EQU 25 ;GET CURRENTLY LOGGED DISK NAME SETDMA EQU 26 ;SET CURRENT DMA GALLOC EQU 27 ;GET ADDRESS OF ALLOCATION VECTOR CURDPB EQU 31 ;GET CURRENT DISK PARAMETERS CURUSR EQU 32 ;GET CURRENTLY LOGGED USER NUMBER BASE EQU 0 TPA EQU 100H FCB EQU BASE+5CH ; M EQU Byte Ptr 0[BX] ; ; BEGIN EXECUTABLE PROGRAM CODE ; ORG TPA START: MOV DL,0FFH ;GET CURRENT USER NUMBER MOV CL,CURUSR CALL CPM MOV Byte Ptr OLDUSR,AL ;INITIALIZE STARTUP USER NUMBER MOV Byte Ptr NEWUSR,AL ;..AND MAKE NEW USER MATCH IT MOV Byte Ptr BASUSR,AL ;SAVE EXTRA COPY FOR MULTI-DISK DIRECTORIES MOV CL,CURDSK CALL CPM ;GET CURRENT DISK NR MOV Byte Ptr OLDDSK,AL ;SAVE FOR RESET IF NEEDED INC AL MOV Byte Ptr OUTFCB,AL ;SET DIRECTORY OUTPUT FILE DRIVE MOV BX,FCB MOV AL,M ;GET DRIVE NAME FOR DIRECTORY SEARCH OR AL,AL ;ANY SPECIFIED? JNZ START2 ;YES SKIP NEXT ROUTINE MOV AL,Byte Ptr OLDDSK ;OTHERWISE, GET DEFAULT DISK INC AL START2: MOV M,AL ;PUT THE ABSOLUTE DRIVE CODE IN DIRECTORY FCB ; ; IF AT LEAST ONE OPTION IS ALLOWED, SCAN THE COMMAND LINE FOR THE ; OPTION FIELD DELIMITER. THE OPTION FIELD DELIMITER IS CONSIDERED ; VALID ONLY IF IT IS PRECEDED BY AT LEAST 1 SPACE (OTHERWISE, IT ; MAY BE PART OF THE DIRECTORY FILENAME). ANY UNRECOGNIZED OPTIONS ; OR ILLEGAL USER NUMBERS WILL BE FLAGGED OR IGNORED (SEE REPERR). ; (WE SCAN THE COMMAND LINE BUFFER RATHER THAN THE 2ND DEFAULT FCB ; BECAUSE ALL 8 OPTIONS PLUS A 2 DIGIT USER NUMBER WON'T FIT IN ; THE FCB NAME FIELD). ; MOV BX,80H ;SET COMMAND LINE BUFFER POINTER MOV CH,M ;GET LENGTH OF COMMAND LINE BUFFER ; ; SEARCH FOR THE COMMAND LINE DELIMITER. IF NOT FOUND, ASSUME NO OPTIONS. ; SCNDOL: LAHF INC BX SAHF DEC CH JNS L_1 JMP CKREST ;EXIT IF COMMAND LINE BUFFER EMPTY L_1: MOV AL,M CMP AL,'$' JNZ SCNDOL DEC BX ;'$' FOUND - MAKE SURE SPACE PRECEDES IT MOV AL,M INC BX CMP AL,' ' JNZ SCNDOL ;NO SPACE - IGNORE "$" AND SEARCH AGAIN ; ; VALID DELIMITER FOUND. SCAN THE REST OF THE BUFFER FOR OPTIONS. ERRORS ; PAST THIS POINT WILL CAUSE AN ABORT IF THE COMMAND LINE ERROR OPTION IS ; ENABLED. OTHERWISE, THE DUD OPTION WILL BE IGNORED AND SD WILL ATTEMPT ; TO CONTINUE STUMBLING THROUGH THE REST OF THE FIELD. ; XCHG BX,DX ;GET OPTION FIELD POINTER TO DE SCNOPT: LAHF ;BUMP TO NEXT OPTION FIELD CHARACTER INC DX SAHF DEC CH ;DOCK CHARACTERS LEFT IN OPTION FIELD JNS L_2 JMP CKREST ;IF OPTION FIELD EXHAUSTED, EXIT L_2: SCNAGN: MOV SI,DX ;GET THE NEXT OPTION CHARACTER MOV AL,[SI] CMP AL,' ' ;DO WE HAVE A SPACE? JZ SCNOPT ;IGNORE IT IF SO MOV BX,(Offset OTBL)-1 ;GET BASE OF OPTION LOOKUP TABLE MOV CL,(Offset OEND)-(Offset OTBL)+1 ;GET LENGTH OF OPTION LOOKUP TABLE NOMACH: INC BX ;BUMP TO NEXT OPTION TABLE CHARACTER DEC CL ;ARE WE OUT OF THE TABLE? JZ CK4USR ;IF SO, CHECK FOR USER OPTION CMP AL,M ;COMPARE OUR CHARACTER WITH OPTION TABLE JNZ NOMACH ;EXIT IF NO MATCH MOV M,0 ;OTHERWISE, ACTIVATE THE FLAG JMPS SCNOPT ;..AND GO GET THE NEXT OPTION CHARACTER ; ; IF OPTION CHARACTER DOESN'T MATCH THE TABLE, SEE IF WE HAVE A USER ; OPTION. ; IF UOPT CK4USR: ;CHECK FOR USER NUMBER OPTION CMP AL,'U' JNZ CLERR ;LAST OPTION, SO BAD DEAL IF THAT AIN'T IT UAGN: LAHF ;BUMP TO USER NUMBER DIGIT INC DX SAHF DEC CH JS CLERR ;ERROR IF NOTHING LEFT MOV SI,DX ;GET DECIMAL DIGIT MOV AL,[SI] CMP AL,' ' ;IGNORE LEADING SPACES JZ UAGN SUB AL,30H ;SUBTRACT ASCII BIAS JB CLERR ;ERROR IF < 0 CMP AL,10 JNB CLERR ;ERROR IF > 9 MOV Byte Ptr NEWUSR,AL ;SAVE USER NUMBER AS IT MAY BE ONLY 1 DIGIT MOV Byte Ptr BASUSR,AL ;DUPLICATE IT IF MULTI-DISK MODE LAHF ;BUMP TO POSSIBLE 2ND DIGIT OF USER NUMBER INC DX SAHF DEC CH JS CKREST ;IF NO MORE BUFFER, EXIT WITH COMPLETE USER # MOV SI,DX ;ELSE, CHECK FOR ANOTHER DIGIT MOV AL,[SI] SUB AL,30H JB SCNAGN ;IF NEXT CHAR NOT NUMERIC, ITS NOT PART OF CMP AL,10 ;..USER NUMBER SO GO CHECK FOR ANOTHER OPTION JNB SCNAGN MOV BL,AL ;SAVE UNITS DIGIT MOV AL,Byte Ptr NEWUSR ;GET TENS DIGIT ADD AL,AL ;MULTIPLY BY 10 MOV BH,AL ADD AL,AL ADD AL,AL ADD AL,BH ADD AL,BL ;COMBINE WITH UNITS DIGIT MOV Byte Ptr NEWUSR,AL ;SAVE THE TOTAL USER NUMBER MOV Byte Ptr BASUSR,AL ;DUPLICATE IT IF MULTI-DISK MODE JMP SCNOPT ;CONTINUE SCANNING ENDIF ; ; IF COMMAND LINE ERROR OPTION ENABLED, PLAYBACK THE COMMAND LINE UP ; TO THE CHARACTER THAT WE GAGGED ON AND EXIT. IF REPERR IS NOT ENABLED, ; THEN CONTINUE AS IF NOTHING WERE AMISS TO AVOID ACKNOWLEDGING THAT ; SOME OPTIONS ARE AVAILABLE. ; IF REPERR CLERR: XOR AL,AL LAHF ;TAG END OF COMMAND LINE WITH TERMINATOR INC DX SAHF MOV SI,DX MOV [SI],AL CALL CRLF MOV DX,(Offset ERRMS2) CALL PRINT MOV DX,(Offset ERRTAG) CALL PRINT MOV BX,81H ;PLAYBACK BAD COMMAND LINE TO ERROR POINT CLELP: MOV AL,M OR AL,AL JZ CLEX CALL SEND_CHAR INC BX JMPS CLELP CLEX: MOV AL,'?' ;TAG LINE WITH A '?' FIELD CALL SEND_CHAR CALL CRLF ;SPACE DOWN 1 MORE LINE JMP EXIT ;..AND RETURN TO CP/M ENDIF ; ; OPTIONS INPUT OR NOT SPECIFIED. IF RESET OPTION SPECIFIED, DO IT NOW. ; CKREST: MOV AL,Byte Ptr ROPFLG ;IF RESET FLAG SET, RESET DISK SYSTEM BEFORE OR AL,AL ;..STARTING TO UPDATE ALLOCATION VECTORS MOV CL,RESET JNZ L_3 CALL CPM L_3: ; ; VALIDATE DRIVE CODE AND USER AREA NUMBERS FROM THE DRIVE TABLE. ; NOOPT: MOV DX,(Offset DREMSG) ;GET THE DRIVE/USER ERROR MESSAGE PUSH DX MOV AL,Byte Ptr .FCB ;GET DIRECTORY DRIVE CODE DEC AL ;NORMALIZE TO RANGE OF 0-15 CMP AL,(Offset HIDRV)-(Offset LODRV) ;COMPARE WITH MAXIMUM DRIVES ON-LINE JNAE L_4 JMP ERXIT ;TAKE DRIVE ERROR EXIT IF OUT OF RANGE L_4: MOV BX,(Offset USRMSG) ;SWITCH TO USER # ERROR MESSAGE MOV BP,SP XCHG BX,[BP] MOV DL,AL ;USE DRIVE CODE AS INDEX INTO TABLE MOV DH,0 MOV BX,(Offset LODRV) ;POINT TO BASE OF DRIVE/USER TABLE ADD BX,DX MOV AL,M ;GET THE MAXIMUM USER # FOR THIS DRIVE AND AL,0FH ;MAKE SURE ITS IN RANGE 0 - 15 MOV Byte Ptr MAXUSR,AL ;SAVE IT FOR LATER MOV BX,(Offset NEWUSR) ;POINT TO THE DIRECTORY USER AREA CMP AL,M ;COMPARE IT WITH THE MAXIMUM JNB L_5 JMP ERXIT ;TAKE ERROR EXIT IF USER NUMBER ILLEGAL L_5: POP DX ;DESTROY ERROR MESSAGE POINTER MOV BX,FCB+1 ;POINT TO NAME MOV AL,M ;ANY SPECIFIED? CMP AL,' ' JNZ GOTFCB ; ; NO FCB - MAKE FCB ALL '?' ; MOV CH,11 ;FN+FT COUNT QLOOP: MOV M,'?' ;STORE '?' IN FCB LAHF INC BX SAHF DEC CH JNZ QLOOP GOTFCB: MOV AL,'?' ;FORCE WILD EXTENT MOV Byte Ptr .FCB+12,AL CALL SETSRC ;SET DMA FOR BDOS MEDIA CHANGE CHECK MOV BX,FCB ;POINT TO FCB DRIVE CODE FOR DIRECTORY MOV DL,M ;GET THE DRIVE CODE OUT OF THE FCB DEC DL ;NORMALIZE DRIVE CODE FOR SELECT MOV CL,SELDSK ;SELECT THE DIRECTORY DRIVE TO RETRIEVE CALL CPM ;..THE PROPER ALLOCATION VECTOR MOV CL,curdpb ;get disk parameter block INT 224 INC BX ;point to block shift INC BX MOV AL,ES: M ;[AL] = block shift = log2(records/block) SUB AL,3 ;[AL] = log2(kbytes/block) MOV Byte Ptr blkshf,AL INC BX ;point to disk size mov al,es: m ;get block mask, and save it mov Byte Ptr blkmsk,al INC BX INC BX MOV DL,ES: M INC BX MOV DH,ES: M MOV Word Ptr dsksiz,DX ;dsksiz = blocks/dsk ; ; ; Now get amount used on disk ; allctd: MOV CL,galloc ;get allocation vector address INT 224 ; push ds ;save the data segment at all cost... MOV DX,Word Ptr dsksiz ;get blocks/disk mov si,bx ;make indirect pointer test dl,7 ;big blocks or little blocks? jz divlp1 add dx,8 divlp1: mov cl,3 ;the great divide... shr dx,cl mov cx,dx mov dl,0 push es ;trick to get to proper data segment pop ds cntit: lods ds: m ;nice way to get data to [AL] pointed by 0[BX] from [SI] ;...with nifty increment of pointer at [SI] mov bx,cx mov cx,8 ;scan thru 8 bits for 1's set try1: shr al,1 jnb not1 inc dx ;got a live one, bump 'used' counter not1: loop try1 mov cx,bx loop cntit pop ds ;restore to orginal data segment ; MOV BX,Word Ptr dsksiz ;[BX] = blocks/disk-1, [DX] = blocks used inc bx ;bump for actual disk size MOV AL,BL ;do a sixteen bit subtract SUB AL,DL MOV BL,AL MOV AL,BH SBB AL,DH MOV BH,AL ;[BX] = unused blocks MOV AL,Byte Ptr blkshf ;now convert from blocks to kilobytes OR AL,AL shftit: JZ savit SHL BX,1 DEC AL JMPS shftit ; savit: MOV Word Ptr freeby,BX ;[BX] = kbytes unused ; ; ; REENTER HERE ON SUBSEQUENT PASSES WHILE IN THE ALL-USERS MODE ; SETTBL: MOV BX,Word Ptr dsksiz ;GET DIRECTORY MAXIMUM AGAIN LAHF ;DIRECTORY SIZE IS dsksiz+1 INC BX SAHF SHL BX,1 ;DOUBLE DIRECTORY SIZE MOV DX,(Offset ORDER) ;TO GET SIZE OF ORDER TABLE LAHF ;ALLOCATE ORDER TABLE ADD BX,DX RCR SI,1 SAHF RCL SI,1 MOV Word Ptr TBLOC,BX ;NAME TABLE BEGINS WHERE ORDER TABLE ENDS MOV Word Ptr NEXTT,BX XCHG BX,DX ; ;+++ MAY HAVE TO CHECK FOR AVAILABLE MEMORY HERE +++ ; IF UOPT MOV AL,Byte Ptr NEWUSR ;GET USER AREA FOR DIRECTORY MOV DL,AL MOV CL,CURUSR ;GET THE USER FUNCTION CALL CPM ;..AND SET NEW USER NUMBER ENDIF ; ; LOOK UP THE FCB IN THE DIRECTORY ; SFIRST: MOV BX,0 MOV Word Ptr COUNT,BX ;INITIALIZE MATCH COUNTER MOV Word Ptr TOTFIL,BX ; " TOTAL FILE COUNTER MOV Word Ptr TOTSIZ,BX ; " TOTAL SIZE COUNTER CALL SETSRC ;SET DMA FOR DIRECTORY SEARCH MOV CL,SEARCH ;GET 'SEARCH FIRST' FUNCTION JMPS LOOK ;..AND GO SEARCH FOR 1ST MATCH ; ; READ MORE DIRECTORY ENTRIES ; MORDIR: MOV CL,NEXT ;SEARCH NEXT LOOK: MOV DX,FCB CALL CPM ;READ DIRECTORY ENTRY INC AL ;CHECK FOR END (0FFH) JNZ L_6 JMP SPRINT ;IF NO MORE, SORT & PRINT WHAT WE HAVE L_6: ; ; POINT TO DIRECTORY ENTRY ; SOME: DEC AL ;UNDO PREV 'INR A' AND AL,3 ;MAKE MODULUS 4 ADD AL,AL ;MULTIPLY... ADD AL,AL ;..BY 32 BECAUSE ADD AL,AL ;..EACH DIRECTORY ADD AL,AL ;..ENTRY IS 32 ADD AL,AL ;..BYTES LONG MOV BX,BASE+81H ;POINT TO BUFFER ; ;SKIP TO FN/FT ; ADD AL,BL ;POINT TO ENTRY ADD AL,9 ;POINT TO SYS BYTE MOV BL,AL ;SAVE (CAN'T CARRY TO H) IF SOPT MOV AL,Byte Ptr SOPFLG ;DID USER REQUEST SYS FILES? OR AL,AL JZ SYSFOK ENDIF MOV AL,M ;GET SYS BYTE OR AL,AL ;CHECK BIT 7 JS MORDIR ;SKIP THAT FILE SYSFOK: MOV AL,BL ;GO BACK NOW SUB AL,10 ;BACK TO USER NUMBER (ALLOC FLAG) MOV BL,AL ;HL POINTS TO ENTRY NOW MOV AL,Byte Ptr NEWUSR ;GET CURRENT USER CMP AL,M JNZ MORDIR ;IGNORE IF DIFFERENT INC BX ; ; MOVE ENTRY TO TABLE ; XCHG BX,DX ;ENTRY TO DE MOV BX,Word Ptr NEXTT ;NEXT TABLE ENTRY TO HL MOV CH,12 ;ENTRY LENGTH (NAME, TYPE, EXTENT) TMOVE: MOV SI,DX ;GET ENTRY CHAR MOV AL,[SI] AND AL,7FH ;REMOVE ATTRIBUTES MOV M,AL ;STORE IN TABLE LAHF INC DX SAHF LAHF INC BX SAHF DEC CH ;MORE? JNZ TMOVE LAHF INC DX SAHF LAHF ;POINT TO SECTOR COUNT INC DX SAHF MOV SI,DX ;GET IT MOV AL,[SI] MOV M,AL ;STORE IN TABLE LAHF INC BX SAHF MOV Word Ptr NEXTT,BX ;SAVE UPDATED TABLE ADDR XCHG BX,DX MOV BX,Word Ptr COUNT ;BUMP THE # OF MATCHES MADE LAHF INC BX SAHF MOV Word Ptr COUNT,BX MOV BX,13 ;SIZE OF NEXT ENTRY LAHF ADD BX,DX RCR SI,1 SAHF RCL SI,1 XCHG BX,DX ;FUTURE NEXTT IS IN DE JMP MORDIR ;ATTEMPT TO GET NEXT DIRECTORY ENTRY ; ; SORT AND PRINT ; SPRINT: CALL SETFOP ;RETURN TO FILE OUTPUT DMA & USER # MOV BX,Word Ptr COUNT ;GET FILE NAME COUNT MOV AL,BL OR AL,BH ;ANY FOUND? JNZ L_7 JMP PRTOTL ;EXIT IF NO FILES FOUND L_7: PUSH BX ;SAVE FILE COUNT MOV Byte Ptr SUPSPC,AL ;ENABLE LEADING ZERO SUPPRESSION ; ; INITIALIZE THE ORDER TABLE ; MOV BX,Word Ptr TBLOC ;GET START OF NAME TABLE XCHG BX,DX ;INTO DE MOV BX,(Offset ORDER) ;POINT TO ORDER TABLE MOV CX,13 ;ENTRY LENGTH BLDORD: MOV M,DL ;SAVE LOW ORDER ADDRESS INC BX MOV M,DH ;SAVE HIGH ORDER ADDRESS INC BX XCHG BX,DX ;TABLE ADDR TO HL ADD BX,CX ;POINT TO NEXT ENTRY XCHG BX,DX MOV BP,SP ;SAVE TBL ADDR, FETCH LOOP COUNTER XCHG BX,[BP] DEC BX ;COUNT DOWN LOOP MOV AL,BL OR AL,BH ;MORE? MOV BP,SP ;(RESTORE TBL ADDR, SAVE COUNTER) XCHG BX,[BP] JNZ BLDORD ;YES, GO DO ANOTHER ONE POP BX ;CLEAN LOOP COUNTER OFF STACK MOV BX,Word Ptr COUNT ;GET COUNT MOV Word Ptr SCOUNT,BX ;SAVE AS # TO SORT DEC BX ;ONLY 1 ENTRY? MOV AL,BL OR AL,BH JNZ L_8 JMP DONE ;YES, SO SKIP SORT L_8: ; ; THIS SORT ROUTINE IS ADAPTED FROM SOFTWARE TOOLS ; BY KERNIGAN AND PLAUGHER. ; SORT: MOV BX,Word Ptr SCOUNT ;NUMBER OF ENTRIES SORT0: OR AL,AL ;CLEAR CARRY MOV AL,BH ;GAP=GAP/2 RCR AL,1 MOV BH,AL MOV AL,BL RCR AL,1 MOV BL,AL OR AL,BH ;IS IT ZERO? JNZ L_9 JMP DONE ;THEN NONE LEFT L_9: MOV AL,BL ;MAKE GAP ODD OR AL,01 MOV BL,AL MOV Word Ptr GAP,BX INC BX ;I=GAP+1 SORT2: MOV Word Ptr I,BX XCHG BX,DX MOV BX,Word Ptr GAP MOV AL,DL ;J=I-GAP SUB AL,BL MOV BL,AL MOV AL,DH SBB AL,BH MOV BH,AL SORT3: MOV Word Ptr J,BX XCHG BX,DX MOV BX,Word Ptr GAP ;JGAP=J+GAP LAHF ADD BX,DX SAHF MOV Word Ptr JGAP,BX MOV AL,12 ;COMPARE 12 CHARS CALL COMPARE ;COMPARE (J) AND (JG) JNS SORT5 ;IF A(J)<=A(JG) MOV BX,Word Ptr J XCHG BX,DX MOV BX,Word Ptr JGAP CALL SWAP ;EXCHANGE A(J) AND A(JG) MOV BX,Word Ptr J ;J=J-GAP XCHG BX,DX MOV BX,Word Ptr GAP MOV AL,DL SUB AL,BL MOV BL,AL MOV AL,DH SBB AL,BH MOV BH,AL JS SORT5 ;IF J>0 GOTO SORT3 OR AL,BL ;CHECK FOR ZERO JZ SORT5 JMPS SORT3 SORT5: MOV BX,Word Ptr SCOUNT ;FOR LATER XCHG BX,DX MOV BX,Word Ptr I ;I=I+1 INC BX MOV AL,DL ;IF I<=N GOTO SORT2 SUB AL,BL MOV AL,DH SBB AL,BH JS L_10 JMP SORT2 L_10: MOV BX,Word Ptr GAP JMP SORT0 ; ; SORT IS ALL DONE - PRINT ENTRIES ; DONE: MOV AL,Byte Ptr FOPFLG OR AL,AL JZ L_11 JMP NOOUT ;IF FILE OUTPUT, FALL THROUGH WITH A=0 L_11: ; ; IF ALL USER OPTION ENABLED, AND WE'RE NOT ON THE FIRST PASS, THEN THE ; OUTPUT FILE IS ALREADY OPEN AND POSITIONED, SO WE CAN SKIP THE OPEN. ; MOV BX,(Offset OPNFLG) ;POINT TO OUTPUT FILE OPEN FLAG CMP AL,M ;A=0, SET Z IF OPNFLG=0 ALSO JZ L_12 JMP NOOUT ;IF OPNFLG NOT ZERO, SKIP OPEN L_12: DEC M ;ELSE, MAKE OPNFLG NOT ZERO AND OPEN ; ; FIRST PASS ON FILE APPEND - PREPARE XDIR.DIR TO RECEIVE NEW OR APPENDED ; OUTPUT. ; MOV DX,(Offset OUTFCB) ;DOES OUTPUT FILE ALREADY EXIST? MOV CL,SEARCH CALL CPM INC AL JNZ OPENIT ;IF IT DOES, THEN OPEN IT FOR PROCESSING MOV CL,MAKE ;OTHERWISE, CREATE THE OUTPUT FILE CALL CPM INC AL JZ L_13 JMP NOOUT ;CONTINUE IF OPEN SUCCESSFUL L_13: ; ; IF MAKE OR OPEN FAILS, DECLARE ERROR ; OPNERR: CALL ERXIT DB 'OPE','N' OR 80H ; WRTERR: CALL ERXIT DB 'WRIT','E' OR 80H ; ; OUTPUT FILE ALREADY EXISTS - OPEN IT AND POSITION TO THE LAST ; RECORD OF THE LAST EXTENT. ; OPENIT: MOV CL,OPEN ;OPEN 1ST EXTENT OF OUTPUT FILE CALL CPM INC AL JZ OPNERR ;BAD DEAL IF 1ST WON'T OPEN OPNMOR: MOV AL,Byte Ptr OUTFCB+15 CMP AL,128 JB LSTEXT ;IF RC<128, THIS IS LAST EXTENT MOV BX,(Offset OUTFCB)+12 INC M ;ELSE, BUMP TO NEXT EXTENT MOV CL,OPEN ;..AND TRY TO OPEN IT CALL CPM INC AL JNZ OPNMOR ;CONTINUE OPENING EXTENTS TIL NO MORE DEC M ;THEN, REOPEN PRECEDING EXTENT MOV CL,OPEN CALL CPM MOV AL,Byte Ptr OUTFCB+15 ;GET RC FOR THE LAST EXTENT ; ; AT THIS POINT, OUTFCB IS OPENED TO THE LAST EXTENT OF THE FILE, ; SO READ IN THE LAST RECORD IN THE LAST EXTENT. ; LSTEXT: OR AL,AL ;IS THIS EXTENT EMPTY? JZ NOOUT ;IF SO, THEN WE'RE STARTING A CLEAN SLATE DEC AL ;NORMALIZE RECORD COUNT MOV Byte Ptr OUTFCB+32,AL ;SET RECORD NUMBER TO READ MOV CL,READ ;..AND READ LAST RECORD OF FILE CALL CPM OR AL,AL ;WAS READ SUCCESSFUL? JZ RDOK ;IF SO, PROCEED TO SCAN FOR EOF MARK APERR: CALL ERXIT DB 'APPEN','D' OR 80H ; ; WE NOW HAVE THE LAST RECORD IN THE FILE IN OUR BUFFER. ; SCAN THE LAST RECORD FOR THE EOF MARK, INDICATING WHERE ; WE CAN START ADDING DATA. ; RDOK: MOV BX,(Offset OUTBUF) ;POINT TO START OF OUTPUT BUFFER MOV CH,128 ;GET LENGTH OF OUTPUT BUFFER SCAN: MOV AL,M CMP AL,'Z'-40H ;HAVE WE FOUND END OF FILE? JZ RESCR ;IF SO, SAVE POINTERS AND RESET CR LAHF INC BX SAHF DEC CH JNZ SCAN ;OTHERWISE, KEEP LOOKING TIL END OF BUFFER ; ; IF WE FIND AN EXPLICIT EOF MARK IN THE LAST BUFFER (OR AN IMPLIED EOF ; IF THE LAST RECORD IS FULL), MOVE THE FCB RECORD AND EXTENT POINTERS ; BACK TO CORRECT FOR THE READ OPERATION SO THAT OUR FIRST WRITE OPERATION ; WILL EFFECTIVELY REPLACE THE LAST RECORD OF THE XDIR.DIR FILE. ; RESCR: PUSH BX ;SAVE EOF BUFFER POINTER PUSH CX ;SAVE EOF BUFFER REMAINING MOV BX,(Offset OUTFCB)+32 ;GET CURRENT RECORD AGAIN DEC M ;DOCK IT JNS SAMEXT ;IF CR >=0, WE'RE STILL IN SAME EXTENT MOV BX,(Offset OUTFCB)+12 ;ELSE, MOVE TO PREVIOUS EXTENT DEC M MOV CL,OPEN ;THEN, REOPEN THE PREVIOUS EXTENT CALL CPM INC AL JZ APERR ;APPEND POSITION ERROR IF WE CAN'T REOPEN MOV AL,Byte Ptr OUTFCB+15 ;ELSE, POSITION TO LAST RECORD OF EXTENT DEC AL MOV Byte Ptr OUTFCB+32,AL SAMEXT: POP AX ;RECALL WHERE EOF IS IN BUFFER XCHG AL,AH SAHF MOV Byte Ptr BUFCNT,AL ;..AND SET BUFFER COUNTER POP BX ;RECALL NEXT BUFFER POINTER MOV Word Ptr BUFPNT,BX ;.. AND SET POINTER FOR FIRST ADDITION NOOUT: MOV BX,(Offset ORDER) ;INITIALIZE ORDER TABLE POINTER MOV Word Ptr NEXTT,BX JMP NEWLIN ;START NEW LINE AND OUTPUT THE FILES ; ; OUTPUT THE DIRECTORY FILES WE'VE MATCHED. ; ENTRY: MOV BX,Word Ptr COUNT DEC BX ;DOCK FILE COUNT MOV Word Ptr COUNT,BX MOV AL,BH ;IS THIS THE LAST FILE? OR AL,BL JZ OKPRNT ;IF COUNT=0, LAST FILE SO SKIP COMPARE ; ; COMPARE EACH ENTRY TO MAKE SURE THAT IT ISN'T PART OF A MULTIPLE ; EXTENT FILE. GO ONLY WHEN WE HAVE THE LAST EXTENT OF THE FILE. ; PUSH CX ;SAVE NPL CALL CKABRT ;CHECK FOR ABORT CODE FROM KEYBOARD MOV BX,Word Ptr NEXTT MOV AL,11 CALL COMPR ;DOES THIS ENTRY MATCH NEXT ONE? POP CX ;RECALL NPL JNZ OKPRNT ;NO, PRINT IT INC BX INC BX ;SKIP SINCE HIGHEST EXTENT COMES LAST IN LIST MOV Word Ptr NEXTT,BX JMPS ENTRY ;LOOP BACK FOR NEXT LOWEST EXTENT ; ; VALID ENTRY OBTAINED - SPIT IT OUT. ; OKPRNT: MOV BX,Word Ptr NEXTT ;GET ORDER TABLE POINTER MOV DL,M ;GET LOW ORDER ADDRESS LAHF INC BX SAHF MOV DH,M ;GET HIGH ORDER ADDRESS LAHF INC BX SAHF MOV Word Ptr NEXTT,BX ;SAVE UPDATED TABLE POINTER XCHG BX,DX ;TABLE ENTRY TO HL MOV CH,8 ;FILE NAME LENGTH CALL TYPEIT ;TYPE FILENAME MOV AL,'.' ;PERIOD AFTER FN CALL SEND_CHAR MOV CH,3 ;DISPLAY 3 CHARACTERS OF FILETYPE CALL TYPEIT ; ; COMPUTE THE SIZE OF THE FILE AND UPDATE OUR SUMMARY DATUM. ; MOV DL,M ;GET EXTENT # MOV DH,0 INC BX MOV AL,M ;GET SECTOR COUNT OF LAST EXTENT XCHG BX,DX SHL BX,1 ;# OF EXTENTS TIMES 16K SHL BX,1 SHL BX,1 SHL BX,1 XCHG BX,DX ;SAVE IN DE MOV BX,(Offset BLKMSK) ADD AL,M ;ROUND LAST EXTENT TO BLOCK SIZE ROR AL,1 ROR AL,1 ;CONVERT FROM SECTORS TO K ROR AL,1 AND AL,1FH MOV BL,AL ;ADD TO TOTAL K MOV BH,0 ADD BX,DX MOV AL,Byte Ptr BLKMSK ;GET SECTORS/BLK-1 ROR AL,1 ROR AL,1 ;CONVERT TO K/BLK ROR AL,1 AND AL,1FH NOT AL ;USE TO FINISH ROUNDING AND AL,BL MOV BL,AL XCHG BX,DX ;SAVE FILE SIZE IN DE MOV BX,Word Ptr TOTSIZ LAHF ;ADD TO TOTAL USED ADD BX,DX RCR SI,1 SAHF RCL SI,1 MOV Word Ptr TOTSIZ,BX MOV BX,Word Ptr TOTFIL ;INCREMENT FILE COUNT LAHF INC BX SAHF MOV Word Ptr TOTFIL,BX XCHG BX,DX ;GET BACK FILE SIZE ; ; IF REPORT SIZE ENABLED, OUTPUT THE SIZE OF THE INDIVIDUAL FILE. ;IF FILE SIZE REPORT WANTED ; CALL DECPRT ;..GO PRINT IT MOV AL,'k' ;..AND FOLLOW WITH K SIZE CALL SEND_CHAR ; ; ONE FILE OUTPUT - TEST TO SEE IF WE HAVE TO OUTPUT ANOTHER ONE. ; MOV BX,Word Ptr COUNT ;GET CURRENT FILE COUNTER AND TEST IT MOV AL,BH OR AL,BL JNZ L_14 JMP PRTOTL ;IF NO MORE FILES, EXIT TO SUMMARY OUTPUT L_14: ; ; AT LEAST ONE MORE FILE TO OUTPUT - CAN WE PUT IT ON THE CURRENT LINE? ; DEC CL LAHF XCHG AL,AH PUSH AX JZ L_15 CALL FENCE ;IF ROOM LEFT, OUTPUT THE FENCE CHARACTER L_15: POP AX XCHG AL,AH SAHF JZ L_16 JMP ENTRY ;.. AND GO OUTPUT ANOTHER FILE L_16: ; ; CURRENT LINE FULL, START A NEW ONE. ; NEWLIN: MOV CL,NPL ;RESET NAMES PER LINE COUNTER CALL CRLF ;SPACE DOWN TO NEXT LINE MOV AL,Byte Ptr .FCB ;.. PRECEDE NEW LINE WITH DRIVE NAME ADD AL,'A'-1 CALL SEND_CHAR MOV AL,':' ;TAG DRIVE LETTER WITH A COLON AND SPACE CALL SEND_CHAR JMP ENTRY ;GO BACK AND OUTPUT ANOTHER FILE ; ;IF REPORTING USER NUMBERS ; ; PRINT HL IN DECIMAL WITH LEADING ZERO SUPPRESSION ; DECPRT: SUB AL,AL ;CLEAR LEADING ZERO FLAG MOV Byte Ptr LZFLG,AL MOV DX,-1000 ;PRINT 1000'S DIGIT CALL DIGIT MOV DX,-100 ;ETC. CALL DIGIT MOV DX,-10 CALL DIGIT MOV AL,'0' ;GET 1'S DIGIT ADD AL,BL JMP SEND_CHAR ; DIGIT: MOV CH,'0' ;START OFF WITH ASCII 0 DIGLP: PUSH BX ;SAVE CURRENT REMAINDER ADD BX,DX ;SUBTRACT JNB DIGEX ;QUIT ON OVERFLOW POP AX ;THROW AWAY REMAINDER INC CH ;BUMP DIGIT JMPS DIGLP ;LOOP BACK DIGEX: POP BX ;RESTORE POINTER MOV AL,CH CMP AL,'0' ;ZERO DIGIT? JNZ DIGNZ ;NO, TYPE IT MOV AL,Byte Ptr LZFLG ;LEADING ZERO? OR AL,AL MOV AL,'0' JZ L_17 JMP SEND_CHAR ;PRINT DIGIT L_17: MOV AL,Byte Ptr SUPSPC ;GET SPACE SUPPRESSION FLAG OR AL,AL ;SEE IF PRINTING FILE TOTALS JNZ L_18 RET ;YES, DON'T GIVE LEADING SPACES L_18: JMP SPACE ;LEADING ZERO...PRINT SPACE DIGNZ: MOV Byte Ptr LZFLG,AL ;SET LEADING ZERO FLAG SO NEXT ZERO PRINTS JMP SEND_CHAR ;AND PRINT DIGIT ; ; SHOW TOTAL SPACE AND FILES USED ; PRTOTL: XOR AL,AL ;GET A ZERO TO... MOV Byte Ptr SUPSPC,AL ;SUPPRESS LEADING SPACES IN TOTALS MOV BX,Word Ptr TOTFIL ;HOW MANY FILES DID WE MATCH? MOV AL,BH OR AL,BL JZ NXTUSR ;SKIP THE SUMMARY IF WE DIDN'T FIND ANY PUSH BX ;SAVE TOTFIL MOV Byte Ptr FNDFLG,AL ;SET FILE FOUND FLAG MOV DX,(Offset TOTMS1) ;PRINT [CR,LF,LF]"DRIVE " CALL PRINT MOV AL,Byte Ptr .FCB ADD AL,'A'-1 CALL SEND_CHAR ;OUTPUT THE DRIVE CODE MOV DX,(Offset TOTMS2) ;PRINT ", USER " CALL PRINT CALL TYPUSR ;OUTPUT THE USER NUMBER MOV DX,(Offset TOTMS3) ;PRINT " CONTAINS " CALL PRINT MOV BX,Word Ptr TOTSIZ ;PRINT TOTAL K USED BY FILES MATCHED CALL DECPRT MOV DX,(Offset TOTMS4) ;PRINT "K IN " CALL PRINT POP BX ;RECALL TOTFIL CALL DECPRT ;PRINT NUMBER OF FILES MATCHED MOV DX,(Offset TOTMS5) ;PRINT " FILES WITH " CALL PRINT CALL PRTFRE ;OUTPUT FREE SPACE REMAINING & " FREE." ; ; DIRECTORY FOR ONE USER AREA COMPLETED. IF ALL USERS OPTION IS ; SELECTED, THEN GO DO ANOTHER DIRECTORY ON THE NEXT USER NUMBER ; UNTIL WE EXCEED THE MAXIMUM USER # FOR THE SELECTED DRIVE. ; IF AOPT NXTUSR: ;IF ALL USERS OPTION ENABLED MOV AL,Byte Ptr AOPFLG ;IF NOT ALL USERS MODE - SKIP NEXT OR AL,AL JNZ GOCLZ CALL CKABRT ;CHECK FOR USER ABORT FIRST MOV AL,Byte Ptr MAXUSR ;NO ABORT - GET MAXIMUM USER NUMBER MOV BX,(Offset NEWUSR) ;BUMP DIRECTORY USER NUMBER INC M CMP AL,M ;DOES NEXT USER # EXCEED MAXIMUM? JNAE L_19 JMP SETTBL ;CONTINUE IF MORE USER AREAS TO GO L_19: ENDIF ; ;IF MULTI-DISK OPTION ENABLED IF AOPT AND DOPT MOV AL,Byte Ptr BASUSR ;RESET BASE USER NUMBER FOR THE MOV M,AL ;..NEXT DIRECTORY SEARCH ENDIF ; ; WE'VE FINISHED ALL OF OUR OUTPUTTING. FLUSH THE REMAINDER OF THE ; OUTPUT BUFFER AND CLOSE THE FILE BEFORE GOING TO EXIT ROUTINE. ; IF FOPT GOCLZ: MOV BX,(Offset OPNFLG) ;GET FILE OPEN STATUS THEN RESET FLAG TO MOV AL,M ;..FORCE REOPEN ON NEXT PASS MOV M,0 OR AL,AL JZ NXTDSK ;SKIP CLOSING XDIR.DIR IF IT WASN'T OPENED MOV BX,(Offset BUFCNT) MOV AL,M ;RETRIEVE # OF UNFLUSHED CHARACTERS IN BUFFER MOV M,128 ;FORCE BUFCNT TO EMPTY STATUS FOR NEXT DRIVE OR AL,AL ;IF BUFCNT=128, BUFFER EMPTY SO SET SIGN BIT JS CLOZE ;CLOSE XDIR.DIR IF BUFFER IS EMPTY JZ FLUSH ;WRITE LAST RECORD TO XDIR.DIR IF BUFFER FULL MOV BX,Word Ptr BUFPNT ;OTHERWISE, PAD UNUSED BUFFER WITH CTRL-ZS PUTAGN: MOV M,'Z'-40H LAHF INC BX SAHF DEC AL JNZ PUTAGN ;CONTINUE PADDING TIL BUFFER FILLED OUT FLUSH: MOV DX,(Offset OUTFCB) ;FLUSH THE LAST OUTPUT BUFFER MOV CL,WRITE CALL CPM OR AL,AL JZ L_20 JMP WRTERR L_20: CLOZE: MOV DX,(Offset OUTFCB) ;CLOSE THE OUTPUT FILE MOV CL,CLOSE CALL CPM ENDIF ; ; DIRECTORY FOR ALL USER AREAS COMPLETED. IF THE MULTI-DISK OPTION ; IS ENABLED AND SELECTED, RESET TO THE BASE USER AREA AND REPEAT ; THE DIRECTORY FOR NEXT DRIVE ON-LINE UNTIL WE EITHER EXCEED THE ; DRIVES IN OUR LODRV-HIDRV TABLE, OR THE BDOS SHUTS US DOWN WITH ; A SELECT OR BAD SECTOR ERROR, WHICH WILL BE INTERCEPTED BACK TO ; THE EXIT MODULE. ; NXTDSK: MOV BX,(Offset FNDFLG) ;GET FILE FOUND FLAG MOV AL,M MOV M,0 ;CLEAR FILE FOUND FLAG FOR NEXT DRIVE OR AL,AL JNZ NDSK ;CONTINUE IF AT LEAST 1 FILE FOUND ; ;IF FILE OUTPUT ENABLED, DISABLE TEMPORARILY ; IF FOPT MOV BX,(Offset FOPFLG) DEC M PUSH BX ENDIF MOV AL,Byte Ptr .FCB ;STASH ASCII DIRECTORY DRIVE IN NO FILE MSG ADD AL,'A'-1 MOV Byte Ptr NOFMS2,AL MOV DX,(Offset NOFMS1) ;PRINT "NO FILE ON ? - " CALL PRINT CALL PRTFRE ;TAG WITH FREE MESSAGE ; ;RESTORE ORIGINAL FILE OUTPUT MODE ; IF FOPT POP BX INC M ENDIF IF DOPT NDSK: ;IF MULTI-DISK OPTION ENABLED MOV AL,Byte Ptr DOPFLG ;IF MULTI-DISK NOT SELECTED - SKIP NEXT OR AL,AL JZ L_21 JMP EXIT L_21: CALL CKABRT ;CHECK FOR USER ABORT FIRST MOV AL,(Offset HIDRV)-(Offset LODRV) ;GET MAXIMUM DRIVE CODE TO SEARCH MOV BX,FCB ;BUMP DIRECTORY FCB DRIVE CODE INC M CMP AL,M ;DOES NEXT DISK EXCEED MAXIMUM? JNAE L_22 JMP NOOPT ;SEARCH NEXT DISK IF NOT L_22: ENDIF JMP EXIT ;ALL DONE - EXIT TO CCP ; ; PRINT THE USER NUMBER OF THE DIRECTORY IN DECIMAL ; TYPUSR: MOV AL,Byte Ptr NEWUSR CMP AL,10 ;IF USER NO. > 9 PRINT LEADING 1 JB DUX MOV AL,'1' CALL SEND_CHAR MOV AL,Byte Ptr NEWUSR ;PRINT LOW DIGIT OF USER NO. SUB AL,10 DUX: ADD AL,'0' JMPS SEND_CHAR ; ; FORCE NEW LINE ON VIDEO AND CHECK FOR PAGE PAUSE ; CRLF: MOV AL,0DH ;SEND CR CALL SEND_CHAR MOV AL,0AH ;SEND LF JMPS SEND_CHAR ;EXIT TO CALLER FROM TYPE ; ; SEPARATE THE DIRECTORY OUTPUT ON A LINE WITH A SPACE, THE DELIMITER, ; FOLLOWED BY ANOTHER SPACE. ; FENCE: CALL SPACE MOV AL,DELIM ;FENCE CHARACTER FPAD: CALL SEND_CHAR ;PRINT IT, FALL INTO SPACE SPACE: MOV AL,' ' ;FALL THROUGH TO TYPE ; ; OUTPUT CHARACTER IN A TO CONSOLE, AND OPTIONALLY TO PRINTER AND/OR ; THE OUTPUT FILE. ; SEND_CHAR: PUSH CX PUSH DX PUSH BX IF FOPT OR POPT OR PGPAWZ LAHF ;SAVE THE CHARACTER TO OUTPUT XCHG AL,AH PUSH AX XCHG AL,AH ENDIF CALL TYPE1 ;SEND IT TO CONSOLE IF FOPT OR POPT OR PGPAWZ POP AX ;RESTORE THE OUTPUT CHARACTER XCHG AL,AH AND AL,7FH ;STRIP PARITY BIT ON CHARACTER ENDIF ; ; TEST FILE OUTPUT MODE AND SKIP TO PAGE PAUSE TEST IF NOT ACTIVE. ; IF FOPT MOV CH,AL ;SAVE STRIPPED CHARACTER TO B MOV AL,Byte Ptr FOPFLG ;IS FILE OUTPUT ACTIVE? OR AL,AL JNZ NOWRIT ;GO CHECK FOR PAGE PAUSE IF NOT ; ; FILE OUTPUT MODE ACTIVE - MAKE SURE WE HAVE ROOM IN BUFFER TO ADD ; NEXT CHARACTER. IF BUFFER FULL, WRITE OUT CURRENT RECORD FIRST ; AND THEN START A NEW RECORD WITH CURRENT CHARACTER. ; MOV BX,Word Ptr BUFPNT ;GET CURRENT BUFFER POINTER MOV AL,Byte Ptr BUFCNT ;GET BUFFER CAPACITY REMAINING OR AL,AL JNZ PUTBUF ;CONTINUE IF BUFFER NOT FULL MOV DX,(Offset OUTFCB) ;OTHERWISE, WRITE THE CURRENT BUFFER OUT MOV CL,WRITE CALL CPM ;(NOTE CALL MUST SAVE CHARACTER IN B) OR AL,AL JZ L_23 JMP WRTERR ;TAKE WRITE ERROR EXIT IF DISK FULL OR R/O L_23: MOV BX,(Offset OUTBUF) ;RESET BUFFER POINTER MOV AL,128 ;RESET BUFFER CAPACITY PUTBUF: MOV M,CH ;SHOVE CHARACTER TO NEXT BUFFER POSITION INC BX ;BUMP BUFFER POINTER MOV Word Ptr BUFPNT,BX ;.. AND SAVE IT DEC AL ;DOCK COUNT OF CHARACTERS LEFT IN BUFFER MOV Byte Ptr BUFCNT,AL ;..AND SAVE IT NOWRIT: MOV AL,CH ;RECALL STRIPPED CHARACTER ENDIF ; ;IF PRINTER OPTION ; IF POPT MOV DL,AL ;SETUP LIST OUTPUT CALL MOV CL,5 MOV AL,Byte Ptr POPFLG ;TEST PRINTER FLAG OR AL,AL JNZ L_24 CALL CPM ;PRINT CHARACTER IF FLAG TRUE L_24: MOV AL,DL ;RECALL CHARACTER ENDIF IF PGPAWZ CMP AL,0AH ;DO WE HAVE A LF? JNZ TYPRET ;EXIT IF NOT ENDIF IF NOPT AND PGPAWZ MOV AL,Byte Ptr NOPFLG ;IS THE PAGE PAUSE FUNCTION DISABLED? OR AL,AL JZ TYPRET ;EXIT IF SO ENDIF IF PGPAWZ MOV AL,Byte Ptr LINCNT ;GET LINE COUNT INC AL ;BUMP IT CMP AL,LPS ;ARE WE AT THE END OF THE SCREEN? JB NOTEOS ;SKIP IF NOT MOV DX,(Offset EOSMSG) ;ELSE, DISPLAY PAUSE MESSAGE MOV CL,9 ;..WITHOUT CHECKING FOR LFS INT 224 CALL CINPUT ;WAIT FOR CHARACTER CMP AL,'C'-40H JNZ L_25 JMP EXIT ;ABORT ON CTRL-C L_25: XOR AL,AL ;RESET LINE COUNT NOTEOS: MOV Byte Ptr LINCNT,AL ;SAVE NEW LINE COUNT ENDIF TYPRET: POP BX ;EXIT FROM TYPE POP DX POP CX RET ; ; OUTPUT CHARACTER ; TYPE1: MOV DL,AL ;GET CHARACTER INTO BDOS ENTRY REGISTER MOV CL,WRCHR INT 224 ;CALL CONOUT VIA THE BDOS RET ; ; PRINT A STRING AT HL OF LENGTH B ; TYPEIT: MOV AL,M CALL SEND_CHAR LAHF INC BX SAHF DEC CH JNZ TYPEIT RET ; ; PRINT STRING TERMINATED WITH LAST BYTE HIGH ON CONSOLE. ; PRINT: MOV SI,DX MOV AL,[SI] LAHF XCHG AL,AH PUSH AX XCHG AL,AH AND AL,7FH CALL SEND_CHAR POP AX XCHG AL,AH OR AL,AL JNS L_26 RET L_26: LAHF INC DX SAHF JMPS PRINT ; ; FETCH CHARACTER FROM CONSOLE (WITHOUT ECHO) ; CINPUT: MOV Byte Ptr FUNC,3 ;bios console input function MOV Word Ptr BIOS_DESC,CX ;pass disk number to bios descriptor MOV Word Ptr BIOS_DESC+2,0 ;fill remaining descriptor (DX) zero MOV DX,(Offset FUNC) ;point to function parameter block MOV CL,50 ;direct bios call INT 224 ;do it, to it... AND AL,7FH RET ; ; CHECK FOR A CTRL-C OR CTRL-S ENTERED FROM THE KEYBOARD. JUMP TO ; EXIT IF CTRL-C, PAUSE ON CTRL-S. ; CKABRT: MOV Byte Ptr FUNC,2 ;bios console status function MOV Word Ptr BIOS_DESC,CX ;pass disk number to bios descriptor MOV Word Ptr BIOS_DESC+2,0 ;fill remaining descriptor (DX) zero MOV DX,(Offset FUNC) ;point to function parameter block MOV CL,50 ;direct bios call INT 224 ;do it, to it... OR AL,AL JNZ L_27 RET ;NO, RETURN TO CALLER L_27: CALL CINPUT ;GET CHARACTER CMP AL,'C'-40H ;CTRL-C? JNZ L_28 JMP EXIT ;IF CTRL-C THEN QUIT L_28: CMP AL,'S'-40H ;CTRL-S? JZ L_29 RET ;NO, RETURN TO CALLER L_29: CALL CINPUT ;YES, WAIT FOR ANOTHER CHAR. CMP AL,'C'-40H ;MIGHT BE CTRL-C JNZ L_30 JMP EXIT ;EXIT IF CTRL-C, ELSE FALL THRU AND CONTINUE L_30: RET ; ; ENTRY TO BDOS SAVING ALL EXTENDED REGISTERS ; CPM: PUSH CX PUSH DX PUSH BX INT 224 POP BX POP DX POP CX RET ; ; FOR FILE OUTPUT MODE, RETURN TO OLD USER AREA AND SET DMA FOR ; THE FILE OUTPUT BUFFER. ; IF UOPT OR AOPT SETFOP: MOV AL,Byte Ptr OLDUSR ;GET USER NUMBER AT STARTUP MOV DL,AL MOV CL,CURUSR CALL CPM ;RESET THE OLD USER NUMBER IF CP/M 2 ENDIF IF FOPT MOV DX,(Offset OUTBUF) ;MOVE DMA FROM SEARCH BUFFER INTO THE JMPS SET2 ;..OUTPUT BUFFER ENDIF RET ; ; MOVE DISK BUFFER DMA TO DEFAULT BUFFER FOR DIRECTORY SEARCH OPERATIONS ; AND BDOS MEDIA CHANGE ROUTINES. ; SETSRC: MOV DX,BASE+80H SET2: MOV CL,SETDMA JMPS CPM ; ; PRINT THE AMOUNT OF FREE SPACE REMAINING ON THE SELECTED DRIVE ; PRTFRE: MOV BX,Word Ptr FREEBY ;GET SPACE LEFT BEFORE ADDING TO MASTER.DIR CALL DECPRT ;PRINT K FREE MOV DX,(Offset TOTMS6) ;PRINT " FREE." JMP PRINT ; ; COMPARE ROUTINE FOR SORT ; COMPR: PUSH BX ;SAVE TABLE ADDR MOV DL,M ;LOAD LOW ORDER INC BX MOV DH,M ;LOAD HIGH ORDER INC BX MOV CL,M INC BX MOV CH,M ; ; BC, DE NOW POINT TO ENTRIES TO BE COMPARED ; XCHG BX,DX MOV DL,AL ;GET COUNT CMPLP: MOV SI,CX MOV AL,[SI] CMP AL,M LAHF INC BX SAHF LAHF INC CX SAHF JNZ NOTEQL ;QUIT ON MISMATCH DEC DL ;OR END OF COUNT JNZ CMPLP NOTEQL: POP BX RET ;COND CODE TELLS ALL ; ; SWAP ENTRIES IN THE ORDER TABLE ; SWAP: MOV CX,(Offset ORDER)-2 ;TABLE BASE SHL BX,1 ;*2 LAHF ;+ BASE ADD BX,CX SAHF XCHG BX,DX SHL BX,1 ;*2 LAHF ;+ BASE ADD BX,CX RCR SI,1 SAHF RCL SI,1 MOV CL,M MOV SI,DX MOV AL,[SI] XCHG BX,DX MOV M,CL MOV SI,DX MOV [SI],AL LAHF INC BX SAHF LAHF INC DX SAHF MOV CL,M MOV SI,DX MOV AL,[SI] XCHG BX,DX MOV M,CL MOV SI,DX MOV [SI],AL RET ; ; NEW COMPARE ROUTINE ; COMPARE:MOV CX,(Offset ORDER)-2 SHL BX,1 ADD BX,CX XCHG BX,DX SHL BX,1 ADD BX,CX XCHG BX,DX MOV CL,M INC BX MOV CH,M XCHG BX,DX MOV DL,M INC BX MOV DH,M XCHG BX,DX MOV DL,AL ;COUNT CMPLPE: MOV SI,CX MOV AL,[SI] CMP AL,M LAHF INC CX SAHF LAHF INC BX SAHF JZ L_31 RET L_31: DEC DL JNZ CMPLPE RET ; ; ERROR EXIT ; IF FOPT ERXIT: MOV AL,0FFH MOV Byte Ptr FOPFLG,AL ;DISABLE FILE OUTPUT MODE ON ERROR ENDIF CALL CRLF ;SPACE DOWN POP DX ;GET POINTER TO MESSAGE STRING CALL PRINT ;PRINT IT MOV DX,(Offset ERRMS1) ;PRINT " ERROR" CALL PRINT CALL CRLF ;SPACE DOWN ; ; EXIT - ALL DONE, RESTORE STACK ; EXIT: MOV CL,CONST ;CHECK CONSOLE STATUS CALL CPM OR AL,AL ;CHAR WAITING? MOV CL,RDCHR JZ L_32 CALL CPM ;GOBBLE UP CHAR L_32: MOV AL,Byte Ptr ROPFLG ;IF DISK SYSTEM WAS RESET OR AL,AL MOV AL,Byte Ptr OLDDSK ;GET DEFAULT DISK AT STARTUP MOV DL,AL MOV CL,SELDSK ;RESELECT IT JNZ L_33 CALL CPM L_33: ; MOV CL,0 ;BDOS FUNCTION, WARM BOOT MOV DL,0 INT 224 ;RETURN TO CP/M-86 ; ; END OF PROGRAM CODE ; ; INITIALIZED DATA AREA ; DREMSG DB 'Driv','e' OR 80H ; IF PGPAWZ EOSMSG DB 0AH,'Press any key to continue, or CTRL-C to exit',0DH,0AH,0AH,'$' ENDIF ; ERRMS1 DB ' ' ; ERRMS2 DB 'Erro','r' OR 80H ; IF REPERR ERRTAG DB ' -','>' OR 80H ENDIF ; NOFMS1 DB 0DH,0AH,'No such file on Drive ' ; NOFMS2 DB ' -',' ' OR 80H ; TOTMS1 DB 0DH,0AH,0DH,0AH,'Drive',' ' OR 80H ; TOTMS2 DB ', User',' ' OR 80H ; TOTMS3 DB ' contains',' ' OR 80H ; TOTMS4 DB 'K in',' ' OR 80H ; TOTMS5 DB ' files with',' ' OR 80H ; TOTMS6 DB 'K free',0DH,0AH OR 80H ; USRMSG DB 'User ','#' OR 80H ; FNDFLG DB 0 ;FLAG WHETHER ANY FILES MATCHED ; IF PGPAWZ LINCNT DB 0 ;COUNT OF LINES PRINTED ON SCREEN ENDIF ; ; DRIVE CODE/USER AREA LOOKUP TABLE ; NOTE THAT THE LODRV-HIDRV TABLE IS INCLUDED HERE FULLY CONFIGURED. ; FOR YOUR OWN USE, YOU SHOULD CHANGE THE MAXIMUM USER AREAS AS ; APPROPRIATE FOR EACH DRIVE ON YOUR SYSTEM, AND THEN DELETE ANY ; DBS REFERENCING DRIVES THAT DON'T EXIST. ; LODRV EQU (Offset $) ;MARK BEGINNING OF DRIVE/USER TABLE DB 15 ;MAXIMUM USER AREA FOR DRIVE A DB 15 ; " " " " " B DB 15 ; " " " " " C DB 15 ; " " " " " D DB 15 ; " " " " " E DB 15 ; " " " " " F DB 15 ; " " " " " G DB 15 ; " " " " " H ; DB 15 ; " " " " " I ; DB 15 ; " " " " " J ; DB 15 ; " " " " " K ; DB 15 ; " " " " " L ; DB 15 ; " " " " " M ; DB 15 ; " " " " " N ; DB 15 ; " " " " " O ; DB 15 ; " " " " " P ; HIDRV EQU (Offset $) ;MARK END OF DRIVE/USER TABLE ; ; OPTION FIELD LOOKUP TABLE. ; NOTE THAT YOU CAN FORCE ANY OF THESE OPTIONS AS A DEFAULT BY ; CHANGING THE LETTER FOR THE OPTION INTO A ZERO (ASSUMING THAT ; ITS ENABLING EQUATE IS TRUE). EACH OPTION THAT YOU HARD-WIRE IN ; THIS MANNER WILL NO LONGER BE RECOGNIZED AS A COMMAND LINE OPTION, ; AND IF YOU REDUNDANTLY KEY IT IN, XDIR WILL FLAG IT AS UNRECOGNIZED. ; OTBL EQU (Offset $) ;MARK START OF OPTION TABLE ; ;ALL USERS-OPTION FLAG ; IF AOPT AOPFLG DB 'A' ENDIF ; ;MULTI-DISK-OPTION FLAG ; IF DOPT DOPFLG DB 'D' ENDIF ; ;FILE-OUTPUT-OPTION FLAG ; IF FOPT FOPFLG DB 'F' ENDIF ; ;NO PAGE-PAUSE OPTION FLAG ; IF NOPT AND PGPAWZ NOPFLG DB 'N' ENDIF ; ;PRINTER OPTION FLAG ; IF POPT POPFLG DB 'P' ENDIF ; ;RESET OPTION FLAG ; ROPFLG DB 'R' ; ;SYSTEM FILE OPTION FLAG ; IF SOPT SOPFLG DB 'S' ; ENDIF OEND EQU (Offset $) ;MARK END OF OPTION TABLE ; ; END OF OPTION LOOKUP TABLE ; IF FOPT BUFPNT DW (Offset OUTBUF) ;POINTER TO NEXT LOCATION IN OUTPUT BUFFER BUFCNT DB 128 ;NUMBER OF BYTES LEFT IN OUTPUT BUFFER OPNFLG DB 0 ;FILE OPEN FLAG FOR ALL USER FILE OUTPUT ; OUTFCB DB 0,'XDIR DIR' DB 0 ;REST OF XDIR.DIR FILE CONTROL BLOCK DB 0 DB 0 DB 0 DB 0 DB 0 DB 0 DB 0 DB 0 DB 0 DB 0 DB 0 DB 0 DB 0 DB 0 DB 0 DB 0 DB 0 DB 0 DB 0 DB 0 ; OUTBUF RS 128 ;OUTPUT FILE BUFFER ENDIF ; ; UNINITIALIZED DATA AREA ; ;storage for 5 byte BIOS function descriptor ; FUNC RS 1 ;bios function code goes here BIOS_DESC RS 2 ;[CX] data goes here RS 2 ;[DX] data goes here ; BASUSR RS 1 ;DUPE OF ORIGINAL DIRECTORY USER # TO SEARCH BLKMAX RS 2 ;HIGHEST BLOCK # ON DRIVE BLKMSK RS 1 ;SEC/BLK - 1 BLKSHF RS 1 ;# SHIFTS TO MULT BY SEC/BLK COUNT RS 2 ;ENTRY COUNT dsksiz RS 2 ;HIGHEST FILE # IN DIRECTORY FREEBY RS 2 ;CONTAINS NUMBER OF K LEFT ON DIRECTORY DRIVE GAP RS 2 ;SORT ROUTINE STORAGE I RS 2 ;SORT ROUTINE STORAGE J RS 2 ;SORT ROUTINE STORAGE JGAP RS 2 ;SORT ROUTINE STORAGE LZFLG RS 1 ;0 WHEN PRINTING LEADING ZEROS MAXUSR RS 1 ;MAXIMUM USER # FOR DRIVE FROM LOOKUP TABLE NEWUSR RS 1 ;CONTAINS USER NUMBER SELECTED BY "$U" OPTION NEXTT RS 2 ;NEXT TABLE ENTRY OLDDSK RS 1 ;HOLDER FOR CURRENTLY LOGGED-IN DRIVE OLDUSR RS 1 ;CONTAINS USER NUMBER UPON INVOCATION SCOUNT RS 2 ;# TO SORT SUPSPC RS 1 ;LEADING SPACE FLAG FOR DECIMAL ROUTINE TBLOC RS 2 ;POINTER TO START OF NAME TABLE TEMP RS 2 ;SAVE DIR ENTRY TOTFIL RS 2 ;TOTAL NUMBER OF FILES TOTSIZ RS 2 ;TOTAL SIZE OF ALL FILES ORDER EQU (Offset $) ;ORDER TABLE STARTS HERE ; RS 4000H DB 0 ; END