TITLE	LDUTIL - Loader support procedures

.xlist
include kernel.inc
include newexe.inc
include tdb.inc
include protect.inc
.list

;externFP GetCurrentPDB

if ROM and PMODE32
externFP HocusROMBase
endif

DataBegin

;externB Kernel_Flags
;externW AltfEEMS
externW headTDB
externW hExeHead
;externW pGlobalHeap
;externW MyCSDS
externW topPDB
externW cur_dos_pdb
externW Win_PDB
externW curTDB
;externW segSwapArea

DataEnd

externFP CallWEP

sBegin	CODE
assumes CS,CODE
assumes DS,NOTHING
assumes ES,NOTHING

externNP MyUpper
externNP MyLock
externNP LoadNRTable
externNP LoadSegment
externNP GetOwner
externNP GetCachedFileHandle

if SDEBUG
externNP DebugFreeSegment
endif

externFP IGlobalAlloc
externFP IGlobalFree
;externFP IGlobalLock
externFP IGlobalUnfix
externFP IGlobalHandle
externFP GlobalFlags
externFP FreeNRTable
externFP Int21Handler

ifdef	FE_SB			;declare it.
externNP MyIsDBCSLeadByte	;Use near one as we are in the same seg
endif

if ROM
externFP FreeSelector
externNP alloc_data_sel16
endif

cProc	SafeCall,<PUBLIC,FAR>
	parmD	Addr
cBegin
	call	Addr
cEnd

;-----------------------------------------------------------------------;
; GetExePtr								;
; 									;
; Returns the exe header pointer for the passed handle.			;
; 									;
; Arguments:								;
;	parmW   hinstance						;
; 									;
; Returns:								;
; 									;
; Error Returns:							;
;	AX = 0								;
;									;
; Registers Preserved:							;
; 									;
; Registers Destroyed:							;
; 									;
; Calls:								;
; 									;
; History:								;
; 									;
;  Mon Aug 03, 1987 04:40:45p  -by-  David N. Weise   [davidw]          ;
; Rewrote it to work with expanded memory.  Note that if the handle	;
; passed is truly bogus we could still catch a page fault under		;
; Win386.								;
;-----------------------------------------------------------------------;

	assumes	ds, nothing
	assumes	es, nothing

cProc	GetExePtr,<PUBLIC,FAR>
	parmW   hinstance
cBegin
	mov	ax,hinstance
	test	ax,GA_FIXED		; Passed a segment address?
	jz	not_module
	mov	es,ax
	cmp	es:[ne_magic],NEMAGIC	;  Yes, does it point to a Module?
	jz	gxdone

not_module:
	SetKernelDS	es
	mov	cx,headTDB
	assumes	es, nothing
find_TDB:
	jcxz	last_chance
	mov	es,cx
	cmp	ax,es:[TDB_module]
	jz	found_it
	mov	cx,es:[TDB_next]
	jmp	find_TDB
found_it:
	mov	ax,es:[TDB_pModule]
	jmps	gxdone

last_chance:				; The problem here is that USER
	cCall	MyLock,<ax>		;  KNOWS that hinstance is but
	or	ax,ax			;  the handle to his data segment.
	jz	gxdone
	mov	es,ax
	cmp	es:[ne_magic],NEMAGIC
	je	gxdone

	cCall	GetOwner,<ax>
	or	ax,ax
	jz	gxfail
	mov	es,ax
	cmp	es:[ne_magic],NEMAGIC
	je	gxdone

	; The owner,ax, is now a PDB. We gotta find the TDB
	SetKernelDS	es
	mov	cx,headTDB
	assumes	es, nothing
find_PDB:
	jcxz	gxfail
	mov	es,cx
	cmp	ax,es:[TDB_PDB]
	jz	found_PDB
	mov	cx,es:[TDB_next]
	jmp	find_PDB
found_PDB:
	mov	ax,es:[TDB_pModule]
	jmps	gxdone


gxfail:
if KDEBUG
	xor	cx,cx
	kerror	ERR_LDMODULE,<Invalid module handle>,cx,hinstance
endif
	xor	ax,ax
gxdone:	mov	cx,ax

cEnd

;-----------------------------------------------------------------------;
;									;
;  GetExpWinVer - return the expected Windows version for a given	;
;		  module handle						;
;									;
; Arguments:								;
;	parmW   hinstance						;
; 									;
; Returns:								;
;	AX = expected windows version					;
;	DX = BOOL proportional font flag				;
;									;
; Error Returns:							;
;	AX = 0								;
;									;
; Registers Preserved:							;
; 									;
; Registers Destroyed:							;
; 									;
; Calls:								;
; 									;
; History:								;
; 									;
; Fri 06 Jan 1989  -- Written by Sankar.				;
;									;
;-----------------------------------------------------------------------;

	assumes	ds, nothing
	assumes	es, nothing

