; =========================================================================
;
; Pseudo EXEC system call for DOS
;
; =========================================================================
.cref
.list

IF 1
__TONYI__1 = 1          ; Space saving hacks 10/31/92
ENDIF


        TITLE   MSPROC - process maintenance
        NAME    MSPROC
;
;       Microsoft Confidential
;       Copyright (C) Microsoft Corporation 1991
;       All Rights Reserved.
;
        PAGE    ,132
; =========================================================================
;**     Process related system calls and low level routines for DOS 2.X.
;       I/O specs are defined in DISPATCH.
;
;       $WAIT
;       $EXEC
;       $Keep_process
;       Stay_resident
;       $EXIT
;       $ABORT
;       abort_inner
;
;       Modification history:
;
;               Created: ARR 30 March 1983
;               AN000   version 4.0 jan. 1988
;               A007    PTM 3957 - fake vesrion for IBMCACHE.COM
;               A008    PTM 4070 - fake version for MS WINDOWS
;
;               M000    added support for loading programs into UMBs 7/9/90
;
;               M004 - MS PASCAL 3.2 support. Please see under tag M003 in
;                      dossym.inc. 7/30/90
;               M005 - Support for EXE programs with out STACK segment and
;                      with resident size < 64K - 256 bytes. A 256 byte
;                      stack is provided at the end of the program. Note that
;                      only SP is changed.
;               M020 - Fix for Rational bug for details see exepatch.asm
;
;               M028 - 4b04 implementation
;
;               M029 - Support for EXEs without stack rewritten. If EXE is
;                       in memory block >= 64K, sp = 0. If memory block
;                       obtained is <64K, point sp at the end of the memory
;                       block. For EXEs smaller than 64K, 256 bytes are still
;                       added for a stack segment which may be needed if it
;                       is loaded in low memory situations.
;
;               M030 - Fixing bug in EXEPACPATCH & changing 4b04 to 4b05
;
;               M040 - Bug #3052. The environment sizing code would flag a
;                       a bad environment if it reached 32767 bytes. Changed
;                       to allow 32768 bytes of environment.
;
;               M047 - Release the allocated UMB when we failed to load a
;                      COM file high. Also ensure that if the biggest block
;                      into which we load the com file is less than 64K then
;                      we provide atleast 256 bytes of stack to the user.
;
;               M050 - Made Lie table search CASE insensitive
;
;               M060 - Removed special version table from the kernal and
;                      put it in a device drive which puts the address
;                      in the DOS DATA area location UU_IFS_DOS_CALL
;                      as a DWORD.
;
;               M063 - Modified UMB support. If the HIGH_ONLY bit is set on
;                      entry do not try to load low if there is no space in
;                      UMBs.
;
;               M068 - Support for copy protect apps. Call ChkCopyProt to
;                      set a20off_count. Set bit EXECA20BIT in DOS_FLAG. Also
;                      change return address to LeaveDos if AL=5.
;
;               20-Jul-1992 bens    Added ifdef RESTRICTED_BUILD code that
;                      controls building a version of MSDOS.SYS that only
;                      runs programs from a fixed list (defined in the
;                      file RESTRICT.INC).  Search for "RESTRICTED_BUILD"
;                      for details.  This feature is used to build a
;                      "special" version of DOS that can be handed out to
;                      OEM/ISV customers as part of a "service" disk.
;
;        797  04/10/93  jdn  Beta drop from MS
;
; =========================================================================

.XLIST
.XCREF

INCLUDE version.inc
INCLUDE dosseg.inc
INCLUDE DOSSYM.INC
INCLUDE DEVSYM.INC
INCLUDE exe.inc
INCLUDE sf.inc
INCLUDE curdir.inc
INCLUDE syscall.inc
INCLUDE arena.inc
INCLUDE pdb.inc
INCLUDE vector.inc

.CREF
.LIST

public  retexepatch

; =========================================================================

ifdef   ROMEXEC
ifndef  JAPAN

BDATA   segment at 70h
        extrn   RomStartAddr    :word
BDATA   ends

endif ; JAPAN
endif ; ROMEXEC

; =========================================================================

DosData SEGMENT WORD PUBLIC 'DATA'

        EXTRN   CreatePDB       :BYTE
        EXTRN   DidCtrlC        :BYTE
        EXTRN   Exit_type       :BYTE
        EXTRN   ExtErr_Locus    :BYTE   ; Extended Error Locus
        EXTRN   InDos           :BYTE
        EXTRN   OpenBuf         :BYTE
        EXTRN   CurrentPDB      :WORD
        EXTRN   Exit_code       :WORD
        EXTRN   DmaAdd          :DWORD

                ; the following includes & i_needs are for exec.asm
                ; which is included in this source

; VCB - Delete unreferenced data area Fake_Count
;
;       EXTRN   Fake_Count      :BYTE   ; Fake version count

        EXTRN   Special_Entries :WORD   ; Address of special entries
        EXTRN   Special_Version :WORD   ; Special version number
        EXTRN   Temp_Var2       :WORD   ; File type from $open

                ; following i_needs are becuse of moving these vars from
                ; exec.asm to ../inc/ms_data.asm

        EXTRN   exec_init_SP    :WORD
        EXTRN   exec_init_SS    :WORD
        EXTRN   exec_init_IP    :WORD
        EXTRN   exec_init_CS    :WORD


        EXTRN   exec_signature  :WORD   ; Must contain 4D5A  (yay zibo!)
        EXTRN   exec_len_mod_512:WORD   ; Low 9 bits of length
        EXTRN   exec_pages      :WORD   ; Number of 512b pages in file
        EXTRN   exec_rle_count  :WORD   ; Count of reloc entries
        EXTRN   exec_par_dir    :WORD   ; Number of paragraphs before image
        EXTRN   exec_min_BSS    :WORD   ; Minimum number of para of BSS
        EXTRN   exec_max_BSS    :WORD   ; Max number of para of BSS
        EXTRN   exec_SS         :WORD   ; Stack of image
        EXTRN   exec_SP         :WORD   ; SP of image
        EXTRN   exec_chksum     :WORD   ; Checksum  of file (ignored)
        EXTRN   exec_IP         :WORD   ; IP of entry
        EXTRN   exec_CS         :WORD   ; CS of entry
        EXTRN   exec_rle_table  :WORD   ; Byte offset of reloc table

        EXTRN   DOS_FLAG        :BYTE   ; flag to indicate to redir that open
                                        ; came from exec.


        EXTRN   AllocMethod     :BYTE   ; how to alloc first(best)last
        EXTRN   SAVE_AX         :WORD   ; temp to save ax
        EXTRN   AllocMsave      :BYTE   ; M063: temp to save AllocMethod

        EXTRN   UU_IFS_DOS_CALL :DWORD  ; M060 Ptr to version table

        EXTRN   A20OFF_PSP      :WORD   ; M068
        EXTRN   A20OFF_COUNT    :BYTE   ; M068


; =========================================================================

        EXTRN   Disa20_Xfer     :WORD

        allow_getdseg

        EXTRN   DriverLoad      :BYTE
        EXTRN   BiosDataPtr     :DWORD
        extrn   DosHasHMA       :byte           ; M021
        extrn   fixexepatch     :word
        extrn   Rational386PatchPtr:word        ; 797 - jdn
        extrn   ChkCopyProt     :word           ; M068
        extrn   LeaveDos        :word           ; M068


DosData ENDS

; =========================================================================

DOSCODE SEGMENT
        ASSUME  SS:DOSDATA,CS:DOSCODE


        EXTRN   ExecReady:near
        EXTRN   UCase:near                      ; M050


SAVEXIT         EQU     10

        BREAK   <$WAIT - return previous process error code>

