        TITLE   'exe - load and execute .com and .exe programs'
        PAGE 59, 132
        .LALL

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Load EXE/COM Programs                                        ;
        ;...............................................................;

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Real Time Dos                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  This material  was created as a published version  of a DOS  ;
        ;  equivalent product.   This program  logically  functions in  ;
        ;  the same way as  MSDOS functions and it  is  internal  data  ;
        ;  structure compliant with MSDOS 6.0                           ;
        ;                                                               ;
        ;  This product is distributed  AS IS and contains no warranty  ;
        ;  whatsoever,   including  warranty  of   merchantability  or  ;
        ;  fitness for a particular purpose.                            ;
        ;                                                               ;
        ;                                                               ;
        ;  (c) Copyright 1990, 1997. Api Software and Mike Podanoffsky  ;
        ;      All Rights Reserved Worldwide.                           ;
        ;                                                               ;
        ;  This product is protected under copyright laws and  may not  ;
        ;  be reproduced  in whole  or in part, in any form  or media,  ;
        ;  included but not limited to source listing, facsimile, data  ;
        ;  transmission, cd-rom, or  floppy disk without the expressed  ;
        ;  written consent of the author.                               ;
        ;                                                               ;
        ;  License  for  distribution  for commercial  use  or  resale  ;
        ;  required from:                                               ;
        ;                                                               ;
        ;  Api Software                                                 ;
        ;  12 South Walker Street                                       ;
        ;  Lowell,  MA   01851                                          ;
        ;                                                               ;
        ;  internet: mikep@world.std.com                                ;
        ;                                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;  Compile with MASM 5.1                                        ;
        ;...............................................................;

        include rxdosmac.asm
        include rxdosdef.asm

RxDOS   SEGMENT PUBLIC 'CODE'
        assume cs:RxDOS, ds:RxDOS, es:RxDOS, ss:RxDOS

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Load EXE/COM Programs                                        ;
        ;...............................................................;

        public copyCurrentPSP
        public loadProgram

        extrn LocateFile                        : near
        extrn ExpandFileName                    : near
        extrn initdiskAccess                    : near
        extrn readLogicalBuffer                 : near
        extrn allocateMemBlock                  : near
        extrn _allocateMinMaxMemBlock           : near

        extrn stdDeviceAssignTable              : near
        extrn _RetCallersStackFrame             : near
        extrn _freeMemBlock                     : near
        extrn CopyString                        : near
        extrn MapAppToSFTHandle                 : near
        extrn FindSFTbyHandle                   : near

        extrn _RxDOS_AllocStrategy              : word
        extrn _RxDOS_CurrentPSP                 : word
        extrn _RxDOS_CurrentInstance            : word
        extrn _RxDOS_MaxMemory                  : word
        extrn _RxDOS_DOSVersion                 : word
        extrn _RxDOS_pDTA                       : dword
        extrn _CallDOS                          : far

        extrn pexterrNotEnoughMemory            : near

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Load Program Image (COM, SYS, etc... )                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ds:dx  asciz name of program to load                        ;
        ;   es:bx  address of LOADEXEC structure                        ;
        ;   al     mode (subfunction)                                   ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   dx     PSP segment address                                  ;
        ;   cy     load failed                                          ;
        ;...............................................................;

loadProgram:

        Entry
        def  _Mode, ax
        def  _programPSP,     0000
        def  _StartSegment,   0000
        def  _RelocFactor,    0000
        def  _SizePara
        ddef _filesize
        ddef _PathName
        ddef _ExecBlock
        ddef _PrevInstance
        ddef _DebuggerReturnTo
        defbytes _expandedName, sizeEXPANDNAME
        defbytes _exeHeader,  sizeEXEHEADER
        defbytes _diskAccess, sizeDISKACCESS
        defbytes _dirAccess,  sizeDIRACCESS
        defbytes _relocitem,  4

        mov bx, word ptr ss:[ _RxDOS_CurrentInstance ]
        les bx, dword ptr ss:[ bx ]
        stordarg _PrevInstance, es, bx                  ; instance before 

        mov dx, word ptr es:[ _CS           ][ bx ]
        mov ax, word ptr es:[ _IP           ][ bx ]
        stordarg _DebuggerReturnTo, dx, ax              ; return address

        RetCallersStackFrame es, si
        mov dx, word ptr es:[ _ExtraSegment ][ si ]
        mov ax, word ptr es:[ _BX           ][ si ]     ; get es: bx from caller
        stordarg _ExecBlock, dx, ax
        
        mov dx, word ptr es:[ _DataSegment  ][ si ]
        mov ax, word ptr es:[ _DX           ][ si ]     ; get ds: dx from caller
        stordarg _PathName, dx, ax

        push dx                                         ; non-canonical name
        push ax
        lea dx, offset _expandedName [ bp ]             ; expanded name
        push ss
        push dx
        mov ax, FILECANNOT_BEDIRECTORY                  ; not a directory
        call ExpandFileName                             ; get actual expanded name

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  initialize mode
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        getarg ax, _Mode                                ; get mode
        Goto   execSetExecuteMode, _loadProgram_SetExec
        Goto   execLoadOverlay,    _loadProgram_Overlay
        jmp short _loadProgram_04

_loadProgram_SetExec:
        call _SetExecuteMode
        jmp _loadProgram_68

