;       SCCSID = @(#)misc.asm   6.5 91/05/08
SCCSID  equ     "@(#)misc.asm   6.5 91/05/08"
TITLE MISC - Miscellaneous routines for MS-DOS
NAME  MISC
;
;
; Miscellaneous system calls most of which are CAVEAT
;
;       Global          Local
;
;                       VerifyLog2
;       SetVPB
;       RetryWait
;                       w_GetDPB
;       w_BufReset
;
;
;   Revisions:
;
;Local  DCR/
;Tag    PTM(R)  Who     When
;               danno   01-Dec-1988
;                   Removed real-mode code, moved some redirector code
;                   from the 3xBox
;
;
.xlist
include dosseg.inc
include basemaca.inc
include osmaca.inc
include fnofs.inc
.xcref
include bpb.inc
include buffer.inc
include curdir.inc
include dirent.inc
include dpb.inc
include error.inc
include fsc.inc
include mi.inc
include mult.inc
include protmode.inc
include sft.inc
include tkexport.inc
include vpb.inc
include proc.inc
include ioexp.inc
.cref
.list
        ModName , FNO_MISC_ASM

        ReferGDT        GDT_VPB,WORD    ; VPB BMP selector

        ReferGlobal     InDos,BYTE
        ReferTask       ProcType,BYTE       ; process type (real or protect)
        ReferTask       ExtErr_Locus,BYTE   ; error location
;### PTR B720732        ReferTask       ThisCDS,DWORD
        ReferTask       CurrTCB,WORD
        ReferTask       JFN_Length,WORD
;### PTR B720732        ReferTask       ptda_pCDS,WORD
        ReferTask       JFN_Flg_Ptr,DWORD       ; pointer to JFN flags


        EXTRNFAR        w_DiskReset
        EXTRNFAR        w_Get_Drive_Freespace
        EXTRNFAR        UnPack
        EXTRNFAR        PhysHandle              ; @@ (@PhysDisk)

        EXTRN16         SFFromSFN

        extrn           WhatVolume:near
        extrn           FSD_Commit:near
        extrn           SFFromHandle:near
        extrn           GetThisDrv:near
        extrn           SFLock:near
        extrn           SFUnlock:near
        extrn           Yield:near

        FARCODE LOW
CODE    SEGMENT
        ASSUME  SS:TASKAREA


;***    VerifyLog2 - get the exponent al
;
;
;       This procedure returns log2 (al) if al is a power of 2,
;       and returns an error otherwise
;
;           if  (al == 0)
;               Exit with Failure
;           ah = 0
;       loop:
;           shift al right
;           if carry set the right most bit from al has been found
;               goto endloop
;           ah = ah + 1
;           goto loop
;
;       endloop:
;           if  al != 0 more than one bit was set and al was not a power of 2
;               Exit with Failure
;           else
;               Exit with Success
;
;
;       ENTRY   al - expected to be a power of 2
;
;       EXIT    if carry clear => ah = n where 2^n == al
;               if carry set  => ERROR, al was invalid
;
;       USES    ax

Procedure   VerifyLog2,NEAR,LOCAL
        or      al,al
        jz      vfl30
        xor     ah,ah
vfl10:  shr     al,1
        jc      vfl20
        inc     ah
        jmp     short vfl10
vfl20:  jnz     vfl30
        clc
        ret
vfl30:  stc
        ret
EndProc     VerifyLog2

;***    SetVPB - Fill in VPB fields from BPB.
;
; Generate VPB fields from BPB.  This is used at system initialization time and
; at format time.
;
;       ENTRY   es:di points to VPB
;               ds:si is pointer to BPB
;
;       EXIT    if not carry =>  es:di still points to VPB
;               if carry => ax = ERROR_GENERAL_FAILURE
;
;       USES    ax, bx, cx, dx
;
Procedure   SetVPB,HYBRID

        SaveReg <ds,si>

.errnz  (BPB_BytesPerSector)
        lodsw                           ; (ax) = bytes per sector
        cmp     ax,SIZE dir_entry       ; Can a sector hold a directory?
        jb      stvp10                  ;   NO

.errnz  (BPB_SectorsPerCluster - BPB_BytesPerSector - 2)
        lodsb                           ; (al) = sectors per cluster (2^n)
        call    VerifyLog2              ; Is al a power of 2?
        jc      stvp10                  ;   NO

.errnz  (BPB_ReservedSectors - BPB_SectorsPerCluster - 1)
        lodsw                           ; (ax) = # of reserved sectors
        or      ax,ax                   ; Is # of sectors > 0?
        jz      stvp10                  ;   NO

.errnz  (BPB_NumberOfFATs - BPB_ReservedSectors - 2)
        inc     si

.errnz  (BPB_RootEntries - BPB_NumberOfFATs - 1)
        lodsw                           ; (ax) = # root entires
.errnz  (BPB_TotalSectors - BPB_RootEntries - 2)
        lodsw                           ; validity checked later
.errnz  (BPB_MediaDescriptor - BPB_TotalSectors - 2)
        lodsb                           ; (ax) = Media descriptor
.errnz  (BPB_SectorsPerFAT - BPB_MediaDescriptor - 1)
        lodsw                           ; (ax) =  # sectors per FAT
        or      ax,ax                   ; Is the total # sectors per FAT > 0?
        jz      stvp10                  ;   NO

.errnz  (BPB_SectorsPerTrack - BPB_SectorsPerFAT - 2)
        lodsw                           ; (ax) = # sectors per track
        or      ax,ax                   ; Is the # sectors per track > 0?
        jz      stvp10                  ;   NO

        RestoreReg  <si,ds>
        jmp     short stvp20

stvp10: pop ax                          ; clear the stack
        pop ax
stvp15: mov     ax,ERROR_GEN_FAILURE
        stc
        jmp     stvpx

stvp20: lodsw                           ; (ax) = bytes per sector
        mov     es:[di.vpb_fsi.vpi_cbSector],ax
        mov     dx,ax                   ; (dx) = bytes per sector

        lodsb                           ; (al) = sectors per cluster (2^n)
        dec     al
        mov     es:[di.vpb_fsd.vpdFAT_cluster_mask],al
        inc     al
        call    VerifyLog2              ; ah = exponent of al
        jc      stvp15                  ; al was not a power of 2

        mov     es:[di.vpb_fsd.vpdFAT_cluster_shift],ah
        mov     bl,ah                   ; (bl) = cluster shift

        lodsw                           ; (ax) = # of reserved sectors
        mov     es:[di.vpb_fsd.vpdFAT_first_FAT],ax

        lodsb                           ; (al) = # of FATs
        mov     es:[di.vpb_fsd.vpdFAT_FAT_count],al
        mov     bh,al                   ; (bh) = # of FATs

        lodsw                           ; (ax) = # of dir entries in root
        mov     es:[di.vpb_fsd.vpdFAT_root_entries],ax
.errnz  (32 -  SIZE dir_entry)          ; 5 = Log2(32)
        shr     dx,5                    ; Directory entries per sector
        dec     ax
        add     ax,dx                   ; Cause Round Up
        mov     cx,dx
        xor     dx,dx
        div     cx
        mov     cx,ax                   ; (cx) = number of directory sectors

        lodsw                           ; (ax) = # sectors
        mov     es:[di.vpb_fsd.vpdFAT_max_cluster],ax ; max_cluster = # sectors (temp)

        lodsb                           ; (al) = media descriptor
        mov     es:[di.vpb_fsd.vpdFAT_media],al

        lodsw                           ; (ax) = # sectors per FAT
        mov     es:[di.vpb_fsd.vpdFAT_FAT_size],ax
                                        ; (ds:si) -> BPB_SectorsPerTrack
        push    bx
        xor     bl,bl
        xchg    bl,bh                   ; (bx) = # of FATs
        mul     bx                      ; (dx:ax) = # of sectors for all FATs
        add     ax,es:[di.vpb_fsd.vpdFAT_first_FAT]
        adc     dx,0                    ; (dx:ax) = sector # of root dir
        mov     word ptr es:[di.vpb_fsd.vpdFAT_dir_sector],ax
        mov     word ptr es:[di.vpb_fsd.vpdFAT_dir_sector]+2,dx
        pop     bx                      ; (bh) = # of FATs, (bl) = cluster shift

        add     ax,cx                   ; Add number of directory sectors
        adc     dx,0                    ; (dx:ax) = first sector # for files
        mov     word ptr es:[di.vpb_fsd.vpdFAT_first_sector],ax
        mov     word ptr es:[di.vpb_fsd.vpdFAT_first_sector]+2,dx

        push    dx                      ; save first sector # high

        mov     cx,bx
        xor     ch,ch                   ; (cx) = cluster shift
; DS:SI -> BPB_SectorsPerTrack
        SaveReg <AX>                    ; Save starting sector of data area
        LODSW                           ; AX = BPB_SectorsPerTrack
        mov     es:[di.vpb_fsi.vpi_trksec],AX
        LODSW                           ; AX = BPB_Heads
        mov     es:[di.vpb_fsi.vpi_nhead],AX
        RestoreReg <AX>                 ; AX = starting sector of data area
; DS:SI -> BPB_HiddenSectors
        mov     bx,es:[di.vpb_fsd.vpdFAT_max_cluster] ;(bx) = # sectors (temp max_cluster)
        xor     dx,dx                   ; (dx:bx) = # sectors
        or      bx,bx                   ; (bx == 0) => big partition
        jnz     stvp30                  ; We are on a <32M volume

; Get total sectors in volume from extended BPB
        mov     bx,[si][BPB_BigTotalSectors-BPB_HiddenSectors].LowWord
        mov     dx,[si][BPB_BigTotalSectors-BPB_HiddenSectors].HighWord
        or      bx,bx                   ; Is BigTotalSectors > 0?
        jnz     stvp30                  ;   YES
        or      dx,dx                   ; Is BigTotalSectors > 0?
        jnz     stvp30                  ;   YES
        pop     ax                      ; clear stack
stvp15j:jmp     stvp15                  ; exit with error

stvp30:                                 ; (dx:bx) = # sectors on volume
        MOV     word ptr es:[di.vpb_fsi.vpi_totsec],BX
        MOV     word ptr es:[di.vpb_fsi.vpi_totsec]+2,DX
        sub     bx,ax                   ; minus first sector number low
        pop     ax                      ; (ax) = first sector # high
        sbb     dx,ax                   ; (dx:bx) = # of sectors in data area
        jcxz    stvp50

; Divide by sectors/cluster by shifting cluster_shift times

stvp40: shr     dx,1
        rcr     bx,1
        loop    stvp40

stvp50: add     bx,1                    ; (bx) = # of clusters in data area
                                        ; INC does not affect CARRY
; use 'adc' below so we catch error if carry OR if dx != 0 from shifting
        adc     dx,0                    ; (dx:bx) = # of clusters in data area
        jnz     stvp51                  ; too big, use max clusters
        cmp     bx,0FFF5h               ; is cluster number bigger than allowed?
        jbe     stvp52                  ; no, go store it

stvp51: mov     bx,0FFF5h               ; (bx) = max cluster allowed

stvp52:
        mov     es:[di.vpb_fsd.vpdFAT_max_cluster],bx

;       Compute size of fat entries, and save away in VPB.      BMW 87Jan16
;       If (vpb_fsd.vpdFAT_max_cluster < (4096-10)) then 12bit else 16bit

        mov     cx,12                   ; Assume 12 bit fat
        mov     ax,00FF8h               ; (ax) = 12-bit FAT minimum EOF value
        cmp     bx,4096-10              ; 12 or 16 bit fat?
        jb      stvp60                  ; 12 bit, go save it

        mov     cx,16                   ; 16 bit fat
        mov     ax,0FFF8h               ; (ax) = 16-bit FAT minimum EOF value
stvp60: mov     es:[di.vpb_fsd.vpdFAT_FATentrysize],cl  ; Save fat entry size
        mov     es:[di].vpb_fsd.vpdFAT_minEOF,ax    ; Set minimum EOF value
        mov     es:[di.vpb_fsd.vpdFAT_next_free],0  ; Init so first ALLOC starts at
                                        ; begining of FAT
        mov     es:[di.vpb_fsd.vpdFAT_free_cnt],-1  ; current count is invalid.
        clc
stvpx:  ret

EndProc SetVPB
;                                                                          ;
;            C  A  V  E  A  T     P  R  O  G  R  A  M  M  E  R             ;
;----+----+----+----+----+----+----+----+----+----+----+----+----+----+----;


Break   <RetryWait - wait for a specified amount of time>

;**     RetryWait - Wait for a bit before retrying a lock/sharing violation.
;
;       When retrying an operation due to a lock/sharing violation, we
;       reschedule the CPU.
;
;       ENTRY   none
;       EXIT    after a while
;       USES    Flags

Procedure RetryWait,<near,far16,faronly>
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA
        push    bp
        mov     bp,CurrTCB
        test    [bp].TCBfSharing,-1
        pop     bp
        jnz     short rtwx
        pusha
        push    ds
        push    es
        call    Yield                   ; let somebody else run
        pop     es
        pop     ds
        popa
rtwx:   RETP
EndProc RetryWait


Procedure Far_SetBad,FAR,local  ; Default redirector dispatch routine
        call    SetBad
        ret     2               ; remove function argument
EndProc   Far_SetBad

;
; SetBad sets up info for bad functions
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:TASKAREA

Procedure SetBad,NEAR,local
        mov     ax,ERROR_UNC_DRIVER_NOT_INSTALLED   ; returned for net requests
        cmp     ProcType,PT_REALMODE        ; Is this a VDM
        jne     stb10                       ;   NO! continue

; NOTE: For compatability reasons, invalid function is returned to the
; 3xbox.  microsoft!philipl (206)867-3127

        mov     ax,ERROR_INVALID_FUNCTION   ; returned for VDM net requests
stb10:  mov     ExtErr_Locus,ERRLOC_UNK
        stc
        ret
EndProc   SetBad


;**     Redir_Dispatcher - call off to the redirector for service
;
;       Redir_Dispatcher is used to transfer control to the installed
;       redirector.  Routines in the DOS will load up all relevant registers,
;       push the function requested of the redirector and call us.  All we
;       do is jump indirect through the redirector dispatch address.
;
;       This address is initialized to point to a general "failing" type of
;       routine (SetBad) that will set up all conditions to indicate a failure
;       of the service.
;
;       The redirector (at initialization) may elect to fill in this address
;       to point to his own handler.  Magically, he'll get control and be able
;       to properly service the request.
;
;       ENTRY   registers appropriate to function
;               pushed word of function number on stack
;               pushed far return address
;       EXIT    as appropriate to function
;       USES    as appropriate to function
;

        PUBLIC  RedirDispAddr
RedirDispAddr   dd  Far_SetBad  ; Address of the redirector function dispatcher

Procedure   Redir_Dispatcher,FAR
        assume  ds:nothing, es:nothing, ss:nothing
        jmp     CS:RedirDispAddr
EndProc Redir_Dispatcher


;**     Far_RetCC - clear carry and execute a far ret.
;

Procedure Far_RetCC,FAR
        clc
        ret     2                       ; Clean off function code
EndProc   Far_RetCC

CODE    ENDS


        FARCODE HIGH2
HIGH2CODE   SEGMENT

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


BREAK <w_BufReset -- update directory entry>
;***    w_BufReset - Update directory entries
;
;       We are commanded to update the disk.  We will do this for a single
;       file handle or for the entire set of file handles for the current
;       process
;
;       ENTRY   (bx) has file handle to commit or 0FFFFh for all handles
;                   on system.
;
;       EXIT    (ax) has error code.
;
;       USES    all

Procedure w_BufReset,hybrid
    ASSUME DS:NOTHING, ES:NOTHING, SS:TASKAREA
;       PMONLY
        LocalVar    jfn,WORD            ; current handle being committed
        LocalVar    jfnMax,WORD         ; maximum jfn to commit
        LocalVar    jfnFlag,WORD        ; == 1 if for all handles
                                        ; == 0 for specific buf reset
        EnterProc
        mov     ax,FS_COMMIT_ONE        ; for single file
        cmp     bx,-1                   ; do we commit all open handles?
        jne     wbr1                    ; no, go use handle as start and max
        mov     ax,FS_COMMIT_ALL        ; flag = 2 for all handles
        mov     jfn, 0                  ; start with the first one
        mov     bx,JFN_Length           ; get number of handles
        dec     bx                      ; last valid = number - 1
        mov     jfnMax, bx              ; put in terminating jfn number
        jmp     short wbr1a             ; go start commit loop

;       commit only a single file.  Test to see if handle is valid

wbr1:   mov     jfn,bx                  ; starting one is specified one
        mov     jfnMax,bx               ; ending jfn = starting
wbr1a:  mov     jfnFlag,ax              ;
;       commit-file loop - commit until an error or until jfns exhausted

wbr2:   mov     bx,jfn                  ; get current handle to commit
        cmp     bx,jfnMax               ; are we done yet?
        ja      wbr8                    ; yes, go return success

        TaskContext DS                  ; address PTDA
        call    SFFromHandle            ; convert to a sft pointer
        jc      wbr4                    ; error, check jfnFlag and take action

;(@PhysDisk start)    check for physical disk handle

        CALLFAR PhysHandle              ; do we have a physical disk handle?
        jc      wbr4                    ; if phys disk handle - skip this handle

;(@PhysDisk end)

        push    bp                      ; save frame pointer
        push    jfnFlag                 ; Push COMMIT type (before BP gets trashed)
        mov     bp,CurrTCB
        mov     [bp].TCBThisSFT.Offst,di
        mov     [bp].TCBThisSFT.Segmt,es ; point ThisSFT at SFT to commit
        call    SFLock                  ; lock SFT entry
        jc      wbr10
                                             ; D132324
        mov     al,TASKAREA:[bp].TCBJfnFlag  ; note that the JFN_Flg_Ptr table
        or      al,JFN_FLUSH                 ; is not being updated currently
        mov     TASKAREA:[bp].TCBJfnFlag,al  ; if it is required, update it

        pop     ax                      ; commit type in ax
        call    FSD_Commit              ; update file

; restore ds
        TaskContext DS                  ; address PTDA

        mov     bp,CurrTCB
        mov     al,TASKAREA:[bp].TCBJfnFlag
        and     al,NOT JFN_FLUSH
        mov     TASKAREA:[bp].TCBJfnFlag,al

    assume ds:nothing,es:nothing

        mov     bp,CurrTCB
        les     di,[bp].TCBThisSFT

; es:di -> SFT

wbr2a:  call    SFUnlock                ; Unlock SFT entry

        pop     bp                      ; restore frame pointer
        jc      wbr9                    ; error => go return it to caller

wbr3:   inc     jfn                     ; advance to next sfn
        jmp     wbr2

wbr4:   cmp     jfnFlag,FS_COMMIT_ALL   ; is this call for all files
        je      wbr3                    ; then ignore error
        stc                             ; to indicate error
        jmp     short wbr9              ; if single handle, error

wbr8:   xor     ax,ax                   ; no errors for this call
wbr9:   LeaveProc
        ret

; The sft vanished by the time we could lock it; cleanup and
; return INVALID_HANDLE error
wbr10:  pop     ax
        mov     ax,ERROR_INVALID_HANDLE ; CY already set; ES:DI -> SFT
        jmp     short wbr2a             ; TOS = BP to be popped

EndProc w_BufReset


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

;***    w_GetDPB - Return pointer to DPB for default drive
;
;       WARNING: This routine is used ONLY by DemGetDPB
;
;       Fill in a static buffer with disk information in the form of
;       a DOS 3.2 DPB.
;
;       ENTRY:  DL = drive number
;               ES:DI = FAKE DPB
;
;       EXIT:   (AL) =  0, success
;                    FAKE DPB is filled with required values
;               (AL) = -1, bad drive (call 50 only)

Procedure   w_GetDPB,<hybrid,far16>

LocalVar wdpb_dummycds,<SIZE curdir>
        EnterProc


        TaskContext <DS>
        PUSH    ES                      ; Save add. of fake DPB
        PUSH    DI

        LEA     di,wdpb_dummycds
        push    si
        mov     si,TASKAREA:[CurrTCB]                   ;### PTR B720732
        mov     TASKAREA:[si].TCBpTmpCDS.Offst,di       ; Initialize TCBpTmpCDS ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pCDS],di ; Initialize ptda_pCDS
        pop     si

        MOV     AL,DL
        call    GetThisDrv              ; Get CDS structure
        JNC     SHORT gd2                     ; Drive is valid

