PAGE ,132
TITLE Bad Cluster Mark Program, Version 2.31, 12-Jan-1987

;
; Written By Steven Georgiades
;
; Mark/UnMark-Bad-Cluster Program
;   This program will allow you to mark or unmark a cluster on your disk as
;   bad.  It should work under all versions of DOS 2.00 and higher, and should
;   work on the PC, XT, AT, PCJr. and all compatibles.  The programs is invoked
;   as follows:
;
;         MARK [d:] [/U | /M | /Q] n
;
;           d:   Optional drive letter.  If this is omitted, the default
;                drive is used.  
;
;           /Q   Query switch.  The status of the following clusters will be 
;                returned.  If no switch is specified, Query is assumed.
;
;           /M   Mark switch.  The following clusters will be marked bad.
;
;           /U   Un-mark switch.  The following clusters will be un-marked.
;
;           n    Cluster Number, in decimal.  The first data cluster is 2.
; 
;
;       If you are using this program and find it of value, your
;       contribution in any amount ($10.00 suggested) will be greatly
;       appreciated.  Makes checks payable to Steven M. Georgiades.
;               Thank you.
;
;       If you have any questions or comments about this or any other
;       SMG program, write to me at the following address:
;
;               Steven M. Georgiades
;               SMG Software
;               701-H South Hayward Street
;               Anaheim, CA 92804
;

CODE      SEGMENT BYTE PUBLIC 'CODE'

          ASSUME  CS:CODE,DS:CODE,ES:CODE,SS:CODE

          ORG     80H

PARAM     LABEL   BYTE

          ORG     100H

MARK:     JMP     BEGIN

DIGIT     DB      "0123456789ABCDEF"
SIGNON    DB      "Bad Cluster Mark Program, Version 2.31",13,10
          DB      "SMG Software",13,10
          DB      "(C) Copyright 1986,1987 Steven Georgiades",13,10,10,"$"
USAGE     DB      "Usage:",13,10,10
          DB      "  MARK [d:] [/U | /M | /Q] n",13,10,10
          DB      "    d:   Optional drive letter.  "
          DB      "If this is omitted, the default drive is used.",13,10,10
          DB      "    /Q   Query switch.  "
          DB      "The status of the following clusters will be returned.",13,10
          DB      "         If no switch is specified, Query is assumed."
          DB      13,10,10
          DB      "    /M   Mark switch.  "
          DB      "The following clusters will be marked as bad.",13,10,10
          DB      "    /U   Un-mark switch.  "
          DB      "The following clusters will be un-marked.",13,10,10
          DB      "    n    Cluster Number, in decimal.  "
          DB      "The first data cluster is 2.",13,10,10,"$"
CLSTMSG   DB      "Cluster XXXXX$"
INVALID   DB      " does not exist!",7,13,10,"$"
BADMSG    DB      " is already marked as bad",13,10,"$"
NOTBAD    DB      " is not marked as bad",13,10,"$"
MARKMSG   DB      " has been marked as bad",13,10,"$"
UNMRKMSG  DB      " has been un-marked as bad",13,10,"$"
STATUS1   DB      " is available",13,10,"$"
STATUS2   DB      " is Screwed Up!!",7,13,10,"$"
STATUS3   DB      " is currently in use by a file",13,10,"$"
STATUS4   DB      " is reserved",7,13,10,"$"
STATUS5   DB      " is marked as bad",13,10,"$"
STATUS6   DB      " is currently in use by a file (EOF)",7,13,10,"$"

CLSTSEC   DW      ?
CLSTTOT   DW      ?
CLUST     DW      ?
DRIVE     DB      ?
EOF       DW      ?
FATSEC    DW      ?
FATSIZE   DB      ?
FLAG      DB      0
OLDDRV    DB      -1
SECSIZE   DW      ?

BEGIN:    MOV     AH,9                          ; Output Sign-On Message
          MOV     DX,OFFSET SIGNON
          INT     21H
          MOV     AH,19H                        ; Get Default Drive Number
          INT     21H
          MOV     DRIVE,AL                      ;   and Save
          MOV     SI,OFFSET PARAM               ; Set up Pointer to Parameter
          LODSB                                 ; Read Parameter Length
          CMP     AL,2                          ; If Length < 2,
          JAE     OK
          MOV     AH,9                          ;   Output Usage Message
          MOV     DX,OFFSET USAGE
          INT     21H
          MOV     AX,4C01H                      ;   Exit to DOS
          INT     21H