cProc	GetExpWinVer,<PUBLIC,FAR>, <si,di>
	parmW   hInstance
cBegin	
	cCall	GetExePtr,<hInstance>
	or	ax,ax
	jz	err_GetExpWinVer
	mov	es,ax
	mov	ax,es:[ne_expver]
	mov	dx,es:[ne_flags]
	and	dx,NEWINPROT
; error if offsets don't match our defines - In which case find the right
; offsets and changes the defines in mvdm\inc\tdb16.h

.erre (NE_LOWINVER_OFFSET EQ ne_expver)
.erre (NE_HIWINVER_OFFSET EQ ne_flags)

err_GetExpWinVer:

cEnd	


;-----------------------------------------------------------------------;
; MyAlloc								;
;									;
; Private interface to memory manager                                   ;
; 									;
; Arguments:								;
;	parmW   aflags							;
;	parmW   nsize							;
;	parmW   nelem							;
; 									;
; Returns:								;
; 									;
; Error Returns:							;
;	AX = Handle							;
;	DX = Seg Address						;
; 									;
; Registers Preserved:							;
;	DI,SI,DS							;
; 									;
; Registers Destroyed:							;
;	BX,CX,ES							;
; 									;
; Calls:								;
;	GlobalAlloc							;
;	MyLock								;
; 									;
; History:								;
; 									;
;  Wed Apr 08, 1987 06:22:57a  -by-  David N. Weise   [davidw]          ;
; Wrote it.								;
;-----------------------------------------------------------------------;
	assumes	ds, nothing
	assumes	es, nothing


cProc	MyAllocLinear,<PUBLIC,NEAR>
	; Same as MyAlloc, except for size parameter
	parmW   aflags
	parmD   dwBytes
cBegin
	jmps	MyAllocNBD
cEnd


	assumes	ds, nothing
	assumes	es, nothing

cProc	MyAlloc,<PUBLIC,NEAR>
	parmW   aflags
	parmD	dwBytes
;	parmW   nsize
;	parmW   nelem
cBegin
	xor	dx,dx
	mov	ax,seg_dwBytes	;nsize
	mov	cx,off_dwBytes	;nelem
	jcxz	ma3
ma2:
	shl	ax,1
	rcl	dx,1
	loop	ma2
ma3:
	mov	seg_dwBytes, dx
	mov	off_dwBytes, ax
MyAllocNBD:
	SetKernelDS	es
	mov	cx,aflags
	mov	al,NSTYPE
	and	al,cl			; al has SEGTYPE
	mov	bx,NSDISCARD
	and	bx,cx
	jz	@F
	shr	bx,1
	shr	bx,1
	shr	bx,1
	shr	bx,1			; BX has GA_DISCARDABLE
	cmp	al,NSCODE
	jne	@F
	or	bl,GA_DISCCODE		; Allocating discardable code
@@:

	cmp	al,NSDATA
	jne	@F
	and	cx,NOT NSWINCODE	; undo Excel bogusness
	or	bl,GA_DGROUP		; Allocating automatic data segment
@@:

	test	cl,NSMOVE
	jz	@F
	or	bl,GA_MOVEABLE
@@:

	cmp	al,NSTYPE
	jz	@F
	or	bh,GA_CODE_DATA
@@:

	cCall	IGlobalAlloc,<bx,dwBytes>	 ;dxax>

	assumes	es, nothing
	push	ax
	test	al,GA_FIXED
	jnz	@F
	cCall	MyLock,<ax>
@@:
	pop	dx
cEnd


;
; MyLock( hseg ) - Procedure to return the physical segment address of
; the passed handle.
;
;;;cProc	MyLock,<PUBLIC,NEAR>
;;;	parmW   hseg
;;;cBegin
;;;	cCall	IGlobalHandle,<hseg>
;;;	xchg	dx,ax
;;;cEnd


