
SCCSID  equ     "@(#)fs32api.asm        6.34 92/03/24"
TITLE   FS32API - 32 bit file system API's

;
;       This file contains the kernel routines needed to support large file
;       reads and writes.
;
;  Change History:
;==============================================================================
;  B726911 9/19/91 Steve Best Save the number of bytes read or written for
;                             dos32read and dos32write dekko hooks
;==============================================================================
;  69350  06-10-93 Rix   Make HotFixHardError display into a No-Op since it is
;                        only called from HOTFIX.C, and issues msg on popup to
;                        User (requiring operator response), which might happen
;                        to be on a remote SERVER!
;
;                        NOTE:  If needed later, these info (hotfix) messages
;                               may be written to HF Error file for User
;                               to see in a normal window session. (DCR?)
;==============================================================================
;  74069  14Oct93  Doug Azzarito
;                        When reading the FAT, as soon as we reach the
;                        last VALID cluster, we have to zero the count of
;                        sectors left in the FAT.  There is a program that
;                        can leave a FAT with extra sectors at the end,
;                        and if we try to scan them, we'll overrun the
;                        cache bitmap table.
;==============================================================================
;  76860  20Dec93  Ken Luo
;                        Rewrite AllocCluster to provide an algorithm for
;                        contiguous cluster allocation in an attempt to reduce
;                        disk fragmentation. The double-packing inherited
;                        from old non-bitmap algorithm is considered redundant
;                        and removed from the code.
;=============================================================================
;  136380 18Sep95  ChgTeam
;                        Change w_BigOper16 to call AdjustError on bad
;                        return from FSD_Read or FSD_Write.  Previously, if
;                        you read/write a character device driver and an
;                        error code is returned, this error code will get lost
;                        and ERROR_ACCESS_DENIED will be provided. This change
;                        will call AdjustError to pick up the correct error
;                        code from the TCB.
;=============================================================================
;  153143 03May96  Jim Wahlig
;                        TKForceThread changes eax so when tkSleepQRemove is
;                        called, eax is no longer valid.
;=============================================================================
;  157795 24June96 Jim Wahlig
;                        remove the call to tkSleepQRemove and the push and
;                        pop that I added for the defect above
;=============================================================================

.xlist
.xcref
include dosseg.inc
include defdbug.inc
include basemaca.inc
include osmaca.inc
include oldmaca.inc
include osconst.inc
include protmode.inc
include dossymc.inc
include error.inc
include kmode.inc
include fileio.inc
include filemode.inc
include fsc.inc
include fsh.inc
include inbuf.inc
include mi.inc
include mult.inc
include seldesc.inc
include sft.inc
include syscalls.inc
include tkexport.inc
include proc.inc
include pdb.inc
include virtsym.inc
include vpb.inc
include mft.inc
include filemacs.inc
include bpb.inc
include boot.inc
include selxport.inc
include pgexport.inc
include vmexport.inc
include vmowner.inc
include fnofs.inc
include filesys.inc
include devsym.inc
include strat2.inc
include dpb.inc
include buffer.inc
include harderr.inc

include infoseg.inc                                                    ;b726911
include mmpha.inc                                                      ;b726911
include utilmd12.inc

include bsedos.inc                                                     ;92265

.cref
.list

        ModName , FNO_FS32API_ASM
CpuMode 386

        ReferGDT        GDT_VPB,WORD

        EXTRN   _TKSuDWord:NEAR
        EXTRN   SFFromHandle:NEAR
        EXTRN   SFLock:NEAR
        EXTRN   SFUnlock:NEAR
        EXTRN   PhysHandle:NEAR
        EXTRN   FSD_Read:NEAR
        EXTRN   FSD_Write_NT:NEAR
        EXTRN   CheckRange:NEAR
        EXTRN   Bmp_Free:NEAR
        ReferTask       CurrTCB,WORD
        ReferTask       CurrTSD,WORD
        ReferTask       AutoFail,BYTE
        ReferGDT        GDT_RLR,WORD
        ReferFlat       _pTCBCur,dword
        ReferFlat       _pPTDACur,dword
        ReferFlat       _TCResched,BYTE
        ReferFlat       _pgCommitSem,BYTE


        EXTRN16 w_QFHandState
        EXTRN32 _SELVirtToLin
        EXTRN32 _TKForceThread
;        EXTRN32 _tkSleepQRemove
;        EXTRN32 _SEMApiResetEventSem
        EXTRN16 SFFromHandle
        EXTRN16 PhysHandle
        EXTRN16 SFLock
        EXTRN16 SFUnlock
        EXTRN16 w_Read
        EXTRN16 Set_Block
        EXTRN16 Clr_Block
        EXTRN16 ScanLock
        EXTRN16 SemClear
        EXTRN16 CheckRange
        EXTRN16 Bmp_Free
        EXTRN32 _TKFuBuff
        EXTRN16 Setup_PhysIO
        EXTRN16 IsEOF
        EXTRN16 Req_List_Exec
        EXTRN16 Req_List_Add_SG
        EXTRN16 Req_List_Add_Extent
        EXTRN16 Req_List_Add_Req
        EXTRN16 Req_List_Init
        EXTRN16 NextSec
        EXTRN16 FigRec
        EXTRN16 Req_List_Read
        EXTRN16 Req_List_Write
        EXTRN16 FatSecRd
        EXTRN16 DWrite
        EXTRN16 GetReqList
        EXTRN16 BufFree
        EXTRN16 FreeReqList
        EXTRN16 SetupForIO
        EXTRN16 InitTCBVars
        EXTRN16 UseCache
        EXTRN16 Pack
        EXTRN16 UnPack
        EXTRN16 Release
        EXTRN16 CRC
        EXTRN16 BufWrt
        EXTRN16 RlseBuffr
        EXTRN16 GetBuffrBP
        EXTRN16 AllocateHotFix
        EXTRN16 HardError
        EXTRN16 WriteFATOrdered
        EXTRN16 GetMRUBufSec
        EXTRN16 Bmp_QueryFree
        EXTRN32 _SELAllocGDT
        EXTRN32 _SELFreeGDT
        EXTRN32 _SELSetDescInfo
        EXTRN32 _SELFreeKHB
        EXTRN32 _SELAllocKHB
        EXTRN32 _VMAllocKHB
        EXTRN32 _VMFreeKHB
        EXTRN32 _BufFlush
        EXTRN32 ioSaveTCB
        EXTRN32 ioRestoreTCB
        EXTRN32 _TKTCYield
        EXTRN   _TKSleep:NEAR
        EXTRN   _TKWakeup:NEAR
        EXTRN32 _SELConvertToLaddr
        EXTRN32 _KSEMQueryMutex
        EXTRNFAR AdjustError                                            ;136380

        EXTRN32  _TKProbeBuff                                           ;92265

        ReferGlobal     FATCache,BYTE
        ReferGlobal     s2BmpSel,WORD

DATA    SEGMENT

LockStr FIOLock <>              ; Structure for access, and lockrange
UnLockStr FIOUnlock <>          ; Structure for unlockrange

DATA    ENDS

BadSec      struc
BadSec_Next     dd      ?
BadSec_RBA      dd      ?
BadSec_Len      dw      ?
BadSec      ends

DATA                SEGMENT
ClusBitMask             db 01h,02h,04h,08h,10h,20h,40h,80h
ClusBitIndex            db 00h  ; invalid
                        db 00h  ; 01h bit mask
                        db 01h  ; 02h bit mask
                        db 00h  ; invalid
                        db 02h  ; 04h bit mask
                        db 00h  ; invalid
                        db 00h  ; invalid
                        db 00h  ; invalid
                        db 03h  ; 08h bit mask
ClusBitIndex2           db 00h  ; invalid
                        db 04h  ; 10h bit mask
                        db 05h  ; 20h bit mask
                        db 00h  ; invalid
                        db 06h  ; 40h bit mask
                        db 00h  ; invalid
                        db 00h  ; invalid
                        db 00h  ; invalid
                        db 07h  ; 80h bit mask
DATA                ENDS

        FARCODE HIGH2

HIGH2CODE       SEGMENT

;***FS          w_BigOper16
;
;
;       This routine is very similar to w_Read, except that it can
;       handle very large ( > 64k) IO requests. It does this by
;       locking the SFT, then issuing multiple transfer requests.
;
;       Entry:  EDX     Flat address of buffer
;               ECX     Number of bytes to read
;               EDI     File Handle LockID
;               BX      Handle to operate on
;               SI      Address of routine to call
;
;       Exit:   ECX     holds number of bytes transferred
;
;               IF success
;                       EAX = 0
;               else
;                       EAX = error code
;
;
;       Uses    EAX,EBX,ECX,EDX,FLAGS
;

        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure       w_BigOper16,far16

LocalVar        BigOp_GDT,WORD
LocalVar        BigOp_TempPtr,WORD
LocalVar        pBuf,DWORD
LocalVar        ulWanted,DWORD          ;Number of bytes to I/O
LocalVar        ulDone,DWORD            ;Number of bytes completed
LocalVar        usCurCount,WORD         ;Number of bytes in current request
LocalVar        usRoutine,WORD          ;IO routine address
LocalVar        fetch,DWORD
LocalVar        FileHandleLockID,DWORD


EnterProc       <0>

                xor     eax,eax

                mov     ulDone,eax              ;Zero out completed
                mov     ulWanted,ecx            ;Save bytes wanted
                mov     usRoutine,si            ;Save away the IO routine
                mov     pBuf,EDX
                mov     FileHandleLockID,edi    ; save file handle lockID

; validate the 32 bit buffer pointer b723854
                or      ecx,ecx
                jz      nowrtrd
                FlatContext     <es,ds>
                SSToDS  eax, fetch
                CCALL32 _TKFuBuff,<eax,edx,1,TK_UM_FATAL>
                cmp     ax, ERROR_PROTECTION_VIOLATION
                je      wbo100

nowrtrd:
                DosContext      <ds,es>

                call    SFFromHandle            ;Get handle to SF
                jc      wbo100

                call    PhysHandle              ;Check for physical disk
                jc      wbo100

; es:di -> sft

        push    ebx                             ; save ebx on stack
        mov     ebx, FileHandleLockID
        cmp     ebx, es:[di].sf_LockID          ; check access
        pop     ebx                            ; reset stack
        je      wbo000

        mov     ax, ERROR_ACCESS_DENIED        ; access denied
        jmp     wbo100                         ; error exit
wbo000:

; if this is for a console device, return VIOKBD_REQUEST

                test    es:[di].sf_flags,DEVID_ISDEV     ; VW B730088
                jz      wbo001                           ; VW B730088
                test    es:[di].sf_flags,DEVID_DEVICE_CON_IN OR DEVID_DEVICE_CON_OUT
                mov     ax,ERROR_VIOKBD_REQUEST
                jnz     wbo60

wbo001:                                                   ;VW B730088
                call    SFLock
                mov     ax,ERROR_INVALID_HANDLE
                jc      wbo50                         ;

;Save away the SFT

                push    bp

                mov     bp,CurrTCB

                mov     word ptr [bp].TCBThisSFT,di
                mov     word ptr [bp].TCBThisSFT + 2,es

                pop     bp
;
; At this point, we have locked the SFT, and are ready to perform the IO
; operations. The following conditions should be treated as special:
;
; - pBuf is 64k aligned and request is for >= 64k of data
;
;       This will require two reads for the segment, since the only way
;       to read a full 64k of data is in two peices. The largest chunk
;       is 64k-1 long.
;
; - pBuf is not 64k aligned
;
;       This case we can just read to the end of the segment
;


wboMainLoop:
                mov     edx,pBuf

                or      dx,dx           ;64k aligned?
                jnz     wbo030          ;No, branch

        ;Now we know that we have 64k of room in this segment. We need
        ;to determine if we are about to read the whole thing

                mov     ecx,ulWanted
                cmp     ecx,0ffffh      ;Largest possible read request
                jbe     wbo020          ;If below, then we can read it
                                        ;faster below

        ;We have now determined that the only way to fill this buffer is
        ;in two parts. Now we make two requests. The first is 60k, and the
        ;second is 4k

                mov     ecx,0f000h      ;Request 60k
                xor     ax,ax           ; tiled
                call    wboDoIo

                mov     ecx,01000h      ;Request 4k
wbo020:         xor     ax,ax           ; tiled
                call    wboDoIo

                jmp     short wboMainLoopEnd

;
; This section is used when the next request will be smaller than 64k.
; and buffer is not 64k aligned
;

wbo030:         movzx   ecx,dx          ;We know that dx has offset
                neg     cx              ;This gives # bytes left in seg
                jz      short wbo033    ;If zero, then 64k aligned buffer

                cmp     ecx,ulWanted
                jle     short wbo040

wbo033:         mov     ecx,ulWanted
                xor     ax,ax           ; tiled
                call    wboDoIo
                jmp     short wboMainLoopEnd

wbo040:         mov     usCurCount,cx   ;Save requested count ;### B727713
                FlatContext <ds,es>
                SSToDS  eax,BigOp_GDT
                CCALL32 _SELAllocGDT,<eax>      ;Allocate a GDT selector

                or      eax,eax
                jnz     wbo100

                movzx   edx,BigOp_GDT
                mov     eax,pBuf
                mov     ecx,ulWanted

; _SELSetDescInfo returns NO errors

                CCALL32 _SELSetDescInfo,<SELSD_ALL,D_DATA3,edx,ecx,eax,HOBNULL,HOBNULL,0>
                ASSUME  DS:NOTHING,ES:NOTHING
;### B727713                mov     ecx,ulWanted
                movzx   ecx, usCurCount         ; Set up number of bytes to read ;### B727713
                mov     ax,1            ; not tiled
                movzx   edx,BigOp_GDT
                shl     edx,16
                call    wboDoIo
wboMainLoopEnd: mov     eax,ulWanted    ;Check to see if ulWanted is zero
                or      eax,eax
                jnz     wboMainLoop     ;if it is, the zero is the exit code

;
; Exit - Return error code from lower level routine if there is one
;
;       Here we unlock the SFT and exit. AX holds the error code.
;

wbo50:          push    bp
                mov     bp,CurrTCB
                les     di,[bp].TCBThisSFT
                call    SFUnlock
                pop     bp

wbo60:          mov     ecx,ulDone      ;Bytes performed
                movzx   eax,ax


                jmp     wbo999          ;Jump to exit

;
; Logical error exit - Didn't do any reading, so zero out return count
;

wbo100:         xor     ecx,ecx
                stc                     ;Force error condition
                jmp     wbo999

;
; This section sets up and performs the actual I/O call
;
; CX holds the number of items to read on this request.
;


wboDoIo:        mov     usCurCount,cx   ;Save requested count
                or      ax,ax           ; do we have a tile selector
                jnz     wboDoIo20       ; no, skip thunk edx setup by caller

                movzx   eax,ss:CurrTSD
                mov     ecx,DWORD PTR SS:[eax].TSDUserSS

        ;Get the pointer to the buffer and convert it to a sel:offset

                mov     edx,pBuf
                ror     edx,16
                shl     dx,3
                arpl    dx,cx
                or      dl,4            ;Force the LDT bit on.
                rol     edx,16

wboDoIo20:      movzx   eax,CurrTCB
                mov     DWORD PTR SS:[eax].TCBDMAAdd,edx
                mov     cx,usCurCount   ;Save requested count

                TaskContext     DS

                push    bp

                mov     si,usRoutine
                call    si              ;Calling FSD_Read or FSD_Write

                pop     bp

                jc      short wbo500    ;Jump if error code exists
                xor     eax,eax
                jmp     short wbo501                                     ;136380

;1363800 wbo500:         movzx   ecx,cx ;Add in the read count           ;136380
wbo500:         CALLFAR  AdjustError                                     ;136380
wbo501:
                movzx   ecx,cx          ;Add in the read count           ;136380
                add     ulDone,ecx

                or      ax,ax
                jnz     wbo50           ;Exit if error condition

                cmp     cx,usCurCount
                jne     wbo50           ;Exit if not full read

                add     pBuf,ecx
                sub     ulWanted,ecx
                retn                    ;Near return within procedure

;
; Final exit location

wbo999:         pushf
                movzx   edx,BigOp_GDT
                or      dx,dx
                jz      wboexit
                SaveReg <ecx,eax>
                FlatContext <ds,es>     ;Setup for Flat call

                CCALL32 _SELFreeGDT,<edx>
                RestoreReg <eax,ecx>
wboexit:        popf
                LeaveProc

                RETP

EndProc         w_BigOper16

HIGH2CODE       ENDS



;******************************

        FARCODE HIGH32

HIGH32CODE      SEGMENT

;***FS  FS32IREAD
;
;       Perform 32-bit read operation
;

Procedure       FS32IREAD,near


        mov     si,offset DOSHIGH2CODE:FSD_Read
        jmp     w_Big32IO


EndProc FS32IREAD


;***FS  FS32IWRITE
;
;       Perform 32-bit write operation
;

Procedure       FS32IWRITE,near


        mov     si,offset DOSHIGH2CODE:FSD_Write_NT
        jmp     w_Big32IO


EndProc FS32IWRITE



;***FS  w_Big32IO       - Thunk to get to big file operation routine
;
;       This routine sets up a thunk to get to a 16-bit worker that
;       can handle very large read/write requests. It checks to see
;       that the handle will fit into 16-bits
;
;       Entry:  DWORD   pulHFlags
;               DWORD   pulBytesTransfer
;               DWORD   ulBufferSize
;               DWORD   pBuffer
;               DWORD   ulHandle
;
;               si      offset DOSHIGH2CODE: [FSD_Write|FSD_Read]
;
;       Exit:   Bytes transfered stored at *pulBytesTransfer
;               if success
;                       eax=0
;               else
;                       eax=error code
;
;       Uses:   EAX,EBX,ECX,EDX,ESI
;

