;    File              : $PROCESS.A86$
;
;    Description       :
;
;    Original Author   : DIGITAL RESEARCH
;
;    Last Edited By    : $CALDERA$
;
;-----------------------------------------------------------------------;
;    Copyright Work of Caldera, Inc. All Rights Reserved.
;      
;    THIS WORK IS A COPYRIGHT WORK AND CONTAINS CONFIDENTIAL,
;    PROPRIETARY AND TRADE SECRET INFORMATION OF CALDERA, INC.
;    ACCESS TO THIS WORK IS RESTRICTED TO (I) CALDERA, INC. EMPLOYEES
;    WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF
;    THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN CALDERA, INC. WHO
;    HAVE ACCEPTED THE CALDERA OPENDOS SOURCE LICENSE OR OTHER CALDERA LICENSE
;    AGREEMENTS. EXCEPT UNDER THE EXPRESS TERMS OF THE CALDERA LICENSE
;    AGREEMENT NO PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED,
;    COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED,
;    CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED, RECAST,
;    TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF
;    CALDERA, INC. ANY USE OR EXPLOITATION OF THIS WORK WITHOUT
;    AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO CRIMINAL AND
;    CIVIL LIABILITY.
;-----------------------------------------------------------------------;
;
;    *** Current Edit History ***
;    *** End of Current Edit History ***
;    $Log$
;    PROCESS.A86 1.25 94/07/13 16:15:27 
;    Int21/26 (create PSP) copies 1st 20 entries of parental XFT
;    PROCESS.A86 1.24 94/06/28 11:15:28
;    Don't issue an int 21 to get curret psp while within int21/4B load overlay
;    PROCESS.A86 1.20 93/09/28 19:44:03 
;    Don't lose 8th character of name in DMD during exec
;    PROCESS.A86 1.14 93/06/18 21:00:57
;    Support for Int 21/4B05 added
;    PROCESS.A86 1.13 93/06/11 02:11:20
;    GateA20 disabled on EXEC for EXEPACKED apps
;    Fix termination code 
;    ENDLOG
;

 	include	pcmode.equ
	include fdos.def
	include	i:psp.def
	include i:mserror.equ
	include	vectors.def
	include i:msdos.equ
	include i:exe.def
	include	i:char.def
	include	i:redir.equ
	include	i:doshndl.def

HILOAD	equ	TRUE

PCM_CODE	CSEG	BYTE
	extrn	check_dmd_id:near
	extrn	dbcs_lead:near
	extrn	error_exit:near
	extrn	fdos_nocrit:near
	extrn	free_all:near
	extrn	get_dseg:near		; Get the PCMODE Data Segment
	extrn	int21_exit:near
	extrn	invalid_function:near
	extrn	dos_entry:near
	extrn	return_AX_CLC:near
	extrn	set_owner:near
	extrn	strlen:near
	extrn	toupper:near
	extrn	valid_drive:near
;
;
;PC-DOS PSP Creation Update and Maintance routines
;
;	*****************************
;	***    DOS Function 55    ***
;	***    Create New PSP     ***
;	*****************************
;
;	entry:	DX = New PSP Segment
;		SI = Top of Available
;
;	This function copies the existing PSP and generates a new Process
;	environment. The file table is updated and dos_SI is used to determine
;	the process' memory size. The PSP is then made the CURRENT_PSP

	Public	func55
func55:
	mov	cx,PSPLEN/2		; copy whole PSP
	call	create_psp
    mov al,0F0h     
    ret             
	
create_psp:
	mov	ax,current_psp		; All based on the Current PSP
	call	copy_psp		; Do the Basic Copy
	mov	ax,current_psp		; get the Current PSP address
	mov	PSP_PARENT,ax		;   and save it in child's psp

	cmp	ax,dx			; Is this the ROOT level DOS process
	 jz	create_psp10		; Yes because Current PSP == New PSP
					; therefore skip the EXEC function
					; because this is done by P_CREATE

	mov	FD_FUNC,FD_EXEC		; Must Update the Open Counts ETC.
	mov	FD_PSPSEG,dx		; New PSP address
	call	fdos_nocrit

create_psp10:
	mov	current_psp,es		; set the New PSP address
	ret

;
;	*****************************
;	***    DOS Function 26    ***
;	***    Create New PSP     ***
;	*****************************
;
;	entry:	DX = New PSP Segment
;
	Public	func26
func26:
	les	di,int21regs_ptr	; Get pointer to INT 21 structure of
	mov	es,es:reg_CS[di]	; IP/CS/Flags and get the USER CS this
	mov	si,PSP_MEMORY		; is used as the PSP for this function 
	mov	ax,es			; call and NOT current_psp
	mov	cx,PSPLEN/2		; copy whole PSP
	push	dx
	call	copy_psp
	pop	es
	mov	cx,20			; default XFT table has twenty files
	mov	di,offset PSP_XFT	;  and is in the PSP at this offset
	mov	PSP_XFNMAX,cx		; say we have 20 files max
	mov	PSP_XFTOFF,di
	mov	PSP_XFTSEG,es
	push	ds
	mov	ds,current_psp		; we copy 1st 20 entries of current
	lds	si,ds:PSP_XFTPTR	;  XFT to the child PSP
	rep	movsb			; we do not update file handle use
	pop	ds			;  counts, unlike Int21/55
	ret

copy_psp:
; copy CX words from AX:0 to DX:0, SI = memory top
	push	si			; Save the Memory TOP
	push	ds
	mov	es,dx			; Point ES to the New PSP
	mov	ds,ax			; Get the current PSP for this function
	xor	ax,ax
	mov di,ax ! mov si,ax
	rep	movsw			; Copy into New PSP

	mov	ds,ax			; Copy the current Terminate, Critical 
	mov	si,INT22_OFFSET		; Error and Control Break Handlers
	mov	di,offset PSP_TERM_IP	; into the new PSP
	mov	cl,6
	rep	movsw			; BREAK,TERM, CRIT ERR SAVED HERE
	pop	ds
	pop	PSP_MEMORY

	mov	PSP_INT20,020CDh	; Interrupt 20h Terminate
	mov	PSP_RES1,0FFFFh
	mov	PSP_RES2,0FFFFh
	mov	PSP_DOSCALL,021CDh	; INT 21h Function Call
	mov	PSP_DOSRETF,0CBh	; RETF

	mov	PSP_LONGCALL,09Ah	; CALLF AnySeg:MemSize
	mov	ax,PSP_MEMORY		; Get the Top of Memory
	sub	ax,dx			; Convert it to Memory Size
	cmp	ax,1000h		; Check for Over 64Kb
	mov	bx,0FEF0h		; Assume Over 64Kb
	jae	sce_10
	mov	bx,ax			; Convert the Paragragh Length
	mov	cl,4			; to a Byte Length	
	shl	bx,cl
	sub	bx,110h			; Reserve 110h Bytes for .COM Stack
