;       SCCSID = @(#)strat2.asm 6.27 92/03/17
SCCSID  equ     "@(#)strat2.asm 6.27 92/03/17"
Page    ,132
TITLE   STRAT2 - strategy 2 (scatter/gather) routines
NAME    STRAT2

;***
;
;       Global          Local
;
;       DWrite2
;       DRead2
;       DRWIO
;72417 2/7/96 MLM - changed  FatDirtyBitClear to HYBRID
;150311 4/1/96 DRB - System Anchor Block support

.xlist
.xcref
include basemaca.inc
include bsedosp.inc
include bpb.inc         ; needed by boot.inc
include boot.inc        ; boot_ID
include defdbug.inc
include devsym.inc
include dosseg.inc
include dpb.inc
include filesys.inc
include harderr.inc     ; for HDERR_IGNORE, etc
include mi.inc
include osmaca.inc
include oldmaca.inc
include fnofs.inc
include dossymc.inc
include protmode.inc
include seldesc.inc     ; D_DATA0, etc. also needed by fsh.inc virtsym.inc
;include tcbdata.inc     ; TCBBufHE, TCBactBufHE
include virtsym.inc     ; SA_GDT, etc. also needed by fsh.inc
include vmowner.inc
include vpb.inc
include buffer.inc
include error.inc
include filemode.inc   ; needed by fsh.inc
include fsh.inc        ; needed by filemacs.inc
include sft.inc        ; needed by filemacs.inc
include filemacs.inc   ; get_advise
include tkexport.inc
include sched.inc
include shutdown.inc   ; #81195
include pgexport.inc
.list
include strat2.inc
.xlist
include fsc.inc

.cref
.list

        ReferTask       VerFlg,BYTE     ; Verify on write flag
        ReferTask       CurrTCB,WORD

        ReferGDT        GDT_VPB,WORD    ; VPB BMP Selector
        ReferGDT        GDT_SFT,WORD    ; #81195
        ReferFlat       _pPTDACur,dword
        ReferGlobal     ErrMap24,BYTE
        ReferGlobal     hVPBFirst,WORD  ; handle for first VPB in link  ;### GS
        ReferGlobal     initfatchkdsk,BYTE  ; init chkdsk of fat boot partition ;### AC DCR 1520

        EXTRNFAR        S2AllocReqPkt
        EXTRNFAR        FatDirtyBitToggle
        EXTRNFAR        FreeReqPacket
        EXTRNFAR        ProcBlock
        EXTRNFAR        ProcRun2
        EXTRNFAR        S2HardError
        EXTRNFAR        SemSet
        EXTRNFAR        SemClear
        EXTRNFAR        SemWait
        EXTRNFAR        VirtToPhys
        EXTRNFAR        GetTempBuffer
        EXTRNFAR        FreeTempBuffer
        EXTRNFAR        WaitVPB
        EXTRNFAR        WakeVPB
        EXTRN32         _BufIODone
        EXTRN32         InitVPBCacheData
        EXTRN32         _SELConvertToLaddr
        EXTRN32         _PGLinToPageList
        EXTRN32         _HotFix


        EXTRN           CRC:NEAR
        EXTRN           RemoveBufVPB:NEAR
        EXTRN           RlseBuffr:NEAR
        EXTRN           WhatVolumePerc:NEAR
        EXTRN           ToggleRequest:NEAR


        ModName , FNO_STRAT2_ASM

DATA                SEGMENT

        PUBLIC  _FSPrtyIO_Mode, FSPrtyIO_Mode
_FSPrtyIO_Mode  label   WORD            ; D150371
FSPrtyIO_Mode   dw      1
public _FATCache, FATCache
_FATCache       label   BYTE            ; D150371
FATCache                db 0
public _fatvolume, fatvolume
_fatvolume      label   BYTE
fatvolume               db 0
public _Defaultcache, Defaultcache
_Defaultcache   label   BYTE
Defaultcache            db 0

DATA                ENDS

PGLSTSIZE       equ     17 *  SIZE pagelist_s

        FARCODE HIGH2
HIGH2CODE SEGMENT

.386p

;***    DWrite2 - basic I/O using strategy 2
;
;   ENTRY   ax          DRead: 0 if normal, ReadBypassAdj if Bypass
;                       DWrite: 0 if normal, WriteBypassAdj if Bypass
;           ds:di       points to transfer area  b724480
;           ebx         -> page list
;           cx          is count of sectors
;           edx         is starting logical sector number
;           si          number of pages
;           bp          VPB handle
;           [TCBJfnFlag] handle flags
;
;   EXIT    carry clear - successful completion of operation
;           carry set - operation was failed
;
;   USES    all except ds,bp, es still points to VPB but may be moved

Procedure DWrite2,faronly
    ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

        or      ax,ax                   ;
        jz      dw2_10                  ; normal write
        mov     ax,8000H                ; set high bit for writebypassadj
dw2_10: add     ax,PB_WRITE_X           ; command code for write
        add     al,VerFlg               ; (AL) = non-verify -or- verify command
                                        ; make it 1fh or 20h depending on verify
;       xor     ah,ah                   ; (AX) = command

dw1:    push    bp
        push    ds
        push    cx
        push    dx
        push    di
        push    bx

        mov     di,ax                   ; di = read/write command
        SaveReg <si>
        get_advise                      ; ax = advisory bits in FSH_DOV_ form

; convert advisory bits from FSH_DOV_ form to DOV_ form

adjust = 3

        RestoreReg <si>
        shr     ax,adjust               ; ax = advisory bits in DOV_ form
        call    DRWIO
        pop     bx                      ; Restore buffer address
        pop     di
        pop     dx                      ; Restore logical sector number
        pop     cx                      ;     and sector count
        pop     ds
        jc      dw3                     ; Done if error of any kind
        mov     bx,di                   ; ds:bx -> transfer area
;
; Check to see if any of the sectors just written are used to build the VID
; ES:BP -> VPB
; CX = sector count
; EDX = starting logical sector
;
; make di:dx = starting logical sector
        mov     edi,edx
        shr     edi,16
        mov     si,es:[bp].vpb_fsd.vpdFAT_IDsector.HighWord
        mov     ax,es:[bp].vpb_fsd.vpdFAT_IDsector.LowWord
        cmp     di,si
        ja      dw2                     ; We are not in range
        jb      dw1_5                   ; if highwords not equal, go do next cmp
        cmp     dx,ax
        ja      dw2                     ; We are not in range
dw1_5:  add     dx,cx
        adc     di,0                    ; di:dx = last sector written
        cmp     di,si
        jb      dw2                     ; We are not in range
        ja      dw1_6                   ; if highwords not equal, we're in range
        cmp     dx,ax
        jbe     dw2                     ; We are not in range

; 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 = last sector written
; SI:AX = ID sector
; CX = number of sectors written

                                        ; Next two subtractions are 16-bit
                                        ; because result is guaranteed to be
                                        ; 16-bit
dw1_6:  sub     dx,cx                   ; (DX) = first sector 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      dw1a                    ; 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 dw2

;       Old style volume, apply CRC to FAT sector

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

dw2:    clc

dw3:    pop     bp
        DBPRT   80h,40h,<)>
        ret
EndProc   DWrite2


;***    DRead2 - basic I/O using strategy 2
;
;   ENTRY   ax          DRead: 0 if normal, ReadBypassAdj if Bypass
;                       DWrite: 0 if normal, WriteBypassAdj if Bypass
;           ebx         -> page list
;           cx          is count of sectors
;           di:dx       is starting logical sector number
;           si          number of pages
;           bp          VPB handle
;           [TCBJfnFlag] handle flags
;
;   EXIT    carry clear - successful completion of operation
;           carry set - operation was failed
;
;   USES    all except ds,bp, es still points to VPB but may be moved

Procedure DRead2,faronly

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

        SaveReg <bp,ds>
;       add     ax,CMDINPUT             ; (AX) = CMDINPUT OR CMDINPUTBypass
        mov     ax,PB_READ_X            ; read command code for strategy2

        shl     edi,16
        mov     di,dx                   ; edi = starting sector #
        mov     edx,edi                 ; edx = starting sector #

        mov     di,ax                   ; di = read/write command
        SaveReg <si>
        get_advise                      ; ax = advisory bits in FSH_DOV_ form
        RestoreReg <si>

; convert advisory bits from FSH_DOV_ form to DOV_ form

adjust = 3

        shr     ax,adjust               ; ax = advisory bits in DOV_ form

        call    DRWIO
        RestoreReg <ds,bp>
        ret

EndProc   DRead2

;***    DRWIO - performs sector I/O
;
;       This is similar to DoVolio in function but uses strat2 callouts and
;       slightly different input parameters.
;
; bugbug 16-May-90 peterwu - assume only non-removable media supports strat2
; We don't check for the correct volume in here (DoVolio does) because only
; non-removable media supports strat2 at this time.
;
;
; WARNING: This may call the Strat2 VDisk, which does not support
;          the full interface. If changes are made to the manner
;          in which Strat2 FAT calls are made, VDisk should be examined
;          for possible errors.
;
;       ENTRY   di    = function (read or write)
;                       high bit set for bypassadj write
;               ebx   -> page list
;               cx    = number of sectors
;               edx   = beginning logical sector number
;               ax    = advisory bits for CMDINPUT/CMDOUTPUT(v)
;                            DOV_AUTOFAIL on = fail errors.
;                            DOV_WRTHRU on = write thru
;                            DOV_NCACHE on = don't cache data
;               si    = # entries in page list
;               bp    = VPB handle
;               [TCBBufHE] = if BufHE_Percolate set, don't handle harderror
;
;       EXIT    es:bp => VPB
;               cx = number of sectors transfered successfully
;               carry clear => operation succeeded
;                              ax = 0
;               carry set => operation was failed
;                            [TCBactBufHE] = harderror action selected by user
;                            ax = DOS error code
;
;       Registers modified: AX, BX, CX, DX, SI, DI, DS, ES, Flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure DRWIO,NEAR