Procedure       w_Big32IO,cdecl

ArgVar  sSEF,<size thunksef_s>

ArgVar  ulHandle,DWORD
ArgVar  pBuffer,DWORD
ArgVar  ulBufferSize,DWORD
ArgVar  pulBytesTransfer,DWORD
ArgVar  pulHandleFlags,DWORD
ArgVar  ulFileHandleLockID,DWORD

        ASSUME  DS:FLAT,ES:FLAT,FS:NOTHING,GS:NOTHING

        EnterProc

        mov     ebx,ulHandle
        mov     ecx,ulBufferSize
        mov     edx,pBuffer
        mov     edi,ulFileHandleLockID

;
; here we check the hanlde to insure it won't get truncated when it
; is converted to 16-bit
;
        movsx   eax,bx                  ;Check handle for validity
        cmp     eax,ebx                 ;Can't be > 64k
        jnz     fsir_BadParam

        CALL16  w_BigOper16

        cmp     eax,ERROR_VIOKBD_REQUEST
        jne     fsir010

        mov     ax,1                    ; 32 bit call
        mov     bx,word ptr ulHandle    ; handle
        or      ah,80h                  ; flag = no filehandle lockID check
        CALL16  w_QFHandState           ; get handle state bits
        shl     edx,16                  ; get dx into high part of edx
        or      dx,cx                   ; get bits in cx into dx
                                        ; edx = handle info

        FlatContext     <ds,es>
        CCALL32 _TKSuDWord, <edx,pulHandleFlags>

        mov     ax,ERROR_VIOKBD_REQUEST

fsir010:
        FlatContext     <ds,es>

        SaveReg <eax,ecx>               ;Save return code, Bytes transfered
        CCALL32 _TKSuDWord, <ecx,pulBytesTransfer>
        RestoreReg <ecx,eax>
        movzx   eax,ax                  ;Extend result code

        LeaveProc
        RETC

fsir_BadParam:

        mov     ax,ERROR_INVALID_HANDLE
        xor     ecx,ecx
        jmp     fsir010

EndProc         w_Big32IO

;***FS  W_SetFileLocks
;
;       Perform 32-bit byte locking on a file
;

Procedure       w_SetFileLocks,near
        jmp     w_realSetFileLocks

EndProc w_SetFileLocks

;***FS  W_ProtectSetFileLocks
;
;       Perform 32-bit byte locking on a file
;

Procedure       w_ProtectSetFileLocks,near
        jmp     w_realProtectSetFileLocks

EndProc w_ProtectSetFileLocks

;***FS  W_CancelFileLocks
;
;       Perform 32-bit cancel outstanding lock request
;

Procedure       w_CancelFileLocks,near

        jmp     w_realCancelFileLocks

EndProc w_CancelFileLocks


;***FS  w_realSetFileLocks   Byte level locking routine for 32 bit
;
;       This routine sets up and calls a 16-bit worker that does
;       byte level locking and unlocking.
;
;       Entry:  DWORD   ulHandle
;               DWORD   pUnLockRange
;               DWORD   pLockRange
;               DWORD   ulTimeout
;               DWORD   ulFlags
;
;       Exit:
;
;               if success
;                       eax=0
;               else
;                       eax=error code
;
;       Uses:   EAX,EBX,ECX,EDX,ESI,EDI
Procedure       w_realSetFileLocks,cdecl
ArgVar  sSEF,<size thunksef_s>

ArgVar  ulHandle,DWORD
ArgVar  pUnLockRange,DWORD
ArgVar  pLockRange,DWORD
ArgVar  Timeout,DWORD
ArgVar  ulFlags,DWORD
LocalVar unlockflag,BYTE
LocalVar fetchwd,DWORD
LocalVar fetchwd1,DWORD
LocalVar unflag,BYTE
LocalVar lockflag,BYTE
LocalVar fhlockID,DWORD

        ASSUME  DS:FLAT,ES:FLAT,FS:NOTHING,GS:NOTHING

        EnterProc
;
; Check if the passed-in pointers are valid or not
; Return ERROR_INVALID_PARAMETER if not valid.
;
        SaveReg <es,ds,ebx,esi,ecx,edx>

        xor     edx, edx
        mov     edx, pUnLockRange
        or      edx, edx
        jz      check_lock
        xor     eax, eax
        mov     ax,  size FILELOCK
        CCALL32 _TKProbeBuff <edx, eax, TK_UM_READ>
        or       eax, eax
        jz       check_lock
ptr_bad:
        RestoreReg <edx,ecx,esi,ebx,ds,es>
        mov     ax,ERROR_INVALID_PARAMETER
        jmp     fsret1

check_lock:
        xor     edx, edx
        mov     edx, pLockRange
        or      edx, edx
        jz      end_check
        xor     eax, eax
        mov     ax,  size FILELOCK
        CCALL32 _TKProbeBuff <edx, eax, TK_UM_READ>
        or       eax, eax
        jnz      ptr_bad
end_check:
        pop     edx
        pop     ecx
        pop     esi
        pop     ebx
        pop     ds
        pop     es

        xor     ax,ax
        mov     fhlockID.LowWord,ax  ; zero the lockID
        mov     fhlockID.HighWord,ax

        mov     ebx,ulHandle
        mov     unlockflag,0
        mov     unflag,0
        mov     lockflag,0

;
; here we check the handle to insure it won't get truncated when it
; is converted to 16-bit
;
        SaveReg <es,ds>
        movsx   eax,bx                  ;Check handle for validity
        cmp     eax,ebx                 ;Can't be > 64k
        jnz     fs1_BadParam

        CALL16  SFFromHandle            ; es:di -> sft entry
        jc      fs1_BadParam            ; if error, go return
                                        ; b724366
        CALL16  PhysHandle              ; is physical disk handle?
        jc      fs1_BadParam            ; yes - return error

        mov     ax, fhlockID.LowWord
        cmp     ax, es:[di].sf_LockID.LowWord  ; check access
        jne     noacc                          ; access denied

        mov     ax, fhlockID.HighWord
        cmp     ax, es:[di].sf_LockID.HighWord  ; check access
        jne     noacc                           ; access denied

        test    es:[di].sf_flags,DEVID_DEVICE_CON_IN OR DEVID_DEVICE_CON_OUT OR DEVID_DEVICE
        LJNZ    fs1_BadParam

; setup sft
        FlatContext     <ds>
        mov     ebx,ds:[_pTCBCur]
        mov     ds:[ebx].TCBThisSFT.Offst,di
        mov     ds:[ebx].TCBThisSFT.Segmt,es

; jump to appropriate file system
        cmp     es:[di].sf_FSC.Offst,FSC_LOCAL  ; FAT?
        jae     fatunlock                       ; yes, go do unlock

        SaveReg <es,di>                 ; ES:DI-> SFT
        les     di,es:[di].sf_FSC       ; ES:DI-> FSC
        les     di,es:[di].FS_ATTRIBUTE ; ES:DI-> Attribute word for FSD
        test    word ptr es:[di],FS_ATTR_LOCKINFO   ; FSD does lock processing?
        RestoreReg <di,es>
        jz      fatunlock               ; No, go break up for local FSD

; translate near 32-bit pointers to 16:16
        mov     eax,pUnLockRange        ; convert DS relative pointer back
        or      eax,eax                 ; check for null
        jz      conlockptr              ; jump if zero
        ror     eax,16                  ; swap high with low
        shl     ax,3                    ; make room for table and DPL bits
        or      al,7                    ; turn on, set for ring 3
        rol     eax,16                  ; swap high with low

conlockptr:
        mov     ebx,pLockRange          ; convert DS relative pointer back
        or      ebx,ebx                 ; check for null
        jz      gettime                 ; jump if zero
        ror     ebx,16                  ; swap high with low
        shl     bx,3                    ; make room for table and DPL bits
        or      bl,7                    ; turn on, set for ring 3
        rol     ebx,16                  ; swap high with low

gettime:
        mov     ecx,Timeout             ; timeout

        mov     esi,dword ptr [ulFlags]   ; access type, only care about low
                                        ; word
;### AA04392   FSDs expect the dword in the correct order
;###       rol     esi,16                  ; swap so we push on stack correctly

        CALL16  Call32FSDLock
        jmp     endof                   ; any return value

; setup lock and unlock structures

fatunlock:
        FlatContext     <es>
        mov     ebx,[pUnLockRange]      ; unlock range begins
        or      ebx,ebx
        jz      getlockrange
        inc     unflag
        SSToDS  edx, fetchwd
        CCALL32 _TKFuBuff,<edx,ebx,4,TK_UM_FATAL>
        add     ebx,4
        SSToDS  edx, fetchwd1
        CCALL32 _TKFuBuff,<edx,ebx,4,TK_UM_FATAL>

        DosContext es
    ASSUME      ES:NOTHING

        lea     di,es:UnLockStr
        mov     eax,[fetchwd]
        mov     es:[di],eax
        mov     ecx,[fetchwd1]
        mov     es:[di+4],ecx
        FlatContext     <es>

getlockrange:
        mov     ebx,[pLockRange]        ; lock range begins
        or      ebx,ebx
        jz      atomic
        inc     lockflag
        SSToDS  edx, fetchwd
        push    eax                     ; save    lock offset, defect 54801
        CCALL32 _TKFuBuff,<edx,ebx,4,TK_UM_FATAL>
        add     ebx,4
        SSToDS  edx, fetchwd1
        CCALL32 _TKFuBuff,<edx,ebx,4,TK_UM_FATAL>
        pop     eax                     ; restore lock offset, defect 54801

        DosContext es
    ASSUME      ES:NOTHING
        lea     di,es:LockStr
        mov     edx,[fetchwd]
        mov     es:[di+2],edx
        mov     ecx,[fetchwd1]             ; length of lock range
        mov     es:[di+6],ecx

atomic:
        DosContext es
    ASSUME      ES:NOTHING
        test    [ulFlags],2
        jz      notatomic
        test    unflag,1
        jz      lockvio
        test    lockflag,1
        jz      lockvio
        cmp     eax,edx                 ; offset of lock and unlock ranges
        jne     lockvio
        lea     di,es:UnLockStr
        mov     eax,es:[di+4]
        cmp     eax,ecx
        jne     lockvio

; atomic bit is set and lock range equals unlock range
; b724383 check for overlapping lock
        mov     ecx,edx                 ; offset of unlock range
        shr     ecx,16                  ; cx:dx offset of area
        mov     esi,eax                 ; length of unlock range
        shr     esi,16                  ; si:ax length of area
        CALL16  CheckRange              ; ax:bx offset of first byte
                                        ; cx:dx offset of last byte
        jc      endof                   ; lock violation
        SaveReg <es,di>
        FlatContext     <ds>
        mov     esi,ds:[_pTCBCur]
        mov     di,ds:[esi].TCBThisSFT.Offst
        mov     es,ds:[esi].TCBThisSFT.Segmt ; es:di -> sft
        mov     esi,es:[di].sf_MFT
                                        ; ds:esi -> mft

        mov     di,RLR_WRITE_ACCESS+RLR_FIND_OWN+RLR_ATO_OVER ; exclusive access
                                        ; and all locks atomically
        CALL16  ScanLock                ; check for an overlapping lock
        RestoreReg <di,es>
        jc      endof                   ; lock violation

        mov     bx,word ptr [ulFlags]   ; flags are to be changed in atomic
                                        ; lock
        and     bx,1                    ; only keep the lock bit
        mov     ax,1
        CALL16  Clr_Block               ; es:di points to unlock range
        jmp     short endof


; setup to call Clr_Block, ES:DI = pointer to offset and length of area
;                                  to unlock
; fillin
notatomic:
        test    unflag,1
        jz      fatlock
        lea     di,es:UnLockStr
        mov     ecx,es:[di+4]
        cmp     ecx,0                   ; test if no unlock is requested
        je      fatlock

        push    ds
        xor     ax,ax
        CALL16  Clr_Block
        pop     ds
        jc      short endof

        inc     unlockflag

; setup to call Set_Block, AX = file handle
;                          ES:DI = pointer to lock record
;                          CX:DX = timeout, 0ffffh is wait indefinitely


fatlock:
        test    lockflag,1
        jz      testnounlock
        mov     eax,ulHandle
        DosContext es
    ASSUME      ES:NOTHING
        lea     di,es:LockStr
        mov     bx,word ptr [ulFlags]   ; access type, only care about low
                                        ; word
                                        ; b724509
        and     bx,3H                   ; only care about two lowest bits
        mov     es:[di],bx

        mov     ecx,es:[di+6]
        cmp     ecx,0                   ; test if no lock is requested
        je      testnounlock

        mov     cx,word ptr[Timeout+2]  ; set timeout
        mov     dx,word ptr[Timeout]

        push    ds
        CALL16  Set_Block               ; make sure LockBlock
                                        ; is interruptible
        pop     ds
        jc      short endof             ; return error, set lock interrupt

;       Set_Block  USES AX,BX,CX,DX,ESI,DS

noerror:
        xor     eax,eax                         ; return no errors

endof:

        RestoreReg <ds,es>
fsret1:
        movzx   eax,ax                  ;Extend result code

        LeaveProc
        RETC

fs1_BadParam:
        mov     ax,ERROR_INVALID_HANDLE
        jmp     short endof

badptr:
        mov     ax,ERROR_INVALID_PARAMETER
        jmp     short endof

noacc:
        mov     ax, ERROR_ACCESS_DENIED        ; access denied
        stc
        jmp     short endof

testnounlock:
        test    unlockflag,1
        jnz     noerror
lockvio:
        mov     ax,ERROR_LOCK_VIOLATION
        jmp     short endof

EndProc         w_realSetFileLocks


;***FS  w_realProtectSetFileLocks   Byte level locking routine for 32 bit
;
;       This routine sets up and calls a 16-bit worker that does
;       byte level locking and unlocking.
;
;       Entry:  DWORD   ulHandle
;               DWORD   pUnLockRange
;               DWORD   pLockRange
;               DWORD   ulTimeout
;               DWORD   ulFlags
;               DWORD   ulFileLockID
;
;       Exit:
;
;               if success
;                       eax=0
;               else
;                       eax=error code
;
;       Uses:   EAX,EBX,ECX,EDX,ESI,EDI
Procedure       w_realProtectSetFileLocks,cdecl
ArgVar  RPsSEF,<size thunksef_s>

ArgVar  rp_ulHandle,DWORD
ArgVar  rp_pUnLockRange,DWORD
ArgVar  rp_pLockRange,DWORD
ArgVar  rp_Timeout,DWORD
ArgVar  rp_ulFlags,DWORD
ArgVar  rp_ulFileHandleLockID,DWORD
LocalVar rp_unlockflag,BYTE
LocalVar rp_fetchwd,DWORD
LocalVar rp_fetchwd1,DWORD
LocalVar rp_unflag,BYTE
LocalVar rp_lockflag,BYTE
LocalVar rp_fhlockID,DWORD

        ASSUME  DS:FLAT,ES:FLAT,FS:NOTHING,GS:NOTHING

        EnterProc

        mov     ebx,rp_ulHandle
        mov     rp_unlockflag,0
        mov     rp_unflag,0
        mov     rp_lockflag,0

;
; here we check the handle to insure it won't get truncated when it
; is converted to 16-bit
;
        SaveReg <es,ds>
        movsx   eax,bx                  ;Check handle for validity
        cmp     eax,ebx                 ;Can't be > 64k
        jnz     rp_fs1_BadParam

        CALL16  SFFromHandle            ; es:di -> sft entry
        jc      rp_fs1_BadParam            ; if error, go return
                                        ; b724366
        CALL16  PhysHandle              ; is physical disk handle?
        jc      rp_fs1_BadParam            ; yes - return error

        mov     ax, rp_ulFileHandleLockID.LowWord
        cmp     ax, es:[di].sf_LockID.LowWord  ; check access
        jne     rp_noacc                          ; access denied

        mov     ax, rp_ulFileHandleLockID.HighWord
        cmp     ax, es:[di].sf_LockID.HighWord  ; check access
        jne     rp_noacc                           ; access denied

        test    es:[di].sf_flags,DEVID_DEVICE_CON_IN OR DEVID_DEVICE_CON_OUT OR DEVID_DEVICE
        LJNZ    rp_fs1_BadParam

; setup sft
        FlatContext     <ds>
        mov     ebx,ds:[_pTCBCur]
        mov     ds:[ebx].TCBThisSFT.Offst,di
        mov     ds:[ebx].TCBThisSFT.Segmt,es

; jump to appropriate file system
        cmp     es:[di].sf_FSC.Offst,FSC_LOCAL  ; FAT?
        jae     rp_fatunlock                       ; yes, go do unlock

        SaveReg <es,di>                 ; ES:DI-> SFT
        les     di,es:[di].sf_FSC       ; ES:DI-> FSC
        les     di,es:[di].FS_ATTRIBUTE ; ES:DI-> Attribute word for FSD
        test    word ptr es:[di],FS_ATTR_LOCKINFO   ; FSD does lock processing?
        RestoreReg <di,es>
        jz      rp_fatunlock               ; No, go break up for local FSD

; translate near 32-bit pointers to 16:16
        mov     eax,rp_pUnLockRange        ; convert DS relative pointer back
        or      eax,eax                 ; check for null
        jz      rp_conlockptr              ; jump if zero
        ror     eax,16                  ; swap high with low
        shl     ax,3                    ; make room for table and DPL bits
        or      al,7                    ; turn on, set for ring 3
        rol     eax,16                  ; swap high with low

rp_conlockptr:
        mov     ebx,rp_pLockRange          ; convert DS relative pointer back
        or      ebx,ebx                 ; check for null
        jz      rp_gettime                 ; jump if zero
        ror     ebx,16                  ; swap high with low
        shl     bx,3                    ; make room for table and DPL bits
        or      bl,7                    ; turn on, set for ring 3
        rol     ebx,16                  ; swap high with low