gd1:    mov     AL,-1                   ; Indicate failure
        POP     DI
        POP     ES
        jmp     gd5

gd2:    lea     DI,wdpb_dummycds        ; check for net CDS
        TEST    ss:[DI.cd_flags],CD_ISNET
        JNZ     gd1                     ; No DPB to point at on NET stuff
        lds     si,ss:[di].cd_devptr    ; point at DPB
        assume  ds:nothing
        call    WhatVolume              ; figure out what volume is there
        JC      gd1                     ; User FAILed to I 24, only error we
                                        ;   have.
        TaskContext <ds>
        lea     si,wdpb_dummycds
        les     di,ss:[si].cd_devptr    ; (ES:DI) = DPB
        POP     SI                      ; (DS:SI) is fake DPB
        POP     DS

;*  Copy fields from real DPB to fake DPB

        mov     al,es:[di].dpb_drive
        mov     ds:[si].dpb4x_drive,al

        mov     al,es:[di].dpb_unit
        mov     ds:[si].dpb4x_UNIT,al

        mov     ax,es:[di].dpb_first_FAT
        mov     ds:[si].dpb4x_first_FAT,ax

        mov     WORD PTR ds:[si].dpb4x_driver_addr,0  ; Bad device driver address?
        mov     WORD PTR ds:[si].dpb4x_driver_addr+2,0

        mov     al,es:[di].dpb_media
        mov     ds:[si].dpb4x_media,al

        mov     WORD PTR ds:[si].dpb4x_next_dpb,-1
        mov     WORD PTR ds:[si].dpb4x_next_dpb+2,-1