VPBhandle equ [bp]

LocalVar drw_rdwr,WORD
LocalVar drw_advise,WORD
LocalVar drw_cbSec,WORD
LocalVar drw_secBeg,DWORD
LocalVar drw_pPList,DWORD
LocalVar drw_cbPList,WORD
LocalVar drw_pReq,DWORD                 ; -> request packet
LocalVar drw_error,BYTE

        EnterProc <di, ax, cx, edx, ebx, si>

        MOV     DX,1            ; STRAT2 and OK to Wait
        CALLFAR S2AllocReqPkt   ; es:bx -> new request packet
        mov     drw_pReq.Segmt,es
        mov     drw_pReq.Offst,bx

; setup strat2 packet
; first setup the request list header

drw5:   lds     si,drw_pReq             ; ds:si -> request packet

        mov     [si].fs2_rlh.RLH_Count,1        ; one request in this list
        mov     [si].fs2_rlh.RLH_Notify_Address.Segmt,cs
        lea     ax,drw_waker
        mov     [si].fs2_rlh.RLH_Notify_Address.Offst,ax
        mov     [si].fs2_rlh.RLH_Lst_Status,0

drw_ctrl = RLH_Single_Req or RLH_Abort_Err or RLH_Notify_Done

        mov     [si].fs2_rlh.RLH_Request_Control,drw_ctrl
        mov     bx,VPBhandle
        VPBContext es,bx
        mov     al,es:[bx].vpb_fsi.vpi_unit
        mov     [si].fs2_rlh.RLH_Block_Dev_Unit,al

; setup the one and only request header

        mov     [si].fs2_rh.RH_Length,RH_LAST_REQ       ; last RH in list
        mov     [si].fs2_rh.RH_Old_Command,PB_REQ_LIST

; set command read/write

        mov     ax,drw_rdwr

drw10:  mov     [si].fs2_rh.RH_Command_Code,al

        mov     [si].fs2_rh.RH_Head_Offset,size Req_List_Header
        mov     [si].fs2_rh.RH_Req_Control,0
        mov     [si].fs2_rh.RH_Priority,PRIO_BACKGROUND_USER
                                           ; Assume this is an ordinary request
        CALLFAR FSGetPriorityInfo          ; get running thread's priority
        or      ax, ax                     ; are we in the foreground?
        jz      drw10a                     ; no
        mov     [si].fs2_rh.RH_Priority,PRIO_FOREGROUND_USER ; foreground I/O
drw10a:
        mov     [si].fs2_rh.RH_Status,RH_NO_ERROR
        mov     [si].fs2_rh.RH_Error_Code,0
        mov     [si].fs2_rh.RH_Notify_Address,0
        mov     [si].fs2_rh.RH_Hint_Pointer,0
        mov     eax,drw_secBeg
        mov     [si].fs2_rh.PB_Start_Block,eax
        movzx   eax,drw_cbSec
        mov     [si].fs2_rh.PB_Block_Count,eax

; BUGBUG - nagara  - do we have to do this ???
        test    drw_rdwr,8000h          ; writebypassadj ? if so make it
        jnz     drw15                   ; write through cache
; END BUGBUG

        xor     ax,ax
        test    drw_advise,DOV_WRTHRU
        jz      drw20

drw15:  mov     ax,RW_Cache_WriteThru

drw20:  mov     [si].fs2_rh.PB_RW_Flags,ax
        xor     ecx,ecx
        mov     cx,drw_cbPList
        mov     bx,cx
; bx = size of pagelist
        mov     [si].fs2_rh.PB_SG_Desc_Count,cx


; copy the page list into the request list

        add     cx,cx                   ; cx = # of dwords to copy
        mov     es,drw_pReq.Segmt
        lea     di,[si].fs2_sg          ; es:di -> SG descriptor in rh
        lds     si,drw_pPList           ; ds:si -> page list
        rep     movsd                   ; copy page list into request list
    assume ds:nothing

; The only time we will have a problem is right at 17.
        cmp     bx,FAT_MIN_MAX_SGLIST+1 ;(17)
        jne     drw30                   ; NO skip this mess

; the IBM scsi adapters must have every 16th pagelist entires end on a
; page boundary.  So if this pagelist has more than 16 entires (the max
; will be 17) then we must fix up the 16th entry to end on a sector
; boundary.  To do this we copy the 17th entry into the 18th entry and
; then split the 16th into two parts.  The first part we leave in the
; 16th entry and modify the size of the write to make it end on a sector
; boundary.  The remaining part of the write for the 16th entry we put
; into the newly vacated 17th entry.
;           before                        after
;  1   0024dbf7 00000409               0024dbf7 00000409
;  .      .       .                       .        .
;  .      .       .                       .        .
;  15  00233000 00001000               00233000 00001000
;  16  00232000 00001000               00232000 000005f7
;  17  00249000 000005F7               002325f7 00000A09
;  18                                  00249000 000005f7
;

        lds     si,drw_pReq             ; ds:si -> request packet
        lea     si,[si].fs2_sg            ; ds:si -> s/g list
        SaveReg <eax,ebx>
; copy 17th entry to 18th
        mov     eax,[si].16* size SG_Descriptor.SG_BufferPtr ;17th entry (one based)
        mov     [si].17 * size SG_Descriptor.SG_BufferPtr,eax ;18th entry (one based)
        mov     eax,[si].(16 * size SG_Descriptor).SG_BufferSize ;17th entry (one based)
        mov     [si].(17 * size SG_Descriptor).SG_BufferSize,eax ;18th entry (one based)

; eax = 17(before) entry size
; set bufptr of 17(after) is bufptr 16(before) + size 17(before)
        SaveReg <eax>
        mov     ebx,[si].(15 * size SG_Descriptor).SG_BufferPtr
        add     eax,ebx
        mov     [si].(16 * size SG_Descriptor).SG_BufferPtr,eax
        RestoreReg <eax>
        mov     ebx,[si].(15 * size SG_Descriptor).SG_BufferSize
;
; size of 17(after) is 16(before) - 17(before)
; ebx = 16th entry size (before start)
; eax = 17th entry size (before start)
        sub     ebx,eax
        mov     [si].(16 * size SG_Descriptor).SG_BufferSize,ebx
        mov     [si].(15 * size SG_Descriptor).SG_BufferSize,eax
; Increment count of s/g entries
        lds     si,drw_pReq             ; ds:si -> request packet
        lea     si,[si].fs2_rh          ; ds:si -> request header
        inc     [si].PB_SG_Desc_Count
        RestoreReg <ebx,eax>


; set semaphore to wait till done
drw30:  mov     bx,drw_pReq.Offst       ; es:bx -> request list
        lea     bx,[bx].fs2_sem         ; es:bx -> semaphore
        xor     eax,eax
        mov     es:[bx],eax             ; zero semaphore
        mov     ax,es                   ; ax:bx -> ram semaphore
        CALLFAR SemSet
        jc      drwie3

; call device drive strategy 2 entry point

;
; WARNING: This may call the Strat2 VDisk, which does not support
;          the full interface. If changes are made to the manner
;          in which Strat2 FAT calls are made, VDisk should be examined
;          for possible errors.
;

        mov     si,VPBhandle
        VPBContext ds,si
        lds     si,ds:[si].vpb_fsi.vpi_pDPB     ; ds:si -> dpb
        mov     eax,ds:[si].dpb_strategy2       ; eax -> strat2 entry pt.
        or      eax,eax                         ; strat2 supported?
        jz      drwie1                          ; no, go panic

        les     bx,drw_pReq                     ; es:bx -> request packet
        call    dword ptr ds:[si].dpb_strategy2 ; call device driver

; wait for device to finish the I/O

drw70:  mov     ax,drw_pReq.Segmt
        mov     bx,drw_pReq.Offst
        lea     bx,[bx].fs2_sem         ; ax:bx -> semaphore
        mov     di,-1
        mov     cx,di                   ; don't time out
        CALLFAR SemWait
        jnc     drw75
        cmp     ax, ERROR_INTERRUPT
        jne     drwie4

drw75:  les     di,drw_pReq             ; es:di -> request packet
        cli                             ; turn off interrupts off
        mov     al,es:[di].fs2_rlh.RLH_Lst_Status
        test    al,RLH_All_Req_Done     ; all done?
        jnz     drw90                   ; yes

; We got interrupted set the semaphore and try again;

        lea     bx,es:[di].fs2_sem      ; es:bx -> semaphore
        xor     eax,eax
        mov     es:[bx],eax             ; zero semaphore
        mov     ax,es                   ; ax:bx -> ram semaphore
        CALLFAR SemSet
        sti                             ;
        jc      drwie3
        jmp     short drw70


; check header for error

drw90:  sti
        mov     cx,drw_cbSec            ; cx = # of sectors read/written
        and     al,0f0h
        cmp     al,RLH_No_Error
        je      drw100

        xor     cx,cx                   ; cx = zero sector read/written

        test    drw_advise,DOV_AUTOFAIL ; autofail hard error?
        jnz     drw95                   ; yes, go fail this call
        les     bx,[drw_pReq]           ;
        xor     dh,dh
        mov     dl,es:[bx].fs2_rh.PB_RqHdr.RH_Error_Code
        cmp     dx,ERROR_I24_UNCERTAIN_MEDIA
        jne     drw91