rp_gettime:
        mov     ecx,rp_Timeout             ; timeout

        mov     esi,dword ptr [rp_ulFlags]   ; access type, only care about low
                                        ; word
;### AA04392   FSDs expect the dword in the correct order
;###       rol     esi,16                  ; swap so we push on stack correctly

        CALL16  Call32FSDLock
        jmp     rp_endof                   ; any return value

; setup lock and unlock structures

rp_fatunlock:
        FlatContext     <es>
        mov     ebx,[rp_pUnLockRange]      ; unlock range begins
        or      ebx,ebx
        jz      rp_getlockrange
        inc     rp_unflag
        SSToDS  edx, rp_fetchwd
        CCALL32 _TKFuBuff,<edx,ebx,4,TK_UM_FATAL>
        add     ebx,4
        SSToDS  edx, rp_fetchwd1
        CCALL32 _TKFuBuff,<edx,ebx,4,TK_UM_FATAL>

        DosContext es
    ASSUME      ES:NOTHING

        lea     di,es:UnLockStr
        mov     eax,[rp_fetchwd]
        mov     es:[di],eax
        mov     ecx,[rp_fetchwd1]
        mov     es:[di+4],ecx
        FlatContext     <es>

rp_getlockrange:
        mov     ebx,[rp_pLockRange]        ; lock range begins
        or      ebx,ebx
        jz      rp_atomic
        inc     rp_lockflag
        SSToDS  edx, rp_fetchwd
        CCALL32 _TKFuBuff,<edx,ebx,4,TK_UM_FATAL>
        add     ebx,4
        SSToDS  edx, rp_fetchwd1
        CCALL32 _TKFuBuff,<edx,ebx,4,TK_UM_FATAL>

        DosContext es
    ASSUME      ES:NOTHING
        lea     di,es:LockStr
        mov     edx,[rp_fetchwd]
        mov     es:[di+2],edx
        mov     ecx,[rp_fetchwd1]             ; length of lock range
        mov     es:[di+6],ecx

rp_atomic:
        DosContext es
    ASSUME      ES:NOTHING
        test    [rp_ulFlags],2
        jz      rp_notatomic
        test    rp_unflag,1
        jz      rp_lockvio
        test    rp_lockflag,1
        jz      rp_lockvio
        cmp     eax,edx                 ; offset of lock and unlock ranges
        jne     rp_lockvio
        lea     di,es:UnLockStr
        mov     eax,es:[di+4]
        cmp     eax,ecx
        jne     rp_lockvio

; atomic bit is set and lock range equals unlock range
; b724383 check for overlapping lock
        mov     ecx,edx                 ; offset of unlock range
        shr     ecx,16                  ; cx:dx offset of area
        mov     esi,eax                 ; length of unlock range
        shr     esi,16                  ; si:ax length of area
        CALL16  CheckRange              ; ax:bx offset of first byte
                                        ; cx:dx offset of last byte
        jc      rp_endof                   ; lock violation
        SaveReg <es,di>
        FlatContext     <ds>
        mov     esi,ds:[_pTCBCur]
        mov     di,ds:[esi].TCBThisSFT.Offst
        mov     es,ds:[esi].TCBThisSFT.Segmt ; es:di -> sft
        mov     esi,es:[di].sf_MFT
                                        ; ds:esi -> mft

        mov     di,RLR_WRITE_ACCESS+RLR_FIND_OWN+RLR_ATO_OVER ; exclusive access
                                        ; and all locks atomically
        CALL16  ScanLock                ; check for an overlapping lock
        RestoreReg <di,es>
        jc      rp_endof                   ; lock violation

        mov     bx,word ptr [rp_ulFlags]   ; flags are to be changed in atomic
                                        ; lock
        and     bx,1                    ; only keep the lock bit
        mov     ax,1
        CALL16  Clr_Block               ; es:di points to unlock range
        jmp     short rp_endof


; setup to call Clr_Block, ES:DI = pointer to offset and length of area
;                                  to unlock
; fillin
rp_notatomic:
        test    rp_unflag,1
        jz      rp_fatlock
        lea     di,es:UnLockStr
        mov     ecx,es:[di+4]
        cmp     ecx,0                   ; test if no unlock is requested
        je      rp_fatlock

        push    ds
        xor     ax,ax
        CALL16  Clr_Block
        pop     ds
        jc      short rp_endof

        inc     rp_unlockflag

; setup to call Set_Block, AX = file handle
;                          ES:DI = pointer to lock record
;                          CX:DX = timeout, 0ffffh is wait indefinitely


rp_fatlock:
        test    rp_lockflag,1
        jz      rp_testnounlock
        mov     eax,rp_ulHandle
        DosContext es
    ASSUME      ES:NOTHING
        lea     di,es:LockStr
        mov     bx,word ptr [rp_ulFlags]   ; access type, only care about low
                                        ; word
                                        ; b724509
        and     bx,3H                   ; only care about two lowest bits
        mov     es:[di],bx

        mov     ecx,es:[di+6]
        cmp     ecx,0                   ; test if no lock is requested
        je      rp_testnounlock

        mov     cx,word ptr[rp_Timeout+2]  ; set timeout
        mov     dx,word ptr[rp_Timeout]

        push    ds
        CALL16  Set_Block               ; make sure LockBlock
                                        ; is interruptible
        pop     ds
        jc      short rp_endof             ; return error, set lock interrupt

;       Set_Block  USES AX,BX,CX,DX,ESI,DS

rp_noerror:
        xor     eax,eax                         ; return no errors

rp_endof:

        RestoreReg <ds,es>
rp_fsret1:
        movzx   eax,ax                  ;Extend result code

        LeaveProc
        RETC

rp_fs1_BadParam:
        mov     ax,ERROR_INVALID_HANDLE
        jmp     short rp_endof

rp_badptr:
        mov     ax,ERROR_INVALID_PARAMETER
        jmp     short rp_endof

rp_noacc:
        mov     ax, ERROR_ACCESS_DENIED        ; access denied
        stc
        jmp     short rp_endof

rp_testnounlock:
        test    rp_unlockflag,1
        jnz     rp_noerror
rp_lockvio:
        mov     ax,ERROR_LOCK_VIOLATION
        jmp     short rp_endof

EndProc         w_realProtectSetFileLocks


;***FS  w_realCancelFileLocks   Cancel byte level locking routine for
;                               an outstanding 32 bit request
;
;       This routine sets up and calls a 16-bit worker that cancels an
;       outstanding byte level locking request.
;
;       Entry:  DWORD   ulHandle
;               DWORD   pLockRange
;
;       Exit:
;               if success
;                       eax=0
;               else
;                       eax=error code
;
;       Uses:   EAX,EBX,ECX,EDX,ESI,EDI
Procedure       w_realCancelFileLocks,cdecl
ArgVar  sSEF,<size thunksef_s>

ArgVar  ulHandle,DWORD
ArgVar  pLockRange,DWORD
LocalVar  fbseg,WORD
LocalVar  fboff,WORD
LocalVar  lbseg,WORD
LocalVar  lboff,WORD
LocalVar  count,WORD
LocalVar  fetchwd,DWORD
LocalVar  fetchwd1,DWORD

        ASSUME  DS:FLAT,ES:FLAT,FS:NOTHING,GS:NOTHING

        EnterProc

        SaveReg <es,ds>
        mov     ebx,ulHandle

;
; here we check the handle to ensure it won't get truncated when it
; is converted to 16-bit
;
        movsx   eax,bx                  ;Check handle for validity
        cmp     eax,ebx                 ;Can't be > 64k
        jnz     fs2_BadParam

        CALL16  SFFromHandle            ; es:di -> sft entry
        jc      fs2_BadParam            ; if error, go return

                                        ; b724366
        CALL16  PhysHandle              ; is physical disk handle?
        jc      fs2_BadParam            ; yes - return error

        test    es:[di].sf_flags,DEVID_DEVICE_CON_IN OR DEVID_DEVICE_CON_OUT OR DEVID_DEVICE
        LJNZ    fs2_BadParam

; setup sft
        FlatContext     <ds>
        mov     ebx,ds:[_pTCBCur]
        mov     ds:[ebx].TCBThisSFT.Offst,di
        mov     ds:[ebx].TCBThisSFT.Segmt,es

; jump to appropriate file system
        cmp     es:[di].sf_FSC.Offst,FSC_LOCAL  ; FAT?
        jae     fatcancel                       ; yes, go do lock

        SaveReg <es,di>                 ; ES:DI-> SFT
        les     di,es:[di].sf_FSC       ; ES:DI-> FSC
        les     di,es:[di].FS_ATTRIBUTE ; ES:DI-> Attribute word for FSD
        test    word ptr es:[di],FS_ATTR_LOCKINFO   ; FSD does lock processing?
        RestoreReg <di,es>
        jz      fatcancel               ; No, go break up for local FSD
; translate near 32-bit pointers to 16:16
        mov     eax,pLockRange          ; convert DS relative pointer back
        or      eax,eax                 ; check for null
        jz      gotofsd                 ; jump if zero
        ror     eax,16                  ; swap high with low
        shl     ax,3                    ; make room for table and DPL bits
        or      al,7                    ; turn on, set for ring 3
        rol     eax,16                  ; swap high with low
gotofsd:
        CALL16  Call32FSDCancelLock
        jmp     fsret2                  ; any return value


; When a thread blocks because of a lock conflict, the
; pending lock is chained to a doubly-linked list of pending locks.
; The head of this list is kept in the SFT.
; Make sure we are still an outstanding request before we go and
; unchain the pending lock!
fatcancel:
        mov     ebx,[pLockRange]        ; lock range begins
        or      ebx,ebx
        jz      badptr2
        CALL16  SFLock                  ; b725016
        jc      fs2_BadParam

        mov     ebx,[pLockRange]        ; lock range begins
        push    es
        FlatContext     <es>
        SSToDS  edx, fetchwd
        CCALL32 _TKFuBuff,<edx,ebx,4,TK_UM_FATAL>
        add     ebx,4
        SSToDS  edx, fetchwd1
        CCALL32 _TKFuBuff,<edx,ebx,4,TK_UM_FATAL>
        pop     es
        mov     dx,word ptr [fetchwd]        ; offset where lock begins
        mov     cx,word ptr [fetchwd+2]        ; segment where lock begins
        mov     ax,word ptr [fetchwd1]       ; length of lock
        mov     si,word ptr [fetchwd1+2]       ; length of lock
; CX:DX and SI:AX are inputs
        CALL16  CheckRange                 ; ax:bx cx:dx returned
        mov     fboff,bx
        mov     fbseg,ax
        mov     lboff,dx
        mov     lbseg,cx
        mov     count,0

        RLRCONTEXT <ds>                 ; DS:SI will point to 1st plock
        mov     si,es:[di].sf_plock     ; 16 bit offset to 1st pending lock rec
        cmp     si,0                    ; is there a next lock?
                                        ; go through all of the plocks because
                                        ; 2 threads in the process may have
                                        ; the same request outstanding!
        jz      endofcancel

; call checkrange with bytes sent and compare with lock record
; then compare the PID, and the handle
; if one thread in a process wants to cancel a lock request that two threads
; in the process are blocked on, both requests will be cancelled!

nextchk:
        mov     bx,fboff
        mov     ax,fbseg
        mov     dx,lboff
        mov     cx,lbseg
        xor     ax,[si].rlr_fba.hiword  ; require a 4-word match
        xor     bx,[si].rlr_fba.loword
        xor     cx,[si].rlr_lba.hiword
        xor     dx,[si].rlr_lba.loword
        or      ax,bx
        or      ax,cx
        or      ax,dx                   ; 'Z' set if exact match
        jz      short matchbyt          ; 'NZ' set if not exact match
        mov     ax,ds:[si].rlr_next     ; get next lock record

nextlock:
        cmp     ax,0                    ; is there a next record?
        jz      short endofcancel
        mov     si,ax                   ; move in the pointer
        jmp     short nextchk           ; check next lock record


; the bytes match, now check the pid and the handle
matchbyt:
        mov     ebx,ulHandle
        cmp     bx,ds:[si].rlr_handle   ; check the handle
        mov     ax,ds:[si].rlr_next     ; get next lock record
        jne     short nextlock

        push    es
        FlatContext     <es>
        mov     ebx,es:[_pTCBCur]
        mov     ax,es:[ebx].TCBProc_ID
        cmp     ax,ds:[si].rlr_PID
        pop     es
        mov     ax,ds:[si].rlr_next     ; get next lock record
        jne     short nextlock

; matched pending lock record, zero out the sleep id and release
; the blocked thread!

match:
; start the process
        inc     count
        mov     ds:[si].rlr_flags,RLR_CANCELPLOCK
        mov     eax,ds:[si].rlr_sptr            ; thread to wake up
        push    es
        push    ds
        FlatContext     <es,ds>
        pushf
        cli                                     ; don't allow interrupts
        CCALL32 _TKForceThread,<NULL,eax>
        popf
        pop     ds
        pop     es
        mov     ax,ds:[si].rlr_next     ; get next lock record
        jmp     short nextlock

endofcancel:
        mov     ebx,ulHandle
        CALL16  SFFromHandle            ; es:di -> sft entry
        CALL16  SFUnlock
        ; did any requests get cancelled, if not return an error
        cmp     count,0
        je      short fs2_BadUnLock
        xor     ax,ax

fsret2:
        movzx   eax,ax                  ;Extend result code
        RestoreReg <ds, es>
        LeaveProc
        RETC

badptr2:
        mov     ax,ERROR_INVALID_PARAMETER
        jmp     short fsret2

fs2_BadParam:
        mov     ax,ERROR_INVALID_HANDLE
        jmp     short fsret2

fs2_BadUnLock:
        mov     ax,ERROR_CANCEL_VIOLATION
        stc
        jmp     short fsret2

EndProc         w_realCancelFileLocks

;***LP  _Setup_PhysIO
;
;       _Setup_PhysIO calls 16 bit version.
;
;       ENTRY   DWORD   pContig
;               DWORD   pNextCluster
;               DWORD   count
;               DWORD   ClusterNum
;               DWORD   pRba
;               DWORD   pSecRemain
;               DWORD   pxferOffst
;               DWORD   SecClusPos
;               DWORD   fDirect
;               DWORD   hVPB
;
;       EXIT
;               if success
;                       eax=0
;               else
;                       eax=error code
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _SetupPhysIO,near

        ArgVar  pContig,DWORD           ; Pointer to Contiguous Sectors
        ArgVar  pNextCluster,DWORD      ; Pointer to Next cluster
        ArgVar  count,DWORD             ; count of sectors
        ArgVar  ClusterNum,DWORD        ; Cluster number to start with
        ArgVar  pRba,DWORD              ; Pointer to starting RBA
        ArgVar  pSecRemain,DWORD        ; Pointer to sectors remaining
        ArgVar  pxferOffst,DWORD        ; Pointer to user offset
        ArgVar  SecClusPos,DWORD        ; Sector Cluster Position
        ArgVar  fDirect,DWORD           ; Direct Access flag
        ArgVar  hVPB,DWORD              ; handle to volume

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        mov     ecx,count               ; CX = count
        mov     ebx,ClusterNum          ; BX = Cluster number
        mov     esi,fDirect             ; SI = Direct Access flag
        mov     edx,SecClusPos          ; DL = Sector Cluster Position
        mov     eax,hVPB                ; AX = handle to volume
        mov     edi,-1                  ; DI = NO Callout routine
        SaveReg <ds,es,bp>
        push    GSEL GDT_VPB
        pop     es                      ; ES = VPB Segment
        TaskContext DS                  ; DS = SS
        CALL16  Setup_PhysIO
        RestoreReg <bp,es,ds>
        jc      seter                   ; if error, go return
        push    edi
        mov     edi,pxferOffst          ; EDI = pointer to return user offset
        movzx   ebx,bx                  ; EBX = User offset
        mov     [edi],ebx               ; Store User Offset address
        mov     edi,pRba                ; EDI = pointer to return RBA
        movzx   ebx,si
        shl     ebx,16
        mov     bx,dx                   ; EBX = RBA
        mov     [edi],ebx               ; Store Rba
        pop     edi
        mov     esi,pContig             ; ESI = pointer to return contig. secs
        movzx   ecx,cx                  ; ECX = number of contiguous sectors
        mov     [esi],ecx               ; Store number of contiguous sector
        mov     esi,pNextCluster        ; ESI = pointer to return next cluster
        movzx   edi,di                  ; EDI = Next cluster
        mov     [esi],edi               ; Store Next cluster to use
        mov     esi,pSecRemain          ; ESI = pointer to return secs. remain
        movzx   eax,ax                  ; EAX = number of sectors remaining
        mov     [esi],eax               ; Store number of sectors remaining
        xor     eax,eax
seter:
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _SetupPhysIO

;***LP  _FigRec
;
;       _FigRec calls 16 bit version.
;
;       ENTRY   DWORD   ClusterNum
;               DWORD   SecClusPos
;               DWORD   hVPB
;
;       EXIT    EAX     RBA
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _FigRec,near

        ArgVar  ClusterNum,DWORD        ; Cluster to use in calculation
        ArgVar  SecClusPos,DWORD        ; Sector Cluster Position
        ArgVar  hVPB,DWORD              ; handle to volume

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        mov     edx,ClusterNum          ; DX = Cluster number
        mov     ebx,SecClusPos          ; BL = Sector cluster position
        push    GSEL GDT_VPB
        pop     es
        mov     esi,hVPB                ; ES:[SI]->VPB
        FigRec32 <SI>                   ; SI:DX = RBA
        movzx   eax,si
        shl     eax,16
        mov     ax,dx                   ; EAX = RBA
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _FigRec