; =========================================================================
;       $WAIT - Return previous process error code.
;
;       Assembler usage:
;
;           MOV     AH, WaitProcess
;           INT     int_command
;
;       ENTRY   none
;       EXIT    (ax) = exit code
;       USES    all
; =========================================================================

        ASSUME  DS:NOTHING,ES:NOTHING

PROCEDURE $Wait ,NEAR

        xor     AX,AX
        xchg    AX,exit_code
        transfer Sys_Ret_OK

ENDPROC $Wait

ifdef RESTRICTED_BUILD
;***    IsFileOnGuestList - Check that file name is allowed to execute
;
;       This function is called by $Exec just before it attempts to
;       $Open the program file.
;
;       Entry
;           ds:dx = asciiz "guest" file name.
;                   Must be upper case and have no meta characters.
;           CLD
;
;       Exit-Success
;           returns Carry CLEAR
;
;       Exit-Failure
;           returns Carry SET
;           ax = error_access_denied

IsFileOnGuestList  proc    near
        SaveReg <bx,cx,si,di,ds,es>

;*  Compute length of guest name

        push    ds
        pop     es
        mov     di,dx                   ; es:di -> guest name
        invoke  StrLen                  ; cx = length of name including 00
        mov     bx,cx                   ; Save length for repeated use

;*  Get list of allowed names
        push    cs
        pop     es
        mov     di,offset aszRestrict   ; es:di -> allowed name list

;*  Set null character and no error in one fell swoop!
        xor     ax,ax

;*  Scan list for matching file name
;
;   ax     = 0 (used for repne scasb and successful return code)
;   bx     = length of guest name, including 00 terminator
;   ds:dx -> guest name
;   es:di -> next name on list

fgl10:  cmp     byte ptr es:[di],00     ; At end of list?
        jz      fgle                    ;   YES, file not allowed, go fail

;*  Compare names
        mov     si,dx                   ; ds:si -> guest name
        mov     cx,bx                   ; Length of guest name
        repe    cmpsb                   ; Do names match?
        jz      fglx                    ;   YES, return success

;*  Now we either ran past the end of the string in the list, or we are not
;   at the end, but found a difference.  We back up one character, since
;   repe cmpsb advanced one character past the difference, and then scan
;   the string in the list to find its end.

        dec     di                      ; es:di -> character that did not match
        mov     cx,-1                   ; Make sure we look far enough ahead
        repne   scasb                   ; Find tail of name in list

;*  Now es:[di] is first character of the next name in the list (or 00)
;
        jmp     fgl10                   ; Go check next name

;*  Name not found on list
;
fgle:   mov     ax,error_access_denied  ; Set error code
        stc                             ; Indicate error

fglx:   RestoreReg <es,ds,di,si,cx,bx>
        ret
IsFileOnGuestList  endp


;**     rstfile - macro to add file name to list
;
rstfile MACRO   name
    db  name,0
endm

;**     aszRestrict - list of files allowed to execute
;
aszRestrict LABEL   byte

include restrict.inc    ; build table

    db  0               ; terminate table

endif ; RESTRICTED_BUILD


; =========================================================================
;BREAK <$exec - load/go a program>
;       EXEC.ASM - EXEC System Call
;
;
; Assembler usage:
;           lds     DX, Name
;           les     BX, Blk
;           mov     AH, Exec
;           mov     AL, FUNC
;           int     INT_COMMAND
;
;       AL  Function
;       --  --------
;        0  Load and execute the program.
;        1  Load, create  the  program  header  but  do  not
;           begin execution.
;        3  Load overlay. No header created.
;
;           AL = 0 -> load/execute program
;
;           +---------------------------+
;           | WORD segment address of   |
;           | environment.              |
;           +---------------------------+
;           | DWORD pointer to ASCIZ    |
;           | command line at 80h       |
;           +---------------------------+
;           | DWORD pointer to default  |
;           | FCB to be passed at 5Ch   |
;           +---------------------------+
;           | DWORD pointer to default  |
;           | FCB to be passed at 6Ch   |
;           +---------------------------+
;
;           AL = 1 -> load program
;
;           +---------------------------+
;           | WORD segment address of   |
;           | environment.              |
;           +---------------------------+
;           | DWORD pointer to ASCIZ    |
;           | command line at 80h       |
;           +---------------------------+
;           | DWORD pointer to default  |
;           | FCB to be passed at 5Ch   |
;           +---------------------------+
;           | DWORD pointer to default  |
;           | FCB to be passed at 6Ch   |
;           +---------------------------+
;           | DWORD returned value of   |
;           | CS:IP                     |
;           +---------------------------+
;           | DWORD returned value of   |
;           | SS:IP                     |
;           +---------------------------+
;
;           AL = 3 -> load overlay
;
;           +---------------------------+
;           | WORD segment address where|
;           | file will be loaded.      |
;           +---------------------------+
;           | WORD relocation factor to |
;           | be applied to the image.  |
;           +---------------------------+
;
; Returns:
;           AX = error_invalid_function
;              = error_bad_format
;              = error_bad_environment
;              = error_not_enough_memory
;              = error_file_not_found
; =========================================================================
;
;   Revision history:
;
;        A000   version 4.00  Jan. 1988
;
; =========================================================================

        EXTRN   Exec_Header_Len :ABS

Exec_Internal_Buffer            EQU     OpenBuf
Exec_Internal_Buffer_Size       EQU     (128+128+53+curdirLEN)

ifdef   ROMEXEC                         ;SR; Externals from romfind.asm

        EXTRN   Check_name      :NEAR
        EXTRN   Check_ROM       :NEAR

        IFDEF   JAPAN                   ; YI 09/05/89

                EXTRN   ROMSTARTSEG     :ABS
                EXTRN   ROMENDSEG       :ABS
        ENDIF
        EXTRN   BioDataSeg      :WORD

endif ; ROMEXEC

; =========================================================================
;
; =========================================================================

procedure       $Exec,NEAR

        PUBLIC EXEC001S
EXEC001S:

        LocalVar    Exec_Blk            ,DWORD
        LocalVar    Exec_Func           ,BYTE
        LocalVar    Exec_Load_High      ,BYTE
        LocalVar    Exec_FH             ,WORD
        LocalVar    Exec_Rel_Fac        ,WORD
        LocalVar    Exec_Res_Len_Para   ,WORD
        LocalVar    Exec_Environ        ,WORD
        LocalVar    Exec_Size           ,WORD
        LocalVar    Exec_Load_Block     ,WORD
        LocalVar    Exec_DMA            ,WORD
        LocalVar    ExecNameLen         ,WORD
        LocalVar    ExecName            ,DWORD

        LocalVar    Exec_DMA_Save       ,WORD
        LocalVar    Exec_NoStack        ,BYTE

ifdef ROMEXEC
                        ; Added 2 local variables to support exec from ROM
        LocalVar    ExecRomFound        ,BYTE
        LocalVar    ExecRomAddr         ,DWORD

endif ; ROMEXEC

        ; ==================================================================
        ; validate function
        ; ==================================================================

        PUBLIC  EXEC001E
EXEC001E:

        ;
        ; M068 - Start
        ;
        ; Reset the A20OFF_COUNT to 0. This is done as there is a
        ; possibility that the count may not be decremented all the way to
        ; 0. A typical case is if the program for which we intended to keep
        ; the A20  off for a sufficiently long time (A20OFF_COUNT int 21
        ; calls), exits pre-maturely due to error conditions.
        ;

        mov     [A20OFF_COUNT], 0

        ;
        ; If al=5 (ExecReady) we'll change the return address on the stack
        ; to be LeaveDos in msdisp.asm. This ensures that the EXECA20OFF
        ; bit set in DOS_FLAG by ExceReady is not cleared in msdisp.asm
        ;
        cmp     al, 5                   ; Q: is this ExecReady call
        jne     @f                      ; N: continue
                                        ; Y: change ret addr. to LeaveDos.
        pop     cx                      ; Note CX is not input to ExecReady
        mov     cx, offset DOSCODE:LeaveDos
        push    cx