_loadProgram_Overlay:
        getdarg es, bx, _ExecBlock
        mov ax, word ptr es:[ loverSegLoadAddress   ][ bx ]
        storarg _StartSegment, ax
        mov ax, word ptr es:[ loverRelocFactor      ][ bx ]
        storarg _RelocFactor,  ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  program file exist ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_loadProgram_04:
        mov ax, (FILE_NODEVICENAME)
        getdarg es, si, _PathName                       ; name from caller (ds: dx )
        lea di, offset _dirAccess [ bp ]                ; work dir access block
        call LocateFile                                 ; try to find file
        ifc _loadProgram_68                             ; if cannot -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  initialize disk access block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        setES ss
        lea bx, offset _diskAccess [ bp ]               ; build access control block
        call initdiskAccess                             ; [ax] is drive, [dx] is cluster

        call checkEXEHeader                             ; exe file ?
        jnz _loadProgram_12                             ; no, COM or SYS type -->

        getarg ax, _Mode                                ; get subfunction code
        getarg cx, _RelocFactor                         ; relocation factor
        getarg dx, _StartSegment                        ; load overlay address 
        lea di, word ptr [ _dirAccess ][ bp ]           ; dir information
        getdarg es, bx, _ExecBlock                      ; original load exec 
        call loadExe_Program                            ; pass off control to load EXE
        ifc _loadProgram_68                             ; if cannot -->
        jmp _loadProgram_40                             ; switch stack -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  load COM or SYS 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_loadProgram_12:
        les di, dword ptr [ _dirAccess. fileAcBufferPtr ][ bp ]
        add di, word ptr [ _dirAccess. fileAcDirOffset ][ bp ]
        mov ax, word ptr es:[ deFileSize. _low  ][ di ]
        mov dx, word ptr es:[ deFileSize. _high ][ di ]
        stordarg _filesize, dx, ax                      ; save file size

        add ax, (PARAGRAPH - 1)
        adc dx, 0                                       ; just in case carry

        shr dx, 1
        rcr ax, 1
        shr dx, 1
        rcr ax, 1
        shr dx, 1
        rcr ax, 1
        shr dx, 1
        rcr ax, 1
        storarg _SizePara, ax                           ; save as paragraphs needed

        cmp byte ptr [ _Mode  ][ bp ], execLoadOverlay  ; if load overlay 
        jz _loadProgram_18                              ; no need to allocate memeory -->

        mov dx, 0FFFFh                                  ; max request (all of memory)
        getdarg es, bx, _ExecBlock                      ; load exec module
        call _LoaderAllocMemory                         ; alloc memory for env block/ PSP
        ifc _loadProgram_68                             ; if cannot allocate -->

        storarg _StartSegment, es                       ; where data load begins 
        storarg _programPSP, ax                         ; save seg address of PSP

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  load module (at es: 0000)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_loadProgram_18:
        xor di, di                                      ; load at es:0000
        getarg es, _StartSegment                        ; load overlay address 
        lea bx, offset _diskAccess [ bp ]               ; build access control block
        mov word ptr ss:[ diskAcPosition. _high ][ bx ], di
        mov word ptr ss:[ diskAcPosition. _low  ][ bx ], di

_loadProgram_20:
        mov cx, 60 * 1024                               ; read 60k at a time
        mov ax, word ptr [ _filesize. _low  ][ bp ]
        mov dx, word ptr [ _filesize. _high ][ bp ]
        sub ax, word ptr ss:[ diskAcPosition. _low  ][ bx ]
        sbb dx, word ptr ss:[ diskAcPosition. _high ][ bx ]  
        or dx, dx                                       ; more than 60k ?
        jnz _loadProgram_22                             ; yes -->
        cmp ax, cx                                      ; more than 60k ?
        jnc _loadProgram_22                             ; yes -->
        mov cx, ax                                      ; exact bytes to read

_loadProgram_22:
        or cx, cx                                       ; more to read ?
        jz _loadProgram_24                              ; if no more to read -->

        mov dx, word ptr ss:[ diskAcPosition. _high ][ bx ]  
        mov ax, word ptr ss:[ diskAcPosition. _low  ][ bx ]
        lea bx, offset _diskAccess [ bp ]               ; build access control block
        call readLogicalBuffer                          ; Access buffer: ss: bx
        or cx, cx                                       ; bytes actually read
        jz _loadProgram_24                              ; if no more to read -->

        push cx
        shr cx, 1
        shr cx, 1
        shr cx, 1
        shr cx, 1                                       ; chars read in paras
        mov ax, es
        add ax, cx
        mov es, ax
        pop cx
        and cx, (PARAGRAPH - 1)
        add di, cx
        jmp _loadProgram_20

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  switch to new process
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_loadProgram_24:
        mov al, byte ptr [ _Mode  ][ bp ]               ; if load overlay 
        getdarg cx, dx, _filesize
        call ifOverlayLoader                            ; set up ret args if overlay
        ifz _loadProgram_46                             ; no need to build return stack -->

        getarg es, _programPSP                          ; child process' PSP
        mov word ptr [ _RxDOS_CurrentPSP ], es          ; change running PSP

        call _getSizeOfEnvString                        ; get size of env string

        xor si, si
     ;  cmp cx, (1024 /PARAGRAPH) * 64                  ; greater than 64k ?
     ;  jnc _loadProgram_28                             ; yes, use max value for seg
     ;  mov si, cx
     ;  shl si, 1
     ;  shl si, 1
     ;  shl si, 1                                       ; actual words
        
