PAGE 60,132 ; USE BIOS DISK VERIFY FUNCTION TO CHECK INTEGRITY OF DISKETTES. ; RICH WINKEL 6/8/85 ; verdisk.asm 11 Jul 85 Craig Milo Rogers at USC/ISI ; Enhanced this program to support hard disks, too. I have ; to assume that the hard disks start with DOS device "C:". The ; error analysis needs more work... the original program seemed to ; think that the error byte was bit-encoded, which it isn't. Check ; the IBM-PC/AT's Technical Reference for the latest and greatest ; list of potential error codes. Also, this program does NOT support ; IBM's 8-in. diskettes. CR EQU 0DH ;CARRIAGE RETURN LF EQU 0AH ;LINE FEED FLPNUM EQU 2 ; Number of floppies before hard disk. ; Assume this program is run as a .COM file. VERDISK SEGMENT ASSUME CS:VERDISK,DS:VERDISK ORG 5CH FCB1 DB ? ; First FCB in the PSP. ORG 100H ; Start of program execution. MAIN PROC NEAR JMP START ; Skip past the data area. ; Floppy disk parameter table: ; Disk type: DS8S, SS8S, DS9S, SS9S, DS15S (quad density) DSKIDT DW 0FFH, 0FEH, 0FDH, 0FCH, 0F9H ; Disk ID bytes. DSKIDL EQU ($-DSKIDT)/2 SIDES DW 1, 0, 1, 0, 1 ; Sides per disk. TRACKS DW 39, 39, 39, 39, 79 ; Tracks per side. SECTORS DW 8, 8, 9, 9, 15 ; Sectors per track. HRDID DB 0F8H ; Hard (fixed) disk ID. ; EACH DISK ERR MESSAGE MUST BE 16 BYTES LONG! DSKMSGS DB CR,LF,'Timeout $';TIMEOUT 80H DB CR,LF,'Drive seek $';DRIVE SEEK 40H DB CR,LF,'Controller $';CONTROLLER ERROR 20H DB CR,LF,'CRC $';CRC 10H DB CR,LF,'Bad Sector $';DMA or Bad Sector 08H DB CR,LF,'Sector NF $';MISSING SECTOR 04H DB CR,LF,'Addr mark NF $';MISSING ADDRESS MARK 02H DB CR,LF,'VERDISK $';BAD CMD SHOULD NEVER HAPPEN 01H ; EM1 DB 'Invalid drive specified.$' EM2 DB 'Error: disk not supported, or invalid FAT ID found.$' IMSG1 DB 'Verifying $' IMSG2 DB ' side(s), $' IMSG3 DB ' tracks, $' IMSG4 DB ' sectors per track:$' PIDMSG DB 'error at side, track, sector: $' RETRYM1 DB '; OK on retry $' RETRYM2 DB '; still bad after 5 retries$' DONEMSG1 DB CR,LF,'No disk errors found.$' DONEMSG2 DB CR,LF,'Verdisk done.$' DEATHCRY DB CR,LF,'Verdisk terminating!!$' VERDRV DB ? ;DRIVE TO VERIFY NUMSIDES DB ? ;NUMBER OF SIDES NUMTRKS DW ? ;NUMBER OF TRACKS NUMSECT DB ? ;NUMBER OF SECTORS CURTRK DW ? ; Current track. RC DB 0 ;RETURN CODE FOR DOS START: CMP AL,0FFH ;DRIVE SPEC INVALID?? JNZ LL1 MOV DX,OFFSET EM1 ;THEN COMPLAIN CALL PRSTR MOV AX,4C08H ;INDICATE COMMAND ERR IN RC INT 21H LL1: MOV DL,FCB1 ;GET DISK ID IF SPECIFIED DEC DL JNS L1 ;JUMP IF DRIVE SPECIFIED MOV AH,19H ;OTHERWISE, GET CURRENT DRIVE AND INT 21H ;USE THAT MOV DL,AL ;PUT IT IN DL L1: MOV VERDRV,DL ;SAVE DRIVE TO VERIFY INC DL ; Convert drive to 1's origin. MOV AH,1CH ; Get FAT ID byte loc in DS:BX. INT 21H MOV AL,[BX] ; Get the FAT ID byte in AL. XOR AH,AH ; Zero-extend into AH. MOV DX,CS ; Restore our DS pointer. MOV DS,DX MOV DI,OFFSET DSKIDT; Point to disk ID table. MOV CX,DSKIDL ; Length of table. CLD ; Clear direction flag, REPNE SCASW ; Scan for a match. JE GOTFLP ; (found it) CMP AL,HRDID ; Is this a hard disk? JNE UNKNWN ; (nope) ; We are looking at a hard disk. GOTHRD: MOV AL,VERDRV ; Get drive number. SUB AL,FLPNUM ; Subtract number of floppies. ADD AL,80H ; Convert to hard disk unit number. MOV VERDRV,AL ; Save for future use. MOV DL,AL ; Copy unit number for BIOS call. MOV AH,08H ; Return drive parms function. INT 13H ; Call Fixed Disk BIOS. JC UNKNWN ; (not a hard disk after all) MOV NUMSIDES,DH ; Get the number of heads. MOV AL,CL ; Get the number of sectors. AND AL,03FH ; Clear cylinder bits. MOV NUMSECT,AL ; Save the number of sectors. MOV AL,CH ; Get the number of cylinders. MOV AH,CL ; Get high 2 bits. MOV CL,6 ; Shift count to extract high 2 bits. SHR AH,CL ; Extract them. MOV NUMTRKS,AX ; Save the number of cylinders. JMP SHOSIZ ; Go print disk size. ; We ar looking at a floppy disk. Get the number of tracks, ; etc. from our table. GOTFLP: SUB DI,OFFSET DSKIDT+2 ; Adjust for offset within table. MOV AX,SIDES[DI] ; Get the number of sides per disk. MOV NUMSIDES,AL ; Save in NUMSIDES. MOV AX,TRACKS[DI] ; Get the number of tracks per side. MOV NUMTRKS,AX ; Store in NUMTRKS. MOV AX,SECTORS[DI] ; Get the number of sectors per track. MOV NUMSECT,AL ; Store in NUMSECT. JMP SHOSIZ ; Go print disk size. ; The disk type is unknown, give up: UNKNWN: MOV DX,OFFSET EM2 ;COMPLAIN IF NO MATCH CALL PRSTR MOV AX,4C04H ;INDICATE SEVERE ERROR. INT 21H ;BACK TO DOS ; Tell the user how big the disk is: SHOSIZ: MOV DX,OFFSET IMSG1 ;PRINT DISK INFO MSG CALL PRSTR ; 'Verifying' MOV AL,NUMSIDES ; Get number of sides (heads) per cylinder. INC AL ; Convert to 1's origin. CALL BASE10PR0 ;PRINT IT MOV DX,OFFSET IMSG2 CALL PRSTR ; 'sides,' MOV AX,NUMTRKS ; Get the number of tracks per side. INC AX ; Convert to 1's origin. CALL BASE10PR ;PRINT IT MOV DX,OFFSET IMSG3 CALL PRSTR ; 'tracks,' MOV AL,NUMSECT ; Get the number of sectors per track. CALL BASE10PR0 ;PRINT IT MOV DX,OFFSET IMSG4 CALL PRSTR ; 'sectors per track:' MOV DL,VERDRV ;GET DRIVE NUMBER MOV CURTRK,0 ; Start with track 0. ; ; MAIN LOOP ; DRIVE=DL, SIDE=DH, TRACK=CH, SECTOR=CL, NUMBER OF SECTORS TO VER=AL LOOP0: MOV DH,0H ;START ON SIDE 0 LOOP1: MOV AL,BYTE PTR CURTRK+1; Get track number high bits. MOV CL,6 ; Number of bits to shift. SHL AL,CL ; Move high bits into position. OR AL,01H ; Start with sector 1. MOV CL,AL ; Copy into proper register. MOV CH,BYTE PTR CURTRK; Get track number low bits. MOV AL,NUMSECT ; Verify NUMSECT sectors. MOV AH,04H INT 13H ;DO THE OPERATION OR AH,AH ;CHECK FOR ZERO RC JNZ BADVER ;NO, THEN COMPLAIN L3: INC DH ;NEXT SIDE CMP DH,NUMSIDES ; Are we done with this cylinder? JLE LOOP1 ;NO, THEN BACK FOR MORE MOV AH,0BH ;CHECK FOR CTRL-BREAK INT 21H ;VIA INPUT STATUS CALL. INC CURTRK ;INCREMENT TRACK MOV AX,CURTRK ; Get new track number. CMP AX,NUMTRKS ;CHECK FOR LAST TRACK JLE LOOP0 ;GO BACK FOR MORE ; ; DONE, SO EXIT MOV BL,RC ;CHECK RC OR BL,BL JNZ DOS MOV DX,OFFSET DONEMSG1 ; 'NO DISK ERRORS FOUND.' CALL PRSTR DOS: MOV DX,OFFSET DONEMSG2 ; 'VERDISK DONE' CALL PRSTR MOV AH,4CH MOV AL,BL ;GET RETURN CODE FLAGS INT 21H ; ; BADVER USES CL, BL & AX. BADVER: MOV AX,0401H ;TEST TRACK ONE SECTOR AT A TIME INT 13H ;DO IT OR AH,AH ;CHECK FOR ERROR JNZ L5 L4: INC CL ;INC SECTOR NUMBER PUSH CX ; Save sector and track. AND CL,03FH ; Mask out track bits CMP CL,NUMSECT ; CHECK FOR LAST SECTOR POP CX ; Restore track and sector. JLE BADVER ;GO DO ANOTHER JMP L3 ; L5: OR RC,01H ;INDICATE DISK ERR IN RC CALL PRERR MOV BL,'1' ;TRY A FEW RETRIES RETRY: MOV AX,0401H INT 13H OR AH,AH JZ RETRYSUC ;THAT ONE WAS OK, SO PRT MSG & LEAVE INC BL ;ELSE CHECK COUNTER CMP BL,'5' JLE RETRY ;TRY AGAIN PUSH DX ;ELSE PRINT MSG & LEAVE MOV DX,OFFSET RETRYM2 CALL PRSTR POP DX OR RC,02H ;INDICATE NON-RECOVERABLE ERR IN RC JMP L4 ; RETRYSUC:PUSH DX MOV DX,OFFSET RETRYM1 CALL PRSTR MOV DL,BL CALL PRCHAR POP DX JMP L4 ; ; PRINT SIDE, TRACK, SECTOR & ERROR ; SIDE=DH, TRACK=CH, SECTOR=CL ; CX, DX, AX, BL, DI ARE USED; CX & DX ARE RESTORED. PRERR: PUSH CX ;SAVE THEM REGS PUSH DX PUSH CX ;SAVE EM AGAIN PUSH DX MOV BL,AH ;DISK STATUS FLAGS TO BL MOV BH,AH ;DISK STATUS FLAGS TO BH MOV CX,8H ;CHECK ON 8 BITS LOOP2: OR BL,BL ;SET CPU FLAGS JS L6 ;JUMP IF HI BIT IS 1 JZ L7 ;JUMP IF NO 1'S LEFT SHL BL,1 LOOP LOOP2 ;DO THE SAME FOR EVERY BIT JMP SHORT L7 ; L6: MOV DX,08H ;CALC OFFSET OF MSG; EACH MSG IS SUB DX,CX ;16 BYTES LONG SHL DX,1 ;MULT BY 16 SHL DX,1 SHL DX,1 SHL DX,1 ADD DX,OFFSET DSKMSGS CALL PRSTR ;PRINT IT ;***** SHL BL,1 ;***** LOOP LOOP2 ;DO THE SAME FOR EVERY BIT L7: MOV DX,OFFSET PIDMSG ;PREFACE TO SIDE, TRK, SECTOR ID MSG CALL PRSTR POP AX ;RECOVER SIDE IN AH MOV AL,AH ; Move it into AL. CALL BASE10PR0 ;PRINT IT MOV DL,',' ;PRINT A COMMA CALL PRCHAR POP AX ;RECOVER TRACK AND SECTOR PUSH AX ; Save it again. XCHG AH,AL ; Properly order high and low parts. MOV CL,6 ; Shift count for upper track bits. SHR AH,CL ; Reposition high 2 bits of track count. CALL BASE10PR ; Print the track number. MOV DL,',' CALL PRCHAR ;PRINT A COMMA POP AX ; Recover sector number (and sector). AND AL,03FH ; Mask out sector number high bits. CALL BASE10PR0 ;PRINT IT POP DX ;RECOVER SIDE, TRK, SECTOR POINTERS POP CX TEST BH,0A1H ;TST FOR TMOUT, NEC, WP OR BAD CMD JNZ L8 TEST BH,40H ; Test for bad seek. JNZ L9 RET ; L8: MOV DX,OFFSET DEATHCRY ;SOMETHING WEIRD HAPPENING .. CALL PRSTR MOV AX,4C04H ;INDICATE SEVERE ERROR. OR AL,RC ;ADD ON ANY OTHERS INT 21H ;GO WHILE THE GOIN'S GOOD ; L9: MOV AH,0H ;RESET DISK DRIVES IF SEEK ERR INT 13H RET ; BASE10PR0: ; Number to print in AL. XOR AH,AH ; Zero-extend it. BASE10PR: ; Number to print in AX. PUSH BX ; Save temp reg. XOR BL,BL ; Leading zeros flag. MOV CX,10000 ; Start with 10000's digit. BLOOP: XOR DX,DX ; Clear the number extension. DIV CX PUSH DX ; Save remainder. CMP AL,BL ; Leading zero? JE BSKIP ; (yup) MOV BL,0FFH ; Mark no more leading zeros. OR AL,30H ; Convert to ASCII. MOV DL,AL ; Put in DL. MOV AH,02H ;PRINT DL INT 21H BSKIP: MOV AX,CX ; Get the current digit divisor. XOR DX,DX ; Zero-extend to 32 bits. MOV CX,10 ; Divide divisor by 10. DIV CX MOV CX,AX ; Save divisor for next decimal digit. POP AX ; Restore the remainder. CMP CX,1 ; Final digit? JNE BLOOP ; (nope) OR AL,30H ; Convert final digit to ASCII. MOV DL,AL ; Put in DL. MOV AH,02H ; Print DL. INT 21H POP BX ; Restore temp reg. RET ; All done. ; PRSTR: MOV AH,09 INT 21H RET ; PRCHAR: MOV AH,02 INT 21H RET ; MAIN ENDP VERDISK ENDS END MAIN