;*  Now we need to copy the information from the VPB that is needed in the DPB.
;
;   Each of the fields is copied one at a time, even though this is
;   expensive.  Setting up for several rep movsbs wouldn't save much code, and
;   it would be dependent upon the layout of each of the structures.

        mov     di,es:[di].dpb_hVPB     ; (DI) = VPB handle
        VPBContext ES,DI                ; (ES:DI) -> VPB

        mov     ax,es:[di].vpb_fsi.vpi_cbSector
        mov     ds:[si].dpb4x_sector_size,ax

        mov     al,es:[di].vpb_fsd.vpdFAT_cluster_mask
        mov     ds:[si].dpb4x_cluster_mask,al

        mov     al,es:[di].vpb_fsd.vpdFAT_cluster_shift
        mov     ds:[si].dpb4x_cluster_shift,al

        mov     al,es:[di].vpb_fsd.vpdFAT_FAT_count
        mov     ds:[si].dpb4x_FAT_count,al

        mov     ax,es:[di].vpb_fsd.vpdFAT_root_entries
        mov     ds:[si].dpb4x_root_entries,ax

        mov     ax,word ptr es:[di].vpb_fsd.vpdFAT_first_sector
        mov     ds:[si].dpb4x_first_sector,ax

        mov     ax,es:[di].vpb_fsd.vpdFAT_max_cluster
        mov     ds:[si].dpb4x_max_cluster,ax

