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

