;[]-----------------------------------------------------------------[]
;|      EXEC.ASM -- Execute a program with Overlay                   |
;[]-----------------------------------------------------------------[]

;
;       C/C++ Run Time Library - Version 5.0
; 
;       Copyright (c) 1987, 1992 by Borland International
;       All Rights Reserved.
; 

        INCLUDE RULES.ASI

;       Segments Definitions

Header@

;       External References

extrn           __cexit:DIST
extrn           __IOERROR:near
extrn           __envseg:word

ExtSym@         DGROUP@, WORD, __PASCAL__
ExtSym@         _psp, WORD, __CDECL__
ExtSym@         _version, WORD, __CDECL__

        ;* Miscellaneous equates */

ExeSignature    equ     05A4Dh

        ;/* Data for the Loader */

LdDesc          STRUC
LdErrorMsg      db      'Exec failure.',00Dh,00Ah
LdStack         db      80H dup(?)      ;Loader stack
LdPSP           dw      ?               ;PSP address
LdPathName      db      80 dup(?)       ;File to be loaded
LdAX            dw      ?               ;Parse file name results
LdExeSignature  dw      ?               ;EXE header buffer
LdLength        dw      ?
LdNbPages       dw      ?
LdNbItems       dw      ?
LdHdrSize       dw      ?
LdMin           dw      ?
LdMax           dw      ?
LdSS            dw      ?
LdSP            dw      ?
LdCheckSum      dw      ?
LdIP            dw      ?
LdCS            dw      ?
LdLoadAddr      dw      ?               ;Load Overlay interface block
LdRelocFactor   dw      ?               ;Relocation factor to be used
LdDesc          ENDS

        SUBTTL  Loader program
        PAGE
;/*                                                      */
;/*------------------------------------------------------*/
;/*                                                      */
;/*     Loader Program                                   */
;/*     --------------                                   */
;/*                                                      */
;/*------------------------------------------------------*/
;/*                                                      */
CSeg@
LoaderDatas     db      size LdDesc dup (0)
_Loader         PROC    NEAR            ;CX = EnvSeg, DX = End of Memory

        ;/* Setup segment registers */

                xor     di, di
                mov     ax, cs
                mov     ds, ax
                mov     es, ax
                cli
                mov     ss, ax
                lea     sp, [di+LdPSP]
                sti

        ;/* Load The Program */

                push    cx
                push    dx
                mov     ax, 4B03h
                lea     bx, [di+LdLoadAddr]
                lea     dx, [di+LdPathName]
                int     21h
                pop     dx
                pop     cx
                jb      LoadError
                xor     di, di
                cli
                mov     ss, [di+LdSS]
                mov     sp, [di+LdSP]
                sti
                mov     bp, sp
                xor     ax, ax
                push    ax              ;Return to MSDOS for .COM
                mov     ax, [di+LdPSP]
                mov     ds, ax
                mov     es, ax
                mov     es:[2h], dx      ;Set End of Program into PSP
                mov     es:[2ch], cx     ;Set EnvSeg into PSP
                mov     ax, cs:[di+LdAX] ;AX = validity of FCBs
                jmp     dword ptr cs:[LdIP]

        ;/* Fatal Error if unable to load the program */

LoadError       label   near
                mov     ah, 040h
                mov     bx, 2
                mov     cx, LdStack - LdErrorMsg
                xor     dx, dx
                int     21h             ;Error message on stderr
                mov     ax, 4C02h
                int     21h             ;exit(2)
_Loader         ENDP
LoaderSize      equ     ($ - LoaderDatas + 15) / 16
wLoaderSize     equ     LoaderSize * 8
LoaderVector    dd      _Loader - LoaderDatas
        SUBTTL  Loader program
        PAGE
;/*                                                      */
;/*------------------------------------------------------*/
;/*                                                      */
;/* int _exec(pathP, cmdP, envP);                        */
;/*     char    *pathP;                                  */
;/*     char    *cmdP;                                   */
;/*     char    *envP;                                   */
;/*                                                      */
;/*------------------------------------------------------*/
;/*                                                      */

pathP           equ     4

IF LDATA
cmdP            equ     pathP + 4
envP            equ     cmdP + 4
ELSE
cmdP            equ     pathP + 2
envP            equ     cmdP + 2
ENDIF

FileHandle      equ     2
MemSize         equ     FileHandle + 2
EnvSize         equ     MemSize + 2
EnvAddr         equ     EnvSize + 4
OldEnv          equ     EnvAddr + 2