@@:
        ;
        ; M068 - End
        ;

        Enter

        cmp     AL,5                    ; only 0, 1, 3 or 5 are allowed ;M028
                                        ; M030
        jna     exec_check_2

Exec_Bad_Fun:
IFDEF __TONYI__1
;---------------------------------------------------
; Save 3 bytes......
;---------------------------------------------------
        invoke  SetErrLocUnk
ELSE
        mov     ExtErr_Locus,ErrLoc_Unk ; Extended Error Locus  ;smr;SS Override
ENDIF
        mov     al,Error_Invalid_Function

Exec_Ret_Err:
        Leave
        transfer    SYS_RET_ERR
ExecReadyJ:
        call    ExecReady               ; M028
        jmp     norm_ovl                ; do a Leave & xfer sysret_OK ; M028

Exec_Check_2:
        cmp     AL,2
        jz      Exec_Bad_Fun

        cmp     al, 4                   ; 2 & 4 are not allowed
        je      Exec_Bad_Fun

        cmp     al, 5                   ; M028 ; M030
        je      ExecReadyJ              ; M028

        mov     Exec_BlkL,BX            ; stash args
        mov     Exec_BlkH,ES
        mov     Exec_Func,AL
        mov     Exec_Load_high,0

        mov     execNameL,DX            ; set up length of exec name
        mov     execNameH,DS
        mov     SI,DX                   ; move pointer to convenient place
        invoke  DStrLen
        mov     ExecNameLen,CX          ; save length

ifdef ROMEXEC
                                        ; Check if ROM program
        mov     execRomFound,0          ; assume not in ROM
                                        ; Do not check for ROM if load overlay
        test    BYTE PTR Exec_Func,EXEC_FUNC_OVERLAY
        jnz     Do_Normal

                                        ; A ROM program exec should have no
                                        ; path or drive specifiers
        push    DX
        push    CX
        push    DS
        push    SI
        push    ES
        dec     CX                      ; length should not include NUL char

                ; Check if the program exists in  ROM - returns address in
                ; dx,ax if success

        call    Check_name              ; DS:SI points to string
        jc      not_rom_name

Exist_ROM:

        push    DS
        mov     DS, CS:[BioDataSeg]
IFDEF   JAPAN                           ; YI 09/05/89
        mov     AX,DS:[ROMSTARTSEG]
ELSE
        mov     AX,DS:RomStartAddr
ENDIF
        pop     DS

        mov     ES,AX
        xor     AX,AX                   ; setup start addr for ROM scan
        call    Check_ROM
        jc      not_rom_name

        mov     execRomFound,1          ; flag that program is in ROM
        mov     execRomAddrL,AX
        mov     execRomAddrH,ES
        pop     ES
        pop     SI                      ; Skip file opening and checking -
        pop     DS                      ; check environment
        pop     CX
        pop     DX
        jmp     short exec_check_environ

not_rom_name:
        pop     ES
        pop     SI
        pop     DS
        pop     CX
        pop     DX                      ; restore dx

do_normal:

endif ; ROMEXEC

        mov     al, [AllocMethod]       ; M063: save alloc method in
        mov     [AllocMsave], al        ; M063: AllocMsave

        xor     AL,AL                   ; open for reading
        push    BP

        or      [DOS_FLAG], EXECOPEN    ; this flag is set to indicate to
                                        ; the redir that this open call is
                                        ; due to an exec.
ifdef RESTRICTED_BUILD
        call    IsFileOnGuestList       ; Should we execute this?
        jc      @F                      ;   NO, skip open and fail
endif ; RESTRICTED_BUILD

        invoke  $OPEN                   ; is the file there?

ifdef RESTRICTED_BUILD
@@: ;* Come here if file name does not pass muster
endif ; RESTRICTED_BUILD

        pushf
        and     [DOS_FLAG], not EXECOPEN; reset flag
        popf

        pop     BP
ifdef   ROMEXEC
        jnc     @F
        jmp     Exec_Ret_Err
@@:
else
        jc      Exec_Ret_Err
endif

        mov     Exec_Fh,AX
        mov     BX,AX
        xor     AL,AL
        invoke  $Ioctl
        jc      Exec_BombJ

        test    DL,DEVID_ISDEV
        jz      Exec_Check_Environ

        mov     AL,ERROR_FILE_NOT_FOUND
Exec_bombJ:
        jmp     Exec_Bomb

BadEnv:
        mov     AL,ERROR_BAD_ENVIRONMENT
        jmp     Exec_Bomb

Exec_Check_Environ:
        mov     Exec_Load_Block,0
        mov     Exec_Environ,0
                                        ; overlays... no environment
        test    BYTE PTR Exec_Func,EXEC_FUNC_OVERLAY
        jnz     Exec_Read_Header

        lds     SI,Exec_Blk             ; get block
        mov     AX,[SI].Exec1_Environ   ; address of environ
        or      AX,AX
        jnz     exec_scan_env

        mov     DS,CurrentPDB           ;smr;SS Override
        mov     AX,DS:[PDB_environ]

;---------------------------------------------BUG 92 4/30/90-----------------
;
; Exec_environ is being correctly initialized after the environment has been
; allocated and copied form the parent's env. It must not be initialized here.
; Because if the call to $alloc below fails Exec_dealloc will deallocate the
; parent's environment.
;       mov     Exec_Environ,AX
;
;----------------------------------------------------------------------------

        or      AX,AX
        jz      Exec_Read_Header

Exec_Scan_Env:
        mov     ES,AX
        xor     DI,DI
        mov     CX,8000h                ; at most 32k of environment ;M040
        xor     AL,AL

Exec_Get_Environ_Len:
        repnz   scasb                   ; find that nul byte
        jnz     BadEnv

        dec     CX                      ; Dec CX for the next nul byte test
        js      BadEnv                  ; gone beyond the end of the environment

        scasb                           ; is there another nul byte?
        jnz     Exec_Get_Environ_Len    ; no, scan some more

        push    DI
        lea     BX,[DI+0Fh+2]
        add     BX,ExecNameLen          ; BX <- length of environment
                                        ; remember argv[0] length
                                        ; round up and remember argc
        mov     CL,4
        shr     BX,CL                   ; number of paragraphs needed
        push    ES
        invoke  $Alloc                  ; can we get the space?
        pop     DS
        pop     CX
        jnc     Exec_Save_Environ

        jmp     SHORT Exec_No_Mem       ; nope... cry and sob

Exec_Save_Environ:
        mov     ES,AX
        mov     Exec_Environ,AX         ; save him for a rainy day
        xor     SI,SI
        mov     DI,SI
        rep     movsb                   ; copy the environment
IFDEF __TONYI__1
;-------------------------------------------------------------
; We *know* CX==0 after that REP MOVS.  We can save a byte here
; with XCHG/INC
;-------------------------------------------------------------
        xchg    ax,cx           ; AX == 0
        inc     ax              ; AX == 1
ELSE
        mov     AX,1
ENDIF
        stosw
        lds     SI,ExecName
        mov     CX,ExecNameLen
        rep     movsb

Exec_Read_Header:

ifdef ROMEXEC
                ; SR; For a ROM program, we skip reading the program header.
                ; We assume it to be a .COM program and go ahead.

        cmp     ExecRomFound,1
        jne     NoRom
        jmp     Exec_Com_File           ;program is in ROM
NoRom:

endif ; ROMEXEC

                ; We read in the program header into the above data area and
                ; determine where in this memory the image will be located.

        Context DS
        mov     CX,Exec_Header_Len      ; header size
        mov     DX,OFFSET DosData:Exec_Signature
        push    ES
        push    DS
        call    ExecRead
        pop     DS
        pop     ES
        jc      Exec_Bad_File

        or      AX,AX
        jz      Exec_Bad_File
        cmp     AX,EXEC_HEADER_LEN      ; did we read the right number?
        jnz     Exec_Com_Filej          ; yep... continue

        test    Exec_Max_BSS,-1         ; indicate load high?
        jnz     Exec_Check_Sig

        mov     Exec_Load_High,-1

Exec_Check_Sig:
        mov     AX,Exec_Signature       ; rms;NSS
        cmp     AX,Exe_Valid_Signature  ; zibo arises!
        jz      Exec_Save_Start         ; assume com file if no signature

        cmp     AX,exe_valid_Old_Signature  ; zibo arises!
        jz      Exec_Save_Start         ; assume com file if no signature

Exec_Com_Filej:
        jmp     Exec_Com_file

                ; We have the program header... determine memory requirements

Exec_Save_Start:
        mov     AX,Exec_Pages           ; get 512-byte pages    ;rms;NSS
        mov     CL,5                    ; convert to paragraphs
        shl     AX,CL
        sub     AX,Exec_Par_Dir         ; AX = size in paragraphs;rms;NSS
        mov     Exec_Res_Len_Para,AX

                ; Do we need to allocate memory?
                ; Yes if function is not load-overlay

        test    BYTE PTR exec_func,exec_func_overlay
        jz      exec_allocate           ; allocation of space

                ; get load address from block

        les     DI,Exec_Blk
IFDEF __TONYI__1
;--------------------------------------------------------------
; Save 3 bytes with LES...
;--------------------------------------------------------------
        .errnz  (EXEC3_RELOC_FAC - EXEC3_LOAD_ADDR - 2)
        push    es
        les     ax, dword ptr es:[di].Exec3_Load_Addr
        mov     exec_dma,ax
        mov     Exec_Rel_Fac,es
        pop     es
ELSE
        mov     AX,ES:[DI].Exec3_Load_Addr
        mov     exec_dma,AX
        mov     AX,ES:[DI].Exec3_Reloc_Fac
        mov     Exec_Rel_Fac,AX
ENDIF
        jmp     Exec_Find_Res           ; M000

Exec_No_Mem:
        mov     AL,Error_Not_Enough_Memory
        jmp     SHORT Exec_Bomb

Exec_Bad_File:
        mov     AL,Error_Bad_Format

Exec_Bomb:
        ASSUME  DS:NOTHING,ES:NOTHING

        mov     BX,Exec_fh
        call    Exec_Dealloc
        LeaveCrit   CritMem
        save    <AX,BP>
        invoke  $CLOSE
        restore <BP,AX>
        jmp     Exec_Ret_Err


Exec_Chk_Mem:
                                        ; M063 - Start
        mov     al, [AllocMethod]       ; save current alloc method in ax
        mov     bl, [AllocMsave]
        mov     [AllocMethod], bl       ; restore original allocmethod
        test    bl, HIGH_ONLY           ; Q: was the HIGH_ONLY bit already set
        jnz     Exec_No_Mem             ; Y: no space in UMBs. Quit
                                        ; N: continue

        test    al, HIGH_ONLY           ; Q: did we set the HIGH_ONLY bit
        jz      Exec_No_Mem             ; N: no memory
        mov     ax, [save_ax]           ; Y: restore ax and
        jmp     short Exec_Norm_Alloc   ;    Try again
                                        ; M063 - End

Exec_Allocate:
        DOSAssume   <DS>,"exec_allocate"


                ; M005 - START
                ; If there is no STACK segment for this exe file and if this
                ; not an overlay and the resident size is less than 64K -
                ; 256 bytes we shall add 256bytes bytes to the programs
                ; resident memory requirement and set Exec_SP to this value.

IFDEF __TONYI__1
;--------------------------------------------------------------
; Save a byte here.  BX is free, it gets reloaded below.
;--------------------------------------------------------------
        xor     bx,bx
        mov     Exec_NoStack,bl
        cmp     Exec_SS, bx             ; Q: is there a stack seg
        jne     @f                      ; Y: continue normal processing
        cmp     Exec_SP, bx             ; Q: is there a stack ptr
        jne     @f                      ; Y: continue normal processing
ELSE
        mov     Exec_NoStack,0
        cmp     Exec_SS, 0              ; Q: is there a stack seg
        jne     @f                      ; Y: continue normal processing
        cmp     Exec_SP, 0              ; Q: is there a stack ptr
        jne     @f                      ; Y: continue normal processing
ENDIF
        inc     Exec_NoStack
        cmp     ax, 01000h-10h          ; Q: is this >= 64K-256 bytes
        jae     @f                      ; Y: don't set Exec_SP

        add     ax, 010h                ; add 10h paras to mem requirement
@@:

                ; M005 - END

                                        ; M000 - start
        test    byte ptr [AllocMethod], HIGH_FIRST
                                        ; Q: is the alloc strat high_first
        jz      Exec_Norm_Alloc         ; N: normal allocate
                                        ; Y: set high_only bit
        or      byte ptr [AllocMethod], HIGH_ONLY
                                        ; M000 - end

Exec_Norm_Alloc:

        mov     [save_ax], ax           ; M000: save ax for possible 2nd
                                        ; M000: attempt at allocating memory
        mov     BX,0ffffh               ; see how much room in arena
        push    DS
        invoke  $Alloc                  ; should have carry set and BX has max
        pop     DS
        mov     ax, [save_ax]           ; M000
        add     AX,10h                  ; room for header
        cmp     BX,11h                  ; enough room for a header

        jb      Exec_Chk_Mem            ; M000

        cmp     AX,BX                   ; is there enough for bare image?
        ja      Exec_Chk_Mem            ; M000

        test    Exec_Load_High,-1       ; if load high, use max
        jnz     Exec_BX_Max             ; use max

        add     AX,Exec_Min_BSS         ; go for min allocation;rms;NSS
        jc      Exec_Chk_Mem            ; M000

        cmp     AX,BX                   ; enough space?
        ja      Exec_Chk_Mem            ; M000: nope...


        sub     AX,Exec_Min_BSS         ; rms;NSS
        add     AX,Exec_Max_BSS         ; go for the MAX
        jc      Exec_BX_Max

        cmp     AX,BX
        jbe     Exec_Got_Block

Exec_BX_Max:
        mov     AX,BX

Exec_Got_Block:
        push    DS
        mov     BX,AX
        mov     exec_size,BX
        invoke  $Alloc                  ; get the space
        pop     DS

        ljc     exec_chk_mem            ; M000

        mov     cl, [AllocMsave]        ; M063:
        mov     [AllocMethod], cl       ; M063: restore allocmethod


;M029; Begin changes
; This code does special handling for programs with no stack segment. If so,
;check if the current block is larger than 64K. If so, we do not modify
;Exec_SP. If smaller than 64K, we make Exec_SP = top of block. In either
;case Exec_SS is not changed.
;
        cmp     Exec_NoStack,0
        je      @f

        cmp     bx,1000h                ; Q: >= 64K memory block
        jae     @f                      ; Y: Exec_SP = 0

;
;Make Exec_SP point at the top of the memory block
;
        mov     cl,4
        shl     bx,cl                   ; get byte offset
        sub     bx,100h         ; take care of PSP
        mov     Exec_SP,bx              ; Exec_SP = top of block
@@:
;
;M029; end changes
;

        mov     exec_load_block,AX
        add     AX,10h
        test    exec_load_high,-1
        jz      exec_use_ax             ; use ax for load info

        add     AX,exec_size            ; go to end
        sub     AX,exec_res_len_para    ; drop off header
        sub     AX,10h                  ; drop off pdb

