        TITLE   DISK2 - Disk utility routines
        NAME    Disk2

IF 1
__TONYI__ = 1           ; Speed/Space hacks w/386 buffers
__TONYI__1= 1           ; Speed/Space hacks 10/30/92
__TONYI__2= 1           ; Speed/Space hacks 11/06/92
__TONYI__3= 1           ; Space/speed saving hacks 12/03/92
__TONYI_486__= 1        ; 486 pipeline optimizations (8088 benign)
VCB_MOVSD = 1           ; 386 dword buffer transfers
ENDIF

;
;       IBM Corporation
;       Copyright (C) IBM Corporation 1992
;       All Rights Reserved.
;
;**     Low level Read and write routines for local SFT I/O on files and devs
;
;       DskRead
;       DWRITE
;       DSKWRITE
;       HarderrRW
;       SETUP
;       BREAKDOWN
;       READ_LOCK_VIOLATION
;       WRITE_LOCK_VIOLATION
;       DISKREAD
;       SET_ACC_ERR_DS
;       SET_ACC_ERR
;       SETSFT
;       SETCLUS
;       AddRec
;
;       Revision history:
;
;               AN000 version 4.00 Jan. 1988
;               M039 DB 10/17/90 - Disk read/write optimization

        extrn   GetCurHead:near
        extrn   ScanPlace:near

        .xlist
        .xcref
        include version.inc
        include dosseg.inc
        INCLUDE DOSSYM.INC
        INCLUDE DEVSYM.INC
        include sf.inc
        include dpb.inc
        include filemode.inc
        include dosmac.inc              ;M039
        .cref
        .list

Installed = TRUE

        i_need  THISSFT,DWORD
        i_need  DMAADD,DWORD
        i_need  NEXTADD,WORD
        i_need  ThisDrv,BYTE
        i_need  SecClusPos,BYTE
        i_need  ClusNum,WORD
        i_need  ReadOp,BYTE
        i_need  Trans,BYTE
        i_need  BytPos,4
        i_need  SecPos,DWORD             ; DOS 4.00 >32mb                       ;AN000;
        i_need  BytSecPos,WORD
        i_need  BytCnt1,WORD
        i_need  BytCnt2,WORD
        i_need  SecCnt,WORD
        i_need  ThisDPB,DWORD
        i_need  LastPos,WORD
        i_need  EXTERRPT,DWORD
        i_need  CALLVIDRW,DWORD
        i_need  ALLOWED,BYTE
        i_need  DEVCALL,BYTE
        i_need  CALLSCNT,WORD

;**RMFHFE**     i_need  DISK_FULL,BYTE            ; disk full flag for ran blk wrt

        i_need  HIGH_SECTOR,WORD          ; F.C. >32mb    ;AN000;
        i_need  TEMP_VAR2,WORD            ; LB.           ;AN000;
        i_need  TEMP_VAR,WORD             ; LB.           ;AN000;
        i_need  DirtyBufferCount,WORD
        i_need  FIRST_BUFF_ADDR,WORD         ; first buffer address             ;AN000;

IFDEF __TONYI__
        i_need  DDMOVE,BYTE     ; 386 flag
ENDIF


DOSCODE Segment
        ASSUME  SS:DOSDATA, CS:DOSCODE


Break   <DSKREAD -- PHYSICAL DISK READ>
;---------------------------------------------------------------------------
;
; Procedure Name : DSKREAD
;
; Inputs:
;       DS:BX = Transfer addr
;       CX = Number of sectors
;       [HIGH_SECTOR] = Absolute record number (HIGH)
;       DX = Absolute record number            (LOW)
;       ES:BP = Base of drive parameters
; Function:
;       Call BIOS to perform disk read
; Outputs:
;       DI = CX on entry
;       CX = Number of sectors unsuccessfully transfered
;       AX = Status word as returned by BIOS (error code in AL if error)
;       Zero set if OK (from BIOS) (carry clear)
;       Zero clear if error (carry clear)
; SI Destroyed, others preserved
;---------------------------------------------------------------------------

procedure   DskRead,NEAR

        Assert  ISDPB,<ES,BP>,"DskRead"
        PUSH    CX
        MOV     AH,ES:[BP.dpb_media]
        MOV     AL,ES:[BP.dpb_UNIT]
        PUSH    BX
        PUSH    ES
        invoke  SETREAD
        JMP     short DODSKOP