;       we just got an undetermined media error. Figure out what volume
;       is in there

        call    RlseBuffr               ;free any buffer as this may cause swapping
                                        ;and if user FAILS then we don't want it
        mov     bx,VPBhandle
        VPBContext ds,bx                ;es:bx -> vpb
        lds     si,ds:[bx].vpb_fsi.vpi_pDPB ;ds:si -> dpb

        call    WhatVolumePerc          ; What volume is really in the drive?
        jnc     drw9a                   ; no error from WhatVolume

        les     di,drw_pReq             ; es:di -> request packet
        or      ax,STERR                ; set error bit
        mov     es:[di].PktStatus,ax    ; al = I24 error code
        jmp     short drw91             ; give up


drw9a:  mov     ax,VPBhandle
        cmp     ax,[si].dpb_hVPB        ; Now, do we have the correct volume?
        jz      drw5                    ; yes, go try again
        VPBContext es ,ax
        SaveReg <bp>
        mov     bp,ax                   ; es:bp ->vpb  ax= hVpb ds:si ->dpb
        call    ToggleRequest           ; prompt him for action and wait
        RestoreReg <bp>

        cmp     ax,HDERR_RETRY          ; RETRY?
        jz      drw5                    ; yes, no need to generate error code
        jmp short drw92                 ; no need for another pop up

drw91:  mov     si,VPBhandle
        VPBContext ds,si
        lds     si,[si].vpb_fsi.vpi_pDPB        ; ds:si -> dpb
        les     bx,[drw_pReq]           ;
        xor     dh,dh
        mov     dl,es:[bx].fs2_rh.PB_RqHdr.RH_Error_Code
        mov     di,dx
        push    ds
        DosContext DS
        mov     dl,ErrMap24[di]         ; get dos error code b724822
        Assume  DS:NOTHING
        pop     ds
        mov     [drw_error],dl
        mov     dx,di        ; b734628 fixes problem caused by b724822
        mov     dl,es:[bx].fs2_rlh.RLH_Block_Dev_Unit
        call    DiskHE3

; We check the flag bits in TCBBufHE to see who should carry out the user
; specified action. If BufHE_Percolate bit is set, we return the action in
; TCBactBufHE and let the caller do the action. Otherwise, we do it here.

drw92:  mov     di,[CurrTCB]
        test    TASKAREA:[di].TCBBufHE,BufHE_Percolate
        jz      drw94                   ; We can handle action at this level

        mov     TASKAREA:[di].TCBactBufHE,al ; Save user specified action
        jmp     short drw95             ; Let caller handle action

drw94:  or      ax,ax                   ; did user want to IGNORE?
        .errnz HDERR_IGNORE
        jz      drw100                  ; yes, pretend successful (carry clear)

        cmp     ax,HDERR_RETRY          ; how about RETRY?
        jz      drw5                    ; yes, back to top

; The user wants to FAIL the request. Fall thru.

drw95:
        xor     ah,ah
        mov     al,[drw_error]
        stc

drw100: pushf
        SaveReg <ax>
        les     bx,drw_pReq
        CALLFAR FreeReqPacket
        RestoreReg <ax>
        popf
        LeaveProc
        VPBContext es,bp
        ret

; drw_waker is called by the device driver when the I/O is done
; beware of multiple wake-ups

Entry   drw_waker

; es:bx -> request packet

        lea     bx,[bx].fs2_sem         ; es:bx -> semaphore
        mov     ax,es                   ; ax:bx -> semaphore
        CALLFAR SemClear
        RETFW

drwie1: InternalError <DRWIO: strat2 not supported>
drwie2: InternalError <DRWIO: unknown cmd>
drwie3: InternalError <DRWIO: can not set sem>
drwie4: InternalError <DRWIO: Unexpected return code >

EndProc   DRWIO

Procedure       DiskHE3,HYBRID

;*  Release disk buffer, if owned, to avoid deadlock between thread
;   owning buffer and hard error daemon (which may want same buffer).

        SaveReg <di,bp,ds,dx>
        mov     bp,CurrTCB
        test    [bp].TCBForce_Flg,FF_BUF    ; buffer assigned?
        jz      dh20                        ; no, just return

        lds     di,TASKAREA:[BP].TCBCurBuf  ; (ds:si) -> current buffer
    assume  ds:nothing
        test    [di].buf_flags,BUF_BUSY
        jz      dh20

        test    [di].buf_flags,BUF_DIRTY    ; dirty buffer?
        jnz     dh10                        ;   YES, just release it

;*  For a clean (i.e., not dirty) buffer, we do not know if it has valid
;   data, since we may have been trying to read into the buffer.  GetBuffr
;   fills in the header PRIOR to doing the read, so the header doesn't
;   tell us anything about the validity of the data.  In cases where we
;   are holding a buffer, but NOT reading into it, doing the invalidation
;   will degrade system performance (since the buffer IS valid).  However,
;   harderrors do not happen often enough for this to be of concern to us.

        call    RemoveBufVPB

dh10:   call    RlseBuffr
        or      TASKAREA:[BP].TCBBufHE,BufHE_CurBufFreed

dh20:   RestoreReg <dx,ds,bp,di>
        SaveReg <ds,es,si,bx,cx>
        xor     cx,cx
        mov     cl,[si].dpb_drive
        lds     si,[si].dpb_driver_addr
        mov     ax,HEPacket
        CALLFAR S2HardError             ; signal error to user
        RestoreReg <cx,bx,si,es,ds>
        DBPRT   80h,40h,<)>
        ret
EndProc   DiskHE3

HIGH2CODE ENDS

        FARCODE HIGH4
HIGH4CODE    SEGMENT
BREAK <Req_List_Init> - Initialize request list data structures
;***   Req_List_Init  - Initializes request list data structures
;
;       Req_List_Init initializes the control block that preceeds the request
;       list header as well as request list header. The request list control
;       block is used by the request list management routines to store various
;       information for its own use as well as information for the client.
;
;       Note, that the request list management routines use different
;       structures for strategy2 request lists than the Non-Cache routines,
;       but both get their memory from the same source.
;
;       ENTRY   ES:DI           = Pointer to request data structure
;               BP              = hVPB
;               CX              = FLAGS
;                                   RLCB_KEEP_REQ  0x01
;
;       EXIT    'NC'
;
;       USES    BX,CX,DX,SI
;
;       NOTE:   This routine should be converted to 32 bit 'C' in the future.
;
Procedure Req_List_Init,<HYBRID,far16>
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
        SaveReg <ax,di>

; Initialize the request list control block.

        xor     ax,ax
        mov     es:[di].RLCB_Flags,cl               ; initialize flags
        mov     es:[di].RLCB_hVPB,bp                ; Save hVPB
        mov     es:[di].RLCB_ClientPtr.HighWord,ax
        mov     es:[di].RLCB_ClientPtr.LowWord,ax   ; initial client ptr
        mov     es:[di].RLCB_NextReadSel,ax         ; Zero next read sel
        mov     es:[di].RLCB_NextReadOff,ax         ; Zero next read off
        mov     ax,di
        add     ax,size RLCB_Header
        mov     es:[di].RLCB_HeaderPtr,ax           ; Initial pointer to header
        mov     es:[di].RLCB_CurrReqPtr,0           ; Zero Current request ptr
        mov     es:[di].RLCB_Sem,0                  ; Clear semaphore
        add     ax,size Req_List_Header
        mov     es:[di].RLCB_Pointer,ax             ; First location to start

; Initialize the request list header

        xor     ax,ax
        add     di,size RLCB_Header                 ; ES:[DI]->Request list hdr
        mov     es:[di].RLH_Count.LowWord,ax
        mov     es:[di].RLH_Count.HighWord,ax       ; Zero Reqlist hdr. count
        lea     dx,DOSHIGH4CODE:ReqListNotifyRtn
        mov     bx,cs                               ; BX:DX->Notify routine
        mov     es:[di].RLH_Notify_Address.Offst,dx ; Set Notify routine offset
        mov     es:[di].RLH_Notify_Address.Segmt,bx ; Set Notify routine sel.

        rli_ctrl = RLH_Abort_Err or RLH_Notify_Err or RLH_Notify_Done

        mov     es:[di].RLH_Request_Control,rli_ctrl

        xor     ax,ax
        mov     es:[di].RLH_Lst_Status,al           ; Zero status
        SaveReg <ds,bp>
        VPBContext DS,BP
        mov     al,ds:[bp].vpb_fsi.vpi_unit         ; AL = unit of new request
        RestoreReg <bp,ds>
        mov     es:[di].RLH_Block_Dev_Unit,al       ; Set unit in request list
        xor     al,al
        mov     es:[di].RLH_y_Done_Count.LowWord,ax ; Zero reserved fields
        mov     es:[di].RLH_y_Done_Count.HighWord,ax
        mov     es:[di].RLH_y_PhysAddr.LowWord,ax   ; Zero reserved fields
        mov     es:[di].RLH_y_PhysAddr.HighWord,ax
        RestoreReg <di,ax>
        ret
EndProc   Req_List_Init


BREAK <ReqListNotifyRtn> - Receive Notification Request List Completion
;***   ReqListNotifyRtn  - Receive Notification Request List Completion
;
;       ReqListNotifyRtn is the routine that receives control when a request
;       list is completed with or without an error. If calls the cache I/O
;       done routine with the first pointer on the chain that is linked to this
;       request list. It is not necessarily the first cache block that
;       completed. If the request completed after the semaphore was set then
;       we issue a procrun to wakeup the waiting thread, otherwise no procrun
;       is issued and we return to the device driver.
;
;       ENTRY   ES:BX           = Pointer to request list header
;
;
;       EXIT    NONE
;
;       USES    NONE
;
Procedure ReqListNotifyRtn,FAR
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
        test    es:[bx].RLH_Lst_Status,RLH_All_Req_Done ; All requests done ?
        jz      rlnr2                                   ; no, go return