;;; BUGBUGBUG This will break for 3.x programs that use a BYTE for FAT size!!
        mov     ax,es:[di].vpb_fsd.vpdFAT_FAT_size
        mov     ds:[si].dpb4x_FAT_size,ax

        mov     ax,word ptr es:[di].vpb_fsd.vpdFAT_dir_sector
        mov     ds:[si].dpb4x_dir_sector,ax

        mov     al,es:[di].vpb_first_access
        mov     ds:[si].dpb4x_first_access,al

        mov     ax,es:[di].vpb_fsd.vpdFAT_next_free
        mov     ds:[si].dpb4x_next_free,ax

        mov     ax,es:[di].vpb_fsd.vpdFAT_free_cnt
        mov     ds:[si].dpb4x_free_cnt,ax

        xor     AL,AL                   ; Indicate success

;### PTR B720732 gd5:    MOV     TASKAREA:[ptda_pCDS],0
gd5:    LeaveProc
        RETP

EndProc     w_GetDPB


;***    VPBContext32 -- 16-bit interface for VPBContext from 32-bit asm code
;
;       This routine simply does a VPBContext on es:di.  It is needed
;       because a far call is not properly generated to chkvselhdprc from
;       DOSHIGH32CODE by the VPBContext macro.  So the routine ends up
;       running as 32-bit code inside of DOSHIGH2CODE, and fails.
;
;       ENTRY:  di = hVPB
;
;       EXIT:   es:di -> VPB
;
;       USES: es
;
Procedure VPBContext32,<far16,faronly>

        VPBContext es,di
        RETP