;***LP  _ReqListAdd_REQ
;
;       _ReqListAdd_Req calls 16 bit version.
;
;       ENTRY   DWORD   rba
;               DWORD   count
;               DWORD   command
;               DWORD   ReqListSel
;               DWORD   ReqListOff
;               DWORD   Priority
;               DWORD   hVPB
;
;       EXIT
;               if success
;                       eax=0
;               else
;                       eax=error code
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _ReqListAdd_REQ,near

        ArgVar  rba,DWORD               ; RBA of request to add
        ArgVar  count,DWORD             ; count of sectors to add
        ArgVar  command,DWORD           ; command
        ArgVar  ReqListSel,DWORD        ; Request List Selector
        ArgVar  ReqListOff,DWORD        ; Request List Offset
        ArgVar  priority,DWORD          ; Priority of new request
        ArgVar  hVPB,DWORD              ; handle to volume

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        mov     edi,ReqListOff
        mov     eax,ReqListSel
        mov     es,ax                   ; ES:[DI]->Request List
        mov     eax,command             ; AL = command
        mov     ebx,priority
        mov     ah,bl                   ; AH = priority
        mov     ecx,count               ; CX = count
        mov     edx,rba
        mov     ebx,edx
        shr     ebx,16                  ; BX:DX = rba
        mov     esi,hVPB                ; SI = hVPB
        CALL16  Req_List_Add_Req
        movzx   eax,ax
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _ReqListAdd_REQ

;***LP  _ReqListAdd_Extent
;
;       _ReqListAdd_Extent calls 16 bit version.
;
;       ENTRY   DWORD   rba
;               DWORD   count
;               DWORD   command
;               DWORD   ReqListSel
;               DWORD   ReqListOff
;               DWORD   IOoffset
;
;       EXIT
;               if success
;                       eax=0
;               else
;                       eax=error code
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _ReqListAdd_Extent,near

        ArgVar  rba,DWORD               ; RBA of request to add
        ArgVar  count,DWORD             ; count of sectors to add
        ArgVar  command,DWORD           ; command
        ArgVar  ReqListSel,DWORD        ; Request List Selector
        ArgVar  ReqListOff,DWORD        ; Request List Offset
        ArgVar  IOoffset,DWORD          ; User IO offset

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        mov     edi,ReqListOff
        mov     eax,ReqListSel
        mov     es,ax                   ; ES:[DI]->Request List
        mov     eax,command             ; AL = command
        mov     ecx,count               ; CX = count
        mov     edx,rba
        mov     ebx,edx
        shr     ebx,16                  ; BX:DX = RBA
        mov     esi,IOoffset            ; SI = user IO offset
        CALL16  Req_List_Add_Extent
        movzx   eax,ax
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _ReqListAdd_Extent

;***LP  _ReqListAdd_SG
;
;       _ReqListAdd_SG calls 16 bit version.
;
;       ENTRY   DWORD   count
;               DWORD   ReqListSel
;               DWORD   ReqListOff
;               DWORD   sgaddr
;
;       EXIT    NONE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _ReqListAdd_SG,near

        ArgVar  count,DWORD             ; Count bytes to add
        ArgVar  ReqListSel,DWORD        ; Request List Selector
        ArgVar  ReqListOff,DWORD        ; Request List Offset
        ArgVar  sgaddr,DWORD            ; Physical Address of data

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        SaveReg <ds,es,bp>
        mov     edi,ReqListOff
        mov     eax,ReqListSel
        mov     es,ax                   ; ES:[DI]->Request List
        mov     ecx,count               ; CX = Count bytes to add
        mov     edx,sgaddr
        mov     ebx,edx
        shr     ebx,16                  ; BX:DX = Physical Address of data
        CALL16  Req_List_Add_SG
        RestoreReg <bp,es,ds>
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _ReqListAdd_SG

;***EP  _ReqListExecute
;
;       _ReqListExecute calls 16 bit version.
;
;       ENTRY   DWORD   ReqListSel
;               DWORD   ReqListOff
;
;       EXIT
;               if success
;                       eax=0
;               else
;                       eax=error code
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _ReqListExecute,near

        ArgVar  ReqListSel,DWORD        ; Request List Selector
        ArgVar  ReqListOff,DWORD        ; Request List Offset

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        SaveReg <ds,es,bp>
        mov     eax,ReqListSel
        mov     es,ax
        mov     edi,ReqListOff          ; ES:[DI]->Request List
        mov     ax,es:[di].RLCB_hVPB    ; AX = Handle to volume
        CALL16  Req_List_Exec
        jc      rle1                    ; If error go return AX = Error Code
        xor     ax,ax                   ; NO ERROR
rle1:   movzx   eax,ax
        RestoreReg <bp,es,ds>
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _ReqListExecute

;***EP  _ReqListAlloc
;
;       _ReqListAlloc allocates a strategy2 request list.
;
;       ENTRY   DWORD   pReqListSel
;               DWORD   pReqListOff
;
;       EXIT    NONE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _ReqListAlloc,near

        ArgVar  pReqListSel,DWORD       ; Pointer to new Request List Selector
        ArgVar  pReqListOff,DWORD       ; Pointer to new Request List Offset

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        CALL16  GetReqList              ; ES:[DI]-> New Request List
        mov     esi,pReqListSel         ; ESI = pointer to return reqlist Sel
        mov     ax,es
        movzx   eax,ax                  ; EAX = Request List Selector
        mov     ds:[esi],eax            ; Store new request list selector
        mov     esi,pReqListOff         ; ESI = pointer to return reqlist Off
        movzx   edi,di                  ; EDI = Request List Offset
        mov     ds:[esi],edi            ; Store new request list offset
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _ReqListAlloc

;***EP  _ReqListInit
;
;       _ReqListInit calls 16 bit version.
;
;       ENTRY   DWORD   ReqListSel
;               DWORD   ReqListOff
;               DWORD   hVPB
;               DWORD   Flags
;
;       EXIT    NONE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _ReqListInit,near

        ArgVar  ReqListSel,DWORD        ; Request List Selector
        ArgVar  ReqListOff,DWORD        ; Request List Offset
        ArgVar  hVPB,DWORD              ; Handle to volume
        ArgVar  Flags,DWORD             ; Flags

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        SaveReg <ebp>
        mov     eax,ReqListSel
        mov     es,ax
        mov     edi,ReqListOff          ; ES:[DI]->Request List
        mov     ecx,Flags               ; CX = Flags
        mov     ebp,hVPB                ; BP = hVPB
        CALL16  Req_List_Init           ; Initialize request list
        RestoreReg <ebp>
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _ReqListInit


;***LP  _ReqListLnkPtr
;
;       _ReqListLnkPtr links a pointer for the client onto a request list.
;
;       ENTRY   DWORD   ClientPtr
;               DWORD   ClientPtrLnk
;               DWORD   ReqListSel
;               DWORD   ReqListOff
;
;       EXIT    NONE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _ReqListLnkPtr,near

        ArgVar  ClientPtr,DWORD            ; New Client Ptr
        ArgVar  ClientPtrLnk,DWORD         ; New Client Ptr link
        ArgVar  ReqListSel,DWORD           ; Request List Selector
        ArgVar  ReqListOff,DWORD           ; Request List Offset

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        mov     eax,ReqListSel
        mov     es,ax
        mov     edi,ReqListOff             ; ES:[DI]->Request List
        mov     esi,es:[di].RLCB_ClientPtr ; ESI = Tail element
        mov     ebx,ClientPtrLnk           ; EBX = New Tail
        mov     [ebx],esi                  ; Set ClientPtr link to old tail
        mov     eax,ClientPtr
        mov     es:[di].RLCB_ClientPtr,eax ; Set new tail = New Client Ptr

        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _ReqListLnkPtr

;***EP  _ReqList_GetNextRead
;
;       _ReqList_GetNextRead gets the next async read request list pointer from
;       the chain.
;
;       ENTRY   DWORD   ReqListSel
;               DWORD   ReqListOff
;               DWORD   pReqListSel
;               DWORD   pReqListOff
;
;       EXIT    NONE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _ReqList_GetNextRead,near

        ArgVar  ReqListSel,DWORD             ; Request List Selector
        ArgVar  ReqListOff,DWORD             ; Request List Offset
        ArgVar  pReqListSel,DWORD            ; Pointer to return next read sel
        ArgVar  pReqListOff,DWORD            ; Pointer to return next read off

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        mov     eax,ReqListSel
        mov     es,ax
        mov     edi,ReqListOff               ; ES:[DI]->Request List
        movzx   eax,es:[di].RLCB_NextReadSel ; Get Next Readahead Request Sel.
        mov     esi,pReqListSel
        mov     ds:[esi],eax                 ; Store it
        movzx   eax,es:[di].RLCB_NextReadOff ; Get Next Readahead Request Sel.
        mov     esi,pReqListOff
        mov     ds:[esi],eax                 ; Store it
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _ReqList_GetNextRead

;***EP  _ReqList_GetReadAheadData
;
;       _ReqList_GetReadAheadData returns the first client ptr
;       associated with this request list.
;
;       ENTRY   DWORD   ReqListSel
;               DWORD   ReqListOff
;               DWORD   ppFirstBuf
;               DWORD   pFATBufferOff
;
;       EXIT    NONE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _ReqList_GetReadAheadData,near

        ArgVar  ReqListSel,DWORD              ; Request List Selector
        ArgVar  ReqListOff,DWORD              ; Request List Offset
        ArgVar  ppFirstBuf,DWORD              ; Pointer to return readahead buf

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        mov     eax,ReqListSel
        mov     es,ax
        mov     edi,ReqListOff                ; ES:[DI]->Request List
        mov     eax,es:[di].RLCB_ClientPtr    ; EAX = First ptr on chain
        mov     esi,ppFirstBuf
        mov     ds:[esi],eax                  ; Store EAX
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _ReqList_GetReadAheadData

;***EP  _ReqList_SetNextRead
;
;       _ReqList_SetNextRead links a new request list at the end of a chain.
;
;       ENTRY   DWORD   TailReqListSel
;               DWORD   TailReqListOff
;               DWORD   NewReqListSel
;               DWORD   NewReqListOff
;
;       EXIT    NONE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _ReqList_SetNextRead,near

        ArgVar  TailReqListSel,DWORD        ; Tail Request List Selector
        ArgVar  TailReqListOff,DWORD        ; Tail Request List Offset
        ArgVar  NewReqListSel,DWORD         ; New Request List Selector
        ArgVar  NewReqListOff,DWORD         ; New Request List Offset

        EnterProc
        SaveReg <edi,ebx,es>

        mov     eax,TailReqListSel
        mov     es,ax
        mov     edi,TailReqListOff          ; ES:[DI]->Tail Request List
        mov     eax,NewReqListSel
        mov     ebx,NewReqListOff           ; AX:[BX]->New Request List
        mov     es:[di].RLCB_NextReadSel,ax ; Set Old Tail fwd Sel
        mov     es:[di].RLCB_NextReadOff,bx ; Set Old Tail fwd Offset
        mov     es,ax
        mov     es:[bx].RLCB_NextReadSel,0  ; New Request list has no fwd Sel
        mov     es:[bx].RLCB_NextReadOff,0  ; New Request list has no fwd Off

        RestoreReg <es,ebx,edi>
        LeaveProc
        RETC
EndProc _ReqList_SetNextRead

;***LP  _IsEOF
;
;       _IsEOF calls 16 bit version.
;
;       ENTRY   DWORD   cluster
;               DWORD   hVPB
;
;       EXIT    TRUE    if End-Of-File
;               FALSE   if not End-Of-File
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _IsEOF,near

        ArgVar  cluster,DWORD           ; Cluster
        ArgVar  hVPB,DWORD              ; handle to volume

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        SaveReg <ebp>
        mov     ebx,cluster             ; BX = Cluster
        mov     ebp,hVPB
        push    GSEL GDT_VPB
        pop     es                      ; ES:[BP]->VPB
        CALL16  IsEOF
        mov     eax,1                   ; Assume End-Of-File
        jae     eof
        xor     eax,eax                 ; Not End-Of-File
eof:
        RestoreReg <ebp>
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _IsEOF

;***LP  _NextSec
;
;       _NextSec calls 16 bit version.
;
;       ENTRY   DWORD   fDirect
;               DWORD   hVPB
;
;       EXIT
;               if success
;                       eax=0
;               else
;                       eax=error code
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _NextSec,near
        ArgVar  fDirect,DWORD           ; Direct Access flag
        ArgVar  hVPB,DWORD              ; handle to volume

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        SaveReg <ds,es,bp>
        mov     esi,fDirect             ; SI = Direct Access flag
        mov     ebp,hVPB
        push    GSEL GDT_VPB
        pop     es                      ; ES:[BP]->VPB
        TaskContext DS
        CALL16  NextSec                 ; Advance to next sector
        mov     eax,1                   ; Assume ERROR
        jc      nsece                   ; if error assumption correct
        xor     eax,eax                 ; NO Error
nsece:
        RestoreReg <bp,es,ds>
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _NextSec


;***LP  _DRead
;
;       _DRead calls 16 bit version of Req_List_Read.
;
;       ENTRY   DWORD   rba
;               DWORD   count
;               DWORD   addr
;               DWORD   hVPB
;               DWORD   fLazyWrite
;
;       EXIT
;               if success
;                       eax=0
;               else
;                       eax=error code
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _DRead,near

        ArgVar  rba,DWORD               ; RBA to read from
        ArgVar  count,DWORD             ; count of sectors to read
        ArgVar  addr,DWORD              ; Physical Address of data
        ArgVar  hVPB,DWORD              ; handle to volume of data

        EnterProc
        SaveReg <esi,edi,ebx,es,ds,bp>

        mov     edx,rba
        mov     edi,edx
        shr     edi,16                  ; di:dx = RBA
        mov     ecx,count               ; cx = number of sectors
        mov     esi,hVPB                ; si = hVPB
        mov     ebx,addr                ; addr of data
        CALL16  Req_List_Read           ; Read data
        movzx   eax,ax                  ; EAX = error if any
        RestoreReg <bp,ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _DRead

;***LP  _DWrite
;
;       _DWrite calls 16 bit version of Req_List_Write.
;
;       ENTRY   DWORD   rba
;               DWORD   count
;               DWORD   addr
;               DWORD   hVPB
;               DWORD   ReqListSel
;
;       EXIT
;               if success
;                       eax=0
;               else
;                       eax=error code
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _DWrite,near

        ArgVar  rba,DWORD               ; RBA to read from
        ArgVar  count,DWORD             ; count of sectors to write
        ArgVar  addr,DWORD              ; Physical Address of data
        ArgVar  hVPB,DWORD              ; handle to volume of data
        ArgVar  ReqListSel,DWORD        ; ReqList Selector

        EnterProc
        SaveReg <esi,edi,ebx,es,ds,bp>

        mov     edx,rba
        mov     edi,edx
        shr     edi,16                  ; di:dx = RBA
        mov     ecx,count               ; cx = number of sectors
        mov     esi,hVPB                ; si = hVPB
        mov     ebx,addr                ; addr of data
        mov     eax,ReqListSel          ; ax = ReqList Selector
        CALL16  Req_List_Write          ; Go write the data
        movzx   eax,ax                  ; EAX = Error if any
        RestoreReg <bp,ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _DWrite

;***EP  _FreeDosBuffer
;
;       _FreeDosBuffer calls 16 bit version of BufFree.
;
;       Extended Attributes uses the DOS buffers to access some of its
;       information. Therefore, when we have a non-partial write
;       we need to free all dos buffers in the range we are writing.
;
;       This only applies for EAs. All other reads and writes except for
;       FAT and Directory sectors bypass the DOS buffers and come directly
;       into the FAT cache.
;
;       ENTRY   DWORD   rba
;               DWORD   count
;               DWORD   hVPB
;
;       EXIT    NONE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _FreeDosBuffer,near

        ArgVar  rba,DWORD               ; Starting RBA of EA write
        ArgVar  count,DWORD             ; count EA sectors being written
        ArgVar  hVPB,DWORD              ; handle to volume of EA data

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>
        mov     esi,rba                 ; ESI = starting rba
        mov     eax,esi
        mov     ecx,count               ; ECX = count of sectors
        add     eax,ecx                 ; EAX = RBA + count (ie. end sect. + 1)
        push    ebp
        mov     ebp,hVPB                ; EBP = handle to volume
        CALL16  BufFree
        pop     ebp
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _FreeDosBuffer

;***EP  _WriteDosBuffer
;
;       _WriteDosBuffer calls 16 bit version of BufWrt.
;
;       Extended Attributes uses the DOS buffers to access some of its
;       information. If the EA code read in a sector using DOS buffers,
;       and then did a DoDiskWrite which does not use DOS buffers for partial
;       sectors, and then called GetBuffr to retrieve the same sector, it
;       would not find the data that it just wrote.
;
;       So, the solution is for the DoDiskWrite to detect if this an EA write
;       and if so pass a flag to _BufWrite. If the EA flag is set and there
;       is partial sector, _BufWrite will call this routine to put the data
;       in the DOS buffers so the EA code can find it.
;
;       This only applies for EAs. All other reads and writes except for
;       FAT and Directory sectors bypass the DOS buffers and come directly
;       into the FAT cache.
;
;       ENTRY   DWORD   ByteCount
;               DWORD   hVPB
;
;       EXIT    NONE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _WriteDosBuffer,near

        ArgVar  bytecount,DWORD         ; Number of bytes to write
        ArgVar  hVPB,DWORD              ; Handle to volume of EA partial write

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>
        push    GSEL GDT_VPB
        pop     es
        xor     esi,esi                 ; Normal File System Write
        mov     ecx,bytecount           ; CX = Byte count
        push    ebp                     ; ES:[BP]->VPB
        mov     ebp,hVPB
        CALL16  BufWrt
        pop     ebp
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _WriteDosBuffer

