SCCSID  equ     "@(#)volio.asm  6.4 92/03/26"
Page    ,132
TITLE   VOLIO - volume ID management IO Routines
NAME    VOLIO

;***    volume ID management for MSDOS
;
;       Global          Local
;
;       DRead
;       DWrite
;       DskRead
;       DskWrite
;       RawDiskIO
;       DiskIO
;                       DiskHE
;       DoVolIO
;       DoVolIONE
;       DoVolIO2
;                       DiskCall
;       ProbeChange
;       ResetMedia
;       FatSecRd
;       ReadBoot
;       BuildBPB
;
.xlist
.xcref
include dosseg.inc
include defdbug.inc
include basemaca.inc
include osmaca.inc
include fnofs.inc
include oldmaca.inc
include dossymc.inc
include filemode.inc
include fsh.inc
include sft.inc
include bpb.inc
include boot.inc
include buffer.inc
include curdir.inc
include devsym.inc
include dpb.inc
include error.inc
include filesys.inc
include harderr.inc
include protmode.inc
include tcbdata.inc
include seldesc.inc
include virtsym.inc
include mi.inc
include filemacs.inc

.cref
.list
include vpb.inc

        ReferGlobal     MaxSec,WORD     ; sector buffer
        ReferGlobal     pVPBBuf,DWORD   ; sector buffer
        ReferGlobal ErrMap24,BYTE

        ReferTask       CurrTCB,WORD    ; Current task control block
        ReferTask       VerFlg,BYTE     ; Verify on write flag

        extrn           RlseBuffr:NEAR
        extrn           RemoveBufVPB:NEAR

        extrn           CRC:NEAR
        extrn           sw_DevIOCtl:NEAR
        extrn           WaitDPB:NEAR
        extrn           WaitVPB:NEAR
        extrn           WhatVolume:NEAR
        extrn           WhatVolumePerc:NEAR
        extrn           WhatVolumeForce:NEAR
        extrn           ToggleRequest:NEAR

        ReferGDT        GDT_VPB,WORD    ; VPB BMP Selector


        EXTRNFAR        DevIOCall
        EXTRNFAR        DevIOCall2
        EXTRNFAR        AllocReqPacketWait
        EXTRNFAR        FreeReqPacket
        EXTRNFAR        HardError

        ModName , FNO_VOLIO_ASM


          FARCODE HIGH2
HIGH2CODE SEGMENT


BREAK   <DRead, DWrite - basic I/O with error reporting>

;***    DRead, DWrite - basic I/O with error reporting.
;
;   DRead and DWrite are called to perform sector I/O and to report errors.
;   We have modified them to be passed a volume pointer (in ES:BP) to assure
;   the I/O is done to the correct volume.  Any errors are reported here and
;   the only action that the caller need see is that of FAIL.  Retry and Ignore
;   are handled internally; Abort sets a global flag and returns FAIL.
;
;   ENTRY   al          DRead:  0 if normal, ReadBypassAdj if Bypass.
;                       DWrite: 0 if normal, WriteBypassAdj if Bypass.
;           ah          DRead:  0 if virtual address in DS:BX,
;                               1 if physical address in [DS:BX]
;
;           ds:bx ([ds:bx]) points to transfer area
;           cx          is count of sectors
;           di:dx       is starting logical sector number
;           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 DRead,<hybrid,far16>
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
        PMONLY
        DBPRT   80h,40h,<DRead($x:$x,$x,$x,$x>,<ds,bx,cx,dx,bp>

        .errnz  (CMDInputBypass-CMDINPUT-ReadBypassAdj)

        add     ax,CMDINPUT             ; (AX) = CMDINPUT OR CMDINPUTBypass
        push    bp
        push    ds
        SaveReg <ax,ds>
        get_advise                      ; ax = advisory bits in FSH_DOV_ form

; convert advisory bits from FSH_DOV_ form to DOV_ form

adjust = 3
        .errnz  (FSH_DOV_OPWRTHRU shr adjust) - DOV_WRTHRU
        .errnz  (FSH_DOV_OPNCACHE shr adjust) - DOV_NCACHE

        shr     ax,adjust               ; ax = advisory bits in DOV_ form
        mov     si,ax                   ; si = advisory bits
        RestoreReg <ds,ax>
        call    DoVolIO
        pop     ds
        pop     bp
        DBPRT   80h,40h,<)>
        ret

EndProc   DRead


