        TITLE   BUF - MSDOS buffer management
        NAME    BUF

IF 1
__TONYI__ = 1           ; Space saving hacks
__TONYI__1= 1           ; Space saving hacks 10/28/92
__TONYI__2= 1           ; 386 hibuffers tweaks (not implemented)
__TONYI__3= 1           ; Pull SET_RQ_SC_PARMS inline.
__TONYI__4= 1           ; Space saving hacks 11/25/92
__TONYI_486__= 1        ; Avoid 486 pipeline stalls
__TONYI_KILL_SC__ = 1   ; Eliminate SC code
VCB_MOVSD = 1           ; 386 dword buffer transfers
ENDIF


;
;       IBM Corporation
;       Copyright (C) IBM Corporation 1993
;       All Rights Reserved.
;

;**     BUF.ASM - Low level routines for buffer cache management
;
;       GETCURHEAD
;       ScanPlace
;       PLACEBUF
;       PLACEHEAD
;       PointComp
;       GETBUFFR
;       GETBUFFRB
;       FlushBuf
;       BufWrite
;       SET_RQ_SC_PARMS
;
;       Revision history:
;
;               AN000  version 4.00  Jan. 1988
;               A004   PTM 3765 -- Disk reset failed
;               M039 DB 10/17/90 - Disk write optimization
;               I001   5.0 PTR 722211 - Preserve CY when in buffer in HMA


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

Installed = TRUE

        i_need  PreRead,WORD
        i_need  LastBuffer,DWORD
        i_need  CurBuf,DWORD
        i_need  WPErr,BYTE
        i_need  ALLOWED,BYTE
        i_need  FAILERR,BYTE
        i_need  HIGH_SECTOR,WORD             ; DOS 4.00 >32mb                   ;AN000;
        i_need  BufferQueue,DWORD
        i_need  DirtyBufferCount,WORD
        i_need  SC_CACHE_PTR,DWORD           ; DOS 4.00 seconadary cache table  ;AN000;
        i_need  SC_CACHE_COUNT,WORD          ; DOS 4.00 secondary cache entries ;AN000;
        i_need  SC_SECTOR_SIZE,WORD          ; DOS 4.00 sector size             ;AN000;
        i_need  SC_DRIVE,BYTE                ; DOS 4.00 drive                   ;AN000;
        i_need  DOS34_FLAG,WORD              ; DOS 4.00 common flag             ;AN000;
        i_need  FIRST_BUFF_ADDR,WORD         ; DOS 4.00 beginning of the chain  ;AN000;

        i_need  BuffInHMA,byte
        i_need  LoMemBuff,dword

IFDEF __TONYI__2
        i_need  DDMOVE,byte     ; 386 flag
ENDIF

DOSCODE SEGMENT
        ASSUME  SS:DOSDATA,CS:DOSCODE

Break   <GETCURHEAD -- Get current buffer header>

;----------------------------------------------------------------------------
; Procedure Name : GetCurHead
; Inputs:
;        No Inputs
; Function:
;       Returns the pointer to the first buffer in Queue
;       and updates FIRST_BUFF_ADDR
;       and invalidates LASTBUFFER (recency pointer)
; Outputs:
;       DS:DI = pointer to the first buffer in Queue
;       FIRST_BUFF_ADDR = offset ( DI ) of First buffer in Queue
;       LASTBUFFER = -1
; No other registers altered
;----------------------------------------------------------------------------


procedure   GETCURHEAD,NEAR

        lds     di, BufferQueue         ; Pointer to the first buffer;smr;SS Override
        mov     word ptr [LASTBUFFER],-1; invalidate last buffer;smr;SS Override
        mov     [FIRST_BUFF_ADDR],di    ;save first buffer addr;smr;SS Override
        ret

EndProc GETCURHEAD

Break   <SCANPLACE, PLACEBUF -- PUT A BUFFER BACK IN THE POOL>

;----------------------------------------------------------------------------
; Procedure Name : ScanPlace
; Inputs:
;       Same as PLACEBUF
; Function:
;       Save scan location and call PLACEBUF
; Outputs:
;       DS:DI Points to saved scan location
; All registers, except DS:DI, preserved.
;----------------------------------------------------------------------------
;M039: Rewritten to preserve registers.