_loadProgram_28:
        mov word ptr es:[ si - 2 ], 0000                ; place a null return address at top of stack

        sub si, 4                                       ; top value minus 2 words
        mov word ptr es:[ pspUserStack. _pointer ], si
        mov word ptr es:[ pspUserStack. _segment ], es  ; child process psp

        sub si, _Flags
        mov bx, word ptr ss:[ _RxDOS_CurrentInstance ]
        mov word ptr ss:[ _pointer ][ bx ], si
        mov word ptr ss:[ _segment ][ bx ], es          ; where new process' startup stack will exist (COM)

        or si, si
        pushf
        pop word ptr es:[ _Flags        ][ si ]         ; Nz Nc ...

        mov word ptr es:[ _IP           ][ si ], 100h
        mov word ptr es:[ _CS           ][ si ], es
        mov word ptr es:[ _ExtraSegment ][ si ], es
        mov word ptr es:[ _DataSegment  ][ si ], es
        mov word ptr es:[ _BP           ][ si ], 0000
        mov word ptr es:[ _DI           ][ si ], 0000
        mov word ptr es:[ _SI           ][ si ], 0000
        mov word ptr es:[ _DX           ][ si ], 0000

        getdarg bx, cx, _filesize
        mov word ptr es:[ _CX           ][ si ], cx
        mov word ptr es:[ _BX           ][ si ], bx
        mov word ptr es:[ _AX           ][ si ], 0000

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  set DTA on the way out; switch stacks.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_loadProgram_40:
        cmp byte ptr [ _Mode  ][ bp ], execLoadOverlay  ; if load overlay 
        jz _loadProgram_46                              ; no need to allocate memeory -->

        mov es, word ptr [ _RxDOS_CurrentPSP ]          ; new PSP
        mov word ptr [ _RxDOS_pDTA. _segment ], es
        mov word ptr [ _RxDOS_pDTA. _pointer ], pspCommandTail

        cli
        mov bx, word ptr ss:[ _RxDOS_CurrentInstance ]
        mov ax, word ptr es:[ pspParentId    ]          ; parent PSP address
        sti                                             ; re-enable interrupts
        or ax, ax                                       ; if no current PSP
        jz _loadProgram_44                              ; none -->

        push word ptr ss:[ _segment ][ bx ]
        push word ptr ss:[ _pointer ][ bx ]
        pop word ptr es:[ pspUserStack. _pointer ]
        pop word ptr es:[ pspUserStack. _segment ]      ; child process

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  copy program name to Environment Block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_loadProgram_44:
        mov es, word ptr [ _RxDOS_CurrentPSP ]          ; new PSP
        lea dx, offset _expandedName [ bp ]             ; expanded name
        call _setProgramNameInMemBlock

        mov ax, word ptr es:[ pspEnvironment ]          ; get environment block
        or ax, ax                                       ; if no environment block
        jz _loadProgram_46                              ; -->

        push ds
        mov es, ax                                      ; get environment block
        call _setProgramNameInMemBlock

        setDS ss
        lea dx, offset _expandedName [ bp ]             ; expanded name to env block
        call appendProgramNametoEnv
        pop ds             

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  see if DEBUGGER support request
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_loadProgram_46:
        cmp byte ptr [ _Mode ][ bp ], execLoadAndReturnDebug
        clc
        jnz _loadProgram_68                             ; if not debugger mode -->

        SaveSegments
        mov es, word ptr [ _RxDOS_CurrentPSP ]          ; new PSP
        mov dx, word ptr es:[ pspUserStack. _segment ]
        mov ax, word ptr es:[ pspUserStack. _pointer ]

      ; get CS: IP

        mov es, word ptr [ _RxDOS_CurrentPSP ]          ; new PSP
        mov bx, word ptr es:[ pspUserStack. _pointer ]
        mov es, word ptr es:[ pspUserStack. _segment ]  ; es:bx = current stack
        mov dx, word ptr es:[ _CS           ][ bx ]     ; get new prog cs:ip
        mov ax, word ptr es:[ _IP           ][ bx ]

        getdarg ds, si, _ExecBlock
        mov word ptr [ lprogCSIP. _segment ][ si ], dx
        mov word ptr [ lprogCSIP. _pointer ][ si ], ax  ; save in exec block

      ; save SS: SP in exec block

        mov word ptr [ lprogSSSP. _segment ][ si ], es
        mov word ptr [ lprogSSSP. _pointer ][ si ], bx  ; stack to exec block

      ; return to CS:IP of previous instance with new stack

        getdarg dx, ax, _DebuggerReturnTo
        mov word ptr es:[ _CS      ][ bx ], dx
        mov word ptr es:[ _IP      ][ bx ], ax

        mov bx, word ptr ss:[ _RxDOS_CurrentInstance ]
        getdarg dx, ax, _PrevInstance
        mov word ptr ss:[ _segment ][ bx ], dx
        mov word ptr ss:[ _pointer ][ bx ], ax

        RestoreSegments
        clc

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  exit.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_loadProgram_68:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Load Exe Program                                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ss:di  pointer to directory block                           ;
        ;   es:bx  address of EXEC structure                            ;
        ;   dx     load segment address if mode (al) is load Overlay    ;
        ;   cx     code relocation factor                               ;
        ;   ax     subfunction code                                     ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   dx     PSP segment address                                  ;
        ;   cy     load failed                                          ;
        ;...............................................................;