Exec_Use_AX:
        mov     Exec_Rel_Fac,AX         ; new segment
        mov     Exec_Dma,AX             ; beginning of dma

                ; Determine the location in the file of the beginning of
                ; the resident

Exec_Find_Res:

        mov     DX, exec_dma
        mov     exec_dma_save, DX

        mov     DX,Exec_Par_Dir
        push    DX
        mov     CL,4
        shl     DX,CL                   ; low word of location
        pop     AX
        mov     CL,12
        shr     AX,CL                   ; high word of location
        mov     CX,AX                   ; CX <- high

                ; Read in the resident image (first, seek to it)

        mov     BX,Exec_FH
        push    DS
        xor     AL,AL
        invoke  $Lseek                  ; Seek to resident
        pop     DS
        jnc     exec_big_read

        jmp     exec_bomb

Exec_Big_Read:                          ; Read resident into memory
        mov     BX,Exec_Res_Len_Para
        cmp     BX,1000h                ; Too many bytes to read?
        jb      Exec_Read_OK

        mov     BX,0fe0h                ; Max in one chunk FE00 bytes

Exec_Read_OK:
        sub     Exec_Res_Len_Para,BX    ; We read (soon) this many
        push    BX
        mov     CL,4
        shl     BX,CL                   ; Get count in bytes from paras
        mov     CX,BX                   ; Count in correct register
        push    DS
        mov     DS,Exec_DMA             ; Set up read buffer

        ASSUME  DS:NOTHING

        xor     DX,DX
        push    CX                      ; Save our count
        call    ExecRead
        pop     CX                      ; Get old count to verify
        pop     DS
        jc      Exec_Bad_FileJ

        DOSAssume   <DS>,"exec_read_ok"

        cmp     CX,AX                   ; Did we read enough?
        pop     BX                      ; Get paragraph count back
        jz      ExecCheckEnd            ; and do reloc if no more to read

                ; The read did not match the request. If we are off by 512
                ; bytes or more then the header lied and we have an error.

        sub     CX,AX
        cmp     CX,512
        jae     Exec_Bad_FileJ

                ; We've read in CX bytes... bump DTA location

ExecCheckEnd:
        add     Exec_DMA,BX             ; Bump dma address
        test    Exec_Res_Len_Para,-1
        jnz     Exec_Big_Read

                ; The image has now been read in. We must perform relocation
                ; to the current location.


ifdef ROMDOS
        cmp     [DosHasHMA], 0          ; DOS using HMA?  (M021)
        je      exec_do_reloc           ; if not, then no patch needed.
endif

exec_do_reloc:
        mov     CX,Exec_Rel_Fac
        mov     AX,Exec_SS              ; get initial SS ;rms;NSS
        add     AX,CX                   ; and relocate him
        mov     Exec_Init_SS,AX         ; rms;NSS

        mov     AX,Exec_SP              ; initial SP ;rms;NSS
        mov     Exec_Init_SP,AX         ; rms;NSS



        les     AX,DWORD PTR exec_IP    ; rms;NSS
        mov     Exec_Init_IP,AX         ; rms;NSS
        mov     AX,ES                   ; rms;NSS
        add     AX,CX                   ; relocated...
        mov     Exec_Init_CS,AX         ; rms;NSS

        xor     CX,CX
        mov     DX,Exec_RLE_Table       ; rms;NSS
        mov     BX,Exec_FH
        push    DS
        xor     AX,AX
        invoke  $Lseek
        pop     DS

        jnc     Exec_Get_Entries

exec_bad_filej:
        jmp     Exec_Bad_File

exec_get_entries:
        mov     DX,Exec_RLE_Count       ; Number of entries left ;rms;NSS

exec_read_reloc:
        ASSUME  DS:NOTHING

        push    DX
        mov     DX,OFFSET DOSDATA:Exec_Internal_Buffer
        mov     CX,((EXEC_INTERNAL_BUFFER_SIZE)/4)*4
        push    DS
        call    ExecRead
        pop     ES
        pop     DX
        jc      Exec_Bad_FileJ

        mov     CX,(EXEC_INTERNAL_BUFFER_SIZE)/4
                                        ; Pointer to byte location in header
        mov     DI,OFFSET DOSDATA:exec_internal_buffer
        mov     SI,Exec_Rel_Fac         ; Relocate a single address

exec_reloc_one:
        or      DX,DX                   ; Any more entries?
        je      Exec_Set_PDBJ

exec_get_addr:
        lds     BX,DWORD PTR ES:[DI]    ; Get ra/sa of entry
        mov     AX,DS                   ; Relocate address of item
        add     AX, exec_dma_save
        mov     DS,AX
        add     [BX],SI
        add     DI,4
        dec     DX
        loop    Exec_Reloc_One          ; End of internal buffer?

                ; We've exhausted a single buffer's worth. Read in the next
                ; piece of the relocation table.

        push    ES
        pop     DS
        jmp     SHORT Exec_Read_Reloc                                 ;AC000;

Exec_Set_PDBJ:

                ;
                ; We now determine if this is a buggy exe packed file and if
                ; so we patch in the right code. Note that fixexepatch will
                ; point to a ret if dos loads low. The load segment as
                ; determined above will be in exec_dma_save
                ;

        push    es
        push    ax                      ; M030
        push    cx                      ; M030
        mov     es, exec_dma_save
        mov     ax, exec_init_CS        ; M030
        mov     cx, exec_init_IP        ; M030
        call    word ptr [fixexepatch]
        call    word ptr [Rational386PatchPtr]  ; 797 - jdn
        pop     cx                      ; M030
        pop     ax                      ; M030
        pop     es

        jmp     Exec_Set_PDB

Exec_No_Memj:
        jmp     Exec_No_Mem

                ; we have a .COM file.  First, determine if we are merely
                ; loading an overlay.

Exec_Com_File:
        test    BYTE PTR Exec_Func,EXEC_FUNC_OVERLAY
        jz      Exec_Alloc_Com_File
        lds     SI,Exec_Blk             ; get arg block
        lodsw                           ; get load address
        mov     Exec_DMA,AX
        mov     AX,0ffffh
        jmp     SHORT Exec_Read_Block   ; read it all!



Exec_Chk_Com_Mem:
                                        ; M063 - Start
        mov     al, [AllocMethod]       ; save current alloc method in ax
        mov     bl, [AllocMsave]
        mov     [AllocMethod], bl       ; restore original allocmethod
        test    bl, HIGH_ONLY           ; Q: was the HIGH_ONLY bit already set
        jnz     Exec_No_Memj            ; Y: no space in UMBs. Quit
                                        ; N: continue

        test    al, HIGH_ONLY           ; Q: did we set the HIGH_ONLY bit
        jz      Exec_No_Memj            ; N: no memory

        mov     ax, exec_load_block     ; M047: ax = block we just allocated
        xor     bx, bx                  ; M047: bx => free arena
        call    ChangeOwner             ; M047: free this block

        jmp     short Exec_Norm_Com_Alloc
                                        ; M063 - End

                ; We must allocate the max possible size block (ick!)
                ; and set up CS=DS=ES=SS=PDB pointer, IP=100, SP=max
                ; size of block.

Exec_Alloc_Com_File:
                                        ; M000 -start
        test    byte ptr [AllocMethod], HIGH_FIRST
                                        ; Q: is the alloc strat high_first
        jz      Exec_Norm_Com_Alloc     ; N: normal allocate
                                        ; Y: set high_only bit
        or      byte ptr [AllocMethod], HIGH_ONLY
                                        ; M000 - end