procedure   ScanPlace,near

        push    [di].buf_next           ;Save scan location
        call    PLACEBUF
        pop     di
        ret

EndProc ScanPlace

;----------------------------------------------------------------------------
; Procedure Name : PlaceBuf
; Input:
;       DS:DI points to buffer (DS->BUFFINFO array, DI=offset in array)
; Function:
;       Remove buffer from queue and re-insert it in proper place.
; NO registers altered
;----------------------------------------------------------------------------

procedure   PLACEBUF,NEAR
        push    AX                      ;Save only regs we modify       ;AN000;
        push    BX                                                      ;AN000;
IFNDEF __TONYI__1
;--------------------------------------------------------------------------
; No need to push/pop SI for the special cases.  Postpone until necessary.
;--------------------------------------------------------------------------
        push    SI                                                      ;AN000;
ENDIF
        mov     ax, [di].BUF_NEXT
        mov     bx, word ptr[BufferQueue]       ; bx = offset of head of list;smr;SS Override

        cmp     ax,bx                           ;Buf = last?            ;AN000;
        je      nret                            ;Yes, special case      ;AN000;
        cmp     di,bx                           ;Buf = first?           ;AN000;
        jne     not_first                       ;Yes, special case      ;AN000;
        mov     word ptr [BufferQueue],ax       ;smr;SS Override
        jmp     short nret                      ;Continue with repositioning;AN000;
not_first:
IFDEF __TONYI__1
;-------------------------------------------------
; Now we *need* to do it.
;-------------------------------------------------
        push    si
ENDIF
        mov     SI,[DI].BUF_PREV                ;No, SI = prior Buf     ;AN000;
        mov     [SI].BUF_NEXT,AX                ; ax has di->buf_next   ;AN000;
        xchg    si, ax
        mov     [SI].BUF_PREV,AX                ;                       ;AN000;

IFDEF __TONYI_486__
        mov     SI,[BX].BUF_PREV                ;SI-> last buffer       ;AN000;
        mov     [BX].BUF_PREV,DI ; Split SI load/ref for 486 pipe
        mov     [SI].BUF_NEXT,DI                ;Add Buf to end of list ;AN000;
ELSE
        mov     SI,[BX].BUF_PREV                ;SI-> last buffer       ;AN000;
        mov     [SI].BUF_NEXT,DI                ;Add Buf to end of list ;AN000;
        mov     [BX].BUF_PREV,DI                                        ;AN000;
ENDIF
        mov     [DI].BUF_PREV,SI                ;Update link in Buf too ;AN000;
        mov     [DI].BUF_NEXT,BX                                        ;AN000;
IFDEF __TONYI__1
;-------------------------------------------------
; Now we *need* to do it.
;-------------------------------------------------
        pop     si
ENDIF

nret:                                                                   ;AN000;
IFNDEF __TONYI__1
;--------------------------------------------------------------------------
; No need to push/pop SI for the special cases.  Postpone until necessary.
;--------------------------------------------------------------------------
        pop     SI                                                      ;AN000;
ENDIF
        pop     BX                                                      ;AN000;
        pop     AX                                                      ;AN000;
                                                                        ;AN000;
        cmp     [di.buf_ID],-1                  ; Buffer FREE?          ;AN000;
        jne     pbx                             ; M039: -no, jump.
        mov     WORD PTR [BufferQueue],di       ; M039: -yes, make it LRU.
pbx:    ret                                                             ;AN000;

EndProc PLACEBUF

Break   <POINTCOMP -- 20 BIT POINTER COMPARE>
;----------------------------------------------------------------------------
;
; Procedure Name : PointComp
; Inputs:
;         DS:SI & ES:DI
; Function:
;          Checks for ((SI==DI) && (ES==DS))
;          Assumes that pointers are normalized for the
;          same segment
;
; Compare DS:SI to ES:DI (or DS:DI to ES:SI) for equality
; DO NOT USE FOR < or >
; No Registers altered
;
;----------------------------------------------------------------------------

