;============================= ; ERAQ.ASM v2.0 06/28/85 || ;============================= ; ; ERAQ - ERAse with query, patterned after the UNIX utility ERAQ ; ; This is the generic video version and can be set for the ; Kaypro video codes if desired (see equates below) ; ; Based on ERAQ, XERA, and most recently KILL ; ; With routines and ideas stolen from many other programs. ; Free space routine jerked from SD*.ASM ; ; Vers 2.0 and Kaypro video mods by Steve Sanders ; ;--------------------------- whatzitdo ? -------------------------- ; ; Replaces CP/Ms built-in ERA command and offers wildcard *.* ; erasure if HELP equate below is NO and no filespec is entered ; on the commandline. If HELP is YES and no filespec is entered ; ERAQ will abort and display a short help message. ; ; No ERAsure(s) takes place until user enters a 'Y' when prompted with ; the filename to be erased. Entry of anything other then 'Y' causes ; ERAQ to display next matching filename for erasure or quits and ; shows Free space on the current du: if no other names match. ; ; ERAQ will display the logged du:, amount of Free space on logged ; disk, and number of files erased on exit. ; ; ERAQ may be aborted at anytime with entry of CTRL-C ; ; Note: ERAQ may be RENamed to anything you desire for ease of use, ; however I recommend you use PATCH or DU and disable your ; built-in ERA command and call it ERA.COM. ; ; Even an ERAsed file may be recovered with UNERA if no other ; writes to the disk have taken place since the ERAsure. ; ; Original ERAQ program written by Thomas Hill ; ;----------------------------------------------------------------------- ; ; Un-comment following ASEG if using M80 ; ; ASEG ; used by M80 ; NO EQU 0 YES EQU NOT NO ; (Cannot be 0FFH as using conditionals ; and M80, SLRMAC, etc. need 0FFFFH) ;----------------------------------------------------------------------- ; Assembly-time Options ; KAYPRO EQU NO ; No, use generic video codes ; Yes, use Kaypro-specific video codes ; HELP EQU NO ; Yes, show help if no filespec entered ; No, use *.* filespec if none entered ; RESET EQU NO ; Yes if a disk reset is wanted at exit ; ;----------------------------------------------------------------------- ; ; General equates ; CR EQU 0DH ; Carriage return EOF EQU 1AH ; End of file (CTL-Z) LF EQU 0AH ; Line feed TAB EQU 09H ; Tab character esc equ 1bh ; ESC for video sequences ; ; ; Kaypro screen control codes ; ; if kaypro curup equ 0bh ; cursor up one row clrln equ 18h ; clear to eol clrsc equ 17h ; clear to eos endif ; ; ; System equates ; BDOS EQU 0005H ; BDOS entry point FCB1 EQU 005CH ; CP/M file control block ; CONIN EQU 1 ; Console input CONOUT EQU 2 ; Console output PRTBUF EQU 9 ; Send a striang to the console SELDSK EQU 14 ; Select Disk SRCHF EQU 17 ; Search for First Ambiguous File SRCHN EQU 18 ; Search for Next Ambiguous File DELETF EQU 19 ; Delete a File WRITEF EQU 21 ; Write a Record GETDRV EQU 25 ; Get drive number (a=0, b=1, etc) SETDMA EQU 26 ; Set Disk DMA Address GALLOC EQU 27 ; Get Disk Allocation Map Address SETATR EQU 30 ; Change file attribute bits CURDPB EQU 31 ; Get Current Disk Parameter Block CURUSR EQU 32 ; Get user number (CP/M 2.x) ; ; Those functions requiring a byte argument will expect that byte to be ; in the "E" register. Address arguments are passed in the "DE" reg- ; ister. Return codes are passed in the 'A' register. In general, a ; return of 0 indicates success, while an 0FFH indicates failure. ; ;----------------------------------------------------------------------- ; ; ORG 100H ; ; ; LXI H,0 ; Set HL to zero DAD SP ; Add the address of CCP stack SHLD STACK ; And save it LXI SP,STACK ; Set the stack pointer to local stack ; LXI D,SIGNON ; Print SIGNON message CALL STROUT LXI H,0 ; Zero HL SHLD DIRCNT ; Count of selected directory entries SHLD ERACNT ; Count of erased files LXI H,BUFFER ; Get buffer address SHLD DIRPTR ; And save it LXI D,DMABUF MVI C,SETDMA ; Set DMA to programs buffer CALL BDOS LDA FCB1 ; Get specified drive code DCR A ; Was it zero (current drive)? JP SAVDRV ; No - it's an explicit code MVI C,GETDRV ; Get current drive CALL BDOS ; From BDOS (returned in A) ; SAVDRV: STA DRIVE ; Save for later MVI E,0FFH MVI C,CURUSR ; Get current user number CALL BDOS ; From BDOS (returned in A) STA USER ; Save for later LDA FCB1+1 CPI ' ' ; No file reference on command line? JNZ DIR0 ; There are some, go process them ; ; ; If no file name specified, abort with a help menu (optional) ; IF HELP LXI D,MENU CALL STROUT JMP EXIT ENDIF ; HELP ; ; If not help then query for a *.* mass erase ; IF NOT HELP LXI D,MSG4 ;erase *.* ? msg CALL STROUT mvi c,conin ;get reply call bdos ani 5fh ;make upper case cpi 'Y' ;if 'y' then proceed, else abort jnz fin1 lxi h,fcb1+1 ; mvi b,11 all1: mvi m,'?' ;fill fcb with question marks inx h dcr b jnz all1 ; mvi b,24 all2: mvi m,0 ;and rest with zeros inx h dcr b jnz all2 ENDIF ; NOT HELP ; if not kaypro call crlf endif ; ; Search and table the files requested ; DIR0: LXI D,FCB1 MVI C,SRCHF ; Get first occurrence of requested file CALL BDOS INR A ; Return 0FFH if no match JZ FIN2 ; Go print message and quit ; DIR1: DCR A ; Undo "INR" above ADD A ; Offset to buffer address (x 2) ADD A ; (x 4) ADD A ; (x 8) ADD A ; (x 16) ADD A ; (x 32) LXI H,DMABUF ; Get address of the buffer MVI D,0 MOV E,A DAD D ; Calculate offset XCHG ; And save it LHLD DIRPTR ; Get current table pointer MVI C,32 ; Move 32 bytes of directory entry ; DIR2: LDAX D ; Get a byte MOV M,A INX H INX D DCR C JNZ DIR2 SHLD DIRPTR ; Save updated table pointer LHLD DIRCNT ; Count entries found INX H ; Add one SHLD DIRCNT ; Store back LHLD DIRPTR ; Next entry address in directory table XCHG ; Save in DE LHLD BDOS+1 ; Get start of BDOS address MOV A,H ; Get base page of BDOS SUI 9 ; CCP base page minus fudge factor CMP D ; Has DIRPTR gotten up there? JNZ DIR3 ; No, get more entries LXI D,MSG5 ; Send warning message CALL STROUT LHLD DIRCNT ; Tell user how many we did get CALL PRNDEC LXI D,MSG6 ; Finish message CALL STROUT JMP DIR4 ; Pretend there are no more files ; DIR3: LXI D,FCB1 ; Find the next entry MVI C,SRCHN CALL BDOS INR A ; Done yet? JNZ DIR1 ; ; ; Table now contains all selected directory entries - display them and ; request permission to erase them ; DIR4: LXI H,BUFFER ; Reset pointer to start of table ; DISP1: SHLD DIRPTR ; Save it for later PUSH H CALL CRLF ; if kaypro mvi e,curup ; this causes each filename call output ; displayed to overwrite the mvi e,clrln ; previous and stop screen scroll call output endif ; ; ; Print drive and current user number to console ; LDA DRIVE ; Get drive code (0=A, 1=B, etc) ADI 'A' ; Add ASCII offset MOV E,A ; Move for call CALL OUTPUT ; Display drive LDA USER MOV L,A ; CVD needs number in HL MVI H,0 MOV D,H ; Set leading zero indicator CALL PRND2 ; Go print user number MVI E,':' ; And a colon CALL OUTPUT ; POP H ; Restore pointer MVI A,8 ; Eight characters in name INX H ; Over user field CALL PRTSTG ; Print the string at (HL) for (A) bytes PUSH H ; Save start of type MVI E,'.' ; Print a period CALL OUTPUT POP H ; Get back start of type PUSH H ; And save it again MVI A,3 ; Three characters in type CALL PRTSTG MVI E,' ' ; Print a space to separate CALL OUTPUT ; ; ; Check attribute bytes ; XRA A ; Zero out A STA ROFLG ; Set R/O to false initially STA SYSFLG ; Set SYS to false initially POP H ; Get back start of type MOV A,M ; Get character RLC ; Put attribute bit in carry JNC DISP2 MVI A,0FFH STA ROFLG ; Set R/O file flag ; DISP2: INX H ; System attribute MOV A,M RLC ; Put attribute bit in carry JNC DISP3 MVI A,0FFH STA SYSFLG ; Set system file flag ; DISP3: LDA ROFLG ; Now see if any attributes are set MOV B,A LDA SYSFLG ORA B JZ DISP5 ; Leave attribute area blank LDA ROFLG ; Print R/O symbol? ORA A JZ DISP4 ; Nope LXI D,RO CALL STROUT ; Print read/only symbol after file name ORA A JZ DISP4 MVI E,',' CALL OUTPUT ; DISP4: LDA SYSFLG ORA A JZ DISP5 ; Print system symbol? LXI D,SYS CALL STROUT ; Yes, print it after file name ; DISP5: LXI D,MSG1 ; Ask about erasing file CALL STROUT MVI C,CONIN ; Get answer CALL BDOS ANI 5FH ; Make upper case CPI 03 ; Check for Control-C abort JZ FIN1 CPI 'Y' ; If answer is yes, then erase CZ ERASE LHLD DIRCNT ; Finished yet? DCX H SHLD DIRCNT MOV A,H ; Finished when DIRCNT reaches zero ORA L JZ FIN2 LHLD DIRPTR ; Adjust table pointer LXI D,32 DAD D JMP DISP1 ;..... ; ; ; Erase the file specified ; ERASE: LHLD DIRPTR ; Get the file name to be erased LDA FCB1 ; Get drive MOV M,A ; And move into table area LDA ROFLG ; Check for R/O status ORA A JZ ERAS1 ; No R/O file PUSH H ; Save file pointer LXI D,9 DAD D MOV A,M ; Change R/O attribute bit ANI 7FH MOV M,A POP H PUSH H ; Recover, save pointer again XCHG MVI C,SETATR ; Alter file attributes CALL BDOS POP H ; Recover saved pointer ; ERAS1: XCHG MVI C,DELETF ; Erase the specified file CALL BDOS LHLD ERACNT ; Count files erased INX H SHLD ERACNT RET ;..... ; ; FIN1: CALL CRLF LXI D,MSG3 ; "Aborted" message CALL STROUT ; if kaypro mvi e,curup ; eat-up one line for asthetics call output endif ; FIN2: CALL CRLF call crlf ; display logged du: LDA DRIVE ADI 'A' MOV E,A CALL OUTPUT ; Display drive LDA USER MOV L,A MVI H,0 MOV D,H CALL PRND2 ; print user number MVI E,':' ; And a colon CALL OUTPUT ; CALL SHOWK ; Calculate amount of free space left ; ; ; Print the amount of free space remaining on the selected drive ; CALL PRNDEC ; Print decimal amount LXI D,KMSG ; And then print 'k' message CALL STROUT LHLD ERACNT ; How many did we erase? (gosh, an orig idea) CALL PRNDEC ; Print value of HL in decimal LXI D,MSG8 ; Turn up some lines CALL STROUT ; Print the message after the value ; IF RESET JMP 0000H ENDIF ; RESET ; EXIT: LHLD STACK ; Get orignal CCP return address SPHL RET ; Go back to CCP, no warm boot needed ;..... ; ; ; Calculate free space left on selected drive (gee, who thought of that) ; SHOWK: LDA DRIVE ; Get current drive MVI C,SELDSK MOV E,A CALL BDOS MVI C,CURDPB ; Request current DPB CALL BDOS ; From BDOS (returned in HL) INX H ; Skip SPT INX H MOV A,M ; Get block shift factor STA BLKSHF ; And save it INX H INX H ; Skip block mask INX H ; Skip extent mask MOV E,M ; Get maximum block number INX H MOV D,M PUSH D ; And save it MVI C,GALLOC ; Get address of allocation vector CALL BDOS ; From BDOS (returned in HL) POP D ; Restore maximum block number XCHG ; Max block number in HL, allocation INX H ; address in DE LXI B,0 ; Initialize free block count to zero ; GSPBYT: PUSH D ; Save allocation address LDAX D MVI E,8 ; Set to process 8 blocks ; GSPLUP: RAL ; Test bit JC NOTFRE ; If bit is on, the block is allocated INX B ; Add one to free block count ; NOTFRE: MOV D,A ; Save bits DCX H ; Count down blocks MOV A,L ; Are we done ORA H JZ ENDALC ; Quit if out of blocks MOV A,D ; Restore bits DCR E ; Count down 8 bits JNZ GSPLUP ; Do another bit POP D ; Bump to next byte INX D ; Of allocation vector JMP GSPBYT ; Process it ; ENDALC: POP D ; Clear stack of allocation vector pntr MOV L,C ; Copy block to HL MOV H,B LDA BLKSHF ; Get block shift factor SUI 3 ; Convert from sectors to K RZ ; Skip shifts if 1K blocks ; ; Return free in HL FREKLP: DAD H ; Multiply blocks by K/BLK DCR A JNZ FREKLP RET ;..... ; ; CNVRT: MVI E,0FFH ; Increments to zero first time used ; CNVRT1: INR E DAD B ; Subtract power of ten from binary value JC CNVRT1 ; Do it until we go negative MOV A,B CMA ; Now add back last value subtracted MOV B,A MOV A,C CMA MOV C,A INX B ; Make sure we two's complement it DAD B ; Add it back in MOV A,E ORA D ; Check for leading zeros MOV D,A ; And save it for next time MOV A,E ; Exit routine with digit in A RET ; PRNDEC: MVI D,0 ; Set for leading zero LXI B,-1000 CALL CNVRT ; Convert to decimal JZ PRND1 ; Don't print if leading zero CALL PRTDIG ; Print thousands digit ; PRND1: LXI B,-100 CALL CNVRT ; Convert to decimal JZ PRND2 ; Don't print if leading zero CALL PRTDIG ; Print hundreds digit ; PRND2: LXI B,-10 CALL CNVRT ; Convert to decimal JZ PRND3 ; Don't print if leading zero CALL PRTDIG ; Print tens digit ; PRND3: MOV A,L ; One's digit was left in L ; ; Print it, even if it is zero ; PRTDIG: ADI '0' ; Convert to ASCII MOV E,A ; Get in 'A' for output routine PUSH D PUSH H CALL OUTPUT ; Print it POP H POP D RET ; CRLF: MVI E,CR ; Print a carriage return CALL OUTPUT MVI E,LF ; Print a line feed CALL OUTPUT RET ;..... ; ; ; Print the string at (HL) for (A) bytes to the console ; PRTSTG: PUSH PSW PUSH H MOV E,M ; Pick up character CALL OUTPUT ; Print it POP H POP PSW INX H DCR A ; Done yet? JNZ PRTSTG RET ; OUTPUT: MVI C,CONOUT ; Console output function JMP BDOS ; BDOS will return to original caller ; STROUT: MVI C,PRTBUF ; Print string function JMP BDOS ; BDOS will return to original caller ;..... ; ; ; Console messages ; KMSG: DB 'k Free - File(s) deleted: ','$' ; if kaypro MSG1: DB ' ',esc,'B0',esc,'B1',' <-ERAse y/n? ' db esc,'C1',esc,'C0',' $' endif ; if not kaypro MSG1: DB ' <-ERAse y/n? $' endif ; MSG2: DB ' file(s) erased, ','$' MSG3: DB CR,LF,' Program aborted ',CR,LF,'$' ; if kaypro MSG4: DB CR,LF,esc,'B0',' Use *.* as file match? ' db esc,'C0',' $' endif ; if not kaypro MSG4: DB ' Use *.* as file match? $' endif ; MSG5: DB 'Out of memory....',CR,LF,'Only the first $' MSG6: DB ' files will be listed.',CR,LF,'$' MSG7: DB CR,LF,' No files requested' MSG8: DB CR,LF,'$' ; if kaypro RO: DB esc,'B2','R/O',esc,'C2','$' SIGNON: DB CR,LF,esc,'B1',' ERAQ v2.0kp - 06/28/85 [sls] ' db esc,'C1',CR,LF,'$' SYS: DB esc,'B2','SYS',esc,'C2','$' endif ; if not kaypro RO: DB 'R/O$' SIGNON: DB 'ERAQ v2.0 - 06/28/85 [sls]',cr,lf,lf,'$' SYS: DB 'SYS$' endif ; ;..... ; ; ; Help guide if no files were named ; IF HELP MENU: DB CR,LF,TAB DB 'ERAQ deletes specified files from the requested' DB CR,LF,TAB DB 'drive after first showing the filename and asking' DB CR,LF,TAB DB 'if it may be deleted. It shows ''k'' remaining on' DB CR,LF,TAB DB 'the active drive and number of files deleted. It' DB CR,LF,TAB DB 'is otherwise similar to the ERA command.' DB CR,LF,CR,LF,TAB DB 'A>ERAQ *.* asks about each file on disk' DB CR,LF,TAB DB 'A>ERAQ *.ASM asks about each .ASM file' DB CR,LF,TAB DB 'A>ERAQ HELLO.DOC asks about just this one file' DB CR,LF,CR,LF,'$' ENDIF ; HELP ;..... ; ; ; Work areas ; DIRPTR: DS 2 DIRCNT: DS 2 ; Count of selected directory entries ERACNT: DS 2 ; Count of erased files ROFLG: DS 1 ; Set initial false SYSFLG: DS 1 DRIVE: DS 1 ; Drive code USER: DS 1 ; Current user number BLKSHF: DS 1 DMABUF: DS 128 DS 24 ; Area for stack ; ; ORG $+255 AND 0FF00H ; Get on page boundary ; STACK EQU $-2 ; Area for CCP stack pointer BUFFER EQU $ ; ; END