; MCAT v4.5 DISK CATALOG PROGRAM 08/29/84 - (CP/M 3.0 COMPATIBLE) ; ; COPYRIGHTED 1984 BY IRV HOFF ; ; This catalog program updates the 'MAST.CAT' file, which was previously ; started by the user with his editor. A Companion program named 'XCAT' ; can then be used to make a cross-reference listing which can generate ; a disk file called 'MAST.LST' or can print a paginated hard copy using ; either fanfold or roll paper. An additional file called 'FIND.COM' can ; be used on either 'MAST.LST' or 'MAST.CAT' programs to quickly locate ; any file names. Example: ; ; A>FIND MAST.LST MCAT ; ; would find any/all files in 'MAST.LST' containing the letters: MCAT. ; 'MAST.CAT' must have at least one 'IGNORE' file. Each disk to be pro- ; cessed must have an "ID number" different from any other disk. This ; number must go in the "file type" area. Example: ; ; A>SAVE 0 -.123 ; ; ; NOTE: FOR CP/M 3.0 USERS - the SAVE command is not ; used in the same way as in CP/M 2.2 and before. ; You can not save a file with 0 bytes. A spe- ; cial program called NULL.ASM was written by ; Guy Gamble that will allow easy naming of CP/M ; 3.0 disks. This is a necessary utility for ; using this program with CP/M 3.0. ; ; ; The '-' identifies this as a special number to the MCAT program. A ; typical 'MAST.CAT' file might have a few names in the "IGNORE" list ; (those files which are plentiful enough you do not need to know their ; quantity or location). Example: ; ; (CONFIG.COM (note opening left parenthesis) ; LOAD.COM ; PIP.COM ; STAT.COM) (note closing right parenthesis, ; which must be followed with a RET) ; ; If all files are to be cataloged, this could be used: ; ; () followed with a RET ; ; ; HOW TO USE: ; ---------- ; ; MCAT for one drive systems, or cataloging the main disk ; itself. ; ; MCAT B: main disk in A: with disks to be cataloged put in B: ; ; MCAT B: A: main disk in B: with disks to be cataloged put in A: ; ; - Notes by Irv Hoff W6FFC ; ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; ; 08/29/84 Fixed NEXTS: so large disks with more than 256 names will ; v4.5 be able to use the program consistently. Restored program ; to my original format. - Irv Hoff ; ; 07/04/84 Added code to ignore CP/ 3.0 time and date stamping. ; v4.4 - Guy Gamble ; ; 03/29/84 Restored to work with all computers, not just the Osborne ; v4.3 Executive. Other changes. - Irv Hoff ; ; 03/19/84 Added CP/M 3.0 free disk space routine from MDM7. ; v4.2 - Guy Gamble ; ; 01/21/84 Added suggestion by Bill Boulton for large directories. ; v4.1 - Irv Hoff ; ; 11/20/83 Released to the public domain. ; v4.0 - Irv Hoff ; ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; ; ORG 0100H ;normal program start ; ; JMP START ;program start ; ; YES: EQU 0FFH NO: EQU 0 ; ; FILERR: SET EXIT ;exit if errors ; BELL: EQU 07H ;bell character CR: EQU 0DH ;carriage return character EOF: EQU 1AH ;end of file marker LF: EQU 0AH ;line feed character ; ; ;----------------------------------------------------------------------- ; ; SEE COMMENTS IN HELP MESSAGE AT END OF FILE ; ; USER MODIFIABLE EQUATES: ; ;----------------------------------------------------------------------- ; ; Clear screen - If set YES, set the CLS equate to the character your ; CRT uses. (Many use CTL-Z) ; CLSCRN: EQU NO ;yes = clear screen CLS EQU 'Z'-40H ;change to your 'clear screen' character ; ; if not CTL-Z ; ;----------------------------------------------------------------------- ; ; Space for the IGNORE buffer (for file names not to be cataloged.) Few ; lists use more than 10-12 names. Each name stores as 14 characters. ; Change the first number below if you need more than 36 file names. ; IGNSIZE:EQU (36*14)+2 ;buffer size for ignored names ; ;----------------------------------------------------------------------- ; ; Some CP/M systems offer only 64 file names per disk. Some offer up to ; 1024. At 14/16 bytes per file name, this is the difference between ; 896 and 13,336 Bytes. Few would wish to discard that much RAM unless ; required. Most DD, DS 8" systems only offer 128 file names on both ; sides combined. Change the following number for your system: ; NAMES: DW (256*NAMSIZE) ;buffer size for directory names ; ; NOTE: If you DO change this to another value, you may ; want to change the similar number in the "Help" ; area, opposite label: NAMES$NUMBER: ; ;----------------------------------------------------------------------- ; ; Set USER to 'NO' if you want a normal display of each file. ; Set USER to 'YES' if you want to include the user area for each file. ; USER: EQU NO ;NO=user areas not shown, YES=shown ; ;----------------------------------------------------------------------- ; ; ;*********************************************************************** ; ; ; ; ; ..... PROGRAM STARTS HERE ..... ; ; ; ; ; ;*********************************************************************** ; ; START: LXI H,0 DAD SP SHLD STACK LXI SP,STACK CALL ILPRT ; IF CLSCRN DB CLS ENDIF ;CLSCRN ; IF NOT CLSCRN DB CR,LF ENDIF ;NOT CLSCRN ; START1: DB 'MCAT CATALOG PGM v4.5 - 08/29/84',0 CALL ILPRT ; IF USER DB CR,LF,'(user areas included)' ENDIF ;USER ; DB CR,LF,'For help, reboot then type: MCAT ?',0 LDA FCB+1 CPI '?' ;help wanted? ; P1: JZ HELP ;jump if so LXI H,EXIT ;disable 'HELP' code for subsequent runs SHLD P1+1 ; as it will be ovewritten by buffers ; ; at end ; ; Finds amount of space available for buffers, divides into 2 equal ; parts for the in/out buffers ; LXI D,NABUF ;start of name buffer LHLD NAMES ;get length of buffer DAD D ;start of main buffer INX H ;one extra for isolation SHLD MASTINADR ;main buffer start address SHLD MASTINPTR ;main buffer start pointers SHLD NAMEND ;names buffer stopping address XCHG ;buffer address to 'DE' LDA BDOS+2 ;get 'BDOS' entry point, even page MOV H,A ;store in 'H' XRA A ;nulls the 'A' reg. ; ; ; Calculate the difference to get free space ; SUB E MOV L,A MOV A,H SBB D ANI 0F8H MOV H,A ; ; ; Now have the difference in 'HL' pair, so divide into 2 equal parts ; XRA A ;clear carry if set MOV A,H RAR MOV H,A XRA A MOV L,A ;make buffers full 256-byte pages ; ; ; Now store the buffer length at appropriate places ; SHLD BUFFERSIZE SHLD MASTINLEN SHLD MASTOUTLEN ; ; ; Set up the output buffer address ; DAD D ;add end of program to buffer length SHLD MASTOUTADR SHLD MASTOUTPTR MVI C,CPMVER ;if ALLUSR enabled, see that we are CALL BDOS ; running under CP/M 2.x or later; if MOV A,H ; not, do an error exit ORA L JZ BADVERS ; ; ; All error checking done, start actual program code ; NEW1: MVI C,CURDSK ;which disk is logged CALL BDOS ;disk in reg 'A' LXI H,CDRIVE ;assume the catalog to be on this drive MOV M,A INX H ;point to MAST.CAT drive MOV M,A ;assume to be on this drive, too LDA FCB+16 ;drive for MAST.CAT specified? ORA A JZ NEW2 ;nope, stick with logged drive DCR A ;else make into drive select code MOV M,A ;and stash for use later (in mdrive) ; NEW2: LDA FCB ;drive for disk to be catalog specified? ORA A JZ NEW4 ;nope, stick with logged drive DCR A STA CDRIVE ;ditto, above ; NEW4: LDA CDRIVE ;re-get catalog drive CMP M ;same as MAST.CAT drive MVI A,0 ;assume so JZ NEW6 DCR A ;else make non-zero ; NEW6: STA DRIVE JNZ NEW8 ;jump if multi-drive system ; ; ; Come here if instructed to use the same drive for catalog and MAST.CAT ; LDA MDRIVE ADI 'A' ;make drive code ASCII [A..P] STA DR1 ;hot-patch message texts STA CDR1 STA MDR1 CALL DISKA ;tell them to leave a disk in drive A: CALL ILPRT ;tell user about drive usage DB CR,LF,CR,LF DB 'The DISK TO BE CATALOGED and the CATALOG SYSTEM DISK' DB CR,LF DB 'will alternately be placed in Drive ' ; DR1: DB 'A:' ;hot-patched at run-time DB CR,LF,0 JMP AGAIN ;... ; ; DISKA: LDA CDRIVE ;drive A: for catalog disk? ORA A RZ ;done if so LDA MDRIVE ;drive A: for MAST.CAT? ORA A RZ ;done if so CALL ILPRT ;else tell user DB CR,LF DB 'A scratch disk must remain in Drive A:',0 RET ;..... ; ; ; Come here if the disk to be cataloged and MAST.CAT disk are on differ- ; ent drives ; NEW8: LDA CDRIVE ;hot patch message texts with ADI 'A' ;drive to be cataloged... STA CDR1 LDA MDRIVE ;and MAST.CAT drive ADI 'A' STA MDR1 CALL DISKA ;tell them to leave disk in drive A:? CALL ILPRT ;tell user about drive usage DB CR,LF,0 ; AGAIN: LXI SP,STACK ;reset stack each time through LXI H,NABUF ;set up file name buffer SHLD BUFPTR SHLD FREPTR ;initialize 'PHANTOM FILE' pointer LXI H,0 SHLD BLKSFRE ;initialize blocks free LXI H,2020H ;get 2 spaces SHLD COUNT ;blank out entry counter SHLD COUNT+2 ;(needed for multiple passes) MVI A,')' ;fix IGNORE table STA IGNORE XRA A ;get a zero... STA MIEOFLG ;fix end-of-file flags STA NAEOFLG LXI H,1 ;initialize file counter to one, to SHLD FCT ;save 16-bit value ; CATMSG: CALL ILPRT DB CR,LF,'Mount DISK TO BE CATALOGED in Drive ' ; CDR1: DB 'B:' ;hot-patched to show correct drive DB ' Ready? (Y/N): ',0 CALL CRTIN ;get a keyboard character. CPI 'Y' ;process disk if yes JZ CATMS1 CPI CR ;accept 'CR' as yes JZ CATMS1 CPI ' ' ;accept 'SPACE' character as yes JNZ CATMSG ;ask again if none of these CALL SHOW$Y ; CATMS1: CALL CRLF ;acknowledge with a new line ; ; ; The following is necessary with double density systems to allow the ; system to log in a diskette with (possibly) changed disk character- ; istics (density, sector size, etc) ; MVI C,RESET CALL BDOS ;(exit with drive A: selected) ; ; ; Select the disk to be cataloged as either the originally logged drive ; or the drive specified at runtime ; LDA CDRIVE ORA A ;set flags MOV E,A MVI C,SELDSK CNZ BDOS ;do not bother reselecting drive A: ; ; ; Now that the drive to be cataloged has been selected, get the address ; of the disk parameter block for that drive and the extent mask. ; MVI C,DPB CALL BDOS SHLD DPBADDR LXI D,4 DAD D MOV A,M STA EXMASK ; ; ; Make FCB all '?' ; LXI D,AMB LXI H,FCB MVI B,13 CALL MOVER ; ; ; Create a "phantom file" with free disk space ; LXI H,NABUF CALL AAAA DB ' .FRE' ; AAAA: MVI B,NAMSIZE-2 POP D CALL MOVER CALL FRESPC ;get free space on disk to new file LXI H,NABUF+NAMSIZE-2 MVI M,CR ;delimiters INX H MVI M,LF INX H ; ; ; Read the directory entries ; MVI C,SRCHF JMP CALLB ; LOOP: MVI C,SRCHN ; CALLB: CALL BUFLEN ;see if names buffer is long enough PUSH H ;save current buffer address LXI D,FCB CALL BDOS POP H ;get current buffer address back INR A JZ NOMORE ; ; ; Move the name into the buffer ; DCR A ;get back original value ANI 3 PUSH H MOV L,A MVI H,0 DAD H ;x32 DAD H DAD H DAD H DAD H LXI D,TBUF DAD D ; ; ; 'HL' now points to entry (table entries will be 14/16 bytes each) ; XCHG LXI H,12 ;if extent byte in directory entry is DAD D ; larger than the extent mask, the en- LDA EXMASK ; try we are looking at is not the CMP M ; 'PRIMARY' directory entry for this POP H ; file, ignore. JC LOOP LDAX D ;if the file has been erased do not move CPI 0E5H ; it to the buffer (do not include it JZ LOOP ; in the catalog). ; IF USER MOV C,A ;save the user number for this file ENDIF ;USER ; INX D ;skip first byte LDAX D ; replace the '-' token used to mark CPI '-' ; the disk name file with a null. This JNZ TOK ; is for internal use only, and pre- XRA A ; serves the correct sort-order. STAX D ; TOK: MVI B,8 ;number of characters in filename CALL MOVNC ;move, filtering out commas & hi-order MVI M,'.' ; bits. INX H MVI B,3 ;do filetype, too CALL MOVNC ; IF USER MVI M,';' INX H MOV A,C ;get user area number ADI 90H DAA ACI 40H DAA MOV M,A ;set user number INX H ENDIF ;USER ; MVI M,CR ;finish the end of line INX H MVI M,LF INX H ; ; ; Increment file count (16 bit) ; PUSH H LHLD FCT INX H SHLD FCT POP H JMP LOOP ;..... ; ; ; See if buffer is long enough for directory entries ; BUFLEN: PUSH H ;save current address XCHG ;put present address into 'DE' LHLD NAMEND ;get end address of buffer DCX H ;protect next buffer MOV A,L SUB E MOV A,H SBB D POP H ;get current address back JC BUFERR ;if past buffer end, error RET ;get next directory name ;..... ; ; ; If not long enough, display an error message and exit ; BUFERR: POP H ;reset stack "call buflen" MVI C,PRINT LXI D,BUFMSG ;send error message CALL BDOS JMP EXIT ;..... ; ; ; Names buffer error message ; BUFMSG: DB CR,LF,'*** File names buffer not big ' DB 'enough. MAKE LARGER ***',CR,LF DB ' (See MCAT.SET file for instructions.)' DB CR,LF,'$' ;..... ; ; ; No more entries ; NOMORE: MVI M,'Z'-40H ;endflag names buffer ; NEXTS: LHLD FCT ;get file count DCX H SHLD FCT SHLD TFCT ;save count for this pass MOV A,H ORA L ;done all yet? JZ DONE ;jump if thru sorting ; ; ; Else pass thru the buff, sorting it. ; LXI D,NABUF ; COMPAR: LXI H,NAMSIZE DAD D PUSH D PUSH H MVI B,NAMSIZE ;compare length ; CLCLP: LDAX D CMP M JC NEXTC JNZ DIFF ; SAME: INX D INX H DCR B JNZ CLCLP ; NEXTC: POP H POP D XCHG ; NEXTC2: LHLD TFCT ;done all for this pass yet DCX H SHLD TFCT MOV A,H ORA L JNZ COMPAR ;check next 2 if not ; ; ; Completed pass thru buff, do next pass if needed ; JMP NEXTS ;..... ; ; ; Unequal compare ; DIFF: POP H POP D ;get pointers MVI B,NAMSIZE PUSH B ; SWAP: MOV C,M LDAX D MOV M,A MOV A,C STAX D INX D INX H DCR B JNZ SWAP POP B JMP NEXTC2 ;..... ; ; ; Sort all done - verify "flag" file present ; DONE: LDA NABUF ORA A JZ NAMEOK CALL ILPRT DB CR,LF,'++ MISSING "-.nnn" FILE ON DISK.',CR,LF DB ' (Put the volume number file on',CR,LF DB ' the disk, then run it again.)',CR,LF,0 JMP AGAIN ;..... ; ; ; Get disk id number, display ; NAMEOK: LXI D,NABUF+1 LXI H,IDNAME MVI B,11 CALL MOVER LXI D,NABUF+1 LXI H,COUNT1 MVI B,11 CALL MOVER CALL ILPRT ;display disk ID number DB CR,LF,' Processing disk: ',0 CALL ILPRTS ; IDNAME: DB ' . ',CR,LF,0 ; LDA DRIVE ;two drives?? ORA A JNZ OK1 ;yes, proceed ; NAME1: CALL ILPRT ;else ask for system disk DB CR,LF,'Mount CATALOG SYSTEM DISK in Drive ' ; MDR1: DB 'A: Ready? (Y/N): ',0 ;hot patched at runtime CALL CRTIN ;get a keyboard character CPI CR ;accept 'CR' as yes JZ MDR2 CPI 'Y' ;if yes, update MAST.CAT JZ MDR2 CPI ' ' ;accept 'SPACE' character as yes JNZ NAME1 ;otherwise, ask again CALL SHOW$Y ;display a 'Y' for yes ; MDR2: CALL CRLF ; OK1: MVI C,RESET ;reset disk, killing r/o status CALL BDOS ; (and reselecting drive A:) LDA MDRIVE ;then select drive for MAST.CAT ORA A MOV E,A MVI C,SELDSK CNZ BDOS ;do not bother reselecting if drive A: ; ; ; Now update mast.cat and set up the files ; MVI C,ERASE LXI D,FCBBAK ;erase MAST.BAK to save disk space CALL BDOS JMP AAA ;..... ; ; FCBMASTIN: DB 0,'MAST CAT' ; DB 0 DS 20 ; MASTINADR: DS 2 ;input buffer start address ; MASTINLEN: DS 2 ;input buffer length ; MASTINPTR: DS 2 ;input buffer pointer address ; AAA: JMP AAE ;..... ; ; GETMASTIN: LHLD MASTINLEN XCHG LHLD MASTINPTR MOV A,L SUB E MOV A,H SBB D JC AADD LXI H,0 SHLD MASTINPTR ; AAB: XCHG LHLD MASTINLEN MOV A,E SUB L MOV A,D SBB H JNC AAD LHLD MASTINADR DAD D XCHG MVI C,SETDMA CALL BDOS LXI D,FCBMASTIN MVI C,READ CALL BDOS ORA A JNZ AAC LXI D,TBUF LHLD MASTINPTR DAD D SHLD MASTINPTR JMP AAB ;..... ; ; AAC: LHLD MASTINPTR SHLD MASTINLEN ; AAD: LXI D,TBUF MVI C,SETDMA CALL BDOS LXI H,0000H SHLD MASTINPTR ; AADD: XCHG LHLD MASTINADR DAD D XCHG LHLD MASTINLEN MOV A,L ORA H MVI A,26 RZ ; LDAX D LHLD MASTINPTR INX H SHLD MASTINPTR RET ;..... ; ; AAE: XRA A STA FCBMASTIN+12 STA FCBMASTIN+32 LHLD BUFFERSIZE SHLD MASTINLEN SHLD MASTINPTR MVI C,OPEN LXI D,FCBMASTIN CALL BDOS INR A JNZ AAG MVI C,PRINT LXI D,AAF CALL BDOS JMP FILERR ; AAF: DB CR,LF,'++ NO MAST.CAT FILE ++','$' ; AAG: JMP AAH ; ; FCBMASTOUT: DB 0,'NEW CAT' ; DB 0 DS 20 ;..... ; ; BUFFERSIZE: DS 2 ;buffer input/output size ; MASTOUTADR: DS 2 ;output buffer start address ; MASTOUTLEN: DS 2 ;output buffer length ; MASTOUTPTR: DS 2 ;output buffer pointer address ; AAH: JMP AAMM ;..... ; ; PUTMASTOUT: PUSH PSW LHLD MASTOUTLEN XCHG LHLD MASTOUTPTR MOV A,L SUB E MOV A,H SBB D JC AAM LXI H,0000H SHLD MASTOUTPTR ; AAI: XCHG LHLD MASTOUTLEN MOV A,E SUB L MOV A,D SBB H JNC AAL LHLD MASTOUTADR DAD D XCHG MVI C,SETDMA CALL BDOS LXI D,FCBMASTOUT MVI C,21 CALL BDOS ORA A JNZ AAJ LXI D,TBUF LHLD MASTOUTPTR DAD D SHLD MASTOUTPTR JMP AAI ;..... ; ; AAJ: MVI C,PRINT LXI D,AAK CALL BDOS POP PSW JMP FILERR ;..... ; ; AAK: DB CR,LF,'++ DISK FULL: MASTOUT ++','$' ; AAL: LXI D,TBUF MVI C,SETDMA CALL BDOS LXI H,0000H SHLD MASTOUTPTR ; AAM: XCHG LHLD MASTOUTADR DAD D XCHG POP PSW STAX D LHLD MASTOUTPTR INX H SHLD MASTOUTPTR RET ;..... ; ; AAMM: XRA A STA FCBMASTOUT+12 STA FCBMASTOUT+32 LHLD BUFFERSIZE SHLD MASTOUTLEN LXI H,0000H SHLD MASTOUTPTR MVI C,ERASE LXI D,FCBMASTOUT CALL BDOS MVI C,MAKE LXI D,FCBMASTOUT CALL BDOS INR A JNZ AAP MVI C,PRINT LXI D,AAN CALL BDOS JMP FILERR ;..... ; ; AAN: DB CR,LF,'NO DIR SPACE: MASTOUT','$' AAP: JMP INITRD ;..... ; ; FCBBAK: DB 0,'MAST BAK' DB 0 DS 20 ; INITRD: CALL READNA ;read disk name from buffer LXI D,NADAT+1 LXI H,DKNAME MVI B,7 CALL MOVER LXI D,NADAT+9 LXI H,DKNAME+9 MVI B,3 CALL MOVER ; ; ; Read in the names to be ignored (i.e., not cataloged). They are at ; the front of MAST.CAT. They are simply a list of filename.filetype ; (cr/lf) with the first having a ( before it, and the last having a ) ; after it. ; CALL GETMASTIN ;get the leading '(' JZ NOMAST ;if EOF CPI '(' JNZ NOIGN CALL PUTMASTOUT LXI H,IGNORE ;point to buffer LXI B,IGNSIZE ;for buffer overflow test ; IGNRD: PUSH B PUSH H CALL GETMASTIN JZ IGNEOF ;unexpected EOF PUSH PSW ;save character CALL PUTMASTOUT POP PSW POP H POP B MOV M,A INX H CPI ')' JZ IGNEND ;table is loaded DCX B ;more room in table? MOV A,B ORA C JNZ IGNRD ; ; ; Table overflow ; CALL EREXIT DB CR,LF,'++ TOO MANY IGNORE NAMES FOR TABLE ++','$' ; NOMAST: CALL EREXIT DB CR,LF,'++ MISSING OR EMPTY MAST.CAT ++','$' ; NOIGN: CALL EREXIT DB CR,LF,'++ NO IGNORE NAMES IN MAST.CAT ++','$' ; IGNEOF: CALL EREXIT DB CR,LF,'++ EOF READING FOR IGNORE NAMES.' DB CR,LF,'MAY BE MISSING ")" AFTER LAST NAME ++','$' ; ; ; Ignore names have been read into table ok ; IGNEND: CALL GETMASTIN JZ IGNEOF PUSH PSW CALL PUTMASTOUT POP PSW CPI LF JNZ IGNEND ;delete CRLF ; ; ; Prime the buffers ; NAMELP: CALL READNA ;read name into nadat MASTLP: CALL READMI ;read master into midat ; ; ; If EOF on both files, we are all done ; COMPLP: LDA NAEOFLG ;name file EOF? ORA A JZ NOTEOF ;..no LDA MIEOFLG ;master in EOF? ORA A JNZ ALLDONE ;yes, that is it ; NOTEOF: MVI B,MASTSIZE LXI D,NADAT LXI H,MIDAT CALL COMPR JZ EQUAL JC WRITEN ; ; ; Master is lower - write it if for another disk ; MVI B,DKSIZE LXI D,MIDSK LXI H,DKNAME CALL COMPR JZ DELMI ;deleting MIDAT LXI D,MIDAT LXI H,MODAT MVI B,MASTSIZE CALL MOVER CALL WRITEMO JMP MASTLP ;..... ; ; ; Delete master in ; DELMI: LXI D,DELMSG LXI H,MIDAT CALL MESG JMP MASTLP ;..... ; ; DELMSG: DB 'DEL: ','$' ; ; ; Name is lower - write it ; WRITEN: LXI D,NADAT LXI H,MODAT MVI B,MASTSIZE CALL MOVER CALL WRITEMO ; ; ; Print that name was added ; LXI D,ADDMSG LXI H,NADAT CALL MESG CALL READNA ;read next name JMP COMPLP ; ADDMSG: DB 'ADD: ','$' ; ; ; Both files equal ; EQUAL: LXI D,MIDAT LXI H,MODAT MVI B,MASTSIZE CALL MOVER CALL WRITEMO ;write out master JMP NAMELP ;read both ;..... ; ; ; Read name file ; READNA: LXI H,NADAT MVI B,8 CALL READNAC ;read characters MVI M,'.' INX H MVI B,3 ;type length CALL READNAC MVI M,',' ; IF USER LXI H,UDAT MVI M,';' INX H MVI B,1 CALL READNAC ENDIF ;USER ; ; ; If this is a name not to be cataloged, read the next ; LXI D,IGNORE ;get ignore table ; IGNLP: LXI H,NADAT MVI B,DKSIZE ;# of characters to match ; IGNCLP: LDAX D CMP M JZ IGNMAT ;matched MOV A,M ;get character CPI ' ' ;space? JZ IGNMAT1 ; ; ; Get next entry ; IGNEXTE:LDAX D INX D CPI ')' ;end of table? RZ ;..yes, return CPI LF ;next line? JNZ IGNEXTE JMP IGNLP ; ; ; Character matched ; IGNMAT: INX D ;skip match character ; IGNMAT1:INX H DCR B JNZ IGNCLP ;loop until done ; ; ; Name is to be deleted ; JMP READNA ;read next name ;..... ; ; ; All done - write EOF to output, ask if more to do ; ALLDONE:MVI A,'Z'-40H ;EOF character CALL PUTMASTOUT ; BBBB: LHLD MASTOUTPTR MOV A,L ANI 7FH JNZ CCCC SHLD MASTOUTLEN ; CCCC: MVI A,EOF PUSH PSW CALL PUTMASTOUT POP PSW JNZ BBBB MVI C,CLOSE LXI D,FCBMASTOUT CALL BDOS INR A JNZ EEEE MVI C,PRINT LXI D,DDDD CALL BDOS JMP EEEE DDDD: DB CR,LF,'CANNOT CLOSE MASTOUT','$' ; ; ; Run was successful - set final file dispositions ; EEEE: JMP HHHH ; FFFF: PUSH H LXI B,16 DAD B ; GGGG: LDAX D MOV M,A INX D INX H DCR C JNZ GGGG POP D MVI C,RENAME CALL BDOS RET ;..... ; ; HHHH: LXI H,FCBMASTIN LXI D,FCBBAK CALL FFFF LXI H,FCBMASTOUT LXI D,FCBMASTIN CALL FFFF CALL ILPRT DB CR,LF,'MAST.CAT has ' ; COUNT: DB ' entries, with disk: ',0 CALL ILPRTS ; COUNT1: DB ' . ',CR,LF,0 ; JMP AGAIN ;go do another (or quit) ;..... ; ; ; Read characters into name buffer ; READNAC:LDA NAEOFLG ;EOF on names? ORA A JNZ NAEOF ;yes, pad with 0FFH PUSH H PUSH B ; GETNA: CALL GETNAM ;get from buffer, not disk CPI LF JZ GETNA ;ignore lf POP B POP H CPI '.' ;end? JZ NAEND ; IF USER CPI ';' JZ NAEND ENDIF ;USER ; CPI CR ;end? JZ NAEND CPI 'Z'-40H ;EOF? JZ NAEOF MOV M,A INX H DCR B JNZ READNAC PUSH B PUSH H CALL GETNAM ;kill delimiter character POP H POP B CPI 'Z'-40H ;EOF? RNZ STA NAEOFLG RET ;..... ; ; NAEND: MVI M,' ' INX H DCR B JNZ NAEND RET ;..... ; ; NAEOF: MVI M,0FFH INX H DCR B JNZ NAEOF MVI A,1 STA NAEOFLG RET ;..... ; ; ; Subroutine to simulate reading from the old NAMES.SUB file. Actually ; the characters come from the names buffer provided by the first part ; of the program ; GETNAM: LHLD BUFPTR ;get pointer to NABUF MOV A,M ;get next character INX H ;bump the pointer SHLD BUFPTR RET ;..... ; ; ; Read master-in name ; READMI: LXI H,MIDAT CALL MINAME ;get file name MVI M,',' ;separator INX H CALL MINAME ;get disk name ; IF USER MVI A,' ' STA MIUSR+1 LDA MASDEL ;delimiter CPI ';' RNZ ;no user areas CALL GETMASTIN STA MIUSR+1 ;save user number CALL GETMASTIN ENDIF ;USER ; RET ;..... ; ; ; Read master-in, 1 field ; MINAME: MVI B,8 CALL READMIC ;get characters MVI M,'.' INX H MVI B,3 CALL READMIC ;get type RET ;..... ; ; ; Read characters into master name ; READMIC:LDA MIEOFLG ORA A JNZ MIEOF PUSH H PUSH B ; GETMI: CALL GETMASTIN CPI LF ;ignore LF JZ GETMI POP B POP H CPI ',' JZ MIEND CPI '.' JZ MIEND ; IF USER CPI ';' JZ MIEND ENDIF ;USER ; CPI CR JZ MIEND CPI 'Z'-40H ;EOF? JZ MIEOF MOV M,A INX H DCR B JNZ READMIC PUSH B PUSH H CALL GETMASTIN ;get delimiter ; IF USER STA MASDEL ;save delimiter ENDIF ;USER ; POP H POP B CPI 'Z'-40H ;EOF? RNZ STA MIEOFLG RET ;..... ; ; MIEND: MVI M,' ' INX H DCR B JNZ MIEND RET ;..... ; ; MIEOF: MVI M,0FFH INX H DCR B JNZ MIEOF STA MIEOFLG ;show EOF RET ;..... ; ; ; Write an entry to master out - also bump count of entries written ; WRITEMO:MVI B,3 LXI D,MODAT+9 LXI H,FTFRE CALL COMPR JZ BUMPD ;if yes, do not count it LXI H,COUNT+3 ; BUMP: MOV A,M ;get count digit CPI ' ' JNZ BUMPNB MVI A,'0' ; BUMPNB: INR A MOV M,A CPI '9'+1 ;time to carry? JNZ BUMPD ;no, done MVI M,'0' DCX H JMP BUMP ;..... ; ; BUMPD: LXI H,MODAT MVI B,MASTSIZE ; WRMOL: MOV A,M CPI ' ' ;null character? JZ WRSKIP PUSH B PUSH H CALL PUTMASTOUT POP H POP B ; WRSKIP: INX H ;point to next character DCR B JNZ WRMOL ;loop until done MVI A,CR CALL PUTMASTOUT MVI A,LF CALL PUTMASTOUT RET ;..... ; ; ; Print message in 'DE', then name in 'HL' ; MESG: PUSH H MVI C,PRINT CALL BDOS POP H MVI B,12 ;name,'.',type ; MESGL: PUSH H PUSH B MOV A,M ;get character CPI ' ' CNZ CRTOUT ;display the character ; MESGS: POP B POP H INX H DCR B JNZ MESGL MVI A,CR ;'CR' char. CALL CRTOUT ;display the character MVI A,LF ;'LF' char. CALL CRTOUT ;display the character RET ;..... ; ; ; Move subroutine (DE)=>(HL), length in (B) ; MOVER: LDAX D ANI 7FH ;strip MOV M,A INX D INX H DCR B JNZ MOVER RET ;..... ; ; ; MOVNC subroutine (DE)=>(HL), length in (B). This version strips the ; hi-order bit, if set. It also filters out commas which might other- ; wise mess up the database. ; MOVNC: LDAX D ANI 7FH ;strip the hi-order bit CPI ',' ;is it a comma? JNZ MN1 ;jump if not MVI A,'/' ;else change to slash ; MN1: MOV M,A INX D INX H DCR B JNZ MOVNC RET ;..... ; ; ; Compare routine (DE)<=>(HL), length in (B) ; COMPR: LDAX D CMP M RNZ ;ret with non zero set INX D INX H DCR B JNZ COMPR RET ;zero set, shows = ;..... ; ; ; Get free disk space; plug it into the phantom file. Since number of ; blocks on disk is DSM+1, get DSM. ; FRESPC: CALL CKCPM3 ;go calculate free space if CP/M 3.0 LHLD DPBADDR ;retrieve adr. of disk parameter block LXI D,5 ;dsm is at DPB+5 DAD D MOV E,M ;low byte of DSM INX H MOV D,M ;high byte of DSM XCHG ;'HL'=DSM INX H ;block=DSM+1 SHLD TBTR ;save total bits to read ; ; ; Get code for bytes per block, where 1=1024, 2=2048, 3=4096, etc. ; LHLD DPBADDR ;recover DPB address INX H ;bsh is at DPB+2 INX H MOV A,M ;BSH is 3-7, SUI 2 ;make it 1-5 CPI 5+1 ;check for over 5 JNC NONSTAN ;if so, it is non-standard CPI 1 ;check for under 1 JC NONSTAN ;if so, it is non-standard STA BLKSIZE ;save the block size ; ; ; Find allocation vector and count the bits reset. ; LHLD TBTR XCHG ;'DE'=total of all vector bits to read MVI C,ALLVEC ;tell 'BDOS' to get address of CALL BDOS ; the allocation vector in 'HL' ; NXTBYT: MOV A,M ;get a byte of allocation vector MVI C,8 ;it has eight bits, right? ; ROTAT: RLC ;move a bit to carry CNC COUNTIT ;not set? it'S A FREE BLOCK DCX D ;count the bit just checked PUSH PSW ;save the bit pattern MOV A,D ;have we done the whole ORA E ; allocation vector? JZ KFREE ;finished here, if so POP PSW ;restore the bit pattern DCR C ;count a bit done JNZ ROTAT ;more bits this byte? do another INX H ;done with byte? bump pointer, JMP NXTBYT ; and get the next byte ; COUNTIT:PUSH H ;save the allocation vector pointer LHLD BLKSFRE ;increment the 'BLOCKS FREE' count INX H SHLD BLKSFRE POP H ;restore the pointer RET ;..... ; ; ; Free space on disk is block size times number of free blocks. ; KFREE: POP PSW ;clean the stack LDA BLKSIZE ;get block size LHLD BLKSFRE ;get free blocks ; MULT: DCR A ;free space=free blocks JZ CONDEC ; times block size DAD H JMP MULT ;..... ; ; ; Calculate free disk space if CP/M 3.0 ; CKCPM3: MVI C,CPMVER ;check version # CALL BDOS MOV A,L CPI 30H ;version 3.0? RC ;use normal method if not CP/M 3.0 POP H ;remove 'CALL CKCPM3' from stack MVI C,CURDSK CALL BDOS MOV E,A MVI C,46 ;CP/M 3.0 compute free space call CALL BDOS MVI C,3 ;answer is 3 bytes long (24 bits) ; FREE30: LXI H,TBUF+2 ;answer is locted here MVI B,3 ;convert to 'K' length ORA A ; FREE31: MOV A,M RAR MOV M,A DCX H DCR B JNZ FREE31 ;loop for 3 bytes DCR C JNZ FREE30 ;shift 3 times LHLD TBUF ;get result in 'K' JMP CONDEC ;display result ;.... ; ; NONSTAN:CALL EREXIT DB CR,LF,'++ CAN''T READ FREE DISK SPACE ++','$' ;..... ; ; ; Arrive here with 'HL' = free disk space. Convert to decimal and store. ; CONDEC: MVI B,0 ;leading zero flag LXI D,-10000 CALL CONDEC1 LXI D,-1000 CALL CONDEC1 LXI D,-100 CALL CONDEC1 LXI D,-10 CALL CONDEC1 MOV A,L ADI '0' CALL CONDEC4 ;print it MVI A,'k' JMP CONDEC4 ;print it, finished ;... ; ; ; Subtract until negative ; CONDEC1:MVI C,'0'-1 ;count in ASCII ; CONDEC2:INR C DAD D JC CONDEC2 ; ; ; Too many, add one back. ; MOV A,D ;get two's complement CMA ; of two's complement MOV D,A ; to get value to MOV A,E ; add back CMA MOV E,A INX D DAD D ; ; ; Arrive here with decimal digit in 'C' ; MOV A,C ;get count in 'A' CPI '1' ;zero? JNC CONDEC3 ;no MOV A,B ;yes, is it a leading zero? ORA A MOV A,C ;get digit to 'A' JNZ CONDEC4 ;not leading zero, store it MVI A,'+' ;convert leading zero JMP CONDEC4 ; CONDEC3:DCR B ;set no leading zero flag ; CONDEC4:PUSH H ;save value LHLD FREPTR ;point to current 'NABUF' slot MOV M,A ;store the digit INX H ;bump the pointer SHLD FREPTR ;save it POP H ;recover value RET ;..... ; ; ; Show an error if CP/M ver 3.0 ; CPM3: CALL ILPRT DB CR,LF,CR,LF,'*** CP/M 3.0 NOT SUPPORTED ***',CR,LF,0 JMP EXIT1 ;..... ; ; CRLF: CALL ILPRT ;carriage ret - line feed DB CR,LF,0 RET ;..... ; ; ; Gets a character from the keyboard ; CRTIN: MVI C,RDCON CALL BDOS ANI 07FH ;strip off any parity CPI ' ' ;space character? RZ ;return, if yes CPI 'C'-40H ;CTL-c? JZ EXIT ;if yes, abort CPI 'X'-40H ;CTL-x? JZ EXIT ;abort, if yes CPI ' ' ;some other control character? RC ;return if yes ANI 5FH ;change to upper case, if lower CPI 'N' ;'N' for 'NO' JZ EXIT RET ;..... ; ; ; Displays one character on the CRT ; CRTOUT: PUSH PSW ;save the character MVI C,WRCON MOV E,A ;get the character into 'E' reg. CALL BDOS ;show the character on the crt POP PSW ;get the character back RET ;..... ; ; ; Exit with error message ; EREXIT: POP D ;get message address MVI C,PRINT CALL BDOS ; EXIT: CALL CRLF CALL CRLF JMP 0000H ;to CP/M return address ;..... ; ; ; Inline print subroutine ; ILPRT: XTHL ;get starting address of string to 'HL' ; ILPLP: MOV A,M PUSH H CALL CRTOUT ;show the character on the CRT POP H INX H MOV A,M ORA A JNZ ILPLP INX H XTHL ;return address to top of stack RET ;..... ; ; ; Inline print subroutine, does not show spaces ; ILPRTS: XTHL ;get starting address of string to 'HL' ; ILPLPS: MOV A,M CPI ' ' ;ignore spaces JZ ILPLPT PUSH H CALL CRTOUT ;display the character POP H ; ILPLPT: INX H MOV A,M ORA A JNZ ILPLPS INX H XTHL ;return addr to top of stack RET ;..... ; ; ; If using space bar for 'YES', show a 'Y' ; SHOW$Y: MVI A,'H'-40H ;display a backspace CALL CRTOUT ;display on the crt MVI A,'Y' ;display a 'Y' on the crt JMP CRTOUT ;..... ; ; DS 80 ;stack space (40 levels) ; STACK: DB 0,0 ;..... ; ; AMB: DB '????????????',0 ;to return all directory entries, NADAT: DB 'XXXXXXXX.YYY,' DKNAME: DB ' . ' DKSIZE: EQU $-DKNAME ;size of disk name ; IF USER UDAT: DB ' ' ;user number ENDIF ;USER ; NAMSIZE:EQU $-NADAT-DKSIZE+1 ; MIDAT: DB 'XXXXXXXX.YYY,' MIDSK: DB 'XXXXXXXX.YYY' ; IF USER MIUSR: DB ';z' ;user number ENDIF ;USER ; MODAT: DB 'XXXXXXXX.YYY,XXXXXXXX.YYY' ; IF USER DB ';z' ;user number ENDIF ;USER ; MASTSIZE: EQU $-MODAT ;size of masterfile record ; FTFRE: DB 'FRE' ;phantom file type RET ;..... ; ; ; Help code and text is overlaid by ignore table in a normal run ; HELP: CALL ILPRT ;frame 1 DB CR,LF,CR,LF DB 'CATALOGING YOUR DISK SYSTEM:',CR,LF,CR,LF DB ' Three programs are needed. They are usually all ' DB 'on the same disk:',CR,LF,CR,LF DB ' 1) MAST.CAT',CR,LF DB ' 2) MCAT.COM',CR,LF DB ' 3) XCAT.COM',CR,LF,CR,LF DB ' MCAT.DOC (not required but ' DB 'usually included)',CR,LF DB ' MCAT.SET (for setting file names ' DB 'buffer)',CR,LF,CR,LF DB 'You originate MAST.CAT (Master Catalog) yourself. ' DB 'Use your editor to',CR,LF,'open a file by that name. ' DB 'It must have at least ONE file name which',CR,LF DB 'will be in parenthesis. This is the "Ignore list". ' DB 'It contains names',CR,LF,'of files which you do not ' DB 'particularly want to display in the catalog',CR,LF DB 'listing since you have numerous copies of them. It ' DB 'might include such',CR,LF,'files as: ASM, ED, DDT, ' DB 'LOAD, PIP, STAT, SUBMIT, XSUB, etc. Example:' DB CR,LF,CR,LF,' (ASM.COM Note opening ' DB 'parenthesis at left',CR,LF,' ED.COM',CR,LF DB ' SUBMIT,COM) Note closing parenthesis at ' DB 'right',CR,LF,CR,LF,'[more] ',0 ; ;end of frame 1 CALL CRTIN CALL ILPRT DB CR,' ',CR,LF DB ' If you want all files to show, use one name that ' DB 'is unlikely to ',CR,LF,'ever appear such as: (DUMMY.' DB 'ABC) or just use: () ',CR,LF,CR,LF,' All ' DB 'disks to be catalogued need a special "volume number" ' DB 'file.',CR,LF,'This uses a "-" as the first character ' DB 'in the file so the MCAT program',CR,LF,'can recognize ' DB 'this special file name. It can include both a name ' DB 'and',CR,LF,'a number for the user''s reference, but ' DB 'the name will be ignored. A',CR,LF,'leading zero may ' DB 'be used. Example:',CR,LF,CR,LF,' A>SAVE 0 B:-' DB '.012',CR,LF,CR,LF,' MCAT.COM is used next. It ' DB 'usually expects the master catalog disk',CR,LF,'to be ' DB 'placed in the A: drive and the disks to be cataloged ' DB 'into the B:',CR,LF,'drive one at a time. It reads the' DB ' disk directory and puts each file',CR,LF,'name into ' DB 'memory and then alphabetizes the list. It then up' DB 'dates the',CR,LF,'MAST.CAT file with the new names or ' DB 'deletes those previously listed that',CR,LF,'are no ' DB 'longer on that disk.',CR,LF,CR,LF,' Double drive ' DB 'users type MCAT B: Thereafter when it asks if ready,' DB CR,LF,'place the next disk to be cataloged into drive ' DB 'B: and type "Y" or "RET"',CR,LF,'or "SPACE" for Yes.' DB CR,LF,CR,LF,'[more] ',0 ; ;end of frame 2 CALL CRTIN CALL ILPRT DB CR,' ',CR,LF DB ' This does not allow the A: drive to be cataloged.' DB ' Use the same',CR,LF,'procedure as for a single drive' DB ' system:',CR,LF,CR,LF,' Single drive users type ' DB 'MCAT or MCAT A: You will alternately be',CR,LF,'asked' DB ' to insert the disk to be cataloged and then the ' DB 'system catalog',CR,LF,'disk. An update of MAST.CAT ' DB 'is made with each new disk.',CR,LF,CR,LF,' When ' DB 'finished updating, use the XCAT.COM program. This ' DB 'creates a',CR,LF,'new file automatically, called ' DB 'MAST.LST (Master List) which is ready to',CR,LF DB 'print. You can also search through this listing ' DB 'rapidly for any file',CR,LF,'you know (or suspect) ' DB 'you have, using a program called FIND.COM. You' DB CR,LF,'can also use XCAT.COM to print the MAST.CAT ' DB 'file.',CR,LF,CR,LF,' At present the program ' DB 'allows 36 ''IGNORE'' file names in MAST.CAT',CR,LF DB 'and ' ; NAMES$NUMBER: DB '256' ; DB ' file names. Either may be easily changed by editing ' DB 'the user',CR,LF,'option area in the source code, then ' DB 'reassembling. (Or use MCAT.SET.)',CR,LF,CR,LF,' ' DB ' MCAT C: B: C: drive for disks to be cataloged' DB CR,LF,' B: drive for the MAST.CAT' DB ', MCAT.COM system disk',CR,LF,CR,LF,'(For 3 or more ' DB 'drives.) Read the MCAT.DOC file for more information.' DB CR,LF,CR,LF,'[more] ',0 ; ;end of frame 3 CALL CRTIN CALL ILPRT DB CR,' ',CR,LF DB 'NOTE FOR CP/M 3.0 USERS:',CR,LF,CR,LF,' CP/M 3.0 ' DB 'does not have the same SAVE 0 FILENAME.EXT function ' DB 'that',CR,LF,'CP/M 2.2 and earlier versions have. If ' DB 'you want to write an unique ID',CR,LF,'number to disk,' DB ' you will need the program called NULL.COM, as the ' DB 'CP/M',CR,LF,'3.0 ''SAVE'' command does something ' DB 'totally different. There is no easy',CR,LF,'way to ' DB 'write a directory entry like -.123 that does not use' DB ' at least',CR,LF,'1024 bytes of disk space.' DB CR,LF,LF,LF,LF,LF,LF,LF,LF,LF,LF,LF,LF,LF,LF,LF,0 ;..... ;end of frame 4 ; ; EXIT1: XRA A ;clear the 'A' reg. and all flags LHLD STACK ;get the original stack pointer back SPHL ;set the stack pointer to that address RET ;return to original CCP location ;..... ; ; ; Error message text if attempting to catalog all user areas while ; running under a pre 2.0 cp/m release. ; BADVERS:CALL EREXIT DB BELL,CR,LF DB '++ FATAL ERROR - No user areas in this CP/M verion ++' DB CR,LF,CR,LF,'$' ;..... ; ; ORG HELP ; ; ; Ignore file-name table overlays help code, since only one or the other ; would be used in any run. ; IGNORE: DS 1 ;dummy end of table mark stored here DS IGNSIZE ;ignore table ; CDRIVE: DS 1 ;drive for disk to be cataloged MDRIVE: DS 1 ;drive for MAST.CAT must follow CDRIVE DRIVE: DS 1 ;flag for both drives the same ; FCT: DS 2 ;file count TFCT: DS 2 ;file count for this sort pass BUFPTR: DS 2 ;name buffer pointer NAEOFLG: DS 1 ;name buffer EOF flag NAMEND: DS 2 ;name buffer end address MIEOFLG: DS 1 ;master in EOF flag IF USER MASDEL: DS 1 ;master name file delimited character ENDIF ;USER ; BLKSIZE: DS 1 ;block size code BLKSFRE: DS 2 ;free blocks DPBADDR: DS 2 ;disk parameter block address EXMASK: DS 1 ;extent mask TBTR: DS 2 ;total bits to read in allocation vector FREPTR: DS 2 ;phantom file pointer ; ; ; BDOS/CBIOS equates ; RDCON: EQU 1 ;read character from console keybord WRCON: EQU 2 ;show character on console PRINT: EQU 9 ;print string, ends with '$' CPMVER: EQU 12 ;CP/M version number RESET: EQU 13 ;reset disk system SELDSK: EQU 14 ;select disk OPEN: EQU 15 ;open file CLOSE: EQU 16 ;close file SRCHF: EQU 17 ;search for first SRCHN: EQU 18 ;search for next ERASE: EQU 19 ;delete file READ: EQU 20 ;read sequential WRITE: EQU 21 ;write sequential MAKE: EQU 22 ;make file RENAME: EQU 23 ;rename file CURDSK: EQU 25 ;query current logged disk SETDMA: EQU 26 ;set DMA address ALLVEC: EQU 27 ;get disk drive allocation vector DPB: EQU 31 ;get disk parameter block address BDOS: EQU 0005H ;location of BDOS jump vector FCB: EQU 005CH ;location of file control block TBUF: EQU 0080H ;location of CP/M buffer ;..... ; ; ; Disk directory entries stored here (see user setable options) ; NABUF: DS 0 ;..... ; ; ; END START