CpuMode 386
        pushad
        SaveReg <es,ds>
        SaveReg <es>
        mov     di,bx
        sub     di,size RLCB_Header                     ; ES:[DI]->reqlst ctrlblk
        movzx   edi,di
        mov     esi,dword ptr es:[di].RLCB_ClientPtr    ; ESI = client ptr.
        FlatContext <ds,es>,cx
        CCALL32 _BufIODone,<esi>                        ; singal cache
rlnr0:  RestoreReg <es>
        xor     ax,ax
        xchg    es:[di].RLCB_Sem,al                     ; reset and get sem.
        or      al,al                                   ; was sem set ?
        jnz     rlnr1                                   ; no, no procrun

; The semaphore was not set, issue a ProcRun to wakeup the waiting thread.

        MOV     AX,ES
        MOV     BX,DI
        add     bx,size RLCB_Header                     ; ES:[BX]->Reqlist hdr
        SaveReg <esi,edi,es>
        CALLFAR ProcRun2                                ; Wakeup thread
        RestoreReg <es,edi,esi>
rlnr1:  RestoreReg <ds,es>
        popad
CpuMode 286
rlnr2:
        ret
EndProc ReqListNotifyRtn

BREAK <Req_List_Add_Req> - Add a request list entry
;***   Req_List_Add_Req  - Adds a request list entry
;
;       Req_List_Add_Req adds a new request entry to the existing request list.
;       The new entry may be appendable to to last request entry if its
;       starting RBA = last request entry rba + last request entry count. We
;       do not check for the same command, because this should never occur.
;
;       If new request entry along with its required S/G descriptors would
;       cause the request list to overflow, then we execute the current
;       request, reinitialize the request list and then add the new request.
;
;       ENTRY   ES:DI           = Pointer to request data structure
;                  AL           = Command Code
;                  CX           = Count
;               BX:DX           = Starting Sector
;                  AH           = Priority
;                  SI           = hVPB
;
;       EXIT    AX              = 0 if no error
;               AX             != 0 if error
;
;       USES    AX,BX,CX,DX
;
;       NOTE:   This routine should be converted to 32 bit 'C' in the future.
;
Procedure Req_List_Add_Req,<HYBRID,far16>
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

LocalVar rlar_hVPB,WORD
LocalVar rlar_PriCommand,WORD
LocalVar rlar_fConcatenate,WORD

        EnterProc <si,ax,0>

        SaveReg <si,di,es,ds>
        mov     si,di

; We check for a possible concatenation by checking first to see if there was
; a previous request list entry and then if its RBA + count = starting RBA of
; new request.

CpuMode 386
        cmp     es:[di].RLCB_CurrReqPtr,0  ; Any request list entries ?
        jz      rlar20                     ; No, do not check for append
        SaveReg <edi,eax,ebx>
        mov     di,es:[di].RLCB_CurrReqPtr
        mov     eax,es:[di].PB_Start_Block ; eax = prev req starting block
        movzx   ebx,bx
        shl     ebx,16
        mov     bx,dx                      ; ebx = requestors starting block
        add     eax,es:[di].PB_Block_Count ; eax = prev req starting block + cnt
        cmp     eax,ebx                    ; Can we append new entry to prev. ?
        RestoreReg <ebx,eax,edi>
        jne     rlar0                      ; No, keep on truckin
        inc     rlar_fConcatenate          ; Yes, set concatenate flag

; We have to check to see if the request will overflow, otherwise we will
; overwrite other request packets.

rlar0:  SaveReg <bx,dx,cx>
        mov     bx,es:[si].RLCB_Pointer
        sub     bx,si                      ; BX = # of bytes used so far

; If we are concatenating a request then all we need to account for is its
; S/G descriptors.

        cmp     rlar_fConcatenate,0        ; Are we concatenating ?
        jne     rlar00                     ; Yes, don't add PB_Read_Write size
        add     bx,size PB_Read_Write      ; No, add PB_Read_Write size
rlar00: mov     ax,cx                      ; AX = byte count of new request
        mov     cx,4                       ; CX = number of sectors in 2K
        xor     dx,dx
        div     cx                         ; AX = count / 2048
        or      dx,dx                      ; ie. # of SG Descriptiors needed
        jz      rlar1                      ; if no remainder, then no increment
        inc     ax                         ; remainder, increment # of S/G desc.
rlar1:  xor     dx,dx
        add     ax,3                       ; Just for safety
        mov     cx,size SG_Descriptor      ; Multiply AX * size of each S/G desc.
        mul     cx                         ; AX = total bytes needed S/G Desc.
        add     bx,ax                      ; BX = total size of bytes needed
        xor     ax,ax                      ; NO ERROR
        mov     cx,S2_RW_RQPKT_SIZE
rlar10: cmp     bx,cx                      ; Will we overflow ?
        RestoreReg <cx,dx,bx>
        jb      rlar15                     ; No,

; The reqlist would overflow. We have to flush the current contents
; and reinitialize the reqlist.

        mov     rlar_fConcatenate,0        ; No concatenation.

        SaveReg <ecx,ebx,edi,esi,edx,ebp,es,ds>
        mov     di,si                      ; ES:[DI]->ReqList
        mov     bl,es:[di].RLCB_Flags      ; BL = flags.
        SaveReg <bx>                       ; Save old flags
        or      es:[di].RLCB_Flags,RLCB_KEEP_REQ
        mov     ax,rlar_hVPB               ; AX = hVPB
        call    Req_List_Exec              ; Execute current request list
        RestoreReg <bx>                    ; Restore old flags
        or      ax,ax                      ; ERROR ?
        jnz     rlar11                     ; Yes, go return

        SaveReg <bp,es,di,bx>
        mov     cl,bl                      ; CL = old flags
        mov     bp,rlar_hVPB               ; BP = hVPB
        call    Req_List_Init              ; Initialize request list
        RestoreReg <bx,di,es,bp>
        xor     ax,ax
rlar11: RestoreReg <ds,es,ebp,edx,esi,edi,ebx,ecx>
rlar15: or      ax,ax                      ; Any Error ?
        jnz     rlar3                      ; Yes, Exit

        SaveReg <edi>
        cmp     rlar_fConcatenate,0        ; Are we concatenating ?
        stc                                ; Assume error
        je      rlar2                      ; No, go add new request
        mov     di,es:[di].RLCB_CurrReqPtr ; yes, DI = current request pointer
        add     es:[di].PB_Block_Count.LowWord,cx  ; add to existing count
        adc     es:[di].PB_Block_Count.HighWord,0
        clc                                ; NO ERROR
rlar2:  RestoreReg <edi>
        jc      rlar20                     ; request not added to previous

        xor     ax,ax                      ; NO ERROR
        jmp     rlar3
CpuMode 286

; Here we are adding a new request entry. Fill in the required fields.

rlar20: SaveReg <bx>
        mov     ax,rlar_PriCommand                     ; AH = priority AL = com.
        mov     bx,es:[di].RLCB_HeaderPtr              ; BX = reqlist hdr. ptr.
        inc     es:[bx].RLH_Count.LowWord              ; Inc count of requests
        mov     di,es:[si].RLCB_Pointer                ; Get new location ptr
        mov     es:[si].RLCB_CurrReqPtr,di             ; Make it current req.
        mov     es:[si].RLCB_CurrByteCount.LowWord,0
        mov     es:[si].RLCB_CurrByteCount.HighWord,0  ; Zero currbytecount
        mov     es:[di].RH_Length,size PB_Read_Write   ; Set length of request
        mov     es:[di].RH_Old_Command,1ch             ; Set old_command
        mov     es:[di].RH_Command_Code,al             ; Set command
;#83284
;Give I/O priority to the foreground user for both database and paging I/O.
;all other users (including LW and RAHEAD) get background queue for
;database and paging I/O.
        mov     es:[di].RH_Priority,PRIO_BACKGROUND_USER  ; set priority
        CALLFAR FSGetPriorityInfo          ; get running thread's priority
        or      ax, ax                     ; are we in the foreground?
        jz      rlar25                     ; no
        mov     es:[di].RH_Priority,PRIO_FOREGROUND_USER ; foreground I/O
rlar25:
        mov     ax,di
        sub     ax,bx                                  ; AX = offset to header
        mov     es:[di].RH_Head_Offset.LowWord,ax      ; Save header offset
        mov     es:[di].RH_Head_Offset.HighWord,0
        RestoreReg <bx>
        xor     ax,ax
        mov     es:[di].RH_Req_Control,al
        mov     es:[di].RH_Status,al
        mov     es:[di].RH_Error_Code,al
        mov     es:[di].RH_Notify_Address.Offst,ax     ; No request notify rtn.
        mov     es:[di].RH_Notify_Address.Segmt,ax
        mov     es:[di].PB_Start_Block.LowWord,dx      ; BX:DX = starting RBA
        mov     es:[di].PB_Start_Block.HighWord,bx
        mov     es:[di].PB_Block_Count.LowWord,cx      ; CX = count of sectors
        mov     es:[di].PB_Block_Count.HighWord,ax
        mov     es:[di].PB_Blocks_Xferred.LowWord,ax   ; Zero Blocks xferred.
        mov     es:[di].PB_Blocks_Xferred.HighWord,ax
        mov     es:[di].PB_SG_Desc_Count,ax            ; No S/G desc. yet
        mov     es:[di].PB_RW_Flags,RW_Cache_Req       ; Zero flags
        add     di,size PB_Read_Write                  ; DI = new location ptr.
        mov     es:[si].RLCB_Pointer,di                ; Save new location ptr
        xor     ax,ax                                  ; NO ERROR carry clear.
rlar3:  RestoreReg <ds,es,di,si>

        LeaveProc
        ret
EndProc   Req_List_Add_Req