;-----------------------------------------------------------------------;
; MyFree								;
; 									;
; Frees a segment allocated by MyAlloc.					;
; 									;
; Arguments:								;
; 									;
; Returns:								;
; 									;
; Error Returns:							;
; 									;
; Registers Preserved:							;
; 									;
; Registers Destroyed:							;
; 									;
; Calls:								;
; 									;
; History:								;
; 									;
;  Tue Dec 09, 1986 12:48:43p  -by-  David N. Weise   [davidw]          ;
; Added this nifty comment block.					;
;-----------------------------------------------------------------------;

	assumes	ds, nothing
	assumes	es, nothing

cProc	MyFree,<PUBLIC,NEAR>
	parmW   hseg
	localW  pseg
cBegin
	mov	bx,hseg
	or	bx,bx
	jz	mf3
	mov	ax,bx
	xor	cx,cx
	test	bl,GA_FIXED
	jnz	mf0a

	push	bx
	cCall	GlobalFlags,<bx>
	mov	cx, ax
	xor	ax, ax
	pop	bx
	xchg	ch, cl
	test	cl, HE_DISCARDED
	jnz	mf0a
	mov	ax, bx
	HtoS	ax

mf0a:
if KDEBUG
	push	cx
endif
	mov	pseg,ax
	or	ax,ax
	jz	mf1
mf0:
if SDEBUG
	cCall	DebugFreeSegment,<pseg,0>
endif
mf1:
if KDEBUG
	pop	cx
	or	ch,ch
	jz	mf2
mf1a:
	push	cx
	cCall	IGlobalUnfix,<hseg>	 ; Prevent RIP if freeing locked DS
	pop	cx
	dec	ch
	or	ch,ch			; If still lock, do it again
	jnz	mf1a
endif
mf2:
	cCall	IGlobalFree,<hseg>
mf3:
cEnd

;-----------------------------------------------------------------------;
; EntProcAddress
;
; Returns the fixed address of a procedure.
;
; Entry:
;
; Returns:
;	DX:AX = thunk for moveable
;	DX:AX = procedure for fixed
;	AX = constant, ES:BX => constant for absolutes
; Registers Destroyed:
;
; History:
;  Wed 30-Nov-1988 19:39:05  -by-  David N. Weise  [davidw]
;
;-----------------------------------------------------------------------;

	assumes	ds, nothing
	assumes	es, nothing

cProc	EntProcAddress,<PUBLIC,NEAR>,<si,di>
	parmW	hExe
	parmW	entno
if KDEBUG
	parmW	bNoRip		; if 1 don't RIP for error, 0 -> do the RIP
endif