;***EP  _IsDriveLocked
;
;       _IsDriveLocked determines whether the drive is locked.
;
;       ENTRY   DWORD   hVPB
;
;       EXIT    TRUE    if drive is locked
;               FALSE   if drive is not locked
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _IsDriveLocked,near

        ArgVar  hVPB,DWORD

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>
ASSUME DS:NOTHING,ES:NOTHING
        push    GSEL GDT_VPB
        pop     es
        mov     ebx,hVPB
        xor     eax,eax                      ; Assume drive not lock
        lds     si,es:[bx].vpb_fsi.vpi_pDPB  ; ES:BX->DPB
        cmp     ds:[si].dpb_drive_lock,0     ; is drive locked ?
        je      idrv11                       ; No, assumption correct
        inc     eax                          ; Yes, signal drive locked
idrv11: RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _IsDriveLocked

;***EP  _GetVIDSector
;
;       _GetVIDSector gets the VID Sector for volume.
;
;       ENTRY   DWORD   hVPB
;
;       EXIT    EAX     VID Sector for volume
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _GetVIDSector,near

        ArgVar  hVPB,DWORD              ; handel to volume

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>
ASSUME DS:NOTHING,ES:NOTHING
        push    GSEL GDT_VPB
        pop     es
        mov     ebx,hVPB                             ; ES:[BX]->VPB
        mov     eax,es:[bx].vpb_fsd.vpdFAT_IDsector  ; EAX = VID sector
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _GetVIDSector

;***EP  _UpdateVID
;
;       _UpdateVID updates the VID in the VPB from the VID sector passed in.
;
;       ENTRY   DWORD   hVPB
;               WORD    BufSel
;               WORD    BufOff
;               DWORD   Rba
;               DWORD   count
;
;
;       EXIT    NONE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:TASKAREA
Procedure _UpdateVID,near
        ArgVar  hVPB,DWORD              ; Handle to volume
        ArgVar  BufSel,WORD             ; Selector of buffer containing new VID
        ArgVar  BufOff,WORD             ; Offset of buffer containing new VID
        ArgVar  Rba,DWORD               ; RBA
        ArgVar  count,DWORD             ; count of sectors in buffer

        EnterProc

        SaveReg <ebp,esi,edi,ebx,es,ds>

        push    GSEL GDT_VPB
        pop     es
        mov     ds,BufSel
        mov     bx,BufOff               ; DS:[BX]->Buffer
        mov     edx,Rba
        mov     edi,edx
        shr     edi,16                  ; DI:DX = RBA
        mov     ecx,count               ; CX = count of sectors in buffer
        mov     ebp,hVPB                ; ES:[BP]->VPB
;
; Check to see if any of the sectors just written are used to build the VID
; ES:BP -> VPB
; CX = sector count
; DI:DX = starting logical sector
; DS:BX = pointer to user buffer
;
        mov     si,es:[bp].vpb_fsd.vpdFAT_IDsector.HighWord
        mov     ax,es:[bp].vpb_fsd.vpdFAT_IDsector.LowWord
;
; We've just written a new volume ID sector.  Use the copy of the
; data in memory to update the VPB.
; DS:BX -> transfer area
; DI:DX = first sector written
; SI:AX = ID sector
; CX = number of sectors written
;
        sub     ax,dx                   ; (AX) = sector index of ID sector
        mov     cx,es:[bp].vpb_fsi.vpi_cbSector ; (CX) = sector size
        mul     cx                      ; (AX) = sector offset of ID sector
        mov     si,ax                   ; (SI) = sector offset of ID sector
        add     si,bx                   ; (DS:SI) -> new ID sector
        test    es:[bp].vpb_flags,VPBNEWBOOT
        jz      uvid1                   ; Not new, go crc

;       New style volume, copy ID from boot sector into VPB

        mov     ax,ds:[si].boot_ID.LowWord
        mov     es:[bp].vpb_fsi.vpi_ID.LowWord,ax
        mov     ax,ds:[si].boot_ID.HighWord
        mov     es:[bp].vpb_fsi.vpi_ID.HighWord,ax
        jmp     short uvid2

;       Old style volume, apply CRC to FAT sector

uvid1:  CALL16  CRC                     ; (DX:AX) = CRC
        mov     es:[bp].vpb_fsi.vpi_ID.LowWord,ax
        mov     es:[bp].vpb_fsi.vpi_ID.HighWord,dx

uvid2:  RestoreReg <ds,es,ebx,edi,esi,ebp>
        LeaveProc
        RETC
EndProc _UpdateVID

;***EP  AllocCluster
;
;       AllocCluster allocates cx clusters from a bitmap. If a bit position is
;       on then that corresponding cluster is free. If a bit position is off
;       then that corresponding cluster is allocated. Therefore, when we find
;       a free cluster we turn the corresponding bit off.
;
;       ENTRY  SI = hVPB
;              CX = # of clusters to allocate
;              AX = Allocate Type
;                   0 = File
;                   1 = EA
;              BX  = Last cluster of file (0 if null file)
;
;
;       EXIT    'C' if error
;                   if disk is full,
;                       AX = ERROR_DISK_FULL
;                       CX = No.of clusters possible to allocate
;               'NC' if space allocated
;                   BX = First cluster allocated
;                   FAT is fully updated
;                   if file
;                       sf_fsd.sfdFAT_firFILEclus field set if file was null
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:TASKAREA
Procedure AllocCluster,near
LocalVar aclus_type,WORD                ; Alloc Type File vs EA
LocalVar aclus_count,WORD               ; Count of clusters to allocate
LocalVar aclus_count2,WORD              ; Saved count of clusters to allocate
LocalVar aclus_FirstClus,WORD           ; Last allocation of file
LocalVar aclus_Chain,WORD               ; First cluster allocated
LocalVar aclus_PrevClus,WORD            ; Previous cluster allocated
LocalVar aclus_pScanStart,DWORD         ; BitMap Position Start
LocalVar aclus_ClusCnt,WORD             ; Count of DWORD clusters on volume
LocalVar aclus_pVPB,DWORD               ; Ptr ot VPB
LocalVar aclus_cScanStart,WORD          ; Count of clusters initially


LocalVar aclus_maxcluster,WORD
LocalVar aclus_ah,WORD
LocalVar aclus_si,WORD
LocalVar aclus_edi,DWORD
LocalVar aclus_currentcount,WORD
LocalVar aclus_save_count,WORD
LocalVar aclus_save_ah,WORD
LocalVar aclus_save_si,WORD
LocalVar aclus_save_edi,DWORD
LocalVar aclus_firstencounter,WORD
LocalVar aclus_round,WORD


        EnterProc <ax,cx,cx,bx,0,0>
        SaveReg <esi,edi,edx,es,ds>
        push    GSEL GDT_VPB
        pop     ds
        mov     aclus_pVPB.Segmt,ds
        mov     aclus_pVPB.Offst,si     ; Save pointer to VPB

        mov     aclus_round,1
        movzx   eax,ds:[si].vpb_fsd.vpdFAT_max_cluster
        mov     cx,8                    ;
        xor     dx,dx
        div     cx
        or      dx,dx                   ; any remainder ?
        jz      aclus00                 ; no, no increment
        inc     ax                      ; ax = number of bytes of clusters
aclus00:
        mov     aclus_maxcluster,ax
        add     ax,3
        shr     ax,2
        mov     aclus_ClusCnt,ax        ; ax =  dword number of clusters

        mov     eax,ds:[si].vpb_fsd.vpdFAT_pNextFreeBitMap
        mov     aclus_pScanStart,eax
        mov     ax,ds:[si].vpb_fsd.vpdFAT_cNextFreeBitMap
        mov     aclus_cScanStart,ax

        xor     eax,eax
        mov     aclus_firstencounter,ax
        mov     aclus_currentcount,ax
        mov     aclus_save_count,ax
        mov     aclus_save_edi,eax
        mov     aclus_save_si,ax
        mov     aclus_save_ah,ax

aclus0: lds     si,aclus_pVPB
        mov     edi,ds:[si].vpb_fsd.vpdFAT_pNextFreeBitMap
        movzx   ecx,ds:[si].vpb_fsd.vpdFAT_cNextFreeBitMap
        FlatContext ES,ax
        xor     eax,eax
        repe    scasd                   ; look for free cluster
        je      aclus_volume_end
        inc     cx
        mov     ds:[si].vpb_fsd.vpdFAT_cNextFreeBitMap,cx
        sub     cx,aclus_ClusCnt
        neg     cx                      ; cx = dword offset of free cluster
        sub     edi,4
        mov     ds:[si].vpb_fsd.vpdFAT_pNextFreeBitMap,edi
        mov     si,cx
        shl     si,2                    ; si = offset of freecluster
        xor     al,al

aclus1:
        cmp     es:[edi],al
        jne     aclus2
        inc     edi
        inc     si
        jmp     short aclus1

        ;  start searching for "1" in this byte
        ;  from the first bit of this byte

aclus2: mov     al,es:[edi]
        mov     ah,01h

aclus3: test    al,ah
        jnz     aclus32                  ; We got a "1" here !

        ;  We got a "0" here, see if we need to restart another round of
        ;  search.

        cmp     aclus_firstencounter,0
        je      short aclus302            ; No, go ahead

        ;  We have to restart a new round of search for "1"s.
        ;  First, update the longest 1-string data if a longer string
        ;  was found.

        xor     ebx,ebx
        mov     aclus_firstencounter,bx
        mov     bx,aclus_save_count
        cmp     bx,aclus_currentcount
        jae     short aclus302
        mov     bx,aclus_currentcount
        mov     aclus_save_count,bx
        mov     bx,aclus_ah
        mov     aclus_save_ah,bx
        mov     bx,aclus_si
        mov     aclus_save_si,bx
        mov     ebx,aclus_edi
        mov     aclus_save_edi,ebx

        ;  Skip all "0"s and look for the first "1".

aclus302:
        rol     ah,1
        cmp     ah,01h
        jne     short aclus3

        ;  all the bits in the byte have been
        ;  scanned, go to the next byte.

        xor     ax,ax

aclus300:
        inc     edi
        inc     si
        cmp     si,aclus_maxcluster
        jae     aclus_volume_end   ; #78707

aclus301:
        cmp     aclus_firstencounter,0
        je      aclus311
        mov     bl,0ffh
        cmp     es:[edi],bl
        jne     short aclus2

        ;  encounter '11111111' pattern in the byte;
        ;  update byte information instead of bit information.

        add     aclus_currentcount,8
        mov     bx,aclus_currentcount
        cmp     bx,aclus_count
        jae     aclus_done               ; We got enough contiguous clusters!!
        jmp     short aclus300

aclus310:
        inc     edi
        inc     si
        cmp     si,aclus_maxcluster
        jae     aclus_volume_end       ;#78707

aclus311:
        xor     bx,bx
        cmp     es:[edi],bl
        jne     aclus2

        ;  encounter '00000000' pattern in the byte.
        ;  skip this byte.

        jmp     short aclus310

aclus32:
        cmp     aclus_firstencounter,0
        jne     short aclus33
        mov     aclus_firstencounter,1
        mov     aclus_currentcount,1
        mov     aclus_ah,ax
        mov     aclus_si,si
        mov     aclus_edi,edi
        cmp     aclus_count,1
        je      aclus_done               ; We got enough contiguous clusters!!
        jmp     aclus302

aclus33:
        inc     aclus_currentcount
        mov     bx,aclus_currentcount
        cmp     bx,aclus_count
        jae     aclus_done               ; We got enough contiguous clusters!!
        jmp     aclus302

       ; We reach the end of the VOLUME, and not satisfied.
       ; See if we've scanned the whole volume.
       ; First, update the longest 1-string data if a longer string
       ; was found.

aclus_volume_end:
        cmp     aclus_firstencounter,0
        je      short aclus_volume_end2
        xor     ebx,ebx
        mov     aclus_firstencounter,bx
        mov     bx,aclus_save_count
        cmp     bx,aclus_currentcount
        jae     short aclus_volume_end2
        mov     bx,aclus_currentcount
        mov     aclus_save_count,bx
        mov     bx,aclus_ah
        mov     aclus_save_ah,bx
        mov     bx,aclus_si
        mov     aclus_save_si,bx
        mov     ebx,aclus_edi
        mov     aclus_save_edi,ebx

aclus_volume_end2:
        lds     si,aclus_pVPB
        mov     eax,ds:[si].vpb_fsd.vpdFAT_pClusBitMap
        cmp     eax,aclus_pScanStart
        je      short aclus_fail
        mov     aclus_pScanStart,eax
        mov     ds:[si].vpb_fsd.vpdFAT_pNextFreeBitMap,eax
        mov     ax,aclus_ClusCnt
        mov     ds:[si].vpb_fsd.vpdFAT_cNextFreeBitMap,ax
        jmp     aclus0

       ; We scanned the whole VOLUME and can not find
       ; enough contiguous sectors for the caller.
       ; So, try to do our best.

aclus_fail:
        cmp     aclus_save_count,0
        je      aclus45                    ; Allocation fails.
        mov     si,aclus_save_si
        mov     aclus_si,si
        mov     edi,aclus_save_edi
        mov     aclus_edi,edi
        mov     ax,aclus_save_ah
        mov     aclus_ah,ax
        mov     al,es:[edi]
        jmp     short aclus4

aclus_done:
        mov     si,aclus_si
        mov     edi,aclus_edi
        mov     ax,aclus_ah
        mov     al,es:[edi]

 ;  The followings are the location information about where to allocate
 ;  the first free bit.
 ;
 ;      ah  = Position within byte
 ;      si  = Relative Cluster BitMap byte
 ;      edi = byte offset in bitmap table
 ;

aclus4: mov     cx,8
        push    ax
        mov     ax,si
        mul     cx                      ; ax  = Starting cluster # of byte
        pop     dx                      ; dh  = Position within byte

        ; Store Updated byte in BitMap Table

        SaveReg <edx>
        not     dh
        and     es:[edi],dh
        RestoreReg <edx>

        ; Put Cluster Number in BX register (ax = Starting Cluster)

        SaveReg <ax>
        DosContext DS
        xor     ah,ah
        mov     al,dh
        mov     bx,offset DOSGROUP:ClusBitIndex
        cmp     al,10h
        jb      aclus5
        shr     al,4
        mov     bx,offset DOSGROUP:ClusBitIndex2
aclus5: xlat    byte ptr ds:[bx]
        xor     ah,ah
        mov     dx,ax
        RestoreReg <ax>
        ASSUME  DS:NOTHING

        mov     bx,ax
        add     bx,dx                   ; bx = New Cluster Number

IFDEF FSSTRICT
        lds     si,aclus_pVPB
        cmp     ds:[si].vpb_fsd.vpdFAT_max_cluster,bx
        jae     dball1
        int     3
dball1: ASSUME  DS:NOTHING
ENDIF

;  11-22-93. The following piece of code is removed because it is redundant.
;  There is no need to "double packing" the same FAT since in the
;  bitmap we already mark the cluster as NOT-FREE and no other thread
;  can steal that cluster.
;
;        SaveReg <bp,bx>
;        les     bp,aclus_pVPB           ; es:bp->VPB
;        mov     dx,1                    ; mark this free guy as "1"
;        TaskContext DS
;        CALL16  Pack
;        RestoreReg <bx,bp>
;        ASSUME  DS:NOTHING
;        jc      aclus60
;

        lds     si,aclus_pVPB
        cmp     ds:[si].vpb_fsd.vpdFAT_free_cnt,-1 ; Free count valid?
        jz      short aclus6            ; No
        dec     ds:[si].vpb_fsd.vpdFAT_free_cnt    ; Reduce free count by 1
aclus6:                                 ; b723519
        cmp     aclus_PrevClus,0        ; previous cluster zero?
        jz      short aclus8            ; yes, don't overwrite FAT byte
        SaveReg <bp,bx>
        mov     dx,bx
        mov     bx,aclus_PrevClus
        les     bp,aclus_pVPB           ; es:bp->VPB
        TaskContext DS
        CALL16  Pack
        RestoreReg <bx,bp>
        ASSUME  DS:NOTHING
        jc      aclus50

        test    [aclus_type],-1
        jnz     short aclus9           ; Don't update SFT for EA allocation

        mov     ax,aclus_pVPB.Offst
        CALL16  WriteFATOrdered

        jmp     short aclus9
aclus8: mov     aclus_Chain,bx

        test    [aclus_type],-1
        jnz     short aclus9           ; Don't update SFT for EA allocation

        SaveReg <ds,ax,es,di>
        FlatContext DS
        mov     edi,_pTCBCur
        les     di,ds:[edi].TCBThisSFT
        CALL16  GetMRUBufSec
        MOV     ES:[DI.sf_fsd.sfdFAT_LastFATSec],ax
        RestoreReg <di,es,ax,ds>

aclus9: mov     aclus_PrevClus,bx
        dec     aclus_count
        jz      aclus907                   ; We got all we want.

        mov     si,aclus_si
        mov     edi,aclus_edi
        FlatContext ES
        mov     ax,aclus_ah
        mov     al,es:[edi]
aclus901:
        rol     ah,1
        mov     aclus_ah,ax
        cmp     ah,01h
        jne     aclus902

       ;  Reach the end of the byte, get the next byte in the bitmap.

        inc     aclus_si
        mov     si,aclus_si
        inc     aclus_edi
        mov     edi,aclus_edi
        cmp     si,aclus_maxcluster
        jae     aclus_volume_end_again  ;#78707
        mov     al,es:[edi]

       ;  Find the next free bit (cluster) in the byte.

aclus902:
        test    al,ah
        jnz     aclus4                      ; go and allocate it.
        jmp     short aclus901

       ;  We have reached the end of the volume during the allocation phase.
       ;  We can not afford to do another scanning phase since the worst case
       ;  would be awful. So we use the old strategy to allocate any free
       ;  cluster encountered until enough clusters are allocated.