BREAK <Req_List_Add_Extent> - Add an Extent to the request list
;***   Req_List_Add_Extent  - Adds an Extent to the request list
;
;       Req_List_Add_Extent adds a user extent to the request list. It is
;       called when the I/O is contiguous from/to user memory. It calls
;       GetPageBreakInfo to get the page break information on the user's
;       buffer. It then calls Req_List_Add_Req to adding the single request
;       list entry to the request list. It then loops the page list buffer
;       calling Req_List_Add_SG to add each break to the request list.
;
;       NOTE: In the future we should do rep movsb to copy the break info over.
;
;
;       ENTRY   ES:DI           = Pointer to request data structure
;                  AX           = Command Code
;                  CX           = Count
;               BX:DX           = Starting Sector
;                  SI           = IOoffset
;       EXIT    'NC'
;
;       USES    ALL
;
;       NOTE:   This routine should be converted to 32 bit 'C' in the future.
;
;
Procedure Req_List_Add_Extent,<HYBRID,far16>
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
LocalVar    rlaex_ReqList,DWORD   ; request list buffer pointer
LocalVar    rlaex_Count,WORD      ; number of sectors
LocalVar    rlaex_Rba,DWORD
LocalVar    rlaex_IOoffset,WORD
LocalVar    rlaex_command,WORD
LocalVar    rlaex_NoOfPgBrks,WORD ;
LocalVar    rlaex_PageList,DWORD  ; page list buffer pointer
LocalVar    rlaex_PageListOffset,WORD
LocalVar    rlaex_PgLstBuffer,PGLSTSIZE
LocalVar    rlaex_ByteCount,WORD
LocalVar    rlaex_hVPB,WORD       ; hVPB

        EnterProc <es,di,cx,bx,dx,si,ax>
        SaveReg <si,di,es,ds>
        mov     si,CurrTCB
        mov     ax,TASKAREA:[si].TCBThishVPB
        mov     rlaex_hVPB,ax
        push    TASKAREA:[si].TCBDMAAdd.Offst    ; save old DMAAdd offset
        mov     ax,rlaex_IOoffset
        mov     TASKAREA:[si].TCBDMAAdd.Offst,ax ; Put temp DMAAdd offset
        mov     ax,cx
        mov     cx,512
        mul     cx                               ; AX = byte cnt in contig I/O
        mov     cx,ax                            ; CX = byte cnt of request
        lea     di,rlaex_PgLstBuffer
        TaskContext     es                       ; ES:[DI]->Page lst
ASSUME  ES:NOTHING
        call    GetPageBreakInfo                 ; get contig.break info
        mov     si,CurrTCB
        pop     TASKAREA:[si].TCBDMAAdd.Offst    ; Restore DMAAdd offset
        LJC     rlaexx                           ; jump if memory not valid
        mov     [rlaex_PageList].Offst,di
        mov     [rlaex_PageList].Segmt,es        ; ES:[DI]->Page list buffer
        mov     [rlaex_PageListOffset],di        ; Save running offset
        mov     [rlaex_NoOfPgBrks],cx            ; CX = number of page breaks

        mov     ax,[rlaex_command]               ; AL = command
        les     di,rlaex_ReqList                 ; ES:[DI]->ReqList Ctrl Block
        mov     dx,rlaex_Rba.LowWord
        mov     bx,rlaex_Rba.HighWord            ; BX:DX = starting RBA
        mov     cx,rlaex_Count                   ; CX = sector count

        mov     ah,PRIO_BACKGROUND_USER          ; AH = priority
        mov     si,rlaex_hVPB                    ; SI = hVPB
        call    Req_List_Add_Req                 ; Add single request list entry
        or      ax,ax                            ; Any Error
        jnz     rlaexx                           ; Yes, Exit
        mov     es,[rlaex_PageList].Segmt
        mov     di,rlaex_PageListOffset          ; ES:[DI]->Curr page list entry
        mov     ax,cx
        mov     cx,512
        mul     cx                               ; AX = byte cnt in contig I/O
        mov     rlaex_ByteCount,ax

rlaex1: mov     cx,rlaex_ByteCount               ; CX = remaining bytes in I/O
        or      cx,cx                            ; Any remaining bytes in I/O ?
        jz      short rlaex4                     ; No, exit

        mov     ax,es:[di].pl_cb.LowWord
        cmp     cx,ax                            ; Is Contiguous Count <= page break count
        jbe     rlaex2                           ; yes, use contiguous count
        mov     cx,ax                            ; no, use page break count
rlaex2:
        mov     dx,es:[di].pl_paddr.LowWord
        mov     bx,es:[di].pl_paddr.HighWord     ; BX:DX = phys address of buf
        add     es:[di].pl_paddr.LowWord,cx
        adc     es:[di].pl_paddr.HighWord,0
        sub     es:[di].pl_cb.LowWord,cx
        jnz     rlaex3
        add     di,SIZE pagelist_s               ; ES:[DI]->Next page lst entry
        mov     rlaex_PageListOffset,di
rlaex3:
        SaveReg <es,di,cx>
        les     di,rlaex_ReqList                 ; ES:[DI]->ReqList Ctrl Block
        call    Req_List_Add_SG                  ; Add S/G entry.
        RestoreReg <cx,di,es>
        sub     rlaex_ByteCount,cx               ; Update Byte count remaining
        jmp     short rlaex1                     ; Go process next S/G entry.
rlaex4: xor     ax,ax                            ; NO ERROR
rlaexx: RestoreReg <ds,es,di,si>
        LeaveProc
        ret
EndProc   Req_List_Add_Extent

BREAK   <Req_List_Read - ReqList read>
;***    Req_List_Read - ReqList Read
;
;   Req_List_Read is used by the new cache to read data into a single cache
;   block. It allocates a request list, initializes it and executes and
;   returns any errors.
;
;   ENTRY   ebx         Physical Address to read into
;           cx          is count of sectors
;           di:dx       is starting logical sector number
;           si          VPB handle
;           ax          fLazyWrite
;
;   EXIT    ax =  0 - successful completion of operation
;           ax != 0 - operation was failed
;
;   USES    all
;
;       NOTE:   This routine should be converted to 32 bit 'C' in the future.
;
Procedure Req_List_Read,<HYBRID,far16>
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
LocalVar    rlr_hVPB,WORD
LocalVar    rlr_RBA,DWORD
LocalVar    rlr_count,WORD
LocalVar    rlr_fLazyWrite,WORD
LocalVar    rlr_addr,DWORD

        EnterProc <si,di,dx,cx,ax>
CpuMode 386
        mov     rlr_addr.LowWord,bx
        shr     ebx,16
        mov     rlr_addr.HighWord,bx
CpuMode 286

        call    GetReqList              ; ES:[DI]->Request List
        SaveReg <bp,es,di,si>
        mov     bp,[rlr_hVPB]           ; BP = hVPB
        xor     cx,cx                   ; No flags
        call    Req_List_Init           ; Initialize request list
        RestoreReg <si,di,es,bp>

        mov     ax,PB_READ_X            ; AL = Command
        mov     dx,rlr_RBA.LowWord
        mov     bx,rlr_RBA.HighWord     ; BX:DX = starting RBA
        mov     cx,rlr_count            ; CX = sector count
        SaveReg <bp,es,di>
        mov     ah,PRIO_BACKGROUND_USER ; Assume this is an ordinary request
        mov     si,rlr_hVPB             ; SI = hVPB
        call    Req_List_Add_Req
        RestoreReg <di,es,bp>
        or      ax,ax                   ; Any Error
        jnz     rlrd2                   ; Yes, Exit

        mov     dx,rlr_addr.LowWord
        mov     bx,rlr_addr.HighWord    ; BX:DX physical buffer pointer
        mov     cx,rlr_count
        shl     cx,9                    ; CX = Sector count * 512
        SaveReg <bp,es,di>
        call    Req_List_Add_SG
        RestoreReg <di,es,bp>

        mov     ax,[rlr_hVPB]           ; AX = hVPB
        call    Req_List_Exec           ; Read data
        jmp     short rlrd3

rlrd2:  SaveReg <es,di,ax>
        call    FreeReqList             ; Free request list on error
        RestoreReg <ax,di,es>
rlrd3:  LeaveProc
        ret
EndProc Req_List_Read

BREAK   <Req_List_Write - ReqList Write>
;***    Req_List_Write - ReqList Write
;
;   Req_List_Write is used by the new cache to write data from a single cache
;   block. It allocates a request list, initializes it and executes and
;   returns any errors.
;
;   ENTRY   ebx         Physical Address to read into
;           cx          is count of sectors
;           di:dx       is starting logical sector number
;           si          VPB handle
;           ax          Request List Selector
;
;   EXIT    carry clear - successful completion of operation
;           carry set - operation was failed
;
;   USES    all
;
;       NOTE:   This routine should be converted to 32 bit 'C' in the future.
;
Procedure Req_List_Write,<HYBRID,far16>
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
LocalVar    rlw_hVPB,WORD
LocalVar    rlw_RBA,DWORD
LocalVar    rlw_count,WORD
LocalVar    rlw_addr,DWORD
LocalVar    rlw_fReqListSel,WORD

        EnterProc <si,di,dx,cx,0>
CpuMode 386
        mov     rlw_addr.LowWord,bx
        shr     ebx,16
        mov     rlw_addr.HighWord,bx    ; rlw_addr = physical address of data
CpuMode 286
        mov     cx,RLCB_KEEP_REQ
        or      ax,ax
        jz      rlwt0
        mov     es,ax
        xor     di,di
        inc     rlw_fReqListSel
        jmp     short rlwt1
rlwt0:  call    GetReqList              ; ES:[DI]->New request list
        xor     cx,cx                   ; no flags for req_list_init