procedure   PointComp,NEAR

        CMP     SI,DI
        jnz     ret_label       ; return if nz
        PUSH    CX
        PUSH    DX
        MOV     CX,DS
        MOV     DX,ES
        CMP     CX,DX
        POP     DX
        POP     CX

ret_label:
        return

EndProc PointComp

        Break   <GETBUFFR, GETBUFFRB -- GET A SECTOR INTO A BUFFER>


;**     GetBuffr - Get a non-FAT Sector into a Buffer
;
;       GetBuffr does normal ( non-FAT ) sector buffering
;       It gets the specified local sector into one of the I/O buffers
;       and shuffles the queue
;
;       ENTRY   (AL) = 0 means sector must be pre-read
;                      ELSE no pre-read
;               (DX) = Desired physical sector number         (LOW)
;               HIGH_SECTOR = Desired physical sector number (HIGH)
;               (ES:BP) = Pointer to drive parameters
;               ALLOWED set in case of INT 24
;       EXIT    'C' set if error (user FAIL response to INT24)
;               'C' clear if OK
;               CURBUF Points to the Buffer for the sector
;                   the buffer type bits OF buf_flags = 0, caller must set it
;       USES    AX, BX, CX, SI, DI, Flags


;**     GetBuffrb - Get a FAT Sector into a Buffer
;
;       GetBuffr reads a sector from the FAT file system's FAT table.
;       It gets the specified sector into one of the I/O buffers
;       and shuffles the queue.  We need a special entry point so that
;       we can read the alternate FAT sector if the first read fails, also
;       so we can mark the buffer as a FAT sector.
;
;       ENTRY   (AL) = 0 means sector must be pre-read
;                      ELSE no pre-read
;               (DX) = Desired physical sector number         (LOW)
;               (SI) != 0
;               HIGH_SECTOR = Desired physical sector number (HIGH)
;               (ES:BP) = Pointer to drive parameters
;               ALLOWED set in case of INT 24
;       EXIT    'C' set if error (user FAIL response to INT24)
;               'C' clear if OK
;               CURBUF Points to the Buffer for the sector
;                   the buffer type bits OF buf_flags = 0, caller must set it
;       USES    AX, BX, CX, SI, DI, Flags


procedure   GETBUFFR,NEAR
        DOSAssume   <DS>,"GetBuffr"

        XOR     SI,SI

;       This entry point is called for FAT buffering with SI != 0

entry   GETBUFFRB

        Assert  ISDPB,<ES,BP>,"GetBuffr"
        MOV     PREREAD,AX                      ; save pre-read flag
        MOV     AL,ES:[BP].DPB_DRIVE
        LDS     DI,LASTBUFFER                   ; Get the recency pointer
ASSUME  DS:NOTHING

;hkn; SS override
        MOV     CX,HIGH_SECTOR                  ; F.C. >32mb            ;AN000;

;       See if this is the buffer that was most recently returned.
;       A big performance win if it is.

        CMP     DI,-1                           ; Recency pointer valid?
        je      getb5                           ; No
        CMP     DX,WORD PTR [DI].BUF_SECTOR
        JNZ     getb5                           ; Wrong sector
        CMP     CX,WORD PTR [DI].BUF_SECTOR+2   ; F.C. >32mb            ;AN000;
        JNZ     getb5                           ; F.C. >32mb            ;AN000;
        CMP     AL,[DI.buf_ID]
IFDEF __TONYI__
;-------------------------------------------
; A slightly faster path.....
;-------------------------------------------
        LJZ     JustUpdateCURBUF                ; Just asked for same buffer
ELSE
        LJZ     getb35                          ; Just asked for same buffer
ENDIF

;       It's not the buffer most recently returned.  See if it's in the
;       cache.
;
;       (cx:dx) = sector #
;       (al) = drive #
;       (si) = 0 iff non fat sector, != 0 if FAT sector read
;       ??? list may be incomplete ???
IFDEF __TONYI__
;------------------------------------------------
; Pulled inline for speed.....
;------------------------------------------------
getb5:  lds     di, BufferQueue         ; Pointer to the first buffer;smr;SS Override
        mov     word ptr [LASTBUFFER],-1; invalidate last buffer;smr;SS Override
        mov     [FIRST_BUFF_ADDR],di    ;save first buffer addr;smr;SS Override