loadExe_Program:

        Entry
        def  _Mode, ax
        def  _programPSP,    0000
        def  _StartSegment,  dx
        def  _RelocFactor,   cx
        def  _SizePara
        def  _CheckSum
        ddef _returnSize
        ddef _ExecBlock, es, bx
        ddef _dirAccess, ss, di
        defbytes _exeHeader, sizeEXEHEADER
        defbytes _diskAccess, sizeDISKACCESS
        defbytes _relocitem, 4

        xor cx, cx
        storarg _programPSP  , cx
        storarg _CheckSum    , cx

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  initialize disk access block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov ax, word ptr ss:[ fileAcDrive    ][ di ]
        mov dx, word ptr ss:[ fileAcCluster  ][ di ]
        lea bx, offset _diskAccess [ bp ]               ; build access control block
        call initdiskAccess                             ; [ax] is drive, [dx] is cluster

        les si, dword ptr [ fileAcBufferPtr ][ di ]
        add si, word ptr [ fileAcDirOffset ][ di ]
        mov ax, word ptr es:[ deFileSize. _low  ][ si ]
        mov dx, word ptr es:[ deFileSize. _high ][ si ]
        mov word ptr [ _diskAccess. diskAcFileSize. _low  ][ bp ], ax
        mov word ptr [ _diskAccess. diskAcFileSize. _high ][ bp ], dx

        sub ax,  200h
        sbb dx, 0000h                                   ; return size
        mov word ptr [ _returnSize. _low  ][ bp ], ax
        mov word ptr [ _returnSize. _high ][ bp ], dx

        xor dx, dx
        xor ax, ax                                      ; position to beg of file
        setES ss
        lea di, offset _exeHeader [ bp ]
        mov cx, sizeEXEHEADER
        call readLogicalBuffer                          ; Access buffer: ss: bx
        call computeChecksum
        mov word ptr [ _CheckSum ][ bp ], ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute size in pages of load module
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov dx, word ptr [ _exeHeader. exeExtraBytes ][ bp ]
        add dx, (PARAGRAPH - 1)                         ; round up parags needed
        shr dx, 1
        shr dx, 1
        shr dx, 1
        shr dx, 1                                       ; paragraphs at end needed

        mov ax, word ptr [ _exeHeader. exePages ][ bp ]
     ;   dec ax                                          ; adjust for last block
        shl ax, 1                                       ; conv pages to paragraphs
        shl ax, 1                                       ;  4
        shl ax, 1                                       ;  8
        shl ax, 1                                       ; 16
        shl ax, 1                                       ; 32
        sub ax, word ptr [ _exeHeader. exeHeaderSize ][ bp ]
        add ax, dx                                      ; actual paragr needed
        add ax, word ptr [ _exeHeader. exeMinAlloc ][ bp ]
        storarg _SizePara, ax                           ; save as paragraphs needed

        cmp byte ptr [ _Mode  ][ bp ], execLoadOverlay  ; if load overlay 
        jz loadExe_Program06                            ; no need to build return stack -->

        mov dx, word ptr [ _exeHeader. exeMaxAlloc ][ bp ]
        getdarg es, bx, _ExecBlock                      ; load exec module
        call _LoaderAllocMemory                         ; alloc memory for env block/ PSP
        ifc loadExe_Program50                           ; if cannot allocate -->

        storarg _StartSegment, es                       ; where data load begins 
        storarg _RelocFactor, es                        ; relocation factor
        storarg _programPSP, ax                         ; save seg address of PSP

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  load module
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
loadExe_Program06:
        mov ax, word ptr [ _exeHeader. exeHeaderSize ][ bp ]
        shl ax, 1                                       ;  2
        shl ax, 1                                       ;  4
        shl ax, 1                                       ;  8
        shl ax, 1                                       ; 16 byte offset
        getarg es, _StartSegment                        ; load overlay address 
        xor dx, dx

loadExe_Program08:
        push es
        push dx
        push ax                                         ; save position in file

        mov cx, word ptr [ _SizePara ][ bp ]            ; get paras to read
        test cx, 0F000h                                 ; over 65k ?
        jz loadExe_Program12                            ; no, read whole file -->
        mov cx, 0FFFh                                   ; read as much as possible.

loadExe_Program12:
        push cx                                         ; save paras to read

        shl cx, 1
        shl cx, 1
        shl cx, 1
        shl cx, 1
        push cx                                         ; save bytes actually to read

        xor di, di                                      ; load at _StartSeg:0000
        lea bx, offset _diskAccess [ bp ]               ; build access control block
        call readLogicalBuffer                          ; Access buffer: ss: bx
        call computeChecksum
        add word ptr [ _CheckSum ][ bp ], ax

        pop cx                                          ; bytes read
        pop bx                                          ; paras read
        pop ax                                          ; previous file offset
        pop dx
        add ax, cx                                      ; incr position by bytes read
        adc dx, 0000

        pop cx                                          ; segment loaded
        add cx, bx                                      ; increment by paras read
        mov es, cx                                      ; set new read segment

        sub word ptr [ _SizePara ][ bp ], bx            ; subtract paragraphs used.
        jnz loadExe_Program08

        xor di, di                                      ; load at _StartSeg:0000
        mov cx, word ptr [ _exeHeader. exeExtraBytes ][ bp ]
        and cx, (PARAGRAPH - 1)                         ; last 15 bytes
        jz loadExe_Program20                            ; if none to read -->
        
        lea bx, offset _diskAccess [ bp ]               ; build access control block
        call readLogicalBuffer                          ; Access buffer: ss: bx
        call computeChecksum
        add word ptr [ _CheckSum ][ bp ], ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  address relocation blocks
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
loadExe_Program20:
        mov ax, word ptr [ _exeHeader. exeRelocItems ][ bp ]
        or ax, ax
        jz loadExe_Program32