rlwt1:  SaveReg <bp,es,di,si>
        mov     bp,[rlw_hVPB]           ; BP = hVPB
        call    Req_List_Init           ; Initialize request list
        RestoreReg <si,di,es,bp>

        mov     ax,PB_WRITE_X           ; AL = command
        mov     dx,rlw_RBA.LowWord
        mov     bx,rlw_RBA.HighWord     ; BX:DX = RBA
        mov     cx,rlw_count            ; CX = count of sectors to write
        SaveReg <bp,es,di>
        mov     ah,PRIO_BACKGROUND_USER ; Assume this is an ordinary request
        mov     si,rlw_hVPB             ; SI = hVPB
        call    Req_List_Add_Req        ; Add new request entry.
        RestoreReg <di,es,bp>
        or      ax,ax                   ; Any Error
        jnz     rlwt2                   ; Yes, Exit

        mov     dx,rlw_addr.LowWord
        mov     bx,rlw_addr.HighWord    ; BX:DX physical buffer pointer
        mov     cx,rlw_count            ; CX = sector count
        shl     cx,9                    ; CX = CX * 512
        SaveReg <bp,es,di>
        call    Req_List_Add_SG         ; Add S/G descriptor to request list
        RestoreReg <di,es,bp>

        mov     ax,[rlr_hVPB]           ; AX = handle to volume
        call    Req_List_Exec           ; Write data to disk
        jmp     short rlwt3

rlwt2:  cmp     rlw_fReqListSel,0
        jne     rlwt3
        SaveReg <es,di,ax>
        call    FreeReqList             ; Free request list on error
        RestoreReg <ax,di,es>
rlwt3:  LeaveProc
        ret
EndProc Req_List_Write

BREAK <Req_List_Add_SG> - Add a SG entry to request list
;***   Req_List_Add_SG  - Adds a SG entry to request list
;
;       Req_List_Add_SG is called to the S/G portion of a new request entry
;       to the request list.
;
;       ENTRY   ES:DI           = Pointer to request data structure
;                  CX           = Count
;               BX:DX           = Buffer Pointer
;
;       EXIT    NONE
;
;       USES    CX,DX
;
;       NOTE:   This routine should be converted to 32 bit 'C' in the future.
;
Procedure Req_List_Add_SG,<HYBRID,far16>
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA

LocalVar rlasg_fAdd17,WORD
LocalVar rlasg_17_BufSize,WORD
LocalVar rlasg_17_BufPtr,DWORD
LocalVar rlasg_ReqListOff,WORD


        EnterProc <0>

        SaveReg <es,di,si,ds,ax,bx>
        mov     si,di                        ; ES:[SI]->Request list ctrl blk
        mov     rlasg_ReqListOff,si
        mov     di,es:[di].RLCB_Pointer      ; ES:[DI]->location to add S/G

; See if adding this S/G descriptor would overflow the request list. Note,
; this code may be put under FSSTRICT.

        SaveReg <cx>
        mov     cx,S2_RW_RQPKT_SIZE          ; CX = max size of a request list
        mov     ax,di
        sub     ax,es:[si].RLCB_HeaderPtr
        add     ax,size SG_Descriptor        ; AX = total size used include S/G
        cmp     ax,cx                        ; Overflow ?
        RestoreReg <cx>
        jbe     rlasg0                       ; No, continue
        jmp     rlasgx                       ; Yes, panic

; The IBM scsi adapters must have every 16th S/G entry end on sector boundary

CpuMode 386
rlasg0: mov     si,es:[si].RLCB_CurrReqPtr
        cmp     es:[si].PB_SG_Desc_Count,15  ; Are we adding the 16th S/G ?
        je      rlasg1                       ; No,
        cmp     es:[si].PB_SG_Desc_Count,31  ; Are we adding the 32th S/G ?
        jne     rlasg2                       ; No,

rlasg1: mov     si,rlasg_ReqListOff          ; ES:[SI]->Request List Ctrl Blk.
        mov     eax,es:[si].RLCB_CurrByteCount
        movzx   ecx,cx                       ; ECX = current S/G desc. count

        add     eax,ecx
        and     eax,512-1                    ; 16th entry end on sec. boundary?
        jz      rlasg2                       ; yes, keep on truckin

        cmp     cx,512                       ; Is 16th S/G < 512
        jb      rlasgx1                      ; Yes, go panic

; We make the following assumptions:
;
; 1. There are one or more S/G entries which will follow.
; 2. The 16th entry is at least 512 bytes in size.
;
;       EAX = Number of bytes to subtract from 16th entry and it is also the
;             the number of bytes to fill in the 17th entry with.


        sub     ecx,eax                      ; ECX = # of bytes for 16th entry
        mov     rlasg_fAdd17,-1              ; Singal we added 17th entry
        mov     rlasg_17_BufSize,ax          ; AX = 17th buffer size

        SaveReg <dx>
        mov     ax,bx                        ; AX:DX = 16th entry buffer pointer
        add     dx,cx
        adc     ax,0                         ; AX:DX = 17th entry buffer pointer
        mov     rlasg_17_BufPtr.LowWord,dx
        mov     rlasg_17_BufPtr.HighWord,ax  ; Save 17th entry buffer pointer
        RestoreReg <dx>

CpuMode reset
rlasg2: mov     si,rlasg_ReqListOff                   ; ES:[SI]->ReqLst CtrlBlk
        mov     es:[di].SG_BufferSize.LowWord,cx      ; Store S/G size
        mov     es:[di].SG_BufferSize.HighWord,0
        add     es:[si].RLCB_CurrByteCount.LowWord,cx ; Compute New CurrByteCount
        adc     es:[si].RLCB_CurrByteCount.HighWord,0
        mov     es:[di].SG_BufferPtr.LowWord,dx
        mov     es:[di].SG_BufferPtr.HighWord,bx      ; Store S/G buffer pointer
        add     di,size SG_Descriptor                 ; ES:[DI]->Next location

        mov     es:[si].RLCB_Pointer,di               ; Store Next location
        mov     di,es:[si].RLCB_CurrReqPtr
        inc     es:[di].PB_SG_Desc_Count              ; Increment S/G desc. count
        add     es:[di].RH_Length,size SG_Descriptor  ; New size of Request entry

        cmp     rlasg_fAdd17,0                        ; Are we adding a 17th
        je      rlasg3                                ; No, exit
        mov     rlasg_fAdd17,0                        ; Reset adding 17th flag
        mov     di,es:[si].RLCB_Pointer               ; ES:[DI]->17th location
        mov     cx,rlasg_17_BufSize                   ; CX = 17th buffer size
        mov     dx,rlasg_17_BufPtr.LowWord
        mov     bx,rlasg_17_BufPtr.HighWord           ; BX:DX = 17th buffer pointer
        jmp     short rlasg2                          ; go add 17th

rlasg3: RestoreReg <bx,ax,ds,si,di,es>
        LeaveProc
        ret
rlasgx:
        Panic   <"Req_List_Add_SG: ReqList Overflow">
rlasgx1:
        Panic   <"Req_List_Add_SG: 16th Entry < 512">
EndProc   Req_List_Add_SG

BREAK <Req_List_Exec> - Execute a request list
;***   Req_List_Exec  - Executes a request list
;
;       Req_List_Exec is called when all request entries and S/G descriptors
;       have been added to the request list and the request list is ready to
;       be executed.
;
;       ENTRY   ES:DI           = Pointer to request data structure
;                  AX           = hVPB
;       EXIT    'NC'            = No Error
;               'CY'            = Error
;
;       USES    CX,DX,DS
;
;
;
Procedure Req_List_Exec,<HYBRID,far16>
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
LocalVar hVPB,WORD
LocalVar pReqList,DWORD
LocalVar req_error,BYTE

        EnterProc <ax,es,di>

        SaveReg <bx,si,es,di>
rle0:   les     di,pReqList                     ; ES:[DI]->Reqlist Control Blk.
        mov     bx,di                           ; BX = DI
        mov     di,es:[di].RLCB_HeaderPtr       ; ES:[DI]->Request list header
        mov     si,es:[di].RLH_Count.LowWord
        or      si,es:[di].RLH_Count.HighWord   ; Any request entries in list?
        LJZ     rle5                            ; No, go free request list
        mov     es:[bx].RLCB_Sem,-1             ; Set semaphore
        SaveReg <es,di,cx,bx,si,ds>
CpuMode 386
        mov     si,hVPB
        VPBContext ds,si                        ; DS:[SI]-VPB

;### AC DCR
; This is the spot to dirty the fatdirtybit if not already done.
; Will call a routine to check and write out value.
        pushf
        pusha
        les     di,pReqList                     ; ES:[DI]->Reqlist Control Blk.
        mov     di,es:[di].RLCB_CurrReqPtr      ; ES:[DI]->Request list header
        cmp     es:[di].RH_Command_Code, PB_WRITE_X    ;
        jne     rle1a
        test    ds:[si].vpb_fsd.vpdFAT_eaflags,0100h ; Clean volume          ;### AC DCR 1520
        jnz     rle1a
        call    FatDirtyBitSet
rle1a:  popa
        popf
        lds     si,ds:[si].vpb_fsi.vpi_pDPB     ; DS:SI-> DPB
        mov     eax,ds:[si].dpb_strategy2       ; EAX -> strat2 entry pt.
        or      eax,eax                         ; strat2 supported?
        jz      rleie                           ; no, go panic

        mov     bx,di                           ; ES:BX -> request packet
        call    dword ptr ds:[si].dpb_strategy2 ; call device driver
CpuMode reset
        RestoreReg <ds,si,bx,cx,di,es>