Break   <DWRITE -- SEE ABOUT WRITING>
;--------------------------------------------------------------------------
;
; Procedure Name : DWRITE
;
; Inputs:
;       DS:BX = Transfer address
;       CX = Number of sectors
;       [HIGH_SECTOR] = Absolute record number (HIGH)
;       DX = Absolute record number            (LOW)
;       ES:BP = Base of drive parameters
;       [ALLOWED] must be set in case HARDERR called
; Function:
;       Calls BIOS to perform disk write. If BIOS reports
;       errors, will call HARDERRRW for further action.
; Output:
;       Carry set if error (currently, user FAILed to I 24)
; BP preserved. All other registers destroyed.
;----------------------------------------------------------------------------


        entry   DWRITE
ASSUME  DS:NOTHING,ES:NOTHING

        Assert  ISDPB,<ES,BP>,"DWrite"
        CALL    DSKWRITE
IFDEF __TONYI__1
;---------------------------------------------------------------------
; Lets spend a byte to get a non-error case fall through.......
;---------------------------------------------------------------------
        jnz     Oops_DWRITE_error       ; Error jumps
        ret                             ; No error

Oops_DWRITE_error:
ELSE
        jz      ret_label       ; Carry clear (retz)
ENDIF

;hkn; SS override
        MOV     BYTE PTR [READOP],1
        invoke  HARDERRRW
        CMP     AL,1            ; Check for retry
        JZ      DWRITE
        CMP     AL,3            ; Check for FAIL
        CLC
        JNZ     NO_CAR2         ; Ignore
        STC
NO_CAR2:

ret_label:
        return

Break   <DSKWRITE -- PHYSICAL DISK WRITE>
;---------------------------------------------------------------------------
;
; Procedure Name : DSKWRITE
;
; Inputs:
;       DS:BX = Transfer addr
;       CX = Number of sectors
;       DX = Absolute record number            (LOW)
;       [HIGH_SECTOR] = Absolute record number (HIGH)
;       ES:BP = Base of drive parameters
; Function:
;       Call BIOS to perform disk read
; Outputs:
;       DI = CX on entry
;       CX = Number of sectors unsuccessfully transfered
;       AX = Status word as returned by BIOS (error code in AL if error)
;       Zero set if OK (from BIOS) (carry clear)
;       Zero clear if error (carry clear)
; SI Destroyed, others preserved
;
;----------------------------------------------------------------------------

        entry   DSKWRITE
ASSUME  DS:NOTHING,ES:NOTHING

        Assert  ISDPB,<ES,BP>,"DskWrite"
        PUSH    CX
        MOV     AH,ES:[BP.dpb_media]
        MOV     AL,ES:[BP.dpb_UNIT]
        PUSH    BX
        PUSH    ES
        invoke  SETWRITE
DODSKOP:
        MOV     CX,DS           ; Save DS
        POP     DS              ; DS:BP points to DPB
        PUSH    DS
        LDS     SI,DS:[BP.dpb_driver_addr]
        invoke  DEVIOCALL2
        MOV     DS,CX           ; Restore DS
        POP     ES              ; Restore ES
        POP     BX

;hkn; SS override
IFDEF __TONYI_486__
        POP     DI
        MOV     CX,[CALLSCNT]   ; Number of sectors transferred
        MOV     AX,[DEVCALL.REQSTAT]
        SUB     CX,DI
        NEG     CX              ; Number of sectors not transferred
;hkn; SS override
ELSE
        MOV     CX,[CALLSCNT]   ; Number of sectors transferred
        POP     DI
        SUB     CX,DI
        NEG     CX              ; Number of sectors not transferred

;hkn; SS override
        MOV     AX,[DEVCALL.REQSTAT]
ENDIF
        test    AX,STERR
        return
EndProc DskRead



Break   <HardErrRW - map extended errors and call harderr>
;---------------------------------------------------------------------------
;
; Procedure Name : HardErrRW
;
; Inputs:
;       AX is error code from read or write
;       Other registers set as per HARDERR
; Function:
;       Checks the error code for special extended
;       errors and maps them if needed. Then invokes
;       Harderr
; Outputs:
;       Of HARDERR
; AX may be modified prior to call to HARDERR.
; No other registers altered.
;
;---------------------------------------------------------------------------

procedure   HARDERRRW,near

        CMP     AL,error_I24_wrong_disk
        JNZ     DO_ERR                          ; Nothing to do

        push    ax
