        TITLE   FAT - FAT maintenance routines
        NAME    FAT


IF 1
__TONYI__ = 1		; Space/Speed saving hacks
__TONYI__1= 1		; Space/Speed saving hacks
__TONYI__2= 1		; Space/Speed saving hacks
__TONYI_486__ = 1	; 486 pipline optimizations (8088 benign)
__TONYI_KILL_SC__ = 1	; Remove SC code
ENDIF


;
;       Microsoft Confidential
;       Copyright (C) Microsoft Corporation 1991
;       All Rights Reserved.
;
;**     FAT.ASM
;
;       Low level local device routines for performing disk change sequence,
;       setting cluster validity, and manipulating the FAT
;
;       IsEof
;       UNPACK
;       PACK
;       MAPCLUSTER
;       FATREAD_SFT
;       FATREAD_CDS
;       FAT_operation
;
;       Revision history:
;
;         AN000  version Jan. 1988
;          A001  PTM          -- disk changed for look ahead buffers
;
;       M014 - if a request for pack\unpack cluster 0 is made we write\read
;              from CL0FATENTRY rather than disk.
;

        extrn   GetCurHead:near, ScanPlace:near

        .xlist
        .xcref
        include version.inc
        include dosseg.inc
        INCLUDE DOSSYM.INC
        INCLUDE DEVSYM.INC
        include sf.inc
        include dpb.inc
        include curdir.inc
        .cref
        .list

FastDiv = TRUE

        i_need  CURBUF,DWORD
        i_need  CLUSSPLIT,BYTE
        i_need  CLUSSAVE,WORD
        i_need  CLUSSEC,DWORD           ;F.C. >32mb  ;AN000;
        i_need  THISDRV,BYTE
        i_need  THISDPB,DWORD
        i_need  DEVCALL,BYTE
        i_need  CALLMED,BYTE
        i_need  CALLRBYT,BYTE
        i_need  CALLXAD,DWORD
        i_need  CALLBPB,DWORD
        i_need  CDSADDR,DWORD
        i_need  CDSCOUNT,BYTE
        i_need  EXTERR,WORD
        i_need  EXTERRPT,DWORD
        i_need  CALLVIDM,DWORD
        i_need  ReadOp,BYTE
        i_need  FAILERR,BYTE
        i_need  ALLOWED,BYTE
        i_need  VOLCHNG_FLAG,BYTE
        i_need  HIGH_SECTOR,WORD
        i_need  DirtyBufferCount,WORD
        i_need  FIRST_BUFF_ADDR,WORD
        i_need  SC_CACHE_COUNT,WORD            ;AN001;
        i_need  CURSC_DRIVE,BYTE               ;AN001;

        i_need  CL0FATENTRY,WORD

IFDEF __TONYI__
        i_need  DirtyBufferCount,WORD
        i_need  BufferQueue,DWORD
        i_need  LastBuffer,DWORD
        i_need  FIRST_BUFF_ADDR,WORD         ; DOS 4.00 beginning of the chain  ;AN000;
ENDIF

DOSCODE SEGMENT
        ASSUME  SS:DOSDATA,CS:DOSCODE


Break <IsEOF - check the quantity in BX for EOF>
;----------------------------------------------------------------------------
;
; Procedure Name : IsEOF
;
; IsEOF - check the fat value in BX for eof.
;
;   Inputs:     ES:BP point to DPB
;               BX has fat value
;   Outputs:    JAE eof
;   Registers modified: none
;
;---------------------------------------------------------------------------

Procedure IsEof,NEAR
        ASSUME  SS:DOSDATA,CS:DOSCODE

        Assert      ISDPB,<ES,BP>,"IsEOF"
        CMP     ES:[BP.dpb_max_cluster],4096-10 ; is this 16 bit fat?
        JAE     EOF16                   ; yes, check for eof there
;J.K. 8/27/86
;Modified to accept 0FF0h as an eof. This is to handle the diskfull case
;of any media that has "F0"(Other) as a MediaByte.
;Hopely, this does not create any side effect for those who may use any value
;other than "FF8-FFF" as an Eof for their own file.
        cmp     bx,0FF0h
        je      IsEOF_other
        CMP     BX,0FF8h                ; do the 12 bit compare
IsEOF_other:
        return
EOF16:
        CMP     BX,0FFF8h               ; 16 bit compare
        return
EndProc IsEof

Break   <UNPACK -- UNPACK FAT ENTRIES>
;---------------------------------------------------------------------------
;
; Procedur Name : UNPACK
;
; Inputs:
;       BX = Cluster number (may be full 16-bit quantity)
;       ES:BP = Base of drive parameters
; Outputs:
;       DI = Contents of FAT for given cluster (may be full 16-bit quantity)
;       Zero set means DI=0 (free cluster)
;       Carry set means error (currently user FAILed to I 24)
; SI Destroyed, No other registers affected. Fatal error if cluster too big.
;
; NOTE: if BX = 0 then DI = contents of CL0FATENTRY
;
;----------------------------------------------------------------------------