sce_10:	
	push	dx
	mov	PSP_LONGOFF,bx		; Save the Byte Length
	xor 	dx,dx			; Call 5 Entry Segment
	mov 	ax,INT30_OFFSET		; Call 5 Entry Offset
	mov	cl,4			
	shr	ax,cl			; Entry Offset/16 => EO
	shr	bx,cl			; Jump Offset/16 => JO
	add	ax,dx			; EO + ES
	sub	ax,bx			; EO + ES - JO => JS 
	mov	PSP_LONGSEG,ax
	pop	dx
	ret

;
;	*****************************
;	***    DOS Function 50    ***
;	***    Set Current PSP    ***
;	*****************************
;
	Public	func50

; WARNING - called on USER stack

func50:
	mov	current_psp,bx
	ret

;	*****************************
;	***  DOS Function 51/62   ***
;	***    Get Current PSP    ***
;	*****************************
;
	Public	func51, func62

; WARNING - called on USER stack

func51:
func62:
	mov	bx,current_psp
	mov	reg_BX[bp],bx
	ret

eject
;**************************************************
;**************************************************
;***						***
;***	    Process Control Functions		***
;***						***
;**************************************************
;**************************************************

;	*****************************
;	***    DOS Function 31    ***
;	***   Terminate and Keep  ***
;	*****************************
;
	Public	func31
func31:
	mov	ax,6			; make 6 paragraphs our minimum size
	cmp	ax,dx			; Are we at our minimum size ?
	 jb	func31_05
	xchg	ax,dx			; no, enforce 6 paragraphs
func31_05:
	mov	exit_type,TERM_RESIDENT	; Exit by Terminate and Stay Resident
	mov	bx,current_psp		; and set the termination PSP to
	mov	term_psp,bx		; be the Current PSP

	push	ds			; Attempt to modify the memory
	mov	ds,bx			; partition size to that given in DX
	mov	bx,dx			; Remember DS and ES are swapped for
	call	mem_setblock		; the internal function.

	mov	ax,ds			; Now update the PSP_MEMORY field to
	add	ax,bx			; reflect the memory available to
	mov	ds:PSP_MEMORY,ax	; to the application now. Required by
	pop	ds			; MicroPro WordFinder

	mov	load_psp,0000		; Do not free PSP memory
	jmp	f31_term		; Common terminate handler

;
;	*****************************
;	***    DOS Function 4B    ***
;	*** Load or Execute Prog  ***
;	*****************************
;
;	An extra sub-function has been defined which is used by the
;	ROOT DOS process loader to ensure compatibility between the
;	Initial Register conditions for the ROOT DOS process and that
;	of any child process.
;
;	4B80h	-	GO Sub-Function expects all the internal and
;			external data areas to have been setup by a 
;			previous 4B01h function. Never Fails !
;
; Undocumented feature:
;	AX=4B03 returns SETVER version in AX, or zero
;

	Public	func4B
func4B:
	cmp	al,80h			; Is this the special GO sub-function
	 jnz	f4B_01			; No Process Normally
	jmp	start_child		; Go for It every thing else OK

f4B_01:
	cmp al,5 ! je f4B05		; Sub-Func 5:- Exec Hook
	cmp al,3 ! je f4B_02		; Sub-Func 3:- Load Overlay
	cmp al,1 ! jbe f4B_02		; Sub-Func 1:- Load and No Execute
					; Sub-Func 0:- Load and Execute
f4B_invalid:
	jmp	invalid_function	; Otherwise illegal Sub-Function

f4B05:
;-----
; On Entry:
;	ES:DX -> ExecState
esReserved	equ	word ptr 0	; reserved, must be zero
esFlags		equ	word ptr 2	; type flags
esProgName	equ	dword ptr 4	; points to ASCIIZ name
esPSP		equ	word ptr 8	; PSP of new program
esStartAddress	equ	dword ptr 10	; CS:IP of new program
esProgSize	equ	dword ptr 14	; program size, including PSP
;
;	type flags
ES_EXE		equ	0001h
ES_OVERLAY	equ	0002h
;
; On Exit:
;	None (A20 gate disabled)
;
	mov	di,dx			; ES:DI -> ExecState
	test	es:esFlags[di],not ES_EXE
	 jnz	f4B_invalid		; only COM or EXE supported
    call    return_AX_CLC       ; assume success
	lds	si,es:esProgName[di]	; DS:SI -> ASIIZ name
	mov	es,es:esPSP[di]		; ES = PSP
	push	es			; save for DX on exit
	call	SetPspNameAndVersion	; set up the name/version fields
	pop	dx			; DX = PSP
	push	ss
	pop	ds			; DS = pcmode data again
	cli				; Stop anybody interfering
	les	bp,int21regs_ptr	; point to user stack
	mov	es:reg_AX[bp],0		; return successful
	and	es:reg_FLAGS[bp],not CARRY_FLAG
	mov	ax,prev_int21regs_off
	mov	int21regs_off,ax
	mov	ax,prev_int21regs_seg
	mov	int21regs_seg,ax
	dec	indos_flag		; no longer in DOS
	jmpf	func4B05_stub		; exit via stub code


f4B_02:
	xor	ax,ax
	mov	load_env,ax		; Load environment NOT allocated
	mov	load_psp,ax		; Load memory NOT allocated
	dec	ax
	mov	load_handle,ax		; Mark Load file as CLOSED

	push es ! push dx		; expand the filename to a
	call	get_filename		;  full path to be inherited
	pop dx ! pop es			;  in the environment
	 jc	f4B_10			; Exit on error
	mov	ax,(MS_X_OPEN*256)+20h	; Open File
;	mov	al,0$010$0$000B		; ReadOnly & DenyWrite
	call	dos_entry
	 jnc	f4B_05			; Save Handle if No Error

	cmp	ax,ED_SHAREFAIL		; Check for a Sharing Error or Access
	 jz	f4B_04			; Denied if neither error codes then
	cmp	ax,ED_ACCESS		; Don't retry the Open function
	 jnz	f4B_10			; in compatibility

f4B_04:
	mov	ax,(MS_X_OPEN*256)+0	; retry the open in read-only
;	mov	al,0$000$0$000B		;  compatibility mode
	call	dos_entry
	 jc	f4B_10			; Stop On error