IFDEF __TONYI__1
;-----------------------------------------------------------------------
; Speed is no factor in hard errors.  Save a couple of bytes.
;-----------------------------------------------------------------------
        push    es
        les     ax, dword ptr callvidrw
        mov     word ptr exterrpt, ax           ; set ext err ptr lo
        mov     word ptr exterrpt+2, es         ; set ext err ptr hi
        pop     es
ELSE
        mov     ax, word ptr callvidrw          ; get ptr lo    ;smr;SS Override
        mov     word ptr exterrpt, ax           ; set ext err ptr lo
        mov     ax, word ptr callvidrw+2        ; get ptr hi from dev
        mov     word ptr exterrpt+2, ax         ; set ext err ptr hi
ENDIF
        pop     ax

DO_ERR:
        invoke  HARDERR
        return

EndProc HARDERRRW

Break   <SETUP -- SETUP A DISK READ OR WRITE FROM USER>
;----------------------------------------------------------------------------
;
; Procedure Name : SETUP
;
; Inputs:
;       ES:DI point to SFT (value also in THISSFT)
;       DMAAdd contains transfer address
;       CX = Byte count
;       DS = DOSDATA
;   WARNING Stack must be clean, two ret addrs on stack, 1st of caller,
;               2nd of caller of caller.
; Outputs:
;           CX = byte count
;           [THISDPB] = Base of drive parameters if file
;                     = Pointer to device header if device or NET
;           ES:DI Points to SFT
;           [NEXTADD] = Displacement of disk transfer within segment
;           [TRANS] = 0 (No transfers yet)
;           BytPos = Byte position in file
;
;       The following fields are relevant to local files (not devices) only:
;
;           SecPos = Position of first sector (local files only)
;           [BYTSECPOS] = Byte position in first sector (local files only)
;           [CLUSNUM] = First cluster (local files only)
;           [SECCLUSPOS] = Sector within first cluster (local files only)
;           [THISDRV] = Physical unit number (local files only)
;
;      RETURNS ONE LEVEL UP WITH:
;          CX = 0
;          CARRY = Clear
;       IF AN ERROR IS DETECTED
; All other registers destroyed
;----------------------------------------------------------------------------

;hkn; called from disk.asm. DS has been set up to DOSDATA.

procedure   SETUP,NEAR
        DOSAssume   <DS>,"SetUp"

        Assert      ISSFT,<ES,DI>,"SetUp"
        LDS     SI,ES:[DI.sf_devptr]
ASSUME  DS:NOTHING

;hkn; SS override
        MOV     WORD PTR [THISDPB+2],DS

;hkn; SS is DOSDATA
IFDEF __TONYI__1
;-------------------------------------------------------------------
; Lets spend 2 bytes for some speed. BX is expendable here.
;-------------------------------------------------------------------
        FASTcontext ds,bx
ELSE
        context DS
ENDIF

IFDEF __TONYI_486__
        MOV     BX,WORD PTR DMAAdd
        MOV     WORD PTR [THISDPB],SI
        MOV     [NEXTADD],BX            ;Set NEXTADD to start of Xaddr
        MOV     BYTE PTR [TRANS],0      ;No transferes
        MOV     AX,WORD PTR ES:[DI.sf_Position]
        MOV     DX,WORD PTR ES:[DI.sf_Position+2]
        MOV     WORD PTR BytPos,AX
        MOV     WORD PTR [BYTPOS+2],DX  ;Set it
ELSE
        MOV     WORD PTR [THISDPB],SI
        MOV     BX,WORD PTR DMAAdd
        MOV     [NEXTADD],BX            ;Set NEXTADD to start of Xaddr
        MOV     BYTE PTR [TRANS],0      ;No transferes
        MOV     AX,WORD PTR ES:[DI.sf_Position]
        MOV     DX,WORD PTR ES:[DI.sf_Position+2]
        MOV     WORD PTR [BYTPOS+2],DX  ;Set it
        MOV     WORD PTR BytPos,AX
ENDIF
        TESTB   ES:[DI].SF_FLAGS,<sf_isnet + devid_device>
        JNZ     NOSETSTUFF              ;Following not done on devs or NET
        PUSH    ES
        LES     BP,[THISDPB]            ;Point at the DPB
        Assert  ISDPB,<ES,BP>,"Setup"
        MOV     BL,ES:[BP.dpb_drive]
IFDEF __TONYI_486__
        PUSH    CX                            ;SHR32 and DIV32 use CX.
        MOV     [THISDRV],BL            ;Set THISDRV
        MOV     BX,ES:[BP.dpb_sector_size]