ELSE
getb5:  CALL    GETCURHEAD                      ; get Q Head
ENDIF

getb10: CMP     DX,WORD PTR [DI].BUF_SECTOR
        jne     getb12                          ; wrong sector lo
        CMP     CX,WORD PTR [DI].BUF_SECTOR+2
        jne     getb12                          ; wrong sector hi
        CMP     AL,[DI.buf_ID]
        jne     @f
        jmp     getb25                          ; Found the requested sector
@@:
getb12: mov     DI,[DI].BUF_NEXT
        cmp     DI,FIRST_BUFF_ADDR              ; back at the front again?
        jne     getb10                          ; no, continue looking

;       The requested sector is not available in the buffers. DS:DI now points
;       to the first buffer in the Queue. Flush the first buffer & read in the
;       new sector into it.
;
;       (DS:DI) = first buffer in the queue
;       (CX:DX) = sector # we want
;       (si) = 0 iff non fat sector, != 0 if FAT sector read

;hkn; SS override
        PUSH    cx
        SAVE    <si, dx, bp, es>
        CALL    BUFWRITE                        ; Write out the dirty buffer
        RESTORE <es, bp, dx, si>
        POP     HIGH_SECTOR
IFDEF __TONYI__1
;-------------------------------------------------
; Save 3 bytes...faster too...
;-------------------------------------------------
        jc      CF_To_getbx
ELSE
        jnc     @f
        jmp     getbx                           ; if got hard error
@@:
ENDIF

IFDEF __TONYI__3
;------------------------------------------------------------------------
; Pull SET_RQ_SC_PARMS inline.
; CX is expendable here.....it gets reloaded just below, and at "getb20"
; Moving the XOR helps the 486's too.
;------------------------------------------------------------------------
        MOV     cx,es:[bp].DPB_SECTOR_SIZE      ; save sector size
        XOR     AH,AH                           ; initial flags
IFNDEF __TONYI_KILL_SC__
        MOV     SC_SECTOR_SIZE,cx
ENDIF
        MOV     cl,es:[bp].DPB_DRIVE            ; save drive #
IFNDEF __TONYI_KILL_SC__
        MOV     SC_DRIVE,cl
ENDIF

ELSE
        CALL    SET_RQ_SC_PARMS                 ; set parms for secondary cache
        XOR     AH,AH                           ; initial flags
ENDIF
;       We're ready to read in the buffer, if need be.  If the caller
;       wanted to just *write* the buffer then we'll skip reading it in.

;hkn; SS override
        CMP     BYTE PTR PREREAD,ah             ; am to Read in the new sector?
        JNZ     getb20                          ; no, we're done
        LEA     BX,[DI].BufInSiz                ; (ds:bx) = data address
        MOV     CX,1
        SAVE    <si, di, dx, es>
; Note:  As far as I can tell, all disk reads into buffers go through
;        this point.  -mrw 10/88
        cmp     BuffInHMA, 0            ; is buffers in HMA?
        jz      @f
        push    ds
        push    bx
        lds     bx, dword ptr LoMemBuff ; Then let's read it into scratch buff
@@:
;M039: Eliminated redundant HMA code.
        OR      SI,SI                   ; FAT sector ?
        JZ      getb15

        invoke  FATSECRD
        MOV     AH,buf_isFAT            ; Set buf_flags
        JMP     SHORT getb17            ; Buffer is marked free if read barfs

getb15:
        invoke  DREAD                   ; Buffer is marked free if read barfs
        MOV     AH,0                    ; Set buf_flags to no type, DO NOT XOR!
getb17:                                                                   ;I001

