;	SCCSID = @(#)exec.asm	1.3 85/08/13
;	SCCSID = @(#)exec.asm	1.3 85/08/13
;    AN000 version 4.0 jan. 1988
;    A007  PTM 3957 - fake vesrion for IBMCACHE.COM
;    A008  PTM 4070 - fake version for MS WINDOWS

;SUBTTL $exec - load/go a program
;PAGE
;
; 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
;
%include "ea.mac"
%include "version.mac"
%include "lmacros2.mac"
%include "memsw.mac"
%include "entryseg.nas"

	I_Need	   Temp_Var2,WORD	     ;AN000;file type from $open
	I_Need	   Special_Entries,WORD      ;AN007;address of special entries
	I_Need	   Special_Version,WORD      ;AN007;special version number
	I_Need	   Fake_Count,BYTE	     ;AN008;fake version count
        I_need  User_SS,WORD
        I_need  User_SP,WORD
        I_need  NSS,WORD
        I_need  NSP,WORD
	extern	restore_world

%ifndef BUF2
%IF	BUFFERFLAG
	extrn	restore_user_map:near
%ENDIF
%endif

section DOSDATATABLE	; in DOSDATA

	align 2, db ?
exec_init_SP	    DW	?
exec_init_SS	    DW	?
exec_init_IP	    DW	?
exec_init_CS	    DW	?

exec_internal_buffer	EQU OpenBuf

exec_signature	    DW	?		; must contain 4D5A  (yay zibo!)
exec_len_mod_512    DW	?		; low 9 bits of length
exec_pages	    DW	?		; number of 512b pages in file
exec_rle_count	    DW	?		; count of reloc entries
exec_par_dir	    DW	?		; number of paragraphs before image
exec_min_BSS	    DW	?		; minimum number of para of BSS
exec_max_BSS	    DW	?		; max number of para of BSS
exec_SS 	    DW	?		; stack of image
exec_SP 	    DW	?		; SP of image
exec_chksum	    DW	?		; checksum  of file (ignored)
exec_IP 	    DW	?		; IP of entry
exec_CS 	    DW	?		; CS of entry
exec_rle_table	    DW	?		; byte offset of reloc table
Exec_Signature equ exec_signature	; NASM port label
Exec_header_len     EQU $-Exec_Signature

curdirLEN equ curdirLen	; NASM port equate
exec_internal_buffer_size   EQU (128+128+53+curdirLEN)
%warning out: Please make sure that the following are contiguous and of the
%warning out: following sizes:
%warning out:
%warning out: OpenBuf     128
%warning out: RenBuf	    128
%warning out: SearchBuf    53
%warning out: DummyCDS    CurDirLen

section DOSCODECODE	; TABLE	ENDS

;.sall

 assume nocheck, ss:DOSGROUP

procedure   D_Exec,NEAR
	cmp al, 85h
	je .state
	cmp al, 5
	jne @F
.state:
	call SYS_RET_OK			; NC in iret fl

		; the following copied from disp.nas
	CLI
	push ss
	pop ds				; ds => DOSDATA, overwritten soon
 assume ds:DOSGROUP
user_SP equ User_SP	; NASM port label
user_SS equ User_SS	; NASM port label
	MOV BP, [user_SP]		; -> user stack
	MOV SS, [user_SS]
	MOV SP, bp			; restore user stack
 assume ss:nothing
	MOV BYTE PTR [BP + user_AX],AL
	MOV AX, [NSP]
	MOV [user_SP], AX
	MOV AX, [NSS]
	MOV [user_SS], AX
        DEC byte [INDOS]
	CALL restore_world		; restore all registers
 assume ds:nothing, es:nothing
	xor ax, ax
	push ax
	jmp do_a20_off_ax_on_stack

@@:
 assume ss:DOSGROUP
	ASSUME	DS:NOTHING, ES:NOTHING
PUBLIC EXEC001S,EXEC001E
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
EXEC001E:
	Enter
;
; validate function
;

	MOV	[exec_func],AL

	and al, 7Fh			; normalise from FreeDOS extension
	CMP	AL,3			; only 0, 1 or 3 are allowed
	Jbe	exec_check_2

exec_bad_fun:
	MOV	byte [ss:EXTERR_LOCUS],errLOC_Unk ; Extended Error Locus
	mov	al,error_invalid_function

exec_ret_err:
	Leave
	transfer    SYS_RET_ERR

exec_check_2:
	CMP	AL,2
	je	exec_bad_fun

	MOV	[exec_blkL],BX		; stash args
	MOV	[exec_blkH],ES
	MOV	byte [exec_load_high],0
;
; set up length of exec name
;
	MOV	[execNameL],DX
	MOV	[execNameH],DS
	MOV	SI,DX			; move pointer to convenient place
	invoke	DStrLen
	MOV	[ExecNameLen],CX		; save length

	XOR	AL,AL			; open for reading
	PUSH	BP
	invoke	D_OPEN			; is the file there?
	POP	BP
	JC	exec_ret_err
;File Type Checking
;	CMP	BYTE PTR [Temp_Var2],EAEXISTING      ;AN000;;FT.  old file ?
;	JZ	oldexf				     ;AN000;;FT.  yes
;	TEST	BYTE PTR EXEC_FUNC,EXEC_FUNC_OVERLAY ;AN000;;FT.  exec overlay?
;	JNZ	exovrly 			     ;AN000;;FT.  yes
;	CMP	BYTE PTR [Temp_Var2],EAEXECUTABLE    ;AN000;;FT.  only file type
;	JZ	oldexf				     ;AN000;;FT.  3 & 4 will pass
;	CMP	BYTE PTR [Temp_Var2],EAINSTALLABLE   ;AN000;;FT.
;	JZ	oldexf				     ;AN000;;FT.
;exerr: 					      ;AN000;;FT.
;	MOV	AL,error_access_denied		     ;AN000;;FT.  error
;	JMP	exec_ret_err			     ;AN000;;FT.
;exovrly:					      ;AN000;;FT.
;	CMP	BYTE PTR [Temp_Var2],EAOVERLAY	     ;AN000;;FT.  only 5,6,7  pass
;	JZ	oldexf				     ;AN000;;FT.
;	CMP	BYTE PTR [Temp_Var2],EADEV_DRIVER    ;AN000;;FT.
;	JZ	oldexf				     ;AN000;;FT.
;	CMP	BYTE PTR [Temp_Var2],EAIFS_DRIVER    ;AN000;;FT.
;	JNZ	exerr				     ;AN000;;FT.
;
;oldexf:					      ;AN000;
;File Type Checking

	MOV	[exec_fh],AX
	MOV	BX,AX
	XOR	AL,AL
	invoke	D_IOCTL
	JC	Exec_bombJ
	TEST	DL,devid_ISDEV
	JZ	exec_check_environ
	MOV	AL,error_file_not_found
Exec_bombJ:
Exec_Bomb equ exec_bomb	; NASM port label
	JMP	Exec_Bomb

BadEnv:
	MOV	AL,error_bad_environment
	JMP	exec_bomb

exec_check_environ:
	and	word [exec_load_block],0
	and	word [exec_environ],0

	TEST	BYTE [exec_func],exec_func_overlay	; overlays... no environment
	JNZ	exec_read_header
	LDS	SI,[exec_blk]		; get block
 assume ds:nothing
	MOV	AX,[SI + Exec1_environ]	; address of environ
	OR	AX,AX
	JNZ	exec_scan_env
	MOV	DS,[ss:CurrentPDB]
 assume ds:PSP
	MOV	AX,[PDB_environ]
	MOV	[exec_environ],AX
	OR	AX,AX
	JZ	exec_read_header

exec_scan_env:
	MOV	ES,AX
 assume es:nothing
	XOR	DI,DI
	MOV	CX,07FFFh		; at most 32k of environment
	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
	jz	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
%ifdef LDOSMEM
	EnterCrit   critMem
	call get_flags
	call AllocateMCBCompatible.flags_cx
	LeaveCrit   critMem
%else
	invoke	D_ALLOC			; can we get the space?
%endif
	POP	DS
 assume ds:nothing
	POP	CX
	JNC	exec_save_environ
	JMP	exec_no_mem		; nope... cry and sob
	nop	; identicalise

exec_save_environ:
	MOV	ES,AX
 assume es:nothing
	MOV	[exec_environ],AX 	; save him for a rainy day
	XOR	SI,SI
	MOV	DI,SI
	REP	MOVSB			; copy the environment
	MOV	AX,1
	STOSW
	LDS	SI,[execName]
	MOV	CX,[execNameLen]
	REP	MOVSB

exec_read_header:
;
; We read in the program header into the above data area and determine
; where in this memory the image will be located.
;
	Context DS
exec_header_len equ Exec_header_len	; NASM port equate
	MOV	CX,exec_header_len	; header size
	MOV	DX,OFFSET exec_signature wrt DOSGROUP
	PUSH	ES
	PUSH	DS
	CALL	ExecRead
	POP	DS
	POP	ES
 assume ds:DOSGROUP, es:nothing
	JC	exec_bad_file
	test	AX,AX
	JZ	exec_bad_file
	CMP	AX,exec_header_len	; did we read the right number?
	JNZ	exec_com_filej		; yep... continue
	TEST	word [exec_max_BSS],-1 	; indicate load high?
	JNZ	exec_check_sig
	MOV	byte [exec_load_high],-1
exec_check_sig:
	MOV	AX,[exec_signature]
	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
	MOV	CL,5			; convert to paragraphs
	SHL	AX,CL
	SUB	AX,[exec_par_dir] 	; AX = size in paragraphs
	MOV	[exec_res_len_para],AX

;
; Do we need to allocate memory?  Yes if function is not load-overlay
;
	TEST	BYTE [exec_func],exec_func_overlay
	JZ	exec_allocate		; allocation of space
;
; get load address from block
;
	LES	DI,[exec_blk]
exec3_load_addr equ Exec3_load_addr	; NASM port equate
	MOV	AX,[ES:DI + exec3_load_addr]
	MOV	[exec_dma],AX
exec3_reloc_fac equ Exec3_reloc_fac	; NASM port equate
	MOV	AX,[ES:DI + exec3_reloc_fac]
	MOV	[exec_rel_fac],AX
	JMP	exec_find_res

exec_no_mem_pop:
	pop ax
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
	SaveReg <AX,BP>
	invoke	D_CLOSE
	RestoreReg  <BP,AX>
Exec_Ret_Err equ exec_ret_err	; NASM port label
	JMP	Exec_Ret_Err

%ifdef LDOSMEM
extern GetMCBOwner, GetMCBFlags, AllocateLargestMCBRandom, GetRawMCBFlags
extern AllocateMCBCompatible.flags_cx

allocate_largest:
	EnterCrit   critMem
	push cx
	push dx
	call GetMCBOwner			; dx = owner
	call get_flags
	call AllocateLargestMCBRandom		; call two-area allocation
	pop dx
	pop cx
	LeaveCrit   critMem
	retn

get_flags:
	call GetMCBFlags			; cx = fixed flags
	test byte [exec_func], 80h		; FreeDOS extension to use UMA ?
	jz .not_force_uma			; no, use current flags -->
	call GetRawMCBFlags
	or cx, 180h				; 100h = ignore UMB link status,
						;  80h = UMA then LMA, dual areas
						;  (80h overrides all lesser areas)
.not_force_uma:
	retn
%endif

exec_allocate:
	DOSAssume   CS,<DS>,"EXEC/exec_allocate"
%ifdef LDOSMEM
	push ax
	add ax, 10h
	mov bx, ax
	jc .max_ffff
	TEST byte [exec_load_high],-1	; if load high, use max
	JNZ .max_ffff		; use max
	add ax, [exec_max_BSS]
	jnc @F
.max_ffff:
	mov ax, -1
@@:
	add bx, [exec_min_BSS]
	jnc @F
	mov bx, -1
@@:
	push ds
	call allocate_largest
	pop ds
 assume ds:DOSGROUP
	jc exec_no_mem_pop
	MOV	[exec_load_block],AX
	pop ax
%else
	PUSH	AX
	MOV	BX,0FFFFh		; see how much room in arena
	PUSH	DS
	invoke	D_ALLOC			; should have carry set and BX has max
	POP	DS
 assume ds:DOSGROUP
	POP	AX
%endif
	ADD	AX,10h			; room for header
	CMP	BX,11h			; enough room for a header
	JB	exec_no_mem
	CMP	AX,BX			; is there enough for bare image?
	JA	exec_no_mem
	TEST	byte [exec_load_high],-1	; if load high, use max
	JNZ	exec_BX_max		; use max
	ADD	AX,[exec_min_BSS] 	; go for min allocation
	JC	exec_no_mem		; oops! carry
	CMP	AX,BX			; enough space?
	JA	exec_no_mem		; nope...
	SUB	AX,[exec_min_BSS]
	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:
	MOV	BX,AX
	MOV	[exec_size],BX
%ifdef LDOSMEM
	MOV	ax, [exec_load_block]
%else
	PUSH	DS
	invoke	D_ALLOC			; get the space
	POP	DS
	JC	exec_no_mem
	MOV	[exec_load_block],AX
%endif
	ADD	AX,10h
	TEST	byte [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_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	D_LSEEK			; seek to resident
	POP	DS
 assume ds:DOSGROUP
	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
		; note: if running this, word [exec_res_len_para] >= 1000h
		;  so that after the next subtraction, >= 20h paragraphs
		;  are remaining. if this read is short <= 511 bytes then
		;  it won't break out from the cmp cx, 512. afterwards
		;  however it will loop back to exec_big_read and then
		;  >= 20h paragraphs (>= 512 bytes) are read in the next
		;  iteration and a done read count of 0 will always be
		;  detected as a read >= 512 bytes short, aborting the load.
		; refer to https://github.com/LoopZ/TheList/issues/1#issuecomment-3780971188

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
 assume ds:DOSGROUP
exec_bad_fileJ equ exec_bad_filej	; NASM port label
	JC	exec_bad_fileJ
	DOSAssume   CS,<DS>,"EXEC/exec_read_ok"
	CMP	CX,AX			; did we read enough?
	POP	BX			; get paragraph count back
execCheckEnd equ ExecCheckEnd	; NASM port label
	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
Exec_Bad_fileJ equ exec_bad_filej	; NASM port label
	JAE	Exec_Bad_fileJ
;
; We've read in CX bytes... bump DTA location
;
ExecCheckEnd:
	ADD	[exec_dma],BX		; bump dma address
	TEST	word [exec_res_len_para],-1
	JNZ	exec_big_read
;
; The image has now been read in.  We must perform relocation to
; the current location.
;
exec_do_reloc:
	MOV	CX,[exec_rel_fac]
	MOV	AX,[exec_SS]		; get initial SS
	ADD	AX,CX			; and relocate him
	MOV	[exec_init_SS],AX

	MOV	AX,[exec_SP]		; initial SP
	MOV	[exec_init_SP],AX

	LES	AX,[exec_IP]
 assume es:nothing
	MOV	[exec_init_IP],AX
	MOV	AX,ES
	ADD	AX,CX			; relocated...
	MOV	[exec_init_CS],AX

	XOR	CX,CX
	MOV	DX,[exec_rle_table]
	MOV	BX,[exec_fh]
	PUSH	DS
	XOR	AX,AX
	invoke	D_LSEEK
	POP	DS
 assume ds:DOSGROUP

	JNC	exec_get_entries
exec_bad_filej:
	JMP	exec_bad_file

exec_get_entries:
	MOV	DX,[exec_rle_count]	; Number of entries left

exec_read_reloc:
	ASSUME	DS:NOTHING
	PUSH	DX
	MOV	DX,OFFSET exec_internal_buffer wrt DOSGROUP
	MOV	CX,((exec_internal_buffer_size)/4)*4
	PUSH	DS
	CALL	ExecRead
	POP	ES
 assume es:nothing
	POP	DX
	JC	exec_bad_filej
	MOV	CX,(exec_internal_buffer_size)/4
	MOV	DI,OFFSET exec_internal_buffer wrt DOSGROUP ; Pointer to byte location in header
;
; Relocate a single address
;
	MOV	SI,[exec_rel_fac]

exec_reloc_one:
	test	DX,DX			; Any more entries?
	JE	exec_set_PDBJ

exec_get_addr:
	LDS	BX,[ES:DI]	; Get ra/sa of entry
 assume ds:nothing
	MOV	AX,DS			; Relocate address of item
	ADD	AX,SI
	MOV	DS,AX
 assume ds:nothing
	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
 assume ds:nothing
	JMP	exec_read_reloc

exec_set_PDBJ:
	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 [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!

; 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:
%ifdef LDOSMEM
	mov bx, [exec_fh]
	xor cx, cx
	xor dx, dx
	mov ax, 2			; seek to EOF
	invoke D_LSEEK			; get file size
	test dx, dx			; >= 64 KiB ?
	jnz exec_no_memj		; yes, too much -->
	cmp ax, 0FF00h			; >= 0FF00h ?
	jae exec_no_memj		; too much -->
	add ax, 15			; round up
	mov cl, 4
	shr ax, cl			; to paragraphs
	add ax, 10h			; add in PSP size
	xchg bx, ax			; bx = minimum size
	mov ax, -1			; maximum size = largest
	call allocate_largest
	jc exec_no_memj
	MOV	[exec_size],BX		; save size of allocation block
%else
	MOV	BX,0FFFFh
	invoke	D_ALLOC			; largest piece available as error
	OR	BX,BX
	JZ	exec_no_memj
	MOV	[exec_size],BX		; save size of allocation block
	PUSH	BX
	invoke	D_ALLOC			; largest piece available as error
	POP	BX			; get size of block...
%endif
	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
exec_read_com:
	SUB	AX,100h 		; remember size of psp
exec_read_block:
	PUSH	AX			; save number to read
	MOV	BX,[exec_fh]		; of com file
	XOR	CX,CX			; but seek to 0:0
	MOV	DX,CX
	XOR	AX,AX			; seek relative to beginning
	invoke	D_LSEEK			; back to beginning of file
	POP	CX			; number to read
	MOV	DS,[exec_dma]
 assume ds:nothing
	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?
	JZ	exec_no_memj		; exactly the wrong number... no memory
	TEST	BYTE [exec_func],exec_func_overlay
	JNZ	exec_set_PDB		; no starto, chumo!
	MOV	AX,[exec_DMA]
	SUB	AX,10h
	MOV	[ss:exec_init_CS],AX
	MOV	word [ss: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
	MOV	[ss:exec_init_SP],SI 	; max value for read is also SP!
	MOV	[ss:exec_init_SS],AX
	MOV	DS,AX
 assume ds:nothing
	MOV	WORD PTR [SI],0 	; 0 for return

exec_set_PDB:
	MOV	BX,[exec_fh]		; we are finished with the file.
	CALL	exec_dealloc
	PUSH	BP
	invoke	D_CLOSE			; release the jfn
	POP	BP
	CALL	exec_alloc
	TEST	BYTE [exec_func],exec_func_overlay
	JZ	exec_build_header
	CALL	Scan_Execname		;MS.;AN007;
	CALL	Scan_Special_Entries	;MS.;AN007;
	Leave
	transfer    SYS_RET_OK		; overlay load -> done

exec_build_header:
	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
 assume ds:MCB
	MOV	[SI],DX 		; assign ownership
NO_OWNER:
	MOV	AX,[exec_load_block]	; get load block pointer
	DEC	AX
	MOV	DS,AX			; point to header
 assume ds:MCB
	MOV	[SI],DX 		; assign ownership

	PUSH	DS			;AN000;MS. make ES=DS
	POP	ES			;AN000;MS.
 assume es:MCB
ARENA_NAME equ arena_name	; NASM port equate
	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

	cmp cx, 8
	jbe @F
	mov cx, 8
@@:
	jcxz mem_done
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
	cmp al, 'a'
	jb @F
	cmp al, 'z'
	ja @F
	sub al, 20h
@@:
	STOSB				;AN000;;MS. move char
	LOOP	movename		;AN000;;MS. continue
mem_done:				;AN000;
	XOR	AL,AL			;AN000;;MS. make ASCIIZ
ARENA_struc_size equ arena_struc_size	; NASM port equate
	CMP	DI,ARENA_struc_size		;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	D_Dup_PDB		; ES is now PDB
	POP	DX

	PUSH	word [exec_environ]
	POP	word [ES:PDB_environ]
;
; set up proper command line stuff
;
	LDS	SI,[exec_blk]		; get the block
 assume ds:nothing
	PUSH	DS			; save its location
	PUSH	SI
exec0_5C_FCB equ Exec0_5C_FCB	; NASM port equate
	LDS	SI,[SI + exec0_5C_FCB]	; get the 5c fcb
 assume ds:nothing
;
; 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
;
	XOR	AX,AX			; zero extent, etc for CPM
	STOSW
	STOSW
;
; DI = 5Ch + 12 + 4 = 5Ch + 10h = 6Ch
;
	POP	CX
	POP	SI			; get block
	POP	DS
 assume ds:nothing
	PUSH	DS			; save (again)
	PUSH	SI
exec0_6C_FCB equ Exec0_6C_FCB	; NASM port equate
	LDS	SI,[SI + exec0_6C_FCB]	; get 6C FCB
 assume ds:nothing
;
; 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
 assume ds:nothing
exec0_com_line equ Exec0_com_line	; NASM port equate
	LDS	SI,[SI + exec0_com_line]	; command line
 assume ds:nothing
;
; 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
exec_Set_Return equ exec_set_return	; NASM port label
	JNC	exec_Set_Return
	MOV	BL,CL
exec_set_return:
	invoke	get_user_stack		; get his return address
	PUSH	word [SI + user_CS]		; suck out the CS and IP
	PUSH	word [SI + user_IP]
	PUSH	word [SI + user_CS]		; suck out the CS and IP
	PUSH	word [SI + user_IP]
	POP	WORD PTR [ES:PDB_Exit]
	POP	WORD PTR [ES:PDB_Exit+2]
	XOR	AX,AX
	MOV	DS,AX
 assume ds:IVT
	POP	word [addr_int_terminate] ; save them where we can get them later
	POP	word [addr_int_terminate+2]   ; when the child exits.
	MOV	WORD [ss:DMAADD],80h
	MOV	DS,[ss:CurrentPDB]
 assume ds:PSP
	MOV	WORD PTR [ss:DMAADD+2],DS

 extern ReleaseOwnersMCBs, backdoor_free_mcb_exec, backdoor_indicator
	push bx
	EnterCrit   critMem
	xor bx, bx
	xchg bx, [ss:backdoor_free_mcb_exec]	; get backdoor owner
	call ReleaseOwnersMCBs			; exec success: free it
	LeaveCrit   critMem
	push ds
	lds bx, [ss:backdoor_indicator]		; -> indicator
 assume ds:nothing
	cmp bx, -1				; invalid address ?
	je @F					; yes -->
	mov byte [bx], -1			; else, set the indicator
	or word [ss:backdoor_indicator], -1	; and disable indicator after this
@@:
	pop ds
 assume ds:PSP
	pop bx

	TEST	BYTE [exec_func],exec_func_no_execute
	JZ	exec_go

	LDS	SI,[ss:exec_init_SP]   ; get stack
 assume ds:nothing
	LES	DI,[exec_blk]		; and block for return
 assume es:nothing
exec1_SS equ Exec1_SS	; NASM port equate
	MOV	[ES:DI + exec1_SS],DS	; return SS

	DEC	SI			; 'push' default AX
	DEC	SI
	MOV	[SI],BX 		; save default AX reg
exec1_SP equ Exec1_SP	; NASM port equate
	MOV	[ES:DI + exec1_SP],SI	; return 'SP'

	LDS	AX,[ss:exec_init_IP]
 assume ds:nothing
exec1_CS equ Exec1_CS	; NASM port equate
	MOV	[ES:DI + exec1_CS],DS	; initial entry stuff

exec1_IP equ Exec1_IP	; NASM port equate
	MOV	[ES:DI + exec1_IP],AX
	Leave
	transfer    SYS_RET_OK

exec_go:
	LDS	SI,[ss:exec_init_IP]   ; get entry point
 assume ds:nothing
	LES	DI,[ss:exec_init_SP]   ; new stack
 assume es:nothing
	MOV	AX,ES
;
; DS:SI points to entry point
; AX:DI points to initial stack
; DX has PDB pointer
; BX has initial AX value
;
	context es
	CLI
	MOV	SS,AX			; set up user's stack
	MOV	SP,DI			; and SP
	ASSUME	SS:NOTHING
	STI
	nop
	pushf				; iret frame
	PUSH	DS			; fake long call to entry
	PUSH	SI
	mov byte [es:INDOS], 0		; clear InDOS upon DOS stack unused
	MOV	ES,DX			; set up proper seg registers
 assume es:nothing
	MOV	DS,DX
 assume ds:nothing
	push bx				; = ax = bx on stack
do_a20_off_ax_on_stack:
	push bx				; for both registers
%ifndef BUF2
%IF	BUFFERFLAG
	invoke	restore_user_map
%ENDIF
%endif
extern a20off_entry

	jmp DOSENTRY:a20off_entry	; must address XMS entry variable with cs

EndProc D_Exec

Procedure   ExecRead,NEAR
 assume ss:DOSGROUP
	CALL	exec_dealloc
	MOV	bx,[exec_fh]
	PUSH	BP
	invoke	D_READ
	POP	BP
	CALL	exec_alloc
	return
EndProc ExecRead

procedure   exec_dealloc,near
 assume ss:DOSGROUP
	ASSUME	    DS:NOTHING,ES:NOTHING
	PUSH	    BX
	MOV	    BX,arena_owner_system
	EnterCrit   CritMEM
	CALL	    ChangeOwners
	POP	    BX
	return
EndProc exec_dealloc

procedure   exec_alloc,near
 assume ss:DOSGROUP
	PUSH	    BX
	MOV	    BX,[ss:CurrentPDB]
	CALL	    ChangeOwners
	LeaveCrit   CritMEM
	POP	    BX
	return
EndProc exec_alloc

procedure   ChangeOwners,NEAR
 assume ss:DOSGROUP
	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
 assume ss:DOSGROUP
	OR	AX,AX			; is area allocated?
	retz				; no, do nothing
	DEC	AX
	PUSH	DS
	MOV	DS,AX
 assume ds:MCB
	MOV	[arena_owner],BX
	POP	DS
 assume ds:nothing
	return
EndProc ChangeOwner

Procedure    Scan_Execname,near 	;AN000;MS.

	LDS	SI,[execName]		;AN000;MS. DS:SI points to name
save_begin:				;AN000;
	MOV	CX,SI			;AN000;MS. CX= starting addr
scan0:					;AN000;
	LODSB				;AN000;MS. get char
	CMP	AL,':'                  ;AN000;;MS. is ':' , may be A:name
	JZ	save_begin		;AN000;;MS. yes, save si
	CMP	AL,'\'                  ;AN000;;MS. is '\', may be A:\name
	JZ	save_begin		;AN000;;MS. yes, save si
	CMP	AL,0			;AN000;;MS. is end of name
	JNZ	scan0			;AN000;;MS. no, continue scanning
	SUB	SI,CX			;AN000;;MS. get name's length
	XCHG	SI,CX			;AN000;;MS. cx= length, si= starting addr

	return				;AN000;;MS.
EndProc Scan_Execname			;AN000;;MS.


Procedure    Scan_Special_Entries,near	;AN000;MS.
 assume ss:DOSGROUP

	DEC    CX			;AN007;MS. cx= name length
	MOV    DI,[ss:Special_Entries]	;AN007;MS. es:di -> addr of special entries
	CALL   Reset_Version		;AN008;MS.
	PUSH   ss			;AN007;MS.
	POP    ES			;AN007;MS.
 assume es:DOSGROUP
Getentries:				;AN007;MS.
	MOV    AL,[ES:DI]		;AN007;MS. end of list
	OR     AL,AL			;AN007;MS.
	JZ     end_list 		;AN007;MS. yes
	MOV    [ss:Temp_Var2],DI	;AN007;MS. save di
	CMP    AL,CL			;AN007;MS. same length ?
	JNZ    skipone			;AN007;MS. no
	INC    DI			;AN007;MS. es:di -> special name
	PUSH   CX			;AN007;MS. save length and name addr
	PUSH   SI			;AN007;MS.
	REPZ   CMPSB			;AN007;MS. same name ?
	JNZ    not_matched		;AN007;MS. no
	MOV    AX,[ES:DI]		;AN007;MS. get special version
	MOV    [ss:Special_Version],AX	;AN007;MS. save it
	MOV    AL,[ES:DI+2]		;AN008;MS. get fake count
	MOV    [ss:Fake_Count],AL	;AN007;MS. save it
	POP    SI			;AN007;MS.
	POP    CX			;AN007;MS.
	JMP    SHORT end_list		;AN007;MS.
not_matched:				;AN007;MS.
	POP    SI			;AN007;MS. restore si,cx
	POP    CX			;AN007;MS.
skipone:				;AN007;MS.
	MOV    DI,[ss:Temp_Var2]	;AN007;MS. restore old di
	XOR    AH,AH			;AN007;MS. position to next entry
	ADD    DI,AX			;AN007;MS.
	ADD    DI,4			;AN007;MS.
	JMP    Getentries		;AN007;MS.


end_list:				;AN007;MS.
	return
EndProc Scan_Special_Entries		;AN000;;MS.

Procedure    Reset_Version,near 	;AN008;MS.

	CMP    byte [ss:Fake_Count],0FFH	;AN008;MS.
	JNZ    dont_reset		;AN008;MS.
	MOV    word [ss:Special_Version],0	;AN008;MS. reset to current version
dont_reset:
	return
EndProc Reset_Version,near		;AN008;;MS.

