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.