OK:       CBW
          MOV     BX,AX
          MOV     BYTE PTR [SI][BX],0           ; Terminate Parameter String
STRIP:    LODSB                                 ; Strip Off Leading Whitespace
          CMP     AL,' '
          JE      STRIP
          CMP     AL,9
          JE      STRIP
          OR      AL,AL                         ; If End-of-Parameter List,
          JNZ     NOTDONE
          MOV     AX,4C00H                      ;   Exit to DOS
          INT     21H
NOTDONE:  CMP     AL,'/'                        ; If Switch, Process
          JNE     NOSWITCH
          LODSB                                 ; Get Switch Letter
          AND     AL,0DFH                       ; Convert to Upper Case
          CMP     AL,'U'                        ; If '/U', Flag = 0
          JE      SWITCHU
          CMP     AL,'M'                        ; If '/M', Flag = 1
          JE      SWITCHM
          CMP     AL,'Q'                        ; If Not '/Q', Next Character
          JNE     STRIP
          MOV     FLAG,0                        ; Else Flag = 0
          JMP     SHORT STRIP
SWITCHU:  MOV     FLAG,1                        ; Flag = 1
          JMP     SHORT STRIP
SWITCHM:  MOV     FLAG,2                        ; Flag = 2
          JMP     SHORT STRIP
NOSWITCH: MOV     BL,AL
          LODSB                                 ; Get Drive Letter
          CMP     AL,':'
          JNE     NODRIVE
          INC     SI
          AND     BL,0DFH                       ; Convert to Upper Case
          SUB     BL,'A'                        ; Convert to Drive Number 
          MOV     DRIVE,BL                      ; Save Drive Number
          JMP     STRIP                         ; Get Next Paramter
NODRIVE:  SUB     SI,2                          ; ReUse Last Character
          CALL    GET_WORD                      ; Read Cluster Number (Hex)
          CMP     DX,2
          MOV     CLUST,DX                      ; Save Cluster Number
          MOV     AL,DRIVE                      ; If Different Drive,
          CMP     AL,OLDDRV
          JE      DRV_OK
          PUSH    SI                            ;   Save Parameter Pointer
          MOV     OLDDRV,AL                     ;   Save New Drive
          MOV     BX,OFFSET FATBUF              ;   Read Boot Record
          MOV     CX,1
          MOV     DX,0
          INT     25H
          POPF
          MOV     AX,FATBUF[11]                 ;   Read Sector Size and Save
          MOV     SECSIZE,AX
          MOV     AL,BYTE PTR FATBUF[13]        ;   Read Sectors/Cluster and Save
          XOR     AH,AH
          MOV     CLSTSEC,AX
          MOV     BX,FATBUF[22]                 ;   Read Sectors/FAT and Save
          MOV     FATSEC,BX
          MOV     AH,36H                        ;   Get Total Data Clusters
          MOV     DL,DRIVE
          INC     DL
          INT     21H
          ADD     DX,2
          MOV     CLSTTOT,DX                    ;     and Save
          MOV     FATSIZE,3
          MOV     EOF,0FF8H
          CMP     DX,4081                       ;   If Necessary, Adjust FAT Size
          JLE     FAT_OK
          MOV     FATSIZE,4
          MOV     EOF,0FFF8H
FAT_OK:   MOV     AL,DRIVE                      ;   Read FAT
          MOV     CX,FATSEC
          MOV     DX,1
          MOV     BX,OFFSET FATBUF
          INT     25H
          POPF
          POP     SI                            ;   Restore Parameter Pointer
DRV_OK:   MOV     BX,CLUST                      ; Get Cluster Number
          MOV     AX,BX                         ; Convert Cluster No. to ASCII
          MOV     DI,OFFSET CLSTMSG[13]
          CALL    DEC5OUT
          CALL    STRIP0                        ; Strip Off Leading Zeroes
          MOV     AH,9                          ; Output Cluster Message
          MOV     DX,OFFSET CLSTMSG
          INT     21H
          CMP     BX,CLSTTOT                    ; If Invalid, Say So
          JAE     CLST_BAD
          CMP     BX,2
          JAE     CLST_OK
