;       SCCSID = @(#)ldrio.asm  6.14 92/03/20
        SCCSID equ "@(#)ldrio.asm       6.14 92/03/20"

        PAGE    ,132
TITLE   LDRIO - Loader I/O interface
NAME    LDRIO

;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;
;       These routines create an interface between the loader and
;       the file system.
;
;       If _IsMiniIFS is set :
;               1) ldrOpen, ldrClose are NOPs.
;               2) ldrTransPath returns a null string.
;               3) ldrRead reads from a predetermined area of memory
;                  which contains the loadable image of the mini-IFS.
;
; HISTORY
;
; 72417   MLM  11/27/95  - moved from PTDA to TCB: PathNameType
;

.xlist
include dosseg.inc
include basemaca.inc
include osmaca.inc
include ostypes.inc
include sft.inc
include mi.inc
include error.inc
include dossymc.inc
include sigdef.inc
include except.inc
include argframe.inc
include osconst.inc
include ldrvars.inc
include pgexport.inc
include vmexport.inc
include selxport.inc
include tkexport.inc
include infoseg.inc
include curdir.inc
include dirent.inc
include find.inc
include screen.inc
include sem.inc
include tkptda.inc
include filemode.inc
include opncreat.inc
include ioexp.inc
include filesys.inc
include fnomem.inc
.list
        CpuMode 386
        ModName ldrio,FNO_LDRIO_ASM

        ReferGlobal     _IsMiniIFS,BYTE
        ReferGlobal     mifs_ptr,DWORD

        EXTRN           _TKLibiRecordMTE:near
        ReferFlat       _pldrOpen,dword
        ReferFlat       _pldrReadAt,dword
        ReferFlat       _pldrClose,dword
        ReferFlat       _pIOSftTransPath,dword
        ReferFlat       _pldrGetMedia,dword
        ReferFlat       _ldrMiniFileIOFlag,BYTE
        ReferFlat       _ldrfromLdrRun,BYTE
ifdef PMDEBUG
        ReferFlat       _pDebugLoadSymDosFS,dword
        ReferFlat       _pDebugLoadSymModule,dword
        EXTRN           _DebugLoadSymMTE:near
endif
        ReferFlat       _pTCBCur,dword
        ReferFlat       _pPTDACur,dword
        ReferFlat       _ldrpFileNameBuf,dword
ifdef SECURITY
        ReferFlat       _fSec,byte                                        ;@SEC
endif

        EXTRN16         AdjustError
        EXTRN16         StrLen32

        EXTRN           _TKSleep:near
        EXTRN           _TKWakeup:near

        EXTRN           _IOSftOpen:near         ; from sftio.asm
        EXTRN           _IOSftClose:near
        EXTRN           _IOSftReadAt:near
        EXTRN           _IOSftTransPath:near
ifdef PMDEBUG
        EXTRN           DebugLoadSymDosFS_Stub:near
        EXTRN           DebugLoadSymModule:near
endif


        EXTRN           _IOGetMediaInfo:near    ; from misc.asm

        EXTRN32         _KSEMRequestMutex
        EXTRN32         _KSEMReleaseMutex
        EXTRN32         _TKTCYield
ifdef SECURITY
        EXTRN32         _SecLdrOpen                                       ;@SEC
endif

;
;   Beginning of initialization code
;

        FARCODE HIGH32
CINIT_TEXT      SEGMENT

;***LP  LDRSetMiniFileIO - Set low level loader IO routines to MINI file system.
;
;       This routine is called to setup the low level loader IO routines to
;       use the MINI file system during boot time to load the base device
;       drivers.
;
;
;       ENTRY   pRoutine                - pointer to the IO routines
;
;
;       EXIT    the following low level loader IO routines setup.
;               _pldrOpen
;               _pldrReadAt
;               _pldrClose
;               _piosftTransPath
;               -pldrGetMedia
;               _pDebugLoadSymDosFS
;               _pDebugLoadSymModule
;
;
;
;       USES    EAX, EBX
;

        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING
Procedure LDRSetMiniFileIO,near,,,esp
ArgVar  pRoutines,DWORD

        mov     eax,[pRoutines]                 ; Get open routine before push

        push    ds
        FlatContext ds,ebx                      ; don't trash eax

        mov     _ldrMiniFileIOFlag,TRUE         ; indicate we are using it
        mov     ebx,[eax]
        mov     [_pldrOpen],ebx
        mov     ebx,[eax+4]                     ; Get close routine
        mov     [_pldrClose],ebx
        mov     ebx,[eax+8]                     ; Get read routine
        mov     [_pldrReadAt],ebx
        mov     ebx,[eax+12]                    ; Get read at routine
        mov     [_pIOSftTransPath],ebx
        mov     ebx,[eax+16]
        mov     [_pldrGetMedia],ebx
ifdef PMDEBUG
        mov     ebx,[eax+20]
        mov     [_pDebugLoadSymDosFS],ebx
        mov     ebx,[eax+24]
        mov     [_pDebugLoadSymModule],ebx
endif

        pop     ds
        RETC
EndProc LDRSetMiniFileIO


;***LP  LDRUnSetMiniFileIO - Reset loader IO routines to file system.
;
;       This routine is called to Reset the low level loader IO routines to
;       use the file system.
;
;       ENTRY   none
;
;
;       EXIT    the following low level loader IO routines reset to filesys
;               _pldrOpen
;               _pldrReadAt
;               _pldrClose
;               _piosftTransPath
;               _pldrGetMedia
;               _pDebugLoadSymDosFS
;               _pDebugLoadSymModule
;
;       USES    EAX
;

        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING
Procedure LDRUnSetMiniFileIO,far32
        push    ds
        FlatContext     ds,eax

        mov     _ldrMiniFileIOFlag,FALSE        ; we are no longer using it
        mov     [_pldrOpen],offset FLAT:_IOSftOpen
        mov     [_pldrClose],offset FLAT:_IOSftClose
        mov     [_pldrReadAt],offset FLAT:_IOSftReadAt
        mov     [_pIOSftTransPath],offset FLAT:_IOSftTransPath
        mov     [_pldrGetMedia],offset FLAT:_ldrGetMedia
ifdef PMDEBUG
        mov     [_pDebugLoadSymDosFS],offset FLAT:DebugLoadSymDosFS_Stub
        mov     [_pDebugLoadSymModule],offset FLAT:DebugLoadSymModule
endif
        pop     ds
        RETC
EndProc LDRUnSetMiniFileIO

CINIT_TEXT ENDS


;
;   Beginning of swappable code
;

        FARCODE HIGH32
SWAP32_8       segment

;***LP  ldrOpen - Open file for loader
;
;       ldrOpen provides an interface to the file system from
;       the loader to open an Executable file.
;
;       ldrOpen takes a module name, and if NO_ERROR, returns :
;
;       1) The ASCIIZ Canonical form of the filename at ldrFileNameBuf
;       2) The SFN of the newly opened file in *pSFN
;       3) The Media Type for the file : (2 bits)
;               Continiguity required for IO to this device.
;               Discardable Segments Allowed.
;
;       ULONG ldrOpen (ULONG pSFN, UCHAR *pchModName, ULONG pflMedia);
;
;       ldrOpen does NOT allocate a JFN, as this would create
;       problems in applications, such as not being able to load a
;       module or exec a program when the application has allocated
;       all it's JFNs, and breaking the dense file handle space.
;
;       ENTRY
;               pSFN            - pointer to location to return SFN
;               pchModName      - Non-Canonical ASCIIZ Path of module
;               pflMedia        - Address to store Media bits
;
;               ldrOpen uses ldrFileNameBuf implicitly
;
;       EXIT
;               The Return Value is held in EAX on exit
;
;               if Return Value == NO_ERROR
;                       *pchModName unchanged
;                       *ldrFileNameBuf = Canonicalized filename
;                       *pSFN = The SFN of the newly opened file
;                       *pflMedia = The Media for the file :
;                               MTE_MEDIA_CONTIG
;                               MTE_MEDIA_DISCARD
;
;               else
;                       Error during open
;                       Return Value = ERROR_CODE, (to be propagated)
;                       *pchModName unchanged
;                       *pflMedia, *pSFN, and ldrFileNameBuf are undefined
;
;       EFFECTS
;               ldrOpen uses ldrFileNameBuf implicitly
;
;       USES            EAX, ECX, EDX, EFlags
;
;       Pseudocode :
;
;       if (_IsMiniIFS)
;               Set up valid return information
;               return
;       IOSftOpen(pchModName, FileMode, OpenFlag, pSFN, SSToDS(&tcbsavbuf));
;       Determine DISCARDABLE and CONTIGUOUS bits using LdrMedia
;       ret
;
        ASSUME DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _ldrOpen,near

        ArgVar  pSFN,DWORD
        ArgVar  pchModName,DWORD
        ArgVar  pflMedia,DWORD

        EnterProc

        xor     eax,eax
        cmp     [_IsMiniIFS],al                 ; Nothing to do if IFS
        jne     short lopnx

        push    eax                             ; use ptda loader buffer
        PushP   pSFN
        PushP   <exist_open + nexist_fail>      ; Open, fail if it doesn't exist
        PushP   <sharing_deny_write + open_no_inherit + open_for_exec>
        PushP   pchModName
        CCALL32 [_pldrOpen],,,PushP
        or      eax,eax
        jnz     short lopne1