loadExe_Program28:
        setES ss
        mov cx, 4
        xor dx, dx
        mov ax, word ptr [ _exeHeader. exeRelocTable ][ bp ]
        lea di, offset _relocitem [ bp ]
        lea bx, offset _diskAccess [ bp ]               ; build access control block
        call readLogicalBuffer                          ; Access buffer: ss: bx
        call computeChecksum
        add word ptr [ _CheckSum ][ bp ], ax

        getdarg dx, bx, _relocitem                      ; get reloc item
        getarg ax, _RelocFactor                         ; starting segment

        add dx, ax
        mov es, dx
        add word ptr es:[ bx ], ax                      ; relocate load module

        add word ptr [ _exeHeader. exeRelocTable ][ bp ], 4
        dec word ptr [ _exeHeader. exeRelocItems ][ bp ]
        jnz loadExe_Program28

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  execute
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
loadExe_Program32:
        mov ax, word ptr [ _CheckSum ][ bp ]
        add ax, word ptr [ _exeHeader. exeChecksum   ][ bp ]

        mov al, byte ptr [ _Mode  ][ bp ]               ; if load overlay 
        getdarg cx, dx, _returnSize                     ; return size
        call ifOverlayLoader                            ; set up ret args if overlay
        ifz loadExe_Program50                           ; no need to build return stack -->

        getarg es, _programPSP                          ; child process' PSP
        mov dx, word ptr [ _exeHeader. exeInitSS   ][ bp ]
        add dx, word ptr [ _StartSegment           ][ bp ]
        mov si, word ptr [ _exeHeader. exeInitSP   ][ bp ]

        sub si, 2                                       ; top value minus 1 word
        mov word ptr es:[ pspUserStack. _pointer ], si
        mov word ptr es:[ pspUserStack. _segment ], dx

        setES dx                                        ; child program's stack seg
        sub si, _Flags
        mov bx, word ptr ss:[ _RxDOS_CurrentInstance ]
        mov word ptr ss:[ _pointer ][ bx ], si
        mov word ptr ss:[ _segment ][ bx ], dx          ; where new process' startup stack will exist (COM)

        or si, si
        pushf
        pop word ptr es:[ _Flags        ][ si ] 

        mov dx, word ptr [ _StartSegment ][ bp ]
        add dx, word ptr [ _exeHeader. exeInitCS     ][ bp ]
        mov ax, word ptr [ _exeHeader. exeInitIP     ][ bp ]
        mov word ptr es:[ _IP           ][ si ], ax
        mov word ptr es:[ _CS           ][ si ], dx

        getarg bx, _programPSP                          ; child process' PSP
        mov word ptr es:[ _ExtraSegment ][ si ], bx
        mov word ptr es:[ _DataSegment  ][ si ], bx
        mov word ptr es:[ _BP           ][ si ], 0000
        mov word ptr es:[ _DI           ][ si ], 0000
        mov word ptr es:[ _SI           ][ si ], 0000
        mov word ptr es:[ _DX           ][ si ], 0000

        getdarg dx, ax, _returnSize
        mov word ptr es:[ _CX           ][ si ], ax
        mov word ptr es:[ _BX           ][ si ], dx
        mov word ptr es:[ _AX           ][ si ], 0000

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  copy startup command line to _PSP
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        getarg ax, _programPSP                          ; child process' PSP
        mov word ptr [ _RxDOS_CurrentPSP ], ax          ; change running PSP
        clc

loadExe_Program50:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Determine if file is EXE formatted                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ss:bx  disk access block                                    ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   zr     file is an EXE                                       ;
        ;   nz     file is not an EXE                                   ;
        ;...............................................................;

checkEXEHeader:
        Entry
        defbytes _exeHeader, sizeEXEHEADER

        xor dx, dx
        xor ax, ax                                      ; file pointer to beg of file
        mov cx, sizeEXEHEADER
        lea di, offset _exeHeader [ bp ]
        call readLogicalBuffer                          ; Access buffer: ss: bx

        cmp word ptr [ _exeHeader. exeSignature ][ bp ], EXE_SIGNATURE
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Copy Current PSP                                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   es     PSP segment address to update                        ;
        ;...............................................................;

copyCurrentPSP:

        SaveSegments
        mov ax, word ptr ss:[ _RxDOS_CurrentPSP ]       ; current PSP
        or ax, ax                                       ; any current PSP ?
        jz copyCurrentPSP_36                            ; no -->

        xor si, si
        xor di, di
        mov ds, ax                                      ; current PSP
        mov cx, ( sizePSP / 2 )
        rep movsw

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; duplicate all file handles (except for no inherit)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov ax, word ptr ss:[ _RxDOS_CurrentPSP ]
        mov word ptr es:[ pspParentId    ], ax          ; parent PSP address

        mov cx, sizePSPHandleTable 
        mov bx, offset PSPHandleTable 
        mov word ptr es:[ pspFileHandleCount ], cx
        mov word ptr es:[ pspFileHandlePtr. _segment ], es
        mov word ptr es:[ pspFileHandlePtr. _pointer ], bx

copyCurrentPSP_08:
        xor ax, ax
        mov al, byte ptr es:[ bx ]                      ; get old handle
        cmp al, -1                                      ; if entry not set
        jz copyCurrentPSP_14                            ; skip around -->

        push es
        push bx
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        test word ptr es:[ sftDevInfo ][ di ], sftNoInherit
        jnz copyCurrentPSP_12                           ; if no inherit -->

        inc word ptr es:[ sftRefCount ][ di ]           ; bump in use count

        pop bx
        pop es
        jmp short copyCurrentPSP_14

copyCurrentPSP_12:
        pop bx
        pop es
        mov byte ptr es:[ bx ], -1                      ; free SFT in copy of psp

copyCurrentPSP_14:
        inc bx
        cmp bx, offset (PSPHandleTable + sizePSPHandleTable)
        jl copyCurrentPSP_08

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; done
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
copyCurrentPSP_36:
        RestoreSegments
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Create New Program Seg Prefix                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es     start address of PSP                                 ;
        ;   cx     allocate size                                        ;
        ;   dx:ax  EXEC BLOCK address                                   ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es     updated es:                                          ;
        ;...............................................................;