CLST_BAD: MOV     DX,OFFSET INVALID
          JMP     SHORT ERROUT
CLST_OK:  CALL    NEXTCLST                      ; Get Next Cluster Number
          MOV     AX,EOF                        ; Get EOF Value
          CMP     FLAG,0                        ; If /Q, Query
          JE      QUERY
          CMP     FLAG,1                        ; Else If /U, Unmark
          JE      UNMARK
          OR      BX,BX                         ; Else Mark
          JZ      IS_FREE
          CMP     BX,AX                         ;   If In Use, Error
          JB      NOTINUSE
IN_USE:   MOV     DX,OFFSET STATUS3
          JMP     SHORT ERROUT
NOTINUSE: DEC     AX                            ;   If Bad, Error
          CMP     BX,AX
          JNE     NOT_BAD
          MOV     DX,OFFSET BADMSG
          JMP     SHORT ERROUT
NOT_BAD:  SUB     AX,7                          ;   If In Use, Error
          CMP     BX,AX
          JB      IN_USE
IS_RESV:  MOV     DX,OFFSET STATUS4             ;   If Reserved, Error
          JMP     SHORT ERROUT
IS_FREE:  MOV     BX,CLUST                      ;   Get Cluster Number
          MOV     AX,EOF                        ;   New Value = Bad Cluster
          DEC     AX
          MOV     DX,OFFSET MARKMSG
          JMP     WRT_FAT
QUERY:    OR      BX,BX                         ; If Cluster = 0, Free
          JNZ     NOT_FREE
          MOV     DX,OFFSET STATUS1
          JMP     SHORT ERROUT
NOT_FREE: CMP     BX,1                          ; If 1, Invalid
          JNE     NOTWEIRD
          MOV     DX,OFFSET STATUS2
          JMP     SHORT ERROUT
NOTWEIRD: CMP     BX,AX                         ; If FF8 to FFF, InUse (EOF)
          JB      NO_USE
          MOV     DX,OFFSET STATUS6
          JMP     SHORT ERROUT
NO_USE:   DEC     AX                            ; If FF7, Bad
          CMP     BX,AX
          JNE     IS_OK
          MOV     DX,OFFSET STATUS5
          JMP     SHORT ERROUT
IS_OK:    SUB     AX,7                          ; If 2 to FEF, In Use
          CMP     BX,AX
          JB      IN_USE
          JMP     SHORT IS_RESV                 ; If FF0 to FF6, Reserved
UNMARK:   DEC     AX
          CMP     BX,AX                         ; If Not Bad,
          JE      IS_BAD
          MOV     DX,OFFSET NOTBAD
ERROUT:   MOV     AH,9                          ; Output Error Message
          INT     21H
          JMP     STRIP
IS_BAD:   MOV     BX,CLUST                      ; Get Cluster Number
          XOR     AX,AX                         ; New Value = Good Cluster
          MOV     DX,OFFSET UNMRKMSG
WRT_FAT:  PUSH    DX
          CALL    SETCLST
          PUSH    SI                            ; Save Parameter Pointer
          MOV     AL,DRIVE                      ; Write FAT #1
          MOV     CX,FATSEC
          MOV     DX,1
          MOV     BX,OFFSET FATBUF
          INT     26H
          POPF
          MOV     AL,DRIVE                      ; Write FAT #2
          MOV     CX,FATSEC
          MOV     DX,CX
          INC     DX
          MOV     BX,OFFSET FATBUF
          INT     26H
          POPF
          POP     SI                            ; Restore Parameter Pointer
          MOV     AH,9                          ; Output UnMarked Message
          POP     DX
          INT     21H
          JMP     STRIP

NEXTCLST: CMP     FATSIZE,3                     ; If FAT Size = 16 Bits,
          JE      NEXT_12
          SHL     BX,1                          ;   Simply Read Next Cluster #
          MOV     BX,FATBUF[BX]
          RET                                   ;   Done