f4B_05:
	push ds ! pop es		; ES local again
	mov	load_handle,ax		; Save for Error Handling
	xchg	ax,bx			; Get the File Handle
	mov	si,offset exe_buffer
	call	get_execdata
	 jc	f4B_10
	call	point_param_block	; CL = subfunc, ES:DI -> param block
	cmp	cl,3			; Sub-Func 3:- Load Overlay
	 jne	f4B_go			; Sub-Func 0:- Load and Execute
					; Sub-Func 1:- Load and No Execute

	mov	si,es:2[di]		; si = Relocation Factor
	mov	di,es:[di]		; di = Load Segment

	call	loadimage		; load and relocate image
	 jc	f4B_10			; f4B_error - Return with an error
if DOS5
	mov	si,offset load_file	; Copy the process name into the DMD
	call	FindName		; DS:SI -> start of name
	call	GetVersion		; AX = version to return
	mov	es,current_psp		; poke the current psp
	mov	PSP_VERSION,ax		;  with the version number
endif
	jmp	return_AX_CLC		; All done
f4b_10:
	jmp	f4B_error


;
;	F4B_GO loads and executes the file whose handle is in BX.
;	This routine corresponds to sub-functions 0 and 1.
;
f4B_go:
	xor	ax,ax
	mov	si,offset exe_buffer	; .COM and .EXE file loading
	mov	exe_loadhigh,al		; Reset the Load High Flag
	cmp	ax,EXE_MAXPARA[si]
	 jnz	f4B_g15			; Load High Flag (MAXPARA == 0)
	dec	ax
	mov	exe_loadhigh,al		; Set the internal LOADHIGH flag
	mov	EXE_MAXPARA[si],ax	; and allocate all memory
f4B_g15:
	mov	ax,es:[di]		; get ENV pointer from param block
	call	build_env		; Build the environment
	 jc	f4B_error		; Stop on error

	call	calc_psp		; calculate new psp
	 jc	f4B_error		; Stop on error
	call	pblk_to_psp		; Copy parameters into PSP
	mov	si,load_image		; read the Load image
	mov	di,si			; to previously calculated address
	call	loadimage		; load in com file
	 jc	f4B_error		; quit if no memory
	call	set_up_psp		; build child's psp
	mov	dx,load_psp		; point at PSP seg

	mov	exit_type,TERM_NORMAL	; Initialise the Return code type
	mov	si,offset exe_buffer	; to normal an go
	call	check_exe
	 jc	f4B_go_com	

	mov	dx,load_image		; Get the Load Paragraph
	add	EXE_CS[si],dx		; bias the code segment
	add	EXE_SS[si],dx		;   and the stack segment too
	jmp	start_child		; goodbye!

;
f4B_go_com:				; Go for it .COM
;	mov	dx,load_psp		; based at PSP seg
	mov	EXE_CS[si],dx		; set up initial cs:ip
	mov	EXE_IP[si],100h		;   and ss:sp for child
	mov	EXE_SS[si],dx
	mov	es,dx
	mov	bx,PSP_LONGOFF		; ax = segment size in bytes
	add	bx,110h - 2		; Initialise stack in reserved area
	mov	EXE_SP[si],bx		; save as stack ptr
	mov	es:word ptr[bx],0	; put one zero on the stack
	jmp	start_child		; goodbye!

;
;	Function 4B Error Handler. This exit routine will free all
;	resources allocated to a process during the EXEC function and
;	exit to the standard error handler with the original error code
;	if any further errors occur they are ignored.
;
f4B_error:
	push	ax			; Save the return Code
	mov	bx,load_handle		; Is the load file still open ?
	inc	bx			; (FFFF = closed)
	 jz	f4B_e10			; YES then Close
	dec	bx
	mov	ah,MS_X_CLOSE
	call	dos_entry
f4B_e10:				; Now Free any memory allocated
	mov	cx,load_psp		; during the execution of FUNC4B
	call	conditional_mem_free	; firstly free PSP/code/data memory
	mov	cx,load_env		; Secondly free the memory allocated
	call	conditional_mem_free	; to hold the ENVIRONMENT
	pop	ax			; Restore the return code and exit
	mov	valid_flg,OK_RF		; fiddle to resume func 4B if we get
	mov	retry_sp,sp		;  a critical error
	mov	retry_off,offset func4B
	call	error_exit		; call the standard error handler
	cmp	ax,-ED_FORMAT		; errors less than ED_FORMAT are OK.
	 jb	f4B_e20			;  (eg. ED_MEMORY, ED_FILE)
	mov	ax,load_handle		; if we didn't manage to open exec file
	inc	ax			;  load_handle=FFFF and we want to
	mov	al,-ED_PATH		;  return ED_PATH
	 jz	f4B_e20			; else we had an error during the load
	mov	al,-ED_FORMAT		;  and should return ED_FORMAT
f4B_e20:
	ret

eject	
start_child:
	mov	es,current_psp		; ds -> psp
	mov	dx,0080h		; default dma offset
	mov	ah,MS_F_DMAOFF		; Set the DMA address
	call	dos_entry		; set child's dma address
	mov	si,offset exe_buffer	; Get EXE Buffer Offset
	call	point_param_block	; CL = subfunc, ES:DI -> param block
	cmp	cl,1
	 jne	start_child_go		; load restisters and go
;
;	The following code updates the Extended parameter block
;	used with the LOAD for DEBUG sub-function.
;
	add	di,DWORD*3+WORD		; skip user supplied info
	mov	ax,EXE_SP[si]
	dec ax ! dec ax			; return ss:sp-2
	stosw
	xchg	ax,bx			; save SP for later
	mov	ax,EXE_SS[si]
	stosw

	push	ds
	mov	ds,ax
	mov	word ptr [bx],0		; zero on user stack
	pop	ds

	lea	si,EXE_IP[si]		; point at IP
	lodsw ! stosw			; copy it, and get in AX for return
	movsw				; copy EXE_CS too
	jmp	return_AX_CLC		; all went OK

start_child_go:
;--------------
;
;	Set the initial registers conditions for a DOS process
;	Check the validity of the drives specified in FCB 1 and FCB 2
;	of the loading PSP and initialise the AX register accordingly.
;
	xor	dx,dx			; start with valid drives
	mov	es,current_psp		; Get the PSP Address and check
	push	dx
	mov	al,PSP_FCB1		; if the drive specifier for FCB1
	call	valid_drive		; is invalid set AL to FF
	pop	dx
	 jz	reg_s10
	mov	dl,0FFh
reg_s10:
	push	dx
	mov	al,PSP_FCB2		; if the drive specifier for FCB2
	call	valid_drive		; is invalid set AH to FF
	pop	dx
	 jz	reg_s20
	mov	dh,0FFh
reg_s20:
	mov	di,EXE_SP[si]		; Get the new stack address
	push	di			; save it
	mov	cl,4
	shr	di,cl			; convert SP to para's
	 jnz	reg_s30
	mov	di,1000h		; if 0k make it 64k