OldEnvSave      dw      ?
UseOldEnv       db      1                       ;flag, defaults to true

                public  __exec
__exec          proc    near
                push    bp
                mov     bp, sp
                sub     sp, OldEnv
                cld
                push    si
                push    ds
                push    di
                push    es

IFDEF   __HUGE__
                mov     ds, cs:DGROUP@@         ; Get DS if we're huge
ENDIF
                mov     ax,__envseg             ;save old environment
                mov     cs:OldEnvSave,ax

        ;/* Open the file to be loaded */

                mov     ax, 3d00h
                pushDS_
                LDS_    dx, [bp+pathP]
                int     21h
                popDS_
                mov     [bp-FileHandle], ax
                jnb     CopyCmdLine
                jmp     ExecExit

        ;/* Copy the command line into the PSP */

CopyCmdLine     label   near
IFDEF   __HUGE__
                mov     ax, seg _psp@
                mov     ds, ax
ENDIF
                mov     es, _psp@
                mov     LoaderDatas.LdPSP, es
                mov     ax, es:[2ch]
                mov     [bp-OldEnv], ax
                mov     di, 080h
                pushDS_
                LDS_    si, [bp+cmdP]
                lodsb
                mov     dx, si           ;Save cmdP for parse
                stosb
                xor     cx, cx
                mov     cl, al
                inc     cx
                rep     movsb

        ;/* Parse the command line for FCBs */

                mov     ax, 2901h
                mov     si, dx
                mov     di, 05ch
                int     21h             ;Build FCB 1 in PSP
                mov     byte ptr LoaderDatas.LdAX, al

FindWhite       label   near
                mov     al,[si]
                cmp     al,20h         ; Space
                je      NextFCB
                cmp     al,09h         ; Tab
                je      NextFCB
                cmp     al,0dh         ; Carriage Return
                je      NextFCB
                inc     si
                jmp     FindWhite

NextFCB         label   near
                mov     ax, 2901h
                mov     di, 06ch
                int     21h             ;Build FCB 2 in PSP
                mov     byte ptr LoaderDatas.LdAX+1, al
                popDS_

        ;/* Get the maximum memory block size */

                mov     ah, 4Ah
                mov     bx, -1
                int     21h
                cmp     byte ptr _version@, 3
                jnb     GetMaxMem
                sub     bx, 280h

GetMaxMem       label   near
                mov     [bp-MemSize], bx

        ;/* Compute environment size */

IF LDATA
                mov     ax, [bp+envP]
                mov     dx, [bp+envP+2]
ELSE
                mov     ax, [bp+envP]
                or      ax, ax
                mov     dx, ds
                jnz     DoWeHaveAnEnv
                cwd
ENDIF

DoWeHaveAnEnv   label   near
                mov     bx, ax
                or      bx, dx
                jnz     WeHaveAnEnv
                xor     ax, ax
                mov     di, ax
                jmp     short SetEnvSize

WeHaveAnEnv     label   near
                mov     es, dx
                mov     di, ax
                push    di
                mov     cx, -1
                xor     ax, ax

GetEnvSize      label   near
                repnz   scasb
                cmp     es:[di], al
                jne     GetEnvSize
                dec     cx
                add     di, 3
                repnz   scasb           ;/* EnvSize += PathSize */
                dec     cx
                mov     ax, cx
                neg     ax
                pop     di

SetEnvSize      label   near
                mov     [bp-EnvAddr], di
                mov     [bp-EnvAddr+2], es
                add     ax, 15
                mov     cx, 4
                shr     ax, cl
                mov     [bp-EnvSize], ax

                mov     si,__envseg     ;get the arena header for the environ
                dec     si
                mov     es,si
                cmp     ax,word ptr es:[03]     ;can we use the orig environ?
                jbe     SavePath

                dec     cs:UseOldEnv            ;set to false
                inc     ax
                sub     [bp-MemSize], ax        ;Reserve space for Env

        ;/* Save the pathname of the file to be loaded */
SavePath        label   near
                LDS_    si, [bp+pathP]
                push    cs
                pop     es
                lea     di, LoaderDatas.LdPathName

CopyPathName    label   near
                lodsb
                stosb
                or      al, al
                jnz     CopyPathName

        ;/* Process the file according to its type */

                mov     bx, [bp-FileHandle]
                mov     ax, [si-5]
                or      ah, ' '
                push    cs
                pop     ds
                mov     di, offset LoaderDatas
                cmp     ax, 'c.'
                jne     EXEFile
                jmp     COMFile

        ;/* Here comes all Exec Error */