Exec_Norm_Com_Alloc:                    ; M000

        mov     BX,0FFFFh
        invoke  $Alloc                  ; largest piece available as error
        or      BX,BX
        jz      Exec_Chk_Com_Mem        ; M000

        mov     Exec_Size,BX            ; save size of allocation block
        push    BX
        invoke  $ALLOC                  ; largest piece available as error
        pop     BX                      ; get size of block...
        mov     Exec_Load_Block,AX
        add     AX,10h                  ; increment for header
        mov     Exec_DMA,AX
        xor     AX,AX                   ; presume 64K read...
        cmp     BX,1000h                ; 64k or more in block?
        jae     Exec_Read_Com           ; yes, read only 64k

        mov     AX,BX                   ; convert size to bytes
        mov     CL,4
        shl     AX,CL
        cmp     AX,200h                 ; enough memory for PSP and stack?
        jbe     Exec_Chk_Com_Mem        ; M000: jump if not

                                        ; M047: size of the block is < 64K
        sub     ax, 100h                ; M047: reserve 256 bytes for stack

Exec_Read_Com:
        sub     AX,100h                 ; remember size of psp

ifdef ROMEXEC
                ; If ROM program, do not read in file - start building header
        cmp     execRomFound,1
        jne     Exec_Read_Block         ; not found, read file
        jmp     exec_build_header       ; skip reading of file
endif

Exec_Read_Block:
        push    AX                      ; save number to read
        mov     BX,Exec_FH              ; of com file
IFDEF __TONYI__1
;------------------------------------------------------------------------
; Reorder this and save a byte with CWD...........
;------------------------------------------------------------------------
        xor     CX,CX                   ; but seek to 0:0
        xor     AX,AX                   ; seek relative to beginning
        cwd             ; DX == 0
ELSE
        xor     CX,CX                   ; but seek to 0:0
        mov     DX,CX
        xor     AX,AX                   ; seek relative to beginning
ENDIF
        invoke  $Lseek                  ; back to beginning of file
        pop     CX                      ; number to read
        mov     DS,Exec_DMA
        xor     DX,DX
        push    CX
        call    ExecRead
        pop     SI                      ; get number of bytes to read
        jnc     OkRead

        jmp     Exec_Bad_File

OkRead:
        cmp     AX,SI                   ; did we read them all?
        ljz     Exec_Chk_Com_Mem        ; M00: exactly the wrong number...no

        mov     bl, [AllocMsave]        ; M063
        mov     [AllocMethod], bl       ; M063: restore allocmethod

        test    BYTE PTR Exec_Func,EXEC_FUNC_OVERLAY
        jnz     Exec_Set_PDB            ; no starto, chumo!

        mov     AX,Exec_DMA
        sub     AX,10h
        mov     Exec_Init_CS,AX
        mov     Exec_Init_IP,100h       ; initial IP is 100

                ; SI is AT MOST FF00h.  Add FE to account for PSP - word
                ; of 0 on stack.

        add     SI,0feh                 ; make room for stack

        cmp     si, 0fffeh              ; M047: Q: was there >= 64K available
        je      Exec_St_Ok              ; M047: Y: stack is fine
        add     si, 100h                ; M047: N: add the xtra 100h for stack

Exec_St_Ok:
        mov     Exec_Init_SP,SI         ; max value for read is also SP!;smr;SS Override
        mov     Exec_Init_SS,AX                                 ;smr;SS Override
        mov     DS,AX
        mov     WORD PTR [SI],0         ; 0 for return

        ;
        ; M068
        ;
        ; We now determine if this is a Copy Protected App. If so the
        ; A20OFF_COUNT is set to 6. Note that ChkCopyProt will point to a
        ; a ret if DOS is loaded low. Also DS contains the load segment.

        call    word ptr [ChkCopyProt]

Exec_Set_PDB:
        mov     BX,Exec_FH              ; we are finished with the file.
        call    Exec_Dealloc
        push    BP
        invoke  $Close                  ; release the jfn
        pop     BP
        call    Exec_Alloc
        test    BYTE PTR Exec_Func,EXEC_FUNC_OVERLAY
        jz      Exec_Build_Header

        call    Scan_Execname
        call    Scan_Special_Entries

;SR;
;The current lie strategy uses the PSP to store the lie version. However,
;device drivers are loaded as overlays and have no PSP. To handle them, we
;use the Sysinit flag provided by the BIOS as part of a structure pointed at
;by BiosDataPtr. If this flag is set, the overlay call has been issued from
;Sysinit and therefore must be a device driver load. We then get the lie
;version for this driver and put it into the Sysinit PSP. When the driver
;issues the version check, it gets the lie version until the next overlay
;call is issued.
;
        cmp     DriverLoad,0            ;was Sysinit processing done?
        je      norm_ovl                ;yes, no special handling
        push    si
        push    es
        les     si,BiosDataPtr          ;get ptr to BIOS data block
        cmp     byte ptr es:[si],0              ;in Sysinit?
        je      sysinit_done            ;no, Sysinit is finished

        mov     es,CurrentPDB           ;es = current PSP (Sysinit PSP)
        push    Special_Version
        pop     es:PDB_Version          ;store lie version in Sysinit PSP
        jmp     short setver_done
sysinit_done:
        mov     DriverLoad,0            ;Sysinit done,special handling off
setver_done:
        pop     es
        pop     si
norm_ovl:

        leave
        transfer    Sys_Ret_OK          ; overlay load -> done

Exec_Build_Header:

ifdef ROMEXEC
                ; Set up cs:ip and ss:sp for the ROM program; ax contains
                ; offset of stack 100h for PSP

        cmp     ExecRomFound,1
        jnz     Not_ROM_Exec

                ; AX is at most FF00h.  Add FE to account for PSP - word of
                ; 0 on stack.

        add     AX,0feh
        mov     SI,AX                   ;store sp value
        mov     Exec_Init_SP, AX
        mov     AX,Exec_load_block      ;address of PSP
        mov     Exec_Init_SS, AX        ;set up ss
                                        ;Set up word of 0 on stack for return
        mov     DS,AX                   ;PSP segment
        mov     WORD PTR [SI],0
        mov     AX, ExecRomAddrL        ;get IP
        mov     Exec_init_IP, AX
        mov     AX,ExecRomAddrH         ;get CS
        mov     Exec_Init_CS, AX

Not_ROM_Exec:

endif ; ROMEXEC

        mov     DX,Exec_Load_Block
                                        ; assign the space to the process
        mov     SI,Arena_Owner          ; pointer to owner field
        mov     AX,Exec_Environ         ; get environ pointer
        or      AX,AX
        jz      No_Owner                ; no environment

        dec     AX                      ; point to header
        mov     DS,AX
        mov     [SI],DX                 ; assign ownership

No_Owner:
        mov     AX,Exec_Load_Block      ; get load block pointer
        dec     AX
        mov     DS,AX                   ; point to header
        mov     [SI],DX                 ; assign ownership

        push    DS                      ;AN000;MS. make ES=DS
        pop     ES                      ;AN000;MS.
        mov     DI,Arena_Name           ;AN000;MS. ES:DI points to destination
        call    Scan_Execname           ;AN007;MS. parse execname
                                        ;          ds:si->name, cx=name length
        push    CX                      ;AN007;;MS. save for fake version
        push    SI                      ;AN007;;MS. save for fake version

MoveName:                               ;AN000;
        lodsb                           ;AN000;;MS. get char
        cmp     AL,'.'                  ;AN000;;MS. is '.' ,may be name.exe
        jz      Mem_Done                ;AN000;;MS. no, move to header
                                        ;AN000;
        stosb                           ;AN000;;MS. move char
                                        ; MSKK bug fix - limit length copied
        cmp     di,16                   ; end of memory arena block?
        jae     mem_done                ; jump if so

        loop    movename                ;AN000;;MS. continue