aclus_volume_end_again:
        cmp     aclus_round, 2
        je      aclus45                     ; Allocation fails !
        inc     aclus_round
        lds     si,aclus_pVPB
        mov     eax,ds:[si].vpb_fsd.vpdFAT_pClusBitMap
        mov     aclus_pScanStart,eax
        mov     ds:[si].vpb_fsd.vpdFAT_pNextFreeBitMap,eax
        mov     ax,aclus_ClusCnt
        mov     ds:[si].vpb_fsd.vpdFAT_cNextFreeBitMap,ax
        mov     edi,ds:[si].vpb_fsd.vpdFAT_pNextFreeBitMap
        movzx   ecx,ds:[si].vpb_fsd.vpdFAT_cNextFreeBitMap
        FlatContext ES,ax
        xor     eax,eax
        repe    scasd                   ; look for free cluster
        je      aclus45                 ; Allocation fails !
        inc     cx
        mov     ds:[si].vpb_fsd.vpdFAT_cNextFreeBitMap,cx
        sub     cx,aclus_ClusCnt
        neg     cx                      ; cx = dword offset of free cluster
        sub     edi,4
        mov     ds:[si].vpb_fsd.vpdFAT_pNextFreeBitMap,edi
        mov     si,cx
        shl     si,2                    ; si = offset of freecluster
        xor     al,al
aclus903:
        cmp     es:[edi],al
        jne     short aclus904
        inc     edi
        inc     si
        jmp     short aclus903
aclus904:
        mov     al,es:[edi]
        mov     ah,01h
aclus905:
        test    al,ah
        jnz     short aclus906
        rol     ah,1
        cmp     ah,01h
        jne     short aclus905
        Panic   <"AllocCluster: Previous Free Cluster Corrupted">
aclus906:
        mov     aclus_si,si
        mov     aclus_edi,edi
        mov     aclus_ah,ax
        jmp     aclus4


;   We've successfully extended the file.  Clean up and exit
;
;   (BX) = last cluster in file

aclus907:
        mov     dx,0ffffH
        SaveReg <bp,bx>
        les     bp,aclus_pVPB           ; (BP) = VPB handle
        TaskContext DS
        CALL16  Pack                    ; mark last cluster EOF
        RestoreReg <bx,bp>
        ASSUME  DS:NOTHING
        jc      aclus60

        test    [aclus_type],-1
        jnz     short aclus91           ; Don't update SFT for EA allocation

        mov     ax,aclus_pVPB.Offst
        CALL16  WriteFATOrdered

aclus91: mov    ax,aclus_Chain          ; (ax) = first cluster allocated
;
; The allocation went fine. If we were allocating for a file, then we
; need to update the cluster information in the SFT and, for existing files,
; also have to link in the new chain to the old.
; For extended attribute allocations, we do not update the SFT.
;
        test    [aclus_type],-1
        jnz     short aclus20           ; Don't update SFT for EA allocation
        cmp     aclus_FirstClus,0       ; first allocation to file?
        jz      short aclus10           ; yes
        mov     dx,ax
        SaveReg <bp,ax>
        mov     bx,aclus_FirstClus
        les     bp,aclus_pVPB           ; (BP) = VPB handle
        TaskContext DS
        CALL16  Pack                    ; add new chain to file
        RestoreReg <bx,bp>
        ASSUME  DS:NOTHING
        jc      aclus60

        mov     ax,bx
        jmp     SHORT aclus20
;
;       We were doing the first allocation for a new file.  Update
;       the SFT cluster info
;
aclus10:FlatContext DS
        mov     edi,_pTCBCur
        les     di,ds:[edi].TCBThisSFT
        mov     ES:[DI.sf_fsd.sfdFAT_firFILEclus],AX
        mov     ES:[DI.sf_fsd.sfdFAT_lstclus],AX
aclus20:mov     bx,ax
        xor     eax,eax                 ; clear carry
aclus30:RestoreReg <ds,es,edx,edi,esi>
        LeaveProc
        RETC

;       Sorry, we've gone over the whole disk, with insufficient
;       luck.  Lets give the space back to the free list and tell
;       the caller how much he could have had.  We have to make sure
;       we remove the "special mark" we put on the last cluster we
;       were able to allocate, so it doesn't become orphaned.
;
;       aclus_count = clusters remaining to be allocated

aclus45:mov     bx,aclus_Chain          ; get start of allocated chain
        or      bx,bx                   ; any allocated?
        jz      short aclus50           ; no
        SaveReg <bp>
        les     bp,aclus_pVPB           ; (BP) = VPB handle
        TaskContext DS
        CALL16  Release
        RestoreReg <bp>
        ASSUME  DS:NOTHING
aclus50:mov     bx,aclus_FirstClus      ; (BX) = last cluster of file
        mov     cx,aclus_count2         ; No. of clusters requested
        sub     cx,aclus_count          ; CX=No. of clusters allocated
        mov     eax,ERROR_DISK_FULL
        stc
        jmp     short aclus30
aclus60:mov     eax,-1
        stc
        jmp     short aclus30

EndProc AllocCluster



;***EP  FreeCluster
;
;       FreeCluster frees a cluster from the bitmap by turning the
;       corresponding bit on.
;
;       ENTRY  bx = cluster to free
;              di = hVPB
;             esi = Cluster Bit Map pointer
;
;       EXIT  NONE
;
;       USES  NONE
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure FreeCluster,near

        EnterProc
        SaveReg <esi,edi,ebx,edx,ecx,eax,es,ds>
        push    GSEL GDT_VPB
        pop     es
        mov     cx,8
        mov     ax,bx
        xor     dx,dx
        div     cx                      ; ax = offset to byte within bit map
                                        ; dx = offset within byte
        SaveReg <ax,es,bx>
        DosContext ES
        mov     ax,dx
        mov     bx,offset DOSGROUP:ClusBitMask
        xlat    byte ptr es:[bx]        ; AL = bit mask to 'OR' byte with
        mov     dx,ax                   ; DL = AL
        RestoreReg <bx,es,ax>
        ASSUME  ES:NOTHING
        movzx   ebx,ax                  ; EBX = byte index in bit map
        FlatContext DS
        or      ds:[esi+ebx],dl         ; Make cluster free

        RestoreReg <ds,es,eax,ecx,edx,ebx,edi,esi>
        LeaveProc
        RETC
EndProc FreeCluster

BREAK <InitVPBCacheData - Initialize VPB Cache Data>
;***    InitVPBCacheData - Initialize VPB Cache Data
;
;       InitVPBCacheData initializes the FAT volume for the new style FAT
;       caching. It reads in the entire FAT in chunks, builds a link list
;       bad sector records and builds a bit map used for fast allocation.
;
;       ENTRY   CX:[DI] = pVBP
;
;       EXIT    EAX  = 0 NO ERROR
;               EAX != 0 ERROR
;
;       USES    ALL
;
;       PSEUDOCODE:
;
;
Procedure   InitVPBCacheData,NEAR
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
LocalVar    ivpbc_pVPB,DWORD
LocalVar    ivpbc_ClusterCount,WORD
LocalVar    ivpbc_TotalClusters,DWORD                                  ;b726249
LocalVar    ivpbc_fatoff,DWORD
LocalVar    ivpbc_pbadrba,DWORD
LocalVar    ivpbc_pClusBitMap,DWORD
LocalVar    ivpbc_Error,DWORD
LocalVar    ivpbc_fatsize,DWORD
LocalVar    ivpbc_fatsec,WORD
LocalVar    ivpbc_sector,WORD
LocalVar    ivpbc_count,WORD
LocalVar    ivpbc_f16BitFAT,WORD
LocalVar    vmacHeapBlockInfo,<size vmac_s>

        EnterProc <cx,di,0,0,0,0,0,0,0,0,0,0,0>                        ;b726249

        mov     vmacHeapBlockInfo.ac_sel,0

; BUGBUG 26-07-1990 Macon Need to handle 2nd FAT

        mov     es,cx
        mov     ivpbc_f16BitFAT,1                          ; assume 16-bit FAT
        cmp     es:[di].vpb_fsd.vpdFAT_FATentrysize,16     ; check with VPB                        ;@@
        je      ivpbc0                                     ; yes, it's 16bit
        mov     ivpbc_f16BitFAT,0                          ; no, it's 12bit
ivpbc0: movzx   eax,es:[di].vpb_fsd.vpdFAT_FAT_size        ; get sectors/FAT
        mov     ivpbc_fatsec,ax
        movzx   ecx,es:[di].vpb_fsi.vpi_cbSector           ; get bytes/sector
        xor     edx,edx
        mul     ecx                                ; eax = total bytes in fat
        mov     ivpbc_fatsize,eax
        cmp     ivpbc_f16BitFAT,0                          ; 12bit FAT?
        je      ivpbc1_5                                   ; yes, skip this
        movzx   eax,es:[di].vpb_fsd.vpdFAT_max_cluster     ; get cluster count
        mov     cx,8
        xor     dx,dx
        div     cx                                         ; ax=clusters/8
        or      dx,dx                                      ; any remainder?
        jz      ivpbc1                                     ; no
        inc     ax                                         ; yes, bump by 1
ivpbc1: add     ax,3                                       ; round up to
        and     ax,not 3                                   ; nearest multiple
        movzx   ecx,ax                                     ; of 4
        xor     edx,edx
        SaveReg <ecx>
        FlatContext <ds,es>,ax
        SSToDS  eax,ivpbc_pClusBitMap                      ; allloc cluster bitmap
        CCall   _VMAllocKHB,<VM_HKH_PUB_RESRW,ecx,FSBUFOWNER,edx,eax>
        RestoreReg <ecx>
        or      eax,eax
        jnz     ivpbc40                 ; return error

        mov     edi,ivpbc_pClusBitMap                       ; zero out bitmap
        SaveReg <edi>
        xor     eax,eax
        cld
        shr     ecx,2
        SaveReg <ecx>
        rep     stosd
        RestoreReg <ecx>
        les     di,ivpbc_pVPB
        RestoreReg <esi>                                    ; restore bitmap ptr
        mov     es:[di].vpb_fsd.vpdFAT_pClusBitMap,esi      ; save bitmap ptr
        mov     es:[di].vpb_fsd.vpdFAT_pNextFreeBitMap,esi  ; init free ptr
        mov     es:[di].vpb_fsd.vpdFAT_cNextFreeBitMap,cx   ; and free count

ivpbc1_5: mov   ax,es:[di].vpb_fsd.vpdFAT_first_FAT         ; get FAT sector num
        mov     ivpbc_sector,ax
        mov     eax,ivpbc_fatsize                           ; get fat size (bytes)
        mov     ebx,1024*16
        cmp     ebx,eax                                     ; fat >= 16K bytes?
        jbe     ivpbc2                                      ; yes, use fat size
        mov     ebx,eax                                     ; no, use 16K

ivpbc2: FlatContext <ds,es>,ax
        SSToDS  eax,vmacHeapBlockInfo
        CCALL32  _SELAllocKHB,<VM_HKH_PUB_RESRW,ebx,PG_CONTIG,0,FSBUFOWNER,eax>
        or      eax,eax
        jnz     ivpbc40                 ; return error

ivpbc3: movzx   ecx,ivpbc_fatsec                            ; get FAT sector count
        xor     eax,eax
        or      cx,cx                                       ; any left to read?
        jz      ivpbc10                                     ; no, we're done
        cmp     cx,32                                       ; how many left?
        jbe     ivpbc4                                      ; < 32, read 'em all
        mov     cx,32                                       ; >=32, just read 32
ivpbc4: sub     ivpbc_fatsec,cx                             ; decrement sects left

        les     di,ivpbc_pVPB
        mov     ax,es:[di].vpb_fsi.vpi_cbSector             ; get bytes/sector
        xor     edx,edx
        mul     cx                                          ; ax=bytes in I/O
        mov     ivpbc_count,ax                              ; save byte count
        shr     ax,1                         ; ax = number of clusters in I/O
        movzx   eax,ax                                                 ;b726249
        add     ivpbc_TotalClusters,eax          ;b726249 add to cluster total
        mov     ivpbc_ClusterCount,ax            ; save cluster count
        movzx   eax,es:[di].vpb_fsd.vpdFAT_max_cluster  ;b726249 get largest cluster
        sub     eax,ivpbc_TotalClusters                 ;b726249 are we past that?
        jae     ivpbc41                                 ; no, continue
        neg     eax                                     ;b726249 how far past?
        sub     ivpbc_ClusterCount,ax                   ; just get up to end
        mov     ivpbc_fatsec,0               ; @74069  zap remaining sec count
ivpbc41:mov     ds,vmacHeapBlockInfo.ac_sel             ; read some FAT from disk
        xor     ebx,ebx
        xor     eax,eax                 ; read normal (ie no cache bypass)
        SaveReg <ds,ecx,ebp,edi,es>
        mov     dx,ivpbc_sector
        mov     bp,di
        xor     di,di                   ; di:bx = starting logical sector
        CALL16  FatSecRd
        RestoreReg <es,edi,ebp,ecx,ds>
        movzx   eax,ax
        jc      ivpbc40                                    ; return with error

ivpbc5: add     ivpbc_sector,cx                            ; add to sector count
        cmp     ivpbc_f16BitFAT,0                          ; 12bit fat?
        jne     ivpbc60                                    ; no, handle 16bit

        mov     ds,vmacHeapBlockInfo.ac_sel
        xor     si,si
        les     di,ivpbc_pVPB
        call    Get12BitBadSectors
        jmp     ivpbc40

; Fill Cluster Bit Map

ivpbc60: movzx  ecx,ivpbc_ClusterCount    ; get clus count in this block of FAT
        mov     es,vmacHeapBlockInfo.ac_sel
        xor     edi,edi
ivpbc6: xor     ax,ax
        repne   scasw                     ; find an in-use cluster
        jne     ivpbc7                    ; found one
        movzx   ebx,di
        sub     ebx,2
        add     ebx,ivpbc_fatoff        ; es:bx offset relative to begining
        shr     ebx,1                   ; bx = free cluster number
        xor     dx,dx
        SaveReg <ax,cx,bx,es,di>
        mov     ax,bx
        mov     cx,8
        div     cx                      ; ax = offset to byte within bitmap
                                        ; dx = offset within bitmap byte
        DosContext ES
        SaveReg <ax>
        mov     ax,dx
        mov     bx,offset DOSGROUP:ClusBitMask
        xlat    byte ptr es:[bx]
        mov     dx,ax
        RestoreReg <ax>

; There is a possibility that some partitions will have either Clusters 0 or 1
; as free. However, we cannot allow the bitmap to indicate this, because these
; clusters are reserved.

        or      ax,ax                   ; first bit map byte ?
        jne     ivpbc61                 ; No, continue
        cmp     dl,2                    ; Cluster 0 or 1 ?
        jbe     ivpbc62                 ; Yes, keep scanning

        ASSUME  ES:NOTHING
ivpbc61:les     di,ivpbc_pVPB
        mov     esi,es:[di].vpb_fsd.vpdFAT_pClusBitMap
        movzx   ebx,ax
        FlatContext DS
        or      ds:[esi+ebx],dl
        ASSUME  DS:NOTHING
ivpbc62:RestoreReg <di,es,bx,cx,ax>
        or      cx,cx
        jnz     ivpbc6

; Create Bad Sector Chain

ivpbc7: movzx   ecx,ivpbc_ClusterCount
        mov     bx,cx
        mov     dx,cx
        mov     ax,0fff7h
        mov     es,vmacHeapBlockInfo.ac_sel
        xor     edi,edi
        cld
ivpbc8: repne   scasw                   ; search
        jne     ivpbc9                  ; no match
        movzx   ebx,di
        dec     ebx
        dec     ebx                     ; es:bx point to bad cluster
        add     ebx,ivpbc_fatoff        ; es:bx offset relative to begining
        shr     ebx,1                   ; bx = bad cluster number

        SaveReg <es,ds,di,cx,ax,bp>
        xor     esi,esi
        mov     dx,bx
        les     di,ivpbc_pVPB
        mov     bp,di
        mov     bl,es:[bp].vpb_fsd.vpdFAT_cluster_mask
        xor     bh,bh
        inc     bx
        push    bx
        xor     bl,bl
        CALL16  FigRec                  ; SI:DX = RBA
        pop     bx
        RestoreReg <bp,ax,cx,di,ds,es>

; Add rba range to VPB chain
        SaveReg <es,edi,edx,eax>

        SaveReg <esi,edx,ebx,ecx>
        xor     edx,edx
        mov     ecx,size BadSec
        FlatContext <ds,es>
        SSToDS  eax,ivpbc_pbadrba
        CCall   _VMAllocKHB,<VM_HKH_PUB_RESRW,ecx,FSBUFOWNER,edx,eax>
        RestoreReg <ecx,ebx,edx,esi>
        or      eax,eax
        jnz     ivpbc40

        mov     edi,ivpbc_pbadrba
        shl     esi,16
        mov     si,dx                   ; esi = rba
        mov     [edi].BadSec_RBA,esi
        mov     [edi].BadSec_Len,bx
        les     si,ivpbc_pVPB
        mov     ebx,es:[si].vpb_fsd.vpdFAT_pBadSector
        mov     [edi].BadSec_Next,ebx
        mov     es:[si].vpb_fsd.vpdFAT_pBadSector,edi

        Assume  DS:Nothing
        RestoreReg <eax,edx,edi,es>
        jmp     ivpbc8

ivpbc9: movzx   eax,ivpbc_count
        add     ivpbc_fatoff,eax
        mov     di,ivpbc_pVPB.Offst
        jmp     ivpbc3