;M039: Optimized this section.
ELSE
        MOV     [THISDRV],BL            ;Set THISDRV
        MOV     BX,ES:[BP.dpb_sector_size]

;M039: Optimized this section.
        PUSH    CX                            ;SHR32 and DIV32 use CX.
ENDIF
        invoke  DIV32                         ;DX:AX/BX = CX:AX + DX (rem)
        MOV     [BYTSECPOS],DX
        MOV     WORD PTR SecPos,AX
        MOV     WORD PTR SecPos+2,CX
IFDEF __TONYI_486__
        MOV     BX,AX
        MOV     DX,CX
ELSE
        MOV     DX,CX

        MOV     BX,AX
ENDIF
        AND     BL,ES:[BP.dpb_cluster_mask]
        MOV     [SECCLUSPOS],BL

        invoke  SHR32                        ;(DX:AX SHR dpb_cluster_shift)
        POP     CX                           ;CX = byte count.
        JNZ     EOFERR                       ;cluster number above 64k
        CMP     AX,ES:[BP.dpb_max_cluster]   ;>32mb    if > disk size ;AN000;   ;AN000;
        JA      EOFERR                       ;>32mb    then EOF     ;AN000;     ;AN000;

        MOV     [CLUSNUM],AX
        POP     ES                           ; ES:DI point to SFT
;M039

NOSETSTUFF:
        MOV     AX,CX           ; AX = Byte count.
        ADD     AX,WORD PTR DMAAdd       ; See if it will fit in one segment
IFDEF __TONYI__1
;-------------------------------------------------------------------
; Lets spend a byte for some speed.
;-------------------------------------------------------------------
        jc      Oops_too_big
        ret
Oops_too_big:
ELSE
        JNC     OK              ; Must be less than 64K
ENDIF
        MOV     AX,WORD PTR DMAAdd
        NEG     AX              ; Amount of room left in segment (know
                                ;    less than 64K since max value of CX
                                ;    is FFFF).
        JNZ     NoDec
        DEC     AX
NoDec:
        MOV     CX,AX           ; Can do this much
        JCXZ    NOROOM          ; Silly user gave Xaddr of FFFF in segment
OK:
        return

EOFERR:
        POP     ES              ; ES:DI point to SFT
        XOR     CX,CX           ; No bytes read
NOROOM:
        POP     BX              ; Kill return address
        CLC
        return                  ; RETURN TO CALLER OF CALLER
EndProc SETUP

Break   <BREAKDOWN -- CUT A USER READ OR WRITE INTO PIECES>
;---------------------------------------------------------------------------
;
; Procedure Name : BREAKDOWN
;
; Inputs:
;       CX = Length of disk transfer in bytes
;       ES:BP = Base of drive parameters
;       [BYTSECPOS] = Byte position witin first sector
;       DS = DOSDATA
; Outputs:
;       [BYTCNT1] = Bytes to transfer in first sector
;       [SECCNT] = No. of whole sectors to transfer
;       [BYTCNT2] = Bytes to transfer in last sector
; AX, BX, DX destroyed. No other registers affected.
;---------------------------------------------------------------------------

procedure   BREAKDOWN,near
        DOSAssume   <DS>,"BreakDown"

        Assert      ISDPB,<ES,BP>,"BreakDown"
        MOV     AX,[BYTSECPOS]
        MOV     BX,CX
        OR      AX,AX
        JZ      SAVFIR          ; Partial first sector?
        SUB     AX,ES:[BP.dpb_sector_size]
        NEG     AX              ; Max number of bytes left in first sector
        SUB     BX,AX           ; Subtract from total length
        JAE     SAVFIR
        ADD     AX,BX           ; Don't use all of the rest of the sector
        XOR     BX,BX           ; And no bytes are left
SAVFIR:
        MOV     [BYTCNT1],AX
        MOV     AX,BX
        XOR     DX,DX
        DIV     ES:[BP.dpb_sector_size]  ; How many whole sectors?
        MOV     [SECCNT],AX
        MOV     [BYTCNT2],DX    ; Bytes remaining for last sector
        return
EndProc BreakDown

;----------------------------------------------------------------------------
;
; Procedure Name : READ_LOCK_VIOLATION
;
; ES:DI points to SFT. This entry used by NET_READ
; Carry set if to return error (CX=0,AX=error_sharing_violation).
; Else do retrys.
; ES:DI,DS,CX preserved
;
;----------------------------------------------------------------------------