Mem_Done:                               ;AN000;
        xor     AL,AL                   ;AN000;;MS. make ASCIIZ
        cmp     DI,SIZE ARENA           ;AN000;MS. if not all filled
        jae     Fill8                   ;AN000;MS.

        stosb                           ;AN000;MS.

Fill8:                                  ;AN000;
        pop     SI                      ;AN007;MS. ds:si -> file name
        pop     CX                      ;AN007;MS.

        call    Scan_Special_Entries    ;AN007;MS.

        push    DX
        mov     SI,exec_size
        add     SI,DX
        Invoke  $Dup_PDB                ; ES is now PDB
        pop     DX

        push    exec_environ
        pop     ES:[PDB_environ]
                                        ; *** Added for DOS 5.00
                                        ; version number in PSP
        push    [Special_Version]       ; Set the DOS version number to
        pop     ES:[PDB_Version]        ; to be used for this application

                                        ; set up proper command line stuff
        lds     SI,Exec_Blk             ; get the block
        push    DS                      ; save its location
        push    SI
        lds     SI,[SI.EXEC0_5C_FCB]    ; get the 5c fcb

                ; DS points to user space 5C FCB

        mov     CX,12                   ; copy drive, name and ext
        push    CX
        mov     DI,5Ch
        mov     BL,[SI]
        rep     movsb

                ; DI = 5Ch + 12 = 5Ch + 0Ch = 68h

IFDEF __TONYI__1
;----------------------------------------------------------------------
; We *know* CX==0 after the REP MOVS.  Save a byte with XCHG
;----------------------------------------------------------------------
        xchg    ax,cx   ; AX==0         ; zero extent, etc for CPM
ELSE
        xor     AX,AX                   ; zero extent, etc for CPM
ENDIF
        stosw
        stosw

                ; DI = 5Ch + 12 + 4 = 5Ch + 10h = 6Ch

        pop     CX
        pop     SI                      ; get block
        pop     DS
        push    DS                      ; save (again)
        push    SI
        lds     SI,[SI.exec0_6C_FCB]    ; get 6C FCB

                ; DS points to user space 6C FCB

        mov     BH,[SI]                 ; do same as above
        rep     movsb
        stosw
        stosw
        pop     SI                      ; get block (last time)
        pop     DS
        lds     SI,[SI.exec0_com_line]  ; command line

                ; DS points to user space 80 command line

        or      CL,80h
        mov     DI,CX
        rep     movsb                   ; Wham!

                ; Process BX into default AX (validity of drive specs on args).
                ; We no longer care about DS:SI.

        dec     CL                      ; get 0FFh in CL
        mov     AL,BH
        xor     BH,BH
        invoke  GetVisDrv
        jnc     Exec_BL

        mov     BH,CL

Exec_BL:
        mov     AL,BL
        xor     BL,BL
        invoke  GetVisDrv
        jnc     exec_Set_Return

        mov     BL,CL

Exec_Set_Return:
        invoke  get_user_stack          ; get his return address
IFDEF __TONYI__1
;------------------------------------------------------------------------
; We're going to zero DS:AX here anyway, so we can reorder this and save
; quite a few bytes...and run faster too!
;------------------------------------------------------------------------
        .errnz (user_CS - user_IP - 2)
        lds     ax, dword ptr [SI.user_IP] ; suck out the CS and IP
        push    ds                      ; Push user CS
        push    ax                      ; Push user IP
        mov     es:[PDB_Exit], ax
        mov     es:[PDB_Exit+2], ds
        xor     ax,ax                   ; Now we'll bonk DS:AX
        mov     ds,ax                   ;
ELSE
        push    [SI.user_CS]            ; suck out the CS and IP
        push    [SI.user_IP]
        push    [SI.user_CS]            ; suck out the CS and IP
        push    [SI.user_IP]
        pop     WORD PTR ES:[PDB_Exit]
        pop     WORD PTR ES:[PDB_Exit+2]
        xor     AX,AX
        mov     DS,AX
ENDIF
                                        ; save them where we can get them
                                        ; later when the child exits.
        pop     DS:[ADDR_INT_TERMINATE]
        pop     DS:[ADDR_INT_TERMINATE+2]
        mov     WORD PTR DMAADD,80h     ; SS Override
        mov     DS,CurrentPDB           ; SS Override
        mov     WORD PTR DMAADD+2,DS    ; SS Override
        test    BYTE PTR exec_func,exec_func_no_execute
        jz      exec_go

        lds     SI,DWORD PTR Exec_Init_SP ; get stack SS Override
        les     DI,Exec_Blk             ; and block for return
        mov     ES:[DI].EXEC1_SS,DS     ; return SS

        dec     SI                      ; 'push' default AX
        dec     SI
        mov     [SI],BX                 ; save default AX reg
        mov     ES:[DI].Exec1_SP,SI     ; return 'SP'

        lds     AX,DWORD PTR Exec_Init_IP ; SS Override
        mov     ES:[DI].Exec1_CS,DS     ; initial entry stuff

        mov     ES:[DI].Exec1_IP,AX
        leave
        transfer Sys_Ret_OK

exec_go:
        lds     SI,DWORD PTR Exec_Init_IP   ; get entry point SS Override
        les     DI,DWORD PTR Exec_Init_SP   ; new stack SS Override
        mov     AX,ES


        cmp     [DosHasHMA], 0          ; Q: is dos in HMA (M021)
        je      Xfer_To_User            ; N: transfer control to user

        push    ds                      ; Y: control must go to low mem stub
        getdseg <ds>                    ;    where we disable a20 and Xfer
                                        ;    control to user

        or      [DOS_FLAG], EXECA20OFF  ; M068:
                                        ; M004: Set bit to signal int 21
                                        ; ah = 25 & ah= 49. See dossym.inc
                                        ; under TAG M003 & M009 for
                                        ; explanation
        mov     [A20OFF_PSP], dx        ; M068: set the PSP for which A20 is
                                        ; M068: going to be turned OFF.

        mov     ax, ds                  ; ax = segment of low mem stub
        pop     ds
        assume  ds:nothing

        push    ax                      ; ret far into the low mem stub
        mov     ax, OFFSET Disa20_Xfer
        push    ax
        mov     AX,ES                   ; restore ax
        retf

Xfer_To_User:


                ; DS:SI points to entry point
                ; AX:DI points to initial stack
                ; DX has PDB pointer
                ; BX has initial AX value

        cli
        mov     BYTE PTR InDos,0        ; SS Override

        ASSUME  SS:NOTHING

        mov     SS,AX                   ; set up user's stack
        mov     SP,DI                   ; and SP
        sti

        push    DS                      ; fake long call to entry
        push    SI
        mov     ES,DX                   ; set up proper seg registers
        mov     DS,DX
        mov     AX,BX                   ; set up proper AX

        retf

EndProc $Exec

; =========================================================================
;
; =========================================================================

Procedure   ExecRead,NEAR
        CALL    exec_dealloc
        MOV     bx,exec_fh
        PUSH    BP
        invoke  $READ
        POP     BP
        CALL    exec_alloc
        return
EndProc ExecRead

; =========================================================================
;
; =========================================================================

procedure   exec_dealloc,near

        push        BX
        .errnz      arena_owner_system
        sub         BX,BX               ; (bx) = ARENA_OWNER_SYSTEM
        EnterCrit   CritMEM
        call        ChangeOwners
        pop         BX
        return

EndProc exec_dealloc

; =========================================================================
;
; =========================================================================

procedure   exec_alloc,near
        ASSUME  SS:DOSDATA

        push        BX
        mov         BX,CurrentPDB       ; SS Override
        call        ChangeOwners
        LeaveCrit   CritMEM
        pop         BX
        return

EndProc exec_alloc

; =========================================================================
;
; =========================================================================