ExecError       label   near
                push    ax
                mov     ah, 3eh
                mov     bx, [bp-FileHandle]
                int     21h
                pop     ax
                jmp     ExecExit

        ;/* This file must be an .EXE file */

EXEFile         label   near
                mov     ah, 3fh
                mov     cx, LdLoadAddr - LdExeSignature
                lea     dx, [di+LdExeSignature]
                int     21h
                jb      ExecError
                cmp     [di+LdExeSignature], ExeSignature
                mov     ax, 11
                jne     ExecError
                mov     ax, [di+LdNbPages]
                xor     dx, dx
                mov     dl, ah
                mov     ah, al
                xor     al, al
                shl     ax, 1
                rcl     dx, 1            ;DX:AX = NbPages * 512
                add     ax, [di+LdLength]
                adc     dx, 0
                mov     cx, 4

EXECompute      label   near
                shr     dx, 1
                rcr     ax, 1
                loop    EXECompute
                inc     ax
                sub     ax, [di+LdHdrSize]
                add     ax, [di+LdMin]
                xchg    bx, ax
                mov     ax, [di+LdPSP]
                add     ax, 16
                add     [di+LdCS], ax
                add     [di+LdSS], ax
                xchg    ax, bx
                jmp     short IsItEnough

        ;/* The file to be loaded is .COM file */

COMFile         label   near
                mov     ax, 4202h
                xor     cx, cx
                xor     dx, dx
                int     21h
                mov     cx, 4

COMCompute      label   near
                shr     dx, 1
                rcr     ax, 1
                loop    COMCompute
                inc     ax
                xchg    bx, ax
                mov     ax, [di+LdPSP]
                mov     [di+LdCS], ax
                mov     [di+LdIP], 0100h
                mov     [di+LdSS], ax
                add     ax, 16
                xchg    ax, bx

IsItEnough      label   near    ;AX = Pgm requirements, BX = Pgm Load Addr
                mov     [di+LdLoadAddr], bx
                mov     [di+LdRelocFactor], bx
                add     ax, LoaderSize
                cmp     ax, [bp-MemSize]
                mov     ax, 8
                jna     $+5
                jmp     ExecError

        ;/* Close the file before load it */

                mov     ah, 3eh
                mov     bx, [bp-FileHandle]
                int     21h

        ;/* Takes all the memory above the current program */

                mov     es, [di+LdPSP]
                mov     ah, 4ah
                mov     bx, [bp-MemSize]
                int     21h
                jnb     $+5
                jmp     ExecError

        ;/* Move the Loader before loading the overlay */

                add     bx, [di+LdPSP]
                mov     dx, bx                   ;Save end of memory
                sub     bx, LoaderSize+1
                mov     word ptr LoaderVector+2, bx
                mov     es, bx
                mov     cx, wLoaderSize
                mov     si, offset LoaderDatas
                xor     di, di
                rep     movsw

        ;/* Copy the environment */

                mov     es, [bp-OldEnv]
                mov     cx, [bp-EnvSize]
                cmp     cs:UseOldEnv,0
                jne     ReUseEnv

                mov     ah, 48h
                mov     bx, cx
                int     21h
                jb      ExecExit
                jmp     short CopyEnv

ReUseEnv:       mov     ax, cs:OldEnvSave
CopyEnv:        mov     es, ax
                xor     di, di
                lds     si, [bp-EnvAddr]
                add     cx, cx
                add     cx, cx
                add     cx, cx
                rep     movsw
                push    es

        ;/* Call exit procedures. Note: do not close file handles */
        ;/* Once we do this we can't go back!                     */

                push    dx
                push    ds
                mov     ds, cs:DGROUP@@         ; Get DS
                call    __cexit
                pop     ds
                pop     dx

        ;/* Free the old environment space */

                cmp     cs:UseOldEnv,0
                jne     ReadyToOverlay
                mov     es, cs:OldEnvSave
                mov     ah, 49h
                int     21h

        ;/* Jump to Loader and overlay the current program */

ReadyToOverlay  label   near
                pop     cx
                jmp     LoaderVector

        ;/* All error conditions set _doserrno and errno */

ExecExit        label   near
                pop     es
                pop     di
                pop     ds
                pop     si
                push    ax
                call    __IOERROR
                mov     sp, bp
                pop     bp
                ret
                endp

CSegEnd@
                END