procedure READ_LOCK_VIOLATION,NEAR
        DOSAssume   <DS>,"Read_Lock_Viol"

        Assert      ISSFT,<ES,DI>,"ReadLockViolation"

        MOV     [READOP],0
ERR_ON_CHECK:
        TESTB   ES:[DI.sf_mode],sf_isfcb
        JNZ     HARD_ERR
        PUSH    CX
        MOV     CL,BYTE PTR ES:[DI.sf_mode]
        AND     CL,sharing_mask
        CMP     CL,sharing_compat
        POP     CX
        JNE     NO_HARD_ERR
HARD_ERR:
        invoke  LOCK_VIOLATION
        retnc                           ; User wants Retrys
NO_HARD_ERR:
        XOR     CX,CX                   ;No bytes transferred
        MOV     AX,error_lock_violation
        STC
IFDEF __TONYI__1
;----------------------------------
; Handy for later streamlining
;----------------------------------
MergeNearReturn:
ENDIF
        return

EndProc READ_LOCK_VIOLATION

;----------------------------------------------------------------------------
;
; Procedure Name : WRITE_LOCK_VIOLATION
;
; Same as READ_LOCK_VIOLATION except for READOP.
; This entry used by NET_WRITE
;
;----------------------------------------------------------------------------

procedure WRITE_LOCK_VIOLATION,NEAR
        DOSAssume   <DS>,"Write_Lock_Viol"
        Assert      ISSFT,<ES,DI>,"WriteLockViolation"

        MOV     [READOP],1
        JMP     SHORT ERR_ON_CHECK                                    ;AC000;

EndProc WRITE_LOCK_VIOLATION


Break   <DISKREAD -- PERFORM USER DISK READ>
;----------------------------------------------------------------------------
;
; Procedure Name : DISKREAD
;
; Inputs:
;       Outputs of SETUP
; Function:
;       Perform disk read
; Outputs:
;    Carry clear
;       CX = No. of bytes read
;       ES:DI point to SFT
;       SFT offset and cluster pointers updated
;    Carry set
;       CX = 0
;       ES:DI point to SFT
;       AX has error code
;----------------------------------------------------------------------------

;hkn; called from disk.asm. DS already set up.

procedure   DISKREAD,NEAR
        DOSAssume   <DS>,"DiskRead"

        Assert  ISSFT,<ES,DI>,"DISKREAD"
        MOV     AX,WORD PTR ES:[DI.sf_size]
        MOV     BX,WORD PTR ES:[DI.sf_size+2]
        SUB     AX,WORD PTR BytPos
        SBB     BX,WORD PTR [BYTPOS+2]
        JB      RDERR                   ;Read starts past EOF
        JNZ     ENUF                    ;More than 64k to EOF
        OR      AX,AX
        JZ      RDERR                   ;Read starts at EOF
        CMP     AX,CX
        JAE     ENUF                    ;I/O fits
        MOV     CX,AX                   ;Limit read to up til EOF
ENUF:
        invoke  CHECK_READ_LOCK         ;IFS. check read lock                    ;AN000;
IFDEF __TONYI__1
;--------------------------------------------------
; Streamline non-error case and save a byte.
;--------------------------------------------------
        jc      MergeNearReturn
ELSE
        JNC     Read_Ok                 ; There are no locks
        return

READ_OK:
ENDIF
        LES     BP,[THISDPB]
        Assert  ISDPB,<ES,BP>,"DISKREAD/ReadOK"


        CALL    BREAKDOWN
        MOV     CX,[CLUSNUM]
        invoke  FNDCLUS
                                        ;M022 conditional removed here
        JC      SET_ACC_ERR_DS          ; fix to take care of I24 fail
                                        ; migrated from 330a - HKN
IFDEF __TONYI__1
;-----------------------------------------------------
; Save 2 bytes......
;-----------------------------------------------------
        jcxz    SKIPERR
ELSE
        OR      CX,CX
        JZ      SKIPERR
ENDIF

RDERR:
        MOV     AH,0EH                  ;MS. read/data/fail ;AN000;
        transfer WRTERR22

SETSFTJ2: JMP   SETSFT

CANOT_READ:
        POP     CX              ;Clean stack.
        POP     BX

        entry   SET_ACC_ERR_DS
ASSUME  DS:NOTHING,ES:NOTHING

;hkn; SS is DOSDATA
        Context DS

entry   SET_ACC_ERR
        DOSAssume   <DS>,"SET_ACC_ERR"

        XOR     CX,CX
        MOV     AX,error_access_denied
        STC
        return