NEXT_12:  PUSH    CX                            ; Save Registers
          MOV     CX,BX                         ; Word # = Cluster # * 1.5
          SHL     CX,1
          ADD     BX,CX
          SHR     BX,1
          MOV     BX,FATBUF[BX]
          JNC     NEXT_OK                       ; If Odd, Use 12 MSB's
          MOV     CL,4
          SHR     BX,CL
NEXT_OK:  AND     BX,0FFFH                      ; Else Use 12 LSB's
          POP     CX                            ; Restore Registers
          RET                                   ; Done

SETCLST:  PUSH    AX                            ; Save Registers
          PUSH    BX
          CMP     FATSIZE,3                     ; If FAT Size = 16 Bits,
          JE      SETC_12
          SHL     BX,1                          ;   Simply Read Next Cluster #
          MOV     FATBUF[BX],AX
          JMP     SHORT SET_DONE                ;   Done
SETC_12:  PUSH    CX                            ; Save Registers
          MOV     CX,BX                         ; Word # = Cluster # * 1.5
          SHL     CX,1
          ADD     BX,CX
          SHR     BX,1
          MOV     CX,0F000H
          JNC     SETC_OK                       ; If Odd, Use 12 MSB's
          MOV     CL,4
          SHL     AX,CL
          MOV     CX,0FH
SETC_OK:  AND     FATBUF[BX],CX
          OR      FATBUF[BX],AX
          POP     CX
SET_DONE: POP     BX
          POP     AX
          RET                                   ; Done

GET_WORD: PUSH    BX                            ; Save Register
          XOR     DX,DX                         ; Value = 0
GET_WRD1: LODSB                                 ; Read Character
          CMP     AL,'0'                        ; If Not Numeric, Done
          JB      GET_WRD2
          CMP     AL,'9'
          JA      GET_WRD2
          SUB     AL,'0'                        ; Convert to BCD
          CBW
          MOV     BX,AX                         ; Value = Value*10+BCD
          MOV     AX,10
          MUL     DX
          ADD     AX,BX
          MOV     DX,AX
          JMP     GET_WRD1                      ; Repeat
GET_WRD2: DEC     SI
          POP     BX                            ; Restore Register
          RET                                   ; Done

STRIP0:   CMP     BYTE PTR [DI],'0'             ; If Character != '0', Done
          JNE     STRIP1
          CMP     BYTE PTR [DI+1],'0'           ; If Next Character != Digit,
          JL      STRIP1                        ;   Done
          CMP     BYTE PTR [DI+1],'9'
          JG      STRIP1
          MOV     BYTE PTR [DI],' '             ; Change '0' to ' '
          INC     DI                            ; Point to Next Character
          JMP     SHORT STRIP0                  ; Repeat
STRIP1:   RET                                   ; Done

DEC2OUT:  PUSH    AX                            ; Save Registers
          PUSH    BX
          XOR     AH,AH                         ; Clear AH
          MOV     BL,10                         ; AH=AX%10,AL=AX/10
          DIV     BL
          ADD     AX,'00'                       ; Convert to ASCII
          SUB     DI,2
          MOV     [DI],AX                       ; Store in String
          POP     BX                            ; Restore Registers
          POP     AX
          RET                                   ; Done

DEC4OUT:  PUSH    AX                            ; Save Registers
          PUSH    BX
          MOV     BL,100                        ; AH=AX%100,AL=AX/100
          DIV     BL
          XCHG    AH,AL                         ; Convert 2 LSD's
          CALL    DEC2OUT
          XCHG    AH,AL                         ; Convert 2 MSD's
          CALL    DEC2OUT
          POP     BX                            ; Restore Registers
          POP     AX
          RET                                   ; Done

DEC5OUT:  PUSH    AX                            ; Save Registers
          PUSH    BX
          PUSH    DX                            ; DX=AX%10000,AX=AX/10000
          MOV     BX,10000
          XOR     DX,DX
          DIV     BX
          XCHG    DX,AX                         ; Convert 4 LSD's
          CALL    DEC4OUT
          XCHG    DX,AX                         ; Convert MSD
          ADD     AL,'0'
          SUB     DI,1
          MOV     [DI],AL
          POP     DX                            ; Restore Registers
          POP     BX
          POP     AX
          RET                                   ; Done

FATBUF    LABEL   WORD

CODE      ENDS

          END     MARK