IFDEF VCB_MOVSD
; Enable DWORD moves

        mov     ch, 0
        mov     cl, BuffInHMA
        jcxz    not_in_hma
        mov     cx, es:[bp].DPB_SECTOR_SIZE
        mov     si, bx
        pop     di
        pop     es
        pushf
        shr     cx, 1
        cld
        IF1
                %out    DWORD sector moves on 386 are ENABLED!
        ENDIF
        CMP     [DDMOVE],0
        JZ      nodd1
        SHR     CX,1
        DB      66H
nodd1:
        rep     movsw
        mov     dx, es
        mov     ds, dx
        popf
not_in_hma:

ELSE    ; VCB_MOVSD

IFDEF __TONYI__1
;----------------------------------------------------------
; Only HMA buffers need to pay the pushf/popf price....
; This is 40% faster when buffers=LOW
;----------------------------------------------------------
        mov     ch, 0
        mov     cl, BuffInHMA
        jcxz    not_in_hma
        pushf
ELSE
        pushf                                                             ;I001
        cmp     BuffInHMA, 0            ; did we read into scratch buff ? ;I001
        jz      not_in_hma              ; no                              ;I001
ENDIF
        mov     cx, es:[bp].DPB_SECTOR_SIZE                               ;I001
        shr     cx, 1                                                     ;I001
        popf    ;;I001                  ; Preserve possible CY from SHR   ;I001
        mov     si, bx                                                    ;I001
        pop     di                                                        ;I001
        pop     es                                                        ;I001
        cld                                                               ;I001
IFNDEF __TONYI__1
;----------------------------------------------------------
; The following REP MOVS and PUSH/POP don't hit the flags...
; Not doing pushf/popf saves bytes and is faster...
;----------------------------------------------------------
        pushf   ;;I001                  ; Preserve possible CY            ;I001
ENDIF

        rep     movsw                   ; move the contents of scratch buf;I001
IFDEF __TONYI__1
;----------------------------------------------------------
; Only HMA buffers need to pay the pushf/popf price....
;----------------------------------------------------------
        mov     dx, es          ; Go for speed here.....
        mov     ds, dx
not_in_hma:
ELSE
        push    es                                                        ;I001
        pop     ds                                                        ;I001
not_in_hma:                                                               ;I001
        popf                                                              ;I001
ENDIF

ENDIF   ; VCB_MOVSD

getbc:                                                                    ;I001
        RESTORE <es, dx, di, si>
IFDEF __TONYI__1
;---------------------------------------
; Enable merge above......
;---------------------------------------
CF_To_getbx:
ENDIF
        JC      getbx

;       The buffer has the data setup in it (if we were to read)
;       Setup the various buffer fields
;
;       (ds:di) = buffer address
;       (es:bp) = DPB address
;       (HIGH_SECTOR:DX) = sector #
;       (ah) = BUF_FLAGS value
;       (si) = 0 iff non fat sector, != 0 if FAT sector read

;hkn; SS override
IFDEF __TONYI_486__
;--------------------------------------------------------------
; Reorg to minimize pipe stalls on 486's.
;--------------------------------------------------------------
getb20: MOV     CX,HIGH_SECTOR
        MOV     AL,ES:[BP].DPB_DRIVE
        MOV     WORD PTR [DI].BUF_SECTOR,DX
        MOV     WORD PTR [DI].buf_DPB,BP
        MOV     WORD PTR [DI].buf_DPB+2,ES
        FOLLOWS BUF_FLAGS, BUF_ID, 1
        MOV     WORD PTR [DI].BUF_SECTOR+2,CX
        MOV     WORD PTR [DI].BUF_ID,AX         ; Set ID and Flags
ELSE
getb20: MOV     CX,HIGH_SECTOR
        MOV     WORD PTR [DI].BUF_SECTOR+2,CX
        MOV     WORD PTR [DI].BUF_SECTOR,DX
        MOV     WORD PTR [DI].buf_DPB,BP
        MOV     WORD PTR [DI].buf_DPB+2,ES
        MOV     AL,ES:[BP].DPB_DRIVE
        FOLLOWS BUF_FLAGS, BUF_ID, 1
        MOV     WORD PTR [DI].BUF_ID,AX         ; Set ID and Flags
ENDIF