createPSP:

        Entry
        def  _AllocSize, cx
        ddef _ExecBlock, dx, ax

        push ds
        mov ax, es
        add ax, (sizePSP / PARAGRAPH)                   ; always a fixed size
        push ax                                         ; address of PSP to return

        xor di, di
        mov ax, word ptr ss:[ _RxDOS_CurrentPSP ]
        or ax, ax                                       ; any parent ?
        jnz createPSP_08                                ; yes, copy parent -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  init a blank PSP
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        xor ax, ax
        mov cx, (sizePSP / 2)                           ; always a fixed size
        rep stosw                                       ; clear/ zero.

        mov ax, -1
        mov di, offset pspHandleTable
        mov cx, (sizePSPHandleTable / 2)
        rep stosw                                       ; init handle table

        mov dx, 1
        mov al, byte ptr ss:[ stdRedirec_Aux * sizeStdRedirec ]. stdDeviceAssignTable. stdIOHandle
        mov byte ptr es:[ pspHandleTable.STDAUX ], al
        call incrRefCount

        mov al, byte ptr ss:[ stdRedirec_Prn * sizeStdRedirec ]. stdDeviceAssignTable. stdIOHandle
        mov byte ptr es:[ pspHandleTable.STDPRN ], al
        call incrRefCount

        mov al, byte ptr ss:[ stdRedirec_Con * sizeStdRedirec ]. stdDeviceAssignTable. stdIOHandle
        mov byte ptr es:[ pspHandleTable.STDIN  ], al
        mov byte ptr es:[ pspHandleTable.STDOUT ], al
        mov byte ptr es:[ pspHandleTable.STDERR ], al

        mov dx, 3
        call incrRefCount

        mov al, ' '
        mov cx, (sizefnName + sizefnExtension)
        mov di, offset pspFCB_1
        rep stosb                                       ; init fcb 1

        mov cx, (sizefnName + sizefnExtension)
        mov di, offset pspFCB_2
        rep stosb                                       ; init fcb 2

        mov word ptr es:[ pspFileHandlePtr. _segment ], es
        mov word ptr es:[ pspFileHandlePtr. _pointer ], pspHandleTable
        mov word ptr es:[ pspFileHandleCount         ], sizepspHandleTable

        mov ax, word ptr [ _RxDOS_DOSVersion ]          ; get DOS Version
        mov word ptr es:[ pspVersion ], ax              ; Major, Minor version (VERS)
        jmp short createPSP_12

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  copy an existing PSP
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
createPSP_08:
        call copyCurrentPSP                             ; copy current PSP

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  other specific initialization
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
createPSP_12:
        mov ax, word ptr ss:[ _RxDOS_CurrentPSP ]
        mov word ptr es:[ pspParentId    ], ax          ; parent PSP address

        mov ax, es                                      ; compute next alloc
        add ax, word ptr [ _AllocSize ][ bp ]
        mov word ptr es:[ pspNextParagraph   ], ax

        mov word ptr es:[ pspInt20       ], 20CDh       ; Int 20 instruction
        mov word ptr es:[ pspDosCall     ], 21CDh       ; Int 21 instruction
        mov word ptr es:[ pspDosCall + 2 ], 0CBh        ; retf

        mov byte ptr es:[ pspDispatcher  ], 9Ah         ; call instruction
        mov word ptr es:[ 1+(pspDispatcher. _segment) ], cs
        mov word ptr es:[ 1+(pspDispatcher. _pointer) ], offset _CallDOS
        mov word ptr es:[ pspShareChain. _pointer ], -1 ; share vector 
        mov word ptr es:[ pspShareChain. _segment ], -1

        push ds
        xor ax, ax
        mov ds, ax

        mov bx, offset ( intCONTROLC * 4 )              ; Int23 control-C vector
        mov ax, word ptr [ _pointer ][ bx ]				; existing value copied to PSP
        mov dx, word ptr [ _segment ][ bx ]
        mov word ptr es:[ pspControlCVect. _pointer ], ax
        mov word ptr es:[ pspControlCVect. _segment ], dx

        mov bx, offset ( intCRITICALERROR * 4 )         ; Int24 criterror vector
        mov ax, word ptr [ _pointer ][ bx ]				; existing value copied to PSP
        mov dx, word ptr [ _segment ][ bx ]
        mov word ptr es:[ pspCritErrorVect. _pointer ], ax
        mov word ptr es:[ pspCritErrorVect. _segment ], dx

        RetCallersStackFrame ds, bx
        mov dx, word ptr [ _CS ][ bx ]					; set terminate vector as return address
        mov ax, word ptr [ _IP ][ bx ]
        mov word ptr es:[ pspTerminateVect. _pointer ], ax
        mov word ptr es:[ pspTerminateVect. _segment ], dx
        pop ds

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  copy command line
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov di, offset pspCommandTail					; build dummy command line
        mov word ptr ss:[ di ], 0D00h                   ; pre-init command line

        mov ax, word ptr [ _ExecBlock. _segment ][ bp ]
        or ax, word ptr [ _ExecBlock. _pointer ][ bp ]
        jz createPSP_56                                 ; if no load execute block -->
        
        getdarg ds, di, _ExecBlock
        cmp word ptr [ lexecCommandTail. _segment ][ di ], -1
        jz createPSP_36                                 ; if no command tail to copy -->
        mov ax, word ptr [ lexecCommandTail. _segment ][ di ]
        or ax, word ptr [ lexecCommandTail. _pointer ][ di ]
        jz createPSP_36                                 ; if no command tail to copy -->

        SaveRegisters es, si
        lds si, dword ptr [ lexecCommandTail ][ di ]
        mov di, offset pspCommandTail

        mov cx, 0001                                    ; add one for length byte  (thanks BillW)
        add cl, byte ptr [ si ]                         ; get character count
        rep movsb                                       ; copy
        mov byte ptr es:[ di ], 0Dh                     ; store trailing cr

        RestoreRegisters si, es                         ; address of PSP
        
createPSP_36:
        getdarg ds, di, _ExecBlock
        cmp word ptr [ lexecFCB_1. _segment ][ di ], -1
        jz createPSP_42                                 ; if no FCB 1 to copy -->
        mov ax, word ptr [ lexecFCB_1. _segment ][ di ]
        or ax, word ptr [ lexecFCB_1. _pointer ][ di ]
        jz createPSP_42                                 ; if no FCB 1 to copy -->

        lds si, dword ptr [ lexecFCB_1 ][ di ]
        mov di, offset pspFCB_1
        mov cx, (pspFCB_2 - pspFCB_1)/ 2
        rep movsw
        
