;       SCCSID = @(#)ldrstea.asm        6.12 92/03/17
SCCSID  equ "@(#)ldrstea.asm    6.12 92/03/17"

PAGE    ,132
TITLE   LDRSTEA - segment loading routines
NAME    LDRSTEA

;***    OS/2 Segment loading routines
;
;       IBM/Microsoft Confidential
;
;       Copyright (c) IBM Corporation 1987, 1989
;       Copyright (c) Microsoft Corporation 1987, 1989
;
;       All Rights Reserved
;
;       The segment loading routines handle the loading of
;       new .EXE file format segments.
;
;       The module table entry is allocated in the file LDRMTE.C
;

.XLIST
        INCLUDE dosseg.inc
        INCLUDE basemaca.inc
        INCLUDE fnomem.inc
        INCLUDE osmaca.inc
        INCLUDE ostypes.inc
        INCLUDE argframe.inc
        INCLUDE osconst.inc
        INCLUDE error.inc
        INCLUDE mi.inc
        INCLUDE vmowner.inc
        INCLUDE protmode.inc
        INCLUDE seldesc.inc
        INCLUDE dossymc.inc
        INCLUDE sigdef.inc
        INCLUDE except.inc
        INCLUDE infoseg.inc
        INCLUDE sft.inc
        INCLUDE inicfg.inc
        INCLUDE tkexport.inc
        INCLUDE pgexport.inc
        INCLUDE vmexport.inc
        INCLUDE selxport.inc
        INCLUDE virtsym.inc
        INCLUDE curdir.inc
        INCLUDE dirent.inc
        INCLUDE find.inc
        INCLUDE screen.inc
        INCLUDE sem.inc
        INCLUDE tkptda.inc
.LIST
        INCLUDE ldrvars.inc

        CpuMode 386
        ModName ldrstea, FNO_LDRSTEA_ASM


        ReferFlat       _DosModMte,byte
        ReferFlat       _pPTDACur,dword
        ReferFlat       _pTCBCur,DWORD


        EXTRN32 _LDRGetSegment
        EXTRN32 _ldrSegFixAddRecord
        EXTRN32 _ldrSegFixStopChain
        EXTRN32 _ldrSegFixAddIterRec
        EXTRN32 _ldrNumToOte
        EXTRN32 _ldrGetTgtMte
        EXTRN32 _ldrGetOrdNum
        EXTRN32 _ldrGetEntAddr
        EXTRN32 _ldrEachObjEntry
        EXTRN32 _ldrEditProLog
        EXTRN32 _ldrMapOrdinal
        EXTRN32 _ldrAscToOrd
        EXTRN32 _ldrRead
        EXTRN32 ldrGetRsrcTypeName
        EXTRN32 _SELVirtToLin
        EXTRN32 _SELGetDescInfo
        EXTRN32 _VMReserveMem

        FARCODE HIGH32


LOAD_TUNEDIT1_SWP segment



;***LP  _ldrLoadSegment - C callable interface for ldrLoadSegment
;
;       ENTRY   pste                    - pointer to ste
;               plv                     - pointer to local vars
;               cbSegHack               - size of segment in bytes
;
;       EXIT    (EAX)                   - error code
;
;       ldrLoadSegment(LDRSTE *pste, LDRLV *plv, int cbSegHack)

Procedure       _ldrLoadSegment,        near

        assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing

        ArgVar  pste,DWORD              ; pointer to ste
        ArgVar  plv,DWORD               ; pointer to local variables
        ArgVar  cbSegHack,DWORD         ; BUGBUG size of segment

        LocalVar        pmte,DWORD
        LocalVar        ex_1_position,DWORD     ; seek address in file
        LocalVar        ex_1_tgtmte,DWORD
        LocalVar        ex_1_pschbuf,DWORD
        LocalVar        ex_1_plchbuf,DWORD
        LocalVar        ex_1_segno,WORD
        LocalVar        ex_1_relocbuf,WORD
        LocalVar        ex_1_reloc_rec,WORD
        LocalVar        fDOSCALLS,WORD          ; TRUE if target MTE is DOSCALLS
        LocalVar        ordnum,DWORD            ; ordinal # from ldrGetOrdNum
        LocalVar        ptgtmte,DWORD
        LocalVar        pprcname,DWORD
        LocalVar        ptgtmod,DWORD
        LocalVar        taddr,<SIZE taddr_s>

SFTLEN  EQU     ((SIZE sf_entry + 1) AND NOT 1)
        LocalVar        reloc,<RELOCBUFLEN+SFTLEN>

        EnterProc
        mov     ex_1_relocbuf,sp        ; save offset of relocation buffer
        SaveReg <ebx,esi,edi>

        xor     eax,eax
        mov     ptgtmod,eax

        ; Setup local variables for call to old loader code
        mov     ex_1_position,eax
        mov     ex_1_tgtmte,eax
        mov     ex_1_reloc_rec,ax
        mov     ebx,plv                 ; get segment of buffer to read in
        mov     eax,[ebx].lv_sbufaddr
        mov     ex_1_pschbuf,eax
        mov     eax,[ebx].lv_lbufaddr
        mov     ex_1_plchbuf,eax
        mov     ax,word ptr [ebx].lv_objnum
        mov     ex_1_segno,ax
        mov     ebx,[ebx].lv_pmte       ; EBX = pointer to MTE
        mov     pmte,ebx
        mov     eax,[ebx].mte_swapmte
        ldrSetupSrcErrTxt eax
        ldrInvTgtErrTxt
        mov     esi,pste                ; ESI = pointer to STE

        CCALL32 ldrLoadSegment

_lsg02:
        or      eax,eax                 ; error ?
        .errnz  NO_ERROR
        jnz     short _lsg1             ; yes, do not invalidate global error
        ldrInvErrTxt
        jmp     short _lsgx

_lsg1:  cmp     ptgtmod,0               ; Do we have a target module ?
        jz      short _lsg2             ; No, skip TgtErrTxt
        mov     ebx,eax                 ; EBX = Error code
        mov     eax,ptgtmod
        mov     eax,[eax].mte_swapmte
        ldrSetupTgtErrTxt eax
        mov     eax,ebx
_lsg2:  cmp     eax,ERROR_PROC_NOT_FOUND
        je      short _lsg3
        cmp     eax,ERROR_INVALID_ORDINAL
        jne     short _lsgx

_lsg3:  mov     ecx,pprcname
        mov     edx,_pTCBCur
        mov     [edx].TCB_ProcNameBuf,ecx
_lsgx:  RestoreReg <edi,esi,ebx>        ; Restore registers for C
        LeaveProc
        RETC

EndProc  _ldrLoadSegment




;***LP  ldrLoadSegment - Load segment within module.
;
;       Reads the segment from the file and does the relocation fixups.
;
;   This procedure performs the following steps:
;
;       - if any data to read
;       -   get offset to start of data
;       -   convert using alignment-shift-count
;       -   if iterated data call load_iterated_data
;       -   else if data to read = 64k read in two 32k chunks
;       -   else read ste size of data
;       - if size of segment <> 64k zero fill to end of segment
;       - if any relocation records present
;       -   read number of relocation items
;       -   if any relocation items present
;       -     while relocation items left
;       -       if relocsperbuf empty
;       -         read more relocation records
;       -         call process fixup record
;       -       get next relocation item
;
;       ENTRY   (EBX) = module table entry laddr
;               (ESI) = segment table entry laddr
;               (BP) = base of loader stack frame variables
;               ex_1_tgtmte, ex_1_plchbuf, ex_1_relocbuf, ex_1_segno
;               and pmte have been setup
;
;       EXIT    (EAX)                   - error code
;               if EAX == ERROR_PROC_NOT_FOUND or ERROR_INVALID_ORDINAL
;                       EDI = pointer to procedure or ordinal.
;
;       USES    EAX, EBX, ECX, EDX, ESI, EDI, FLAGS
;

Procedure ldrLoadSegment,near
        assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing

;       PMONLY  TASKTIME
        TaskTime

        movzx   edx,[esi].ste_offset
        mov     edi,edx                 ; assume no file data
        or      edx,edx                 ; zero offset indicates no file data
        jz      short lsg6              ;  no file data, skip read

;       alignment-shift-count = mte_alignshft

        mov     eax,[ebx].mte_swapmte   ; (EAX) - swappable mte pointer
        movzx   ecx,[eax].smte_alignshift; get segment alignment shift count
lsg1:   shl     edx,cl                  ; convert alignment offset

        mov     ex_1_position,edx       ; (EDX) = file offset of segment data

        test    [esi].ste_flags,ITERATED
        jz      short lsg2              ; the data is not iterated

        mov     eax,ex_1_pschbuf        ; (EAX) = 16:16 address to segment
        mov     edx,ex_1_plchbuf        ; (EDX) = 0:32 address to segment
        mov     ecx,ex_1_position       ; (ECX) = file offset of segment data
        CCALL32 load_iterated_data      ; expand & load iterated data
        mov     ex_1_position,ecx
        or      eax,eax
        jnz     lsgx
        sub     edi,ex_1_plchbuf        ; (EDI) = iterated data size
        jmp     short lsg5

;       the data is not iterated

lsg2:   movzx   eax,[esi].ste_size
        or      eax,eax
        jnz     short lsg3
        mov     eax,010000H             ; 64K
lsg3:   PushP   ebx                     ; pMte
        PushP   eax                     ; cchbuf
        PushP   ex_1_pschbuf            ; 16:16 address to buffer to read into
        PushP   ex_1_plchbuf            ; 0:32 address to buffer to read into
        mov     edx,ex_1_position
        PushP   edx                     ; offset
        add     eax,edx
        mov     ex_1_position,eax       ; update offset for next time
        movzx   eax,[ebx].mte_sfn
        PushP   eax                     ; SFN
        CCALL32 _ldrRead,,,PushP
        or      eax,eax
        .errnz  NO_ERROR
        jnz     lsgx

;       (EBX) -> Pointer to the MTE
;       (ESI) -> Pointer to the STE

        movzx   edi,[esi].ste_size      ; (EDI) -> byte after filedata in
                                        ; the segment
lsg5:   or      edi,edi                 ; is file/iterated data size = 64k?
        jz      short lsg7              ;  yes, needn't clear segment

lsg6:   mov     eax,[cbSegHack]
        CCALL32 clear_segment

;       do relocation fixups for segment
;       ex_1_position = offset in file to relocation info
;       EBX = pmte
;       ESI = pste

lsg7:   test    [esi].ste_flags,RELOCINFO
        jz      lsg24                   ; segment has no relocation items

        push    ax                      ; make room for count of reloc records,
        mov     eax,esp
        mov     di,ss                   ; make 16:16 address pschbuf
        rol     edi,WORDSHIFT
        .errnz  (WORDSHIFT - 16)
        mov     di,ax
        SSToDS  eax                     ; make 0:32 address pschbuf
        mov     ecx,ex_1_position       ; offset in file
        inc     ecx
        inc     ecx
        mov     ex_1_position,ecx       ; new offset in file
        dec     ecx
        dec     ecx
        movzx   edx,[ebx].mte_sfn               ; SFN

        CCALL32 _ldrRead,<edx,ecx,eax,edi,2,ebx>
        or      eax,eax
        .errnz  NO_ERROR
        jz      short lsg8
        pop     cx                      ; discard number of relocation items
        jmp     lsgx

lsg8:   pop     ax                      ; (AX) = number of relocation items
        or      ax,ax                   ; Check that # of relocation records
        jnz     short lsg8a             ; is != 0
        mov     eax,ERROR_INVALID_ORDINAL
        jmp     lsgx
lsg8a:  movzx   edi,ex_1_relocbuf       ; (SS:EDI) = pointer to reloc buffer
        sub     edi,SIZE ri_s           ; Buffer is empty

;       process each relocation item
;       (EBX) -> Pointer to the MTE

lsg9:   or      eax,eax
        jz      lsg24                   ; all done check thunks
        push    eax                     ; save number of relocation items
                                        ; note: ax is used below at <><>

        movzx   esi,ex_1_relocbuf       ; (ESI) = offset of buffer
        cmp     edi,esi                 ; Is buffer empty?
        jae     short lsgB              ;  no, skip ahead

;       If no type flags are set the item is skipped.  This allows
;       a blank relocation item to exist in the relocation records.

        cmp     eax,RELOCSPERBUF        ; <><> compare the number of relocation
                                        ; records left against a buffer full
        jbe     short lsgA              ; jump if at most one buffer-full left
        mov     eax,RELOCSPERBUF        ; if lots of record left, just fill buffer
lsgA:   shl     eax,3                   ; (EAX) = number of bytes to read
        .errnz  (SIZE ri_s - (1 SHL 3))
        add     edi,eax                 ; (EDI) = pointer to last record in buffer
        PushP   ebx                     ; pMte
        PushP   eax                     ; cchbuf
        mov     edx,eax
        mov     ax,ss                   ; 16:16 address pschbuf
        rol     eax,WORDSHIFT
        .errnz  (WORDSHIFT - 16)
        mov     ax,si
        PushP   eax                     ; pschbuf
        mov     eax,esi                 ; 0:32 address plchbuf
        SSToDS  eax
        PushP   eax                     ; plchbuf
        mov     eax,ex_1_position
        PushP   eax                     ; offset
        add     eax,edx
        mov     ex_1_position,eax       ; new offset
        movzx   eax,[ebx].mte_sfn
        PushP   eax                     ; SFN
        CCALL32 _ldrRead,,,PushP
        or      eax,eax
        .errnz  NO_ERROR
        jnz     lsg16

lsgB:   mov     ex_1_reloc_rec,di       ; save pointer to current record
        mov     al,ss:[di].ri_flags
        and     al,TARGET_MASK          ; (AL) = relocation target type
        cmp     al,INTERNALREF
        jne     short lsgE              ; not internal, check for import

        movzx   eax,ss:[di].ri_target_seg
        xor     ah,ah
        cmp     al,0FFh                 ; is the segment movable?

;       the internal segment could be movable or fixed.. that
;       should not matter... the only difference is that the
;       ste_flags for the segment would specify not-movable
;       and at allocation time, the memory manager would be requested
;       to allocate a fixed segment in memory...
;       however, linker will generate the relocation record in that
;       format... hence we need to extract the segment number...

        jne     short lsgC              ; no, get fixed segment address
        mov     ex_1_tgtmte,ebx
        ;ldrSetupTgtErrTxt ebx
        mov     ptgtmod,ebx
        jmp     lsg17

lsgC:   PushP   eax                     ; segment number to check for
        PushP   ebx
        CCALL32 _ldrNumToOte,,,PushP    ; get ste offset
        or      eax,eax                 ; ckeck if segment not present
        jne     short lsgD
        mov     eax,ERROR_INVALID_SEGMENT_NUMBER
        jmp     lsg16

lsgD:   mov     esi,eax                 ; save pointer to ste
        mov     bx,ss:[di].ri_target_off

;       the above target address should be a selector:offset pair
;       in the case of internal reference,
;       get the selector from the segment table entry.

        mov     dx,[esi].ste_selector   ; target address is linear
        jmp     lsg22                   ; do relocation fixups

lsgE:   cmp     al,IMPORTNAME           ; is target offset an import name?
        je      short lsgF              ;  yes, get module table entry
        cmp     al,IMPORTORDINAL        ; is target offset an import ordinal #?
        jne     lsg23                   ;  no, skip to next item

lsgF:   mov     esi,pste
        movzx   esi,[esi].ste_flags
        and     esi,SEGDPL              ; privilege level mask = 0C00h
        cmp     esi,RING_3              ; RING_3 = 0C00h
        je      short lsg10
        cmp     esi,RING_2              ; RING_2 = 0800h
        je      short lsg10

        mov     eax,ERROR_DYNLINK_FROM_INVALID_RING ; disallow dynlinks from ...
        jmp     lsg16                   ;  ... segments other than ring 2 or 3

;       ldrGetTgtMte returns target mte laddr in ptgtmte

lsg10:  SSToDS  eax,ptgtmte
        PushP   eax                     ; return pointer to target mte
        PushP   ebx                     ; source mte
        movzx   ecx,ex_1_reloc_rec      ; (SS:BX) -> relocation record
                                        ; get module ordinal number
        movzx   eax,ss:[ecx].ri_target_modnam
        PushP   eax
        CCALL32 _ldrGetTgtMte,,,PushP
        or      eax,eax
        .errnz  NO_ERROR                ; check for error
        jnz     lsg16

        mov     eax,ptgtmte
        or      eax,eax                 ; eax set to 0 if tgtmte == 0
        jz      short lsg11             ; reference is to DOSCALLS

;       If this module is DOSCALL1 the reference is to DOSCALLS and
;       the DOSCALLS module must be searched first.

        mov     fDOSCALLS,FALSE         ; reset flag for DOSCALLS
        test    [eax].mte_flags1,DOSLIB ; is this DOSCALL1?
        jz      short lsg12             ;  no, go find procedure entry
        mov     fDOSCALLS,TRUE          ; set flag for DOSCALLS

lsg11:  lea     eax,_DosModMte          ; (EAX) = DOSCALLS mte laddr

;       (EAX) = the address of the target module

lsg12:  mov     ex_1_tgtmte,eax         ; save target module laddr

;;;     ldrSetupTgtErrTxt eax
        mov     ptgtmod,eax

        mov     di,ex_1_reloc_rec       ; (SS:DI) -> relocation record
        mov     al,ss:[di].ri_flags
        and     al,TARGET_MASK          ; (AL) = relocation target type
        cmp     al,IMPORTNAME           ; is target offset an import name?
        jne     short lsg17             ;  no, target is an ordinal#

;       set (EDI) -> imported procedure name

        movzx   edi,ss:[di].ri_target_impnam
        mov     eax,[ebx].mte_swapmte   ; (EAX) - swappable mte pointer
        add     edi,[eax].smte_impproc
        mov     pprcname,edi

;       BUGBUG 4/13/89 - add code to
;       make sure that the string pointed to is wholly addressable

        cmp     byte ptr [edi+1],'#'    ; is this an ASCII ordinal?
        jne     short lsg13

        movzx   ecx,byte ptr [edi]      ; ECX = length of procedure name
        inc     edi                     ; skip over string length
        inc     edi                     ; (EDI) -> ASCII ordinal number
        dec     ecx                     ; adjust for '#'

        PushP   edi
        PushP   ecx
        CCALL32 _ldrAscToOrd,,,PushP    ; returns integer in AX
        mov     ecx,eax                 ; (ECX) = ordinal #
        jmp     short lsg18

;       find proc name in target mte's resident name table

lsg13:  mov     edx,ex_1_tgtmte
        test    [edx].mte_flags1,DOSMOD ; is this the DOSCALLS module?
        jz      short lsg14             ; no

        mov     eax,ptgtmte             ; get mte of target again
        or      eax,eax                 ; was it DOSCALLS
        jz      short lsg15             ; yes, no procedure name table
        mov     ex_1_tgtmte,eax         ; otherwise search DOSCALL1
;;;     ldrSetupTgtErrTxt eax
        mov     ptgtmod,eax

lsg14:  SSToDS  ecx,ordnum
        PushP   ecx                     ; get return value space on stack
        PushP   edi                     ; procname (length & name)
        mov     eax,ex_1_tgtmte         ; pointer to mte
        PushP   eax
        CCALL32 _ldrGetOrdNum,,,PushP   ; get ordinal number
        movzx   ecx, word ptr ordnum
        or      eax,eax
        jz      short lsg18             ; proc name found

lsg15:  mov     eax,ERROR_PROC_NOT_FOUND ; report an error

;       (EAX) = errorcode

lsg16:  pop     ecx                     ; clean stack up (count of relocs)
        RETC

;       target offset is procedure ordinal number
;       (SS:DI) -> relocation record

lsg17:  movzx   ecx,ss:[di].ri_target_off

;       (ECX) = procedure ordinal number

lsg18:  PushP   ecx                     ; Argument for ldrMapOrdinal

;       Check if this module is protected. If it is and reference is to
;       any of the APIs associated with shared memory (See ldrOrdinalMap in
;       ldrfixup.c) then this ordinal needs to be mapped

; ????  test    [ebx].mte_flags1,MTEMODPROT; Is this a protected module ?
;       jz      short lsg19             ; No, skip mapping
;       cmp     fDOSCALLS,0             ; Is target DOSCALLS ?
;       jz      short lsg19             ; No, skip mapping
;       CCALL32 _ldrMapOrdinal,,,PushP  ; Map ordinal
;       push    eax                     ; Put new ordinal on stack

;       setup link to procedure call thunk using ordinal#

lsg19:  mov     esi,pste                ; (ESI) -> segment table entry
        movzx   ecx,[esi].ste_flags
        xor     eax,eax
        mov     eax,NRALIAS             ; always request 16:16 alias address
        test    ecx,STE_CONFORM         ; check if segment is conforming
        jz      short lsg1A
        or      eax,OBJCONFORM          ; set 32-bit conforming flag
lsg1A:  and     ecx,STE_SEGDPL
        cmp     ecx,STE_RING_2          ; check if segment is ring 2
        jne     short lsg1B
        or      eax,OBJIOPL             ; set 32-bit IOPL flag

lsg1B:  mov     ecx,[esp]               ; ECX = ordinal number

        mov     edx,[_pPTDACur]         ; segment packing needs the ptda
        mov     edx,[edx].pPTDAExecChild
        or      edx,edx
        jnz     short lsg1B1
        mov     edx,_pPTDACur
lsg1B1: PushP   edx
        PushP   ebx
        PushP   eax                     ; source segment iopl and comform flags
        SSToDS  eax,taddr               ; return for target address
        PushP   eax
        mov     eax,ex_1_tgtmte         ; pointer to mte
        PushP   eax
        PushP   ecx                     ; ordinal number
        CCALL32 _ldrGetEntAddr,,,PushP  ; get entry address
        pop     ecx                     ; restore ordinal
        or      eax,eax
        .errnz  NO_ERROR
        jnz     short lsg1C             ; jump if no entry exists for ordinal
        movzx   esi,[taddr].taddr_toffl
        movzx   edx,[taddr].taddr_tsel  ; (DX:SI) = target address
        mov     al,[taddr].taddr_tflags ; (AL) = entry point flags
        jmp     short lsg1F

;       ECX = Ordinal number
lsg1C:  mov     eax,ex_1_tgtmte
        test    [eax].mte_flags1,DOSMOD ; is this the DOSCALLS module?
        jnz     short lsg1E             ; yes, get DOSCALL1 module handle
lsg1D:  mov     pprcname,ecx            ;  no, report an error
        jmp     short lsg20

lsg1E:  push    ecx                     ; save ordinal number
        SSToDS  eax,ptgtmte
        PushP   eax                     ; return pointer to target mte
        PushP   ebx
;;;     ldrSetupTgtErrTxt ebx
        mov     ptgtmod,ebx
        movzx   eax,ex_1_reloc_rec      ; get module ordinal from fixup record
        movzx   eax,ss:[eax].ri_target_modnam
        PushP   eax                     ; ordinal number of ref module
        CCALL32 _ldrGetTgtMte,,,PushP
        pop     ecx                     ; (CX) = ordinal number
        or      eax,eax
        .errnz  NO_ERROR                ; check for error
        jnz     short lsg1D

        mov     eax,ptgtmte
        or      eax,eax                 ; eax set to 0 if tgthndl == 0
        jz      short lsg20             ; report error

        mov     ex_1_tgtmte,eax         ; save target module laddr
;;;     ldrSetupTgtErrTxt eax
        mov     ptgtmod,eax
        jmp     lsg18                   ; search DOSCALL1 entry table

;       If the relocation record is for an external reference, we have
;       to  check  that  the  entry  in the target module is exported.
;       Note  that  since  entries  for DOSCALLS do not contain a flag
;       word, we assume that all doscalls entries (i.e. call gates and
;       absolute symbols) are exported.  For DOSCALLS entries, AL has
;       "fake" flags with the EF_EXPORT bit set. See ldrGetEntAddr above

lsg1F:  movzx   edi,ex_1_reloc_rec      ; get pointer to relocation record
        mov     ah,ss:[di].ri_flags     ; get relocation record flags
        and     ah,TARGET_MASK          ; (AL) = relocation target type
        cmp     ah,INTERNALREF          ; Is relocation record internal ?
        je      short lsg21             ; if internal, skip check for export
        test    al,EF_EXPORT            ; if external, is the fixup exported ?
        jnz     short lsg21             ; jump if exported by target module

lsg20:  mov     eax,ERROR_INVALID_ORDINAL ; else the ordinal reference
        jmp     lsg16

lsg21:  mov     ebx,esi                 ; (DX:BX) = target address
lsg22:  mov     di,ex_1_reloc_rec       ; (SS:DI) -> current relocation record
        mov     esi,ex_1_plchbuf        ; (ESI) = address of segment
        mov     eax,pmte                ; (EAX) = pmte
        mov     ecx,pste                ; (ECX) = pste
        CCALL32 dofixups                ; do relocation fixups
        jc      lsg16                   ; CARRY set if there was error

        mov     ebx,pmte                ; (EBX) = pointer to source MTE

;       (SS:EDI) -> current relocation record

lsg23:  movzx   edi,di
        sub     edi,SIZE ri_s           ; point to next relocation record
        pop     eax
        dec     eax
        jmp     lsg9                    ; get next relocation item

lsg24:  mov     eax,plv
        PushP   eax                     ; pointer to local vars
        mov     [eax].lv_type,0         ; flag that ldrLoadSegment calling
        mov     eax,offset FLAT:_ldrEditProLog
        PushP   eax
        PushP   ebx                     ; pointer to mte
        movzx   eax,ex_1_segno          ; object number to seach for
        PushP   eax
        CCALL32 _ldrEachObjEntry,,,PushP ; edit prolog for windows compatability
        xor     eax,eax                 ; return no error
        .errnz  NO_ERROR
lsgx:   RETC

EndProc ldrLoadSegment




;***LP  load_iterated_data - load iterated data
;
;       Reads the iterated data from the file, expands it and loads
;       it into the segment.
;
;       ENTRY   (EAX) = 16:16 address of segment
;               (EBX) = module table entry laddr
;               (ECX) = offset to segment data in file
;               (EDX) = 0:32 address of segment
;               (ESI) = segment table entry laddr
;
;       EXIT    (EAX) = NO_ERROR
;                  (ECX) = seek address
;                  (EDI) = offset to uninitialized space in segment
;
;               (EAX) = error code returned from ldrRead
;                         ERROR_ITERATED_DATA_EXCEEDS_64k
;                         ERROR_INVALID_MINALLOCSIZE
;                         ERROR_BAD_EXE_FORMAT
;
;       USES    EAX, ECX, EDX, EDI, FLAGS

        assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing

Procedure load_iterated_data,near

        LocalVar lid_position,DWORD     ; seek address in file
        LocalVar lid_orgbuf,DWORD       ; 0:32 address to start of segment
        LocalVar lid_plchbuf,DWORD      ; 0:32 address offset of segment
        LocalVar lid_pschbuf,DWORD      ; 16:16 address of segment
        LocalVar lid_sf_pste,DWORD      ; Pointer to segment table entry
        LocalVar lid_avail_offset,DWORD ; offset to available space in segment
        LocalVar lid_avail_size,DWORD   ; space available in segment
        LocalVar lid_rem_filedata,WORD  ; remaining data to be read from file
        LocalVar lid_sfn,WORD           ; system file number
        LocalVar lid_sf_size,DWORD      ; Size of current data
        LocalVar lid_sf_iter,DWORD      ; count of iterations
        LocalVar lid_sf_foff,DWORD      ; File offset of data record


;       initialize local variables

        EnterProc <ecx,edx,edx,eax,esi> ; -> lid_position  = ecx
                                        ;    lid_orgbuf = edx
                                        ;    lid_plchbuf = edx
                                        ;    lid_pschbuf = eax
                                        ;    lid_pste = esi

        SaveReg <esi>
        mov     ax,[ebx].mte_sfn        ; (AX) = system file number
        mov     lid_sfn,ax              ; save system file number
        mov     lid_avail_sizeh,1       ; assume segment size = 64k
        movzx   ecx,[esi].ste_minsiz    ; (CX) = final segment size.
        mov     lid_avail_sizel,cx      ;
        movzx   eax,[esi].ste_size      ; (EAX) = bytes of seg data in file
        mov     lid_rem_filedata,ax     ;
        jecxz   short lid4

        xor     edi,edi
        mov     lid_avail_sizeh,di      ; segment size is less than 64k

;       (EAX) = lid_rem_filedata
;       take care of the case where filedata size = 64k

lid4:   or      eax,eax                 ; is filedata size == 64k?
        jnz     short lid8              ;  no, continue

        mov     lid_rem_filedata,0FFFCh ; save size of remaining filedata
        jmp     short lid12

;       while (TRUE) {
;         if ((lid_rem_filedata -= 4) < 0)
;           error;
;         if (lid_rem_filedata == 0)
;           break;
;         read iteration record header;
;         if ((NumIterations ==  0) || (PatternSize == 0))
;           continue;
;         if ((lid_rem_filedata -= PatternSize) < 0)
;           error;
;         ExpandedSize = PatternSize * NumIterations
;         if ((lid_avail_size -= ExpandedSize) < 0)
;           error;
;         read Pattern;
;         expand the Pattern;
;         lid_avail_offset = offset to byte after expanded data
;         if (lid_rem_filedata == 0)
;           break ;
;       }

lid8:   sub     lid_rem_filedata,4      ; update remaining file data
        jz      lid60                   ; return if no filedata

        jc      lid52                   ; error if incomplete record header

lid12:  push    eax                     ; temp space to read iteration values
        mov     edx,esp
        PushP   ebx                     ; pMte
        PushP   4                       ; cchbuf
        mov     ax,ss                   ; 16:16 address pschbuf
        rol     eax,WORDSHIFT
        .errnz  (WORDSHIFT - 16)
        mov     ax,dx
        PushP   eax                     ; pschbuf
        SSToDS  edx                     ; (EDX) -> space to read in data
        PushP   edx                     ; 0:32 address plchbuf
        mov     eax,lid_position
        PushP   eax                     ; offset
        add     eax,4
        mov     lid_position,eax        ; new offset
        movzx   eax,lid_sfn             ; (EBX) = system file number
        PushP   eax                     ; SFN
        CCALL32 _ldrRead,,,PushP        ; read iterated data record header
        pop     dx                      ; (DX) = NumIterations
        pop     cx                      ; (CX) = PatternSize

        movzx   ecx,cx                  ; Save info for fixup records
        movzx   edx,dx
        mov     lid_sf_size,ecx
        mov     lid_sf_iter,edx

        or      eax,eax
        .errnz  NO_ERROR
        jnz     lidx                    ; return error if the read failed
                                        ; error code in AX from ldrRead
        mov     ax,dx                   ; (AX) = NumIterations
        or      ax,ax                   ; NumIterations == 0 ?
        jz      short lid8              ;  yes, move on to the next record

        jcxz    short lid8              ; if (PatternSize == 0) ...
                                        ; ... move on to the next record
        sub     lid_rem_filedata,cx     ; update size of remaining file data
        jc      lid52                   ; error if we had a bad record

;       duplicate the iterated data for the required number of iterations

        mul     cx                      ; (DX:AX)= ExpandedSize
        cmp     dx,1                    ; is ExpandedSize == 64k?
        je      short lid20             ;  may be, check the low word (AX)

        jb      short lid28             ; ExpandedSize is less than 64k

        jmp     short lid24             ; error, ExpandedSize > 64k

lid20:  sub     lid_avail_sizeh,dx      ; is available size == 64k?
        jc      short lid32             ;   no, return error

;       (DX:AX) = ExpandedSize
;       We found DX = 1, hence AX must be 0 (since maximum size is 64k).

        or      ax,ax                   ; ExpandedSize larger than 64k?
        jnz     short lid24             ;  yes, return error

        mov     ax,0FFFFh
        sub     ax,cx                   ; (CX) = PatternSize
        inc     ax                      ; (AX) = ExpandedSize - PatternSize
        jmp short lid44

lid24:  stc
        mov     eax,ERROR_ITERATED_DATA_EXCEEDS_64k
        jmp     lidx

;       (AX) = ExpandedSize
;       (CX) = PatternSize
;       We have determined that ExpandedSize is less than 64k.

lid28:  mov     dx,lid_avail_sizeh
        or      dx,dx                   ; is available size == 64k?
        jnz     short lid36             ;  yes

        sub     lid_avail_sizel,ax      ; adjust available space in segment
        jnc     short lid40

lid32:  mov     ax,ERROR_INVALID_MINALLOCSIZE
        jmp     lidx                    ;  yes, report an error

;       We have determined that available memory = 64k.

lid36:  mov     dx,0FFFFh
        sub     dx,ax
        inc     dx
        mov     lid_avail_sizel,dx      ; update the available space...
        mov     lid_avail_sizeh,0       ; ...in segment

lid40:  sub     ax,cx                   ; (AX) = ExpandedSize - PatternSize

lid44:  push    ax                      ; (AX) = ExpandedSize - PatternSize
        push    cx                      ; (CX) = PatternSize

        mov     eax,lid_position        ; Save data file offset
        mov     lid_sf_foff,eax

        PushP   ebx                     ; pMte
        movzx   ecx,cx
        PushP   ecx                     ; cchbuf
        xor     eax,eax                 ; pass no 16:16 pointer
        PushP   eax                     ; 16:16 address pschbuf
        mov     eax,lid_plchbuf         ; 0:32 address plchbuf
        PushP   eax
        mov     esi,eax                 ; ESI = plchbuf
        mov     eax,lid_position
        PushP   eax                     ; offset in file
        add     eax,ecx
        mov     lid_position,eax        ; new file offset
        movzx   eax,lid_sfn
        PushP   eax                     ; SFN
        CCALL32 _ldrRead,,,PushP        ; (fh, seg_addr, segdata->size);
        pop     di                      ; (DI) = PatternSize
        pop     cx                      ; (CX) = ExpandedSize - PatternSize
        or      eax,eax
        .errnz  NO_ERROR
        jnz     short lidx              ; return error if the read failed
                                        ; (EAX) = error code from ldrRead

        cmp     di,4                    ; Is pattern <= 4 bytes
        ja      short lid51

        push    esi                     ; Store pattern in file offset
        push    edi                     ; Only touch PatternSize bytes
        push    ecx

        movzx   ecx,di
        SSToDS  edi,lid_sf_foff

        rep     movsb

        pop     ecx
        pop     edi
        pop     esi

lid51:
        mov     eax,lid_sf_pste         ; Are fixups being recorded?
        cmp     [eax].ste_fixups,0
        jz      short lid51a

        SaveReg <ecx>                   ; Save count

        PushP   lid_sf_iter             ; Iteration Count
        PushP   lid_sf_size             ; Data size


        mov     eax,lid_plchbuf         ; Current segment pointer
        sub     eax,lid_orgbuf          ; eax = segment relative offset
        PushP   eax

        PushP   lid_sf_foff             ; File data offset
        PushP   lid_sf_pste


        CCALL32 _ldrSegFixAddIterRec,,,PushP
        RestoreReg <ecx>

lid51a:
        movzx   edi,di
        add     edi,lid_plchbuf         ; (EDI) -> byte after Pattern in seg
        movzx   ecx, cx
        rep     movsb                   ; iterate the data
        mov     lid_plchbuf,edi         ; (EDI) = offset to avail space in seg
        movzx   ecx,lid_rem_filedata
        jecxz   short lidret
        jmp     lid8

lid52:  mov     eax,ERROR_BAD_EXE_FORMAT
lid56:  jmp     short lidx

lid60:  mov     edi,lid_plchbuf

        .errnz  NO_ERROR
lidret: xor     eax,eax                 ; (EAX) = NO_ERROR

lidx:   RestoreReg      <esi>

        cmp     [esi].ste_fixups,0
        jz      short lidx2
;
; An iteration record of all zeros is the sentinel
;
        push    eax                     ; Save error code

        xor     eax,eax
        PushP   eax                     ;uliters
        PushP   eax                     ;ulsize
        PushP   eax                     ;ulsoff
        PushP   eax                     ;uldoff
        PushP   esi                     ; Push pste
        CCALL32 _ldrSegFixAddIterRec,,,PushP

        pop     eax                     ; Pop error code

lidx2:
        mov     ecx,lid_position        ; (ECX) = seek address

        LeaveProc
        RETC
EndProc load_iterated_data


LOAD_TUNEDIT1_SWP  ENDS
LOAD_TUNEDIT1_SWP segment


;***LP  clear_segment - fills segment with zero
;
;       ENTRY   (EAX) = size of segment
;               (EDI) = offset into seg at which clearing should start
;               (EBX) = module table entry laddr
;               (ESI) = segment table entry laddr
;               ex_1_plchbuf set up
;
;       EXIT    segment cleared
;
;       USES    EAX,ECX,EDX,EDI,FLAGS

Procedure clear_segment,near
        assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing

;       (EAX) = segment size
;       (EDI) = offset into segment at which clearing should start

        mov     edx, eax
        sub     edx, edi                ; (EDX) = byte count
        jz      short csx               ; if (EDX) = 0, there is no clearing
                                        ;   to do
        add     edi,ex_1_plchbuf
        xor     eax,eax                 ; (EAX) = zero to store
        mov     ecx,edx
        shr     ecx,2                   ; ecx = no of dwords to clear
        jz      short cls10             ; jmp if ecx = 0

        rep     stosd                   ; clear ecx dwords
cls10:  mov     ecx,edx
        and     ecx,3                   ; no of odd bytes
        jz      short csx               ; skip if no odd bytes
        rep     stosb                   ; clear odd no of bytes
csx:    RETC
clear_segment   ENDP



;***LP  dofixups - Do the fixups following the source chain.
;
;       ENTRY   (DX:BX) = target address
;               (SS:DI) -> relocation record structure (ri_s)
;               (EAX) = pmte
;               (ECX) = pste
;               (ESI) = address of segment
;
;       EXIT    CARRY clear if Success
;               CARRY set if Error
;                 (AX) = ERROR_RELOC_CHAIN_XEEDS_SEGLIM
;                        ERROR_INFLOOP_IN_RELOC_CHAIN
;
;       USES    ECX, EDX

Procedure dofixups,near
        assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing

LocalVar        dfu_linear,DWORD
LocalVar        dfu_pste,DWORD
LocalVar        dfu_WordsInSeg,WORD

        EnterProc <esi,ecx>             ; dfu_linear = esi
                                        ; dfu_pste = ecx
        test    [eax].mte_flags1,FSDMOD
        jz      short dfu0

        and     edx,RPL_CLR             ; for an FSDMOD, do fixups with RPL = 0
dfu0:   movzx   eax,ss:[di].ri_source
        add     esi,eax                 ; (ESI) = source address
        movzx   ecx,[ecx].ste_minsiz    ; get size of segment
        dec     cx      ; convert size to limit (MAX is FFFFh) B728594 Edd Doutre
        add     ecx,dfu_linear          ; (ECX) = max address of segment
        test    ss:[di].ri_flags,ADDITIVE
        jz      short dfu0d             ; follow chain of fixups

;       Process additive fixup
                                        ; (EAX = offset from segment)
        CCALL32 fixup ; c set on error  ; (DX:BX) = target address
        jmp     short dfu6              ; All done for an additive fixup, error
                                        ; or not.

;       Process chained fixup
;
;       A loop in a fixup chain is detected as follows: we
;       calculate the maximum number of chained fixups that
;       the segment may contain, increment it by one, and
;       then decrement it each time through the loop.  If
;       the count ever reaches zero, we know we have exceeded
;       the maximum number possible, and we return an error.
;       Note that in practice, a loop in the relocation chain
;       may not manifest itself in this fashion, since we
;       overwrite the links with the target value.  In fact,
;       in the worst case, a loop in the relocation chain
;       could go undetected. So, in actuality, a loop in a fixup
;       chain implies corrupt data.

dfu0d:  mov     eax,[dfu_pste]
        mov     ax,[eax].ste_minsiz     ; (AX) = size of segment
        dec     ax                      ; (AX) = segment limit
        add     ax,1                    ; (AX) = size in bytes (ADD affects CY)
        rcr     ax,1                    ; (AX) = number of words in the segment
        inc     ax                      ; (AX) = number of words + 1
        mov     dfu_WordsInSeg,ax

;       Loop to do fixups.

dfu1:   dec     dfu_WordsInSeg          ; decrement source_count
        jz      short dfu2
        cmp     esi,ecx                 ; Limit violation?
        jae     short dfu3              ;  yes, return error
        push    word ptr [esi]          ; save current source address

        mov     eax,esi                 ; (EAX) = source offset
        sub     eax,dfu_linear

        CCALL32 fixup                   ; (DX:BX) = target address
        jc      short dfu3a             ;  exit on error
        pop     si                      ; restore current source address
        cmp     si,-1                   ; is this the end of the source chain?
        je      short dfu5              ;  yes, done with chain
        movzx   esi,si
        add     esi,dfu_linear          ; point to next source offset
        jmp     short dfu1              ; process next source offset

dfu2:   mov     eax,ERROR_INFLOOP_IN_RELOC_CHAIN ; set errorcode
        jmp     short dfu4

dfu3a:  pop     si
        pop     si                      ; restore current source address
        jmp     short dfu4

dfu3:   mov     eax,ERROR_RELOC_CHAIN_XEEDS_SEGLIM ; Assume error
dfu4:   stc                             ; Error
        jmp     short dfu6

dfu5:   mov     ecx,dfu_pste            ; Is there a fixup record in progress?
        cmp     [ecx].ste_fixups,0
        jz      short dfu5a
        CCALL32 _ldrSegFixStopChain,<dfu_pste>
dfu5a:
        clc

dfu6:   LeaveProc
        RETC
EndProc dofixups




;***LP  fixup - Fixup source address.
;
;       Fixes up the source address defined by the source type.
;       The source address offset may be modified.
;
;       ENTRY   (eax) = source offset
;               (ESI) = source address
;               (DX:BX) = target address
;               (SS:DI) -> relocation record structure (ri_s)
;               (ECX) = source segment size
;
;       EXIT    Carry set
;                   Source location exceeds segment limit
;               Carry clear
;                   Fixup okay
;
;       USES    AX
;
; Note: On entry to this routine, a call frame is setup for
;       the call to ldrSegFixAddRecord. This call frame is normally
;       removed after the call to ldrSegFixAddRecord. If you change the
;       error path, be sure to remove this frame from the stack.
;

Procedure fixup,near
        assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing

        push    edx
        push    ecx
;
; Setup call to ldrSegFixAddRecord
;
        PushP   0
        test    ss:[di].ri_flags,ADDITIVE
        setz    BYTE PTR [esp]          ; If not additive, then set fchained
        PushP   esi                     ; push pData
        PushP   2                       ; push size

ulSize  EQU     DWORD PTR [esp+8]

        PushP   eax                     ; Push offset into segment
        PushP   dfu_pste                ; push segment table entry

        mov     al,ss:[di].ri_stype
        and     eax,SOURCE_MASK         ; Mask off all but source type
        cmp     al,SEG_ADR
        je      fix5
        cmp     al,FAR_ADR              ; if source type is FAR or OFFSET
        je      short fix1              ;  do an offset fixup
        cmp     al,OFF_ADR
        je      short fix11
        cmp     al,BYTE_ADR
        je      fix90

;-------------FAR_ADR--------------------------------------------------------
;       Do the offset fixup and increment the source pointer
;       for the far address segment fixup.

fix1:   cmp     esi,ecx                 ; Limit violation?
        jae     fix_e_1                 ;  yes, return error
        test    ss:[di].ri_flags,ADDITIVE
        jz      short fix2              ; no additive fixup
        add     [esi],bx                ; add target to source contents
        jmp     short fix3
fix2:   mov     [esi],bx                ; replace source contents with target
fix3:   inc     esi                     ; increment pointer to segment value
        inc     esi
        add     ulSize,2                ; Add two bytes to fixup size

;-------------SEG_ADR or segment portion of FAR_ADR--------------------------
;       Do segment fixup or segment portion of far address
;       fixup.

fix5:   cmp     esi,ecx                 ; Limit violation?
        jae     short fix_e_1           ;  yes, return error
        test    ss:[di].ri_flags,ADDITIVE
        jz      short fix6              ; no additive fixup
        add     [esi],dx                ; add target to source contents
        jmp     short fix7
fix6:   mov     [esi],dx                ; replace source contents with target

fix7:
        mov     eax,dfu_pste            ; Only record when needed
        cmp     [eax].ste_fixups,0
        jz      fix_do_not_record
        CCALL32 _ldrSegFixAddRecord,,,PushP         ; returns error in eax
        or      eax,eax                             ; clears the carry
        jnz     short fix_e_2                       ; error
        pop     ecx
        pop     edx
        RETC


;-------------OFF_ADR-------------------------------------------------------
;       Do the offset fixup and increment the source pointer

fix11:  cmp     esi,ecx                 ; Limit violation?
        jae     short fix_e_1           ;  yes, return error
        test    ss:[di].ri_flags,ADDITIVE
        jz      short fix12              ; no additive fixup
        add     [esi],bx                ; add target to source contents
        jmp     short fix13
fix12:  mov     [esi],bx                ; replace source contents with target

fix13:
        mov     eax,dfu_pste            ; Only record when needed
        cmp     [eax].ste_fixups,0
        jz      fix_do_not_record
        CCALL32 _ldrSegFixAddRecord                 ; returns error in eax
        or      eax,eax                             ; clears the carry
        jnz     short fix_e_2                       ; error
        pop     ecx
        pop     edx
        RETC


;------------BYTE_ADR-------------------------------------------------------
;----Byte fixups are few and far between, don't duplicate code to make------
;----it blazingly fast, jump to re-use other code---------------------------
;----
;       Do a byte fixup.

fix90:  cmp     ecx,esi                 ; Limit violation?
        jc      short fix_e_1           ;  yes, return error
        dec     ulSize                  ; decrement size for BYTE_ADR
        test    ss:[di].ri_flags,ADDITIVE
        jz      short fix95             ; no additive fixup
        add     [esi],bl                ; add target to source contents
        jmp     short fix13
fix95:  mov     [esi],bl                ; replace source contents with target
        jmp     short fix13

;---------------------------------------------------------------------------
;---------------------------------------------------------------------------
;       Exits.


fix_e_1:add     esp,20
        mov     eax,ERROR_RELOC_CHAIN_XEEDS_SEGLIM
        stc                             ; Limit violation detected
        jmp     short fix_x

fix_e_2:stc                             ; Limit violation detected
        jmp     short fix_x

fix_do_not_record:
        add     esp,20                  ; Remove parameters from stack
        clc

fix_x:  pop     ecx                       ; return carry set on error
        pop     edx
        RETC

EndProc fixup

LOAD_TUNEDIT1_SWP  ENDS


;;;;***LP  ldrChkIOPLTable - Check if module is in IOPL table.
;;;;
;;;;       Search table of module names which are always allowed
;;;;       to load IOPL segments.  The IOPL module name table
;;;;       is a series of strings with a length byte followed by
;;;;       the module name and is terminated by a null length
;;;;       byte.
;;;;
;;;;       ENTRY   psmte   Pointer to swappable MTE
;;;;
;;;;       EXIT    NO_ERROR if module is in table
;;;;               ERROR_IOPL_NOT_ENABLED if not in table
;;;;
;;;        ASSUME DS:FLAT, ES:FLAT, SS:NOTHING
;;;
;;;Procedure _ldrChkIOPLTable,near
;;;ArgVar  psmte,DWORD
;;;        EnterProc
;;;        SaveReg <esi,edi>
;;;        mov     eax,ERROR_IOPL_NOT_ENABLED; Assume module not in IOPL_TABLE
;;;        mov     edi,psmte
;;;        mov     esi,[edi].smte_restab   ; point to module name entry
;;;        mov     edi,_pchIOPLlist        ;get pointer to IOPL
;;;        xor     ecx,ecx
;;;
;;;cit2:   add     edi,ecx
;;;        mov     cl,[edi]
;;;        jecxz   cit4                    ; zero indicates end of table
;;;
;;;        inc     ecx                     ; include length byte in compare
;;;        SaveReg <esi,edi,ecx>
;;;        repe    cmpsb                   ; compare the length, name string
;;;        RestoreReg <ecx,edi,esi>
;;;        jne     cit2                    ; no match, try next module in list
;;;
;;;        xor     eax,eax                 ; match found, return success
;;;        .errnz  NO_ERROR
;;;cit4:   RestoreReg <edi,esi>
;;;        LeaveProc
;;;        RETC
;;;EndProc _ldrChkIOPLTable

LOAD_FTUNE1_SWP segment

;***LP  ldrGetHugeSel - allocate selectors for possible huge segment
;
;       We assume that any sequence of n consecutive segments
;       in an .EXE file of which the first n - 1 are of length
;       64K bytes may constitute a statically allocated huge
;       array providing they are all data segments having the
;       same attributes.  Such a huge segment needs to have
;       consecutive selectors assigned to its constituents so
;       that selector arithmetic will work properly.  Note that
;       the segments need not actually form a huge array, but we
;       must treat them as such since we cannot tell the difference
;       on the basis of the information in the mte.  This function
;       allocates the required number of consecutive selectors.
;       In the event of an error, this function returns CY set,
;       with the proper error code in AX.
;
;       ldrGetHugeSel(pmte, pste, iste)
;
;       ENTRY   pmte            - MTE pointer
;               pste            - STE pointer
;               segno           - STE index (1-based)
;               psize           - pionter to return size of huge resource
;               phugecnt        - pointer to return count of huge segments
;
;       EXIT    (EAX) = NO_ERROR
;                 Selectors allocated if we have a huge segment
;               (EAX) = error code
;
;       USES    EAX, ECX, EDX, FLAGS
;
;       WARNING This function depends on the fact that the ptda_child
;               field in the PTDA is the handle of the child's PTDA
;               for DOSEXECPGM and ZERO for DOSLOADMODULE.  Currently,
;               this is guaranteed to be true because the loader
;               semaphore prevents (among other things) a process from
;               doing a exec and a runtime module load at the same
;               time.

Procedure _ldrGetHugeSel,near
        ASSUME DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING

        ArgVar          ghs_pmte,DWORD          ; MTE pointer
        ArgVar          ghs_pste,DWORD          ; STE pointer
        ArgVar          ghs_segno,DWORD         ; segment number
        ArgVar          ghs_psize,DWORD         ; pointer to return size
        ArgVar          ghs_phugecnt,DWORD      ; num of segments in huge

        LocalVar        <ghs_hugecnt>,WORD      ; number of segments in huge
        LocalVar        <ghs_isrsrc>,WORD       ; rsrc flag
        LocalVar        <ghs_typeid>,WORD       ; resource type id
        LocalVar        <ghs_nameid>,WORD       ; resource name id
        LocalVar        <ghs_ac>,<size vmac_s>  ; return buffer

        xor     ecx,ecx                 ; (ECX) = 0
        EnterProc <ecx>                 ; zero both hugecnt and isrsrc
        SaveReg <ebx,esi,edi>
        mov     edi,[ghs_pmte]          ; (EDI) = MTE pointer
        mov     esi,[ghs_pste]          ; (ESI) = STE pointer
        cmp     [esi].ste_minsiz,cx     ; Size = 64K?
        jnz     ghs4                    ;  no, exit
        cmp     [esi].ste_selector,cx   ; Selector assigned already?
        jnz     ghs4                    ;  yes, exit
        movzx   edx,[esi].ste_flags     ; (DX) = segment flags
        mov     ecx,edx                 ; (DI) = segment flags

        and     edx,STE_HUGE_MASK AND NOT TYPE_MASK ; Not all bits matter
        or      dx,DATA_TYPE            ; Must have data segment
        and     ecx,STE_HUGE_MASK       ; Get significant bits only
        cmp     cx,dx                   ; Flags match?
        jne     ghs4                    ;  no, exit

;       (cy,bx=typeid,cx=nameid)=ldrGetRsrcTypeName(eax=prsrc,bx=segno,edi=mteptr)

        mov     ebx,[ghs_segno]         ; (EBX) = segment number
        xor     eax,eax                 ; (EAX) = 0 used for 32-bit guys only
        CCALL32 ldrGetRsrcTypeName
        jc      short ghs0              ; if not a rsrc seg, continue

        mov     [ghs_isrsrc],TRUE       ; we have a resource segment
        mov     [ghs_typeid],bx         ; save  resource type id
        mov     [ghs_nameid],cx         ; save resource name id

ghs0:   mov     eax,[ghs_segno]         ; (EAX) = segment number
        inc     [ghs_hugecnt]           ; ghs_hugecnt = 1

;       Loop to find all segments in huge segment.
;
;       (EAX) = current segment number
;       (DX) = flags
;       (EDI) = mte pointer
;       (ESI) = ste pointer

ghs0d:  inc     eax                     ; (EAX) = next segment number
        mov     ebx,[edi].mte_swapmte   ; (EBX) = swappable mte pointer
        cmp     eax,[ebx].smte_objcnt   ; End of table yet?
        ja      short ghs1              ;  yes, exit loop
        add     esi,SIZE ldrste_s       ; (ESI) = next ste pointer
        cmp     [esi].ste_selector,0    ; Selector assigned yet?
        jne     short ghs1              ;  yes, exit loop
        movzx   ecx,[esi].ste_flags     ; (CX) = segment flags
        and     ecx,STE_HUGE_MASK       ; Get bits we're interested in
        cmp     cx,dx                   ; Important flags match?
        jne     short ghs1              ;  no, exit loop

        mov     cx,[ghs_isrsrc]         ; (CX) = TRUE if resource, else 0
        jcxz    short ghs0l

;       (cy,bx=typeid,cx=nameid)=ldrGetRsrcTypeName(eax=prsrc,bx=segno,edi=mteptr)

        mov     bx,ax                   ; (BX) = segment number
        push    ax                      ; (AX) = segment number
        xor     eax,eax                 ; (EAX) = 0 used for 32-bit guys only
        CCALL32 ldrGetRsrcTypeName      ; must xret CY clr, i.e, always rsrc seg
        pop     ax                      ; (AX) = segment number

        cmp     bx,[ghs_typeid]         ; does the type id match?
        jne     short ghs1              ;  no, exit loop

        cmp     cx,[ghs_nameid]         ; does the name id match?
        jne     short ghs1              ;  no, exit loop

ghs0l:  inc     [ghs_hugecnt]           ; Another part of huge array
        cmp     [esi].ste_minsiz,0      ; Size = 64K?
        jz      short ghs0d             ;  yes, keep looking

;       If we have a huge segment, allocate consecutive
;       selectors for it, and set them in the segment table.

ghs1:   cmp     [ghs_hugecnt],2         ; At least two segments?
        jc      ghs4                    ;  no, exit

;       (DX) = flags
;       (EDI) = mte pointer
;       (ESI) = ste pointer

        SSToDS  eax,ss:ghs_ac
        PushP   eax                     ; return buffer
        xor     eax,eax
        PushP   eax                     ; selector flags
        mov     ecx,_pPTDACur
        movzx   eax,[ecx].ptda_child
        or      eax,eax
        jnz     short ghs1a
        movzx   eax,[ecx].ptda_handle
ghs1a:  PushP   eax                     ; ptda handle
        test    [edi].mte_flags1,LIBRARYMOD; Shared wanted?
        jnz     short ghs2a
        mov     ebx,[edi].mte_swapmte   ; (EBX) = swappable mte pointer
        mov     eax,[ebx].smte_objcnt   ; (AX) = number of segments
        sub     eax,[ebx].smte_rsrccnt  ; (AX) = number of non-resrc segments
        cmp     [ghs_segno].LowWord,ax  ; Is this a resource segment?
        ja      short ghs2a             ;  yes, we need to use shared selector
        mov     eax,VMAC_ARENAPRV       ; private arena wanted
        jmp     short ghs2b

ghs2a:  mov     eax,VMAC_ARENASHR
        cmp     [ghs_isrsrc],0        ; test for rsrc                     103120
        jnz     short ghs2b           ; put in GlobalSharedRegion if rsrc 103120
        or      eax,PG_W              ; force non GSR allocation for data 103120

ghs2b:  or      eax,(VMAC_ALIGNSEL+VMAC_PRERES+VMAC_LOCANY+VMAC_OBHUGE)
        PushP   eax                     ; selector flags
        movzx   eax,[ghs_hugecnt]       ; (EAX) = segment count
        dec     eax                     ; (EAX) = # of 64K segments
        shl     eax,16                  ; (EAX) = # of bytes in 64K segments
        .errnz  (_64K - (1 SHL 16))
        movzx   ebx,[esi].ste_minsiz    ; add last segment
        or      ebx,ebx                 ; check if last segment 64K
        jnz     short ghs2c
        mov     ebx,_64K

ghs2c:  add     eax,ebx                 ; (EAX) = size in bytes of huge segment
        PushP   eax
        cmp     [ghs_isrsrc],TRUE       ; save size if huge rsrc
        jne     short ghs2d
        mov     ebx,[ghs_psize]         ; (EBX) = pointer to return size
        mov     [ebx],eax               ; return size of huge rsrc

ghs2d:  CCALL32 _VMReserveMem,,,PushP
        or      eax,eax
        .errnz  NO_ERROR
        jne     short ghs8              ; Jump if error

;       Loop to fill in selectors in segment table.

        movzx   ecx,[ghs_hugecnt]
        mov     ebx,[ghs_phugecnt]      ; (EBX) = pointer to return huge cnt
        mov     [ebx],ecx               ; return count
        mov     esi,[ghs_pste]          ; restore ste pointer
        mov     ax,[ghs_ac].ac_sel      ; get selector allocated
ghs3:   mov     [esi].ste_selector,ax   ; Set selector
        or      [esi].ste_flags,STE_SELALLOC+STE_HUGE; Selector is allocated
        add     esi,SIZE ldrste_s       ; Increment pointer
        add     ax,HUGEINCR             ; Increment selector
        loop    short ghs3

        mov     esi,[ghs_pste]          ; restore ste pointer

;       Return to caller.

ghs4:   xor     eax,eax                 ; (EAX) = NO_ERROR
        .errnz  NO_ERROR
        jmp     short ghsx

;       return error

ghs8:   movzx   eax,ax                  ; (EAX) = error code

ghsx:   RestoreReg <edi,esi,ebx>
        LeaveProc
        RETC

EndProc _ldrGetHugeSel

LOAD_FTUNE1_SWP ENDS
LDRSWAP_TEXT       segment

;***LP  ldrGDTPreload - preload GDT segments in a mte
;
;       ENTRY   (EBX) = pointer to resident mte
;
;       EXIT    CARRY clear - success
;                 (AX) = 0
;                 all the GDT segments in the passed mte have been loaded
;               CARRY set - error
;                 (AX) = ERROR_PROTECTION_VIOLATION
;
;       USES    EAX, FLAGS
;
;       PSEUDOCODE
;               for each segment in the mte {
;                 if (selector is a GDT selector)
;                   touch the segment
;               }
        assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing

Procedure ldrGDTPreload,near
        LocalVar        gpl_segno,WORD  ; segment number
        LocalVar        pste,DWORD      ; pointer to ste

        EnterProc
        SaveReg <ecx,edx,edi>

        mov     gpl_segno,0             ; gpl_segno = segment number
        mov     edi,[ebx].mte_swapmte   ; (EAX) = swappable mte pointer
        mov     ecx,[edi].smte_objtab   ; (Edi) pointer to segment table
gpl4:   mov     pste,ecx                ; save pointer to ste
        movzx   eax,gpl_segno           ; (AX) = segment number
        cmp     eax,[edi].smte_objcnt   ; for each segment in module
        jae     short gpl12             ; end of segment table

        movzx   eax,[ecx].ste_selector
        test    eax,TABLE_MASK          ; is this a GDT selector?
        jnz     short gpl8              ;  no, needn't force preload here

        push    eax                     ; save selector
        PushP   eax                     ; selector
        PushP   0                       ; offset 0
        CCALL32 _SELVirtToLin,,,PushP   ; convert gdt selector to linear addr
        pop     edx                     ; restore selector
        cmp     eax,-1                  ; return of (-1) = error
        je      short gpl16

gpl6:   push    eax                     ; save linear addr
        PushP   edx                     ; selector
        PushP   eax                     ; linear addr
        movzx   eax,[ebx].mte_handle
        PushP   eax                     ; mte handle
        CCALL32 _LDRGetSegment,,,PushP  ; preload load segment
        pop     edx                     ; restore linear addr
        or      eax,eax
        .errnz  NO_ERROR
        jne     short gpl16

gpl8:   inc     gpl_segno
        mov     ecx,pste                ; restore ste
        add     ecx, SIZE ldrste_t      ; move to next ste entry
        jmp     short gpl4

gpl12:  xor     eax,eax                 ; indicate success
gpl16:  RestoreReg <edi,edx,ecx>
        LeaveProc
        RETC
EndProc ldrGDTPreload


LDRSWAP_TEXT ENDS

        END
