;       SCCSID = @(#)thunk.asm	6.1 90/11/16
;	SCCSID = @(#)thunk.asm	6.1 90/01/26
;	SCCSID = @(#)srclabel.asm	6.2 90/01/12
; ****************************************************************************
; *									     *
; *			  IBM/Microsoft Confidential			     *
; *									     *
; *		    Copyright (c) IBM Corporation  1987, 1990		     *
; *		    Copyright (c) Microsoft Corp.  1987, 1990		     *
; *			      All Rights Reserved			     *
; *									     *
; ****************************************************************************

	page	,132
	.286p

	TITLE	Thunk - load-on reference

.xlist
	include basemaca.inc		;;;;;; 2.0 unique (dosmac replacement)
	include mi.inc			;;;;;; 2.0 unique (dossymc replacement)
	include struc.inc
	include ansidecl.inc
.list

EXTRN	DOSLOADMODULE:FAR
EXTRN	DOSGETPROCADDR:FAR

gdseg	segment public	'data'
gdseg	ends

ansi1	segment public	'code'
	assume	cs:ansi1, ds:nothing, es:nothing, ss:nothing

;***	Thunker - handle load-on-reference calls
;
;	Dynlink modules, despite being segmentd, etc, are not very granular.
;	In particular, at load time all linkes between modules are snapped
;	resulting in a fair amount of overhead in terms of LDT initialization,
;	loading PRELOAD segments and allocating instance data segments.
;	This occurs whether or not a particular reference is ever used.
;
;	We cannot change the loader to perform the linkage at reference time
;	since there are nor error paths from things like MOV DS,REF, we are
;	forced to implement a thunk mechanism whereby the first reference
;	to a routine loads the relevant module.
;
;	Thunker is the main driver for this.  It is passed the address
;	of a thunk.  Thunker will the load the indicated module and
;	retrieve the indicated entry point.  Once this occurs
;	successfully, Thunker will then fix the thunk so that subsequent
;	references to the thunk will simply return.
;
;	ENTRY	(AX) =	offset in DS of thunk
;	EXIT	(AX) =	error code or 0.  If successful, AX contains
;			0 and the thunk is set up to call the relevant
;			routine.  If failure, AX contains error code
;			and the thunk is left alone.  It is assumed
;			that immediately following the call to Thunker,
;			a call to the function occurs.	By default, the
;			routine is a simple far return which appears
;			that the error in AX gets returned.

Procedure   Thunker,near
LocalVar    hMod,WORD
LocalVar    pfn,DWORD
	EnterProc
	push	bx

	xchg	ax,bx
	push	0			; sel of error buffer
	push	0			; off of error buffer
	push	0			; length of error buffer
	push	gdseg			; sel of mod name
	push	[bx].szModOff		; off of mod name
	push	ss			; sel of returned handle
	lea	ax,hMod
	push	ax			; off of returned handle

	call	DOSLOADMODULE		; load the module
	.if	<zero ax>		; did we load sucessfully?
	    push    hMod		; module handle for entry retrieval
	    push    ax			;
	    push    [bx].hOrdinal	; use ordinal number
	    push    ss			; selector of returned address
	    lea     ax,pfn		;
	    push    ax			; offset of returned address

	    call    DOSGETPROCADDR	; find entry point
	    .if     <zero ax>		; did we find entry?
		mov	ax,pfn.Segmt	; (ax) = selector of function
		mov	[bx].pfnCall.Segmt,ax
		mov	ax,pfn.Offst	; (ax) = offset of function
		mov	[bx].pfnCall.Offst,ax
		mov	[bx].pfnTnk, offset ThunkerNearRet ; patch out thunk
		sub	ax,ax		; clear return code
	    .endif
	.endif
	pop	bx
	LeaveProc
ThunkerNearRet:
	ret
EndProc Thunker

Procedure   ThunkerFarRet,far
	ret
EndProc     ThunkerFarRet

ansi1	ends

end