getb25: MOV     [DI].BUF_WRTCNT,1               ; Default to not a FAT sector   ;AC000;
        XOR     AX,AX
        OR      SI,SI                           ; FAT sector ?
        JZ      getb30
        MOV     AL,ES:[BP].DPB_FAT_COUNT        ; update number of copies of
        MOV     [DI.buf_WRTCNT],AL              ;  this sector present on disk
        MOV     AX,ES:[BP].DPB_FAT_SIZE         ; offset of identical FAT
                                                ;  sectors
;       BUGBUG - dos 6 can clean this up by not setting wrtcntinc unless wrtcnt
;               is set

getb30: MOV     [DI.buf_wrtcntinc],AX
        CALL    PLACEBUF

;hkn; SS override for next 4
IFDEF __TONYI__
;---------------------------------------------------
; A little shuffling here allows us to take a faster
; path when we get a hit on "LASTBUFFER"....  we can
; avoid 2 of the MOV's in this case because we never
; touched LASTBUFFER, and DS:DI are still loaded with
; it's value.
;---------------------------------------------------
getb35: MOV     WORD PTR LASTBUFFER+2,DS
        MOV     WORD PTR LASTBUFFER,DI
JustUpdateCURBUF:
        MOV     WORD PTR CURBUF+2,DS
        MOV     WORD PTR CURBUF,DI
ELSE
getb35: MOV     WORD PTR CURBUF+2,DS
        MOV     WORD PTR LASTBUFFER+2,DS
        MOV     WORD PTR CURBUF,DI
        MOV     WORD PTR LASTBUFFER,DI
ENDIF
        CLC

;       Return with 'C' set appropriately
;
;       (dx) = caller's original value
IFDEF __TONYI__
;-------------------------------------
; It's OK to whack AX on exit
;-------------------------------------
getbx:  FASTcontext  ds, ax
ELSE

getbx:  Context DS
ENDIF
        return

EndProc GETBUFFR

Break   <FLUSHBUF -- WRITE OUT DIRTY BUFFERS>

;----------------------------------------------------------------------------
; Input:
;       DS = DOSGROUP
;       AL = Physical unit number local buffers only
;          = -1 for all units and all remote buffers
; Function:
;       Write out all dirty buffers for unit, and flag them as clean
;       Carry set if error (user FAILed to I 24)
;           Flush operation completed.
; DS Preserved, all others destroyed (ES too)
;----------------------------------------------------------------------------

procedure   FlushBuf,NEAR
        DOSAssume   <DS>,"FlushBuf"

        call    GetCurHead
        ASSUME  DS:NOTHING
        TESTB   [DOS34_FLAG], FROM_DISK_RESET   ; from disk reset ? ;hkn;
        jnz     scan_buf_queue
        cmp     [DirtyBufferCount], 0                           ;hkn;
        je      end_scan
scan_buf_queue:
        call    CheckFlush
        mov     ah, [di].buf_ID
        cmp     byte ptr [wperr], ah                            ;hkn;
        je      free_the_buf
        TESTB   [DOS34_FLAG], FROM_DISK_RESET   ; from disk reset ? ;hkn;
        jz      dont_free_the_buf
free_the_buf:
        mov     word ptr [di].buf_ID, 00ffh
dont_free_the_buf:
        mov     di, [di].buf_next
        cmp     di, [FIRST_BUFF_ADDR]                           ;hkn;
        jne     scan_buf_queue
end_scan:
        Context DS
        cmp     [FAILERR], 0
        jne     bad_flush
        ret
bad_flush:
        stc
        ret

EndProc FlushBuf

;----------------------------------------------------------------------------
;
; Procedure Name : CHECKFLUSH
;
; Inputs : AL - Drive number, -1 means do not check for drive
;          DS:DI - pointer to buffer
;
; Function : Write out a buffer if it is dirty
;
; Carry set if problem (currently user FAILed to I 24)
;
;----------------------------------------------------------------------------

        procedure CHECKFLUSH,NEAR

        Assert  ISBUF,<DS,DI>,"CheckFlush"
        mov     ah, -1
        CMP     [DI.buf_ID],AH
        retz                            ; Skip free buffer, carry clear
        CMP     AH,AL                   ;
        JZ      DOBUFFER                ; do this buffer
        CMP     AL,[DI.buf_ID]
        CLC
        retnz                           ; Buffer not for this unit or SFT