ifdef SECURITY
        cmp     _fSec,0                                                   ;@SEC
        jnz     lopne3                                                    ;@SEC
lopn3:                                                                    ;@SEC
endif

;       Determine media bits.

        mov     eax,pSFN
        movzx   eax,word ptr [eax]              ; eax = SFN
        mov     ecx,[pflMedia]
        CCALL32 [_pldrGetMedia]
        or      eax,eax
        jnz     short lopne2

        mov     ecx,_pPTDACur                   ; address the PTDA

        mov     ecx,[ecx].pPTDAExecChild        ; get the child PTDA
        jecxz   lopn7                           ; are we exec'ing a child?
        jmp     short lopn8                     ; yes, jmp to check if dbg'n
lopn7:  mov     ecx,_pPTDACur                   ; no, re-address the PTDA
lopn8:  mov     ecx,[ecx].ptda_flDbgEnabled     ; Get debug enabled flag
        jecxz   lopnx                           ; if no debug flag set, we leave
        mov     ecx,[pflMedia]                  ; debug flags! pretend we are
        and     [ecx], NOT MTE_MEDIAFIXED       ; on a diskette or something.


lopnx:  LeaveProc
        RETC


lopne1: ; open of the file failed

        ; pass back SFN_NULL as the file handle if the open failed
        mov     ecx,pSFN
        mov     word ptr [ecx],SFN_NULL

        ; pass back not_found instead of open_failed
        cmp     eax,ERROR_OPEN_FAILED
        jnz     short lopnx
        mov     eax,ERROR_FILE_NOT_FOUND
        jmp     short lopnx


lopne2: ; media check failed - this will panic before here on a debug system

        push    eax                            ; preserve return code
        mov     edx,pSFN
        push    edx
        CCALL32 <[_pldrClose]>,<[edx]>
        pop     edx
        pop     eax
        mov     word ptr [edx],SFN_NULL
        jmp     short lopnx

ifdef SECURITY
lopne3: ; Call security to see if this call should be allowed             ;@SEC
        ; if the call isn't allowed then SecLdrOpen will close            ;@SEC
        ; the file and return with an ERROR_ACCESS_DENIED message.        ;@SEC
        mov     eax,pSFN                                                  ;@SEC
        CCALL32 _SecLdrOpen, <_ldrpFileNameBuf,eax>                       ;@SEC
        or      eax,eax                                                   ;@SEC
        jz      lopn3               ; continue the call                   ;@SEC
        jmp     lopne1              ; fail the call                       ;@SEC
endif

EndProc _ldrOpen




;***LP  ldrGetmedia - Get Media type for loader.
;
;       ldrGetmedia gets the Media type for a file for the loader.
;
;       MTE_MEDIA_CONTIG means that the device requires contiguous
;       memory for I/O.
;
;       MTE_MEDIA_DISCARD means that the loader may discard memory
;       from this device, and will be assured that it will still
;       be there when requested.
;
;       ENTRY
;               EAX     SFN
;               ECX     pflMedia        where the media bits will be returned
;
;       EXIT
;               (EAX) = NO_ERROR
;               (ECX) = Media bits
;           or
;               (EAX) = Error Code
;
;       USES    EAX, ECX, EDX, FLAGS
;
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING
Procedure _ldrGetMedia,near

        push    ecx                     ; Save pointer to media bits
        CCALL32 _IOGetMediaInfo,<eax,ecx>
        pop     edx
        or      eax,eax