EndProc VPBContext32

HIGH2CODE   ENDS

CpuMode 386

        FARCODE HIGH32
HIGH32CODE SEGMENT

;***    _IOGetMediaInfo -- determine whether a file in on removable media
;
;       _IOGetMediaInfo is used to determine whether a file resides on
;       removable media and whether the driver controlling the device on
;       which the media is mounted can handle non-contiguous memory.  A flags
;       byte is filled in with this info.
;
;       MEDIA_NON_CONTIG_MEM set means driver can handle non-contigous memory.
;       MEDIA_NON_REMOVABLE set means file is on non-removable media.
;
;       This routine is 32-bit and C-Callable.
;
;
;       ENTRY:  _IOGetMediaInfo(SFN,pMediaInfo)
;                       DWORD SFN               ; SFN of file in question
;                       UCHAR *pMediaInfo       ; where to return info
;
;       EXIT:   eax = 0: no error, pMediaInfo set as described above.
;               eax!= 0: return code, SFN is invalid
;
;       USES:   eax, flags
;
;
;       ATTENTION: Currently there is no interface to device drivers to
;               determine whether they can handle non-contiguous memory.
;               The FAT fs does not require contiguous memory, so we set
;               MEDIA_NON_CONTIG_MEM if the file is a FAT file, otherwise
;               it is always cleared.
;
;
;       PSEUDOCODE:
;
;       pSFT = SFFromSFN(SFN)
;       if(error) return(ERROR)
;       if(file is remote file) {               //examine SFT flags
;               Info = MEDIA_NON_REMOVABLE
;       } else if(file is device or pipe) {     //examine SFT flags
;               return(ERROR)
;       } else {
;               pVPB = VPBContext(pSFT->hVPB)
;               pDPB = pVPB->DPB
;               Info = (pDPB->flags) AND DPBNONREMOV
;       }
;       Info |= (file is FAT file)*MEDIA_NON_CONTIG_MEM
;       *pMediaInfo = Info
;       return(NO_ERROR)
;