ivpbc10:movzx   ebx,vmacHeapBlockInfo.ac_sel
        or      ebx,ebx
        jz      ivpbc20
        FlatContext <ds,es>,ax
        CCALL32  _SELFreeKHB,<VM_HKH_PUB_RESRW, 0, ebx>
ivpbc20:xor     eax,eax
        cmp     ivpbc_Error,eax
        je      ivpbc30
        les     di,ivpbc_pVPB
        or      es:[di].vpb_fMisc,VPB_FM_INITCACHE_ERROR
        call    FreeVPBCacheData
ivpbc30:mov     eax,ivpbc_Error
        LeaveProc
        ret
ivpbc40:mov     ivpbc_Error,eax
        jmp     short ivpbc10
EndProc InitVPBCacheData

BREAK <Get12BitBadSectors - Get 12 Bit FAT Bad Sectors>
;***
;       Get12BitBadSectors - Get 12 Bit FAT Bad Sectors
;
;       ENTRY   ES:[DI] = pVBP
;               DS:[SI] = pFATBuffer
;
;       EXIT    NONE
;
;       USES    ALL
;
;       PSEUDOCODE:
;
;
Procedure   Get12BitBadSectors,NEAR
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
LocalVar g12b_tcyield,WORD
LocalVar g12b_pVPB,DWORD
LocalVar g12b_pbadrba,DWORD
LocalVar g12b_SecPerClus,WORD
LocalVar g12b_pFATBuf,DWORD

        EnterProc <0>
        mov     g12b_pFATBuf.Segmt,ds
        mov     g12b_pFATBuf.Offst,si
        mov     g12b_pVPB.Segmt,es
        mov     g12b_pVPB.Offst,di
        mov     al,es:[di].vpb_fsd.vpdFAT_cluster_mask
        xor     ah,ah
        inc     ax
        mov     g12b_SecPerClus,ax

        movzx   ecx,es:[di].vpb_fsd.vpdFAT_max_cluster
        mov     bx,2

g12b1:  lds     si,g12b_pFATBuf
        mov     ax,bx
        shr     ax,1                    ; AX = cluster/2
        add     ax,bx                   ; AX = 1.5*cluster
        add     ax,si
        mov     di,ax
        mov     ax,ds:[di]              ; get contents of cluster
        test    bx,1                    ; Is cluster odd ?
        jz      g12b2                   ; No,
        shr     ax,4                    ; Yes, shift right four bits
        jmp     short g12b3
g12b2:  and     ax,0FFFH                ; Mask off top 4 bits
g12b3:  cmp     ax,0FF7H                ; Is cluster bad ?
        jne     g12b4                   ; No,


        SaveReg <cx,bx,bp>
        xor     esi,esi
        mov     dx,bx
        mov     bp,g12b_pVPB.Offst
        xor     bl,bl
        push    GSEL GDT_VPB
        pop     es                      ; ES = VPB Segment
        CALL16  FigRec                  ; SI:DX = RBA
        RestoreReg <bp,bx,cx>

; Add rba range to VPB chain
        SaveReg <ebx,ecx>

        SaveReg <edx>
        xor     edx,edx
        mov     ecx,size BadSec
        FlatContext <ds,es>
        SSToDS  eax,g12b_pbadrba
        CCall   _VMAllocKHB,<VM_HKH_PUB_RESRW,ecx,FSBUFOWNER,edx,eax>
        RestoreReg <edx>
        or      eax,eax
        jnz     g12b6
        ASSUME  DS:NOTHING,ES:NOTHING

        mov     ax,g12b_SecPerClus
        mov     edi,g12b_pbadrba
        shl     esi,16
        mov     si,dx                   ; esi = rba
        mov     [edi].BadSec_RBA,esi
        mov     [edi].BadSec_Len,ax
        les     si,g12b_pVPB
        mov     eax,es:[si].vpb_fsd.vpdFAT_pBadSector
        mov     [edi].BadSec_Next,eax
        mov     es:[si].vpb_fsd.vpdFAT_pBadSector,edi

        Assume  DS:Nothing
        RestoreReg <ecx,ebx>

g12b4:  inc     bx
        inc     g12b_tcyield
        cmp     g12b_tcyield,128        ; check for time to check resched
        jb      g12b5
        FlatContext <DS,ES>,ax
        cmp     _TCResched,0            ; do we have to yield ?
        jz      short g12b5
        pushad
        SaveReg <es>
        FlatContext ES
        CCALL32 _TKTCYield              ; yield to time critical threads
        RestoreReg <es>
        popad
        ASSUME DS:NOTHING,ES:NOTHING
        mov     g12b_tcyield,0
g12b5:  dec     cx
        jnz     g12b1
        xor     eax,eax
g12b6:  LeaveProc
        ret
EndProc Get12BitBadSectors

BREAK <FreeVPBCacheData - Free VPB Cache Data>
;***    FreeVPBCacheData - Free VPB Cache Data
;
;       FreeVPBCacheData frees the cluster bit map if one exists and it also
;       will free the bad sector records if they exist.
;
;       ENTRY   DI      hVBP
;
;       EXIT    NONE
;
;       USES    ALL
;
;
Procedure   FreeVPBCacheData,NEAR
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

        EnterProc

        push    GSEL GDT_VPB
        pop     es
        mov     eax,es:[di].vpb_fsd.vpdFAT_pBadSector
fvpbc1: or      eax,eax                    ; Any more Bad Sector records ?
        jz      fvpbc2                     ; No, go on to cluster bit map
        mov     ebx,ds:[eax].BadSec_Next   ; Yes, get next Bad Sector record
        SaveReg <es>
        FlatContext es
        CCall   _VMFreeKHB,<VM_HKH_PUB_RESRW,eax> ; Free bad sector record
        ASSUME ES:NOTHING
        RestoreReg <es>
        mov     eax,ebx                    ; Next Bad Sector record to free
        jmp     short fvpbc1

fvpbc2: mov     es:[di].vpb_fsd.vpdFAT_pBadSector,eax  ; No more Bad Sec. Recs
        mov     eax,es:[di].vpb_fsd.vpdFAT_pClusBitMap
        or      eax,eax                    ; Is there a cluster BitMap ?
        jz      fvpbc3                     ; No, go return
        SaveReg <es,ds>                    ; Yes, free it
        FlatContext <ds,es>
        CCall   _VMFreeKHB,<VM_HKH_PUB_RESRW,eax>
        RestoreReg <ds,es>
        xor     eax,eax
fvpbc3: mov     es:[di].vpb_fsd.vpdFAT_pClusBitMap,eax  ; No Cluster BitMap
        LeaveProc
        ret
EndProc FreeVPBCacheData

;***LP  _IsRBABad Determine if requested RBA is BAD.
;
;       IsRBABad walks the Bad sector chain looking to see if the requested
;       RBA is in range. If it is we return TRUE, otherwise we return FALSE.
;
;       This function is used so we don't keep reading Bad sectors over and
;       over again. It is also must simpler to handle it this way.
;
;       ENTRY   DWORD   rba
;               DWORD   hVPB
;
;       EXIT    TRUE    if rba is bad
;               FALSE   if rba is not bad
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _IsRBABad,near

        ArgVar rba,DWORD                ; rba
        ArgVar hVPB,DWORD               ; handle to volume

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        SaveReg <ds,es,bp>
        mov     esi,hVPB
        push    GSEL GDT_VPB
        pop     es                      ; ES:[SI]->VPB
        mov     ebx,rba                 ; EBX = rba;
        mov     edi,es:[si].vpb_fsd.vpdFAT_pBadSector
        mov     eax,1                   ; Assume rba bad
        cmp     es:[si.vpb_fsi.vpi_totsec],ebx
        jbe     irbab2                  ; if above total sectors panic
        xor     eax,eax                 ; Assume rba not bad
irbab0:
        or      edi,edi                 ; at end of bad sector chain
        jz      irbab2                  ; yes, exit

        mov     ecx,ds:[edi].BadSec_RBA ; ECX = starting bad sector
        movzx   edx,ds:[edi].BadSec_Len ; EDX = # of bad sectors
        add     edx,ecx                 ; EDX = last bad sector in record
        cmp     ebx,ecx                 ; is rba within range ?
        jb      irbab1                  ; no, get next record
        cmp     ebx,edx                 ; is rba within range ?
        jae     irbab1                  ; no, get next record
        mov     eax,1                   ; yes, signal bad sector
        jmp     short irbab2
irbab1:
        mov     edi,ds:[edi].BadSec_Next
        jmp     short irbab0
irbab2:
        RestoreReg <bp,es,ds>
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
irbab3:
        Panic   <"IsRBABad: Sector Beyond Partition">
EndProc _IsRBABad

;***LP  _IsRBAInSysArea Determine if requested RBA is in the system area
;
;       IsRBAInSysArea
;
;       ENTRY   DWORD   rba
;               DWORD   hVPB
;
;       EXIT    TRUE    if rba is in system area
;               FALSE   if rba is not system area
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _IsRBAInSysArea,near

        ArgVar rba,DWORD                ; rba
        ArgVar hVPB,DWORD               ; handle to volume

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        mov     esi,hVPB
        push    GSEL GDT_VPB
        pop     es                      ; ES:[SI]->VPB
        xor     eax,eax                 ; assume not in system area
        mov     edx,rba
        cmp     edx,es:[si].vpb_fsd.vpdFAT_first_sector
        jae     isrs0
        inc     eax
isrs0:  RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _IsRBAInSysArea

;***LP  _IsRBAForFAT  Debug Code!!!!
;
;
;       ENTRY
;
;       EXIT
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _IsRBAForFAT,near

        ArgVar rba,DWORD
        ArgVar hVPB,DWORD

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        push    GSEL GDT_VPB
        pop     es
        mov     ebx,hVPB
        mov     edx,rba
        xor     eax,eax
        movzx   ebx,es:[bx].vpb_fsd.vpdFAT_FAT_size
        cmp     edx,ebx
        ja      irfa1
        inc     eax
irfa1:
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _IsRBAForFAT

;***LP  _CheckFATSector Debug code!!!
;
;
;       ENTRY
;
;       EXIT
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _CheckFATSector,near

        ArgVar hVPB,DWORD
        ArgVar pFAT,DWORD

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        push    GSEL GDT_VPB
        pop     es
        mov     ebx,hVPB
        cmp     es:[bx].vpb_fsd.vpdFAT_FATentrysize,16                             ;@@
        jne     cfs1
        mov     esi,pFAT
        mov     ax,ds:[esi]
        add     esi,2
        mov     di,ds:[esi]
        cmp     ax,0fff8h
        je      cfs1
        cmp     ax,0ffffh
        je      cfs1
        cmp     ax,0fff7h
        je      cfs1
        cmp     ax,es:[bx].vpb_fsd.vpdFAT_max_cluster
        ja      cfs1x
        cmp     di,0fff8h
        je      cfs1
        cmp     di,0ffffh
        je      cfs1
        cmp     di,0fff7h
        je      cfs1
        cmp     di,es:[bx].vpb_fsd.vpdFAT_max_cluster
        jbe     cfs1
cfs1x:  int     3
cfs1:
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _CheckFATSector

;***EP  _FreeReqList Free Request List
;
;       FreeReqList calls the 16 bit version of FreeReqList.
;
;       ENTRY   DWORD   ReqListSel
;               DWORD   ReqListOff
;
;       EXIT    NONE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _FreeReqList,near

        ArgVar ReqListSel,DWORD         ; Request List selector
        ArgVar ReqListOff,DWORD         ; Request List offset

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>
        mov     eax,ReqListSel
        mov     es,ax
        mov     edi,ReqListOff          ; ES:[DI]->Request List to free
        CALL16  FreeReqList
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _FreeReqList

;***EP  _SetupForIO Setup for FAT variables for an upcoming I/O
;
;       SetupForIO calls the 16 version to setup some FAT variables for an
;       upcoming I/O operation
;
;       ENTRY   DWORD   count
;
;       EXIT
;               if success
;                       eax=0
;               else
;                       eax=error code
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _SetupForIO,near

        ArgVar count,DWORD              ; count of bytes for upcoming I/O

        EnterProc
        SaveReg <ebp,esi,edi,ebx,es,ds>
        mov     edi,_pTCBCur            ; EDI->Current TCB
        les     di,[edi].TCBThisSFT     ; ES:DI -> sft
        mov     ecx,count               ; CX = count bytes for I/O
        TaskContext DS                  ; DS = TASKAREA
        CALL16  SetupForIO              ; Setup For I/O
        movzx   eax,ax                  ; EAX has error if any
        jc      sfio1                   ; if error go return it in EAX
        xor     eax,eax                 ; No error return 0
sfio1:  RestoreReg <ds,es,ebx,edi,esi,ebp>
        LeaveProc
        RETC
EndProc _SetupForIO

;***EP  _FatReadInit initializes more FAT TCB variables for an upcoming I/O.
;
;       FatReadInit calls the 16 bit version of InitTCBVars to initialize
;       more FAT variables.
;
;       ENTRY   DWORD   hVPB
;
;       EXIT
;               if success
;                       eax=0
;               else
;                       eax=error code
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _FATReadInit,near

        ArgVar hVPB,DWORD               ; Handle to volume

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>
        mov     eax,hVPB                ; EAX = handle to volume
        TaskContext DS                  ; DS = TASKAREA
        CALL16  InitTCBVars             ; Initialize FAT variables
        movzx   eax,ax                  ; EAX = error code if any
        jc      itcb1                   ; if error go return it in EAX
        xor     eax,eax                 ; NO ERROR
itcb1:  RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _FATReadInit

;***EP  _FindNonChar Find non-character in string
;
;       FindNonChar determines whether a character other than the source
;       character is found in the string.
;
;       ENTRY   DWORD   pString
;               BYTE    findchar
;               DWORD   count
;
;       EXIT    TRUE    if a character other than source character was found.
;               FALSE   if source character is the only character in string.
;
;       WARNING!! We assume count is a multiple of 4.
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _FindNonChar,near

        ArgVar pString,DWORD            ; pointer to source string
        ArgVar findchar,BYTE            ; source character
        ArgVar count,DWORD              ; count of bytes to search

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        mov     edi,pString             ; EDI->Source string
        mov     ecx,count               ; ECX = count of bytes
        xor     edx,edx                 ; Assume no other character
        mov     al,findchar
        mov     ah,al
        push    ax
        shl     eax,16
        pop     ax                      ; EAX = source characters
        cld
        shr     ecx,2
        repe    scasd                   ; Did we find any other character
        je      fnch1                   ; No, assumption correct
        inc     edx                     ; Yes, signal non-character found
fnch1:  mov     eax,edx
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _FindNonChar

;***EP  _BufFillFromBitMap
;
;       BufFillFromBitMap searches through a bit map determining what bytes to
;       copy from the source buffer to the destination bufer.
;
;       ENTRY   DWORD   pBitMap
;               DWORD   pSrc
;               DWORD   pDest
;
;       EXIT    NONE
;
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _BufFillFromBitMap,near

        ArgVar pBitMap,DWORD            ; pointer to bit map
        ArgVar pSrc,DWORD               ; pointer to source buffer
        ArgVar pDest,DWORD              ; pointer to dest buffer

        EnterProc
        SaveReg <esi,edi,ebx,es,ds,ebp>

        mov     ecx,512/8               ; CX = number of bytes in bit map
        mov     esi,pSrc                ; ESI->Source Buffer
        mov     edi,pDest               ; EDI->Destination Buffer
        mov     ebx,pBitMap             ; EBP->Bit Map
        mov     ebp,8
        xor     ah,ah
ALIGN 4
bffb1:  cmp     [ebx],ah                ; Should we copy 8 bytes ?
        je      short bffb4             ; Yes,
        cmp     byte ptr [ebx],0FFh     ; Copy any bytes ?
        je      short bffb5             ; No, get next bitmap byte
        mov     dh,8                    ; DH = # of bits in a byte
        mov     dl,1                    ; DL = Starting Bit Mask
ALIGN 4
bffb2:  test    [ebx],dl                ; Should we copy a byte ?
        jz      short bffb3             ; Yes,
        inc     esi
        inc     edi
        shl     dl,1                    ; DL = Next Bit Mask
        dec     dh                      ; Are we done copying 8 bytes ?
        jnz     short bffb2             ; No, continue
        inc     ebx
        loop    bffb1
        jmp     short bffb6

bffb3:  lodsb
        stosb
        shl     dl,1                    ; DL = Next Bit Mask
        dec     dh                      ; Are we done copying 8 bytes ?
        jnz     short bffb2             ; No, continue
        inc     ebx
        loop    bffb1
        jmp     short bffb6

; Copy 8 bytes of data

bffb4:  movsd
        movsd                           ; Copy 8 bytes
        inc     ebx                     ; Get next Bit Map Byte Index
        loop    bffb1
        jmp     short bffb6

; No bytes to copy, Adjust pointers

bffb5:  add     esi,ebp                 ; Source += 8;
        add     edi,ebp                 ; Dest += 8;
        inc     ebx                     ; Get next Bit Map Byte Index
        loop    bffb1

bffb6:  RestoreReg <ebp,ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _BufFillFromBitMap

;***EP  _SaveFatTCBInfo Save FAT TCB Info
;
;       SaveFATTCBInfo copies FAT file system specific changes in the TCB into
;       a temporary save area.
;
;       ENTRY   DWORD   pTcbBuf
;
;       EXIT    NONE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _SaveFatTCBInfo,near

        ArgVar  pTcbBuf,DWORD           ; Pointer to TCB save area

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>
        mov     edi,[pTcbBuf]           ; EDI = pointer to TCB save area
        CCALL32 ioSaveTCB               ; save FAT TCB info
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _SaveFatTCBInfo