PROCEDURE   ChangeOwners,NEAR

        pushf
        push    AX
        mov     AX,exec_environ
        call    ChangeOwner
        mov     AX,exec_load_block
        call    ChangeOwner
        pop     AX
        popf
        return

ENDPROC ChangeOwners

; =========================================================================
;
; =========================================================================

PROCEDURE   ChangeOwner,NEAR

        or      AX,AX                   ; is area allocated?
        retz                            ; no, do nothing
        dec     AX
        push    DS
        mov     DS,AX
        mov     DS:[ARENA_OWNER],BX
        pop     DS
        return

EndProc ChangeOwner

; =========================================================================
;
; =========================================================================

Procedure       Scan_Execname,near
        ASSUME  SS:DosData

        lds     SI,ExecName             ; DS:SI points to name
Entry   Scan_Execname1                  ; M028
Save_Begin:                             ;
        mov     CX,SI                   ; CX= starting addr
Scan0:                                  ;
        lodsb                           ; get char

IFDEF   DBCS                            ; MSKK01 07/14/89
        invoke  TESTKANJ                ; Is Character lead byte of DBCS?
        jz      @F                      ; jump if not
        lodsb                           ; skip over DBCS character
        jmp     short scan0             ; do scan again
@@:
ENDIF

        cmp     AL,':'                  ; is ':' , may be A:name
        jz      save_begin              ; yes, save si
        cmp     AL,'\'                  ; is '\', may be A:\name
        jz      save_begin              ; yes, save si
        cmp     AL,0                    ; is end of name
        jnz     scan0                   ; no, continue scanning
        sub     SI,CX                   ; get name's length
        xchg    SI,CX                   ; cx= length, si= starting addr

        return

EndProc Scan_Execname

; =========================================================================
;
; =========================================================================

Procedure    Scan_Special_Entries,near
        assume  SS:DOSDATA

        dec     CX                      ; cx= name length
        mov    [Special_Version],(Minor_Version SHL 8) + Major_Version
        les     DI,SS:UU_IFS_DOS_CALL   ;M060; ES:DI --> Table in SETVER.SYS
        mov     AX,ES                   ;M060; First do a NULL ptr check to
        or      AX,DI                   ;M060; be sure the table exists
        jz      End_List                ;M060; If ZR then no table

GetEntries:
        mov     AL,ES:[DI]              ; end of list
        or      AL,AL
        jz      End_List                ; yes

        mov     [Temp_Var2],DI          ; save di
        cmp     AL,CL                   ; same length ?
        jnz     SkipOne                 ; no

        inc     DI                      ; es:di -> special name
        push    CX                      ; save length and name addr
        push    SI

;
; M050 - BEGIN
;
        push    ax                      ; save len
sse_next_char:
        lodsb
        call    UCase
        scasb
        jne     Not_Matched
        loop    sse_next_char
        pop     ax                      ; take len off the stack
;
; M050 - END
;
        mov     AX,ES:[DI]              ; get special version
        mov     [Special_Version],AX    ; save it
        pop     SI
        pop     CX
IFDEF __TONYI__1
;------------------------------------------------------------------------
; Let's just stick "End_List" here and save 2 bytes!  Faster too.
;------------------------------------------------------------------------
End_List:
        return
ELSE
        jmp     SHORT end_list
ENDIF

Not_Matched:
        pop     ax                      ; get len from stack ; M050
        pop     SI                      ; restore si,cx
        pop     CX

SkipOne:
        mov     DI,[Temp_Var2]          ; restore old di use SS Override
        xor     AH,AH                   ; position to next entry
        add     DI,AX
        add     DI,3                    ; DI -> next entry length
        jmp     SHORT Getentries

IFNDEF __TONYI__1
;------------------------------------------------
; Merged in above to save space....
;------------------------------------------------
End_List:
        return
ENDIF
EndProc Scan_Special_Entries

PAGE
; =========================================================================
;SUBTTL Terminate and stay resident handler
;
; Input:    DX is  an  offset  from  CurrentPDB  at which to
;           truncate the current block.
;
; output:   The current block is truncated (expanded) to be [DX+15]/16
;           paragraphs long.  An exit is simulated via resetting CurrentPDB
;           and restoring the vectors.
;
; =========================================================================
PROCEDURE $Keep_process ,NEAR
        ASSUME DS:NOTHING,ES:NOTHING,SS:DosData

        push    AX                      ; keep exit code around
        mov     BYTE PTR Exit_type,EXIT_KEEP_PROCESS
        mov     ES,CurrentPDB
        cmp     DX,6h                   ; keep enough space around for system
        jae     Keep_shrink             ; info

        mov     DX,6h

Keep_Shrink:
        mov     BX,DX
        push    BX
        push    ES
        invoke  $SETBLOCK               ; ignore return codes.
        pop     DS
        pop     BX
        jc      keep_done               ; failed on modification

        mov     AX,DS
        add     AX,BX
        mov     DS:PDB_block_len,AX     ;PBUGBUG

Keep_Done:
        pop     AX
        jmp     SHORT exit_inner        ; and let abort take care of the rest

EndProc $Keep_process

; =========================================================================
;
; =========================================================================

PROCEDURE Stay_Resident,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

        mov     AX,(Keep_process SHL 8) + 0 ; Lower part is return code;PBUGBUG
        add     DX,15
        rcr     DX,1
        mov     CL,3
        shr     DX,CL

        transfer    COMMAND

ENDPROC Stay_resident


PAGE
; =========================================================================
;SUBTTL $EXIT - return to parent process
;   Assembler usage:
;           MOV     AL, code
;           MOV     AH, Exit
;           INT     int_command
;   Error return:
;           None.
;
; =========================================================================

PROCEDURE   $Exit ,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING,SS:DosData

        xor     AH,AH
        xchg    AH,BYTE PTR DidCtrlC
        or      AH,AH
        mov     BYTE PTR Exit_Type,EXIT_TERMINATE
        jz      exit_inner
        mov     BYTE PTR Exit_type,exit_ctrl_c

        entry   Exit_inner

        invoke  get_user_stack          ;PBUGBUG

        ASSUME DS:NOTHING

        push    CurrentPDB
        pop     [SI.User_CS]            ;PBUGBUG
        jmp     SHORT Abort_Inner

EndProc $EXIT

BREAK <$ABORT -- Terminate a process>
; =========================================================================
; Inputs:
;       user_CS:00 must point to valid program header block
; Function:
;       Restore terminate and Cntrl-C addresses, flush buffers and transfer to
;       the terminate address
; Returns:
;       TO THE TERMINATE ADDRESS
; =========================================================================

PROCEDURE   $Abort ,NEAR
        ASSUME  DS:NOTHING,ES:NOTHING   ;PBUGBUG

        xor     AL,AL
        mov     exit_type,exit_abort

                ; abort_inner must have AL set as the exit code! The exit type
                ; is retrieved from exit_type. Also, the PDB at user_CS needs
                ; to be correct as the one that is terminating.

        PUBLIC  Abort_Inner
Abort_Inner:

        mov     AH,Exit_Type
        mov     Exit_Code,AX
        invoke  Get_User_Stack

        ASSUME DS:NOTHING

        mov     DS,[SI.User_CS] ; set up old interrupts ;PBUGBUG
        xor     AX,AX
        mov     ES,AX
        mov     SI,SavExit
        mov     DI,Addr_Int_Terminate
        movsw
        movsw
        movsw
        movsw
        movsw
        movsw
        transfer reset_environment

ENDPROC $ABORT

;==========================================================================
;
; fixexepatch will poin to this is DOS loads low.
;
;=========================================================================
retexepatch     proc    near

        ret

retexepatch     endp

; =========================================================================

DOSCODE ENDS

; =========================================================================

        END