Procedure _IOGetMediaInfo
        ASSUME  DS:FLAT, ES:NOTHING     ; es will address DOS structures

ArgVar   iogmi_sfn,DWORD                ; sfn
ArgVar   iogmi_pInfo,DWORD              ; where to put info about the media

LocalVar iogmi_FAT,BYTE                 ; whether file is (local) FAT file


        EnterProc <0>                   ; assume not (local) FAT file
        SaveReg <es,edi,ebx>            ; preserve C registers


; First get pointer to sft from SFN supplied by caller

        mov     ebx,iogmi_sfn           ; (e)bx = SFN
        CALL16  SFFromSFN               ; on return, es:di -> sft
        jc      short iogmie1
        movzx   edi,di                  ; es:edi -> sft


; Remote files do not have DPBs.  Devices and pipes do not have VPBs.  So
; we filter out these cases before we walk the sft-vpb-dpb chain.

        movzx   eax,es:[edi].sf_flags

        test    ax,(SF_PIPE OR DEVID_DEVICE)    ; is this a file?
        jnz     short iogmie2                   ; no - error

        mov     al,MEDIA_NON_REMOVABLE          ; assume non removable
        test    es:[edi].sf_flags,SF_ISNET      ; remote file?
        jnz     short iogmi2                    ; yes, assumption was correct