SKIPERR:
        MOV     [LASTPOS],DX
        MOV     [CLUSNUM],BX
        CMP     [BYTCNT1],0
        JZ      RDMID
        invoke  BUFRD
        JC      SET_ACC_ERR_DS
RDMID:
        CMP     [SECCNT],0
        JZ      RDLAST
        invoke  NEXTSEC
        JC      SETSFTJ2
        MOV     BYTE PTR [TRANS],1      ; A transfer is taking place
ONSEC:
        MOV     DL,[SECCLUSPOS]
        MOV     CX,[SECCNT]
        MOV     BX,[CLUSNUM]
RDLP:
        invoke  OPTIMIZE
        JC      SET_ACC_ERR_DS
        PUSH    DI                      ;DI = Next physical cluster.
        PUSH    AX                      ;AX = # of sectors remaining.
        PUSH    BX                      ;[DMAADD+2]:BX = Transfer address.
        MOV     [ALLOWED],allowed_RETRY + allowed_FAIL + allowed_IGNORE
        MOV     DS,WORD PTR DmaAdd+2
ASSUME  DS:NOTHING
        PUSH    DX                      ;[HIGH_SECTOR]:DX = phys. sector #.
        PUSH    CX                      ;CX = # of contiguous sectors to read.
        invoke  SET_RQ_SC_PARMS          ;LB. do this for SC                   ;AN000;

        invoke  DREAD

;M039
        pop     cx
        pop     dx
        pop     WORD PTR [TEMP_VAR]
        jc      CANOT_READ

        mov     WORD PTR [TEMP_VAR2],ds

;       CX = # of contiguous sectors read.  (These constitute a block of
;            sectors, also termed an "Extent".)
;       [HIGH_SECTOR]:DX = physical sector # of first sector in extent.
;       [TEMP_VAR2]:[TEMP_VAR] = Transfer address (destination data address).
;       ES:BP -> Drive Parameter Block (DPB).
;
;       The Buffer Queue must now be scanned: the contents of any dirty
;       buffers must be "read" into the transfer memory block, so that the
;       transfer memory reflects the most recent data.

        call    DskRdBufScan

IFDEF __TONYI__1
;------------------------------------------------------------
; Let's spend 2 bytes here for some speed. CX is free.
;------------------------------------------------------------
        FASTcontext ds,cx
ELSE
        Context DS
ENDIF
        pop     cx
        pop     bx

;       CX = # of sector remaining.
;       BX = Next physical cluster.
;M039

        JCXZ    RDLAST
        invoke  IsEOF                   ; test for eof on fat size
        JAE     SETSFT
        MOV     DL,0
        INC     [LASTPOS]               ; We'll be using next cluster
        JMP     SHORT RDLP                                            ;AC000;
IFDEF __TONYI__1
;--------------------------------------------------------------
; Streamline by moving this JMP out of line.
;--------------------------------------------------------------
NearTo_SET_ACC_ERR_DS:
        JMP     SET_ACC_ERR_DS
ENDIF

RDLAST:
        MOV     AX,[BYTCNT2]
        OR      AX,AX
        JZ      SETSFT
        MOV     [BYTCNT1],AX
        invoke  NEXTSEC
        JC      SETSFT
        MOV     [BYTSECPOS],0
        invoke  BUFRD
IFDEF __TONYI__1
;---------------------------------------------------------------------
; Optimize non-error case to be a fall through.
;---------------------------------------------------------------------
        jc      NearTo_SET_ACC_ERR_DS
ELSE
        JNC     SETSFT
        JMP     SET_ACC_ERR_DS
ENDIF

;------------------------------------------------------------------------------
;
; Procedure Name : SETSFT
; Inputs:
;       [NEXTADD],[CLUSNUM],[LASTPOS] set to determine transfer size
;               and set cluster fields
; Function:
;       Update [THISSFT] based on the transfer
; Outputs:
;       sf_position, sf_lstclus, and sf_cluspos updated
;       ES:DI points to [THISSFT]
;       CX No. of bytes transferred
;       Carry clear
;
;----------------------------------------------------------------------------

entry   SETSFT
        DOSAssume   <DS>,"SetSFT"
        ASSUME  ES:NOTHING

        LES     DI,[THISSFT]