createPSP_42:
        getdarg ds, di, _ExecBlock
        cmp word ptr [ lexecFCB_2. _segment ][ di ], -1
        jz createPSP_56                                 ; if no FCB 2 to copy -->
        mov ax, word ptr [ lexecFCB_2. _segment ][ di ]
        or ax, word ptr [ lexecFCB_2. _pointer ][ di ]
        jz createPSP_56                                 ; if no FCB 2 to copy -->

        lds si, dword ptr [ lexecFCB_2 ][ di ]
        mov di, offset pspFCB_2
        mov cx, (pspFCB_2 - pspFCB_1)/ 2
        rep movsw

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

createPSP_56:
        pop es
        pop ds
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Increment Ref Count                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   al     SFT handle                                           ;
        ;   dx     increment value                                      ;
        ;...............................................................;

incrRefCount:

        push es
        push di
        push dx
        xor ah, ah
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )

        pop dx
        test word ptr es:[ sftDevInfo ][ di ], sftNoInherit
        jnz incrRefCount_08                             ; if no inherit -->
        add word ptr es:[ sftRefCount ][ di ], dx       ; bump in use count

incrRefCount_08:
        pop di
        pop es
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Allocate Loader Memory, copy Env block, init PSP             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:bx  load execute block                                   ;
        ;   ax     paragraphs of program expected                       ;
        ;   dx     max requested paragraphs                             ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ax     PSP segment address                                  ;
        ;   es     Data segment address                                 ;
        ;   cx     size in paras of area allocated                      ;
        ;   cy     if cannot create                                     ;
        ;...............................................................;

_LoaderAllocMemory:

        Entry
        ddef _ExecBlock, es, bx
        def  _requiredParas, ax
        def  _maxRequestedParas, dx
        def  _allocatedSize, 0000
        def  _envSeg, 0000
        def  _AllocEnv, 0000
        def  _programPSP, 0000
        def  _loadHigh, FALSE
        def  _StartSegment
        def  _envSize
        def _originalMemPolicy

        add word ptr [ _requiredParas ][ bp ], (sizePSP / PARAGRAPH)
        ifc _loaderAllocMemory_50

        or dx, dx                                       ; special 0000 request ?
        jnz _loaderAllocMemory_04                       ; no -->
        mov word ptr [ _maxRequestedParas ][ bp ], 0FFFFh
        mov word ptr [ _loadHigh          ][ bp ], TRUE ; if load HIGH

_loaderAllocMemory_04:
        mov ax, word ptr [ _requiredParas ][ bp ]
        cmp ax, word ptr [ _maxRequestedParas ][ bp ]
        jc _loaderAllocMemory_08
        jz _loaderAllocMemory_08
        mov word ptr [ _maxRequestedParas ][ bp ], ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  determine the size of the environment block, if any.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_loaderAllocMemory_08:
        mov ax, word ptr [ _RxDOS_AllocStrategy ]
        storarg _originalMemPolicy, ax                  ; save current alloc policy

        mov ax, word ptr es:[ lexecEnvironment ][ bx ]
        or ax, ax                                       ; env block address passed ?
        jnz _loaderAllocMemory_14                       ; if segment passed -->

        mov ax, word ptr ss:[ _RxDOS_CurrentPSP ]
        or ax, ax                                       ; current PSP available ?
        jz _loaderAllocMemory_12                        ; no, go figure something -->

        mov es, ax
        mov ax, word ptr es:[ pspEnvironment ]          ; get environment block address
        or ax, ax                                       ; available ?
        jnz _loaderAllocMemory_14                       ; yes, use it -->

_loaderAllocMemory_12:
        mov word ptr [ _RxDOS_AllocStrategy ], _MEM_LASTFIT_HIGH
        mov cx, sizeParasPerPage                        ; if no exec block, default to this for now
        jmp short _loaderAllocMemory_24                 ; go allocate default size

_loaderAllocMemory_14:
        mov es, ax                                      ; memory block address
        storarg _envSeg, ax                             ; save env segment to copy
        call _getSizeOfEnvString                        ; get size of env string

_loaderAllocMemory_24:
        storarg _envSize, cx                            ; save size in paras
        call allocateMemBlock                           ; return seg pointer to avail memory

        getarg ax, _originalMemPolicy                   ; restore current alloc policy
        mov word ptr [ _RxDOS_AllocStrategy ], ax
        jc _loaderAllocMemory_50                        ; if insufficient space to load -->
        storarg _AllocEnv, es                           ; store alloc environment

        xor ax, ax
        xor di, di                                      ; start of area
        getarg cx, _envSize                             ; get size in paras
        shl cx, 1                                       ; convert paras to words
        shl cx, 1                                       ; convert paras to words
        shl cx, 1                                       ; convert paras to words
        rep stosw                                       ; clear/ zero.

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  copy environment
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        cmp word ptr [ _envSeg ][ bp ], 0000            ; no default segment ?
        jz _loaderAllocMemory_42                        ; skip copy -->

        push ds
        xor di, di
        xor si, si
        getarg ds, _envSeg
        getarg cx, _envSize                             ; env size in paras
        shl cx, 1                                       ; convert to words !
        shl cx, 1
        shl cx, 1
        rep movsw                                       ; copy env block

        pop ds

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  allocate block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_loaderAllocMemory_42:
        getarg ax, _requiredParas
        getarg dx, _maxRequestedParas
        call _allocateMinMaxMemBlock                    ; return seg pointer to avail memory
        jc _loaderAllocMemory_50                        ; if insufficient space to load -->
        storarg _programPSP, es                         ; save PSP seg address
        storarg _allocatedSize, cx                      ; allocated size

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  create child PSP
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        getdarg dx, ax, _ExecBlock                      ; load exec structure
        call createPSP                                  ; build PSP (cx contains alloc size)
        storarg _StartSegment, es                       ; PSP here

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  set Env Block in PSP
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        getarg es, _programPSP
        getarg ax, _AllocEnv                            ; alloc environment
        mov word ptr es:[ pspEnvironment ], ax          ; environment block

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  insure parent's handle are set for both blocks
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov dx, es                                      ; PSP address
        mov ax, es
        sub ax, (sizeMEMBLOCK/ PARAGRAPH)
        mov es, ax                                      ; point to allocation block
        mov word ptr es:[ _memParent ], dx              ; assign parent

        getarg ax, _AllocEnv
        or ax, ax                                       ; none required ?
        jz _loaderAllocMemory_56                        ; no -->

        sub ax, (sizeMEMBLOCK/ PARAGRAPH)
        mov es, ax                                      ; point to allocation block
        mov word ptr es:[ _memParent ], dx              ; assign parent
        jmp short _loaderAllocMemory_56                 ; if everything is ok -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  free env block if error
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_loaderAllocMemory_50:
        getarg ax, _AllocEnv
        or ax, ax                                       ; Env allocated ?
        jz _loaderAllocMemory_54                        ; no -->

        mov es, ax
        call _freeMemBlock