; We have a local file.
; look at vpi_flags to determine whether the FSD can handel non_contig memory

        movzx   edi,es:[edi].sf_fsi.sfi_hVPB
        CALL16  VPBContext32

        test    es:[edi].vpb_fsi.vpi_flags,VPB_NONCONTIG_ALLOWED
        jz      short iogmi0

        mov     iogmi_FAT,MEDIA_NON_CONTIG_MEM  ; indicate FAT file


; There is no DPB for a remote file.  The test above should have caught this
; case, so if we get here and there is no DPB, panic.

iogmi0: test   es:[edi].vpb_fsi.vpi_flags,VPB_ABOVE16M_ALLOWED
        jnz     short iogmi1
        or      iogmi_FAT,MEDIA_16M

iogmi1:

IFDEF FSSTRICT
        cmp     es:[edi].vpb_fsi.vpi_pDPB,0     ; null dpb pointer?
        je      short iogmip                    ; yes, panic
ENDIF


; Address the DPB.

iogmi10:
        les     di,es:[edi].vpb_fsi.vpi_pDPB    ; es:di -> dpb
        mov     al,es:[edi].dpb_flags           ; al 0 if removable
.errnz  (DPBNONREMOV - MEDIA_NON_REMOVABLE)
        and     al,MEDIA_NON_REMOVABLE          ; isolate removable media flag


; AL:  contains info about removability of media.

iogmi2:
        or      al,iogmi_FAT                    ; set NON_CONTIG_MEM if FAT file
        mov     edi,iogmi_pInfo                 ; FLAT:edi -> where to put info
        mov     ds:[edi],al                     ; store info about media
        xor     eax,eax                         ; indicate no error

iogmix:
        RestoreReg <ebx,edi,es>                 ; restore C registers
        LeaveProc
        RETC

iogmie1:
        mov     eax, ERROR_INVALID_HANDLE       ; Invalid SFT (ldr should panic)
        jmp     short iogmix

iogmie2:
        mov     eax, ERROR_NOT_SAME_DEVICE      ; Device error
        jmp     short iogmix

IFDEF FSSTRICT
iogmip:
        Panic   <'_IOGetMediaInfo: pDPB is null'>
ENDIF


EndProc _IOGetMediaInfo


HIGH32CODE ENDS
        END