rle1:   cli
        xor     al,al
        xchg    es:[bx].RLCB_Sem,al             ; Reset and get semaphore
        or      al,al                           ; Was semaphore Set ?
        jz      rle2                            ; No, request completed
        PUSHA                                   ; Yes, wait on request
        push    es
        push    bx
        MOV     AX,ES
        MOV     BX,DI                           ; ES:[BX]->Request List header
        MOV     CX,-1
        MOV     DI,CX                           ; CX:DI = infinite timeout
        MOV     DH,1
        CALLFAR ProcBlock                       ; Wait for request to finish
        pop     bx
        pop     es
        POPA

; We have to check for spurious wakeup. We check to make sure that the request
; list header indicates that the request list is actually completed before
; going on.

        cli
        mov     es:[bx].RLCB_Sem,-1             ; Set semaphore
        test    es:[di].RLH_Lst_Status,RLH_All_Req_Done ; All requests done ?
        jz      rle1                            ; No, go back to sleep
rle2:   sti
        xor     ah,ah
        mov     al,es:[di].RLH_Lst_Status       ; AL = request list status
        shr     al,4                            ; Get significant error bits
        or      al,al                           ; Was there an ERROR ?
        jz      rle5                            ; No, go free request list.
        shl     al,4
        cmp     al,RLH_Rec_Error                ; Did retry work ?
        je      rle5                            ; Yes, no error


; Find Request that fail

        mov     cx,es:[di].RLH_Count.LowWord  ; CX = count of requests
        add     di,size Req_List_Header       ; ES:[DI]->first request
        xor     ah,ah
rle7:   cmp     es:[di].RH_Error_Code,0       ; Error on this request ?
        je      rle8                          ; No, proceed to next request
        mov     al,es:[di].RH_Error_Code      ; Yes, AL = error code
        jmp     short rle9
rle8:   add     di,es:[di].RH_Length          ; ES:[DI]->next request list
        loop    rle7

        InternalError <Req_List_Exec: could not find error code>

; There was an ERROR take appropriate action.

rle9:   test    es:[bx].RLCB_Flags,RLCB_HOTFIX
        jz      rle10

        SaveReg <ebx,es,eax>
        movzx   edx,dx                  ; EDX = request list head offset
        movzx   eax,al                  ; EAX = error code
        movzx   edi,di
        movzx   ecx,cx                  ; ECX = number of request lists remain
        mov     si,es
        movzx   esi,si                  ; ESI:EDI->First failing reqlist
        movzx   ebx,es:[bx].RLCB_hVPB   ; EBX = hVPB
        FlatContext <ds,es>
        CCALL32 _HotFix,<ecx,esi,edi,ebx>
        RestoreReg <edx,es,ebx>
        ASSUME  DS:NOTHING,ES:NOTHING
        or      eax,eax                 ; did hotfix work ?
        jz      rle5                    ; yes, go exit carry clear
        mov     esi,eax
rle10:  xor     ah,ah
        SaveReg <esi>
        mov     si,ax
        DosContext DS
        mov     al,ErrMap24[si]         ; get dos error code b724822
        mov     req_error,al
        mov     ax, si               ; b734628 fixes problem caused by b724822
        RestoreReg <esi>
        Assume  DS:NOTHING
        cmp     esi,-1                  ; just return the error ?
        je      rle4                    ; yes
        SaveReg <di>
        mov     di,[CurrTCB]
        test    TASKAREA:[di].TCBJfnFlag,JFN_FAIL ; autofail hard error?
        RestoreReg <di>
        jnz     rle4                          ; yes, go fail this call

        SaveReg <es,bx>
        mov     si,hVPB
        VPBContext ds,si
        lds     si,[si].vpb_fsi.vpi_pDPB      ; ds:si -> dpb
        mov     bx,di
        xor     dh,dh
        mov     di,ax                         ; di = error code
        mov     dl,es:[bx].RLH_Block_Dev_Unit
        CALLFAR DiskHE3                       ; Signal hard error
        RestoreReg <bx,es>

; We check the flag bits in TCBBufHE to see who should carry out the user
; specified action. If BufHE_Percolate bit is set, we return the action in
; TCBactBufHE and let the caller do the action. Otherwise, we do it here.

        mov     di,[CurrTCB]
        test    TASKAREA:[di].TCBBufHE,BufHE_Percolate
        jz      rle3                          ; We can handle action at this level

        mov     TASKAREA:[di].TCBactBufHE,al  ; Save user specified action
        jmp     short rle4                    ; Let caller handle action

rle3:   or      ax,ax                         ; did user want to IGNORE?
        .errnz HDERR_IGNORE
        jz      rle5                          ; yes, pretend successful (carry clear)

        cmp     ax,HDERR_RETRY                ; how about RETRY?
        jz      rle0                          ; yes, back to top

; The user wants to FAIL the request. Fall thru.
rle4:
        xor     ah,ah
        mov     al,req_error
        stc                                   ; set carry for exit
rle5:   pushf
        test    es:[bx].RLCB_Flags,RLCB_KEEP_REQ  ; Keep request list around ?
        jnz     short rle6                    ; Yes, don't free it.
        SaveReg <es,di,ax>                    ; No, free request list
        mov     di,bx                         ; ES:[BX]->Request list
        call    FreeReqList                   ; Free request list
        RestoreReg <ax,di,es>
rle6:   popf
        RestoreReg <di,es,si,bx>
        LeaveProc
        ret
rleie: InternalError   <Req_List_Exec: No Strategy2 Routine>
EndProc   Req_List_Exec

BREAK <GetReqList> - Gets a request list
;***   GetReqList  - Gets a request list
;
;       GetReqList allocates a request list by calling S2AllocReqPkt.
;
;       ENTRY   NONE
;       EXIT    ES:DI         = Pointer to request list
;
;       USES    Flags
;
;
Procedure GetReqList,<hybrid,far16>
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING
;       PMONLY

        SaveReg <dx,bx,ds>
        MOV     DX,1                    ; STRAT2 and OK to Wait
        CALLFAR S2AllocReqPkt           ; ES:[BX]->new request packet
        mov     di,bx                   ; ES:[DI]->new request list
        RestoreReg <ds,bx,dx>

        ret
EndProc   GetReqList

BREAK <FreeReqList> - Free a request list
;***   FreeReqList  - Free a request list
;
;       Frees a request list by calling FreeReqPacket.
;
;       ENTRY   ES:DI         = Pointer to request list
;       EXIT    NONE
;
;       USES    Flags
;
;
Procedure FreeReqList,<hybrid,far16>
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING
;       PMONLY
        SaveReg <bx>
        mov     bx,di                   ; ES:[BX]->Request list
        CALLFAR FreeReqPacket           ; Free request list
        RestoreReg <bx>

        ret
EndProc   FreeReqList

BREAK <UseCache> - Determines if this volume should use the cache.
;***   UseCache  - Determines if this volume should use the cache.
;
;       UseCache determines whether certain conditions exists that prevents
;       this volume from using the new FAT cache. The following are the
;       condition that must be met before using the new FAT cache.
;
;         1. There must be a valid DiskCache statement in config.sys. This is
;            kept via the FATCache variable.
;
;         2. There must not have been an error initializing the cache for this
;            volume.
;
;         3. The device for this volume must not be removable.
;
;         4. The device driver must support Strategy2.
;
;         5. The device must not be used for a VDISK.
;
;       ENTRY   BX            = Handle to volume
;       EXIT    NC            = Use Cache
;               CY            = Do Not Use Cache
;
;       USES    Flags
;
;
Procedure UseCache,<hybrid,far16>
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING
        SaveReg <ds,es,bx,si>
        DosContext es
        cmp     es:[FATCache],0                          ; DiskCache ?
        ASSUME ES:NOTHING
        je      uche2                                    ; No, no cache
        cmp     bx, 0                                    ; If BUF_FREE        ;### b731143
        je      uche1                                    ; don't cont.        ;### b731143
        VPBContext ES,BX
        cmp     es:[bx].vpb_FSC.Offst,FSC_LOCAL          ; FSD volume?
        jne     uche2                                    ; no (FAT), go exit
        test    es:[bx].vpb_fMisc,VPB_FM_INITCACHE_ERROR
        jnz     uche2
        lds     si,es:[bx].vpb_fsi.vpi_pDPB              ; es:bx ->DPB
        test    ds:[si].dpb_flags,DPBNONREMOV
        ASSUME ES:NOTHING
        jz      uche2                                    ; assumption was correct
        cmp     ds:[si].dpb_strategy2.Segmt,0
        je      uche2
        test    ds:[si].dpb_flags,DPBVCRAMDISK           ; VDISK ?
        jnz     uche2                                    ; Yes, no cache
        clc
uche1:  RestoreReg <si,bx,es,ds>
        ret
uche2:  stc
        jmp     short uche1
EndProc UseCache

BREAK <Init_VPB_Cache> - Initializes the cache data for this volume
;***   Init_VPB_Cache  - Initializes the cache data for this volume
;
;       Init_VPB_Cache checks to see if the drive is not locked and that the
;       cache has not been initialized yet, and if not it locks the VPB,
;       calls InitVPBCacheData to initialize the VPB data for the new FAT
;       cache and unlocks the VPB.
;
;       ENTRY   SI      = Handle to volume
;       EXIT    NONE
;
;       USES    Flags
;
;       PSEUDOCODE:
;
Procedure Init_VPB_Cache,hybrid
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING
CpuMode 386
        SaveReg <ds,es>
        pushad

        VPBContext ES,SI
        SaveReg <ds,si>
        lds     si,es:[si].vpb_fsi.vpi_pDPB     ; es:bx ->DPB
        cmp     DS:[SI].dpb_drive_lock,0 ; drive lock word
        RestoreReg <si,ds>
        jne     ivpbc1
        test    es:[si].vpb_fMisc,VPB_FM_INITCACHE_DONE
        jnz     ivpbc1
        ASSUME  ES:NOTHING
        SaveReg <bp>
        mov     ax,si                   ; (ax) = VPB handle
        mov     bl,VPBINITCACHE         ; (bl) = VPB allocation lock bit
        CALLFAR WaitVPB                 ; Wait for VPB allocate bit to become 0
        RestoreReg <bp>