DOBUFFER:
        TESTB   [DI.buf_flags],buf_dirty
        retz                            ; Buffer not dirty, carry clear by TEST
        PUSH    AX
        PUSH    WORD PTR [DI.buf_ID]
        CALL    BUFWRITE
        POP     AX
        JC      LEAVE_BUF               ; Leave buffer marked free (lost).
        AND     AH,NOT buf_dirty        ; Buffer is clean, clears carry
        MOV     WORD PTR [DI.buf_ID],AX
LEAVE_BUF:
        POP     AX                      ; Search info
        return
EndProc CHECKFLUSH

        Break   <BUFWRITE -- WRITE OUT A BUFFER IF DIRTY>


;**     BufWrite - Write out Dirty Buffer
;
;       BufWrite writes a buffer to the disk, iff it's dirty.
;
;       ENTRY   DS:DI Points to the buffer
;
;       EXIT    Buffer marked free
;               Carry set if error (currently user FAILed to I 24)
;
;       USES    All buf DS:DI
;               HIGH_SECTOR


procedure   BufWrite,NEAR

        Assert  ISBUF,<DS,DI>,"BufWrite"
        MOV     AX,00FFH
        XCHG    AX,WORD PTR [DI.buf_ID] ; Free, in case write barfs
        CMP     AL,0FFH
        retz                            ; Buffer is free, carry clear.
        test    AH,buf_dirty
        retz                            ; Buffer is clean, carry clear.
        invoke  DEC_DIRTY_COUNT         ; LB. decrement dirty count

;hkn; SS override
        CMP     AL,BYTE PTR [WPERR]
        retz                            ; If in WP error zap buffer

;hkn; SS override
IFNDEF __TONYI_KILL_SC__
        MOV     [SC_DRIVE],AL           ;LB. set it for invalidation    ;AN000;
ENDIF

IFDEF __TONYI__4
;------------------------------------------------------------
; Reorg for space/speed savings, and 486 pipline optimization
;------------------------------------------------------------
        les     dx, dword ptr [di].BUF_SECTOR    ;F.C. >32mb             ;AN000;
        assume  es:nothing
        lea     bx,[di.BufInSiz]        ; Point at buffer
        mov     cl,[di.buf_wrtcnt]              ;>32mb                  ;AC000;
        mov     ch,0
        mov     [HIGH_SECTOR],es                ;F.C. >32mb             ;AN000;
        les     bp,[di.buf_DPB]
ELSE
        LES     BP,[DI.buf_DPB]
        LEA     BX,[DI.BufInSiz]        ; Point at buffer
        MOV     DX,WORD PTR [DI].BUF_SECTOR     ;F.C. >32mb             ;AN000;
        MOV     CX,WORD PTR [DI].BUF_SECTOR+2   ;F.C. >32mb             ;AN000;

;hkn; SS override
        MOV     [HIGH_SECTOR],CX                ;F.C. >32mb             ;AN000;
        MOV     CL,[DI.buf_wrtcnt]              ;>32mb                          ;AC000;
        XOR     CH,CH
ENDIF

;hkn; SS override for ALLOWED
        MOV     [ALLOWED],allowed_RETRY + allowed_FAIL
        test    AH, buf_isDATA

        JZ      NO_IGNORE
        OR      [ALLOWED],allowed_IGNORE
NO_IGNORE:

        MOV     AX,[DI.buf_wrtcntinc]           ;>32mb                          ;AC000;

        PUSH    DI              ; Save buffer pointer
        XOR     DI,DI           ; Indicate failure

        push    ds
        push    bx
WRTAGAIN:
        SAVE    <DI,CX,AX>
        MOV     CX,1
        SAVE    <BX,DX,DS>