reg_s30:
	mov	ax,load_max		; find top of prog area
	sub	ax,EXE_SS[si]		; find para's left for stack
	cmp	di,ax			; SP too high ?
	pop	di			; assume OK
	 jb	reg_s40
	mov	di,ax			; no, so lower SP
	shl	di,cl			; convert to bytes
reg_s40:
	mov	cx,EXE_SS[si]		; CX:DI -> initial stack
	les	si,dword ptr EXE_IP[si]	; get initial CS:IP
	cli
	mov	ax,current_psp		; AX = PSP we are going to use
	xchg	ax,dx
	mov	indos_flag,0		; zap the indos flag
if 0
	mov	ss,cx			; switch to new USER stack
	mov	sp,di
	push	es
	push	si			; CS:IP on USER stack
	mov	ds,dx			; DS = ES = PSP we are exec'ing
	mov	es,dx
	xor	bx,bx			; BX = zero, set flags
	sti
	retf				; lets go!
else
	jmpf	exec_stub
endif

eject
;	*****************************
;	***    DOS Function 00    ***
;	***   Terminate Process	  ***
;	*****************************
;
;	This code is executed for both INT 20 and INT 21/00 and they both
;	implicitly set the current PSP to the users calling CODE segment.
;	This overwrites the correct value held in CURRENT_PSP.
;
	Public	func00
func00:
	mov	byte ptr int21AX,0	; force return code of zero
	les	di,int21regs_ptr
	mov	bx,es:reg_CS[di]	; normally users CS is current_psp
	mov	ax,current_psp		; but application call here
	cmp	ax,bx			;  with an Int 20 at funny moments
	 je	func4c			;  (I have "NOW!" in mind)
	mov	es,bx			; fiddle CS PSP parent so we return to
	mov	PSP_PARENT,ax		;  current_psp then fiddle current_psp
	mov	current_psp,bx		;  to be user CS

;	*****************************
;	***    DOS Function 4C    ***
;	***   Terminate Process	  ***
;	*****************************
;
	Public	func4C
func4c:
	mov	ax,current_psp		; the current PSP is terminating
	mov	term_psp,ax		;  so set term_psp and load_psp
	mov	load_psp,ax		;  to that value

f31_term:				; INT27 and INT21/31 Entry Point
	push	ds
	mov	ds,term_psp
	xor	ax,ax
	mov	es,ax			; Copy the Three interrupt vectors
	mov	si,offset PSP_TERM_IP	; saved on process creation from the 
	mov	di,INT22_OFFSET		; termination PSP to the interrupt
	mov	cx,6			; vector table.
	rep	movsw
	mov	ax,8200h		; call the REDIR hooks to clean up
	int	2ah			; first the server hook
	mov	ax,I2F_PTERM		; then call cleanup code
	int	2fh			; via magic INT 2F call
	pop	ds			; back to PCMODE data
	mov	al,byte ptr int21AX	; Get the User Return Code
	mov	user_retcode,al		; Save the User Ret Code and Set the 
	mov	al,TERM_NORMAL		; Now get the Termination Type
	xchg	al,exit_type		; and exchange with the default value
	mov	system_retcode,al	; EXIT_TYPE is set so Non-Zero values
					; when a Special Form of termination
					; takes place. ie INT 27h

; But thence came VTERM, and it looked upon terminating the ROOT process,
; and saw that it was good.
;
; VTERM gives access to the cmdline by doing func31 and becoming a TSR. You can
; then re-invoke it with a hot-key but the next time you invoke the cmdline
; option does a func4C in whatever context it was re-invoked in. This will
; either blow away an application, or try and terminate the ROOT process.


	mov	es,term_psp		; make the terminating PSP's
	mov	ax,es:PSP_PARENT	;  parental PSP into the
	mov	bx,current_psp
	cmp	ax,bx			; Is the user trying to terminate
	 jz	f4C_20			;  the ROOT DOS process if YES then
					;  skip freeing resources (VTERM)

	mov	cx,load_psp		; if we are TSR'ing
	 jcxz	f4C_20			;  skip the free

	push	ax			; save parental PSP
	mov	es,bx			; ES = current PSP
	xor	bx,bx			; start with handle zero
f4C_10:
	mov	ah,MS_X_CLOSE		; close this handle
	call	dos_entry		; so freeing up PSP entry
	inc	bx			; onto next handle
	cmp	bx,PSP_XFNMAX		; done them all yet?
	 jb	f4C_10
	mov	FD_FUNC,FD_EXIT		; Must Close all Open FCB's
	call	fdos_nocrit
	push	ds 			; We have already closed all the
	pop	es			; open MSNET files we know about
	mov	ax,I2F_PCLOSE		; but we will call the MSNET
	int	2fh			; extention's cleanup code anyway

	push ss ! pop ds		; reload DS with data segment

	mov	bx,current_psp		; free all memory associated
	call	free_all		;  with this PSP

	pop	ax			; recover parental PSP
f4C_20:
	mov	current_psp,ax		; make current PSP = parental PSP