_loaderAllocMemory_54:
        SetError pexterrNotEnoughMemory, _loaderAllocMemory_56

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_loaderAllocMemory_56:
        getarg ax, _programPSP
        getarg es, _StartSegment
        getarg cx, _allocatedSize

_loaderAllocMemory_66:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Compute Checksum                                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:di  start address of block                               ;
        ;   cx     number of bytes in block                             ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ax     sum, ignoring carry, of all words in block           ;
        ;...............................................................;

computeChecksum:
        push di
        push cx
        shr cx, 1
        xor ax, ax

computeChecksum_04:
        add ax, word ptr es:[ di ]
        add di, 2
        loop computeChecksum_04

        pop cx
        pop di
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Set Execution State                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ds:dx  address of SETEXEC block                             ;
        ;                                                               ;
        ;...............................................................;

_SetExecuteMode:

        clc
        ret  

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Append Program Name to Environment Block                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   es     environment block                                    ;
        ;   ds:dx  program name                                         ;
        ;                                                               ;
        ;...............................................................;

appendProgramNametoEnv:

        mov si, dx                                      ; save name to program

        xor al, al                                      ; byte to search for
        xor di, di                                      ; byte offset
        mov cx, 0FFFFh                                  ; maximum length

appendPgmName_10:
        repnz scasb                                     ; scan for end of string
        scasb                                           ; test if double null
        jne appendPgmName_10                            ;

        mov ax, 0001                                    ; # strings to append
        stosw                                           ;

appendPgmName_14:
        lodsb
        stosb
        or al, al                                       ; null byte copied ?
        jnz appendPgmName_14                            ; not yet -->

        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Size of Envirnment String                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es     environment block string (must be non-zero)          ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cx     # paragraphs requested. (if zero, allocate none).    ;
        ;                                                               ;
        ;...............................................................;

_getSizeOfEnvString:

        push di

        xor al, al                                      ; byte to search for
        xor di, di                                      ; byte offset
        mov cx, 0FFFFh                                  ; maximum length

getSizeEnv_10:
        repnz scasb                                     ; scan for end of string
        scasb                                           ; test if double null
        jne getSizeEnv_10                               ;

        mov cx, di                                      ; get length
        add cx, (sizeEXPANDNAME + sizeParagraph - 1)/2  ; include size of trailing filename
        add cx, 2                                       ; include count word
        shr cx, 1                                       ; size to paragraphs
        shr cx, 1
        shr cx, 1
        shr cx, 1
        or cx, cx

        pop di
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Set Program Name in Memory Block                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es     block to change (not header address)                 ;
        ;   dx     full or partial filename (null terminated)           ;
        ;                                                               ;
        ;...............................................................;

_setProgramNameInMemBlock:

        SaveSegments
        mov ax, es
        or ax, ax                                       ; no destination mem block ?
        jz _setProgName_22                              ; ignore set name -->

        sub ax, (sizeMEMBLOCK/ PARAGRAPH)
        push ax                                         ; save seg address

        mov ax, ss
        mov ds, ax                                      ; just in case it's not
        mov es, ax                                      ; scan expanded name

        xor ax, ax
        mov cx, -1
        mov di, dx                                      ; scan name for null terminator
        repnz scasb                                     ; scan name
        not cx                                          ; valid count

_setProgName_08:
        cmp byte ptr es:[ di - 1 ], '\'
        jz _setProgName_12                              ; we have start of name -->
        dec di
        loop _setProgName_08

        mov di, dx                                      ; name contained no path

_setProgName_12:
        mov si, di                                      ; save filename 
        mov dx, di                                      ; save filename 
        pop es                                          ; restore memblock seg address

        mov ax, 2020h
        mov di, offset _memPgmName                      ; where to init
        mov cx, sizefnName / 2                          ; size filename in words
        rep stosw                                       ; initialize to blanks

        mov di, offset _memPgmName                      ; where to init
        mov cx, sizefnName                              ; size filename in bytes

_setProgName_16:
        lodsb                                           ; get byte
        or al, al                                       ; end of name ?
        jz _setProgName_22                              ; if end of name -->
        cmp al, '.'                                     ; extension ?
        jz _setProgName_22                              ; if end of name -->

        stosb                                           ; save character in mem block
        loop _setProgName_16

_setProgName_22:
        RestoreSegments
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  If Overlay Loader, return size                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   al     mode flag                                            ;
        ;   cx:dx  file size to return                                  ;
        ;                                                               ;
        ;...............................................................;

ifOverlayLoader:
        push es
        push si
        RetCallersStackFrame es, si                     ; save size only if non-Overlay
        mov word ptr es:[ _CX           ][ si ], dx
        mov word ptr es:[ _BX           ][ si ], cx
        mov word ptr es:[ _AX           ][ si ], 0000
        pop si
        pop es

        cmp al, execLoadOverlay                         ; load overlay ?
        clc
        ret

RxDOS   ENDS
        END


