; ; SD.A86 ; (revised 05/05/81) ; ; SUPER DIRECTORY PROGRAM ; by Bruce R. Ratoff ; ;Displays the directory of a CP/M disk, sorted alphabetically, ;with the file size in K, rounded to the nearest CP/M block size. ; ;This latest variation on a common theme will automatically adjust ;itself for any block size and directory length under CP/M 1.4 or 2.x ;or MP/M (any version). If the screen fills, program will pause until ;a key is struck (see NPL and LPS equates below). Total space used ;and number of files are printed at end. ; ;Command: SD FILENAME.FILETYPE or just SD ; ;Allows '*' or '?' type specifications. Drive name may also be ;specified. Ignores "SYS" files unless SOPT is TRUE and 'S' option ;is given (i.e., SD *.* S will print all files). ; ;05/03/81 First 8086 version. (Bruce R. Ratoff) ; ;01/21/81 Fixed print abort test so it would work like "DIR" ; [aborts on any character, or on ^S, ^C]. ; (ASB) ; ; ;01/06/81 Made output go through BDOS so if the printer was on, ; you could get the output on it. The ^S test was rendered ; useless, as was the ^C test, as BDOS buffers one character ; ahead when it is writing out to the console. [You now need ; to do a ^S ^C combonation to abort, and the size message ; is not printed, but I don't think that is a major problem.] ; (Andrew S. Beals) ; ;01/06/81 Added conditional assembly to print user number when in CP/M ; 2.x. (ASB) ; ;12/15/80 Added space suppression when printing file ; totals. (KBP) ; ;12/14/80 Added logic to print space remaining on disk. ; Changed ^C test so that interrupting character is ; not echoed (makes remote use cleaner). (BRR) ; ;12/02/80 Fixed bug in print routine which compared last file ; against garbage before printing. (BRR) ; ;11/29/80 Changed to allow printing 4 file names. (Ben Bronson ; and Keith Petersen) ; ;11/22/80 Fixed bug in handling >256 files. Changed abort test in ; print routine to only abort on control-c. (brr) ; ;Based on 'DIRS' by Keith Petersen, W8SDZ ; ;Set 'RMAC' TRUE to assemble with relocating assembler (requires ;link with PAGE 0 equates in separate file). ; FALSE EQU 0 ;DEFINE LOGICAL FALSE TRUE EQU NOT FALSE ;DEFINE LOGICAL TRUE ; SOPT EQU TRUE ;PUT TRUE TO ALLOW 'DIR *.* S' FORM WIDE EQU true ;PUT TRUE TO ALLOW 4 NAMES ACROSS user equ true ;print user numbers for cp/m 2.x also? ; ; ; BASE EQU 0 TPA EQU 100H ; FCB EQU 5CH ; IF WIDE NPL EQU 4 ;NUMBER OF NAMES PER LINE ENDIF ; IF NOT WIDE NPL EQU 3 ;NUMBER OF NAMES PER LINE ENDIF ; LPS EQU 23 ;NUMBER OF LINES PER SCREEN DELIM EQU ':' ;FENCE (DELIMITER) CHARACTER ; org TPA ; START: cld ; IF SOPT mov al,byte ptr .FCB+17 ;SAVE S OPTION FLAG mov SOPFLG,al ;(BLANK OR LETTER S) ENDIF ; mov USERNO,0 ;DEFAULT TO USER 0 mov LINCNT,0 ;CLEAR COUNT OF LINES ON SCREEN mov cl,12 CALL BDOS ;CHECK CP/M VERSION mov word ptr VERFLG,bx ;LO ORD >0 IF 2.X, HI ORD>0 IF MP/M mov dl,0FFH mov cl,CURUSR ;INTERROGATE USER NUMBER CALL BDOS mov USERNO,al ; if not user mov al,MPMFLG ;MP/M? test al,al ;IF SO, TYPC HEADING LINE JZ CHKDRV ; ELSE SKIP IT endif ; mov dx,offset USRMSG ;DISPLAY IT mov cl,PRINT CALL BDOS ;FIRST PART OF MESSAGE mov al,USERNO cmp al,10 ;IF USER NO. > 9 PRINT LEADING 1 JB DUX mov al,'1' CALL TYPC mov al,USERNO ;PRINT LOW DIGIT OF USER NO. sub al,10 ; DUX: add al,'0' CALL TYPC mov dx,offset USRMS2 ;PRINT TAIL OF MESSAGE mov cl,PRINT CALL BDOS mov LINCNT,1 ;WE USED A LINE ; CHKDRV: mov si,offset FCB lods al ;get drive name test al,al ;ANY SPECIFIED? JNZ START2 ;YES SKIP NEXT ROUTINE mov cl,CURDSK CALL BDOS ;GET CURRENT DISK NR inc al ;MAKE A:=1 mov byte ptr .FCB,al ; START2: add al,'A'-1 ;MAKE IT PRINTABLE mov DRNAM,al ;SAVE FOR LATER mov di,offset FCB+1 ;POINT TO NAME mov al,[di] ;ANY SPECIFIED? cmp al,' ' JNZ GOTFCB ;No FCB - make FCB all '?' mov cx,11 ;FN+FT COUNT mov al,'?' ; rep stos al ;fill fcb with '?' ; GOTFCB: mov byte ptr .FCB+12,'?' ;FORCE WILD EXTENT mov al,byte ptr .FCB ;CHECK FOR EXPLICIT DRIVE dec al mov dl,al ;SELECT SPECIFIED DRIVE mov cl,SELDSK CALL BDOS mov byte ptr .FCB,0 ; mov cl,CURDPB;IT'S 2.X OR MP/M...REQUEST DPB push es ;save current extra segment int 224 ;return bx=offset dpb, es=segment dpb add bx,2 mov al,es: [bx] mov BLKSHF,al ;GET BLOCK SHIFT inc bx ;BUMP TO BLOCK MASK mov al,es: [bx] mov BLKMSK,al add bx,2 mov ax,es: [bx] mov BLKMAX,ax add bx,2 mov ax,es: [bx] mov DIRMAX,ax ;SAVE IT pop es ;restore our extra segment ; SETTBL: inc ax ;DIRECTORY SIZE IS DIRMAX+1 shl ax,1 ;DOUBLE DIRECTORY SIZE add ax,offset ORDER ;TO GET SIZE OF ORDER TABLE mov TBLOC,ax ;NAME TABLE BEGINS WHERE ORDER TABLE ENDS mov NEXTT,ax mov bx,word ptr .BASE+6 ;MAKE SURE WE HAVE ROOM TO CONTINUE cmp ax,bx jb SFIRST JMP OUTMEM ; ;Look up the FCB in the directory ; SFIRST: mov cl,FSRCHF ;GET 'SEARCH FIRST' FNC mov dx,offset FCB CALL BDOS ;READ FIRST inc al ;WERE THERE ANY? JNZ SOME ;GOT SOME ; NONE: mov dx,offset FNF ;PREPARE MP/M ERROR MESSAGE mov al,MPMFLG test al,al ;USE IT IF REALLY MP/M jz NOFILE JMP ERXIT1 NOFILE: CALL ERXIT ;ELSE USE CP/M ERROR MESSAGE DB 'NO FILE$' FNF DB 'File not found.$' ; USRMSG DB 'Directory for user $' USRMS2 DB ':',13,10,'$' ; ;Read more directory entries ; MORDIR: mov cl,FSRCHN ;SEARCH NEXT mov dx,offset FCB CALL BDOS ;READ DIR ENTRY inc al ;CHECK FOR END (0FFH) JZ SPRINT ;NO MORE - SORT & PRINT ; ;Point to directory entry ; SOME: dec al ;UNDO PREV 'INR A' mov cl,5 shl al,cl ;entry no. times 32 mov ah,0 add al,80h mov bx,ax ;POINT TO BUFFER ;(SKIP TO FN/FT) ; IF SOPT mov al,SOPFLG ;DID USER REQUEST SYS FILES? cmp al,'S' JZ SYSFOK ENDIF ; test byte ptr 10[bx],80H ;check bit 7 of SYS byte JNZ MORDIR ;SKIP THAT FILE ; SYSFOK: mov al,USERNO ;GET CURRENT USER cmp al,[bx] JNZ MORDIR ;IGNORE IF DIFFERENT inc bx ; ;Move entry to table ; mov si,bx ;si points to name mov di,NEXTT ;NEXT TABLE ENTRY TO di mov cx,12 ;ENTRY LENGTH (NAME, TYPC, EXTENT) ; TMOVE: lods al ;GET ENTRY CHAR and al,7FH ;REMOVE ATTRIBUTES stos al ;store in table loop TMOVE mov al,2[si] ;get sector count MOV [di],al ;STORE IN TABLE inc di mov NEXTT,di ;SAVE UPDATED TABLE ADDR inc COUNT add di,13 ;SIZE OF NEXT ENTRY sub di,word ptr .BASE+6 ;PICK UP TPA END JB MORDIR ;IF TPA END>NEXTT THEN LOOP BACK FOR MORE ; OUTMEM: CALL ERXIT DB 'Out of memory.',13,10,'$' ; ;Sort and print ; SPRINT: mov cx,COUNT ;GET FILE NAME COUNT test cx,cx jnz SPRINI jmp NONE ;NONE, EXIT ;Init the order table SPRINI: mov ax,TBLOC ;GET START OF NAME TABLE mov di,offset ORDER ;POINT TO ORDER TABLE ; BLDORD: stos ax add ax,13 loop BLDORD mov bx,COUNT ;GET COUNT mov SCOUNT,bx ;SAVE AS # TO SORT dec bx ;only 1 entry? JZ DONE ;..YES, SO SKIP SORT ; SORT: mov SWITCH,0 ;SHOW NONE SWITCHED mov bx,SCOUNT ;GET COUNT dec bx ;use 1 less mov word ptr TEMP,bx ;SAVE # TO COMPARE mov SCOUNT,bx ;SAVE HIGHEST ENTRY JZ DONE ;EXIT IF NO MORE mov bx,offset ORDER ;POINT TO ORDER TABLE ; SORTLP: mov cx,12 ;# BYTES TO COMPARE CALL COMPR ;COMPARE 2 ENTRIES jbe NOSWAP CALL SWAP ;SWAP IF NOT IN ORDER NOSWAP: add bx,2 ;bump order table ptr dec TEMP ;BUMP COUNT JNZ SORTLP ;CONTINUE ;One pass of sort done mov al,SWITCH ;ANY SWAPS DONE? test al,al JNZ SORT ; ;Sort is all done - print entries ; DONE: mov bx,offset ORDER mov NEXTT,bx ; ;Print an entry ; IF NOT WIDE CALL DRPRNT ;PRINT DRIVE NAME ENDIF mov cx,NPL ;NR. OF NAMES PER LINE mov TOTSIZ,0 ; TOTAL K USED mov TOTFIL,0 ; AND TOTAL FILES ; ENTRY: mov bx,COUNT ; CHECK COUNT OF REMAINING FILES dec bx ; skip compare if only 1 left JZ OKPRNT PUSH cx ; mov cl,dconio ;get console status mov dl,0ffh call bdos test al,al ;char? jz nobrk ;no char, bypass the other stuff jmp exit ;abort ; NOBRK: mov bx,NEXTT mov cx,11 CALL COMPR ;DOES THIS ENTRY MATCH NEXT ONE? pop cx JNE OKPRNT ;NO, PRINT IT add bx,2 ;SKIP, SINCE HIGHEST EXTENT COMES LAST IN LIST mov NEXTT,bx dec COUNT ;COUNT DOWN JMP ENTRY ;GO GET NEXT ; OKPRNT: push cx ; IF NOT WIDE CALL FENCE ;PRINT FENCE CHAR AND SPACE ENDIF ; mov bx,NEXTT ;GET ORDER TABLE POINTER mov si,[bx] add bx,2 mov NEXTT,bx ;SAVE UPDATED TABLE POINTER mov cx,8 ;FILE NAME LENGTH CALL TYPCIT ;TYPC FILENAME mov al,'.' ;PERIOD AFTER FN CALL TYPC mov cx,3 ;GET THE FILETYPC CALL TYPCIT mov dl,[si] mov dh,0 inc si mov al,[si] ;GET SECTOR COUNT OF LAST EXTENT mov cl,4 ;# OF EXTENTS TIMES 16K shl dx,cl ADD al,BLKMSK ;ROUND LAST EXTENT TO BLOCK SIZE mov cl,3 shr al,cl ;CONVERT FROM SECTORS TO K mov ah,0 add dx,ax ;add to total K mov al,BLKMSK ;GET SECTORS/BLK-1 mov cl,3 shr ax,cl ;CONVERT TO K/BLK not ax ;USE TO FINISH ROUNDING and dx,ax add TOTSIZ,dx ;add to total used inc TOTFIL ;INCREMENT FILE COUNT mov ax,dx ;GET BACK FILE SIZE CALL DECPRT ; AND PRINT IT mov al,'k' ;FOLLOW WITH K CALL TYPC ; IF NOT WIDE CALL SPACE ENDIF ; ;See if more entries ; dec COUNT ;COUNT DOWN ENTRIES pop cx JZ PRTOTL ;IF OUT OF FILES, PRINT TOTALS DEC CX ;ONE LESS ON THIS LINE jz DOCRLF ; IF WIDE CALL FENCE ;NO CR-LF NEEDED, DO FENCE ENDIF ; jmps NOCRLF ; DOCRLF: CALL CRLF ;CR-LF NEEDED NOCRLF: JMP ENTRY ; ;Print HL in decimal with leading zero suppression ; DECPRT: ;CLEAR LEADING ZERO FLAG mov LZFLG,0 mov bx,1000 ;PRINT 1000'S DIGIT CALL DIGIT mov bx,100 ;ETC CALL DIGIT mov bx,10 CALL DIGIT add al,'0' ;GET 1'S DIGIT JMP TYPC ; DIGIT: mov dx,0 ;init hi order dividend div bx ;divide ax by digit value (dx gets rmdr) add al,'0' ;convert to ASCII digit ; cmp al,'0' ;ZERO DIGIT? JNZ DIGNZ ;NO, TYPC IT mov al,LZFLG ;LEADING ZERO? test al,al mov al,'0' JNZ DIGPR ;PRINT DIGIT mov al,SUPSPC ;GET SPACE SUPPRESSION FLAG test al,al ;SEE IF PRINTING FILE TOTALS jz DIGNP ;YES, DON'T GIVE LEADING SPACES mov al,' ' JMPS DIGPR ;LEADING ZERO...PRINT SPACE ; DIGNZ: mov LZFLG,0ffh ;SET LEADING ZERO FLAG SO NEXT ZERO PRINTS DIGPR: call TYPC ;AND PRINT DIGIT DIGNP: mov ax,dx ;set up remainder for next digit ret ; ;Show total space and files used ; PRTOTL: mov SUPSPC,0 ;SUPPRESS LEADING SPACES IN TOTALS CALL CRLF ;NEW LINE (WITH PAUSE IF NECESSARY) ; IF WIDE mov dx,offset TOTMS1 ;PRINT FIRST PART OF TOTAL MESSAGE ENDIF ; IF NOT WIDE mov dx,offset TOTMS1+1 ;PRINT FIRST PART OF TOTAL MESSAGE ENDIF ; mov cl,PRINT CALL BDOS mov ax,TOTSIZ ;PRINT TOTAL K USED CALL DECPRT mov dx,offset TOTMS2;NEXT PART OF MESSAGE mov cl,PRINT CALL BDOS mov ax,TOTFIL ;PRINT COUNT OF FILES CALL DECPRT mov dx,offset TOTMS3;TAIL OF MESSAGE mov cl,PRINT CALL BDOS mov cl,GALLOC ;GET ADDRESS OF ALLOCATION VECTOR push es ;save our ES int 224 ;return bx=offset ALV, es=segment ALV mov dx,BLKMAX ;GET ITS LENGTH inc dx mov cx,0 ;INIT BLOCK COUNT TO 0 ; GSPBYT: PUSH bx ;SAVE ALLOC ADDRESS mov al,es: [bx] mov bl,8 ;SET TO PROCESS 8 BLOCKS ; GSPLUP: shl al,1 ;TEST BIT JB NOTFRE inc cx ; NOTFRE: dec dx ;COUNT DOWN BLOCKS JZ ENDALC ;QUIT IF OUT OF BLOCKS dec bl ;COUNT DOWN 8 BITS JNZ GSPLUP ;DO ANOTHER BIT POP bx ;BUMP TO NEXT BYTE INC bx ;OF ALLOC. VECTOR JMPS GSPBYT ;PROCESS IT ; ENDALC: pop es ;restore our es mov ax,cx mov cl,BLKSHF ;GET BLOCK SHIFT FACTOR sub cl,3 ;CONVERT FROM SECTORS TO K JZ PRTFRE ;SKIP SHIFTS IF 1K BLOCKS ; shl ax,cl ;mult blks by k/blk ; PRTFRE: CALL DECPRT ;PRINT K FREE mov dx,offset TOTMS4 mov cl,PRINT CALL BDOS JMP EXIT ;ALL DONE...RETURN TO CP/M ; TOTMS1 DB ' : Total of $' DRNAM equ TOTMS1 TOTMS2 DB 'k in $' TOTMS3 DB ' files with $' TOTMS4 DB 'k space remaining.$' ; FENCE: IF WIDE CALL SPACE ENDIF mov al,DELIM ;FENCE CHARACTER CALL TYPC ;PRINT IT, FALL INTO SPACE ; SPACE: mov al,' ' ; ;Type character in A ; TYPC: PUSH cx PUSH dx push bx push si mov dl,al ;use bdos calls, that's what they're there for mov cl,dconio call bdos pop si POP bx POP dx POP cx RET ; TYPCIT: lods al CALL TYPC loop TYPCIT RET ; ;Fetch character from console (without echo) ; CINPUT: mov cl,dconio mov al,0ffh call BDOS and al,7FH jz CINPUT RET ; CRLF: mov al,LINCNT ;CHECK FOR END OF SCREEN inc al cmp al,LPS JB NOTEOS ;SKIP MESSAGE IF MORE LINES LEFT ON SCREEN mov dx,offset EOSMSG;SAY WE'RE PAUSING FOR INPUT mov cl,PRINT CALL BDOS CALL CINPUT ;WAIT FOR CHAR. mov al,0 ;SET UP TO ZERO LINE COUNT ; NOTEOS: mov LINCNT,al ;SAVE NEW LINE COUNT mov al,13 ;print cr call TYPC mov al,10 ;lf call TYPC ; IF NOT WIDE CALL DRPRNT ;DRIVE NAME ENDIF ; mov cx,NPL ;RESET NUMBER OF NAMES PER LINE RET ; EOSMSG DB 13,10,'(Strike any key to continue)$' ; IF NOT WIDE DRPRNT: mov al,DRNAM JMP TYPC ENDIF ; ;Compare routine for sort ; COMPR: mov si,[bx] mov di,2[bx] repe cmps al,al ret ; ; ;Swap entries in the order table ; SWAP: mov SWITCH,1 ;SHOW A SWAP WAS MADE mov dx,[bx] xchg dx,2[bx] mov [bx],dx ret ; ;Error exit ; ERXIT: POP dx ;GET MSG ; ERXIT1: mov cl,PRINT ; CALLB: CALL BDOS ;PERFORM REQUESTED FUNCTION ; ;(fall into exit) ;Exit - all done, restore stack ; EXIT: mov cl,0 ;exit is via BDOS call 0 ; BDOS: push es ;preserve es thru bdos call int 224 ;call bdos 8086 style pop es ret ; ;Temporary storage area ; BLKSHF DB 0 ;# SHIFTS TO MULT BY SEC/BLK BLKMSK DB 0 ;SEC/BLK - 1 BLKMAX DW 0 ;HIGHEST BLOCK # ON DRIVE DIRMAX DW 0 ;HIGHEST FILE # IN DIRECTORY TOTSIZ DW 0 ;TOTAL SIZE OF ALL FILES TOTFIL DW 0 ;TOTAL NUMBER OF FILES LINCNT DB 0 ;COUNT OF LINES ON SCREEN TBLOC DW 0 ;POINTER TO START OF NAME TABLE NEXTT DW 0 ;NEXT TABLE ENTRY COUNT DW 0 ;ENTRY COUNT SCOUNT DW 0 ;# TO SORT SWITCH DB 0 ;SWAP SWITCH FOR SORT SUPSPC DB 0FFH ;LEADING SPACE FLAG FOR DECIMAL RTN. BUFAD DW BASE+80H ;OUTPUT ADDR SOPFLG db 0 ;SET TO 'S' TO ALLOW SYS FILES TO PRINT USERNO db 0 ;CONTAINS CURRENT USER NUMBER TEMP dw 0 ;SAVE DIR ENTRY VERFLG db 0 ;VERSION FLAG MPMFLG db 0 ;MP/M FLAG LZFLG db 0 ;0 WHEN PRINTING LEADING ZEROS ORDER EQU $ ;ORDER TABLE STARTS HERE ; ;BDOS equates ; RDCHR EQU 1 ;READ CHAR FROM CONSOLE WRCHR EQU 2 ;WRITE CHR TO CONSOLE DCONIO EQU 6 ;direct console i/o PRINT EQU 9 ;PRINT CONSOLE BUFF CONST EQU 11 ;CHECK CONS STAT SELDSK EQU 14 ;SELECT DISK FOPEN EQU 15 ;0FFH=NOT FOUND FCLOSE EQU 16 ; " " FSRCHF EQU 17 ; " " FSRCHN EQU 18 ; " " CURDSK EQU 25 ;GET CURRENTLY LOGGED DISK NAME GALLOC EQU 27 ;GET ADDRESS OF ALLOCATION VECTOR CURDPB EQU 31 ;GET CURRENT DISK PARAMETERS CURUSR EQU 32 ;GET CURRENTLY LOGGED USER NUMBER (2.x ONLY) ; END