ifdef LDRSTRICT
        jnz     short lgmp1
else
        jnz     short lgmx
endif

        mov     ecx,MTE_MEDIAFIXED      ; Assume fixed media
        test    byte ptr [edx],MEDIA_NON_REMOVABLE
        jnz     short lgm1
        xor     ecx,ecx                 ; Media removable

lgm1:   test    byte ptr [edx],MEDIA_NON_CONTIG_MEM
        jnz     short lgm2
        or      ecx,MTE_MEDIACONTIG     ; contiguous required

lgm2:   test    byte ptr [edx],MEDIA_16M
        jz      short lgmx
        or      ecx,MTE_MEDIA16M        ; below 16 meg physical required

lgmx:   mov     dword ptr [edx],ecx
        RETC

ifdef LDRSTRICT
lgmp1:  Panic   <'ldrGetMedia: media lost'>
endif

EndProc _ldrGetMedia


SWAP32_8       ENDS
SWAP32_9      segment


;***LP  ldrClose - Close file for loader
;
;       _ldrClose provides an interface to the file system from
;       the loader to close an Executable file.
;
;       _ldrClose (SFN);
;
;       ENTRY
;               SFN     : SFN of file to read from
;
;       EXIT
;               The Return Value is held in EAX on exit
;
;               Return Value == NO_ERROR
;                       Close was successful
;
;       USES            EAX, ECX, EDX, EFlags
;
;       Pseudocode :
;
;       IOSftClose(SFN, SSToDS(&tcbsavbuf);
;       return (NO_ERROR)

        ASSUME  DS:FLAT,ES:FLAT,SS:NOTHING
Procedure _ldrClose,near
ArgVar  SFN,DWORD                ; lies, lies - the SFN is 16-bit !!

        EnterProc
        xor     eax,eax                         ; return code

        cmp     [_IsMiniIFS],al                 ; If mini-IFS,
        jne     short lclsx                     ; NOP this procedure

; BUGBUG - the mini-FS used at boot time can only have one file open at
; a time, and it always gives us an SFN of -1 (which is how SFN_NULL is
; defined). This check therefore disables close calls for a valid file
; to the mini-FS at boot time ! Fortunately, the mini-FS just throws
; away the currently open file when told to open a new one !!

        mov     edx,SFN
        movzx   edx,dx                          ; SFN is 16 bits !!
        cmp     dx,SFN_NULL                     ; is the file open?
        jz      short lclsx                     ; no - no file to close

        CCALL32 <[_pldrClose]>,<edx>
lclsx:  LeaveProc
        RETC

EndProc _ldrClose

SWAP32_9   ENDS
SWAP32_15 segment

;***LP  ldrTransPath - Call TransPath for the loader
;
;       ldrTransPath provides an interface to the file
;       system from the loader to call TransPath.
;
;       The result is always stored in ldrFileNameBuf.
;
;       ldrTransPath will return ERROR_FILE_NOT_FOUND if the
;       path specifies either a pseudo-device, a local device,
;       or a named pipe.
;
;       ldrTransPath (pchName);
;
;       ENTRY
;               pchName         : ASCIIZ filename to canonicalize
;
;       EXIT
;               The Return Value is held in EAX on exit
;
;               Return Value == NO_ERROR
;                       TransPath was successful
;
;                   Side Effects :
;                       [WFP_Start]      -> beginning of buffer
;                       [WFP_Path_End]   -> end of path part of target
;                       [Curr_Dir_End]   -> end of curdir in path
;                       [TCBThishVPB]     = current hVPB
;                       [TCBThisFSC]     -> FSC
;                       [ldrFileNameBuf] -> canonicalized filename
;                       [PathNameType]   -> type of pathname
;
;               Return Value != NO_ERROR
;                       (EAX) = Error Code
;
;       USES            EAX, ECX, EDX, EFlags
;

Procedure _ldrTransPath,near

        ASSUME  DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING

        ArgVar  pchName,DWORD

        EnterProc
        cmp     [_IsMiniIFS],0                  ; mini-IFS?
        jnz     short ltnsp5                    ;  yes, just set up ldrFileNameBuf
        CCALL32 [_pIOSftTransPath],<pchName>
        or      eax,eax                         ; Error?
        jnz     short ltnspx                    ;  yes, return it
                                                ;72417 moved from PTDA to TCB
        mov     ecx,_pTCBCur                    ; (ECX) = current TCB address
        test    [ecx].TCBPathNameType,PNType_LOCDEV+PNType_PSDEV+PNType_NMPIPE
                                                ;72417 end
        jz      short ltnspx                    ; Skip ahead if file name okay
        mov     eax,ERROR_FILE_NOT_FOUND        ; Return error
        jmp     short ltnspx

ltnsp5: SaveReg <esi,edi>
        mov     edi,pchName
        CALL16  StrLen32                        ; (ECX) = strlen(pchName)
        mov     esi,edi
        mov     edi,_ldrpFileNameBuf
        rep     movsb                           ; Copy pchName to ldrFileNameBuf
        RestoreReg <edi,esi>
        xor     eax,eax
ltnspx: LeaveProc
        RETC

EndProc _ldrTransPath

SWAP32_15   ENDS


;
;   Beginning of resident code
;

        FARCODE HIGH32
HIGH32CODE      SEGMENT




;***LP  ldrRead - Read file for loader
;
;       ldrRead provides an interface to the file system from the
;       loader to read an Executable file.
;
;       After seeking to the location in the file specified by lSeek,
;       ldrRead will read cchBuf number of bytes into the location
;       at pchBuf.
;
;       ldrRead (SFN, lSeek, plchBuf, pschBuf, cchBuf, pMte );
;
;       ENTRY
;               SFN             : SFN of file to read from (may be SFN_NULL)
;               lSeek           : Seek position address
;               plchBuf         : 0:32 address to read into (always passed in)
;               pschBuf         : 16:16 address to read into (optional)
;                               : when the 16:16 address is provided a selector
;                               : does not need to be allocated.
;                               : ***** IMPORTANT *****
;                               : To read more then 64K do not pass a 16:16
;                               : address it will not work......
;                               : *********************
;               cchBuf          : number of bytes to read
;               pMte            : pointer to the mte (may be null)
;
;               ES, DS : FLAT
;
;       EXIT
;               The Return Value is held in EAX on exit
;
;               Return Value == NO_ERROR
;                       Read was successful
;                       p{l|s}chBuf filled in with cchBuf bytes
;
;               Return Value != NO_ERROR
;                       Read failed
;                       p{l|s}chBuf invalid
;
;       USES            EAX, ECX, EFlags
;
;       Pseudocode :
;
;       if (_IsMiniIFS) {
;           copy directly from mifs_ptr to pchBuf,
;           at the appropriate seek position, for
;           the appropriate length.
;           return (NO_ERROR)
;       }
;       Read (SFN, pchBuf, cchBuf)
;
;       return (rc)
;

        ASSUME  DS:FLAT, ES:FLAT,SS:NOTHING
Procedure _ldrRead,near

        ArgVar  SFN,DWORD
        ArgVar  lOffset,DWORD
        ArgVar  plchBuf,DWORD
        ArgVar  pschBuf,DWORD
        ArgVar  cchBuf,DWORD
        ArgVar  pMte,DWORD

        LocalVar  cbread,DWORD
        LocalVar  cRetry,DWORD

        EnterProc

        SaveReg <ebx,esi,edi>

        xor     edx,edx
lsr0:   inc     edx              ; retry count to protect us from the
        mov     cRetry,edx       ; whims of the filesystem

        cmp     [_IsMiniIFS],dh  ; Loading mini-FSD ? goto special purpose code
        jne     short lsr90

        mov     edx,SFN          ; File handle of -1 means file is closed. Go
        cmp     dx,SFN_NULL      ; out of line to check if CacheModule
                                 ; Note file handles are shorts
        je      short lsr80

lsr1:
        ASSUME  DS:FLAT
        mov     esi,cchBuf                      ; ESI - count of bytes to read
        mov     edi,plchBuf                     ; EDI - Current buffer pointer

; Push parameters and call _IOSftReadAt

        PushP   lOffset                         ; uOffset
        PushP   pschBuf                         ; 16:16 transfer address
        PushP   edi                             ; 0:32 transfer address
        SSToDS  eax,cbread                      ; pointer to byte count to read
        PushP   eax                             ; pcBytes
        PushP   SFN                             ; uSFN
        mov     cbread,esi                      ; cbread <- the Xfer count
        CCALL32 <[_pldrReadAt]>,,,PushP

        ; Return values from IOSFTRead ...
        ; (EAX) = Error Code
        ; (ESI) = byte count requested for read
lsr4:   or      eax,eax
        jnz     short lsre2

        cmp     esi,cbread                      ; Was the read successful ?
        jne     short lsre1

        ; All done.  (EAX) = Error Code
lsrx:   RestoreReg <edi,esi,ebx>
        LeaveProc
        RETC



lsr90:
;       We have a mini-FSD to read.  This "file" really resides in
;       memory, and we assume that there does not exist a file
;       system yet. This is why ldrOpen and ldrClose are
;       NOPs in this case.  We just go fetch the bytes we need.

        SaveReg <ds>
        lds     si,es:mifs_ptr
        ASSUME  DS:NOTHING

        movzx   esi,si                          ; (DS:ESI) -> mini-IFS "file"
        add     esi,lOffset                     ; Seek into the mini-IFS
        mov     ecx,cchBuf                      ; (ECX) = read length
        jecxz   short lsr91                     ; zero length - NOP.

        mov     edi,plchBuf                     ; (ES:EDI) -> Buf
        rep     movsb                           ; read the mini-IFS
                                                ; BUGBUG use movsd !
lsr91:  RestoreReg <ds>
        xor     eax,eax                         ; NO_ERROR
        jmp     lsrx                            ; Finished.  Exit.



lsr80:

; Support code for DosReplaceModule. If file handle is -1, the file is
; closed, and hopefully cached !! Come through here to see if we have
; it cached, and extract from the cache if so.

        mov     edx,pMte      ; check we were passed a pointer to an MTE
        or      edx,edx
        jz      lsr1          ; no - try to read with null handle

        mov     esi,[edx].mte_swapmte         ; get the swappable mte
        mov     esi,[esi].smte_pfilecache     ; get the pointer to cache
        or      esi,esi
        jz      lsr1                          ; no cache - try to read with
                                              ; the invalid handle ... maybe
                                              ; mini-IFS running so valid

        ; file is cached - simply do a memcpy from the cache to dummy the read

        add     esi,lOffset                     ; "Seek" into the cached file
        mov     ecx,cchBuf                      ; (ECX) = read length
        jecxz   short lsr81                     ; zero length - NOP.

        mov     edi,plchBuf                     ; (ES:EDI) -> read-into buffer
        rep     movsb                           ; "read" the file (use movesd!)

lsr81:
        xor     eax,eax                       ; return with a good error code
        jmp     lsrx




lsre1:  mov     eax,ERROR_BAD_EXE_FORMAT  ; assume corrupt file
        jmp     short lsre3

lsre2:  CALL16  AdjustError               ; Find error code from IOSftReadAt

lsre3:  mov     edx,cRetry                ; retry if it might help
        cmp     edx,16
        jnc     lsrx

; Reduce retries to 1 for LAN failure Error handling. Ramon.#75589  "START"
        cmp     eax,ERROR_VC_DISCONNECTED
        jz      gonce                     ; yes! retry once
        cmp     eax,ERROR_BAD_NETPATH
        jz      gonce
        cmp     eax,ERROR_ADAP_HDW_ERR
        jz      gonce
        cmp     eax,ERROR_UNEXP_NET_ERR
        jz      gonce
        cmp     eax,ERROR_BAD_NET_RESP
        jz      gonce
        jmp     short lsre4               ; no! retry 16 times
gonce:  mov     edx,15                    ; retry only once!

lsre4:
; Reduce retries to 1 for LAN failure Error handling. Ramon.#75589  "END"

        push    edx                       ; preserve the count
        CCALL32 _TKTCYield                ; yield as we are in a retry anyway
        pop     edx
        jmp     lsr0

EndProc _ldrRead




;***LP  ldrGetMTESem - Get the MTE semaphore
;
;       Test and Set the MTE semaphore. If the  MTE semaphore is set,
;       block until it is cleared.
;
;       ENTRY   (EBX) = pointer to the MTE
;       EXIT    (EAX) = NO_ERROR
;                       Got the semaphore for the SFT
;               (EAX) = ERROR_INTERRUPT
;
;       The code below makes an assumption that there could never be more than
;       32K - 1 threads waiting on the MTE semaphore.  Structured control
;       flow is sacrificed for efficiency.
;
;       USES    EAX, ECX, EDX, Flags

Procedure ldrGetMTESem,near
        ASSUME  DS:NOTHING, ES:NOTHING, SS:NOTHING

        xor     eax,eax
        mov     ecx,[ebx].mte_swapmte
        inc     [ecx].smte_semcount     ; Indicate semaphore taken/waiting
ifdef LDRSTRICT
        jle     short lgmse1
endif

        cmp     [ecx].smte_semcount,1   ; Was it free?
        jne     short lgms1             ;  no, skip ahead

lgms0:
ifdef LDRSTRICT
        cmp     [ecx].smte_semowner,ax  ; Check if semaphore is indeed free
        jnz     short lgmse2            ; Spurious wakeup or count error
        mov     edx,_pTCBCur
        mov     dx,[edx].TCBNumber      ; DX = current slot number
        mov     [ecx].smte_semowner,dx  ; Update semaphore owner
endif
        RETC

lgms1:  lea     edx,[ecx].smte_semcount
        CCALL32 _TKSleep,<edx,-1,0,WAKEUP_SINGLE>
        mov     ecx,[ebx].mte_swapmte   ; restore swappable pointer
        or      eax,eax                 ; Normal wakeup?
        jz      short lgms0             ;  yes, go exit
        dec     [ecx].smte_semcount     ; Adjust for increment above
ifdef LDRSTRICT
        jl      short lgmse3            ; Check for underflow
endif
        RETC

ifdef   LDRSTRICT
lgmse1: Panic   <'ldrGetMTESem: Semaphore count overflow'>
lgmse2: Panic   <'ldrGetMTESem: Spurious wakeup'>
lgmse3: Panic   <'ldrGetMTESem: Semaphore count underflow'>
endif

EndProc ldrGetMTESem


;***LP  ldrClrMTESem - Clear the MTE semaphore and wakeup waiting procs
;
;       Clear the MTE semaphore for the passed in MTE. Wakeup proceses
;       blocked on this semaphore.
;
;       ENTRY   (EBX) = Pointer to the MTE
;
;       EXIT    NONE
;               MTE semaphore is cleared
;               Processes blocked on the semaphore are woken up
;
;       The code below makes an assumption that there could never be more than
;       32K - 1 threads waiting on the MTE semaphore.
;
;       USES    EAX, ECX, EDX, FLAGS

Procedure ldrClrMTESem,near

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

        mov     ecx,[ebx].mte_swapmte   ; (ECX) - pointer to swappable MTE
ifdef LDRSTRICT
        mov     eax,_pTCBCur
        mov     ax,[eax].TCBNumber      ; (AX) = current slot number
        cmp     ax,[ecx].smte_semowner  ; Do we own it?
        jne     short lcmse1            ;  no, internal error
endif
        mov     [ecx].smte_semowner,0   ; No owner
        dec     [ecx].smte_semcount     ; Decrement semaphore count
ifdef LDRSTRICT
        jl      short lcmse2            ; Check for underflow
endif
        jnz     short lcms0
        RETC

lcms0:  lea     eax,[ecx].smte_semcount
        CCALL32 _TKWakeup,<eax,WAKEUP_SINGLE,0>
                                        ; wakeup first process waiting
        RETC

ifdef LDRSTRICT
lcmse1: Panic   <'ldrClrMTESem: not owner'>
lcmse2: Panic   <'ldrClrMTESem: counter underflow'>
endif

EndProc ldrClrMTESem



HIGH32CODE ENDS

        END
