INTERRUPTS SEGMENT AT 0H ORG 9H*4 ;holds the address of its service routine KEYBOARD_INT LABEL DWORD ORG 21H*4 ;This is to use INT 21H INT_21H LABEL DWORD ;which is the DOS function call interrupt INTERRUPTS ENDS ROM_BIOS_DATA SEGMENT AT 40H ;BIOS statuses held here, also keyboard buffer ORG 1AH HEAD DW ? ;Unread chars go from Head to Tail TAIL DW ? BUFFER DW 16 DUP (?) ;The buffer itself BUFFER_END LABEL WORD ROM_BIOS_DATA ENDS CODE_SEG SEGMENT ASSUME CS:CODE_SEG ORG 100H ;ORG = 100H to make this into a ;".com" file FIRST: JMP LOAD_INT_21H COPY_RIGHT DB '(C)1985 S Holzner' BYPASS_FLAG DB 0 ;Bypass our checking for #? 1=Yes ZERO_FLAG DB 0 ;Was there a zero in filename? 1=Yes CR_FLAG DB 0 DTA DD ? ;The old disk transfer area address OLD_INT_21H DD ? ;Address INT 21H uses normally OLD_KEYBOARD_INT DD ? ;Location of old kbd interrupt COUNT DW ? FCB_OFFSET DW ? ;Offset of given filename to be deleted FCB_SEG DW 0 ;Segment address of the same. COMMAND_INDEX DW 0 DEL_Z DB 'DEL/Z',0 CRLF DB 13,10,'$' ;Carriage return, linefeed for messages MSG DB ' ZEROED AND DELETED.',13,10,'$' ;The message ERR DB 'Error deleting $' ;Error message DELZ PROC FAR ;The function call interrupt will now come here. PUSHF ;Save flags first (will get changed by CMPs) CMP AH,13H ;Are we deleting? JNE JUMP ;No, jump to function call Int and do not return CMP ZERO_FLAG,1 ;Are we supposed to zero? JNE JUMP ;If not, don't TEST BYPASS_FLAG,1 ;We are deleting. Is bypass on? JZ DEL_CHECK ;No, check if we should delete file. JUMP: POPF ;Restore flags JMP OLD_INT_21H ;Jump to function call Int. (CALL won't work). DEL_CHECK: ;DS:DX are pointing to filename to be deleted (from delete call) PUSH BX ;Save all used registers to be polite PUSH CX PUSH DX PUSH SI PUSH DI PUSH AX ;Save AX last since will pop first and return status in it MOV FCB_OFFSET,DX ;Save address of the file-to-be-deleted's FCB MOV FCB_SEG,DS ;Ditto for the segment address MOV AH,2FH ;Now get Disk Transfer Area (DTA) address INT 21H ;This will work since AH is not equal to 13H MOV DTA,BX ;Store DTA address Low part MOV DTA[2],ES ;Ditto High part PUSH DS ;Save file-to-be-deleted's FCB's Segment address MOV AH,1AH ;Put the new DTA in our Program Segment Prefix, MOV DX,80H ; CS:0080H (CS came from INT 21H vector we set) PUSH CS ;Now move DS into CS to set DTA POP DS INT 21H ;Set Disk Transfer Area (DTA) POP DS ;Restore Segment address of given file's FCB MOV DX,FCB_OFFSET ;Restore the given file's FCB's address low part MOV AH,11H ;Ask for DOS service 11H, which asks for the INT 21H ; first match to the given file's FCB TEST AL,1 ;Was a match found? JZ TOP ;Yes, start checking if we should delete. POP AX ;No, return status 0FF (not found) in AL MOV AL,0FFH JMP NONE_FOUND ;Over and out. TOP: MOV SI,81H ;the matching file's FCB is in DTA from search MOV DI,0C0H ;We will move the name to print and scan for # MOV CX,0BH ;11 characters per file PUSH DS ;Get ready for string move, set up DS and ES PUSH CS ;Set them both to CS (use program segment POP DS ; prefix area) MOV DX,80H ;Point to match to open file MOV AH,0FH ;Select the correct DOS call INT 21H CMP AL,0 ;If error opening file, exit JNE ERROR MOV BX,80H ;Set record size to 512 MOV WORD PTR [BX+14],512 MOV AX,[BX+16] ;Find the file's size in sectors XOR DX,DX TEST AX,511 ;Do we have to add an add'l sector? JZ SHIF ;No, do the shift INC DX ;Yes, add 1 SHIF: MOV CL,9 ;Divide by 512 SHR AX,CL MOV COUNT,AX ;Store in Count ADD COUNT,DX ;And add possible add'l sector MOV AX,[BX+18] ;Now for the high part of size MOV CL,7 ;Mult by 65536 div by 512 SHL AX,CL ADD COUNT,AX ;And add to what we already have MOV DX,80H ;Now prepare for sequential write MOV CX,COUNT ;Do COUNT sectors MOV AH,15H FILL: INT 21H ;Fill with copies of this prog. CMP AL,0 ;Error writing? JNE ERROR ;Yes, jump to ERROR LOOP FILL ;No, go back for next one MOV AH,10H ;Close the file now. INT 21H CMP AL,0 ;Error closing? JNE ERROR ;Yep, go to ERROR MOV AH,13H ;No, delete the file at last (Whew) MOV BYPASS_FLAG,1 ;Don't intercept this call INT 21H MOV BYPASS_FLAG,0 CMP AL,0 ;Everything OK? JNE ERROR ;No, go to ERROR CALL PRINT_NAME ;Yes, print the name LEA DX,MSG ;And the zeroed message MOV AH,9 INT 21H JMP SAVE ERROR: MOV AH,10H ;First make sure file is closed INT 21H LEA DX,ERR ;Then print error message and go on MOV AH,9 ; to next file INT 21H CALL PRINT_NAME SAVE: POP DS ;Get segment of original given file's FCB MOV DX,FCB_OFFSET ;Search for next match -- point to original FCB MOV AH,12H ;Search for next match INT 21H TEST AL,1 ;Was a match found? JNZ OUT JMP TOP OUT: POP AX ;At least one file deleted, set AL acordingly, MOV AL,0 ; which means set it to 0 NONE_FOUND: PUSH DS ;Now we have to reset the Disk Transfer Area PUSH AX ;Save AX since it contains success status MOV AH,1AH ;Function call 1AH will do want we want MOV DX,DTA[2] ;Get original DTA's segment into DS MOV DS,DX MOV DX,DTA ;Now get offset inside that segment of same INT 21H ;And reset DTA POP AX ;Restore AX with status POP DS ;And DS with original DS POP DI ;And restore the other registers POP SI POP DX POP CX POP BX POPF ;We musn't forget our original PUSHF IRET ;An interrupt deserves an IRET DELZ ENDP ;And that's it PRINT_NAME PROC NEAR ;This small subroutine just prints MOV BX,80H+1 ; file's name from the FCB MOV AH,2 ;Use DOS service 2 MOV CX,11 ;Print all 11 letters PRINT: MOV DL,[BX] ;Printing loop INT 21H INC BX ;Get next letter LOOP PRINT RET ;And return PRINT_NAME ENDP READ_KEY PROC NEAR ;The keyboard interrupt will now come here. ASSUME CS:CODE_SEG PUSH AX ;Save the used registers for good form PUSH BX PUSH CX PUSH DX PUSH DI PUSH SI PUSH DS PUSHF ;First, call old keyboard interrupt CALL OLD_KEYBOARD_INT ASSUME DS:ROM_BIOS_DATA ;Examine the char just put in MOV BX,ROM_BIOS_DATA MOV DS,BX MOV BX,TAIL ;Point to current tail CMP BX,HEAD ;If at head, kbd int has deleted char JNE CR ;So leave JMP NOCR CR: SUB BX,2 ;Point to just read in character CMP BX,OFFSET BUFFER ;Did we undershoot buffer? JAE NO_WRAP ;Nope MOV BX,OFFSET BUFFER_END ;Yes -- move to buffer top SUB BX,2 NO_WRAP:MOV DX,[BX] ;Char in DX now CMP DL,'Z' ;Make sure we are in upper case JBE CHAROK SUB DL,'a'-'A' ;Make REALLY sure. CHAROK: PUSH CS POP DS ;Point to Code Seg with DS ASSUME DS:CODE_SEG CMP CR_FLAG,1 ;CR_Flag resets Zero_Flag JNE CHECK MOV CR_FLAG,0 ;Reset CR_Flag MOV ZERO_FLAG,0 ;And Zero_Flag MOV COMMAND_INDEX,0 ;As well as Command_Index CHECK: LEA SI,DEL_Z ;Check the typed character ADD SI,COMMAND_INDEX ;Find place in test string CMP DL,[SI] ;Match? JNE NOSET ;If not, forget it INC COMMAND_INDEX ;Match! Move to next char next time CMP DL,'/' ;For DOS 3+, delete the /Z from buffer JNE NOTSLSH ASSUME DS:ROM_BIOS_DATA ;Examine the char just put in MOV CX,ROM_BIOS_DATA MOV DS,CX MOV TAIL,BX ;Erase character from buffer MOV AH,10 ;Get ready to print the character MOV CX,1 MOV AL,DL XOR BX,BX ;Display page 0 INT 10H ;Print the '/' MOV AH,3 ;Now prepare to move cursor over 1 INT 10H ;Get present position ADD DX,1 ;Add 1 MOV AH,2 ;And reset cursor INT 10H NOTSLSH:CMP DL,'Z' ;For DOS 3+, delete the /Z from buffer JNE NOTZ ASSUME DS:ROM_BIOS_DATA ;Examine the char just put in MOV CX,ROM_BIOS_DATA MOV DS,CX MOV TAIL,BX ;Erase character from the buffer MOV AH,10 ;Prepare to type the 'Z' MOV CX,1 MOV AL,DL XOR BX,BX INT 10H MOV AH,3 ;And now adjust the cursor INT 10H ;Moving it to the left 1 space ADD DX,1 MOV AH,2 INT 10H NOTZ: ASSUME DS:CODE_SEG PUSH CS POP DS CMP BYTE PTR [SI+1],0 JNE NOSET MOV ZERO_FLAG,1 MOV COMMAND_INDEX,0 NOSET: MOV CR_FLAG,0 CMP DX,1C0DH JNE NOCR MOV CR_FLAG,1 NOCR: POP DS POP SI POP DI POP DX POP CX POP BX POP AX IRET READ_KEY ENDP LOAD_INT_21H PROC NEAR ;This subroutine installs DELZ ASSUME DS:INTERRUPTS ;Now set DS to point to INTERRUPTS seg. MOV AX,INTERRUPTS MOV DS,AX MOV AX,KEYBOARD_INT ;Get the old interrupt service routine MOV OLD_KEYBOARD_INT,AX ;address and put it into our location MOV AX,KEYBOARD_INT[2] ;OLD_KEYBOARD_INT so we can call it. MOV OLD_KEYBOARD_INT[2],AX MOV KEYBOARD_INT,OFFSET READ_KEY MOV KEYBOARD_INT[2],CS ;routine into the keyboard interrupt MOV AX,INT_21H ;Get the original function call INT's MOV OLD_INT_21H,AX ;address and put it into our location MOV AX,INT_21H[2] ;OLD_INT_21H so we can still jump there MOV OLD_INT_21H[2],AX MOV INT_21H[2],CS ;Install our delete filter's address MOV INT_21H,OFFSET DELZ ; as new function call INT PUSH CS ;Now point to CS in preparation for POP DS ; terminate and stay resident call MOV DX,OFFSET LOAD_INT_21H ;Set up everything but LOAD_INT_21H to INT 27H ;stay and attach itself to DOS LOAD_INT_21H ENDP ;End of loading subroutine CODE_SEG ENDS ;End of Code Segment END FIRST ;END "FIRST" so 8088 will go to FIRST first.