;
;	Function 4C requires a different termination technique. It needs
;	to return to the parent process on the stack that was used on the
;	function 4B. The interrupt structure has been forced to contain
;	the interrupt 22 vector. Therefore all registers will contain
;	their original values unless the stack has been overlayed
;
;
	cli				; Stop anybody interfering
	mov	indos_flag,0		; Force the INDOS_FLAG to 0 for PCTOOLS
	mov	error_flag,0		; and SideKick Plus.
	mov	ax,retcode
	mov	ds,current_psp
	mov	ss,ds:PSP_USERSS	; Retrieve the entry SS and SP from
	mov	sp,ds:PSP_USERSP	;  the PSP and return with all
	mov	bp,sp			;  registers as on user entry
	mov	ss:reg_AX[bp],ax	; Set AX to the Process RETCODE
	xor	ax,ax
	mov	ds,ax
	mov	ax,ds:word ptr .INT22_OFFSET
	mov	ss:reg_IP[bp],ax   	; PSP_TERM_IP
	mov	ax,ds:word ptr .INT22_OFFSET+WORD
	mov	ss:reg_CS[bp],ax   	; PSP_TERM_CS
	mov	ss:reg_FLAGS[bp],0b202h	; force flags to 0F202h
					;  ie Interrupts enabled and
					;  NEC processor Mode Switch SET
					; changed to B202 to have clear
					; NT flag (DPMS doesn't like it)
	jmp	int21_exit		; Jump to the Exit routine	


eject
;	*****************************
;	***    DOS Function 4D    ***
;	*** Get Sub-Func Ret-Code ***
;	*****************************
;
	Public	func4D
func4D:
	xor	ax,ax			; Zero the return code for
	xchg	ax,retcode		; subsequent calls and return the
	jmp	return_AX_CLC		; saved value to the caller

eject
;****************************************
;*					*
;*	Process Control Subroutines	*
;*					*
;****************************************

;
; We need a full pathname for the application to inherit in it's environment.
; MS_X_EXPAND can't do the job - it returns a PHYSICAL path which may be
; unreachable (bug circa DRDOS 3.41).
; On Entry:
;	ES:DX	Points to the Original FileName
; On Exit:
;	None
;

get_filename:
	push	ds
	push	es			; swap ES and DS
	pop	ds
	pop	es
	mov	si,dx			; DS:SI -> filename
	mov	di,offset load_file	; ES:DI -> local buffer
	mov	cx,MAX_PATHLEN-4	; max length (allow for d:\,NUL)
	lodsw				; get 1st two chars in filename
	cmp	ah,':'			; is a drive specified ?
	 je	get_filename10
	dec si ! dec si			; forget we looked
	mov	al,ss:current_dsk	; and use the default drive
	add	al,'A'
get_filename10:
	stosb				; put in the drive
	and	al,1fh			; convert from ASCII to 1 based
	xchg	ax,dx			; keep in DL for ms_x_curdir
	mov	ax,':'+256*'\'		; make it "d:\"
	stosw
	lodsb				; do we start at the root ?
	cmp	al,'\'
	 je	get_filename20
	cmp	al,'/'
	 je	get_filename20
	dec	si			; forget we looked for a root
	push	si			; save where we were
	mov	ah,MS_X_CURDIR
	mov	si,di			; ES:SI -> buffer
	call	dos_entry		; get current directory
	xor	ax,ax
	repne	scasb			; look for NUL
	xchg	ax,si			; AX = start of path
	pop	si			; recover pointer to source
	 jne	get_filename30
	dec	di			; point at NUL
	cmp	ax,di			; are we at the root ?
	 je	get_filename20
	mov	al,'\'
	stosb				; no, append a '\'
get_filename20:
	rep	movsb			; copy the remainder of the string
get_filename30:
	xor	ax,ax
	stosb				; ensure we are terminated
	push	es
	pop	ds			; DS back to nornal
	ret


;
;	BUILD_ENV determines the size of the Source environment and 
;	allocates memory and finally copies it.
;
;	ON entry AX contains the segment address of the environment
;	to be used or zero if the parents is to be copied.
build_env:
	mov	es,ax			; Assume user has specified the
	or	ax,ax			; environment to be used. If AX is
	 jnz	b_e10			; 0000 then use the current environment
	mov	es,current_psp
	mov	cx,PSP_ENVIRON		; Current Environment Segment 
	mov	es,cx			; If the current environment segment
	mov	di,cx			; is zero then return a size of
	 jcxz	b_e35			; zero bytes

b_e10:
	xor	ax,ax			; Now determine the Environment size
	mov	cx,32*1024		; CX is maximum size
	mov	di,ax
b_e20:
	repnz	scasb			; Look for two zero bytes which
	 jcxz	b_e40			; mark the end of the environment
	cmp	al,es:byte ptr [di]	; continue search till the end is found
	 jnz	b_e20
	dec	di			; DI == Environment Size - 2

b_e30:
	mov	si,offset load_file	; Get the Load pathname length
	call	strlen			; String length returned in CX
	inc	cx			; Add in the terminator

	push	bx
	mov	bx,cx			; Get the String Length
	add	bx,di			; Add the environment size
	add	bx,15 + 4		; and convert to paragraphs
	shr bx,1 ! shr bx,1
	shr bx,1 ! shr bx,1
	mov	load_envsize,bx		; Save the Environment Size
	call	mem_alloc		; allocate the memory
	pop	bx
	 jc	b_e50
	mov	load_env,ax		; Save the Environment location
	push cx ! push di		; Save STRLEN and Offset
	push	ds			; Save DS
	push	es
	mov	es,ax			; Point ES at the NEW environment
	pop	ds			; Point DS at the Old environment
	mov	cx,di			; Get the environment size
	xor si,si ! mov di,si		; Initialize the pointers
	rep	movsb 			; and copy. Nothing moves if CX == 0
	pop	ds

	pop di ! pop cx			; Get the string pointers
	xor ax,ax ! stosw		; Add terminating zeros
	inc ax ! stosw			; Initialise the String COUNT field


	mov	si,offset load_file	; and size information and 
	rep	movsb			; copy the load filename.
b_e35:
	clc				; Return with no errors	
	ret
b_e40:
	mov	ax,ED_ENVIRON		; Invalid environment
b_e50:
	stc
	ret


;	Calculate the new program segment prefix
;	save:	bx -> Handle
calc_psp:
	push	bx
	mov	si,offset exe_buffer	; Calculate the Minimum and Maximum
					; amount of memory required to load
	call	image_size		; the program image (Returned in DX)
	add	dx,PSPLEN/16		; Do not forget the PSP
	mov	cx,dx			; Save the Load Image Size
	mov	bx,dx			; BX will be memory required
	mov	ax,ED_MEMORY
	add	dx,EXE_MINPARA[si]	; force DX to be the minimum and if
	 jc	cp_exit			;  more than 1 MByte exit with error
	add	bx,EXE_MAXPARA[si]	; add the maximum amount of memory
	 jnc	c_p10			;  to the load image size
	mov	bx,0FFFFh		;  clipping to 1 MByte
c_p10:
if HILOAD
	test	mem_strategy,80h	; HILOAD ON ?
	 jz	c_p15
	mov	bx,dx			; use minimum amount of memory
	add	bx,40h			; add 1 K extra for luck (stack etc)
	call	mem_alloc		; Allocate the requested block
	 jc	cp_exit			; if alloc fails exit with error
	push	ds
	mov	ds,ax
	mov	bx,0ffffh		; find how much we can grow this block
	call	mem_setblock
	call	mem_setblock		; then grow it to that size
	mov	ax,ds			; ax = base of the block again
	pop	ds
	jmps	c_p20			
c_p15:
endif
	call	mem_alloc		; allocate size and if error occurs
	 jnc	c_p20			; then the maximum size is greater
	cmp	bx,dx			; than the minimum required
	 jc	cp_exit			; if not exit with error

	call	mem_alloc		; Allocate what we've got
	 jc	cp_exit			; Exit on error	
c_p20:
	mov	load_psp,ax		; Save the load paragraph == PSP
	add	bx,ax			; Save the block top
	mov	load_top,bx
	mov	load_max,bx		; save top of block for SP adjust
	add	ax,PSPLEN/16		; Set AX to be the Relocation Paragraph

	cmp	exe_loadhigh,0		; Should the Load Image be
	 jz	c_p30			; forced into to High Memory with the
	mov	ax,bx			; data area and PSP loaded low.
	sub	ax,cx			; Subtract the Load Image Size from
	mov	cx,PSPLEN/16		; the top of allocated memory and
	add	ax,cx			; load at that address.	

c_p30:
	mov	load_image,ax		; Save the Address of the Load Image
cp_exit:
	pop	bx
	ret

eject
;LOADIMAGE:
;
;	This function reads in the load image of the file into memory
;	(Paragraph DI) asserting the relocation factor (SI) if any relocation
;	items exist in the file. The size of the load image is calculated 
;	using the EXE_SIZE and EXE_FINAL fields enough memory exists at DI
;	to load the image. The valid .EXE header has been moved to exe_buffer.
;
;	Read in and relocate the EXE image
;	entry:	bx -> handle
;		di = load segment
;		si = reloc segment
;	exit:	cf = 1, ax = Error Code if load fails
;
loadimage:
;---------
	call	readfile		; Read the load image into memory
	 jc	load_error		; Exit if error

	mov	cx,exe_buffer+EXE_RELCNT
					; get number of reloc entries
	 jcxz	load_done		; if none there, forget it .COM's
					; drop out here because RELCNT is zero

	push	cx			; seek to 1st relocation entry
	xor	cx,cx			;  in the file
	mov	dx,exe_buffer+EXE_RELOFF
	mov	ax,(MS_X_LSEEK*256)+0
	call	dos_entry
	pop	cx
	 jc	load_error		; stop on error
	xchg	ax,cx			; AX = # items to relocate
	call	reloc_image		; relocate the image
	 jc	load_error
load_done:
	mov	load_handle,-1
	mov	ah,MS_X_CLOSE		; and close the loadfile
	jmp	dos_entry		; close the com file

load_error:				; Error exit from relocation
	push	ax			; save error code
	call	load_done		; close the file
	pop	ax			; recover error code
	stc				; say we had an error
	ret
	
;
;	The following code will relocate CX items from the open handle BX
;
reloc_image:
; On Entry:
;	BX = handle
;	AX = # items to relocate
;	SI = relocation segment
;	DI = relocation fixup
;
; On Exit:
;	CY clear if OK, else AX = error code


	push ds ! pop es		; ES -> Local Buffer Segment
	mov	dx,offset reloc_buf	; DX -> Local Buffer Offset

	mov	cx,RELOC_CNT		; AX -> Buffer Size
	shl	cx,1			; convert reloc size from paras
	shl	cx,1			;  to an item count
	sub	ax,cx			; buffer. which contains a maximum
	 jnc	reloc_i10		; of RELOC_SIZE items.
	add	cx,ax			; CX contains # of items to Read
	xor	ax,ax			; AX contains # left to read
reloc_i10:
	push	ax			; save # items left to read
	push	cx			; and # reloc to read
	shl cx,1 ! shl cx,1		; calculate # byte to read
	mov	ah,MS_X_READ		; relocation buffer.
	call	dos_entry
	pop	cx
	 jnc	reloc_i20		; Exit on Error
	pop	cx			; clean up stack
	ret				; return with error

reloc_i20:
	push	bx			; save handle
	xchg	ax,di			; AX = reloc fixup

	mov	bx,dx			; Get buffer offset
reloc_i30:
	add	word ptr 2[bx],ax	; Correct segment to Load Seg
	les	di,dword ptr [bx]	; es:di = reloc entry
	add	es:[di],si		; add reloc seg into image
	add	bx,4			; and update for next entry
	loop	reloc_i30

	xchg	ax,di			; restore fixup to DI
	pop	bx			; recover handle
	pop	ax			; recover # left to do
	test	ax,ax
	 jnz	reloc_image		; keep going until all done
	ret
	
;READFILE
;
;	This function reads in the load image of the file into memory
;	(Paragraph DI) the size of the load image is calculated using
;	the EXE_SIZE and EXE_FINAL fields enough memory exists at DI
;	to load the image. The valid .EXE header has been moved to 
;	exe_buffer.
;
;	Read in a Binary Image .COM or .EXE
;	entry:	bx -> handle
;		di = load segment
;	
;	exit:	bx, si, di Preserved
;		cf = 1, ax = Error Code if load fails
;
MAX_READPARA	equ	3200		; Maximum Number of Paragraphs to 
					; read in one command 50Kb
readfile:
	push si ! push di
	mov	si,offset exe_buffer	; Get the .EXE header
	mov	dx,EXE_HEADER[si]	; get the header size in paragraphs
	mov	cx,4			; and seek to that offset in the
	rol	dx,cl			; file before reading any data
	mov	cl,dl
	and cx,0Fh ! and dx,not 0Fh
	mov	ax,(MS_X_LSEEK*256)+0
	call	dos_entry		; Execute LSEEK Function
	jc	rf_error
	call	image_size		; Get the Load Image Sizes in Paras
	mov	si,dx			; Returned in DX save in SI
rf_10:
	mov	es,di			; Set the Buffer address
	sub	dx,dx			; es:dx -> load segment

	cmp	si,MAX_READPARA		 ; Can we read the rest of the file
	jbe	rf_20		 	 ; in one command jif YES
	sub	si,MAX_READPARA		 ; Decrement the Image Size
	mov	cx,MAX_READPARA * 16	 ; Number of bytes to read
	add	di,MAX_READPARA		 ; Number of Paragraphs Read
	mov	ah,MS_X_READ		 ; Read the Block into the
	call	dos_entry		 ; buffer Exit if Error
	jc	rf_error
	jmps	rf_10			 ; Else go for the next bit

rf_20:					; Now reading the last part of
	mov	cl,4			; the image so convert remainder
	shl	si,cl			; in SI to bytes and Read File
	mov	cx,si
	mov	ah,MS_X_READ		; Read data into the buffer
	call	dos_entry
	jc	rf_error		; Stop on Error
	xor	ax,ax			; Reset the carry Flag and Zero AX
rf_error:				; Error exit Carry Flag Set and AX
	pop di ! pop si			; contains the error code.
	ret

;	Copy old PSP contents to new PSP.
;	Parameter block supplied by user contains command line
;	and default FCB's - copy these into the load_psp.
;	save:	bx -> Handle
pblk_to_psp:
	push	ds			; Save the PcMode Data Segment
	push	bx			; and file handle
	mov	dx,load_psp
	call	point_param_block	; ES:DI -> users parameter block

	push es ! push di
	lds	si,es:dword ptr 2[di]	; Get the Source Pointer
	mov	cx,128			; Copy the complete command line
	mov	di,offset PSP_COMLEN	; because BASCOM places a segment value
	mov	es,dx			; after the CR which was not previously
	rep	movsb			; copied.
	pop di ! pop es

	lds	si,es:dword ptr 6[di]	; get 1st FCB address
	mov	ax,offset PSP_FCB1	; First FCB Offset
	call	copy_fcb		; copy FCB
	lds	si,es:dword ptr 10[di]	; Get the Source Pointer
	mov	ax,offset PSP_FCB2	; Second FCB Offset
	call	copy_fcb		; copy FCB
	pop	bx			; file handle back again
	pop	ds			; Restore PcMode Data Segment
	ret

copy_fcb:
;--------
; On Entry:
;	DS:SI -> source
;	DX:AX -> destination
; On Exit:
;	None
;	ES:DI, DX preserved
;
	push	es
	push	di
	mov	es,dx
	xchg	ax,di			; ES:DI -> destination
	mov	cx,12			; Copy Drive, Name and Extension
	rep	movsb			;  and copy it
	xchg	ax,cx			; AX = 0
	stosw ! stosw			; zero last 4 bytes
	pop	di
	pop	es
	ret



;	Set up a new psp for the child
;
set_up_psp:
	mov	ax,load_psp		; Change the ownership of the
	mov	bx,load_env		; Environment and Load Memory
	call	set_owner		; partitions.
	mov	ax,load_psp
	mov	bx,ax
	call	set_owner
	
	cmp	current_psp,1		; Is This the ROOT DOS process
	jnz	setup_psp10		; No! Continue as Normal

	mov	ax,load_psp		; Force the LOAD_PSP to
	mov	current_psp,ax		; to be the current PSP

	mov	es,ax			; Now Zero Fill the New PSP
	mov	cx,(offset PSP_FCB1)/2	;  up to user supplied parameters
	xor ax,ax ! mov di,ax
	rep	stosw
	jmps	setup_psp20		; and skip the INT22 Fudge

setup_psp10:				; Get the Function return address
	xor di,di ! mov es,di		;  and force into INT 22
	mov	di,INT22_OFFSET		; Set Interrupt Vectors 22
	push	ds
	lds	si,int21regs_ptr
	lea	si,reg_IP[si]		; DS:SI -> callers IP
	movsw				; Save User IP
	movsw				; Save User CS
	pop	ds
setup_psp20:
	mov	dx,load_psp		; Get the new PSP address
	mov	si,load_top		; Get the last paragraph allocated
	mov	cx,(offset PSP_FCB1)/2	; Copy PSP up to user supplied bits
;
;	CREATE_PSP is a local function called by the DOS EXEC function (4B)
;	to create a new PSP and initialize it as a new process. 
;
;	The PSP_MEMORY field was original calculated as the highest memory
;	location that could be allocated to a process. However this caused
;	Carbon Copy Plus to Fail so the routine now uses the LOAD_TOP
;	value calculated by the CALC_PSP function. This is the last
;	paragraph allocated to the current PSP.
;
	call	create_psp		; Create the New Process
	mov	ax,load_env		; Now Update the Environment
	mov	PSP_ENVIRON,ax
	mov	si,offset load_file	; Copy the process name into the DMD
;	jmp	SetPspNameAndVersion

SetPspNameAndVersion:
;---------------------
; On Entry:
;	ES = PSP
;	DS:SI -> pathaname (nb. DS need not be dos data seg!)
; On Exit:
;	None
;
	mov	bx,es
	dec	bx
	mov	es,bx			; ES points at DMD (We Hope)
	call	check_dmd_id		; Check for a valid DMD
	 jc	SetPspNameAndVersion10	; bail out now if none
if DOS5
	inc	bx			; BX -> PSP again
	push	bx			; keep it on the stack
endif
	call	FindName		; DS:SI -> start of name
	push	si
	call	SetName			; update the name field
	pop	si
if DOS5
	call	GetVersion		; AX = version to return
	pop	es			; ES = PSP
	mov	PSP_VERSION,ax		; set version number
endif
SetPspNameAndVersion10:
	ret

FindName:
;--------
; On Entry:
;	DS:SI -> pathname of file
; On Exit:
;	DS:SI -> final leaf name of file
;	CX = length of leaf name
;
	mov	cx,si			; remember start of leaf
FindName10:
	lodsb
	cmp	al,' '			; end of the name ?
	 jbe	FindName30
	call	dbcs_lead		; is it a double byte pair ?
	 jne	FindName20
	lodsb				; include the second byte
	jmps	FindName10
FindName20:
	cmp	al,'\'			; is it a seperator ?
	 je	FindName
	cmp	al,'/'
	 je	FindName
	jmps	FindName10
FindName30:
	xchg	cx,si			; SI -> start of leaf name
	sub	cx,si
	dec	cx			; CX = length
	ret


SetName:
;-------
; On Entry:
;	DS:SI -> leaf name to update
;	ES = DMD to update
; On Exit:
;	CX preserved
;
	mov	di,offset DMD_NAME	; point at the owners name field
SetName10:
	lodsb
	cmp	al,' '			; end of the name ?
	 jbe	SetName30
	call	dbcs_lead		; is it a double byte pair ?
	 jne	SetName20
	stosb				; copy 1st byte of pair
	cmp	di,(offset DMD_NAME)+DMD_NAME_LEN
	 jae	SetName30		; don't overflow if name too long
	movsb				; and the second
	jmps	SetName10

SetName20:
	stosb
	cmp	al,'.'			; discard all following '.'
	 je	SetName30
	cmp	di,(offset DMD_NAME)+DMD_NAME_LEN
	 jb	SetName10		; don't overflow if name too long
	ret

SetName30:
	dec	di
	xor	ax,ax
SetName40:
	stosb				; zero the '.'
	cmp	di,(offset DMD_NAME)+DMD_NAME_LEN
	 jb	SetName40		; zero the rest of the name
	ret

if DOS5
GetVersion:
;----------
; On Entry:
;	DS:SI -> start of name
;	CX = length
; On Exit:
;	AX = dos version to return
;
	les	di,ss:setverPtr
	mov	ax,es
	or	ax,di			; check for a setver list
	 jnz	GetVersion30
GetVersion10:
	mov	ax,ss:dos_version	; better use default version
	ret

GetVersion20:
	mov	al,es:0FFFFh[di]	; skip the name
	cbw
	inc ax ! inc ax			; skip the version
	add	di,ax			; try the next entry
GetVersion30:
	mov	al,es:byte ptr [di]	; get length field
	test	al,al			; end of the list ?
	 jz	GetVersion10

	inc	di			; point at potential name
	cmp	al,cl			; do the lengths match ?
	 jne	GetVersion20
	xor	bx,bx			; start scan with 1st character
GetVersion40:
	mov	ax,ds:[bx+si]		; get a character from filename
	call	dbcs_lead		; is it a DBCS character ?
	 jne	GetVersion50
	inc	bx			; we will skip 2 characters
	cmp	ax,es:[bx+di]		; do both character match ?
	jmps	GetVersion60

GetVersion50:
	call	toupper			; upper case it
	mov	ah,al			; save it
	mov	al,es:[bx+di]		; get a character from setver list
	call	toupper			; upper case it
	cmp	al,ah			; do we match ?
GetVersion60:
	 jne	GetVersion20		; no, try next name in list
	inc	bx			; we match, have we done them all ?
	cmp	bx,cx			; check against length
	 jb	GetVersion40
	mov	ax,es:[bx+di]		; get version number from setver list
	ret
endif

eject
;
;	GET_DATA reads the EXE header using the handle passed in BX
;

get_execdata:
; On Entry:
;	BX = handle
;	ES:SI = buffer
; On Exit:
;	CY set if error, AX = error code
;	BX/SI preserved

	mov	ah,MS_X_READ
	mov	cx,EXE_LENGTH		; read the exe header
	mov	dx,si			; ES:DX -> buffer
	call	dos_entry		; try and read the data
	 jc	gd_exit			; Error Exit
	mov	EXE_FINAL[si],0200h	; Force value to Full Page
	call	check_exe		; all done if it's an .EXE
	 jnc	gd_exit
	mov	ax,(MS_X_LSEEK*256)+2	; it's a .COM
	xor	cx,cx			; seek to end of file
	xor	dx,dx
	call	dos_entry		; get file length in DX:AX
	 jc	gd_exit
	xchg al,ah ! mov ah,dl		; DX:AX / 512
	shr dx,1 ! rcr ax,1
	inc	ax			; Handle Final Partial Page
	mov	EXE_SIZE[si],ax		; No. of 512 Byte Pages
	xor	ax,ax
	mov	EXE_HEADER[si],ax	; Load Image starts a 0000
	mov	EXE_RELCNT[si],ax	; No Relocation Items
	dec	ax			; Force Maximum Memory Allocation
	mov	EXE_MAXPARA[si],ax	;  to the .COM
	mov	EXE_MINPARA[si],0010h	; give it at least an extra 100h
					; bytes for the Default Stack
gd_exit:
	ret

eject
;
;	Determine if the file to be loaded is a DOS EXE format file
;	if YES then return with the carry flag reset. Assume that the
;	header has already been read into EXE_HEADER
;
	public	check_exe
check_exe:
	cmp	EXE_SIGNATURE[si],'ZM'	; look for exe signature
	 jz	check_e10
	cmp	EXE_SIGNATURE[si],'MZ'	; look for exe signature
	 jz	check_e10
	stc 				; flag the error
check_e10:
	ret

;
;	IMAGE_SIZE assumes SI points to a valid EXE header and from this
;	it calculates the size of the load image and returns this value
;	in paragraphs in DX. AX and CX are corrupted.
;
	Public	image_size
image_size:
	mov	dx,EXE_SIZE[si]		; No of 512 pages in System Image
	dec	dx			; Adjust for Final Partial Page
	mov cl,5 ! shl dx,cl		; No. 512 Byte Blocks to Para
	sub	dx,EXE_HEADER[si]	; Remove the Header Size	

	mov 	ax,EXE_FINAL[si]
	add	ax,15
	dec cl ! shr ax,cl		; AX is Partial Block in PARA
	add	dx,ax			; DX is Image Size in PARA's
	ret

mem_alloc:
	mov	ah,MS_M_ALLOC			; call DOS to allocate
	jmp	dos_entry			; some memory

mem_setblock:
	mov	ah,MS_M_SETBLOCK		; call DOS to ajust
	jmp	dos_entry			; a memory block

conditional_mem_free:
; On Entry:
;	CX = para to free
;	    (0 = none to free)
; On Exit:
;	None
;
	 jcxz	cmem_free10			; only free up allocated
	push	ds				;  memory
	mov	ds,cx
	mov	ah,MS_M_FREE			; free up a memory block
	call	dos_entry
	pop	ds 
cmem_free10:
	ret

point_param_block:
;-----------------
; On Entry:
;	None
; On Exit:
;	CL = subfunction number (callers AL)
;	ES:DI -> parameter block (callers ES:BX)
;	AX corrupted
;
	les	di,int21regs_ptr	; point at callers registers
	mov	cl,es:reg_AL[di]	; CL = subfunction# (range-checked)
	mov	ax,es:reg_BX[di]
	mov	es,es:reg_ES[di]	; callers ES:BX -> parameter block
	xchg	ax,di			; ES:DI -> parameter block
	ret

	
		
PCMODE_DATA	DSEG	WORD
	extrn	current_dsk:byte
	extrn	current_psp:word
	extrn	retcode:word		; Complete return code passed to F4B
	extrn	user_retcode:byte	; User retcode set by funcs 4C and 31
	extrn	system_retcode:byte	; System retcode returns the cause of
	extrn	switch_char:byte
	extrn	mem_strategy:byte	; memory allocation strategy
	extrn	int21AX:word		; Int 21's AX
	extrn	indos_flag:byte
	extrn	int21regs_ptr:dword
	extrn	int21regs_off:word
	extrn	int21regs_seg:word
	extrn	prev_int21regs_off:word
	extrn	prev_int21regs_seg:word
	extrn	error_flag:byte
	extrn	exe_buffer:word
	extrn	valid_flg:byte
	extrn	retry_off:word
	extrn	retry_sp:word
	extrn	last_drv:byte
	extrn	exec_stub:dword
	extrn	func4B05_stub:dword

if DOS5
	extrn	dos_version:word
	extrn	setverPtr:dword
endif

eject
;	To improve Network performance the EXE relocation items are
;	now read into the following buffer. All the data items contained
;	between RELOC_BUF and RELOC_SIZE are destroyed by the LOADIMAGE
;	sub-routine when it relocates a DOS .EXE file.
;
;	Only variables which are unused after the LOADIMAGE function can
;	be placed in this buffer.
;
;	******** Start of .EXE Relocation Buffer ******** 
;

; We can re-use the MSNET pathname buffers during an EXEC

	extrn	reloc_buf:byte
	extrn	load_file:byte
	extrn	RELOC_CNT:abs

;
;	******** End of .EXE Relocation Buffer ******** 
;

	extrn	exit_type:byte
	extrn	term_psp:word
	extrn	load_handle:word
	extrn	load_env:word			; Paragraph of the new environment
	extrn	load_envsize:word		; Size of new environment
	extrn	load_psp:word			; Paragraph of the new PSP.
	extrn	load_image:word			; Paragraph of the Load Image.
	extrn	load_top:word			; Last paragraph of Allocated Memory
	extrn	load_max:word			; ditto, but not messed with
	extrn	exe_loadhigh:byte		; load high flag

	end