; Same as SETSFT except ES:DI already points to SFT
        entry   SETCLUS
        DOSAssume   <DS>,"SetClus"
        ASSUME  ES:NOTHING

        Assert  ISSFT,<ES,DI>,"SetClus"
        MOV     CX,[NEXTADD]
        SUB     CX,WORD PTR DMAAdd       ; Number of bytes transfered
        TESTB   ES:[DI].SF_FLAGS,devid_device
        JNZ     ADDREC                   ; don't set clusters if device
        MOV     AX,[CLUSNUM]
        MOV     ES:[DI.sf_lstclus],AX
        MOV     AX,[LASTPOS]
        MOV     ES:[DI.sf_cluspos],AX

;----------------------------------------------------------------------------
;
; Procedure : AddRec
; Inputs:
;       ES:DI points to SFT
;       CX is No. Bytes transferred
; Function:
;       Update the SFT offset based on the transfer
; Outputs:
;       sf_position updated to point to first byte after transfer
;       ES:DI points to SFT
;       CX No. of bytes transferred
;       Carry clear
;----------------------------------------------------------------------------

        ASSUME  ES:NOTHING
entry   AddRec
        DOSAssume   <DS>,"AddRec"

        Assert  ISSFT,<ES,DI>,"AddRec"
        JCXZ    RET28           ; If no records read,  don't change position
        ADD     WORD PTR ES:[DI.sf_position],CX  ; Update current position
        ADC     WORD PTR ES:[DI.sf_position+2],0
RET28:  CLC
        return
EndProc DISKREAD


Break   <DskRdBufScan -- Disk Read Buffer Scan>
;----------------------------------------------------------------------------
;
; Procedure Name : DskWrtBufScan
;
; Inputs:
;       CX = # of contiguous sectors read.  (These constitute a block of
;            sectors, also termed an "Extent".)
;       [HIGH_SECTOR]:DX = physical sector # of first sector in extent.
;       [TEMP_VAR2]:[TEMP_VAR] = Transfer address (destination data address).
;       ES:BP -> Drive Parameter Block (DPB).
;
; Function:
;       The Buffer Queue is scanned: the contents of any dirty buffers are
;       "read" into the transfer memory block, so that the transfer memory
;       reflects the most recent data.
;
; Outputs:
;       Transfer memory updated as required.
;
; Uses:
;       DS,AX,BX,CX,SI,DI destroyed.
;       SS override for all global variables.
;
; Notes:
;       FIRST_BUFF_ADDR is set-up to contain the LAST buffer to check, rather
;       than the FIRST.
;----------------------------------------------------------------------------
;M039: Created

procedure   DskRdBufScan,NEAR

ASSUME  DS:NOTHING

        cmp     [DirtyBufferCount],0    ;Any dirty buffers?
        je      bufx                    ; -no, skip all work.

        mov     bx,[HIGH_SECTOR]
IFDEF __TONYI_486__
        add     cx,dx
        mov     si,bx
ELSE
        mov     si,bx
        add     cx,dx
ENDIF
        adc     si,0

        call    GETCURHEAD              ;DS:DI -> 1st buf in queue.
        mov     ax,[di].buf_prev
        mov     [FIRST_BUFF_ADDR],ax

        Assert  ISDPB,<ES,BP>,"DISKREAD/bufchk"
        mov     al,es:[bp].dpb_drive

;            BX:DX = Extent start.
;            SI:CX = Extent end + 1.
;               AL = Drive #.
;            DS:DI-> 1st buffer in queue.
;[FIRST_BUFF_ADDR] = Address offset of last buffer in queue.

bufq:   cmp     al,BYTE PTR [di.buf_ID] ;Same drive?
        jne     bufq1                   ;  -no, jump.

        Cmp32   bx,dx,<WORD PTR [di.buf_sector+2]>,<WORD PTR [di.buf_sector]>
        ja      bufq1                   ;Jump if Extent start > buffer sector.
        Cmp32   si,cx,<WORD PTR [di.buf_sector+2]>,<WORD PTR [di.buf_sector]>
        ja      bufq2                   ;Jump if Extent end >= buffer sector.

bufq1:  cmp     di,[FIRST_BUFF_ADDR]    ;Scanned entire buffer queue?
        mov     di,[di].buf_next        ; Set-up for next buffer.
        jne     bufq                    ; -no, do next buffer

bufx:   return                          ;Exit.

;       Buffer's sector is in Extent: if it is dirty, copy its contents to
;       transfer memory; otherwise, just re-position it in the buffer queue
;       as MRU (Most Recently Used).