;***EP  _RestoreFatTCBInfo Restores FAT TCB Info
;
;       RestoreFATTCBInfo copies the saved FAT TCB info from some location
;       most likely on the stack back to the original locations in the TCB.
;
;       ENTRY   DWORD   pTcbBuf
;
;       EXIT    NONE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _RestoreFatTCBInfo,near

        ArgVar  pTcbBuf,DWORD           ; Pointer to saved TCB area

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>
        mov     esi,[pTcbBuf]           ; ESI = pointer to saved TCB area
        CCALL32 ioRestoreTCB            ; go restore TCB variables
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _RestoreFatTCBInfo

;***EP  _AllocateClus Allocate cluster
;
;       AllocateClus allocates a cluster for HotFixing
;
;       ENTRY   DWORD   hVPB
;               DWORD   pCluster
;
;       EXIT    EAX =   0 NO ERROR
;               EAX !=  0 ERROR CODE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _AllocateClus,near

        ArgVar  hVPB,DWORD              ; handle to volume
        ArgVar  pCluster,DWORD          ; pointer to cluster

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>
        mov     esi,hVPB
        mov     ecx,1                   ; 1 cluster
        xor     ebx,ebx
        SaveReg <ebp,ds>
        push    GSEL GDT_VPB
        pop     es
        mov     ebp,esi
        TaskContext DS
        CALL16 AllocateHotFix
        RestoreReg <ds,ebp>
        ASSUME  DS:FLAT
        movzx   eax,ax
        jc      allocc2
        xor     eax,eax
        movzx   ebx,bx
        mov     edi,pCluster
        mov     [edi],ebx
allocc1:RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
allocc2:mov     eax,1
        jmp     short allocc1
EndProc _AllocateClus

;***EP  _FreeCluster Free cluster
;
;       FreeCluster frees a cluster used by HotFixing
;
;       ENTRY   DWORD   hVPB
;               DWORD   Cluster
;
;       EXIT    NONE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _FreeCluster,near

        ArgVar  hVPB,DWORD              ; handle to volume
        ArgVar  Cluster,DWORD           ; cluster to free

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>
        mov     edi,hVPB                ; di = hVPB
        mov     ebx,Cluster             ; bx = cluster
        push    GSEL GDT_VPB
        pop     es
        SaveReg <bp>
        TaskContext DS
        mov     bp,di                   ; es:[bp]->VPB
        CALL16  Release
        RestoreReg <bp>
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _FreeCluster

;***EP  _FindClusInfo Find cluster info
;
;       FindClusInfo finds the previous and next cluster for a given cluster.
;
;       ENTRY   DWORD   CurrClusterNum
;               DWORD   pPrevClusterNum
;               DWORD   pNextClusterNum
;               DWORD   hVPB
;
;       EXIT    EAX =   0 NO ERROR
;               EAX !=  0 ERROR CODE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _FindClusInfo,near

        ArgVar  CurrClusterNum,DWORD    ; current cluster number
        ArgVar  pPrevClusterNum,DWORD   ; pointer to prev cluster number
        ArgVar  pNextClusterNum,DWORD   ; pointer next cluster number
        ArgVar  hVPB,DWORD              ; handle to volume

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>
        mov     esi,hVPB
        mov     edi,pPrevClusterNum
        mov     dword ptr ds:[edi],0
        mov     edi,pNextClusterNum
        mov     dword ptr ds:[edi],0

        mov     eax,CurrClusterNum
        cmp     eax,2
        je      fclus2

        push    GSEL GDT_VPB
        pop     es
        mov     bx,2
        mov     cx,es:[si].vpb_fsd.vpdFAT_max_cluster
        dec     cx

fclus0: SaveReg <es,si,bp,bx,cx,ds,ax>
        mov     bp,si                   ; es:[si]->vpb
        TaskContext DS
        CALL16  UnPack
        RestoreReg <ax,ds,cx,bx,bp,si,es>
        jc      short fclus2
        jz      short fclus1            ; if zero go to next cluster

        cmp     di,ax                   ; does cluster at bx point to us ?
        jne     fclus1                  ; no, continue

        mov     dx,bx                   ; dx = previous cluster
        mov     bx,ax                   ; bx = current cluster
        SaveReg <es,si,bp,bx,cx,ds,ax,dx>
        mov     bp,si                   ; es:[si]->vpb
        TaskContext DS
        CALL16  UnPack                  ; add new chain to file
        RestoreReg <dx,ax,ds,cx,bx,bp,si,es>
        jc      short fclus2
        stc
        jz      short fclus2            ; if zero bad error but exit gracefully

        ASSUME  DS:FLAT

        ; dx = previous cluster
        ; di = next cluster

        mov     esi,pPrevClusterNum
        movzx   edi,di
        movzx   edx,dx
        mov     [esi],edx
        mov     esi,pNextClusterNum
        mov     [esi],edi
        clc
        jmp     short fclus2

fclus1: inc     bx
        loop    fclus0
fclus2: mov     eax,1
        jc      fclus3
        xor     eax,eax
fclus3: RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _FindClusInfo

;***EP  _ReLinkClus ReLink cluster
;
;       ReLinkClus replaces an existing cluster with a new cluster and
;       assigns the replaced cluster as containing a BAD cluster.
;
;       ENTRY   DWORD   NewClusterNum
;               DWORD   PrevClusterNum
;               DWORD   NextClusterNum
;               DWORD   CurrClusterNum
;               DWORD   hVPB
;
;       EXIT    EAX =   0 NO ERROR
;               EAX !=  0 ERROR CODE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _ReLinkClus,near

        ArgVar  NewClusterNum,DWORD     ; new cluster number
        ArgVar  PrevClusterNum,DWORD    ; prev cluster number
        ArgVar  NextClusterNum,DWORD    ; next cluster number
        ArgVar  CurrClusterNum,DWORD    ; current cluster number
        ArgVar  hVPB,DWORD              ; handle to volume

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>
        mov     esi,hVPB

        mov     ebx,NewClusterNum
        mov     edx,NextClusterNum      ; [ebx] = edx
        push    GSEL GDT_VPB
        pop     es
        SaveReg <es,si,bp>
        mov     bp,si                   ; es:[si]->vpb
        TaskContext DS
        CALL16  Pack                    ; add new chain to file
        RestoreReg <bp,si,es>
        jc      short rlclu2

        mov     ebx,PrevClusterNum
        mov     edx,NewClusterNum      ; [ebx] = edx
        SaveReg <es,si,bp>
        mov     bp,si                   ; es:[si]->vpb
        TaskContext DS
        CALL16  Pack                    ; add new chain to file
        RestoreReg <bp,si,es>
        jc      short rlclu2

        mov     edx,0fff7h
        cmp     es:[si].vpb_fsd.vpdFAT_FATentrysize,16                             ;@@
        je      rlclu0
        mov     edx,0ff7h
rlclu0: mov     ebx,CurrClusterNum
        SaveReg <es,si,bp>
        mov     bp,si                   ; es:[si]->vpb
        TaskContext DS
        CALL16  Pack                    ; add new chain to file
        RestoreReg <bp,si,es>
        jc      short rlclu2
rlclu1: xor     eax,eax
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
rlclu2: mov     eax,1
        jmp     short rlclu1
EndProc _ReLinkClus

;***EP  _RBAToClus RBA to cluster
;
;       RBAToClus coverts a rba to cluster number and returns information
;       about this volume.
;
;       ENTRY   DWORD   rba
;               DWORD   hVPB
;               DWORD   pFirstRBA
;               DWORD   pSecsPerClus
;
;       EXIT    EAX =   Cluster number
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _RBAToClus,near

        ArgVar  rba,DWORD               ; rba to be converted
        ArgVar  hVPB,DWORD              ; handle to volume
        ArgVar  pFirstRBA,DWORD         ; pointer to first rba
        ArgVar  pSecsPerClus,DWORD      ; pointer to sectors per cluster

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>
        mov     esi,hVPB
        mov     edx,rba

        push    GSEL GDT_VPB
        pop     es

        sub     edx,es:[si].vpb_fsd.vpdFAT_first_sector

        mov     bl,es:[si].vpb_fsd.vpdFAT_cluster_mask
        inc     bl
        movzx   ebx,bl
        mov     edi,pSecsPerClus
        mov     [edi],ebx

        mov     cl,es:[si].vpb_fsd.vpdFAT_cluster_shift
        shr     edx,cl
        add     edx,2                   ; First 2 entries in FAT are invalid

        SaveReg <es,si,bp,edx>
        mov     bp,si
        xor     bl,bl
        CALL16  FigRec                  ; SI:DX = RBA
        movzx   eax,si
        shl     eax,16
        mov     ax,dx                   ; EAX = RBA
        RestoreReg <edx,bp,si,es>

        mov     edi,pFirstRBA
        mov     [edi],eax

        mov     eax,edx

        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _RBAToClus

;***EP  _GetDriveLetterFromhVPB
;
;       GetDriveLetterFromhVPB gets the drive letter assoicated with the hVPB
;
;       ENTRY   DWORD   hVPB
;
;       EXIT    EAX =   Drive Letter
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _GetDriveLetterFromhVPB,near

        ArgVar  hVPB,DWORD              ; handle to volume

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>
        mov     esi,hVPB
        push    GSEL GDT_VPB
        pop     es
        xor     eax,eax
        les     di,es:[si].vpb_fsi.vpi_pDPB     ; es:di -> DPB
        mov     al,es:[di].dpb_drive    ; al = 0-based drive number
        add     al,'A'                  ; al = drive letter

        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _GetDriveLetterFromhVPB

;***EP  _GetBuf
;
;       GetBuf gets the a buffer needed by HotFixing
;
;       ENTRY   DWORD   rba
;               DWORD   hVPB
;               DWORD   pBufSel
;               DWORD   pBufOff
;
;       EXIT    EAX =   pBuffer
;               EAX =   0 No buffer
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _GetBuf,near

        ArgVar  rba,DWORD               ; rba
        ArgVar  hVPB,DWORD              ; hVPB
        ArgVar  pBufSel,DWORD           ; pointer to buffer selector
        ArgVar  pBufOff,DWORD           ; pointer to buffer offset

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>
        mov     esi,hVPB

        or      ss:AutoFail,DOSERR_HARDERR_FLAG ; automatically fail

        push    GSEL GDT_VPB
        pop     es
        xor     eax,eax                 ; al = 0 sector will be read from disk
        mov     ecx,rba
        mov     dx,cx
        shr     ecx,16                  ; cx:dx = rba
        SaveReg <bp,ds>
        mov     bp,si                   ; es:[bp]->BPB
        CALL16  GetBuffrBP
        RestoreReg <ds,bp>
        pushf
        movzx   eax,ax
        and     ss:AutoFail,not DOSERR_HARDERR_FLAG ; do we automatically fail?
        popf
        jnc     gbuf1
        xor     eax,eax
        jmp     short gbuf2

gbuf1:  mov     ebx,ds:[_pTCBCur]
        les     si,ds:[ebx].TCBCurBuf   ; es:si->Buffer
        movzx   esi,si
        mov     ax,es
        movzx   eax,ax
        mov     edi,pBufSel
        mov     [edi],eax                ; store buffer sel
        mov     edi,pBufOff
        mov     [edi],esi                ; store buffer offset

        movzx   eax,ax
        FlatContext <ds,es>,bx
        CCALL32  _SELConvertToLaddr,<eax>

IFDEF FSSTRICT
        cmp     eax,-1                  ; error ?
        je      short gbufie
ENDIF
        lea     si,[si].BUFINSIZ        ; Point at buffer
        add     eax,esi                 ; eax = linear address of buffer

gbuf2:  RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
gbufie: Panic   <"GetBuf: Bad Linear Address">
EndProc _GetBuf

;***EP  _ReleaseBuf
;
;       ReleaseBuf releases a previous buffer
;
;       ENTRY   NONE
;
;       EXIT    NONE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _ReleaseBuf,near


        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        CALL16  RlseBuffr

        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _ReleaseBuf

;***EP  _HotFixHardError
;
;       HotFixHardError cause a harderror to be displayed for hotfixing
;
;       ENTRY   hVPB
;
;       EXIT    NONE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _HotFixHardError,near

        ArgVar  hVPB,DWORD
        LocalVar Drive,BYTE

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        push    GSEL GDT_VPB
        pop     es
        mov     ebx,hVPB
        xor     eax,eax                      ; Assume drive not lock
        les     si,es:[bx].vpb_fsi.vpi_pDPB  ; DS:SI->DPB
        mov     al,es:[si].dpb_drive    ; get drive number
        add     al,'A'                  ; convert to asciz
        mov     Drive,al
        mov     dx,MSG_HOTFIX_ERROR     ; dx = message number
        mov     cx,1                    ; cx = number of replacement strings
                                        ; ds:[esi]->replacement strings
        mov     ax,HEMessage            ; message
;69350; mov     edi,_pTCBCur
;69350; mov     [edi].TCBAllowed,ALLOWED_ACKNOWLEDGE
;69350; SSToDS  esi,Drive
;69350; CALL16  HardError

        mov     edi,_pTCBCur
        mov     [edi].TCBFailErr,0
        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _HotFixHardError

;***EP  _GetClusterSize
;
;       GetClusterSize returns the number of bytes in a cluster
;
;       ENTRY   hVPB
;
;       EXIT    cbClusSize      cluster size
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _GetClusterSize,near

        ArgVar  hVPB,DWORD

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        mov     esi,hVPB
        push    GSEL GDT_VPB
        pop     es

        mov     al,es:[si].vpb_fsd.vpdFAT_cluster_mask
        inc     al
        movzx   eax,al
        mul     es:[si].vpb_fsi.vpi_cbSector    ; dx:ax = cluster size in bytes
        shl     edx,16
        mov     dx,ax
        mov     eax,edx                         ; eax = cluster size in bytes

        RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _GetClusterSize

;***EP  CacheFlush Flush FAT Cache buffers to disk
;
;       CacheFlush calls _BufFlush to flush FAT Cache buffers to disk.
;
;       ENTRY   DWORD   fldMask
;               WORD    hVPB
;
;       EXIT    NONE
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure CacheFlush,near

        ArgVar fldmask,DWORD            ; Per File Dirty Mask
        ArgVar hVPB,WORD                ; handle to volume

        EnterProc
        SaveReg <esi,edi,ebx,es,ds,ecx>
        movzx   ebx,hVPB                ; EBX = handle to volume
        CALL16  UseCache                ; Using cache for this volume ?
        jc      cflu1                   ; No, no flush necessary
        CCALL32  _BufFlush,<fldmask,ebx>
cflu1:  RestoreReg <ecx,ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc CacheFlush

;***EP  _fReqListFree
;
;       fReqListFree returns whether we will block getting a request list
;
;       ENTRY   NONE
;
;       EXIT    TRUE    Request list can be gotten without waiting on PGCommitSem
;               FALSE   Request list cannot be gotten without waiting on PGCommitSem
;
;       USES            EAX, ECX, EDX, EFlags
;
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _fReqListFree,near

LocalVar cnest,WORD

        EnterProc
        SaveReg <esi,edi,ebx,es,ds>

        DosContext <es>
        mov     es,es:[s2BmpSel]
        CALL16  Bmp_QueryFree           ; any free request list entries
        mov     eax,1                   ; assume free DON'T Modidy flags
        jnc     frlf1

        FlatContext <es>

        mov     edx,offset FLAT:_pgCommitSem
        SSToDS  eax,cnest
        CCALL32 _KSEMQueryMutex, <edx, eax>

; NOTE!!! This code is called by the FAT LazyWriter it should never own the
; pgCommitSem while calling this routine.

        or      eax,eax                 ; Owned by somebody ?
        jz      short frlf1             ; Yes,

        cmp     cnest,0                 ; Free ?
        jz      short frlf1             ; Yes,

        xor     eax,eax                 ; semaphore owned

frlf1:  RestoreReg <ds,es,ebx,edi,esi>
        LeaveProc
        RETC
EndProc _fReqListFree
HIGH32CODE      ENDS

        FARCODE HIGH4
HIGH4CODE SEGMENT

;***    Call32FSDLock - worker for Byte Level Locking to call the FSD
;
;       This routine is to be used only by w_realsetfilelocks, to allow
;       us to call the FS_FILELOCKS FSD entry point.  This call must be
;       done from 16-bit code.
;
;       ENTRY:
;
;               es:di -> sft
;
;       EXIT:   ax = error code from fsd
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure Call32FSDLock,<far16>
LocalVar        savebx,WORD             ;save register bx

        mov     savebx,bx
        push    es
        lea     bx,es:[di].sf_fsi
        push    bx
        push    es
        lea     bx,es:[di].sf_fsd
        push    bx
        mov     bx,savebx

        CpuMode 386
        push    eax                     ; pointer to unlock range
        push    ebx                     ; pointer to lock range
        push    ecx                     ; timeout
        push    esi                      ; flags
        CpuMode reset
        les     di,es:[di].sf_FSC       ; ES:DI->FSC
        call    es:[di].FS_FILELOCKS
        RETP
EndProc Call32FSDLock


;***    Call32FSDCancelLock - worker for Byte Level Locking to call the FSD
;                             when cancelling an outstanding lock request
;
;       This routine is to be used only by w_realcancelfilelocks, to allow
;       us to call the FS_CANCELLOCKREQUEST FSD entry point.  This call must
;       be done from 16-bit code.
;
;       ENTRY:
;               es:di -> sft
;               eax   -> lock range
;
;       EXIT:   ax = error code from fsd
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
Procedure Call32FSDCancelLock,<far16>

        push    es
        lea     bx,es:[di].sf_fsi
        push    bx
        push    es
        lea     bx,es:[di].sf_fsd
        push    bx

        CpuMode 386
        push    eax                     ; pointer to lock range
        CpuMode reset
        les     di,es:[di].sf_FSC       ; ES:DI->FSC
        call    es:[di].FS_CANCELLOCKREQUEST
        RETP
EndProc Call32FSDCancelLock


HIGH4CODE ENDS


END