IFDEF __TONYI__
;------------------------------------------------------------------
; Pull special case out of line to make normal case a fall through
;------------------------------------------------------------------
UnpackClusterZero proc near
	DOSAssume   <DS>,"UnpackClusterZero"
	mov	di, [CL0FATENTRY]	; Y: return value in CL0FATENTRY
	or	di,di 			; return z if di=0
	ret				; done
UnpackClusterZero endp
ENDIF

procedure   UNPACK,NEAR
        DOSAssume   <DS>,"UnPack"
                                        ; M014 - Start
        or      bx, bx                  ; Q: are we unpacking cluster 0
IFDEF __TONYI__
	jz	UnpackClusterZero
ELSE
        jnz     up_cont                 ; N: proceed with normal unpack
        mov     di, [CL0FATENTRY]       ; Y: return value in CL0FATENTRY
        or      di,di                   ; return z if di=0
        ret                             ; done
ENDIF
up_cont:                                ; M014 - End

        Assert      ISDPB,<ES,BP>,"Unpack"
        CMP     BX,ES:[BP.dpb_max_cluster]
        JA      HURTFAT
        CALL    MAPCLUSTER
ASSUME  DS:NOTHING
        jc      DoContext
        MOV     DI,[DI]
        JNZ     High12                  ; MZ if high 12 bits, go get 'em
        MOV     SI,ES:[BP.dpb_max_cluster]  ; MZ is this 16-bit fat?
        CMP     SI,4096-10
        JB      Unpack12                ; MZ No, go 'AND' off bits
        OR      DI,DI                   ; MZ set zero condition code, clears carry
IFDEF __TONYI__
;-------------------------------------------
; Pulled inline for speed...
; UNPACK is known to whack SI, so we
; can use it for some speed....
;-------------------------------------------
	mov	si, ss
	mov	ds, si
	return
ELSE
        JMP     SHORT DoContext         ; MZ go do context
ENDIF

High12:
        SHR     DI,1
        SHR     DI,1
        SHR     DI,1
        SHR     DI,1
Unpack12:
        AND     DI,0FFFH                ; Clears carry
DoContext:
IFDEF __TONYI__
;------------------------------------
; UNPACK is known to whack SI, so we
; can use it for some speed....
;------------------------------------
	mov	si, ss
	mov	ds, si
ELSE
        PUSH    SS
        POP     DS
ENDIF
        return

HURTFAT:
        MOV     ES:[BP.dpb_free_cnt],-1 ; Err in FAT must force recomp of freespace
        PUSH    AX
        MOV     AH,allowed_fail + 80h

;hkn; SS override
        MOV     Allowed,allowed_fail