bufq2:  SaveReg <ax>
        testb   [di.buf_flags],buf_dirty ;Buffer dirty?
        jz      bufq3                    ; -no, jump.

        SaveReg <cx,dx,si,di,es>

        mov     ax,dx
        sub     ax, word ptr [di].buf_sector
        neg     ax

;       AX = offset (in sectors) of buffer sector within Transfer memory
;            block.  (Note: the upper word of the sector # may be ignored
;            since no more than 64k bytes will ever be read.  This 64k limit
;            is imposed by the input parameters of the disk read operation.)

IFDEF __TONYI__3
;---------------------------------------------------------
; Load TEMPVAR as a DWORD and shuffle LEA to help 486's
;---------------------------------------------------------
        mov     cx,es:[bp].dpb_sector_size ;CX = sector size (in bytes).
        lea     si,[di].BUFINSIZ        ;DS:SI -> buffer data.
        mul     cx                      ;AX = offset (in bytes) of buf. sector
        les     di,DWORD PTR [TEMP_VAR]
        add     di,ax
ELSE
        lea     si,[di].BUFINSIZ        ;DS:SI -> buffer data.
        mov     cx,es:[bp].dpb_sector_size ;CX = sector size (in bytes).
        mul     cx                      ;AX = offset (in bytes) of buf. sector
        mov     di,WORD PTR [TEMP_VAR]
        add     di,ax
        mov     es,WORD PTR [TEMP_VAR2]
ENDIF

IFDEF VCB_MOVSD
; Enable DWORD moves and omit byte (or word) remainder check
; Considering the same code is BUF.ASM without the remainder
; check why bother doing it here, the only way a remainder
; would be left is if the sector size wasn't a multiple of 4 !!

        shr     cx,1

;          CX = sector size (in WORDs); CF=1 if odd # of bytes.
;       DS:SI-> Buffer sector data.
;       ES:DI-> Destination within Transfer memory block.

        IF1
                %out    DWORD sector moves on 386 are ENABLED!
        ENDIF
        CMP     [DDMOVE],0
        JZ      nodd
        SHR     CX,1
        DB      66H
nodd:
        rep     movsw                   ;Copy buffer sector to Transfer memory

ELSE    ; VCB_MOVSD

IFDEF __TONYI__
;------------------------------------------------------
; DWORD tweaks for the move on a 386....
;------------------------------------------------------
        IF1
                %out    DWORD sector moves on 386 are ENABLED!
        ENDIF
        cmp     DDMOVE, 0
        jne     do_DWORD_move
ENDIF

        shr     cx,1

;          CX = sector size (in WORDs); CF=1 if odd # of bytes.
;       DS:SI-> Buffer sector data.
;       ES:DI-> Destination within Transfer memory block.

        rep     movsw                   ;Copy buffer sector to Transfer memory

IFDEF __TONYI__2
;-------------------------------------------------------------------------
; Since CX==0 after the REP MOVS, we can do ADC CX,CX here and save a bit.
;-------------------------------------------------------------------------
        adc     cx,cx   ; CX is *known* to be zero!
ELSE
        adc     cx,0                    ;CX=1 if odd # of bytes, else CX=0.
ENDIF
        rep     movsb                   ;Copy last byte.

IFDEF __TONYI__
SectorMoveDone:
ENDIF

ENDIF   ; VCB_MOVSD

        RestoreReg <es,di,si,dx,cx>

;       DS:DI -> current buffer.

bufq3:  mov     ax,di                   ;DS:AX -> Current buffer.
        invoke  SCANPLACE
        cmp     ax,[FIRST_BUFF_ADDR]    ;Last buffer?
        RestoreReg <ax>
        jnz     bufq                    ; -no, jump.
IFDEF __TONYI__2
;---------------------------------------------------------
; Smaller/faster.  "bufx" is just a return.
;---------------------------------------------------------
        ret
ELSE
        jmp     short bufx              ; -yes, exit.
ENDIF

IFNDEF VCB_MOVSD
; Delete unneeded remainder check code

IFDEF __TONYI__
do_DWORD_move:
        .386
        mov     dx, cx          ; Save size in DX (it's clear)
        shr     cx, 2           ; Divide by 4 for DWORDS
        rep     movsd           ; Even DWORDS (CX is now 0)
        mov     cl, dl          ; Get size back (low part)
        and     cl, 3           ; Mask off odd bytes
        rep     movsb           ; Pick up the stragglers.
        jmp     short   SectorMoveDone
        .8086
ENDIF

ENDIF   ; VCB_MOVSD

EndProc DskRdBufScan

DOSCODE ENDS
        END