; Note:  As far as I can tell, all disk reads into buffers go through this point.  -mrw 10/88

        cmp     BuffInHMA, 0
        jz      @f
        push    cx
        push    es
        mov     si, bx
        mov     cx, es:[bp].DPB_SECTOR_SIZE
IFDEF __TONYI_486__
        les     di, dword ptr LoMemBuff
        shr     cx, 1
ELSE
        shr     cx, 1
        les     di, dword ptr LoMemBuff
ENDIF
        mov     bx, di
        cld

IFDEF VCB_MOVSD
; Enable DWORD moves

        IF1
                %out    DWORD sector moves on 386 are ENABLED!
        ENDIF
        CMP     [DDMOVE],0
        JZ      nodd2
        SHR     CX,1
        DB      66H
nodd2:

ENDIF   ; VCB_MOVSD

        rep     movsw
        push    es
        pop     ds
        pop     es
        pop     cx
@@:
        invoke  DWRITE          ; Write out the dirty buffer
        RESTORE <DS,DX,BX>
        RESTORE <AX,CX,DI>
        JC      NOSET
        INC     DI              ; If at least ONE write succeedes, the operation
NOSET:                          ;       succeedes.
        ADD     DX,AX
        LOOP    WRTAGAIN
        pop     bx
        pop     ds
IFDEF __TONYI__4
;------------------------------------------------------------
; Streamline and save 2 bytes.
;   If (DI != 0), SUB clears CF.
;   If (DI == 0), SUB sets CF.
;------------------------------------------------------------
        sub     di, 0001        ; No DEC!!! (set CF if DI=0, else clear CF)
        pop     di              ;
ELSE
        OR      DI,DI           ; Clears carry
        JNZ     BWROK           ; At least one write worked
        STC                     ; DI never got INCed, all writes failed.
BWROK:  POP     DI
ENDIF
        return

EndProc BufWrite

Break   <SET_RQ_SC_PARMS-set requesting drive for SC>

;**     Set_RQ_SC_Parms - Set Secondary Cache Parameters
;
;       Set_RQ_SC_Parms sets the sector size and drive number value
;       for the secondary cache. This updates SC_SECTOR_SIZE &
;       SC_DRIVE even if SC is disabled to save the testing
;       code and time
;
;       ENTRY   ES:BP = drive parameter block
;
;       EXIT    [SC_SECTOR_SIZE]= drive sector size
;               [SC_DRIVE]= drive #
;
;       USES    Flags

procedure   SET_RQ_SC_PARMS,NEAR

;hkn; SS override for all variables used in this procedure.

        SAVE    <ax>

        MOV     ax,ES:[BP].DPB_SECTOR_SIZE      ; save sector size
IFNDEF __TONYI_KILL_SC__
        MOV     SC_SECTOR_SIZE,ax
ENDIF

        MOV     al,ES:[BP].DPB_DRIVE            ; save drive #
IFNDEF __TONYI_KILL_SC__
        MOV     SC_DRIVE,al
ENDIF

        RESTORE <ax>

srspx:  return

EndProc SET_RQ_SC_PARMS                         ;LB. return

Break   <INC_DIRTY_COUNT-increment dirty count>

;----------------------------------------------------------------------------
; Input:
;       none
; Function:
;       increment dirty buffers count
; Output:
;       dirty buffers count is incremented
;
; All registers preserved
;----------------------------------------------------------------------------
procedure   INC_DIRTY_COUNT,NEAR

        inc     [DirtyBufferCount]                      ;hkn;
        ret

EndProc INC_DIRTY_COUNT

Break   <DEC_DIRTY_COUNT-decrement dirty count>

;----------------------------------------------------------------------------
; Input:
;       none
; Function:
;       decrement dirty buffers count
; Output:
;       dirty buffers count is decremented
;
; All registers preserved
;----------------------------------------------------------------------------

procedure   DEC_DIRTY_COUNT,NEAR
        cmp     [DirtyBufferCount], 0                   ;hkn;
        jz      ddcx
        dec     [DirtyBufferCount]                      ;hkn;
ddcx:   ret

EndProc DEC_DIRTY_COUNT


DOSCODE ENDS
        END