cBegin
	mov	es,hExe
	mov	cx,entno
	and	cx,7fffh		; clear ResidentName bit (#5108 donc)
	jcxz	entfail
	dec	cx
	mov	si,es:[ne_enttab]

entloop:
	mov	ax, es:[si.PM_entstart]
	cmp	cx, ax			; Below this block?
	jb	entfail			;   yes, must be invalid!
	cmp	cx, es:[si.PM_entend]	; Above this block?
	jae	entnext			;  yes, go to next block
	sub	cx, ax			; Found the right block, get offset
	mov	bx, cx
	shl	cx, 1
	add	bx, cx
	add	bx, cx			; bx = cx*5
	lea	si, [si+bx+size PM_entstruc]
	mov	bx, si
	jmps	gotent
entnext:
	mov	si, es:[si.PM_entnext]	; Next block
	or	si, si
	jnz	entloop
	jmps	entfail

entfail:
if KDEBUG
	cmp	bNoRip,0
	jnz	dont_rip_here
	mov	bx,entno
	krDebugOut <DEB_WARN or DEB_krLoadSeg>, "Invalid ordinal reference (##BX) to %ES1"
;	kerror	ERR_LDORD,<Invalid ordinal reference to >,es,bx
dont_rip_here:
endif
	xor	dx,dx
	xor	ax,ax
	jmps	entdone

; Here if the current block has the entry we want

gotabs:
	add	bx,pentoffset		; make ES:BX point to constant (SLIME!)
	mov	dx,-1			; make != 0 since abs might be!
	jmps	gotent1			; Offset is symbol value

gotent:
	xor	ax, ax
	mov	al, es:[bx].penttype 	; Get segno/type field
	mov	si,es:[bx].pentoffset
	cmp	al,ENT_ABSSEG		; If segno field absoulute segment
	je	gotabs			; Yes, have absolute symbol

	cmp	al, ENT_MOVEABLE
	je	gotmoveable

	.errnz	10 - SIZE NEW_SEG1
	; ax = segno
	push	bx
	mov	bx, es:[ne_segtab]	; see if really fixed
	mov	cx, ax			; look up in seg table
	dec	cx
	shl	cx, 1			; get segtable + (segno-1)*10
	add	bx, cx
	shl	cx, 2
	add	bx, cx
	test	es:[bx].ns_flags, NSMOVE + NSALLOCED
	pop	bx
	jnz	gotmoveable

	mov	cx,-1			; Fixed, make sure it's loaded
	cCall	LoadSegment,<es,ax,cx,cx>
	or	ax,ax
	mov	dx,ax
	jnz	gotent1
	jmp	entfail

gotmoveable:
	xor	ax,ax
	mov	al,es:[bx].pentsegno	; Segment number
	dec	ax
	shl	ax,1
	mov	di,ax
	shl	ax,2
	add	di,ax
	.errnz	10 - SIZE NEW_SEG1
	add	di,es:[ne_segtab]

	mov	dx,es:[di].ns_handle
	or	dx, dx
	jnz	ok
	mov	ax, dx			; No handle, probably from an aborted
	jmps	entdone			; LoadModule - just return 0:0
ok:

	Handle_To_Sel	dl

gotent1:
	mov	ax,si

entdone:
	mov	cx,ax
	or	cx,dx
cEnd


;-----------------------------------------------------------------------;
; FindOrdinal								;
; 									;
; Searches the resident and non-resident name tables for a procedure	;
; name and return its corresponding entry ordinal.			;
; 									;
; Arguments:								;
;	parmW   hExe							;
;	parmD	lpname	pointer name, strings starts with length	;
;	parmW	fh	file handle NOT to be discarded from cache	;
; 									;
; Returns:								;
;	AX = ordinal number						;
;									;
; Error Returns:							;
; 									;
; Registers Preserved:							;
; 									;
; Registers Destroyed:							;
; 									;
; Calls:								;
;	MyUpper								;
;	LoadNRTable							;
;	FreeNRTable							;
; 									;
; History:								;
; 									;
;  Tue 09-May-1989 18:38:04  -by-  David N. Weise  [davidw]		;
; Added the batching if out of memory.					;
;									;
;  Thu Sep 17, 1987 08:55:05p  -by-  David N. Weise   [davidw]          ;
; Added this nifty comment block and fixed it.				;
;-----------------------------------------------------------------------;

	assumes	ds, nothing
	assumes	es, nothing

CLNRBUFFER	equ	150

cProc	FindOrdinal,<PUBLIC,NEAR>,<si,di>
	parmW   hExe
	parmD	lpname
	parmW	fh

if ROM
	localW	fInROM
endif
	localD	oNRTable		; if batching, continue here
	localV	LoadNRbuffer,CLNRBUFFER ; if batching this is temp buffer
cBegin
	xor	ax,ax
	mov	oNRTable.hi,ax
	mov	oNRTable.lo,ax
if ROM
	mov	fInROM,ax		; assume module not in ROM
endif
	les	si,lpname
	cmp	byte ptr es:[si+1],'#'
	je	foint

fonorm:	push	ds
	mov	ds,hExe
if ROM
	test	ds:[ne_flags],NEMODINROM	; is module actually in ROM?
	jz	@f
	inc	fInROM
@@:
endif
	mov	si,ds:[ne_restab]
	cld

foinit:	xor	ax,ax			; Skip module name or description
	lodsb
	add	si,ax
	inc	si
	inc	si

foloop: lodsb				; get length of entry
	les	di,lpname
	mov	cx,ax
	jcxz	fodone			; no more entries?
	cmp	es:[di],al		; do lengths match?
	jne	noteq
	inc	di
fo_find:
	mov	al,es:[di]
	call	MyUpper
	mov	bl,ds:[si]
	inc	si
	inc	di
	cmp	al,bl
	jne	noteq1
	loop	fo_find
;	repe	cmpsb
;	jne	noteq
	lodsw				; get ordinal number!
	mov	bx,ds
	pop	ds
	cmp	hExe,bx 		; did we load the nrtable?
	jnz	foexit_j
	jmp	foexit1
foexit_j:
	jmp	foexit

noteq1:	dec	cx
noteq:	add	si,cx
	inc	si
	inc	si
	jmps	foloop

; Here if pName points to string of the form:  #nnnn

foint:	lods	byte ptr es:[si]	; nbytes = *pName++
	mov	cl,al
	xor	ch,ch

	dec	cx			; ignore #
	inc	si

	xor	ax,ax			; sum = 0
foint0:	mov	dx,ax
	lods	byte ptr es:[si]	; c = *pName++
	sub	al,'0'			; if (!isdigit(c))
	cmp	al,9
	ja	fonorm			;	treat like normal
	xor	ah,ah
	mov	bx,ax			; sum = (sum * 10) + (c - '0')
	mov	al,10
	mul	dx
	add	ax,bx
	loop	foint0
	jmp	foexit1

fodone:	mov	bx,ds
	pop	ds
;%out help me				; what was this line for?
	mov	cx,oNRTable.hi
	cmp	cx,oNRTable.lo
	jnz	fo_batching
	cmp	hExe,bx 		; have we looked at NRTable yet?
if ROM
	jne	foexit_j
else
	jne	foexit
endif

if ROM
	cmp	fInROM,0		; this module in ROM?
	jz	fo_nr_not_in_rom
	cCall	FindROMNRTable,<hExe>	; yes, get selector to ROM NR Table
	mov	fInROM,ax
	push	ds
	mov	ds,ax			; ds -> ROM NR table
	xor	bx,bx
	mov	bl,ds:[0]		; skip over first NR table entry
	lea	si,[bx+3]		;   (the module description string)
	xor	ax,ax
	jmp	foloop
fo_nr_not_in_ROM:
endif

fo_batching:
	xor	bx,bx
	mov	ax,fh
;;;	cmp	ax,-1
;;;	jz	no_file_handle

	SetKernelDS	ES
;;;	mov	dx, topPDB
;;;	mov	es, curTDB
;;;	UnSetKernelDS	es
;;;	mov	ax, es:[TDB_PDB]
;;;
;;;	cmp	dx, cur_dos_pdb
;;;	jz	@F
;;;	mov	bx,ax
;;;@@:
	mov	bx, Win_PDB
	UnSetKernelDS

	mov	dx, -1
	cCall	GetCachedFileHandle,<hexe,ax,dx>
no_file_handle:
	push	bx
	lea	cx,LoadNRbuffer
	mov	dx,CLNRBUFFER
	cCall	LoadNRTable,<hexe,ax,oNRTable,ss,cx,dx>
if KDEBUG
	push	es
	push	di
	les	di, [lpName]
	krDebugOut	<DEB_TRACE or DEB_krLoadSeg>, "    looking for @ES:DI"
	pop	di
	pop	es
endif
	pop	si
	or	si,si
	jz	@F
;;;	push	ax
;;;	push	bx
;;;	mov	bx,si
;;;	mov	ah,50h
;;;	DOSCALL
;;;	pop	bx
;;;	pop	ax
	push	es
	SetKernelDS	ES
	mov	Win_PDB, si
	pop	es
	UnSetKernelDS	ES
@@:	mov	oNRTable.hi,cx
	mov	oNRTable.lo,bx
	push	ds
	mov	ds,dx
	mov	si,ax
	or	ax,dx			; did we get a table?
	jz	@F
	xor	ax,ax
	jmp	foloop
@@:	pop	ds
foexit: push	ax
if ROM
	cmp	fInROM,0		; this module in ROM
	jz	@f
	cCall	FreeSelector,<fInROM>	; yes, free ROM NR table selector
	jmps	foexit0
@@:
endif
	mov	ax,ne_nrestab
	cCall	FreeNRTable,<hExe,ax>
foexit0:
	pop	ax
foexit1:

;??? commented out 3/7/88 by rong
;??? we should put this back in once everyone has the new keyboard driver
;if KDEBUG
;	or	ax,ax
;	jnz	foexit2
;	les	bx,lpname
;	inc	bx
;	kerror	ERR_LDNAME,<Invalid procedure name >,es,bx
;	xor	ax,ax
;foexit2:
;endif
cEnd


if ROM	;----------------------------------------------------------------

;-----------------------------------------------------------------------
; FindROMNRTable
;
; Returns a selector to the ROM copy of the Non-Resident name table for
; a module stored in ROM.
;
; Arguments:
;	parmW	hExe
;
; Returns:
;	AX = selector mapping NR table
;
;-----------------------------------------------------------------------

	assumes	ds, nothing
	assumes	es, nothing

cProc	FindROMNRTable,<PUBLIC,NEAR>,<ds>
	parmW   hExe
cBegin
	mov	ds,hExe
	mov	ax,word ptr ds:[ne_nrestab]
	mov	bx,word ptr ds:[ne_nrestab+2]
	cCall	alloc_data_sel16,<bx,ax,1000h>
if PMODE32
	cCall	HocusROMBase, <ax>
endif
if KDEBUG
	or	ax,ax
	jnz	@f
	int	3
@@:
endif
cEnd

endif ;ROM	---------------------------------------------------------

; Search the list of new EXE headers for the passed executable file name
; (no path)
;
; this must be resident so we can search for modules in the file handle
; cache from the int 21 handler -- craigc
;

cProc	ResidentFindExeFile,<PUBLIC,FAR>,<ds,si,di>
    parmD   pname
cBegin
	SetKernelDS

	mov	ax,hExeHead
ffloop:
	or	ax,ax
	jz	ffdone
	mov	es,ax
	mov	di,es:[ne_pfileinfo]
	or	di,di
	jz	ffnext

;
;	Double Byte Character is not able to search from the end of string,
;	so this function must search from the top of string.
;
ifdef FE_SB
	lea	si,[di.opFile]		; get address of file name
	mov	cl,es:[di.opLen]	; get structure length
	xor	ch,ch
	add	cx,di
	mov	di,cx			; save end address
	sub	cx,si			; get length of file name
	mov	bx,si			; save top address of file name
	cld
delineator_loop:
	lods	byte ptr es:[si]
	call	MyIsDBCSLeadByte
	jc	delineator_notlead	; if not DBCS lead byte
	dec	cx
	jcxz	delineator_next
	inc	si
	loop	delineator_loop
	jmp	delineator_next
delineator_notlead:
	cmp	al,"\"
	jz	found_delineator
	cmp	al,":"
	jz	found_delineator
	loop	delineator_loop
	jmp	delineator_next
found_delineator:
	mov	bx,si			; save delineator address
	loop	delineator_loop
delineator_next:
	xchg	bx,di			; set address of file name to di
	sub	bx,di			; get lenfth of file name
else
	mov	si,di
	xor	cx,cx
	mov	cl,es:[di]
	add	si,cx
	dec	si
	std
	xor	bx,bx
delineator_loop:			; look for beginning of name
	lods	byte ptr es:[si]
	cmp	al,"\"
	jz	found_delineator
	cmp	al,":"
	jz	found_delineator
	inc	bx
	loop	delineator_loop
	dec	si
found_delineator:			; ES:SI -> before name
	mov	di,si
	inc	di
	inc	di
endif

	lds	si,pname
	UnSetKernelDS
	mov	cx,bx
	cld
	repe	cmpsb
	mov	ax,es
	je	ffdone
ffnext:
	mov	ax,word ptr es:[ne_pnextexe]
	jmp	ffloop
ffdone:
cEnd

sEnd	CODE

externFP FarLoadSegment

sBegin	NRESCODE
assumes CS,NRESCODE
assumes DS,NOTHING
assumes ES,NOTHING

externNP DelModule
externNP MapDStoDATA

cProc	FindExeFile,<PUBLIC,NEAR>
    parmD   pFile
cBegin
    cCall   ResidentFindExeFile, <pFile>
cEnd


; Search the list of new EXE headers for the passed executable name

cProc	FindExeInfo,<PUBLIC,NEAR>,<ds,si,di>
    parmD   pname
    parmW   nchars
cBegin

	cCall	MapDStoDATA
	ReSetKernelDS

	mov	bx,nchars
	mov	ax,hExeHead
feloop:
	or	ax,ax
	jz	fedone
	mov	es,ax
	mov	di,es:[ne_restab]
	cmp	es:[di],bl
	jne	fenext
	inc	di
	lds	si,pname
	UnSetKernelDS
	mov	cx,bx
	repe	cmpsb
	je	fedone
fenext:
	mov	ax,word ptr es:[ne_pnextexe]
	jmp	feloop
fedone:
cEnd

cProc	FarFindExeInfo,<PUBLIC,FAR>
	parmD	pname
	parmW	nchars
cBegin
	cCall	FindExeInfo,<pname,nchars>
cEnd


;
; IncExeUsage( hExe ) - procedure to increment the usage count of this
; EXE header.  Indirectly increments the usage count of all the EXE
; headers it points to.
;
cProc	IncExeUsage,<PUBLIC,NEAR>,<ax,di>
	parmW   hexe
cBegin
	mov	cx,hexe
	jcxz	iexj
	mov	es,cx
	cmp	es:[ne_magic],NEMAGIC
	jne	iexj
	test	es:[ne_usage],8000h
	jz	iego
iexj:
	krDebugOut	<DEB_ERROR or DEB_KRLOADMOD>, "IncExeUsage(#ES) not DLL"
	jmp	iex
iego:

	cmp	es:[ne_usage], 4000h
	jb	OKusage
	krDebugOut	DEB_FERROR, "IncExeUsage: ne_usage overflow"
OKusage:

		;
		; Save time and space by saving stuff on stack
		; rather than recursing.
		;

	or	es:[ne_usage], 8000h
NextExe0:
	or	es:[ne_usage], 4000h		; Mark node visited
	inc	es:[ne_usage]
;if kdebug
;	push	ax
;	mov	ax, es:[ne_usage]
;	krDebugOut <DEB_TRACE or DEB_krLoadMod>, "IncExeUsage(%ES0) #ax"
;	pop	ax
;endif
	mov	cx,es:[ne_cmod]
	jcxz	NoDeps0
	mov	di,es:[ne_modtab]
ieloop:
	push	es
	cmp	word ptr es:[di], 0
	je	ieloop1
	lar	ax, es:[di]			; Make sure valid selector
	jnz	ieloop1
	mov	es, es:[di]
	cmp	es:[ne_magic],NEMAGIC
	jne	ieloop1
	test	es:[ne_usage], 0C000h
	jnz	ieloop1
	push	cx
	push	di				; Fake recursion
	jmp	NextExe0
NextExeDone0:					; Return from fake recursion
	pop	di
	pop	cx
ieloop1:
	pop	es
	add	di,2
	loop	ieloop

NoDeps0:
	mov	cx, es
	cmp	cx, hExe
	jne	NextExeDone0

NextExe1:
	and	es:[ne_usage], NOT 4000h	; Mark node visited
	mov	cx,es:[ne_cmod]
	jcxz	NoDeps1
	mov	di,es:[ne_modtab]
UnMarkLoop:
	push	es
	cmp	word ptr es:[di], 0
	je	UnMarkLoop1
	lar	ax, es:[di]			; Make sure valid selector
	jnz	UnMarkLoop1
	mov	es, es:[di]
	cmp	es:[ne_magic],NEMAGIC
	jne	UnMarkLoop1
	test	es:[ne_usage], 08000h
	jnz	UnMarkLoop1
	test	es:[ne_usage], 04000h
	jz	UnMarkLoop1
	push	cx
	push	di				; Fake recursion
	jmp	NextExe1
NextExeDone1:					; Return from fake recursion
	pop	di
	pop	cx
UnMarkLoop1:
	pop	es
	add	di,2
	loop	UnMarkLoop

NoDeps1:
	mov	cx, es
	cmp	cx, hExe
	jne	NextExeDone1

	xor	es:[ne_usage], 8000h
iex:
cEnd

;-----------------------------------------------------------------------;
; DecExeUsage								;
; 									;
; Decrements the usage count of the given EXE header.  Indirectly	;
; decrements the usage count of all the EXE headers it points to.	;
; 									;
; Arguments:								;
;	parmW   hexe							;
; 									;
; Returns:								;
;	ZF = 1 if usage count is now zero.				;
; 									;
; Error Returns:							;
; 									;
; Registers Preserved:							;
;	DI,SI,DS							;
;									;
; Registers Destroyed:							;
; 	AX,BX,CX,DX,ES							;
;									;
; Calls:								;
;	DecExeUsage							;
;	DelModule							;
; 									;
; History:								;
; 									;
;  Mon Sep 21, 1987 01:21:00p  -by-  David N. Weise   [davidw]          ;
; Added this nifty comment block.					;
;-----------------------------------------------------------------------;

cProc	DecExeUsage,<PUBLIC,NEAR>,<ds,di,si>
	parmW   hexe
cBegin
	call	MapDStoDATA
	ReSetKernelDS

	xor	si,si
	mov	cx,hexe
	xor	ax,ax
	jcxz	dexj
	mov	es,cx
	cmp	es:[si].ne_magic,NEMAGIC
	jne	dexj
	test	es:[si].ne_usage,8000h
	jz	dego
dexj:
	krDebugOut	<DEB_ERROR or DEB_KRLOADMOD>, "DecExeUsage(#ES) not DLL"
	jmp	dex
dego:

		;
		; Save time and space by saving stuff on stack
		; rather than recursing.
		;

	dec	es:[si].ne_usage
	or	es:[si].ne_usage, 8000h
;if kdebug
;	push	ax
;	mov	ax, es:[si].ne_usage
;	krDebugOut <DEB_TRACE or DEB_krLoadMod>, "DecExeUsage(%ES0) #ax"
;	pop	ax
;endif
NextExe2:
	or	es:[si].ne_usage, 4000h		; Mark node visited
	mov	cx,es:[si].ne_cmod
	jcxz	NoDeps2
	mov	di,es:[si].ne_modtab
MarkLoop2:
	push	es
	cmp	si, es:[di]
	je	MarkLoop3
	lar	ax, es:[di]			; Make sure valid selector
	jnz	MarkLoop3
	mov	es, es:[di]
	cmp	es:[si].ne_magic,NEMAGIC
	jne	MarkLoop3
	test	es:[si].ne_usage, 0C000h
	jnz	MarkLoop3
	push	cx
	push	di				; Fake recursion
	jmp	NextExe2
NextExeDone2:					; Return from fake recursion
	pop	di
	pop	cx
MarkLoop3:
	pop	es
	add	di,2
	loop	MarkLoop2

NoDeps2:
	mov	cx, es
	cmp	cx, hExe
	jne	NextExeDone2

        xor     cx, cx
        push    cx                              ; End of list of Exes to delete
	mov	di, hExeHead			; Scan Exes once to dec them
scan_exes:
	or	di, di
	jz	de_done
	mov	es, di
	mov	di, es:[si].ne_pnextexe
	test	es:[si].ne_usage, 4000h
	jz	scan_exes
	and	es:[si].ne_usage, NOT 4000h	; Remove the mark
	test	es:[si].ne_usage, 8000h		; Skip this one?
	jnz	scan_exes
;	krDebugOut <DEB_TRACE or DEB_krLoadMod>, "DecExeUsage dependent %ES0"
	dec	es:[si].ne_usage
        jnz     scan_exes
        push    es                              ; We will delete this one
        jmps    scan_exes


de_done:				; Call WEP each module before
	mov	bx, sp			; we free any modules
de_done0:
	mov	cx, ss:[bx]
	add	bx, 2
	jcxz	de_done1
	push	bx
	cCall	CallWEP, <cx, 0>
	pop	bx
	jmps	de_done0


de_done1:
        pop     cx                              ; Get next module to delete
        jcxz    all_deleted
        cCall   DelModule,<cx>                  ; Delete him
	jmps    de_done1

all_deleted:
	mov	es, hExe
	and	es:[si].ne_usage,NOT 0C000h
dex:
cEnd


;
; StartProcAddress( hExe ) - procedure to return the fixed address of
; a new EXE start procedure
;
cProc	StartProcAddress,<PUBLIC,NEAR>,<di>
	parmW   hexe
	parmW   fh
cBegin
	mov	es,hexe
	mov	di,ne_csip
	xor	dx,dx
	mov	ax,es:[di+2]
	or	ax,ax
	jz	sp1
	mov	di,es:[di]
	cCall	FarLoadSegment,<es,ax,fh,fh>
	jcxz	sp1
	mov	ax,di			; DX:AX is start address of module
sp1:					; (DX is really a handle)
	mov	cx,ax
	or	cx,dx
cEnd



; GetStackPtr - returns the initial SS:SP for a module.
;
cProc GetStackPtr,<PUBLIC,NEAR>,<si>
	parmW   hExe
cBegin
	mov	es,hExe
if KDEBUG
	cmp	es:[ne_sssp].sel,0
	jnz	@F
	mov	cx,ne_sssp
	fkerror 0,<Invalid stack segment>,es,cx
@@:
endif
	cmp	es:[ne_sssp].off,0
	jne	re3
	mov	dx,es:[ne_stack]
	mov	bx,es:[ne_pautodata]
	or	bx,bx
	jz	re2
	add	dx,es:[bx].ns_minalloc
re2:	and	dx,0FFFEh		; Word aligned stack
	mov	word ptr es:[ne_sssp],dx

re3:	mov	dx,es:[ne_sssp].sel
	mov	ax,es:[ne_sssp].off
	push	ax
	mov	cx,-1
	cCall	FarLoadSegment,<es,dx,cx,cx>
	pop	ax			; Handle in DX and offset in AX
cEnd




;
; GetInstance( hExe ) - Procedure to return the instance handle for
; the current automatic data segment associated with the passed exe
;
cProc	GetInstance,<PUBLIC,NEAR>
	parmW   hexe
cBegin
	mov	es,hexe
	mov	ax,es:[ne_flags]
	test	ax,NEINST+NESOLO
	mov	ax,es
	jz	gidone
	mov	bx,es:[ne_pautodata]
	or	bx,bx
	jz	gidone
	mov	ax,es:[bx].ns_handle
gidone:
	mov	cx,ax
cEnd

sEnd	NRESCODE

end