; DATE 10/07/83 09:43 last revision ; ; SIG/M release REVIDEO set TRUE & INTOREV & OUTAREV set for ADM-31 ; TITLE 'SD Version 4.8B for CP/M-86' ; ; DATE 10/03/83 14:32 ; by: H.M. Van Tassell 120 Hill Hollow Rd, Watchung, NJ 07060 (201)755-5372 ; ; This is an XLT86 translation of SD-4.8B the original of which was released ; on SIG/M vol 145, see the original release for history and author credits. ; ; The "usual" minor changes have been made to enable the translation. ; References to CP/M ver 2.2 and lower have been deleted, segment override ; added where required, and the version notice updated. ; ; NOTE: XLT86 deletes all conditional statements so this version is setup ; as shown in the USER OPTION SPECIFICATIONS. None of these options will ; work and are included here as reference for those who wish to add back ; any of the OPTIONS. ; I did add the REVIDEO option for those who dont wish to use enhanced video. ; ; This version should work with CP/M-86 ver 1.1, Concurrent 3.0, and Plus 3.1 ; It works with my beta test copy of PLUS 3.1 but I have not tested Concurrent. ; ; To generate program: ; ASM86 SD $SZPZ ; GENCMD SD 8080 CODE[M3FF] ; ; ;>>>> The following notes appeared in the preamble to CP/M-80 SD-4.8B. <<<< ; ; Made several changes to make SD more useful and friendly to me and maybe ; to others. Added HELP if // is filename or if a bad option specified. ; Changed so will always start with drive A: if D option choosen and no ; drive is specified in command line, user = 0 if A option choosen and no ; user was specified as an option,-see the new STARTZ, ZUSR & ZDRV. ; Added STSKIP & SKIPTO to skip over drives that dont exist but are in ; drive table. Useful for some of us that use M: to specify memory drive. ; To more closely conform to Digial Research notation used in CP/M PLUS, ; changed from "$" to "[]" to indicate options and eliminated need for a ; leading space and allow the use of either space or comma between options. ; Added code for 3 byte INTO/OUTA reverse video so can use esc,G,x code to ; indicate file attributes. Since directorys can be long, added a message ; at start of each section, see STMSG equate. Added option 'T' which is ; a sort by filetype and then by filename, TOPREV reverses 'T' option. ; NOTE: Edit help message at start of code to match your choice of options ; SD.ASM Ver 4.8A ; (Rev 14 MAY 83) ; ; SUPER DIRECTORY PROGRAM ; by Bruce R. Ratoff ; ; Based on 'DIRS' by Keith Petersen, W8SDZ ; ; 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). Provisions are made for (1) automatic pauses ; when the screen fills up; (2) searching individual or multiple drives ; and/or user areas; (3) unconditional or optionally resetting the disk ; system before execution begins; (4) directing output to a disk file ; and appending to it on subsequent runs; (5) summary line output giving ; drive and user information, # of files matched and how much space they ; consume, and the amount of free space remaining on the disk; (6) displaying ; or suppressing CP/M 2 "system" files; (7) accepting ambiguous filenames ; with or without a drive name; and (8) printer output. ; See SD.DOC for detailed instructions on configuring and running SD.ASM ; ;****************************** ;* * ;* USER OPTION SPECIFICATIONS * ;* * ;****************************** ; ;****** NOTE: none of these work - they are here for reference only ***** ; ;ALTCPM EQU FALSE ;True for H8 or TRS-80 ;DIRCON EQU FALSE ;True for direct console output ;LOWCCP EQU FALSE ;True only if you are running with BYELOW ;BDOSPG EQU 0CCh ;Set properly if LOWCCP is true ;OPTION EQU TRUE ;True if allowing ANY command line options ;AOPT EQU TRUE ;True to allow searching all user areas ;DOPT EQU TRUE ;True to allow searching all drives on-line ;STARTZ EQU TRUE ;True to start with zero dr & user in none spec ;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 ;REPUSR EQU TRUE ;True to report user numbers ;RMAC EQU FALSE ;True for RMAC assembly and external def link ;ROPT EQU TRUE ;True to allow reset option ;SOPT EQU TRUE ;True to allow system file option ;TOPT EQU TRUE ;True for sort by filetype & filename ;TOPREV EQU FALSE ;False to reverse "T" opt for normal sort ;UOPT EQU TRUE ;True to allow user number option ;VOPT EQU TRUE ;True to allow version number display ;LOPT EQU TRUE ;True to allow Library file member printing ;LOPREV EQU FALSE ;False to reverse the "L" option and not print ;************ See above NOTE in reference to above equates ************** ;=========================================================================== M EQU Byte Ptr 0[BX] FALSE EQU 0 TRUE EQU NOT FALSE REVIDEO EQU TRUE ;Use reverse video sequences LEADIN EQU 27 ;Esc. leadin for ADM-31 & Z19 ; INTOREV EQU 3847H ;Seq. to enter underline mode (for WYSE-200) ; OUTAREV EQU 3047H ;and to get out ((note digits are reversed)) ; NOTE: use a 2 digit INTO/OUTA sequence & set one to 00 if only one needed INTOREV EQU 0029H ;Dim video for ADM-31 type terminals OUTAREV EQU 0028H ; " " " " " " ; INTOREV EQU 0070H ;Sequence to enter reverse video (Z19) ; OUTAREV EQU 0071H ;And to get out ; LEADIN EQU '~' ;Tilda leadin for hazeltine 1500 ; INTOREV EQU 0019H ;Background mode on Hazeltine 1500 ; OUTAREV EQU 001FH ;Foreground mode on Hazeltine 1500 DELIM EQU 7CH ;Fence (delimiter) character (vertical bar) ;DELIM EQU 20H ;Space NPL EQU 4 ;# of names per line (max of 3 for 64x16) ; (max of 4 for 80x24) LPS EQU 23 ;# of lines per screen (max of 14 for 64x16) ; (max of 23 for 80x24) STMSG EQU TRUE ;true to start each directory with message ; STSKIP EQU 3 ;Start skipping at this drive number SKIPTO EQU 13 ;and skip to this drive number ; ; 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 ;Default to 0 (or 100H with MAC +R option) TPA EQU 100H FCB EQU BASE+5CH BDOS EQU BASE+5 ORG TPA ;******************************** ; * ; BEGIN EXECUTABLE PROGRAM CODE * ; * ;******************************** JMP START L_1 EQU $ DSEG ORG Offset L_1 MLEADIN DB LEADIN ;leadin char for hilite sequences MINTOREV DW INTOREV ;code to enter hilite mode MOUTAREV DW OUTAREV ;code to return to normal mode MDELIM DB DELIM ;char to delimit columns on display MNPL DB NPL ;names per line (# of columns) MLPS DB LPS ;lines per screen (between pauses) VERNAME RS 0 DB 'SD 4.8B-86 03Oct83/HMVT ',0 HLPMES DB 13,10,10 DB 'Useage: SD dr:FILENAME.TYP [option,option...]' DB 13,10,10,'Drive and/or filespec are optional' DB 13,10,'Wildcards * and ? allowed' DB 13,10,'SD // for help' DB 13,10,10,'Options Are:' DB 13,10,09,'A=All Users' DB 13,10,09,'D=All Disks' DB 13,10,09,'F=Make File' DB 13,10,09,'L=Library' DB 13,10,09,'N=No Paging' DB 13,10,09,'P=Printer' DB 13,10,09,'R=Reset' DB 13,10,09,'S=System' DB 13,10,09,'T=Sort by Type' DB 13,10,09,'Un=User No.' DB 13,10,09,'V=Version',0 L_2 EQU $ CSEG ORG Offset L_2 START: MOV CL,12 ;Get and save the CP/M version # INT 224 MOV AL,BL MOV Byte Ptr VERFLG,AL 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 ; 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+2 pointer MOV CH,M ;Get length of command line buffer ; Check for a help request INC BX INC BX MOV AL,M CMP AL,'/' JNZ L_4 CALL HELP L_4: LAHF DEC BX SAHF LAHF DEC BX SAHF ; Search for the command line delimiter. If not found, assume no options. SCNDOL: LAHF INC BX SAHF DEC CH JNS L_5 JMP CKREST ;Exit if command line buffer empty L_5: MOV AL,M CMP AL,'[' ;Digital Research changing to "[" JNZ SCNDOL ; DCX H ;'$' found - make sure space precedes it ; MOV A,M ; INX H ; CPI ' ' ; 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_6 JMP CKREST ;If option field exhausted, exit L_6: 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 CMP AL,',' ;Same for comma JZ SCNOPT CMP AL,']' ;End of option list JNZ L_7 JMP CKREST L_7: 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. 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 XCHG AL,AH PUSH AX MOV AL,0FFH ;Set got user flag MOV Byte Ptr GOTUSR,AL POP AX XCHG AL,AH SAHF 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 JNB L_8 JMP SCNAGN ;If next char not numeric, its not part of L_8: CMP AL,10 ;..user number so go check for another option JNAE L_9 JMP SCNAGN L_9: 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 ;Balance UOPT ; 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. 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 TTYPE INC BX JMPS CLELP CLEX: MOV AL,'?' ;Tag line with a '?' field CALL TTYPE JMP HELP1 ;..and tell how to do it ;Balance OPTION ; Options input or not specified. If reset option specified, reset ; the disk system now. Its important that the reset be done OUTSIDE ; the multiple drive loop if the file output option is enabled because ; CP/M 1.4 clobbers the DMA buffer on reset. 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_10 CALL CPM L_10: ;Balance ROPT MOV AL,Byte Ptr VOPFLG ;If version display flag set, print it OR AL,AL JNZ NOVOPT MOV DX,(Offset VERNAME) CALL PRINT CALL CRLF NOVOPT: MOV AL,Byte Ptr DOPFLG ;If multi-disk flag set, OR AL,AL ;..need to set error traps JNZ L_11 CALL SWAPEM ;Swap BDOS error vector tables L_11: ; If "A" option choosen and no user number specified ; set user to zero ZUSR: MOV AL,Byte Ptr GOTUSR ;Was a user specified? OR AL,AL JNZ ZDRV ;YES-quit now MOV AL,Byte Ptr AOPFLG ;Was D option specified? OR AL,AL JNZ ZDRV ;JMP if NO XOR AL,AL MOV Byte Ptr NEWUSR,AL ;ZERO NEWUSR for all users MOV Byte Ptr BASUSR,AL ;ZERO BASUSR for all users ; If "D" option choosen and no drive specified ; set drive to physical zero ZDRV: MOV BX,FCB MOV AL,M ;Get drive name for directory search OR AL,AL ;Any specified? JNZ NOOPT ;Yes skip next routine MOV AL,Byte Ptr DOPFLG ;else if DOPT then OR AL,AL JNZ ZDRV1 INC AL ;always start with A MOV M,AL JMPS NOOPT ZDRV1: MOV AL,Byte Ptr OLDDSK ;Otherwise, get default disk INC AL MOV M,AL ;Put the absolute drive code in directory FCB ; 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_12 JMP ERXIT ;Take drive error exit if out of range L_12: 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_13 JMP ERXIT ;Take error exit if user number illegal L_13: POP DX ;Destroy error message pointer MOV BX,FCB+1 ;Point to name MOV AL,M ;Any specified? CMP AL,'[' JZ GOTDL CMP AL,' ' JNZ GOTFCB ; No FCB - make FCB all '?' GOTDL: MOV CH,11 ;FN+FT count QLOOP: MOV M,'?' ;Store '?' IN FCB INC BX 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 ;request DPB INT 224 INC BX INC BX MOV AL,ES:M ;Get block shift MOV Byte Ptr BLKSHF,AL INC BX ;Bump to block mask MOV AL,ES:M MOV Byte Ptr BLKMSK,AL ;Get it INC BX INC BX MOV DL,ES:M ;Get max block # INC BX MOV DH,ES:M XCHG BX,DX MOV Word Ptr BLKMAX,BX ;Save it XCHG BX,DX INC BX MOV DL,ES:M ;Get directory size INC BX MOV DH,ES:M XCHG BX,DX ; Calculate # of K free on selected drive now so that the FREE figure ; will not reflect either the creation or additions to the SD.DIR ; file (which we would probably erase or move anyway). FREE: MOV Word Ptr DIRMAX,BX ;Save max # of entries in directory MOV AL,Byte Ptr VERFLG ;check version # CMP AL,30H ;3.0 or higher? JB FREE20 ;use old method if not MOV AL,Byte Ptr .FCB ;get drive # DEC AL MOV DL,AL ;Use new Compute Free Space BDOS call MOV CL,46 CALL CPM MOV CL,3 ;answer is a 24-bit integer FRE3L1: MOV BX,BASE+82H ;answer is in 1st 3 bytes of DMA adr MOV CH,3 ;convert it from sectors to K OR AL,AL ;by dividing by 8 FRE3L2: MOV AL,M RCR AL,1 MOV M,AL DEC BX DEC CH JNZ FRE3L2 ;Loop for 3 bytes DEC CL JNZ FRE3L1 ;Shift 3 times MOV BX,Word Ptr .BASE+80H ;Now get result in K JMPS SAVFRE ;Go store it FREE20: MOV CL,GALLOC ;Get address of allocation vector INT 224 ;address ret as ES:BX XCHG BX,DX MOV BX,Word Ptr BLKMAX ;Get its length INC BX MOV CX,0 ;Init block count to 0 GSPBYT: PUSH DX ;Save alloc address MOV SI,DX ;offset in SI MOV AL,ES:[SI] ;seg in ES MOV DL,8 ;Set to process 8 blocks GSPLUP: RCL AL,1 ;Test bit JB NOTFRE INC CX NOTFRE: MOV DH,AL ;Save bits DEC BX ;Count down blocks MOV AL,BL OR AL,BH JZ ENDALC ;Quit if out of blocks MOV AL,DH ;Restore bits DEC DL ;Count down 8 bits JNZ GSPLUP ;Do another bit POP DX ;Bump to next byte.. INC DX ;..of alloc. vector JMPS GSPBYT ;Process it ENDALC: POP DX ;Clear allocation vector pointer from stack MOV BL,CL ;Copy blocks to hl MOV BH,CH MOV AL,Byte Ptr BLKSHF ;Get block shift factor SUB AL,3 ;Convert from sectors to K JZ SAVFRE ;Skip shifts if 1K blocks - return free in HL FREKLP: SHL BX,1 ;Multiply blocks by K/BLK DEC AL JNZ FREKLP SAVFRE: MOV Word Ptr FREEBY,BX ;Save the free space for output later ; Reenter here on subsequent passes while in the all-users mode SETTBL: MOV BX,Word Ptr DIRMAX ;Get directory maximum again INC BX ;Directory size is DIRMAX+1 SHL BX,1 ;Double directory size MOV DX,(Offset ORDER) ;To get size of order table ADD BX,DX ;Allocate order table MOV Word Ptr TBLOC,BX ;Name table begins where order table ends MOV Word Ptr NEXTT,BX XCHG BX,DX MOV BX,Word Ptr .BDOS+1 ;Make sure we have room to continue MOV AL,DL SUB AL,BL MOV AL,DH SBB AL,BH JNAE L_14 JMP OUTMEM L_14: 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 ; 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_16 JMP SPRINT ;If no more, sort & print what we have L_16: ; 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) MOV AL,Byte Ptr SOPFLG ;Did user request SYS files? OR AL,AL JZ SYSFOK 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] MOV M,AL ;Store in table INC DX INC BX DEC CH ;More? JNZ TMOVE INC DX INC DX ;Point to sector count MOV SI,DX ;Get it MOV AL,[SI] MOV M,AL ;Store in table INC BX MOV Word Ptr NEXTT,BX ;Save updated table addr XCHG BX,DX MOV BX,Word Ptr COUNT ;Bump the # of matches made INC BX MOV Word Ptr COUNT,BX MOV BX,13 ;Size of next entry ADD BX,DX XCHG BX,DX ;Future NEXTT is in DE MOV BX,Word Ptr .BDOS+1 ;Pick up TPA end MOV AL,DL SUB AL,BL ;Compare NEXTT-TPA end MOV AL,DH SBB AL,BH JNB L_17 JMP MORDIR ;If TPA END > NEXTT then loop back for more L_17: OUTMEM: CALL ERXIT ;Exit if directory too large DB 'Memor','y',0 ; 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_18 JMP PRTOTL ;Exit if no files found L_18: 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_19 JMP DONE ;Yes, so skip sort L_19: ; This sort routine is adapted from SOFTWARE TOOLS ; by Kernigan and Plaugher. SORT: MOV BX,Word Ptr SCOUNT ;Number of entries L0: 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_20 JMP DONE ;Then none left L_20: MOV AL,BL ;Make gap odd OR AL,01 MOV BL,AL MOV Word Ptr GAP,BX INC BX ;I=GAP+1 SL2: 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 SL3: MOV Word Ptr J,BX XCHG BX,DX MOV BX,Word Ptr GAP ;JIG=J+GAP LAHF ADD BX,DX SAHF MOV Word Ptr JIG,BX CALL COMPARE ;Compare (J) and (JIG) JNS SL5 ;If A(J)<=A(JIG) MOV BX,Word Ptr J XCHG BX,DX MOV BX,Word Ptr JIG CALL SWAP ;Exchange A(J) and A(JIG) 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 SL5 ;If J>0 GOTO SL3 OR AL,BL ;Check for zero JZ SL5 JMPS SL3 SL5: 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 SL2 SUB AL,BL MOV AL,DH SBB AL,BH JS L_21 JMP SL2 L_21: MOV BX,Word Ptr GAP JMP L0 ; Sort is all done - print entries DONE: ;If output option wanted, prepare file MOV AL,Byte Ptr FOPFLG OR AL,AL JZ L_22 JMP NOOUT ;If file output, fall through with A=0 L_22: ; 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. ; IF AOPT MOV BX,(Offset OPNFLG) ;point to output file open flag CMP AL,M ;a=0, set z if opnflg=0 also JZ L_23 JMP NOOUT ;if opnflg not zero, skip open L_23: DEC M ;else, make opnflg not zero and open ; ENDIF ;Balance AOPT ; First pass on file append - prepare SD.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_24 JMP NOOUT ;Continue if open successful L_24: ; If make or open fails, declare error OPNERR: CALL ERXIT DB 'Ope','n',0 WRTERR: CALL ERXIT DB 'Writ','e',0 ; 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',0 ; 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 SD.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 ;Balance FOPT NOOUT: MOV BX,Word Ptr COUNT MOV Word Ptr LCOUNT,BX MOV BX,0 MOV Word Ptr LBTOTL,BX MOV Word Ptr LMTOTL,BX MOV BX,(Offset ORDER) ;Initialize order table pointer MOV Word Ptr NEXTL,BX MOV Word Ptr NEXTT,BX ;Print "Driectory for... XXX: MOV DX,(Offset DIRMS1) ;at start of each section CALL PRINT CALL PRDIR ;Print " Drive, x User, y" MOV AL,Byte Ptr LINCNT INC AL ;Bump line count MOV Byte Ptr LINCNT,AL 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 INC BX MOV DH,M ;Get high order address INC BX 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 TTYPE 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 TTYPE ; 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_25 JMP PRTOTL ;If no more files, exit to summary output L_25: ; 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_26 CALL FENCE ;If room left, output the fence character L_26: POP AX XCHG AL,AH SAHF JZ L_27 JMP ENTRY ;.. and go output another file L_27: ; Current line full, start a new one. NEWLIN: MOV AL,Byte Ptr MNPL MOV CL,AL ;Reset names per line counter CALL CRLF ;Space down to next line MOV AL,3 ;If printing less than 4 wide CMP AL,CL JB NWLA MOV AL,Byte Ptr .FCB ;.. precede new line with drive name ADD AL,'A'-1 CALL TTYPE ;If reporting user numbers CALL TYPUSR ;Balance REPUSR MOV AL,':' ;Tag header with a colon and a space CALL FPAD ;..and exit back to ENTRY NWLA: JMP ENTRY ;Go back and output another file ; 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 TTYPE 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_29 JMP TTYPE ;Print digit L_29: MOV AL,Byte Ptr SUPSPC ;Get space suppression flag OR AL,AL ;See if printing file totals JNZ L_30 RET ;Yes, don't give leading spaces L_30: JMP SPACE ;Leading zero...print space DIGNZ: MOV Byte Ptr LZFLG,AL ;Set leading zero flag so next zero prints JMP TTYPE ;And print digit ; Show total space and files used PRTOTL: MOV AL,Byte Ptr LOPFLG OR AL,AL JNZ L_31 MOV BX,Word Ptr TOTFIL ;How many files did we match? MOV AL,BH OR AL,BL JZ L_32 CALL PRTLMEM ;Skip the Library check if we didn't find any L_32: L_31: PRTOT1 EQU (Offset $) 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 CALL CRLF CALL PRDIR ;Print " Drive, x User, y" NOUSER: 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. 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_33 JMP SETTBL ;Continue if more user areas to go L_33: ;AOPT ;If multi-disk option enabled MOV AL,Byte Ptr BASUSR ;Reset base user number for the MOV M,AL ;..next directory search ;Balance DOPT and AOPT ; We've finished all of our outputting. Flush the remainder of the ; output buffer and close the file before going to exit routine. GOCLZ: ; LDA FOPFLG ;Is file output mode active? ; ORA A ; JNZ NXTDSK ;If not, move to next drive, otherwise .. ; STA PASS1 ;Make PASS1=0 to force reopen on next drive 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 sd.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 sd.dir if buffer is empty JZ FLUSH ;write last record to sd.dir if buffer full MOV BX,Word Ptr BUFPNT ;OTHERWISE, PAD UNUSED BUFFER WITH CTRL-ZS PUTAGN: MOV M,'Z'-40H INC BX DEC AL JNZ PUTAGN ;CONTINUE PADDING TIL BUFFER FILLED OUT ; MVI A,128 ; STA BUFCNT ;Force buffer to empty status FLUSH: MOV DX,(Offset OUTFCB) ;FLUSH THE LAST OUTPUT BUFFER MOV CL,WRITE CALL CPM OR AL,AL JZ L_34 JMP WRTERR L_34: CLOZE: MOV DX,(Offset OUTFCB) ;CLOSE THE OUTPUT FILE MOV CL,CLOSE CALL CPM ;BALANCE FOPT ; 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 MOV BX,(Offset FOPFLG) DEC M PUSH BX 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 POP BX INC M NDSK: ;If multi-disk option enabled MOV AL,Byte Ptr DOPFLG ;If multi-disk not selected - skip next OR AL,AL JNZ NPRT CALL CKABRT ;Check for user abort first MOV AL,STSKIP ;Skip if STart SKIP MOV BX,FCB ;Bump directory FCB drive code INC M CMP AL,M JNZ NEXT1 MOV AL,SKIPTO ;If STSKIP then SKIPTO MOV M,AL NEXT1: MOV AL,(Offset HIDRV)-(Offset LODRV) ;Get maximum drive code to search CMP AL,M ;Does next disk exceed maximum? JNAE L_35 JMP NOOPT ;Search next disk if not L_35: ;Balance DOPT NPRT: ;if no printer, fall through to EXIT ;now, check if printer is in use MOV AL,Byte Ptr POPFLG OR AL,AL ;printer active? JZ L_36 JMP EXIT ;no, just exit... L_36: MOV CL,5 MOV DL,13 ;print a CRLF CALL CPM MOV DL,10 ;line feed CALL CPM ;POPT JMP EXIT ;All done - exit to CCP ; Print "Directory for.... PRDIR: MOV DX,(Offset TOTMS1) ;Print [CR,LF] " Drive " CALL PRINT MOV AL,Byte Ptr .FCB ADD AL,'A'-1 CALL TTYPE ;Output the drive code MOV DX,(Offset TOTMS2) ;Print ", USER " CALL PRINT CALL TYPUSR ;Output the user number RET ; 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 TTYPE MOV AL,Byte Ptr NEWUSR ;Print low digit of user no. SUB AL,10 DUX: ADD AL,'0' JMPS TTYPE ; Force new line on video and check for page pause CRLF: MOV AL,0DH ;Send CR CALL TTYPE MOV AL,0AH ;Send LF JMPS TTYPE ;Exit to caller from TTYPE ; Separate the directory output on a line with a space, the delimiter, ; followed by another space. FENCE: CALL SPACE MOV AL,Byte Ptr MDELIM ;Fence character FPAD: CALL TTYPE ;Print it, fall into space SPACE: MOV AL,' ' ;Fall through to TTYPE ; Output character in A to console, and optionally to printer and/or ; the output file. TTYPE: PUSH CX PUSH DX PUSH BX PUSH AX ;Save the character to output CALL TYPE2 ;Send it to console POP AX ;Restore the output character AND AL,7FH ;Strip parity bit on character ; Test file output mode and skip to page pause test if not active. 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 CALL SETFOP ;Set the DMA address 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_37 JMP WRTERR ;Take write error exit if disk full or R/O L_37: 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 ;Balance FOPT ;If printer option AND AL,7FH ;Strip parity bit on character MOV DL,AL ;Setup list output call MOV CL,5 MOV AL,Byte Ptr POPFLG ;Test printer flag OR AL,AL JNZ L_38 CALL CPM ;Print character if flag true L_38: MOV AL,DL ;Recall character CMP AL,0AH ;Do we have a LF? JNZ TYPRET ;Exit if not MOV AL,Byte Ptr NOPFLG ;Is the page pause function disabled? OR AL,AL JZ TYPRET ;Exit if so MOV AL,Byte Ptr LINCNT ;Get line count INC AL ;Bump it MOV BX,(Offset MLPS) CMP AL,M ;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_39 JMP EXIT ;Abort on CTRL-C L_39: XOR AL,AL ;Reset line count NOTEOS: MOV Byte Ptr LINCNT,AL ;Save new line count TYPRET: POP BX ;Exit from TTYPE POP DX POP CX RET ; Output character TYPE2: IF REVIDEO TEST AL,80H ;is high bit set? JZ TYPE99 ;jmp if not PUSH AX ;save char. MOV AL,Byte Ptr MLEADIN ;Send ESC char first CALL TYPE99 MOV AL,Byte Ptr MINTOREV ;INTO REV VIDEO CALL TYPE99 MOV AL,Byte Ptr MINTOREV+1 CALL TYPE99 POP AX ;Retrieve character AND AL,7FH CALL TYPE99 MOV AL,Byte Ptr MLEADIN ;Send ESC first CALL TYPE99 MOV AL,Byte Ptr MOUTAREV ;The out of reverse video character CALL TYPE99 MOV AL,Byte Ptr MOUTAREV+1 ;The out of reverse video character TYPE99: ENDIF ;Balance REVIDEO MOV DL,AL ;Get character into BDOS entry register MOV CL,WRCHR INT 224 ;Call CONOUT via the BDOS RET ;Balance DIRCON ; Print a string at HL of length B TYPEIT: MOV AL,M CALL TTYPE LAHF INC BX SAHF DEC CH JNZ TYPEIT RET ; Print string terminated with binary 0 on console. PRINT: MOV SI,DX MOV AL,[SI] CMP AL,0 JNZ L_40 RET L_40: CALL TTYPE INC DX JMPS PRINT ; Fetch character from console (without echo) CINPUT: MOV DL,0FFH MOV CL,6 INT 224 CMP AL,0 JZ CINPUT ;wait for char. 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 DL,0FEH MOV CL,6 ;Check status of keyboard INT 224 ;Any key pressed? OR AL,AL JNZ L_41 RET ;No, return to caller L_41: CALL CINPUT ;Get character CMP AL,'C'-40H ;CTRL-C? JNZ L_42 JMP EXIT ;If CTRL-C then quit L_42: CMP AL,'S'-40H ;CTRL-S? JZ L_43 RET ;No, return to caller L_43: CALL CINPUT ;Yes, wait for another char. CMP AL,'C'-40H ;Might be CTRL-C JNZ L_44 JMP EXIT ;Exit if CTRL-C, else fall thru and continue L_44: RET ; Kludge to allow call to address in HL GOHL: JMP BX ; Entry to BDOS saving all extended registers CPM: PUSH CX PUSH DX PUSH BX INT 224 MOV CH,AL ; Save return code MOV AL,Byte Ptr VERFLG ; Is this 3.0 or higher? CMP AL,30H MOV AL,CH JB CPM20 ; No, exit normally CMP AL,0FFH ; It is 3.0 - was return code FF? JNZ CPM20 ; No, exit normally MOV AL,BH ; 3.0 and A=FF - check for extended error code OR AL,AL JZ L_45 JMP DSKERR ; Trap out if we got a physical error L_45: MOV AL,CH ; Else continue normally CPM20: POP BX POP DX POP CX RET ; For file output mode, return to old user area and set DMA for ; the file output buffer. SETFOP: MOV AL,Byte Ptr OLDUSR ;Get user number at startup MOV DL,AL MOV CL,CURUSR CALL CPM ;Reset the old user number MOV DX,(Offset OUTBUF) ;Move DMA from search buffer into the JMPS SET2 ;..output buffer RET ; Move disk buffer DMA to default buffer for directory search operations ; and BDOS media change routines (necessary for pre-CP/M 2 systems while ; in file output mode with an active buffer). 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 AL,M AND AL,7FH MOV DH,AL MOV SI,CX MOV AL,[SI] AND AL,7FH CMP AL,DH 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 - modified for filetype sort 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 ;If T option true MOV AL,Byte Ptr TOPFLG ;Get option flag OR AL,AL ;test for zero JNZ CMPFNFT ; ; Compare by Filename, Filetype, and Extent - in that order ; PUSH BX PUSH CX MOV DX,8 ;Pt to FT CALL ADDEM MOV DL,3 ; 3 bytes in type CALL CMPLPE ;compare FT'S POP CX POP BX JZ L_47 RET ;Continue if match L_47: PUSH BX PUSH CX MOV DL,8 ;8 bytes in name CALL CMPLPE ;Compare FN'S POP CX POP BX JZ L_48 RET ;Continue if match L_48: MOV DX,11 ;Pt to EXT CALL ADDEM MOV SI,CX ;Compare EXT'S MOV AL,[SI] CMP AL,M RET ADDEM: PUSH BX ;Adds DE to HL & BC MOV BH,CH MOV BL,CL LAHF ADD BX,DX SAHF MOV CH,BH MOV CL,BL POP BX LAHF ADD BX,DX RCR SI,1 SAHF RCL SI,1 RET ; ; Compare by Filename, Filetype, Extent - in that order ; CMPFNFT: MOV DL,12 ;Compare FN, FT, EX ; and fall into routine CMPLPE: MOV AL,M AND AL,7FH MOV DH,AL MOV SI,CX MOV AL,[SI] AND AL,7FH CMP AL,DH LAHF INC CX SAHF LAHF INC BX SAHF JZ L_49 RET L_49: DEC DL JNZ CMPLPE RET HELP: INC BX ;Make sure there is a CMP AL,M ;second "/" else return LAHF ;since some people use / DEC BX SAHF JZ L_50 RET ;for a file name L_50: HELP1: MOV DX,(Offset HLPMES) ;Print a short HELP message CALL PRINT CALL CRLF JMPS EXIT ; Error exit ERXIT: MOV AL,-1 MOV Byte Ptr FOPFLG,AL ;Disable file output mode on error 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_51 CALL CPM ;Gobble up char L_51: ;Restore BDOS intercept vectors MOV AL,Byte Ptr VERFLG ;Or error mode, depending on version CMP AL,30H JB EXIT0 MOV CL,45 MOV DL,0 ;Set error mode back to default CALL CPM JMPS EXIT1 EXIT0: MOV AL,Byte Ptr DOPFLG ;..if they were swapped OR AL,AL JNZ L_52 CALL SWAPEM L_52: EXIT1: 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_53 CALL CPM L_53: ;Balance ROPT MOV CL,0 ;EXIT PROGRAM MOV DL,0 INT 224 ; Trap BDOS select and sector error vectors to our own intercept ; routine so we can catch a reference to an illegal drive. SWAPEM: MOV AL,Byte Ptr VERFLG ;Check version CMP AL,30H ;see if error mode call is available JB SWAP20 ;If not, use BDOS error vectors MOV CL,45 MOV DL,0FFH ;Use Set Error Mode call CALL CPM ;set "return code only" mode SWAP20: RET ;this doesn't make sense in CP/M-86 ;...version 1.1 ; Check CP/M version number. Return carry flag set if pre-CP/M 2. ; If CP/M 2 or later or MP/M (any version), return carry clear. CKVER: MOV AL,Byte Ptr VERFLG CMP AL,20H RET ; Recovery point from intercepted BDOS select and bad sector errors. DSKERR: JMP EXIT ;..and exit back to CCP ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; ; SUBROUTINES to read library file directory ; ;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; PRTLMEM:MOV BX,(Offset ORDER) ;Initialize order table pointer MOV Word Ptr NEXTL,BX XOR AL,AL MOV Byte Ptr LNCNT,AL ENTRYL: MOV BX,Word Ptr LCOUNT MOV AL,BH ;Is this the last file? OR AL,BL JZ LBEXIT ;If COUNT=0, last file so RETURN LAHF ;Dock file count DEC BX SAHF MOV Word Ptr LCOUNT,BX PUSH CX CALL CKABRT ;Check for abort code from keyboard MOV BX,Word Ptr NEXTL MOV AL,11 CALL COMPR ;Does this entry match next one? POP CX JNZ LBRTST ;No, print it INC BX INC BX ;Skip since highest extent comes last in list MOV Word Ptr NEXTL,BX JMPS ENTRYL ;Loop back for next lowest extent ; ; Exit Library member printing ; LBEXIT: MOV BX,Word Ptr LMTOTL MOV AL,BH OR AL,BL JNZ L_54 RET L_54: PUSH BX ;Save Member count XOR AL,AL ;Get a zero to... MOV Byte Ptr SUPSPC,AL ;Suppress leading spaces in totals MOV BX,(Offset MNPL) MOV AL,Byte Ptr LNCNT CMP AL,M JZ L_55 CALL CRLF L_55: MOV DX,(Offset CONTM) ;Print "There are " CALL PRINT POP BX ;GET TOTAL MEMBER COUNT BACK CALL DECPRT MOV DX,(Offset MFILES) ;PRINT "Members in " CALL PRINT MOV BX,Word Ptr LBTOTL CALL DECPRT MOV DX,(Offset LIBR) CALL PRINT RET ; Valid entry obtained - spit it out. LBRTST: MOV BX,Word Ptr NEXTL ;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 NEXTL,BX ;Save updated table pointer MOV BX,8 LAHF ADD BX,DX SAHF CALL CKLBR JZ L_56 JMP ENTRYL L_56: PUSH DX MOV BX,(Offset MNPL) MOV AL,Byte Ptr LNCNT CMP AL,M JZ L_57 CALL CRLF L_57: MOV BX,(Offset LFMSEP) MOV CH,31 CALL TYPEIT POP BX PUSH BX MOV CH,8 ;File name length CALL TYPEIT MOV AL,'.' ;Period after FN CALL TTYPE MOV CH,3 ;Display 3 characters of filetype CALL TYPEIT CALL CRLF POP BX ; ; Saves the library file name into LBRFCB ; MOV AL,Byte Ptr .FCB MOV DX,(Offset LBRFCB) ;TO MOV SI,DX MOV [SI],AL LAHF INC DX SAHF MOV CH,11 ;LEN CALL MOVE ;DO THE MOVE XCHG BX,DX MOV CH,25 CLMFCB: MOV M,0 LAHF INC BX SAHF DEC CH JNZ CLMFCB CALL SETLDMA MOV DX,(Offset LBRFCB) ;POINT TO FILE MOV CL,OPEN ;GET FUNCTION CALL CPM ;OPEN IT MOV CL,READ MOV DX,(Offset LBRFCB) CALL CPM CALL SETFOP MOV BX,(Offset LBBUF) MOV AL,M OR AL,AL JZ CKLDIR ;check directory present? BADLBR: MOV BX,(Offset NLBRF) MOV CH,25 CALL TYPEIT LMLEXI: CALL LBCLOSE JMP ENTRYL L_58 EQU $ DSEG ORG Offset L_58 NLBRF DB '++Not a LIBRARY file.++',13,10 L_59 EQU $ CSEG ORG Offset L_59 ; ; Close the library file ; LBCLOSE:MOV DX,(Offset LBRFCB) MOV CL,CLOSE CALL CPM RET ; ; SETLDMA - Set the Library file DMA adderss ; SETLDMA: 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 MOV DX,(Offset LBBUF) MOV CL,SETDMA CALL CPM RET ; ; CKLDIR - check to see if there indeed is a LBR file ; directory and barf if not ; CKLDIR: MOV CH,11 ;len of file name MOV AL,' ' ;space INC BX CKDLP: CMP AL,M JNZ BADLBR DEC CH LAHF INC BX SAHF JNZ CKDLP ; ; The first entry in the LBR directory is indeed blank. ; Now see if the directory size is >0 ; MOV DL,M ;file starting location low INC BX ;must be zero here MOV AL,M ;file starting location high OR AL,DL ;must be zero here also JNZ BADLBR INC BX MOV DL,M ;get library size low INC BX ;point to library size high MOV DH,M ;get library size high MOV AL,DH OR AL,DL ;library must have some size JNZ L_61 JMP BADLBR L_61: DEC DX XCHG BX,DX MOV Word Ptr SLFILE,BX MOV BX,Word Ptr LBTOTL INC BX MOV Word Ptr LBTOTL,BX MOV AL,Byte Ptr MNPL MOV Byte Ptr LNCNT,AL ;Reset names per line counter MOV CH,3 MOV BX,17 ADD BX,DX MOV AL,Byte Ptr MNPL MOV Byte Ptr LNCNT,AL ;Reset names per line counter JMPS LMTEST LFMLOP: MOV BX,Word Ptr SLFILE ;GET MOV AL,BL OR AL,BH JNZ L_62 JMP LMLEXI L_62: LAHF DEC BX SAHF MOV Word Ptr SLFILE,BX CALL SETLDMA MOV CL,READ MOV DX,(Offset LBRFCB) CALL CPM CALL SETFOP MOV CH,4 ;GET FILE COUNT PER SECTOR MOV BX,(Offset LBBUF) ;GET BUFFER STARTING ADDRESS LMTEST: MOV AL,M ;Get member open flag OR AL,AL ;TEST FOR OPEN JZ PRMNAM LMTESA: MOV DX,32 ;Member not open get offset ADD BX,DX ;to next and add it in. DEC CH ;is buffer empty ? JNZ LMTEST ;No so test next entry JMPS LFMLOP ;Yes get next buffer... PRMNAM: PUSH BX ;Print member NAME and SIZE PUSH CX CALL CKABRT ;Check for abort code from keyboard MOV BX,(Offset LNCNT) MOV AL,Byte Ptr MNPL CMP AL,M JNZ PRMNA1 MOV AL,3 ;If printing less than 4 wide CMP AL,M JB PRMNA1 MOV AL,Byte Ptr LBRFCB ;.. precede new line with drive name ADD AL,'A'-1 CALL TTYPE ;If reporting user numbers CALL TYPUSR MOV AL,':' ;Tag header with a colon and a space CALL FPAD ;..and exit back to ENTRY PRMNA1: POP CX POP BX PUSH BX PUSH CX LAHF INC BX SAHF MOV CH,8 ;File name length CALL TYPEIT MOV AL,'.' ;Period after FN CALL TTYPE MOV CH,3 ;Display 3 characters of filetype CALL TYPEIT LAHF INC BX SAHF LAHF INC BX SAHF MOV DL,M LAHF INC BX SAHF MOV DH,M XCHG BX,DX ; If report size enabled, output the size of the individual file. ;If file size report wanted CALL DECPRT ;..go print it MOV AL,'s' ;..and follow with S size CALL TTYPE ; At least one more file to output - can we put it on the current line? MOV BX,Word Ptr LMTOTL LAHF INC BX SAHF MOV Word Ptr LMTOTL,BX MOV AL,Byte Ptr LNCNT DEC AL MOV Byte Ptr LNCNT,AL LAHF XCHG AL,AH PUSH AX JZ L_64 CALL FENCE ;If room left, output the fence character L_64: POP AX XCHG AL,AH SAHF POP CX POP BX JZ L_65 JMP LMTESA ;.. and go output another file L_65: ; Current line full, start a new one. MOV AL,Byte Ptr MNPL MOV Byte Ptr LNCNT,AL ;Reset names per line counter CALL CRLF ;Space down to next line JMP LMTESA ; ; Move characters from 'HL' to 'DE' length in 'B' ; MOVE: MOV AL,M ;GET A CHAR MOV SI,DX ;STORE IT MOV [SI],AL LAHF ;TO NEXT "FROM" INC BX SAHF LAHF ;TO NEXT "TO" INC DX SAHF DEC CH ;MORE? JNZ MOVE ; YES, LOOP RET ; NO, RETURN ; ; cklbr -- test file extent for LBR ; CKLBR: PUSH BX PUSH DX PUSH CX XCHG BX,DX MOV BX,(Offset LBRTYP) MOV CL,3 CKLBL: MOV SI,DX MOV AL,[SI] AND AL,7FH CMP AL,M JNZ CKLBX LAHF INC BX SAHF LAHF INC DX SAHF DEC CL JNZ CKLBL CKLBX: POP CX POP DX POP BX RET L_66 EQU $ DSEG ORG Offset L_66 ; LFMSEP DB ' Library file members for : ' LBRTYP DB 'LBR' L_67 EQU $ CSEG ORG Offset L_67 ;LOPT ;********************** ; * ; End of Program Code * ; * ;********************** ; Initialized data area DREMSG DB 'Driv','e',0 EOSMSG DB ' [More]',0DH,'$' ERRMS1 DB ' ' ERRMS2 DB 'Erro','r',0 ERRTAG DB ' -','>',0 NOFMS1 DB 0DH,0AH,'No file on ' NOFMS2 DB ' -',' ',0 DIRMS1 DB 0DH,0AH,' Directory fo','r',0 TOTMS1 DB ' Drive',' ',0 TOTMS2 DB ', user',' ',0 TOTMS3 DB ' contains',' ',0 TOTMS4 DB 'k in',' ',0 TOTMS5 DB ' files with',' ',0 TOTMS6 DB 'k free','.',0DH,0AH,0 ;,0DH,0AH ,0 USRMSG DB 'User ','#',0 CONTM DB ' There are',' ',0 MFILES DB ' Member Files in',' ',0 LIBR DB ' Library(s)','.',0 FNDFLG DB 0 ;Flag whether any files matched LINCNT DB 0 ;Count of lines printed on screen ; 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. Note also that there ; are only 16 user areas available under CP/M 2, so the highest ; legal user area you can specify is 15 (range 0-15 = 16 areas). ; The program will convert anything over 15 into mod 15. ; NOTE: If some drives in active table are unused then skip these ; drives by using STSKIP and SKIPTO equates. LODRV EQU (Offset $) ;Mark beginning of drive/user table DB 15 ;Maximum user area for Drive A DB 15 ; " " " " " B DB 00 ; " " " " " C DB 00 ; " " " " " D DB 00 ; " " " " " E DB 00 ; " " " " " F DB 00 ; " " " " " G DB 00 ; " " " " " H DB 00 ; " " " " " I DB 00 ; " " " " " J DB 00 ; " " " " " K DB 00 ; " " " " " 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, SD will flag it as unrecognized. OTBL EQU (Offset $) ;Mark start of option table ;All users-option flag AOPFLG DB 'A' ;Multi-disk-option flag DOPFLG DB 'D' ;File-output-option flag FOPFLG DB 'F' ;Display Library members flag LOPFLG DB 'L' ;No page-pause option flag NOPFLG DB 'N' ;Printer option flag POPFLG DB 'P' ;Reset option flag ROPFLG DB 'R' ;System file option flag SOPFLG DB 'S' TOPFLG DB 'T' ;Type sort option flag ;Display version number flag VOPFLG DB 'V' OEND EQU (Offset $) ;Mark end of option table ; End of option lookup table ; IF AOPT ;PASS1: DB 0 ;First pass flag for all user file output ; ENDIF VECTBL DW (Offset DSKERR) ;BDOS SECTOR ERROR INTERCEPT VECTOR DW (Offset DSKERR) ;BDOS SELECT ERROR INTERCEPT VECTOR 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,'SD DIR' DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 OUTBUF RS 128 ;OUTPUT FILE BUFFER GOTUSR DB 0 ; Uninitialized data area 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 DIRMAX 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 ; ditto J RS 2 ; ditto JIG RS 2 ; ditto LZFLG RS 1 ;Zero 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 VERFLG RS 1 ;CP/M version number (0=pre-CP/M 2) LMTOTL DW 0 LBTOTL DW 0 LNCNT DB 0 LCOUNT DW 0 NEXTL DW 0 SLFILE DW 0 LBRFCB RS 36 LBBUF RS 80H ORDER EQU (Offset $) ;Order table starts here END