;;#127739
        clc
        test    es:[si].vpb_fMisc,VPB_FM_INITCACHE_DONE ; VPB init cache done
                                                        ; while blocked?
        jnz     ivpbc1                                  ; yes, then bail out.

        or      es:[si].vpb_flags,VPBINITCACHE ; Set VPB init cache bit
;;#127739

        SaveReg <es,si>
        mov     di,si                   ; cx:di point to vpb
        mov     cx,es
        FlatContext <ds,es>,ax
        CCALL32 InitVPBCacheData,,nocall
        RestoreReg <si,es>
        or      es:[si].vpb_fMisc,VPB_FM_INITCACHE_DONE
        ASSUME  DS:NOTHING,ES:NOTHING
        pushf
        mov     ax,si                   ; (ax) = VPB handle
        mov     bl,VPBINITCACHE         ; (bl) = VPB InitCache lock bit
        CALLFAR WakeVPB                 ; Release VPB allocation lock
                                        ; (WakeVPB clears bit)
        popf

ivpbc1: popad
        RestoreReg <es,ds>
CpuMode reset
        ret
EndProc Init_VPB_Cache

BREAK   <GetPageBreakInfo - gets the contig.break info>

;*** GetPageBreakInfo
;       This Procedure queries page manager for
;       page contiguity info for the given user buffer
;
;       INPUT:  cx = count
;               TCBDMAAdd ->transfer address
;               es:di = temp buff where page break info is stored
;
;       OUTPUT: NC ->   cx = no of breaks
;                       es:di = temp buff where page break info is stored
;               CY -> invalid user buffer ??
;
;       USES    es,di,cx,ax,bx,flags
;
;       PSEUDOCODE:
;               Convert the User selector to 0:32
;               Call PGLinToPage to get page break info
;

Procedure       GetPageBreakInfo,NEAR,LOCAL
        assume  ds:nothing,es:nothing,ss:TASKAREA
LocalVar        gpb_count,WORD
LocalVar        gpb_tmpbuf,DWORD
LocalVar        gpb_tran_addr,DWORD

CpuMode 386
        EnterProc      <cx,es,di>

        mov     bx,CurrTCB
        test    TASKAREA:[bx].TCBfSwapping,SM_TCB_SWAPPING
        jz      short gpb1        ; swap IO always to CONTIGUOUS memory (pages)

        SaveReg <ds,si,es,di>
        lds     si,TASKAREA:[bx].TCBDMAAdd      ; ds:si -> transfer address
        CALLFAR VirtToPhys                      ; ax:bx -> phys. address
        RestoreReg <di,es,si,ds>                ; no other register affected
IFDEF FSSTRICT
        jc      gpbiie                          ; we are hosed! got error for
                                                ; a memory addr given to us by
                                                ; swapper
ENDIF
        mov     es:[di].pl_paddr.Segmt,ax       ;
        mov     es:[di].pl_paddr.Offst,bx       ;

        movzx   ecx,cx
        mov     es:[di].pl_cb,ecx   ; This can never be more than 64k
        mov     cx,1                ; no page breaks ; contiguous memory
        jmp     gpb2
gpb1:
; convert the user selector to a 0:32 ptr

        mov     di,CurrTCB
        movzx   eax,TASKAREA:[di].TCBDMAAdd.Segmt
        FlatContext <ds,es>,bx
        CCALL32  _SELConvertToLaddr,<eax>

IFDEF FSSTRICT
        cmp     eax,-1                          ; error ?
        je      short gpbiie
ENDIF

        assume ds:nothing,es:nothing

        mov     di,CurrTCB
        movzx   ebx,TASKAREA:[di].TCBDMAAdd.Offst
        add     eax,ebx                         ; laddr +=offset
        mov     gpb_tran_addr,eax


; convert  the 16:16 temp buffer address to 0:32
; since this is on stack use sstods

        movzx   eax,gpb_tmpbuf.Offst

        ASSUME  DS:FLAT
        SSToDS  eax

        SaveReg <es,ds>
        movzx   ecx,gpb_count           ; (ecx) = count of bytes
                                        ; parm4: page info buffer
                                        ; parm3: count of bytes
                                        ; parm2: context
                                        ; parm1: base linear address
        CCALL32  _PGLinToPageList,<gpb_tran_addr,[_pPTDACur],ecx,eax> ; (eax) = number of elements in array
        RestoreReg      <ds,es>
        mov     cx,ax
        assume ds:nothing,es:nothing
gpb2:   xor     ax,ax
        les     di,gpb_tmpbuf
        LeaveProc
        ret

gpberr: stc

IFDEF FSSTRICT
gpbiie: InternalError   <GetPageBreakInfo: invalid transfer address?>
ENDIF

EndProc GetPageBreakInfo

BREAK   <FSGetPriorityInfo - get running thread's priority>
;
;       FSGetPriorityInfo returns the running thread's execution priority
;       class: "background vs. foreground" as defined by the schedular .
;
;       ENTRY   n/a
;
;               call    FSGetPriorityInfo
;
;       EXIT    (ax) = foreground priority indicator
;                       ffffh = yes, assign foreground class
;                       0000h = no, assign background class
;
;       USES    AX, DI, Flags
;
;       CALLS   n/a
;
;       PSEUDOCODE
;
;
Procedure   FSGetPriorityInfo,HYBRID
   assume ds:nothing, es:nothing, ss:TASKAREA

        EnterProc                       ;
        xor     ax, ax                  ; init ax to = background indicator
        push    ds
        DosContext ds                   ; get access to global priority mode
                                        ; flag
        cmp     FSPrtyIO_Mode, 0        ; are we in priority I/O mode?
        Assume  ds:nothing
        pop     ds
        jz      fsgpExit                ; no, exit

        push    di                      ; save di
        mov     di, CurrTCB             ; get addressibility to running thread
        test    ss:[di].TCBSpareFlags,SPFLAGS_FGND_DISKIO  ; foreground?
        pop     di                      ; restore di
        jz      fsgpExit                ; no
        mov     ax, 0ffffh              ; yes, set foreground indicator

fsgpExit:
        LeaveProc                       ;
        ret                             ;

EndProc FSGetPriorityInfo

BREAK   <FatDirtyBitSet  - Sets dirty bit for fat volume>

;***   FatDirtyBitSet
;
;       IN:     si = hVPB
;       OUT:    NC ->   Value set
;               CY ->   error
;
;       USES:   AX,CX,SI,DI,DS,ES
;
;       CALLS:  FatDirtyBitToggle
;

Procedure FatDirtyBitSet,NEAR,LOCAL

        assume  DS:nothing,ES:nothing,SS:TASKAREA
        push    ds                                                                 ;### AC DCR 1520
        DosContext ds                                                              ;### AC DCR 1520
        cmp     initfatchkdsk,1         ; If trying to chkdsk a fat boot partition ;### AC DCR 1520
        pop     ds                                                                 ;### AC DCR 1520
        je      fdbs1                   ; Init time                                ;### AC DCR 1520
   ;    #81195 fix  begins...
        push    ds
        push    bp
        push    bx
        DosContext ds
        mov     bx,GSEL GDT_SFT
        mov     ds,bx
        mov     bx,ds:[0]
        mov     ds,bx
        cmp     ds:[sft_inshutdown],StopAll
        jne     short fdbs00
        mov     bp,TASKAREA:CurrTCB
        cmp     TASKAREA:[bp].TCBfSwapping,SM_TCB_SWAPPING
        je      short fdbs01
fdbs00:
        pop     bx
        pop     bp
        pop     ds
        jmp     short fdbs02
fdbs01:
        pop     bx
        pop     bp
        pop     ds
        je      short fdbs1
   ;    #81195 fix ends...
fdbs02:
        xor     eax,eax
        mov     al,01h
        CALLFAR FatDirtyBitToggle
fdbs1:  ret

EndProc FatDirtyBitSet

BREAK   <FatDirtyBitClear - Clears dirty bit for fat volume>

;***   FatDirtyBitClear
;
;      Called from w_shutdown to clear all fat volumes dirty bits
;
;       IN:     Nothing
;       OUT:    Nothing
;
;       USES:   AX,CX,SI,DI,DS,ES
;
;       CALLS:  FatDirtyBitToggle
;

Procedure FatDirtyBitClear,HYBRID       ;72417 allow far call

        assume  DS:nothing,ES:nothing,SS:TASKAREA
        pushf
        SaveReg <es,di,ds,si>                                                      ;###
        DosContext es                                                        ;###
        mov     si,[hVPBFirst]          ; bx => VPB to examine
    ASSUME      es:NOTHING
fdbc0:  or      si,si                   ; are we at the end of the list?
        jz      fdbc10                  ; yes, go return
        VPBContext ES,SI                ; (es:bx) -> VPB we wish to examine
        cmp     es:[si].vpb_FSC.Offst,FSC_LOCAL         ; fat file system?
        JNE     fdbc5
        test    es:[si].vpb_fsd.vpdFAT_eaflags,0100h ; Clean volume          ;### AC DCR 1520
        jz      fdbc5
        xor     eax,eax
        SaveReg    <es,si>
        CALLFAR FatDirtyBitToggle
        RestoreReg  <si,es>
fdbc5:  mov     si,es:[si].vpb_flink    ; no, link to next VPB
        jmp     fdbc0

fdbc10: RestoreReg <si,ds,di,es>                                                      ;###
        popf
        ret

EndProc FatDirtyBitClear

HIGH4CODE ENDS
        END
