Category : Assembly Language Source Code
Archive   : ASM4.ZIP
Filename : UNDEL.ASM
Output of file : UNDEL.ASM contained in archive : ASM4.ZIP
COMMENT*--------------------------------------------------------
This program will undelete files. To use, type UNDEL FILE.EXT or
UNDEL DRIVE:FILE.EXT where DRIVE is A or B. For ASCII files use UNDEL/A
FILE.EXT or UNDEL/A DRIVE:FILE.EXT; this prints out what is in each
cluster before it adds it to the file and asks (Y/N) if it's correct.
Only for use with DOS 2.0 and 2.10 double sided disks.
---------------------------------------------------------------*
FCB_LOC EQU 5CH ;Location of FCB for deleted file in the PSP
SWITCH_LOC EQU 81H ;Location of text typed after 'UNDEL'
BIG_A EQU 412FH ;Test chars for /A
SMALL_A EQU 612FH ;Test chars for /a
CR EQU 13 ;ASCII carriage return
LF EQU 10 ;ASCII line feed
CODE_SEG SEGMENT
ASSUME CS:CODE_SEG,DS:CODE_SEG,ES:CODE_SEG
ORG 100H ;To make this a .com file
ENTRY: JMP FIRST ;Skip over data area
COPY_RIGHT DB '(C) 1984 S HOLZNER'
I_O_FLAG DW ? ;Selects read or write of cluster
START_CLUSTER DW ? ;Beginning cluster of deleted file
FILE_SIZE DW ? ;File size in clusters (1 cluster=2 sectors)
DISK_DRIVE DB 0 ;Drive of deleted file
NOT_FOUND_MSG DB 13,'File not found deleted$' ;Messages
WRITTEN_OVER_MSG DB 13,'File already written over$'
IS_IT_MSG DB 13,10,'Is part of your file here? (Y/N) $'
DATA DB 1024 DUP(0) ;Space for disk directory and FAT
PROMPT_SECTOR DB 512 DUP(0) ;/A option uses for parts of file
FIRST: ;Start the process
UNDEL PROC NEAR
MOV AL,CS:FCB_LOC ;Get the drive specified - 0 if none given
SUB AL,1 ;Was it a 1 (A:) or 2 (B:)?
JNC DRIVE_KNOWN ;Yes, store drive number
MOV AH,19H ;No, get default drive from INT 21H
INT 21H
DRIVE_KNOWN:
MOV DISK_DRIVE,AL ;Store drive
MOV DX,3 ;Start to search dir, starts at sector 5
LOOP: ADD DX,2 ;Add two to point at correct dir cluster
CMP DX,11 ;Past end of directory?
JB READ_DIR ;If not, read dir into data area
MOV AH,9 ;Past end of dir & no match, exit with error
LEA DX,NOT_FOUND_MSG
INT 21H
JMP OUT ;Exit
READ_DIR:
AND I_O_FLAG,0 ;Select read
CALL CLUSTER_I_O ;Get two sectors into data area
LEA DI,DATA ;Prepare to search for deleted entry
MOV AL,0E5H ;DOS set first char of deleted entry to E5H
MOV CX,400H ;Counter=1024 to search this entire dir cluster
SEARCH: ;Look-for-next-deleted-entry loop
REPNE SCASB
JCXZ LOOP ;If no match (Counter=0), get next dir cluster
MOV SI,FCB_LOC+2 ;Possible match, point to 2nd char of file name
MOV BX,11 ;Compare all chars in name
CMPLOOP:DEC BX ;Decrement the name comparison loop counter
CMPS [DI],[SI] ;Compare dir entry and deleted file name
JZ CMPLOOP ;If match, check next char
CMP BX,0 ;Compare done, all chars matched perfectly?
JNZ SEARCH ;No, keep checking through this dir cluster
MOV AX,CS:FCB_LOC+1 ;Yes, get file's first letter
MOV [DI-12],AX ;Move it into deleted entry (replace E5H)
OR I_O_FLAG,1 ;Select a write of 1 cluster
CALL CLUSTER_I_O ;Write directory with undeleted name to disk
MOV AX,[DI+14] ;Get starting cluster to use in FAT
MOV START_CLUSTER,AX ;Store it
MOV AX,[DI+16] ;Get low word of file size (in bytes)
TEST AX,1023 ;Find # of clusters - is MOD(size,1024)=0?
JZ EVEN_K ;Yes, don't add 1 cluster before SHR
ADD AX,1024 ;No, need another cluster
EVEN_K: MOV CL,10 ;Divide by 1024 (=cluster size)
SHR AX,CL
MOV DX,[DI+18] ;High word of file size
MOV CL,6 ;Multiply by 2^16=65536, divide by 2^10=1024
SHL DX,CL
ADD AX,DX ;Add high word clusters to low word clusters
MOV FILE_SIZE,AX ;And store in FILE_SIZE
MOV DX,1 ;Read in File Allocation Table (FAT)
AND I_O_FLAG,0 ;Select a read of 1 cluster
CALL CLUSTER_I_O
MOV CX,FILE_SIZE ;Counter to loop over # of clusters
MOV AX,START_CLUSTER ;Check if written over already
DEC AX ;Move back one cluster from beginning of file
CALL GET_NEXT_ZERO ;Is next empty space in FAT the START_CLUSTER?
CMP DX,START_CLUSTER
JE FILL ;Yes, OK to start filling FAT
MOV AH,13H ;No, file written over
MOV DX,FCB_LOC ;Delete restored dir entry; point to file name
INT 21H ;And delete it
MOV AH,9 ;Exit with error
LEA DX,WRITTEN_OVER_MSG
INT 21H
JMP OUT ;So long
FILL: MOV AX,DX ;Set old empty space in FAT to one just found
MOV DX,0FFFH ;Assume this is last entry (FFFH=End of file)
CMP CX,1 ;Is it the last entry? (CX=cluster counter)
JZ LAST ;Yes, don't need to find next empty entry (zero)
CALL GET_NEXT_ZERO ;Call with AX=old zero, returns DX=new zero
LAST: CALL PUT_FAT_ENTRY ;Call with AX=old zero, DX=new zero, changes FAT
LOOP FILL ;Work on next cluster
MOV DX,1 ;Prepare to write new FAT, 1st copy
OR I_O_FLAG,1 ;Select to write 1 cluster
CALL CLUSTER_I_O ;Write the cluster
MOV DX,3 ;Prepare to write new FAT, 2nd copy
CALL CLUSTER_I_O
OUT: INT 20H ;And leave
UNDEL ENDP
CLUSTER_I_O PROC NEAR ;Reads specified cluster (dir, FAT etc.)
COMMENT* Put start sector in DX, loads Cluster into 'DATA' area*
PUSH AX ;Save the used registers
PUSH BX ;INT 25H destroys all reg.s
PUSH CX
PUSH DX
PUSH DI
MOV AL,DISK_DRIVE ;Get disk drive
MOV CX,2 ;Request 2 sectors (1 cluster) to be read
LEA BX,DATA ;Point to DATA area
TEST I_O_FLAG,1
JNZ WRITE
INT 25H ;Read sector interrupt
JMP POPOUT
WRITE: INT 26H
POPOUT: POPF ;Pop the extra push of flags
POP DI ;Pop used (destroyed) registers
POP DX
POP CX
POP BX
POP AX
RET ;Return
CLUSTER_I_O ENDP
GET_NEXT_ZERO PROC NEAR ;Unravel FAT and find next empty space in it
COMMENT* FAT entry number (cluster #) in AX, returns next zero in DX*
PUSH AX ;Push used reg.s
PUSH BX
PUSH CX
CHECK_NEXT: ;Entry by entry loop
INC AX ;AX is entry pointer, start with next entry
MOV BX,AX ;Get 3/2*AX for actual offset into FAT
SHL BX,1 ; (Since each entry is 1.5 bytes)
ADD BX,AX
SHR BX,1 ;BX has FAT offset value for entry # in AX
MOV DX,WORD PTR DATA[BX] ;DX now has FAT entry's value
TEST AX,1 ;Is the entry # even?
JZ EVEN_ENTRY ;Yes, use bottom 12 bits
MOV CL,4 ;No, use top 12 bits
SHR DX,CL
EVEN_ENTRY:
AND DX,0FFFH ;Get bottom 12 bits (OK now for even or odd)
CMP DX,0 ;Is the value for the given cluster # 0?
JNE CHECK_NEXT ;No, look for next cluster
MOV DX,AX ;Move entry value of zero into AX
CMP AX,START_CLUSTER ;If this is the 1st one, skip /A option
JE POPS
MOV BX,SWITCH_LOC ;Was /A specified? Check data area in PSP
MOV CX,[BX]
CMP CX,SMALL_A ;Check for /a
JE A_OPTION ;Yes, do /A
CMP CX,BIG_A ;No, maybe a /A?
JNE POPS ;No, leave.
A_OPTION: ;/A was specified
CALL PRINT_OUT ;Print out prompt sector
JCXZ CHECK_NEXT ;If CX set to 0, sector wasn't right
POPS: POP CX ;The pops before going
POP BX
POP AX
RET
GET_NEXT_ZERO ENDP
PRINT_OUT PROC NEAR ;Print out a prompt sector
COMMENT* Returns CX=1 if found the right sector, 0 otherwise *
PUSH DX ;Push used reg
ADD DX,DX ;Get 2*DX for sector number
ADD DX,8 ;Add 8 to skip boot, FATs and dir
MOV AL,DISK_DRIVE ;Get disk drive
MOV CX,1 ;Ask for only 1 sector
LEA BX,PROMPT_SECTOR ;Load into PROMPT_SECTOR area
INT 25H ;Read the sector
POPF ;Pop extra flags put on by INT 25H
MOV AH,2 ;Prepare to print
MOV DL,CR ;Send a carriage return
INT 21H
MOV DL,LF ;Send a line feed
INT 21H
MOV BX,0 ;Initialize char printout counter
PRINT_LOOP: ;Like it says
MOV DL,PROMPT_SECTOR[BX] ;Get char from read-in cluster
INT 21H ;Print char
INC BX ;Go on to next char
CMP BX,160 ;Done 160 Chars yet?
JBE PRINT_LOOP ;No; go back for more
MOV AH,9 ;Yes, print Prompt message
LEA DX,IS_IT_MSG
INT 21H
MOV AH,1 ;Get a char from keyboard
INT 21H
MOV CX,1 ;Assume found right sector (always optimistic)
CMP AL,'Y' ;Was typed char a 'Y'?
JE FOUND ;Yes, found right sector exit with CX still=0
CMP AL,'y' ;No; was it a 'y'?
JE FOUND ;Yes, leave with CX still = 0
MOV CX,0 ;Not found, exit with CX=1
FOUND: POP DX ;Pop destroyed DX reg
RET
PRINT_OUT ENDP
PUT_FAT_ENTRY PROC NEAR ;Writes new FAT entry into FAT in DATA area
COMMENT* Pass FAT cluster number in AX and new entry in DX*
PUSH AX ;The requisite Pushes
PUSH BX
PUSH CX
PUSH DX
MOV BX,AX ;Get offset into FAT, 3*AX/2
SHL BX,1 ;Multiply by 2
ADD BX,AX ;Add AX to get 3*AX
SHR BX,1 ;Divide by 2 -- BX has FAT offset value
TEST AX,1 ;Do we have an even entry number?
JZ P_EVEN_ENTRY ;Yes, use bottom 12 bits
MOV CL,4 ;No, use top 12 bits
SHL DX,CL
P_EVEN_ENTRY:
OR WORD PTR DATA[BX],DX ;Put Cluster # into FAT
POP DX ;Do the Pops
POP CX
POP BX
POP AX
RET ;Return and you're done
PUT_FAT_ENTRY ENDP
CODE_SEG ENDS
END ENTRY ;This sets the starting address to ENTRY
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/