;
; Signal Bad FAT to INT int_fatal_abort handler.  We have an invalid cluster.
;
        MOV     DI,0FFFH                ; In case INT int_fatal_abort returns (it shouldn't)
        invoke  FATAL
        CMP     AL,3
        CLC
        JNZ     OKU_RET                 ; Try to ignore bad FAT
        STC                             ; User said FAIL
OKU_RET:
        POP     AX
        return
EndProc UNPACK

Break   <PACK -- PACK FAT ENTRIES>
;----------------------------------------------------------------------------
;
; Procedure Name : PACK
;
; Inputs:
;       BX = Cluster number
;       DX = Data
;       ES:BP = Pointer to drive DPB
; Outputs:
;       The data is stored in the FAT at the given cluster.
;       SI,DX,DI all destroyed
;       Carry set means error (currently user FAILed to I 24)
;       No other registers affected
;
; NOTE: if BX = 0 then data in DX is atored in CL0FATENTRY.
;
;---------------------------------------------------------------------------

IFDEF __TONYI__
;------------------------------------------------------------------
; Pull special case out of line to make normal case a fall through
;------------------------------------------------------------------
PackClusterZero proc near
	DOSAssume   <DS>,"PackClusterZero"
	mov	[CL0FATENTRY], dx	; Y: place value in CL0FATENTRY
	ret				; done
PackClusterZero endp
ENDIF

procedure   PACK,NEAR
        DOSAssume   <DS>,"Pack"

        Assert      ISDPB,<ES,BP>,"Pack"
                                        ; M014 - start
        or      bx, bx                  ; Q: are we packing cluster 0
IFDEF __TONYI__
;------------------------------------------------------------------
; Pull special case out of line to make normal case a fall through
;------------------------------------------------------------------
	jz	PackClusterZero
ELSE
        jnz     p_cont                  ; N: proceed with normal pack
        mov     [CL0FATENTRY], dx       ; Y: place value in CL0FATENTRY
        ret                             ; done
ENDIF
p_cont:                                 ; M014 - end

        CALL    MAPCLUSTER
    ASSUME DS:NOTHING
        JC      DoContext
        MOV     SI,[DI]
        JZ      Aligned                 ; byte (not nibble) aligned
IFDEF __TONYI__
;-------------------------------
; Go for speed.......
;-------------------------------
	add	dx, dx	; *2
	add	dx, dx	; *4
	add	dx, dx	; *8
	add	dx, dx	; *16
ELSE
        PUSH    CX                      ; move data to upper 12 bits
        MOV     CL,4
        SHL     DX,CL
        POP     CX
ENDIF
        AND     SI,0FH                  ; leave in original low 4 bits
        JMP     SHORT PACKIN
ALIGNED:
        CMP     ES:[BP.dpb_max_cluster],4096-10 ; MZ 16 bit fats?
        JAE     Pack16                  ; MZ yes, go clobber original data
        AND     SI,0F000H               ; MZ leave in upper 4 bits of original
IFDEF __TONYI__
;-------------------------------------
; All we really need to do is strip DH.
; Just doing it, is faster/smaller...
;-------------------------------------
	and	dh, 0FH			; MZ store only 12 bits
ELSE
        AND     DX,0FFFh                ; MZ store only 12 bits
ENDIF
        JMP     SHORT PackIn            ; MZ go store
Pack16:
        XOR     SI,SI                   ; MZ no original data
PACKIN:
        OR      SI,DX
        MOV     [DI],SI

;hkn; SS override
        LDS     SI,[CURBUF]
        TESTB   [SI.buf_flags],buf_dirty  ;LB. if already dirty         ;AN000;
        JNZ     yesdirty                  ;LB.    don't increment dirty count   ;AN000;
IFDEF __TONYI__
;-------------------------------------
; Pulled inline for speed.........
;-------------------------------------
        inc	DirtyBufferCount
ELSE
        invoke  INC_DIRTY_COUNT           ;LB.                                  ;AN000;
ENDIF
        OR      [SI.buf_flags],buf_dirty  ;LB.                                  ;AN000;
yesdirty:                                 ;LB.                                  ;AN000;

;hkn; SS override
        CMP     BYTE PTR [CLUSSPLIT],0

;hkn; SS is DOSDATA
IFDEF __TONYI__
	FASTcontext ds, dx
ELSE
        Context DS
ENDIF
        retz                            ; Carry clear
        PUSH    AX
        PUSH    BX
        PUSH    CX
        MOV     AX,[CLUSSAVE]
IFDEF __TONYI_486__
	ADD	SI,BUFINSIZ
	MOV	DS,WORD PTR [CURBUF+2]
ASSUME	DS:NOTHING
	MOV	[SI],AH
ELSE
        MOV     DS,WORD PTR [CURBUF+2]
ASSUME  DS:NOTHING
        ADD     SI,BUFINSIZ
        MOV     [SI],AH
ENDIF

;hkn; SS is DOSDATA
IFDEF __TONYI__1
;------------------------------------------------------
; Reorg for speed..........
;------------------------------------------------------
	lds	dx, dword ptr ss:[CLUSSEC]
	PUSH	AX			; 486 pipeline reorg!
	mov	ss:[HIGH_SECTOR],ds
	FASTcontext ds,si
ELSE
        Context DS
        PUSH    AX
        MOV     DX,WORD PTR [CLUSSEC+2]            ;F.C. >32mb                 ;AN000;
        MOV     WORD PTR [HIGH_SECTOR],DX          ;F.C. >32mb                 ;AN000;

        MOV     DX,WORD PTR [CLUSSEC]
ENDIF
        MOV     SI,1
        XOR     AL,AL
        invoke  GETBUFFRB
        POP     AX
        JC      POPP_RET
        LDS     DI,[CURBUF]
ASSUME  DS:NOTHING
        TESTB   [DI.buf_flags],buf_dirty  ;LB. if already dirty                 ;AN000;
        JNZ     yesdirty2                 ;LB.    don't increment dirty count   ;AN000;
IFDEF __TONYI__
;-----------------------------------
; Pulled inline for speed......
;-----------------------------------
        inc	DirtyBufferCount
ELSE
        invoke  INC_DIRTY_COUNT           ;LB.                                  ;AN000;
ENDIF
        OR      [DI.buf_flags],buf_dirty
yesdirty2:
IFDEF __TONYI__
;-------------------------------------
; Why waste 2 instructions?...
; BUFINSIZ is a compile time constant.
;-------------------------------------
	add	di, (BUFINSIZ - 1)
ELSE
        ADD     DI,BUFINSIZ
        DEC     DI
ENDIF

        ADD     DI,ES:[BP.dpb_sector_size]
IFDEF __TONYI_486__
	CLC
	MOV	[DI],AL
ELSE
        MOV     [DI],AL
        CLC
ENDIF

POPP_RET:
IFDEF __TONYI__
;-------------------------------
; Go for speed........
;-------------------------------
	mov	cx, ss
	mov	ds, cx
ELSE
        PUSH    SS
        POP     DS
ENDIF
        POP     CX
        POP     BX
        POP     AX
        return

EndProc PACK

Break   <MAPCLUSTER - BUFFER A FAT SECTOR>
;---------------------------------------------------------------------------
;
; Procedure Name : MAPCLUSTER
;
; Inputs:
;       ES:BP Points to DPB
;       BX Is cluster number
; Function:
;       Get a pointer to the cluster
; Outputs:
;       DS:DI Points to contents of FAT for given cluster
;       DS:SI Points to start of buffer
;       Zero Not set if cluster data is in high 12 bits of word
;       Zero set if cluster data is in low 12 or 16 bits
;       Carry set if failed.
; SI is destroyed.
;
;---------------------------------------------------------------------------

IFDEF __TONYI__
IF FastDiv
;----------------------------------------------
; Non-512 byte sector case is pulled out of line
; to further optimize the normal case.
;----------------------------------------------
DoDiv:
	mov	dx, di			; 2
	DIV	CX			; 155 AX is FAT sector # DX is sector index
	jmp	short DivDone
ENDIF
ENDIF

procedure   MAPCLUSTER,NEAR
        DOSAssume   <DS>,"MapCluster"

        Assert      ISDPB,<ES,BP>,"MapCluster"
        MOV     BYTE PTR [CLUSSPLIT],0
        SAVE    <AX,BX,CX,DX>
        MOV     AX,BX                   ; AX = BX
        CMP     ES:[BP.dpb_max_cluster],4096-10  ; MZ 16 bit fat?
        JAE     Map16                   ; MZ yes, do 16 bit algorithm
        SHR     AX,1                    ; AX = BX/2
Map16:                                  ; MZ skip prev => AX=2*BX
        XOR     DI,DI                   ; >32mb     fat                         ;AN000;
        ADD     AX,BX                   ; AX = 1.5*fat = byte offset in fat
        ADC     DI,DI                   ; >32mb fat ;DI is zero before op;AN000;
        MOV     CX,ES:[BP.dpb_sector_size]

IF FastDiv
;
; Gross hack:  99% of all disks have 512 bytes per sector.  We test for this
; case and apply a really fast algorithm to get the desired results
;
; Divide method takes 157+4*4=173 (MOV and DIV)
; Fast method takes 39+20*4=119
;
; This saves a bunch.
;
        CMP     CX,512                  ; 4     Is this 512 byte sector?
        jne     DoDiv                   ; 4     for no jump
        MOV     DX,AX                   ; 2     get set for remainder
        AND     DX,512-1                ; 4     Form remainder
        MOV     AL,AH                   ; 2     Quotient in formation in AL
IFDEF __TONYI_486__
;---------------------------------------------------------
; This is 13% faster on a 486......
;---------------------------------------------------------
	shr	di, 1			; 2
	mov	ah, 0			; Avoid pipe stall based on CF result
	rcr	al, 1			; 2
ELSE
        shr     di, 1                   ; 2
        rcr     al, 1                   ; 2
        xor     ah, ah                  ; 3
ENDIF

IFDEF __TONYI__
	IF FastDiv
;--------------------------------------
; Label pulled above so normal 512 case 
; is a fall through.
;--------------------------------------
	ELSE
		jmp	short DivDone		; 16
	DoDiv:
	ENDIF
ELSE
	jmp	short DivDone		; 16
DoDiv:
ENDIF

ENDIF

IFDEF __TONYI__
	IF FastDiv
	;---------------------------------------------
	; Moved out of line to facilitate a fallthrough
	; on the normal 512 case...
	;---------------------------------------------
	ELSE
		mov	dx, di			; 2
		DIV	CX			; 155 AX is FAT sector # DX is sector index
	ENDIF
ELSE
	mov	dx, di			; 2
	DIV	CX			; 155 AX is FAT sector # DX is sector index
ENDIF

IF FastDiv
DivDone:
ENDIF
        ADD     AX,ES:[BP.dpb_first_FAT]
        DEC     CX                      ; CX is sector size - 1
        SAVE    <AX,DX,CX>
        MOV     DX,AX
        MOV     [HIGH_SECTOR],0         ;F.C. >32mb  low sector #
        XOR     AL,AL
        MOV     SI,1
        invoke  GETBUFFRB
        RESTORE <CX,AX,DX>              ; CX is sec siz-1, AX is offset in sec
        JC      MAP_POP
        LDS     SI,[CURBUF]
ASSUME  DS:NOTHING
        LEA     DI,[SI.BufInSiz]
        ADD     DI,AX
        CMP     AX,CX
        JNZ     MAPRET
        MOV     AL,[DI]
IFDEF __TONYI__
;------------------------------------
; Go for speed...SI is expendable.
;------------------------------------
	FASTcontext ds, si
ELSE
        Context DS                      ;hkn; SS is DOSDATA
ENDIF
        INC     BYTE PTR [CLUSSPLIT]
        MOV     BYTE PTR [CLUSSAVE],AL
        MOV     WORD PTR [CLUSSEC],DX
IFDEF __TONYI__
;---------------------------------------------------------------
; A little rearranging here can tighten up things by 
; eliminating the imbedded zero constants. GETBUFFRB() doesn't
; seem to care about AH from what I can tell, and the value in
; AX used to be a sector offset, so the high order byte left
; in AH is pretty worthless by now...
;---------------------------------------------------------------
	xor	ax, ax			 ; AL == 0
	inc	dx			; 486 pipe reorg...
	mov	word ptr [CLUSSEC+2], ax ;F.C. >32mb			;AN000;
	mov	[HIGH_SECTOR], ax	 ;F.C. >32mb  FAT sector <32mb	;AN000;
;---------------------------------------------------------------
ELSE
        MOV     WORD PTR [CLUSSEC+2],0        ;F.C. >32mb                       ;AN000;
        INC     DX
        MOV     [HIGH_SECTOR],0               ;F.C. >32mb  FAT sector <32mb     ;AN000;
        XOR     AL,AL
ENDIF
        MOV     SI,1
        invoke  GETBUFFRB
        JC      MAP_POP
        LDS     SI,[CURBUF]
ASSUME  DS:NOTHING
        LEA     DI,[SI.BufInSiz]
        MOV     AL,[DI]
IFDEF __TONYI__
;-----------------------------------------
; Go for speed...DI is expendable
;-----------------------------------------
	FASTcontext ds, di
ELSE
        Context DS                      ;hkn; SS is DOSDATA
ENDIF
        MOV     BYTE PTR [CLUSSAVE+1],AL

;hkn; CLUSSAVE is in DOSDATA
        MOV     DI,OFFSET DOSDATA:CLUSSAVE
MAPRET:
        RESTORE <DX,CX,BX>
        XOR     AX,AX                   ; MZ allow shift to clear carry
        CMP     ES:[BP.dpb_max_cluster],4096-10 ; MZ is this 16-bit fat?
        JAE     MapSet                  ; MZ no, set flags
        MOV     AX,BX
MapSet:
        TEST    AL,1                    ; set zero flag if not on boundary
        RESTORE <AX>
        return

MAP_POP:
        RESTORE <DX,CX,BX,AX>
        return

EndProc MAPCLUSTER, NoCheck

Break   <FATREAD_SFT/FATREAD_CDS -- CHECK DRIVE GET FAT>
;----------------------------------------------------------------------------
;
; Procedure Name : FATREAD_SFT
;
; Inputs:
;       ES:DI points to an SFT for the drive of intrest (local only,
;               giving a NET SFT will produce system crashing results).
;       DS DOSDATA
; Function:
;       Can be used by an SFT routine (like CLOSE) to invalidate buffers
;       if disk changed.
;       In other respects, same as FATREAD_CDS.
;       (note ES:DI destroyed!)
; Outputs:
;       Carry set if error (currently user FAILed to I 24)
; NOTE: This routine may cause FATREAD_CDS to "miss" a disk change
;       as far as invalidating curdir_ID is concerned.
;       Since getting a true disk changed on this call is a screw up
;       anyway, that's the way it goes.
;
;---------------------------------------------------------------------------

procedure  FATREAD_SFT,NEAR
        DOSAssume   <DS>,"FATRead_SFT"

        LES     BP,ES:[DI.sf_devptr]
        Assert  ISDPB,<ES,BP>,"FatReadSFT"
IFDEF __TONYI__2
FATREAD_common label near
ENDIF
        MOV     AL,ES:[BP.dpb_drive]
        MOV     [THISDRV],AL
        invoke  GOTDPB                  ;Set THISDPB
        CALL    FAT_GOT_DPB
        return

EndProc FATREAD_SFT

;----------------------------------------------------------------------------
;
; Procedure Name : FATREAD_CDS
;
; Inputs:
;       DS:DOSDATA
;       ES:DI points to an CDS for the drive of intrest (local only,
;               giving a NET or NUL CDS will produce system crashing results).
; Function:
;       If disk may have been changed, media is determined and buffers are
;       flagged invalid. If not, no action is taken.
; Outputs:
;       ES:BP = Drive parameter block
;       THISDPB = ES:BP
;       THISDRV set
;       Carry set if error (currently user FAILed to I 24)
; DS preserved , all other registers destroyed
;
;---------------------------------------------------------------------------

procedure   FATREAD_CDS,NEAR
        DOSAssume   <DS>,"FATRd_CDS"

        PUSH    ES
        PUSH    DI
        LES     BP,ES:[DI.curdir_devptr]
        Assert  ISDPB,<ES,BP>,"FatReadCDS"
IFDEF __TONYI__2
;------------------------------------------------------
; Save some space...this should be removable media only
;------------------------------------------------------
	call	FATREAD_common
ELSE
        MOV     AL,ES:[BP.dpb_drive]
        MOV     [THISDRV],AL
        invoke  GOTDPB                  ;Set THISDPB
        CALL    FAT_GOT_DPB
ENDIF
        POP     DI                      ;Get back CDS pointer
        POP     ES
        retc
        JNZ     NO_CHANGE               ;Media NOT changed

;       Media changed. We now need to find all CDS structures which use this
;       DPB and invalidate their ID pointers.

MED_CHANGE:
IFDEF __TONYI__
;---------------------------------------------------------
; This is dumb.  A MOV AX,-1 is faster on all the CPU's,
; particularly the 286, and it's the same size as XOR/DEC.
;---------------------------------------------------------
	mov	ax, -1
ELSE
        XOR     AX,AX
        DEC     AX                      ;AX = -1
ENDIF

        PUSH    DS
        MOV     CL,[CDSCOUNT]
        XOR     CH,CH                   ; CX is number of structures
        LDS     SI,ES:[DI.curdir_devptr] ; Find all CDS with this devptr
ASSUME  DS:NOTHING

;hkn; SS override

;       Find all CDSs with this DevPtr
;
;       (ax) = -1
;       (ds:si) = DevPtr

        LES     DI,CDSADDR              ; (es:di) = CDS pointer
frcd20: TESTB   ES:[DI.curdir_flags],curdir_isnet
        JNZ     frcd25                  ; Leave NET guys alone!!
        cmp     si,word ptr es:[di].curdir_devptr
        jne     frcd25                  ; no match
        mov     bx,ds
        cmp     bx,word ptr es:[di].curdir_devptr+2
        jne     frcd25                  ; CDS not for this drive
        test    ES:[DI].curdir_ID,AX
        JZ      frcd25                  ; If root (0), leave root
        MOV     ES:[DI].curdir_ID,AX    ; else invalid
frcd25: ADD     DI,SIZE curdir_list     ; Point to next CDS
        LOOP    frcd20
        POP     DS
        DOSAssume   <DS>,"FrCd25"
NO_CHANGE:
        LES     BP,THISDPB
        CLC
        return

EndProc FATREAD_CDS

Break   <Fat_Operation - miscellaneous fat stuff>

        procedure   FAT_operation,NEAR
FATERR:
        DOSAssume   <DS>,"FATERR"
        MOV     ES:[BP.dpb_free_cnt],-1 ; Err in FAT must force recomp of freespace
        AND     DI,STECODE              ; Put error code in DI
        MOV     [ALLOWED],allowed_FAIL + allowed_RETRY
        MOV     AH,2 + allowed_FAIL + allowed_RETRY ; While trying to read FAT
        MOV     AL,BYTE PTR [THISDRV]    ; Tell which drive
        invoke  FATAL1
        LES     BP,THISDPB
        CMP     AL,3
        JNZ     FAT_GOT_DPB             ; User said retry
        STC                             ; User said FAIL
        return

        public  fat_got_dpb

FAT_GOT_DPB:
        Context DS                      ;hkn; SS is DOSDATA
        MOV     AL,DMEDHL
        MOV     AH,ES:[BP.dpb_UNIT]
IFDEF __TONYI_486__
	MOV	BYTE PTR [DEVCALL.REQFUNC],DEVMDCH
	MOV	WORD PTR [DEVCALL],AX
	MOV	AL,ES:[BP.dpb_media]
	MOV	[DEVCALL.REQSTAT],0
	MOV	BYTE PTR [CALLMED],AL
ELSE
        MOV     WORD PTR [DEVCALL],AX
        MOV     BYTE PTR [DEVCALL.REQFUNC],DEVMDCH
        MOV     [DEVCALL.REQSTAT],0
        MOV     AL,ES:[BP.dpb_media]
        MOV     BYTE PTR [CALLMED],AL
ENDIF
        PUSH    ES
        PUSH    DS

;hkn; DEVCALL is in DOSDATA
        MOV     BX,OFFSET DOSDATA:DEVCALL
        LDS     SI,ES:[BP.dpb_driver_addr]  ; DS:SI Points to device header
ASSUME  DS:NOTHING
        POP     ES                      ; ES:BX Points to call header
        invoke  DEVIOCALL2
        Context DS                      ;hkn; SS is DOSDATA
        POP     ES                      ; Restore ES:BP
        MOV     DI,[DEVCALL.REQSTAT]
        .errnz  STERR-8000h
        or      di,di
        js      FATERR                  ; have error
        XOR     AH,AH
IFDEF __TONYI_486__
	MOV	AL,BYTE PTR [THISDRV]	; Use physical unit number
	XCHG	AH,ES:[BP.dpb_first_access] ; Reset dpb_first_access
ELSE
        XCHG    AH,ES:[BP.dpb_first_access] ; Reset dpb_first_access
        MOV     AL,BYTE PTR [THISDRV]   ; Use physical unit number
ENDIF
; See if we had changed volume id by creating one on the diskette
        cmp     [VOLCHNG_FLAG],AL
        jnz     CHECK_BYT
        mov     [VOLCHNG_FLAG],-1
        jmp     GOGETBPB                ; Need to get device driver to read in
                                        ; new volume label.
CHECK_BYT:
        OR      AH,BYTE PTR [CALLRBYT]
        JNS     CHECK_ZR                ; ns = 0 or 1
        JMP     short NEWDSK

CHECK_ZR:
        JZ      CHKBUFFDIRT             ; jump if I don't know
        CLC
        return                          ; If Media not changed (NZ)

DISK_CHNG_ERR:
ASSUME  DS:NOTHING
        PUSH    ES
        PUSH    BP
        LES     BP,ES:[BP.dpb_driver_addr]  ; Get device pointer
        TESTB   ES:[BP.SDEVATT],DEVOPCL ; Did it set vol id?
        POP     BP
        POP     ES
        JZ      FAIL_OPJ2               ; Nope, FAIL
        PUSH    DS                      ; Save buffer pointer for ignore
        PUSH    DI
        Context DS                      ;hkn; SS is DOSDATA
        MOV     [ALLOWED],allowed_FAIL + allowed_RETRY
        PUSH    ES
        LES     DI,[CALLVIDM]           ; Get volume ID pointer
        MOV     WORD PTR [EXTERRPT+2],ES
        POP     ES
        MOV     WORD PTR [EXTERRPT],DI
        MOV     AX,error_I24_wrong_disk
        MOV     [READOP],1              ; Write
        invoke  HARDERR
        POP     DI                      ; Get back buffer for ignore
        POP     DS
ASSUME  DS:NOTHING
        CMP     AL,3
FAIL_OPJ2:
        JZ      FAIL_OP
        JMP     FAT_GOT_DPB             ; Retry

CHKBUFFDIRT:
        DOSAssume   <DS>,"ChkBuffDirt"
        assume  DS:NOTHING      ; BUGBUG - why this assume after the DOSASSUME?
        cmp     [DirtyBufferCount], 0           ; any dirty buffers ? ;hkn;
        je      NEWDSK                          ; no, skip the check
        call    GetCurHead                      ; get pointer to first buffer
nbuffer:
        cmp     [di].buf_ID, al                 ; Unit OK ?
        jne     lfnxt                           ; no, go for next buffer
        TESTB   [di].buf_flags, buf_dirty       ; is the buffer dirty ?
        jz      lfnxt                           ; no, go for next buffer
IFDEF __TONYI_486__
;----------------------------------------------------------
; This is 7% faster on a 486 and benign on the others
;----------------------------------------------------------
	Context486  ds, <clc>
ELSE
        Context DS
        clc
ENDIF
        ret

FAIL_OP:                                        ; This label & code is here
IFDEF __TONYI_486__
;----------------------------------------------------------
; This is 7% faster on a 486 and benign on the others
;----------------------------------------------------------
	Context486  ds, <stc>
ELSE
        Context DS                              ;  for reachability
        STC
ENDIF
        return

        assume  DS:NOTHING
lfnxt:
        mov     di, [di].buf_next               ; get next buffer
        cmp     [FIRST_BUFF_ADDR], di           ; is this where we started ?;hkn;
        jne     nbuffer                         ; no, check this guy also

; If no dirty buffers, assume Media changed
NEWDSK:
        mov     ES:[bp].dpb_free_cnt, -1        ; Media changed, must
                                                ;  recompute
        call    GetCurHead
nxbuffer:
        cmp     [di].buf_ID, al                 ; This drive ?
        jne     lfnxt2
        TESTB   [di].buf_flags, buf_dirty
        DLJNZ   DISK_CHNG_ERR
        mov     word ptr [di].buf_ID, (buf_visit SHL 8) OR 0FFh ; free up
        call    ScanPlace
        jmp     short skpbuff
lfnxt2:
        mov     di, [di].buf_next
skpbuff:
        cmp     di, [FIRST_BUFF_ADDR]                                   ;hkn;
        jne     nxbuffer

IFNDEF __KILL_SC__
        CMP     [SC_CACHE_COUNT],0      ;LB.  look ahead buffers ?                ;AN001;
        JZ      GOGETBPB                ;LB.  no                                  ;AN001;
        CMP     AL,[CURSC_DRIVE]        ;LB.  same as changed drive               ;AN001;
        JNZ     GOGETBPB                ;LB.  no                                  ;AN001;
        MOV     [CURSC_DRIVE],-1        ;LB.  invalidate look ahead buffers       ;AN000;
ENDIF

GOGETBPB:
        LDS     DI,ES:[BP.dpb_driver_addr]
        TESTB   [DI.SDEVATT],ISFATBYDEV
        JNZ     GETFREEBUF
        context DS                      ;hkn; SS is DOSDATA
        MOV     BX,2
        CALL    UNPACK                  ; Read the first FAT sector into CURBUF
FAIL_OPJ:
        JC      FAIL_OP
        LDS     DI,[CURBUF]
ASSUME  DS:NOTHING
        JMP     SHORT GOTGETBUF

GETFREEBUF:
ASSUME  DS:NOTHING
IFDEF __TONYI_486__
;-------------------------------------------------------
; 486 pipeline optimization
;-------------------------------------------------------
	XOR	DX,DX			     ;LB.  fake to get 1st		  ;AN000;
	PUSH	ES			; Get a free buffer for BIOS to use
	PUSH	BP
ELSE
        PUSH    ES                      ; Get a free buffer for BIOS to use
        PUSH    BP
        XOR     DX,DX                        ;LB.  fake to get 1st                ;AN000;
ENDIF

;hkn; SS override
        MOV     [HIGH_SECTOR],DX             ;LB.  buffer addr                    ;AN000;
        invoke  GETCURHEAD                   ;LB.                                 ;AN000;

        invoke  BUFWRITE
        POP     BP
        POP     ES
        JC      FAIL_OPJ
GOTGETBUF:
        ADD     DI,BUFINSIZ

;hkn; SS override
IFDEF __TONYI_486__
;-------------------------------------------------------
; 486 pipeline optimization and other stuff......
;-------------------------------------------------------
	MOV	WORD PTR [CALLXAD+2],DS
	FASTcontext  ds, ax			;hkn; SS is DOSDATA
	MOV	WORD PTR [CALLXAD],DI
	MOV	AL,DBPBHL
	MOV	AH,BYTE PTR ES:[BP.dpb_UNIT]	; Split DS load/ref for 486

	MOV	BYTE PTR [DEVCALL.REQFUNC],DEVBPB
	MOV	WORD PTR [DEVCALL],AX
	MOV	[DEVCALL.REQSTAT],0
	MOV	AL,BYTE PTR ES:[BP.dpb_media]
	PUSH	ES
	PUSH	DS
	MOV	[CALLMED],AL
ELSE
        MOV     WORD PTR [CALLXAD+2],DS
        Context DS                      ;hkn; SS is DOSDATA
        MOV     WORD PTR [CALLXAD],DI
        MOV     AL,DBPBHL
        MOV     AH,BYTE PTR ES:[BP.dpb_UNIT]
        MOV     WORD PTR [DEVCALL],AX
        MOV     BYTE PTR [DEVCALL.REQFUNC],DEVBPB
        MOV     [DEVCALL.REQSTAT],0
        MOV     AL,BYTE PTR ES:[BP.dpb_media]
        MOV     [CALLMED],AL
        PUSH    ES
        PUSH    DS
ENDIF

IFDEF __TONYI__
;-------------------------------------------------------
; This push/push/pop/pop stuff is grotesque!
; How about a simple LDS SI, ES:[BP.dpb_driver_addr]
; This is over 3X faster and a lot smaller.
;-------------------------------------------------------
	MOV	BX,OFFSET DOSDATA:DEVCALL
	lds	si, dword ptr ES:[BP.dpb_driver_addr]
ELSE
        PUSH    WORD PTR ES:[BP.dpb_driver_addr+2]
        PUSH    WORD PTR ES:[BP.dpb_driver_addr]

;hkn; DEVCALL is in DOSDATA
        MOV     BX,OFFSET DOSDATA:DEVCALL
        POP     SI
        POP     DS                      ; DS:SI Points to device header
ENDIF
ASSUME  DS:NOTHING
        POP     ES                      ; ES:BX Points to call header
        invoke  DEVIOCALL2
        POP     ES                      ; Restore ES:BP
IFDEF __TONYI__
	FASTcontext ds, di
ELSE
        Context DS                      ;hkn; SS is DOSDATA
ENDIF
        MOV     DI,[DEVCALL.REQSTAT]

        .errnz  STERR-8000h
        or      di,di
        js      FATERRJ                 ; have error
        MOV     AL,BYTE PTR ES:[BP.dpb_media]
        LDS     SI,[CALLBPB]
ASSUME  DS:NOTHING
        MOV     ES:[BP].DPB_next_free,0 ; recycle scanning pointer
        invoke  $SETDPB

;hkn; SS override
        LDS     DI,[CALLXAD]            ; Get back buffer pointer
        MOV     AL,BYTE PTR ES:[BP.dpb_FAT_count]
        MOV     [DI.buf_wrtcnt-BUFINSIZ],AL   ;>32mb                            ;AN000;
        MOV     AX,ES:[BP.dpb_FAT_size]       ;>32mb                            ;AC000;
        MOV     [DI.buf_wrtcntinc-BUFINSIZ],AX   ;>32mb Correct buffer info     ;AC000;

IFDEF __TONYI__
	FASTcontext ds, ax
ELSE
        Context DS                      ;hkn; SS is DOSDATA
ENDIF
        XOR     AL,AL                   ;Media changed (Z), Carry clear
        return

FATERRJ: JMP    FATERR

EndProc FAT_operation

DOSCODE ENDS
        END