Procedure DWrite,<hybrid,far16>
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
        PMONLY
        DBPRT   80h,40h,<DWrite($x:$x,$x,$x,$x>,<ds,bx,cx,dx,bp>

if2
if      ((CMDOUTPUT+WriteBypassAdj+1) GT 255)
        %out    DWrite - command doesn't fit in one byte.
        .err2
endif
endif

        .errnz  (CMDOutputBypass-CMDOUTPUT-WriteBypassAdj)
        .errnz  (CMDOutputBypassV-CMDOUTPUTV-WriteBypassAdj)
        .errnz  (CMDOUTPUTV-CMDOUTPUT-1)
        .errnz  (CMDOutputBypassV-CMDOutputBypass-1)

        add     al,CMDOUTPUT            ; (AL) = basic output command
        add     al,VerFlg               ; (AL) = non-verify -or- verify command
        xor     ah,ah                   ; (AX) = command

dw1:    push    bp
        push    ds
        push    cx
        push    dx
        push    di
        push    bx
        SaveReg <ax,ds>
        get_advise                      ; ax = advisory bits in FSH_DOV_ form

; convert advisory bits from FSH_DOV_ form to DOV_ form

adjust = 3
        .errnz  (FSH_DOV_OPWRTHRU shr adjust) - DOV_WRTHRU
        .errnz  (FSH_DOV_OPNCACHE shr adjust) - DOV_NCACHE

        shr     ax,adjust               ; ax = advisory bits in DOV_ form
        mov     si,ax                   ; si = advisory bits
        RestoreReg <ds,ax>
        call    DoVolIO
        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
;
; Check to see if any of the sectors just written are used to build the VID
; ES:BP -> VPB
; CX = sector count
; DI:DX = starting logical sector
;
        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   DWrite

BREAK   <DskRead, DskWrite - do simple I/O and just return the error>

;
;   DskRead and DskWrite are the entry points for the INT 25/26 guys.  These
;   are passed the packet contents and are expected to perform the I/O without
;   any special handling of the errors.
;
;   Inputs:     ds:bx   transfer address
;               cx      count of sectors
;               di:dx   starting logical sector number
;               es:bp   pointer to VPB
;   Outputs:    carry clear: operation succeeded
;               carry set: some disk failure
;                   ax = status from packet
;   Registers modified: all
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure   DskRead,HYBRID
        PMONLY
        DBPRT   80h,40h,<DskRead($x:$x,$x,$x,$x>,<ds,bx,cx,dx,bp>
        chkvselhd es,bp
        mov     al,CMDINPUT
        call    RawDiskIO
        DBPRT   80h,40h,<)>
        ret
EndProc DskRead

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

Procedure DskWrite,HYBRID
        PMONLY
        DBPRT   80h,40h,<DskWrite($x:$x,$x,$x,$x>,<ds,bx,cx,dx,bp>
        chkvselhd es,bp
        mov     al,CMDOUTPUT
        test    VerFlg,01h
        jz      dsw1                    ; Verify on Write is OFF
        mov     al,CMDOUTPUTV           ; Verify on Write is ON
dsw1:   call    RawDiskIO
        DBPRT   80h,40h,<)>
        ret
EndProc   DskWrite


BREAK   <DiskIO, RawDiskIO - do simple disk IO>

;
; DiskIO and RawDiskIO are the internal routines that constructs a packet,
; calls the device driver and returns the error code.  RawDiskIO returns the
; errors while DiskIO signals them to the HardError daemon.  Since we issue
; DiskIO calls during the process of building a VPB, we CANNOT allow them to be
; ignored.  The best we can hope for is fail.
;
;   Inputs: RawDiskIO:
;               al = operation (input or output)
;               ds:bx points to tranfer area
;               cx is count of sectors
;               di:dx is starting sector number
;               es:bp is VPB
;
;           DiskIO:
;               al = operation
;               es:bx points to transfer area
;               cx is count of sectors
;               di:dx is starting sector number
;               ds:si points to DPB
;
;   Outputs:    ax has packet status
;               ds:si points to DPB
;               cx = number of sectors transfered successfully
;               carry set => error detected
;                   zero set => uncertain media detected
;                   zero reset => operation was failed
;               carry clear => operation succeeded
;
;   Uses:       AX, BX, CX, DX, SI, DI, ES, Flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure RawDiskIO,NEAR
        PMONLY
        DBPRT   80h,40h,<RawDiskIO($x:$x,$x,$x,$x>,<ds,bx,cx,dx,es>

        chkvselhd es,bp
        push    di                      ; Save high word of sector number
        mov     ah,es:[bp].vpb_fsd.vpdFAT_media
        mov     di,ds                   ; (DI:BX) points to transfer address
        lds     si,es:[bp].vpb_fsi.vpi_pDPB     ; (DS:SI) points to DPB
        mov     es,di                   ; (ES:BX) points to transfer address

        xor     di,di                   ; indicate no signalling
        jmp     short dio0

Entry   DiskIO

        PMONLY
        DBPRT   80h,40h,<DiskIO($x:$x,$x,$x,$x:$x>,<es,bx,cx,dx,ds,si>

        push    di                      ; Save high word of sector number
        mov     di,1                    ; signal those hard errors
        mov     ah,[si].dpb_media

dio0:   push    es                      ; save transfer address selector
        push    bx                      ;

        CALLFAR AllocReqPacketWait      ; (ES:BX) = tiled request packet

        pop     es:[bx].IOpData.Offst   ; put transfer addr selector in packet
        pop     es:[bx].IOpData.Segmt   ;

        mov     es:[bx].PktLen,LENIO+PktHeadSize
        mov     es:[bx].PktCmd,al
        mov     es:[bx].IOmedia,ah
        mov     es:[bx].IOcount,cx
        mov     es:[bx].IOstart,dx
        pop     dx                      ; Restore high word of starting sector
        mov     es:[bx].IOstart+2,dx
        mov     es:[bx].IOSFN,0         ; dummy SFN
        mov     al,[si].dpb_unit
        mov     es:[bx].PktUnit,al
        mov     es:[bx].PktFlag.HighByte,0 ; Pass virtual address
        mov     dx,di                   ; (DL) = hard error flag
        push    bp
        xor     bp,bp                   ; Sector Transfer Count
;
; Begining of retry hard error loop
;
; (ES:BX) = request packet
; (DS:SI) = DPB address
; (CX) is the count to perform
; (DL) = hard error flag (0 = don't signal hard errors, 1 = signal them)
; (BP) = Current sector Transfer Count
;
dio1:   SaveReg <ds,si>
        lds     si,[si].dpb_driver_addr
        mov     es:[bx].PktStatus,0
        CALLFAR DevIOCall2
        RestoreReg <si,ds>
        add     bp,es:[bx].IOcount

        mov     ax,es:[bx].PktStatus
        test    ax,STERR
        jz      dio5                    ; exit with carry clear

        mov     di,CurrTCB              ; (SS:DI) = current TCB
        and     ax,STECODE
        cmp     ax,ERROR_I24_DISK_CHANGE
        je      dio2                    ; Disk changed.

        or      dl,dl                   ; if hard flag zero, return error
        jz      dio3
;
; (ES:BX) points to request packet
; (DS:SI) points to dpb
; (CX) is the count to perform
; (DL) is hard error flag
; (BP) is number of sectors transfered successfully
;
; Check for uncertain media nesting. If found, return with zero set
;
        cmp     ax,ERROR_I24_UNCERTAIN_MEDIA
        je      dio4                    ; exit with zero flag set
;
; We check for errors. If this not a "drive not ready" error, we assume
; that we have an unformatted medium in the drive. In this case, we do
; not call the hard error daemon but let the caller handle the error.
;
        cmp     ax,ERROR_I24_NOT_READY
        jz      dio2                    ; Drive not ready. Call HE daemon

        cmp     dl,2
        je      dio3

        test    ss:[di].TCBModeFlags,fOPENDASD+fLOCKDRV
        jnz     dio3                    ; Let caller handle error

dio2:   mov     dh,ss:[di].TCBAllowed   ; (DH) = old allowed flags
        mov     ss:[di].TCBAllowed,ALLOWED_FAIL + ALLOWED_RETRY
        call    DiskHE
        mov     ss:[di].TCBAllowed,dh   ; restore original allowed flags

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

        ; Zero is reset!  Necessary for exit condition!
        mov     TASKAREA:[di].TCBactBufHE,al ; Save user specified action
        jmp     short dio4              ; Let caller handle action

dio2_5: or      ax,ax                   ; check if user returned ignore
        .errnz HDERR_IGNORE
        jz      dio5                    ; (carry is clear)

        cmp     ax,HDERR_RETRY          ; check if user returned retry
        jz      dio1

dio3:   or      sp,sp                   ; make sure zero flag is clear

dio4:   stc                             ; return the error via carry

dio5:   pushf
        mov     ax,es:[bx].PktStatus    ; (AX) = packet status
        CALLFAR FreeReqPacket           ; give back tiled request packet
        popf
        mov     cx,bp                   ; cx = # of sectors transferd OK !
        pop     bp
        DBPRT   80h,40h,<)>
        ret
EndProc   RawDiskIO

;
;   DiskHE - update a packet and signal a hard error for a disk I/O request
;
;   Inputs:     ds:si point to dpb
;               es:bx point to packet with error direct from device
;               cx is the count to perform
;   Outputs:    packet updated to reflect next transfer
;               cx is sectors remaining to be transfered
;               ax has options:
;                       0 => ignore
;                       1 => retry
;                       2 => abort
;                       3 => fail
;   Registers modified:
;               ax, cx, flags
;
        ASSUME  DS:NOTHING,ES:NOTHING

Procedure DiskHE,NEAR,LOCAL

        PMONLY
        DBPRT   80h,40h,<DiskHE($x:$x,$x:$x,$x>,<ds,si,es,bx,cx>
        mov     ax,es:[bx].IOcount      ; get what was successfully completed
        sub     cx,ax                   ; get remaining count
        add     es:[bx].IOstart,ax      ; bump starting point to offending sect.
        SaveReg         <dx>
        mul     [si].dpb_cbSector       ; convert to byte count
        RestoreReg      <dx>
        add     es:[bx].IOpData.Offst,ax; adjust transfer address too
        mov     ax,cx                   ; fix count-left-to-go
        mov     es:[bx].IOcount,ax

Entry   DiskHE2

;*  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,es>
        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 <es,ds,bp,di>
        SaveReg <ds,si,cx>
        xor     cx,cx
        mov     cl,[si].dpb_drive
        lds     si,[si].dpb_driver_addr
        mov     ax,HEPacket
        CALLFAR HardError               ; signal error to user
        RestoreReg <cx,si,ds>
        DBPRT   80h,40h,<)>
        ret
EndProc   DiskHE

BREAK   <DoVolIO - perform volume-based I/O>

;
;       DoVolIO - performs I/O to a specified volume.  It will guarantee (as
;       much as possible) that the I/O is done to the correct volume.
;
;       We call the hard error handler regardless of whether signal-hard-
;       error bit is set or not if the error returned is a 'disk change'
;       error since we need to prompt the user for the correct disk in the
;       drive.
;       All retries and ignores are handled here.
;
;       Note that Cache-bypass and no-cache are two different ways to tell
;       the device driver not to cache data. Cache-bypass causes different
;       read/write command packet to be sent to the device driver than the
;       standard read/write command. No-cache is a bit that's tag on to the
;       standard read/write command packet. The no-cache mechanism is the
;       preferred method.
;
;       ENTRY   al    = function (read or write)
;               ah    = 0 : logical transfer address in ds:bx.
;                     = 1 : physical transfer address in [ds:bx].
;               ds:bx ([ds:bx]) = transfer address
;               cx    = number of sectors
;               di:dx = beginning logical sector number
;               bp    = VPB handle
;               si    = advisory bits for CMDINPUT/CMDOUTPUT(v)
;                       DOV_AUTOFAIL on = fail errors.
;                       DOV_WRTHRU on = write thru
;                       DOV_NCACHE on = don't cache data
;
;       EXIT    es:bp => VPB
;               cx = number of sectors transfered successfully
;               carry clear => operation succeeded
;                              ax = 0
;               carry set => operation was failed
;                            ax = DOS error code
;
;       Registers modified: AX, BX, CX, DX, SI, DI, DS, ES, Flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure DoVolIO,NEAR

VPBhandle equ [bp]

LocalVar  RP_PktCMD,WORD        ; low byte = command, high byte phys\virt flag
LocalVar  RP_IOcount,WORD
LocalVar  RP_IOpData,DWORD
LocalVar  RP_IOstart,DWORD
LocalVar  dov_advise,WORD
LocalVar  SectorCount,WORD
LocalVar  ReqPacketAddr,DWORD
LocalVar  pDPB,DWORD

        PMONLY
        DBPRT   80h,40h,<DoVolIO($x,$x:$x,$x,$x,$x>,<ax,ds,bx,cx,dx,bp>
;
; Allocate and initialize as much as possible the I/O request packet
;

ifdef FSSTRICT
        test    si,not DOV_USED         ; reserved bits on?
        LJNZ    dove1                   ; yes, go panic
endif

dov0:   EnterProc <ax, cx, ds,bx, di,dx, si, 0> ; setup stack frame
        mov     si,bx                   ; (ds:si) = transfer address
        CALLFAR AllocReqPacketWait      ; (ES:BX) = tiled request packet addr

        mov     ReqPacketAddr.Segmt,es
        mov     ReqPacketAddr.Offst,bx

        jmp     SHORT dov12             ; registers already set up

; In the code below, we will be jumping to this top position quite a lot.  In
; normal operation, we will never do this; there are odd circumstances that will
; cause us to need to wait for two separate events (disk being toggled and
; vol check in progress).  Rather than set a semaphore for these two conditions
; we will keep on testing for them in the same order. If we fail on either we'll
; wait for the offending event to clear and then test'em all over.  Since this
; is a relatively rare event, we'll "waste" little time here.
;
; Each time we jump here we must fill in all the request packet fields
; again from the stack frame in case the device driver changed any.

dov10:  les     bx,ReqPacketAddr        ; (es:bx) -> request packet
        mov     ax,RP_PktCMD            ; (al)    = command
        mov     cx,RP_IOcount           ; (cx)    = count
        lds     si,RP_IOpData           ; (ds:si) = transfer address
        mov     di,RP_IOstart.Segmt
        mov     dx,RP_IOstart.Offst     ; (di:dx) = logical sector

dov12:  mov     es:[bx].PktCmd,al       ; al = Command
        mov     es:[bx].PktFlag.HighByte,ah ; ah = virtual addr flag.
        mov     es:[bx].IOcount,cx

        or      ah,ah                   ; Virtual address ?
        mov     ax,ds
        jz      dov13                   ; Yes, ax:si = transfer address
        mov     ax,ds:[si + 2]          ; No, [ds:si] = transfer address
        mov     si,ds:[si]
dov13:
        mov     es:[bx].IOpData.Segmt,ax
        mov     es:[bx].IOpData.Offst,si
        mov     es:[bx].IOstart.HighWord,di
        mov     es:[bx].IOstart.LowWord,dx

        mov     es:[bx].PktLen,LENIO+PktHeadSize+2
        mov     es:[bx].IOSFN,0         ; dummy SFN
        mov     ax,dov_advise           ; ax = advisory bits + hard error bit
        shr     ax,1                    ; ax = advisory bits
        mov     es:[bx].PktAdvise,ax

; We can jump back here ONLY if we know that the request packet is still ok
; (haven't called the device driver yet)
        mov     ax,VPBhandle            ; (ax) = VPB handle
        mov     bl,VPBCHECK             ; (bl) = VPB flags to check for
        WaitVPBandDPB                   ; wait for VPB and DPB to become free
;
; The VPB and drive are free.  Is the volume in the drive?
;
        mov     pDPB.Offst,si           ; save pDPB for later
        mov     pDPB.Segmt,ds

        cmp     ax,[si].dpb_hVPB        ; Is the volume in the drive?
        jz      dov20                   ; yes, go do the I/O
;
; We need to call WhatVolume.  WhatVolume may call FS_MOUNT, which may need
; to be swapped in.  In order to prevent deadlock with the swapper and
; this thread wanting the same buffer, we RlseBuffr the buffer and fake
; a harderror retry.  This way the volume will get mounted and the retry
; should succeed.

        call    RlseBuffr
        SaveReg <ax,si,ds>
        call    WhatVolumePerc          ; What volume is really in the drive?
        mov     bx,ax                   ; bx = possible error code
        RestoreReg <ds,si,ax>
        LJC     dov91                   ; WhatVolume returned an error

        cmp     ax,[si].dpb_hVPB        ; Now, do we have the correct volume?
        jnz     dov16                   ; no, go request volume
;
; we only return a retry to those callers which have
;    TCBBufHE == BufHE_Percolate
; because all callers which don't have that set must not rely on RlseBuffr
; not being called.  this is because ToggleRequest can call RlseBuffr.
;
        mov     di,TASKAREA:CurrTCB
        test    TASKAREA:[di].TCBBufHE,BufHE_Percolate ; handle error here?
        jz      dov20                   ; yes, go do I/O
        or      TASKAREA:[di].TCBBufHE,BufHE_CurBufFreed
        mov     TASKAREA:[di].TCBactBufHE,HDERR_RETRY
        stc
        mov     bx,ERROR_I24_WRONG_DISK
        jmp     dov91

dov16:  call    ToggleRequest           ; prompt him for action and wait

; In case the user wants us to fail the request we have to ensure a valid
; return code to the FSD.

        cmp     ax,HDERR_RETRY          ; RETRY?
        jz      dov17                   ; yes, no need to generate error code

        push    ax
        les     bx,ReqPacketAddr        ; (ES:BX) = request packet address
        mov     ax,ERROR_I24_WRONG_DISK
        or      ax,STERR                ; set error bit
        mov     es:[bx].PktStatus,ax
        pop     ax

dov17:  mov     di,CurrTCB              ; (di) = CurrTCB
; go check TCBBufHE to see who should carry out the user specified action.
        jmp     dov60                   ; go check TCBBufHE


; Everything seems to be cool.  Fill in the last details of the packet from
; the parts that were not determinable before we had the VPB and DPB and
; perform the operation.
;
; NOTE: ES may have been trashed by the call to WhatVolume!!! So we need to
; set it up again to point to the correct VPB.
;

dov20:
;;;
;;; The following is old code that is not relevant anymore since the media ID
;;; byte for IFS drives is not really defined. No block device driver should
;;; be looking at this byte in the rquest packet since it is not very
;;; informative any more - more a relic from the old DOS days.
;;;
;;;     push    bp
;;;     mov     bp,ax
;;;     VPBContext ES,BP                ; ES:BP -> VPB
;;;     mov     al,es:[bp].vpb_fsd.vpdFAT_media    ; (AL) = media is volume-dependent flag
;;;     pop     bp

        les     bx,ReqPacketAddr        ; (ES:BX) = request packet address
;;;     mov     es:[bx].IOmedia,al      ; save vpb_fsd.vpdFAT_media
        mov     al,[si].dpb_unit        ; unit is per-DPB
        mov     es:[bx].PktUnit,al
        call    DiskCall                ; call the device
        mov     ax,es:[bx].IOcount      ; ax = number of sectors transfered
        add     SectorCount,ax          ; Sector = Total sectors transfered
;
; Check the return here for errors.  No errors means we work fine => go to
; end
;
        mov     ax,es:[bx].PktStatus
        test    ax,STERR                ; any error?
        LJZ     dov80                   ; no, carry is clear and we go return

        lds     si,pDPB                 ; point to drive for vol determination
        and     ax,STECODE              ; mask off error code
        cmp     ax,ERROR_I24_UNCERTAIN_MEDIA
        jnz     dov40                   ; If error is not media prob
                                        ; then go signal it
;
; The volume suddenly became unreliable.  We need to go to it and determine
; the true volume that's present.  Unfortunately, we need to do the operation
; completely over from the start.  After the called to DiskCall we added the
; sectors transfered to our counter. Since the volume is unreliable we must
; adjust our counter for this operation
;
        call    RlseBuffr
        push    word ptr es:[bx].IOcount    ; Save number of sectors xferred
        call    WhatVolumeForce         ; examine it and set up volume info
    ASSUME  ES:NOTHING
        pop     bx                      ; bx = number of sectors transfered
        LJC     dov92                   ; failed here too

        mov     di,TASKAREA:CurrTCB
        test    TASKAREA:[di].TCBBufHE,BufHE_Percolate ; handle error here?
        jz      dov30                   ; yes, go do I/O
        or      TASKAREA:[di].TCBBufHE,BufHE_CurBufFreed
        mov     TASKAREA:[di].TCBactBufHE,HDERR_RETRY
        stc
        mov     bx,ERROR_I24_WRONG_DISK
        jmp     dov91

dov30:  sub     SectorCount,bx          ; Sector = Total sectors transfered
        jmp     dov10                   ; and start over.
;
; We had a true hard error.  We want to report things as exactly as possible
; to the hard error daemon; the packet info is in an intermediate state;
; currently it has the result of this incomplete operation.  We update the
; packet for a restart (presuming retry response) and give the packet to
; the hard error daemon.  The first sector pointed to by the packet is the
; offending sector (as best as we can tell).
;
dov40:  cmp     ax,ERROR_I24_DISK_CHANGE; need to change disks?
        mov     di,CurrTCB              ; (di) = CurrTCB
        jne     dov50

; call hard error handler to prompt user

        mov     dl,TASKAREA:[di].TCBAllowed     ; (dl) = old allowed flags
        mov     TASKAREA:[di].TCBAllowed,ALLOWED_FAIL + ALLOWED_RETRY
        call    DiskHE
        mov     TASKAREA:[di].TCBAllowed,dl     ; restore old allowed flags
        jmp     short dov60

dov50:  cmp     ax,ERROR_I24_NOT_READY  ; Disk not ready?          #55458
        je      dov55                   ; Ignore autofail          #55458

        test    dov_advise,DOV_AUTOFAIL ; autofail hard error?
        jnz     dov70                   ; yes, fail this call
dov55:                                  ;                          #55458
        call    DiskHE                  ; no, call hard error handler

;
; We check the flag bits in TCBBufHE to see who should carry out the user
; specified action. If BufHE_Percolate bit is set, we save the action in
; TCBactBufHE and return to the caller so that the action can be done at the
; appropriate level. Otherwise, we handle it here at the low level.
;
dov60:  test    TASKAREA:[di].TCBBufHE,BufHE_Percolate
        jz      dov65                   ; We can handle action at this level
        mov     TASKAREA:[di].TCBactBufHE,al ; Save user specified action
        jmp     short dov70             ; Let caller handle action

dov65:  or      ax,ax                   ; did user want to IGNORE
        .errnz HDERR_IGNORE
        jz      dov80                   ; yes, pretend successful (carry clear)
        cmp     ax,HDERR_RETRY          ; how about RETRY?
        LJZ     dov10                   ; yes, back to top; packet is all set
;
; The user wants to FAIL the request. Indicate it.
;
dov70:  stc

dov80:  pushf
        les     bx,ReqPacketAddr        ; (ES:BX) = request packet address
        push    bx
        mov     bx,es:[bx].PktStatus    ; bl = I24 error code
        mov     ax,0                    ; Assume no error
        jnc     dov90                   ; No error, go return I/O count
        xor     bh,bh                   ; Yes, translate I24 error code
        DosContext DS
        mov     al,ErrMap24[bx]         ; get dos error code
        Assume  DS:NOTHING
dov90:  pop     bx
        CALLFAR FreeReqPacket           ; free the tiled request packet
        popf
        mov     cx,SectorCount          ; CX = number of sectors transfered
        LeaveProc
        VPBContext ES,BP                ; reset es:bp to point to VPB
        DBPRT   80h,40h,<)>
        ret

dov91:  mov     ax,bx                   ; AX = I24 error code
        les     bx,ReqPacketAddr        ; (ES:BX) = request packet address
        or      ax,STERR                ; set error bit
        mov     es:[bx].PktStatus,ax    ; al = I24 error code
        stc
        jmp     short dov80             ; carry set

dov92:  mov     bx,ax                   ; BX = error code
        jmp     short dov91             ; carry set

ifdef FSSTRICT
dove1:  InternalError <DoVolIO: bad advise>
endif

EndProc   DoVolIO

BREAK   <DoVolIO2 - perform volume-based IOCTL>

;
;       DoVolIO2 - performs IOCTL to a specified volume.  It will guarantee (as
;       much as possible) that the
;       IOCTL is done to the correct volume.  Any hard errors are reported and
;       appropriate action is taken.  All retries and ignores are handled
;       here.
;
;       Entry:
;               PUSH  pointer to parameter packet
;               PUSH  Device Handle (DWORD) (one of the following):
;                   Physical Disk category  :  pPhysDiskInfoTblEntry
;                   Device, etc. (BL = 1)   :  pDevDriverHeader
;                   Other (file) (BL = 0)   :  pDPB
;               PUSH  SFN (from original request) (optional)
;               PUSH  length of parameter packet
;               PUSH  length of data packet
;               PUSH  segment of data packet
;               PUSH  offset of data packet
;               PUSH  (Lowbyte = Category code, Highbyte = Function code)
;               PUSH  flag      ( ==1 if raised by FSD; 0 otherwise)
;
;               CALL (Near only, because of stack parameters)
;
;       Exit:   Parameters are cleaned off the stack (4)
;
;               Carry set  :  AX = error code
;                 or
;               Carry clear:  no error
;
;       Registers modified: AX, BX, CX, DX, SI, DI, DS, ES, Flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure DoVolIO2,NEAR
ArgVar  dvio2_FSDFlag,WORD              ;
ArgVar  dvio2_FuncCat,WORD              ; function code (within category)
ArgVar  dvio2_pData,DWORD               ; pointer to data area
ArgVar  dvio2_cbData,WORD               ; length of data area
ArgVar  dvio2_cbParm,WORD               ; length of parameter area
ArgVar  dvio2_SFN,WORD                  ; SFN
ArgVar  dvio2_hDev,DWORD                ; pointer to DPB
ArgVar  dvio2_pParm,DWORD               ; parameter packet

LocalVar  VPBhandle,WORD

        PMONLY

        EnterProc                       ; setup stack frame

        lds     si,dvio2_hDev           ; get the last-used drive
        mov     ax,[si].dpb_hVPB        ; AX = VPB
        mov     VPBhandle,ax
;
; In the code below, we will be jumping to this top position quite a lot.  In
; normal operation, we will never do this; there are odd circumstances that will
; cause us to need to wait for two separate events (disk being toggled and
; vol check in progress).  Rather than set a semaphore for these two conditions
; we will keep on testing for them in the same order. If we fail on either we'll
; wait for the offending event to clear and then test'em all over.  Since this
; is a relatively rare event, we'll "waste" little time here.
;

        cmp     ax,NULLVPB              ; VPB = Null ?
        jne     dov21                   ; No, go wait for DPB and VPB
        call    WhatVolume              ; What volume is really in the drive?
        LJC     dov27                   ; if error go return
        mov     ax,[si].dpb_hVPB        ; AX = VPB
        mov     VPBhandle,ax

dov21:  mov     ax,VPBhandle            ; retrieve VPB handle
        mov     bl,VPBCHECK

        WaitVPBandDPB dvio2_hDev        ; wait for VPB and DPB to become free
;
; The VPB and drive are free.  Is the volume in the drive?
;
        cmp     ax,[si].dpb_hVPB        ; Is the volume in the drive?
        jz      dov22                   ; yes, go do the IOCTL
        SaveReg <ax,si,ds>
        call    WhatVolume              ; What volume is really in the drive?
        mov     bx,ax                   ; bx = possible error code
        RestoreReg <ds,si,ax>
        LJC     dov26                   ; WhatVolume returned an error
        cmp     ax,[si].dpb_hVPB        ; Now, do we have the correct volume?
        jz      dov22                   ; Yes, go do the IOCTL.
        call    ToggleRequest           ; prompt him for action and wait

; In case the user wants us to fail the request we have to ensure a valid
; return code to the FSD.

        cmp     ax,HDERR_RETRY          ; RETRY?
        jz      dov21                   ; yes, no need to generate error code

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

        mov     ax,ERROR_WRONG_DISK
        jmp     short dov24

;
; Everything seems to be cool.  Setup the registers and the stack and call
; sw_devioctl.
;
dov22:  mov     di,dvio2_SFN            ; di = SFN
        mov     cx,dvio2_cbParm         ; cx = size of parameter packet
        mov     si,dvio2_cbData         ; si = size of data packet
        mov     ds,dvio2_pData.Segmt
        mov     dx,dvio2_pData.Offst    ; ds:dx -> data packet
        mov     ax,dvio2_FuncCat        ; al = category ah = function

        mov     bx,dvio2_FSDFlag        ;
        xchg    bh,bl                   ; bl = 0 ->not a devise
                                        ; if (bh == 1) -> raised by FSD
; If this flag is set,it means that this call is raised by FSD and not passed
; on by an FSD (and definitely not a FAT call); So the pointers passed are
; considered to be protect mode and the device driver is called in protect mode.
; If it is not set, the pointers can be protect mode or real mode; Then we
; decide that by looking at the ProcType to see whether the current process is
; a real mode process.

        push    dvio2_pParm.Segmt       ; set up stack parameters
        push    dvio2_pParm.Offst
        push    dvio2_hDev.HighWord
        push    dvio2_hDev.LowWord
        call    sw_DevIOCtl
;
; Check the return here for errors.  No errors means we work fine => go to
; end
;
        lds     si,dvio2_hDev           ; get the last-used drive
        jnc     dov25                   ; no, carry is clear and we go return

        cmp     ax,ERROR_UNCERTAIN_MEDIA
        jnz     dov23                   ; If error is not media prob
                                        ; then go signal it
;
; The volume suddenly became unreliable.  We need to go to it and determine
; the true volume that's present.  Unfortunately, we need to do the operation
; completely over from the start.  After the called to DiskCall we added the
; sectors transfered to our counter. Since the volume is unreliable we must
; adjust our counter for this operation
;
        call    WhatVolumeForce         ; examine it and set up volume info
    ASSUME  ES:NOTHING
        jc      dov27                   ; failed here too
        jmp     dov21                   ; and start over.
;
;
dov23:  cmp     ax,ERROR_DISK_CHANGE    ; need to change disks?
        mov     di,CurrTCB
        jne     dov24

; call hard error handler to prompt user

        mov     dl,ss:[di].TCBAllowed   ; (DL) = old allowed flags
        mov     ss:[di].TCBAllowed,ALLOWED_FAIL + ALLOWED_RETRY
        call    DiskHE2
        mov     ss:[di].TCBAllowed,dl   ; restore original allowed flags

        or      ax,ax                   ; did user want to IGNORE
        .errnz HDERR_IGNORE
        jz      dov25                   ; yes, pretend successful (carry clear)
        cmp     ax,HDERR_RETRY          ; how about RETRY?
        LJZ     dov21                   ; yes, back to top packet is all set

        mov     ax,ERROR_DISK_CHANGE    ; need to change disks
;
; The user wants to FAIL the request. Indicate it.
;
dov24:  stc
dov25:  LeaveProc                       ; EXIT
        ret     ?aframe                 ; clean up stack
dov26:  mov     ax,bx                   ; BX = error code from WhatVolume
dov27:  xor     ah,ah                   ; Yes, translate I24 error code
        mov     bx,ax
        DosContext DS
        mov     al,ErrMap24[bx]         ; get dos error code
        Assume  DS:NOTHING
        jmp     short dov24

EndProc   DoVolIO2

BREAK   <DiskCall - call a block device and simulate UNCERTAIN returns>

;
;       DiskCall - we call a block device driver and return its output.  We
;       want to indicate uncertain media errors (if any) from ALL drivers
;       whether they are new or old.  If they are new, then we have no problem.
;       If they are old, then we poll them (via media check).  If the poll
;       returns I-don't-know or yes-changed, we return UNCERTAINMEDIA.
;
;       Inputs:         es:bx points to tiled requested packet
;                       ds:si points to DPB
;
;       Outputs:        es:bx packet status and flags are filled in
;
;       Registers modified: ax, si, ds, flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure DiskCall,NEAR,LOCAL
        PMONLY
        DBPRT   80h,40h,<DiskCall($x:$x,$x:$x>,<es,bx,ds,si>
        SaveReg <si,ds>
        lds     si,[si].dpb_driver_addr ; look at device driver
        mov     cx,[si].SDevAtt
        RestoreReg <ds,si>
;
; Check to see if driver lever is 2 or higher; if so, we pass the advisory
; bits in the device packet as well. Since the advisory bits are already
; set by the caller (DoVolIO), we just adjust the packet length for old drivers.

dc20:   Dev_FuncLev DEVLEV_2,cx         ; level 2 or above?
        jc      dc23                    ; no, forget the advisory bits

; level ok, check command type

        mov     al,es:[bx].PktCmd       ; al = command
        cmp     al,CMDINPUT             ; input command?
        je      dc25                    ; yes, leave advisory bits alone
        cmp     al,CMDOUTPUT            ; output command?
        je      dc25                    ; yes, leave advisory bits alone
        cmp     al,CMDOUTPUTV           ; outputv command?
        je      dc25                    ; yes, leave advisory bits alone

dc23:   mov     es:[bx].PktLen,LENIO+PktHeadSize ; truncate advisory bits

dc25:   lds     si,[si].dpb_driver_addr ; (DS:SI) = device driver header addr
        mov     es:[bx].PktStatus,0
        CALLFAR DevIOCall
        DBPRT   80h,40h,<=$x>,<es:[bx].PktStatus>

dc30:   DBPRT   80h,40h,<)>
        ret
EndProc  DiskCall


Break   <ProbeChange - see if the media has been changed>

;
;       ProbeChange is a fast method of telling whether the media is uncertain
;       or not.  It must function on both old and new drivers.  We perform
;       this action by doing a Media-Check call.  Old drivers that return I-
;       don't-know or Changed will indicate a change.  New drivers will return
;       UncertainMedia errors.  We do not signal errors as hard errors as
;       this is called when hard error signalling is impossible (inside
;       WhatVolume and DiskCall)
;
;       For non-removable media, we can optimize by saying that non-removable
;       media will *NEVER* change.
;
;
;       Inputs:         ds:si point to a dpb
;       Outputs:        carry clear - no change indicated
;                       carry set - media is uncertain
;       Registers modified: Flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure ProbeChange,NEAR
        PMONLY
        DBPRT   80h,40h,<ProbeChange($x:$x>,<ds,si>
        test    ds:[si].dpb_flags,DPBNONREMOV   ; 'NC' set by TEST instruction
        jnz     pbc10                   ; Non-removable media. Cannot change.
        SaveReg <ax,bx,si,ds,es>
        CALLFAR AllocReqPacketWait      ; (ES:BX) = tiled request packet
        mov     es:[bx].PktLen,LENMedChk+PktHeadSize
        mov     al,[si].dpb_unit
        mov     es:[bx].PktUnit,al
        mov     es:[bx].PktCmd,CMDMedChk
        mov     al,[si].dpb_media
        mov     es:[bx].MedChkmedia,al
        lds     si,[si].dpb_driver_addr
        mov     es:[bx].PktStatus,0
        CALLFAR DevIOCall2
;
; examine return code.  If we have an error, then return it
;
        test    es:[bx].PktStatus,STERR ; was there an error?
        jnz     pbc1                    ; no, examine class of change
        cmp     es:[bx].MedChkflag,1    ; was it no change
        jz      pbc2                    ; yes, all is OK (carry clear)

pbc1:   DBPRT   80h,40h,<*>,<>
        stc

pbc2:   pushf
        CALLFAR FreeReqPacket           ; free tiled request packet
        popf
        RestoreReg <es,ds,si,bx,ax>
        DBPRT   80h,40h,<)>
pbc10:  ret

EndProc   ProbeChange

;***    ResetMedia - inform the device driver that we are handling the Uncertain
;                    media and to stop telling us about it.
;
;   Inputs:     ds:si point to dpb
;   Outputs:    none
;   Registers modified: ax, es, bx
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure ResetMedia,NEAR
        PMONLY
        SaveReg <ds,si>
        mov     al,[si].dpb_unit
        lds     si,[si].dpb_driver_addr
        CALLFAR AllocReqPacketWait      ; (ES:BX) = tiled request packet addr
        mov     es:[bx].PktLen,LENResMed+PktHeadSize
        mov     es:[bx].PktUnit,al
        mov     es:[bx].PktCmd,CMDResMed
        mov     es:[bx].PktStatus,0
        CALLFAR DevIOCall2
        CALLFAR FreeReqPacket           ; give back the request packet
        RestoreReg  <si,ds>
        ret
EndProc   ResetMedia

Break   <FatSecRd - read in multiple fat sectors>

;
;   FatSecRd reads in multiple fat sectors and steps to other FATs if there is
;   an error reading in one of the sectors.  The exceptions to this are if the
;   error stored in TCBFailErr shows that there is no use in continuing, e.g a
;   volume check error such as wrong disk in drive, error disk change etc.
;
;   Inputs:     ds:bx point to buffer
;               di:dx is starting sector number
;               cx = count
;               bp is VPB handle
;   Outputs:    carry clear => success
;               carry set => operation was failed
;   Registers modified: all except DS,BP
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure FatSecRd,<hybrid,far16>


        PMONLY

        DBPRT   80h,40h,<FatSecRd($x:$x,$x,$x>,<ds,si,dx,bp>
        VPBContext es,bp
        xor     ah,ah
        mov     al,es:[bp].vpb_fsd.vpdFAT_FAT_count

        mov     si,DOV_AUTOFAIL             ; Use this for all but last FAT
fsr1:   PUSHA
        push    ds
        push    es
        cmp     al,1                        ; doing the last FAT?
        jne     fsr1a                       ; no
        xor     si,si

fsr1a:
        mov     al,CMDINPUT
        call    DoVolIO                     ; Read a FAT Sector
        pop     es
        pop     ds
        POPA
        jnc     fsr3                        ; Jump if read was ok

; There are two sources of error, volume check errors and Fat Sector
; read errors.  Error codes from Fat Sector reads, except for an error
; occurring on a read of the final FAT Sector, are not returned in TCBFailErr
; (we use DoVolIONE for all FAT sector reads except the final sector, where
; we use DoVolIO).  Carry is still returned by these routines.  This means that,
; if an error code is returned in TCBFailErr, we know that its either a volume
; check error, or an error from reading the final FAT sector.
;
; In the event of a hard error, DiskHE always releases the buffer that was
; was to contain the FAT sector, so that routine now sets a flag in TCBBufHE
; (BufHE_CurBufFreed) to indicate this was done.  We are guaranteed the flag
; is clear on entry, thanks to GetBuffrB (our only caller).
;
        push    bp
        mov     bp,CurrTCB                  ; Point at current task
        test    TASKAREA:[bp].TCBBufHE,BufHE_CurBufFreed
        jnz     fsr2                        ; We need to restart (buf released)
        test    [bp].TCBFailErr,-1          ; Volume check error ?
        jnz     fsr2                        ; jump if error, fail

;
; The sector on the FAT was bad. We try the operation on the next FAT, if any.
;
        pop     bp                          ; BP = hVPB
        add     dx,es:[bp].vpb_fsd.vpdFAT_FAT_size ; skip to next Fat sector
        adc     di,0
        dec     ax
        jnz     fsr1                        ; all FATs exhausted
        jmp     short fsr2a                 ; all FATs exhausted

fsr2:   pop     bp                          ; BP = hVPB

fsr2a:  stc

fsr3:   DBPRT   80h,40h,<)>
        ret
EndProc FatSecRd

Break   <ReadBoot - Read Boot Sector On Media>

;***    ReadBoot - Read Boot Sector On Media>
;
;   ReadBoot is used to read sector 0 on Media.
;
;   Inputs:     ds:si   point to DPB of interest.  It and whatever volume was
;                       in it last are locked.
;
;   Outputs:    Carry clear =>
;               Carry set => operation generated an error ax packet status
;
;   Registers modified: ax,bx,cx,di,dx,es
;
Procedure ReadBoot,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

        push    bp
        mov     al,CMDINPUT
        DosContext es
        les     bx,pVPBBuf              ; es:bx => VPB buf
        mov     es:[bx],4f42H           ; Put "BOOT" in the buffer
        mov     es:[bx+2],544fH         ;  so the CDROM device driver
                                        ;  will know it is us and not
                                        ;  fail if sector 0 is not
                                        ;  mode 1.  Defect 91985.
    ASSUME  ES:NOTHING
        mov     cx,1
        xor     dx,dx
        mov     di,dx                   ; read just the boot sector
        call    DiskIO                  ; try to get the sector
        pop     bp
        ret
EndProc   ReadBoot

BREAK   <BuildBPB - construct a BPB for a particular device>

;
;   BuildBPB is called to generate a valid BPB for an old disk; one that does
;   not have a 4.0-compatible boot sector.  The newer disks have a KNOWN and
;   VALID BPB in the boot sector.  If the device driver is an old IBM-style
;   driver, we read in the first sector of the first FAT into the VPB buffer.
;   In any case, we pass the buffer to the device driver as part of the BuildBPB
;   call.
;
;   Inputs:     ds:si point to DPB of interest
;               pVPBBuf is locked
;   Outputs:    carry clear =>
;                   ds:si points to a BPB
;               carry set =>
;                   (AX) = status word from device
;                   zero set => nested uncertain media error
;                   zero reset => operation was failed
;   Registers modified: all except BP
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure BuildBPB,NEAR
        PMONLY
        DBPRT   80h,40h,<BuildBPB($x:$x>,<ds,si>

        SaveReg <bp>
        CALLFAR AllocReqPacketWait      ; (ES:BX) = tiled request packet
        push    es
        les     di,[si].dpb_driver_addr
        test    es:[di].SDevAtt,DEV_NON_IBM ; do we need to read in the FAT?
        pop     es
        jnz     bbp1

        mov     al,CMDINPUT
        mov     cx,1
        mov     dx,[si].dpb_first_FAT
; We zero out di since we know that this is for an ancient disk and the
; boot sector will be in the first 64K sectors
        xor     di,di                   ; DI:DX is sector to read
        push    es
        push    bx
        DosContext es
        les     bx,pVPBBuf              ; (ES:BX) = transfer address
        call    DiskIO
        pop     bx
        pop     es
    ASSUME   ES:NOTHING
        LJC     bbp6                    ; CARRY and ZERO flag set correctly

bbp1:   push    ds                      ; Save DPB selector
        DosContext ds
        mov     ax,pVPBBuf.Segmt
        mov     es:[bx].BldBPBbuffer.Segmt,ax
        mov     ax,pVPBBuf.Offst
        mov     es:[bx].BldBPBbuffer.Offst,ax
        pop     ds                      ; Restore DPB selector
    ASSUME   DS:NOTHING
        mov     es:[bx].PktLen,LENBldBPB+PktHeadSize
        mov     al,[si].dpb_unit
        mov     es:[bx].PktUnit,al
        mov     es:[bx].PktCmd,CMDBldBPB
        mov     al,[si].dpb_media
        mov     es:[bx].BldBPBmedia,al
        mov     es:[bx].PktStatus,0

        SaveReg <ds,si>
        lds     si,[si].dpb_driver_addr
        CALLFAR DevIOCall2
        RestoreReg <si,ds>              ; preserve DPB pointer

        mov     ax,es:[bx].PktStatus
        test    ax,STERR                ; was there an error?
        jz      bbp5                    ; nope, return the data! (CLEARS CARRY)

        and     ax,STECODE
        cmp     ax,ERROR_I24_UNCERTAIN_MEDIA
        je      bbp4                    ; (SETS ZERO FLAG)

        mov     bp,CurrTCB              ; (SS:DI) = current TCB
        cmp     ax,ERROR_I24_NOT_READY
        je      bbp3                    ; Drive not ready. Call HE daemon
;
; If the error is "not DOS disk", then we do not put up a hard error pop-up
; since the caller will mount FAT by default onto this volume.
;
        cmp     ax,ERROR_I24_NOT_DOS_DISK
        je      bbp35                   ; Clear zero flag and return Carry.
        test    [bp].TCBModeFlags,fOPENDASD+fLOCKDRV
        jnz     bbp4                    ; Let caller handle error

;       (BP) must equal CurrTCB.

bbp3:   SaveReg <si,ds>
        mov     dl,[bp].TCBAllowed      ; (DL) = old allowed flags
        mov     [bp].TCBAllowed,ALLOWED_FAIL + ALLOWED_RETRY

        mov     cl,[si].dpb_drive       ; drive in error
        lds     si,[si].dpb_driver_addr ; point to dd
        mov     ax,HEPacket
        CALLFAR HardError

        mov     [bp].TCBAllowed,dl      ; restore original allowed flags
        RestoreReg <ds,si>

        cmp     ax,HDERR_RETRY          ; was it a retry?
        LJZ     bbp1                    ; yes, go back for more

;       ZERO FLAG IS CLEAR (operation failed)
;
        mov     ax,es:[bx].PktStatus    ; (AX) = packet status
bbp35:  or      sp,sp                   ; Clear ZERO flag to indicate failure
bbp4:   stc                             ; only choice is fail, set the indicator
        jmp     short bbp6

bbp5:   call    ValidateBPB             ; see if the BPB is in range
        jc      short bbp35             ; bad BPB, return error

bbp6:   pushf
        CALLFAR FreeReqPacket           ; free the tiled request packet
        popf
        RestoreReg <bp>
        DBPRT   80h,40h,<bb)>
        ret
EndProc   BuildBPB

BREAK   <ValidateBPB - validate the fields in the BPB>

;
;   ValidateBPB is called to make sure that the fields in the BPB are
;   within valid ranges.  If they are not, an error is returned.
;
;   Inputs:     ds:si -> DPB
;               es:bx -> request packet
;
;   Outputs:    carry clear =>
;                   ds:si -> BPB
;               carry set =>
;                   (AX) = error code
;                   ds:si -> DPB
;
;   Registers modified: AX, SI, DS, Flags
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure ValidateBPB,NEAR,LOCAL
        PMONLY

        SaveReg <si,ds>

        lds     si,es:[bx].BldBPBpBPB   ; ds:si -> old style BPB
        mov     ax,ds:[si].BPB_BytesPerSector   ; (ax) = bytes per sector
        DosContext <ds>
        cmp     ax,MaxSec               ; Is the sector size > max sector size?
        ja      vbp90                   ; too big, return error to the caller

        mov     ds,es:[bx].BldBPBpBPB.Segmt ; ds:si -> BPB
        pop     ax                      ; clear the stack
        pop     ax
        clc                             ; no error
vbpx:   ret

vbp90:  mov     ax,ERROR_I24_NOT_DOS_DISK   ; this is a bad disk
        RestoreReg  <ds,si>             ; ds:si -> DPB
        stc                             ; error
        jmp     short vbpx              ; go exit

EndProc   ValidateBPB

HIGH2CODE ENDS
        END
