TITLE 'XDIR - CP/M DIRECTORY DISPLAY PROGRAM' ; - XDIR - ; BY S J SINGER ;MODIFICATIONS ; MAY 15, 1980 - ADDED SUPPORT FOR DRIVES A-D ; MAY 15, 1980 - CHANGED THE DISPLAY TO 18 LINES ; MAY 15, 1980 - FIXED BUG WITH DIR EXTENTS NOT BEING IN ; ORDER. IF A LARGER EXTENT CAME BEFORE ; EXTENT ZERO, THE PROGRAM WOULD LOOP INFINITELY. ; MAY 24, 1980 - CHANGED SO THAT DIR ENTRIES ARE READ VIA ; BDOS CALLS (011H AND 012H). ; MAY 24, 1980 - CHANGED SO THAT SPACE INFO IS GOTTEN IN ; ACCORDANCE WITH CP/M 2.0 AND ABOVE. ; ; XDIR IS A CPM UTILITY THAT DISPLAYS A DISK DIRECTORY IN A MORE ;READABLE FORM. THE DIRECTORY IS READ FROM THE SPECIFIED DISK, SORTED ;THEN DISPLAYED IN A 3 COLUMN FORMAT. BOTH THE FILE NAMES AND FILE SIZES ;IN 1 K GROUPS ARE DISPLAYED. ; THE PRESENT VERSION OF XDIR HAS CERTAIN MINOR LIMITATIONS WHICH ;MAY BE EASILY REMOVED IF THEY CAUSE PROBLEMS. ; ; 1. ONLY 54 FILES ARE DISPLAYED. THIS LIMIT IS SUFFICIENT FOR ; ALMOST ALL DISKS AND LEAVES ROOM ON THE SCREEN FOR EDITING. ; TO CHANGE THE NO OF LINES DISPLAYED, CHANGE THE LINES VARIABLE ; IN THE DATA ALLOCATION SECTION OF THE PROGRAM. FOR EXAMPLE ; TO DISPLAY A MAXIMUM OF 54 FILES CHANGE LINES TO 18. ; ; OCCASIONALLY THE FILE NAMES WILL BE DISPLAYED WITH THE FORMAT ;SCRAMBLED OR THE SPACE REMAINING ON THE DISK WILL NOT AGREE WITH THAT REPORTED ;BY THE STAT UTILITY. THIS ALMOST ALWAYS MEANS THE DISK DIRECTORY HAS BEEN ;MESSED UP SOMEHOW. USUALLY THE PROBLEM CAN BE CORRECTED BY COPYING ALL THE ;FILES TO ANOTHER DISK USING PIP. ; XDIR WAS ASSEMBLED USING THE NEW CP/M MACRO ASSEMBLER AND USES A LARGE ;NUMBER OF MACROS. THESE ARE CONTAINED IN A LIBRARY CALLED MACRO.LIB WHICH ;IS REQUIRED IF THE PROGRAM IS TO BE REASSEMBLED. ; ; ; COMMAND FORMAT ; ; XDIR DISPLAY DIRECTORY OF LOGGED DISK ; XDIR A DISPLAY DIRECTORY OF DISK A ; XDIR B DISPLAY DIRECTORY OF DISK B ; XDIR C DISPLAY DIRECTORY OF DISK C ; XDIR D DISPLAY DIRECTORY OF DISK D ; ; ; THE PROGRAM STARTS READING THE CP/M DIRECTORY UNTIL AN EOF OCCURS. ;AS IT READS EACH ENTRY, IT MAINTAINS A TABLE OF ALL FILES. THE TABLE CON- ;TAINS ONE ENTRY FOR EACH FILE FROM ITS LARGEST EXTENT. WHEN ALL FILES ARE ;IN THE TABLE, IT SORTS THEM USING QUICKSORT AND AN AUXILIARY ADDRESS TABLE. ;WHEN THE SORT COMPLETES, THE FILE TABLE IS PRINTED IN THE ORDER OF THE AUX ;ADDRESS TABLE AND CONTROL IS GIVEN BACK TO CP/M. ; * * * COMMON EQUATES * * * LINES EQU 18 ;LINES PER PAGE OF DISPLAY MAXDIR EQU 128 ;MAXIMUM NUMBER OF DIRECTORY ENTRIES MACLIB MACRO ;INCLUDE MACRO LIBRARY PAGE ;************************************************ ;* PROGRAM INITIALIZATION * ;************************************************ ORG 100H ;SET PROG START LXI H,0 DAD SP ;GET OLD STACK POINTER SHLD OLDSTK ;SAVE IT LXI SP,NEWSTK ;LOAD NEW STACK POINTER ; ; START OF DIRECTORY ROUTINE READ IN GROUPS 0 AND 1 AND STORE ; DIRECTORY FILE NAMES NOT FLAGGED E5. IF EXTENT NOT ZERO STORE ; OVER PREVIOUS FILE NAME. TABLE OF POINTERS WILL BE BUILT IN PDIR ; DIR: DISKIO ?DRIVE STA NEWDRV LDA 81H ORA A ;CHECK IF INPUT BUFFER EMPTY JZ DDD LDA 82H ;CHECK NEW DRIVE (A-D) CPI 'A' JC DSKERR CPI 'D'+1 JNC DSKERR SUI 'A' ;MAKE IT RELATIVE TO ZERO. STA NEWDRV ;SAVE IT. DDD: PRINT LDA NEWDRV ;LOGGED DISK ADI 'A' CHAROUT PRINT CALL FIXB ;RESTORE DRIVE. ; GET THE CURRENT DISK MAX VALUE. MVI C,01FH ;GET DPB ADR FROM CP/M. CALL 5 SHLD DPBADR ;SAVE IT. LXI D,2 ;GET THE BSH. DAD D MOV A,M ;SAVE IT. STA DSKBSH MOV C,A ; AGAIN. INX H ;GET THE BLM FOR LATER. MOV A,M STA DSKBLM ;SAVE IT. MOV A,C ;CONVERT BSH TO REL SHIFT. SUI 3 MOV C,A ;SAVE IT. INX H ;GET THE DSM. INX H MOV E,M INX H MOV D,M INX H XCHG INX H ;MAKE IT RELATIVE TO ONE. DCR C ;SHIFT MAX BY (C) TIMES. INR C JZ $+8 DAD H DCR C JMP $-7 SHLD MAXSTR ;SAVE VALUE. ; GET DIRECTORY SIZE. XCHG ;HL = DPBDRM MOV E,M INX H MOV D,M XCHG INX H ;MAKE IT RELATIVE TO ONE. LXI D,32-1 ;ROUND IT TO EVEN # OF K. DAD D HALF ;DIVIDE IT BY 32 (# IN 1K). HALF ;/4 HALF ;/8 HALF ;/16 HALF ;/32 SHLD DIRSIZ ;SAVE IT. ; ADJUST MAX SIZE BY DIRECTORY SIZE. LHLD MAXSTR DSUB DIRSIZ SHLD MAXSTR DIR2: XRA A STA COUNT ;COUNT OF DIRECTORY ENTRIES ; FILL PDIR,PDIR+(2*(MAXDIR+1)) ;ZERO DIR PTR TABLE LXI H,PDIR LXI D,2*(MAXDIR+1) MVI M,0 INX H DCX D MOV A,E ORA D JNZ $-6 LXI H,DIRBUF ;POINTS TO DIRECTORY BUFFER SHLD OUTB LXI H,PDIR ;POINTER TABLE SHLD IPOINT LDA NEWDRV MOV E,A DISKIO LOGIN ;LOG IN NEW DRIVE NO DIR4: LXI H,80H ;POINTS TO INPUT BUFFER SHLD INB MVI C,01AH ;ISSUE SET DMA. XCHG CALL 5 MVI C,011H ;ISSUE DIR FIRST GET. LXI D,DIRFCB CALL 5 JMP DIR6 ; GET NEXT DIR BUFFER. DIR5: MVI C,012H CALL 5 CPI 255 ;END OF DIR? JZ SORT ;...YES, GO SORT TABLE. ADD A ;CONVERT # TO DISP. ADD A ADD A ADD A ADD A ADI 80H ;ADD IN BUFFER ADDRESS. MOV L,A MVI H,0 SHLD INB ;SAVE PTR. ; SEE IF EOF. DIR6: LHLD OUTB ;LOAD DESTINATION POINTER XCHG ;PUT IT IN DE LHLD INB ;LOAD SOURCE POINTER MVI A,0E5H ;FLAG BYTE CMP M ;TEST FIRST BYTE JNZ DIR8 INX H CMP M ;TEST SECOND BYTE JZ SORT ;SORT DIRECTORY JMP DIR12A DIR8: ; IF ITS THE ZERO ENTRY, ADD IT TO TABLE. INX H SAVE H,D ; SCAN TABLE TO SEE IF AN EXTENT EXISTS FOR THE FILE. LXI H,0 ;SEARCH FOR SAME NAME AND SWITCH SHLD J ;INITIALIZE INDEX DIR9: DLOAD PDIR,J MOV A,H ORA L JZ DIR10 ;ERROR TABLE EMPTY XCHG LHLD INB ;POINTER TO NEW DIR ENTRY SAVE D,H INX H MATCH ,,11 ;COMPARE 11 CHARAACTERS RESTORE H,D JZ SWITCH ;STORE NEW ENTRY OVER OLD INDEX J,2 ;INCR INDEX BY 2 JMP DIR9 ; REPLACE EXTENT IN TABLE IF ITS GREATER. SWITCH: CALL CLCSPC ;GET NUMBER OF GROUPS. XCHG LXI B,11 ;ADD IT TO CNT IN TBL. DAD B ADD M MOV M,A JNC $+5 INX H INR M JMP DIR12 ; INSERT ENTRY IN TABLE. DIR10: RESTORE D,H SAVE D,H MOVE ,,15 ;MOVE THE DIRECTORY ENTRY RESTORE H,D LXI H,12 ;MOVE # OF GRPS TO EX. DAD D CALL CLCSPC ;GET NUMBER OF CPM GROUPS. MVI M,0 ;ZERO S1. DCX H MOV M,A LDA COUNT INR A STA COUNT ;INCR COUNT OF DIRECTORY ENTRIES LHLD OUTB DSTORE 0,IPOINT ;INDEXED STORE HL INDEX OUTB,16 INDEX IPOINT,2 JMP DIR12A ; BUMP INPUT POINTERS AND CHECK FOR END OF BUFFER. DIR12: RESTORE H DIR12A: JMP DIR5 ;READ ANOTHER BLOCK FROM DIRECTORY ; GET NUMBER OF CP/M GROUPS IN THIS EXTENT. CLCSPC: SAVE B,D,H LHLD INB ;POINT TO EXTENT GROUP #'S. LXI D,16 DAD D MVI B,0 ;ZERO COUNT. MVI C,16 ;SET FOR MAX # OF GROUPS. CLCLP: MOV A,M ;GET A GROUP #. ORA A ;ALLOCATED? JZ $+4 ;...NO. INR B ;...YES, BUMP COUNT. INX H ;BUMP GROUP PTR. DCR C ;DECR COUNT. JNZ CLCLP ;LOOP FOR ALL ENTRIES. MOV A,B ;GET COUNT. RESTORE H,D,B RET ; THIS ROUTINE PRINTS THE DIRECTORY IN 3 COLUMNS. NO OF LINES ; PRINTED IS CONTROLED BY VARIABLE LINES. ALL DIRECTORY NAMES ; ARE PRESENT IN TABLE BUT ONLY A MAXIMUM OF 3*LINES WILL BE ; PRINTED. ; DIR14: LXI H,0 SHLD W ;INITIALIZE ALLOCATION SHLD I ;INITIALIZE INDEX DIR16: DLOAD PDIR,I ;INDEX LOAD POINTER DJZ ENDFIL ;EXIT IF POINTER ZERO CALL DIR20 ;CALL PRINT ROUTINE FOR AN ENTRY. PRINT ' ' DLOAD PDIR+LINES*2,I ;POINTER COL 2 DJZ DIR18 ;NO PRINT IF ZERO CALL DIR20 ;PRINT IT PRINT ' ' DLOAD PDIR+LINES*4,I ;POINTER COL 3 DJZ DIR18 CALL DIR20 ;CALL PRINT ROUTINE DIR18: PRINT CRLF,$ INDEX I,2 ;INCR INDEX BY 2 LXI D,LINES*2 ;CHECK INDEX LIMIT CPHL JZ ENDFIL ;EXIT WHEN INDEX 32 JMP DIR16 ;PRINT SOME MORE ; *** SUBROUTINE TO PRINT A SINGLE DIRECTORY ENTRY *** ; ; PRINT THE FILE NAME. DIR20: MVI C,11 ;NAME LENGTH DIR22: SAVE B,H ;SAVE REGISTERS MOV A,M ;GET A CHAR. ANI 07FH ;REMOVE POSSIBLE INDICATOR. CHAROUT ;PUT IT TO THE CONSOLE. RESTORE H,B ;RESTORE THE REGISTERS INX H ;INCR NAME POINTER DCR C ;DECR CHAR COUNT JNZ DIR22 ;LOOP FOR LENGTH OF NAME. ; CALCULATE AND PRINT THE FILE SIZE. DIR24: MOV E,M ;DE = # OF ALLOCATED GROUPS INX H MOV D,M XCHG LDA DSKBSH ;CONVERT CP/M GROUP BACK TO K. SUI 3 JZ $+8 DAD H DCR A JNZ $-2 SAVE H XCHG ;ADD IT TO CUM TOTAL. LHLD W DAD D SHLD W PRINT ' ' POP H PUSH H LXI D,1000 CPHL JNC DIR30 PRINT ' ' POP H PUSH H LXI D,100 CPHL JNC DIR30 PRINT ' ' POP H PUSH H LXI D,10 CPHL JNC DIR30 PRINT ' ' DIR30: POP H DECOUT PRINT 'K' RET ; ; ; THIS ROUTINE RESTORES DRIVE B ; FIXB: LDA NEWDRV ;CHECK DRIVE NO ORA A RZ ;RETURN IF DRIVE A LDA NEWDRV ;SELECT THE DRIVE. MOV E,A DISKIO LOGIN RET ; ; THIS IS THE EXIT POINT FROM THE PROGRAM. PRINT NO OF FILES AND ; SPACE REMAINING, RELOAD OLD STACK POINTER AND RETURN BACK TO CCP. ; ENDFIL: PRINT LXI H,0 LDA COUNT MOV L,A DECOUT PRINT ' FILES ' LHLD MAXSTR ;GET MAX STORAGE SIZE. DSUB W ;SUBTRACT IN-USE SIZE. DECOUT PRINT <'K BYTES REMAINING ON DISK',CR,LF> EF1: LHLD OLDSTK SPHL ;RELOAD OLD STACK POINTER RET ;RETURN TO CCP WITHOUT REBOOT ; DSKERR: PRINT JMP EF1 ;EXIT ; ; THIS SECTION DOES THE ACTUAL SORTING OF THE DIRECTORY. DURING THE ; INPUT OF THE DIRECTORY NAMES, A TABLE OF ADDRESS POINTERS PDIR ; WAS CONSTRUCTED. THE SORT ROUTINE SORTS THE ADDRESS POINTERS ; RATHER THAN THE ACTUAL DIRECTORY. ; THIS IS AN IMPLEMENTATION OF C. A. R. HOARE'S QUICKSORT ALGORITHM. ; THE ALGORITHM IS VERY FAST AND GENERALLY USEFUL, HOWEVER CAUTION ; SHOULD BE USED WITH LARGE FILES. THE ALGORITHM IS RECURSIVE AND ; THE STACK SPACE REQUIRED IS PROPORTIONAL TO THE NO OF ITEMS TO BE ; SORTED. ; SORT: LDA COUNT ;NO OF ENTRIES IN DIR ORA A JZ ENDFIL ;EXIT IF DIRECTORY EMPTY DCR A LXI H,0 ;ZERO HL MOV L,A DAD H SHLD LAST ;END OF ARRAY LXI H,0 SHLD FIRST ;START OF ARRAY LXI H,0FFFFH PUSH H ;FLAG FOR STACK EMPTY LHLD FIRST PUSH H LHLD LAST PUSH H ;STACK CONTAINS FIRST AND LAST INDICES ; ; NOW POP STACK AND KEEP CALLING SPLIT RECURSIVELY TILL STACK EMPTY ; SORT2: POP H MOV A,H CPI 0FFH JZ DIR14 ;GO TO PRINT ROUTINE SHLD J SHLD LAST POP H SHLD I SHLD FIRST CALL SPLIT LHLD I XCHG LHLD FIRST CPHL JZ SORT4 PUSH H ;I ON STACK DCX D DCX D PUSH D ;J ON STACK SORT4: LHLD J XCHG LHLD LAST CPHL JZ SORT8 INX D INX D PUSH D ;NEW I ON STACK PUSH H ;NEW J ON STACK SORT8: JMP SORT2 ; ; SPLIT SUBROUTINE DOES A SINGLE PARTITION ON AN ARRAY OF POINTERS ; SPLIT: HALF I XCHG HALF J DAD D MOV A,L ANI 0FEH MOV L,A SHLD K ;K=I+J/2 DLOAD PDIR,K SHLD W ;W IS POINTER TO PARTITION ELEMENT OF PDIR SPLIT2: DLOAD PDIR,I ;GET ITEM FROM LEFT XCHG LHLD W ;PARTITION ELEMENT MATCH ,,11 ;CONPARE KEYS JP SPLIT4 INDEX I,2 ;INCR I JMP SPLIT2 SPLIT4: DLOAD PDIR,J ;GET ITEM FROM RIGHT XCHG LHLD W ;PARTITION ELEMENT XCHG MATCH ,,11 ;COMPARE KEYS JP SPLIT6 INDEX J,-2 JMP SPLIT4 ;LOOP BACK SPLIT6: LHLD I XCHG LHLD J CPHL ;COMPARE I AND J RZ ;RET IF I = J DLOAD PDIR,I ;SWITCH POINTERS SAVE H DLOAD PDIR,J DSTORE PDIR,I RESTORE H DSTORE PDIR,J JMP SPLIT2 ; ; DATA ALLOCATIONS ; LINES EQU 18 ;LINES PER PAGE ON DISPLAY SPACE: DB ' $' ;ASCII SPACE CRLF: DB 0DH,0AH,24H ;ASCII CR LF I: DW 0 ;PSEUDO INDEX REGISTER J: DW 0 ;PSEUDO INDEX REGISTER K: DW 0 ;PSEUDO INDEX REGISTER FIRST: DW 0 ;START OF ARRAY LAST: DW 0 ;END OF ARRAY W: DW 0 ;STORAGE FOR PARTITION INDEX DIRSIZ: DW 0 ;SIZE OF DIRECTORY IN K MAXSTR: DW 0 ;MAXIMUM STORAGE AVAILABLE LINE: DW 0 ;LINE NUMBER FOR LISTING IPOINT: DW 00 ;VARIABLE BUFFER POINTER DRVNO: DB 0 ;STORAGE FOR ORIGINALLY LOGGED DRIVE NEWDRV: DB 0 ;STORAGE FOR NEW DRIVE NO COUNT: DB 0 ;COUNT OF DIRECTORY ENTRIES OLDSTK: DW 0 ;STORAGE FOR OLD STACK POINTER ENDSTK: DS 128 ;STORAGE FOR NEW STACK NEWSTK: DW 0 ;NEW STACK INB: DW 0 ;STORES POINTER TO INPUT BUFFER AREA OUTB: DW 0 ;STORES POINTER TO DIRECTORY BUFFER AREA DPBADR: DW 0 ;ADDRESS OF DISK'S CP/M DPB DSKBSH: DB 0 ;DISK DPBBSH VALUE DSKBLM: DB 0 ;DISK DPBBLM VALUE DIRFCB: DB '?','????????','???','?',0,0,0 DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DB 0,0,0 PDIR DW 0 ;POINTER TABLE TO DIRECTORY DIRBUF: EQU PDIR+(2*MAXDIR) ;START OF AREA USED TO STORE AND SORT DIRECTORY END