; DISKSCAN.ASM -- Checks out disk by reading sectors ; -------------------------------------------------- CSEG Segment Assume CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG Org 100h Entry: Jmp Begin ; All Data ; -------- db ' Copyright 1986 Ziff-Davis Publishing Co.' db ' Programmed by Charles Petzold ' DriveError db 'Invalid Drive$' DosVersErr db 'Needs DOS 2.0+$' MemoryError db 'Needs 64K$' ReadSegment dw ? DriveNum db ? DiskBlock db 18 dup (?) TotalSectors dw ? SectorsIn64K dw ? StartSector dw 0 SectorLabel2 db 9,'Sector $' SectorLabel db 13,'Sectors $' DashLabel db ' - $' ErrorLabel db ': Error!' CRLF db 13,10,'$' ErrorAddr dw Err0,Err1,Err2,Err3,Err4,Err5,Err6,Err7 dw Err8,Err9,ErrA,ErrB,ErrC,ErrD,ErrD,ErrD Err0 db 'Write Protect$' Err1 db 'Unknown Unit$' Err2 db 'Drive Not Ready$' Err3 db 'Unknown Command$' Err4 db 'CRC Error$' Err5 db 'Request Length$' Err6 db 'Seek Error$' Err7 db 'Unknown Media$' Err8 db 'Sector Not Found$' Err9 db 'No Paper$' ErrA db 'Write Fault$' ErrB db 'Read Fault$' ErrC db 'General Failure$' ErrD db 'Undocumented Error$' BootSectMsg db 'Boot Sector$' RootDirMsg db 'Root Directory$' BadFatMsg db 'File Alloc. Table$' InUseMsg db 'Used by file$' NotInUseMsg db 'Unallocated$' BadFlagMsg db 'Flagged as bad$' FatReadMsg db "Can't Read FAT$" Divisors dw 10000, 1000, 100, 10, 1 ; For decimal conversion ; Check Drive Parameter, DOS Version, and Enough Memory ; ----------------------------------------------------- ErrorExit: Mov AH,9 ; Write error message Int 21h ; through DOS Int 20h ; And terminate Begin: Mov DX, Offset DriveError ; Possible message Or AL, AL ; Check Drive Validity Byte Jnz ErrorExit ; If not zero, invalid drive Mov DX, Offset DosVersErr ; Possible message Mov AH, 30h Int 21h ; Get DOS Version Number Cmp AL, 2 ; Check for 2.0 or later Jb ErrorExit ; If not, terminate with message Mov DX, Offset MemoryError ; Possible error message Mov BX, 256+Offset EndProg ; Set beyond program Mov SP, BX ; Move stack closer to code Add BX, 15 ; Add 15 to round up Mov CL, 4 ; Divide BX by 16 Shr BX, CL Mov AH, 4Ah ; Free allocated memory Int 21h ; by calling DOS Set Block Jc ErrorExit ; Terminate on error Mov BX, 1000h ; Ask for 64K bytes Mov AH, 48h ; by using DOS Int 21h ; Allocate Memory call Jc ErrorExit ; Terminate on error Mov [ReadSegment], AX ; Save segment of memory block ; Get Disk Information From DOS ; ----------------------------- Mov DL, DS:[005Ch] ; Get Drive Parameter Push DS ; Save DS Mov AH, 32h ; Call DOS to Int 21h ; get DOS Disk Block (DS:BX) Mov SI, BX ; Now DS:SI points to Disk Block Mov DI, Offset DiskBlock ; DI points to destination Mov CX, 18 ; 18 bytes to copy' Cld ; Forward direction Rep Movsb ; Move 'em in Pop DS ; Get back DS Mov BX, Offset DiskBlock ; BX to address Disk Block Mov DX, 1 ; Set DX:AX to 65,536 Sub AX, AX Div Word Ptr [BX + 2] ; Divide by Bytes Per Sector Mov [SectorsIn64K], AX ; Save that values Mov AX, [BX + 13] ; Last Cluster Number Dec AX ; AX = Number of Clusters Mov CL, [BX + 5] ; Cluster to Sector Shift Shl AX, CL ; AX = Number Data Sectors Add AX, [BX + 11] ; Add First Data Sector Mov [TotalSectors], AX ; AX = Number Total Sectors Mov AL, DS:[005Ch] ; Drive Number (0=def, 1=A) Dec AL ; Make it 0=A, 1=B Jns GotDriveNumber ; If no sign, not default drive Mov AH, 19h ; Get current disk Int 21h ; by calling DOS GotDriveNumber: Mov [DriveNum], AL ; Save Drive Number (0=A, 1=B) ; Start Reading ; ------------- MainLoop: Mov DX, Offset SectorLabel ; String to display on screen Call StringWrite ; Display it Mov AX, [StartSector] ; Starting sector number Call WordWrite ; Display number on screen Mov DX, Offset DashLabel ; String containing a dash Call StringWrite ; Display it on the screen Mov CX, [SectorsIn64K] ; Number of sectors to read Add AX, CX ; Add it to starting sector Jc NumRecalc Cmp AX, [TotalSectors] ; See if bigger than total Jbe NumSectorsOK ; If so, proceed NumRecalc: Mov AX, [TotalSectors] ; Otherwise get total sectors Mov CX, AX ; Move it to CX also Sub CX, [StartSector] ; Now CX = sectors to read NumSectorsOK: Dec AX ; AX = last sector to read Call WordWrite ; Display it on screen Call ReadSectors ; Read the sectors Jnc NextSectors ; If no error, skip detail Call ReadSectors ; Repeat read Jnc NextSectors ; If still no error, skip DiskError: Mov DX, Offset ErrorLabel ; String saying "Error!" Call StringWrite ; Display it on screen ErrorLoop: Push CX ; Now save previous number Mov CX, 1 ; So we can read one at a time Call ReadSectors ; Read one sector Jnc NoError ; If no error, proceed Mov BL, AL ; Save error code Mov DX, Offset SectorLabel2 ; String with "Sector " Call StringWrite ; Display it on screen Mov AX, [StartSector] ; The sector we just read Call WordWrite ; Display it on screen Mov DX, Offset DashLabel ; String with a dash Call StringWrite ; Display it on screen And BL, 0Fh ; Blank out error top bits Sub BH, BH ; Now BX is error code Add BX, BX ; Double it for word access Mov DX, [ErrorAddr + BX] ; Get address of message Call StringWrite ; Display message on screen Call FindSector ; See where sector is Mov DX, Offset CRLF ; String for new line Call StringWrite ; Do carriage ret & line feed NoError: Inc [StartSector] ; Kick up the start sector Pop CX ; Get back counter Loop ErrorLoop ; And read next sector Mov AX, [StartSector] ; Sector of next group Jmp Short CheckFinish ; Check if at end yet NextSectors: Mov AX, [StartSector] ; For no error, increment Add AX, [SectorsIn64K] ; StartSector for next group Jc Terminate ; (If overflow, terminate) Mov [StartSector], AX ; And save it CheckFinish: Cmp AX, [TotalSectors] ; See if at then end Jae Terminate ; If so, just terminate Jmp MainLoop ; If not, do it again Terminate: Int 20h ; Terminate ; Find Sector in FAT to see if used by file, etc. ; ----------------------------------------------- FindSector: Mov DX, Offset DashLabel ; Print dash Call StringWrite Mov AX, [StartSector] ; Sector with error Mov DX, Offset BootSectMsg ; Set up message Cmp AX, Word Ptr [DiskBlock + 6] ; See if sector boot Jb PrintMsg ; If so, print as such Mov DX, Offset BadFatMsg ; Set up message Cmp AX, Word Ptr [DiskBlock + 16] ; See if sector in FAT Jb PrintMsg ; If so, print as such Mov DX, Offset RootDirMsg ; Set up message Cmp AX, Word Ptr [DiskBlock + 11] ; See if sector in dir Jb PrintMsg ; If so, print as such Push [StartSector] ; Save the sector Mov AX, Word Ptr [DiskBlock + 6] ; Reserved sectors Mov [StartSector], AX ; Start of first FAT Mov CL, [DiskBlock + 15] ; Sectors for FAT Sub CH, CH ; Zero out top byte Call ReadSectors ; Read in FAT Pop [StartSector] ; Get back bad sector Mov DX, Offset FatReadMsg ; Set up possible msg Jc PrintMsg ; If read error, print Mov AX, [StartSector] ; Get bad sector Sub AX, Word Ptr [DiskBlock + 11] ; Subtract data start Mov CL, [DiskBlock + 5] ; Sector Shift Shr AX, CL ; Shift the sector Add AX, 2 ; AX is now cluster Push ES ; Save ES for awhile Mov ES, [ReadSegment] ; ES segment of FAT Cmp Word Ptr [DiskBlock + 13], 0FF0h; 12 or 16-bit FAT? Jge Fat16Bit ; And jump accordingly Mov BX, AX ; This is cluster number Mov SI, AX ; So is this Shr BX, 1 ; This is one-half cluster Mov AX, ES:[BX + SI] ; BX + SI = 1.5 CX Jnc NoShift ; If no CY from shift, got it Mov CL, 4 ; If CY from shift must Shr AX, CL ; shift word 4 bits right NoShift: Or AX, 0F000h ; Now put 1's in top bits Cmp AX, 0F000h ; See if zero otherwise Jmp Short CheckWord ; And continue checking Fat16Bit: Mov BX, AX ; This is cluster number Shl BX, 1 ; Double it Mov AX, ES:[BX] ; Pull out word from sector Or AX, AX ; See if zero (unallocated) CheckWord: Pop ES ; Get back ES Mov DX, Offset NotInUseMsg ; Set up possible message Jz PrintMsg ; If so, print message Mov DX, Offset BadFlagMsg ; Set up possible message Cmp AX, 0FFF7h ; See if cluster flagged bad Jz PrintMsg ; If so, print message Mov DX, Offset InUseMsg ; If not, cluster is in use PrintMsg: Call StringWrite ; Print cluster disposition Ret ; And return ; Read Sectors (CX = Number of Sectors, Return CY and AL for error) ; ----------------------------------------------------------------- ReadSectors: Push BX ; Push all needed registers Push CX Push DX Push DS Mov AL, [DriveNum] ; Get the drive number code Sub BX, BX ; Buffer address offset Mov DX, [StartSector] ; Starting Sector Mov DS, [ReadSegment] ; Buffer address segment Int 25h ; Absolute Disk Read Pop BX ; Fix up stack Pop DS ; Get back registers Pop DX Pop CX Pop BX Ret ; Return to program ; Screen Display Routines ; ----------------------- WordWrite: Push AX ; Push some registers Push BX ; AX contains word to display Push CX Push DX Push SI Mov SI, Offset Divisors ; SI points to divisors Mov CX, 4 ; CL counter; CH zero blanker WordWriteLoop: Mov BX, [SI] ; Get divisor Add SI, 2 ; Increment SI for next one Sub DX, DX ; Prepare for division Div BX ; Divide DX:AX by BX Push DX ; Save remainder Or CH, AL ; See if zero Jz LeadZero ; If so, do not display it Add AL, '0' ; Convert number to ASCII Mov DL, AL ; Print out character Mov AH, 2 ; by calling DOS Int 21h LeadZero: Pop AX ; Get back remainder Dec CL ; Decrement counter Jg WordWriteLoop ; If CL still > 0, do it again Mov CH, 1 ; No more zero blanking Jz WordWriteLoop ; Convert last digit to ASCII Pop SI ; Get back pushed registers Pop DX Pop CX Pop BX Pop AX Ret StringWrite: Push AX ; Displays string from DX Mov AH, 9 ; to screen by calling DOS Int 21h Pop AX Ret EndProg Label Byte ; End of program CSEG EndS End Entry