	.387
;
;	prot.asm - Protected mode assembler code for gserver
;
;************************************************************************/
;*	Copyright (C) 1986-1993 Phar Lap Software, Inc.			*/
;*	Unpublished - rights reserved under the Copyright Laws of the	*/
;*	United States.  Use, duplication, or disclosure by the 		*/
;*	Government is subject to restrictions as set forth in 		*/
;*	subparagraph (c)(1)(ii) of the Rights in Technical Data and 	*/
;*	Computer Software clause at 252.227-7013.			*/
;*	Phar Lap Software, Inc., 60 Aberdeen Ave., Cambridge, MA 02138	*/
;************************************************************************/
;

; 
; Here we define the segments in this module to force their
; ordering.
;

CODESEG	segment public byte 'CODE'
CODESEG	ends

DATASEG	segment public dword 'DATA'
DATASEG	ends

;
; Global data
;
DATASEG segment 

ebpsave	dd	?		; Saved protected mode EBP 
espsave	dd	?		; Saved protected mode ESP
sssave	dw	?		; Saved protected mode SS

ssexit	dw	?		; Saved SS for exit
espexit	dd	?		; Save ESP for exit

exitflag dw	0		; Exiting from real mode server flag

;
; EXEC parameter block for EXEC to load real mode server
;
ldparm	dd	0		; Environment offset
	dw	0		; Environment selector
	dd	offset cmdtail	; Offset of the command tail
	dw	14h		; Selector of the command tail

;
; We pass some parameters to the real mode server in the DOS command tail
;
cmdtail db	12		; Length of the command tail is 12
callback dd	?		; real mode FAR ptr to 386|DOS-X routine to
				;    call protected mode
	dd	protret		; address of "protret" entry point the real
				;    mode server calls after it initializes
protcs	dw	?		; protected mode CS
protpsp	dw	?		; Protected mode program's PSP 
	db	0dh		; CR to make DOS happy

TRUE	equ	1
FALSE	equ	0
fp387f	dd	?		; T ==> 387 present, F ==> not present
prot_387state db 108 dup (?)	; protected mode pgm floating point state

;
;	Special stack used only during the EXEC call
;
	align	4
	db	128 dup (?)	
exec_stack label	byte

DATASEG	ends

	assume	cs:CODESEG,ds:DATASEG

CODESEG	segment 

;
;	realload - Load and initialize a real mode driver
;
;	retcode = realload(filenamep, srvptrp, exe_blockp);
;
;		int retcode;		/* Return code */
;		char *filenamep;	/* Pointer to the file name */
;		REALPTR *srvptrp;	/* returned;  real mode FAR address
;					   of server interface routine */
;		EXEC_BLK *exe_blockp;	/* Pointer to parameter block used to 
;					   load real mode server */ 
;
;	This routine loads the real mode server program.
;	The caller passes in the file name of 
;	the server to be loaded.  No path searching is done by the routine for
;	file therefore a complete file path must be given for the file or the
;	file must be located in the current directory.  If the load is
;	successful, then the return code is set to zero and the real mode
;	address in segment:offset format of the server is returned
;	to the caller.  The server is called by passing this address to
;	the routine "realcall".  If the load is unsuccesful, then the return
;	code is set to one of the following MS-DOS error codes:
;
;		2 = File not found or invalid path
;		5 = Access denied
;		8 = Not enough memory to load program
;		10 = Invalid environment segment
;		11 = Invalid file format
;	
	public	realload	; Publics for High C/C++
	public	_realload	; Publics for Microsoft C/C++
	public	realload_	; Publics for Watcom C/386

realload proc	near
_realload:
realload_:
;
; Stack frame
;
#filenamep equ	dword ptr [ebp + 8]	; File name pointer
#srvptrp equ	dword ptr [ebp + 12]	; Pointer to returned server address
#exe_blockp equ	dword ptr [ebp + 16]	; Pointer to EXEC parameter block

	push	ebp		; Set-up a stack frame and save registers
	mov	ebp,esp		;
	push	ebx		; 
	push	ecx		;
	push	edx		;
	push	edi		;
	push	esi		;
	push	es		;

;
; If we have a 387 present, save the floating point state so we can restore
; it again after the real mode program initializes.  (It's not necessary
; to save the state if there's no coprocessor and we're emulating, since
; the real mode and prot mode emulators won't interfere with each other.
; Since we don't unconditionally do an FSAVE/FRSTOR, someone could pick up
; this glue code and use it with a non-floating-point program without
; having it break on a machine without a coprocessor).
;
	mov	fp387f,FALSE	; assume no coprocessor
	int	11h		; branch if no coprocessor
	test	eax,2			;
	jz	short #no_87		;
	mov	fp387f,TRUE	; flag coprocessor present
	fsave	prot_387state	; save current coprocessor state
#no_87:

;
; Complete the parameter block that we pass to the real mode server in
; the DOS command tail
;
	mov	ah,51h		; protected mode program's PSP
	int	21h		;    on the stack
	mov	protpsp,bx	;

	mov	protcs,cs	; Save the CS register for our protected mode
				;  return address.  The offset is filled in
				;  at link time.

	mov	ax,250DH	; Get the real-to-protected mode
	int	21h		;    call-back address and save it in our
	mov	callback,eax	;    parameter block.

;
; Now EXEC to the real mode program.  Before making the EXEC call, switch
; to a temporary stack buffer that we use just for the EXEC.  This is
; because after the real mode server initializes, we will return from
; this routine and continue using our current stack.  Later on, when
; we tell the real mode server to terminate, the INT 21h from the EXEC
; call will return, on the same stack it was on when the INT 21h was made --
; so if we didn't have a temp stack we'd be stepping on the current stack,
; which by that time might be in use again (if realexit() is down more
; levels of function calls than realload() is).
;
	mov	ebpsave,ebp	; Save our current EBP and SS:ESP in globals
	mov	espsave,esp	;
	mov	sssave,ss	;

	mov	edx,#filenamep	; get filename ptr before switching stacks

	lea	esp,exec_stack	; switch to temp EXEC stack

	lea	ebx,ldparm	; EXEC to the real mode server normally
	mov	ax,ds		;
	mov	es,ax		;
	mov	ax,4B00h	;
	int	21H		;

	jc	#err		; Branch if a load error.  Error return code
				;    is already in EAX

	jmp	exitret		; EXEC returns successfully when the real
				;    mode server terminates.  This only
				;    occurs when we tell it to terminate
				;    by calling the realexit() routine.
				;    So jump back into the point in the
				;    realexit() routine where it makes the
				;    real mode server terminate.

;
; After the real mode server loads and initializes, it uses the 386|DOS-X
; service to call through to protected mode to the "protret" label, whose
; address we passed to the server in the command tail.  The real mode
; server passes the real mode FAR address of the server function in EAX.
;
; When we get here, we are on the real mode server's stack, and the current
; PSP is the real mode server's PSP.  We want to get back on our own stack,
; switch back to our own PSP, and return to the caller of realload().
;
protret:
	
	mov	ssexit,ss	; Save the current stack for when we
	mov	espexit,esp	;    want to return to the real mode
				;    code in order to force an exit.

	mov	ebp,ebpsave	; Switch back to our regular stack.
	mov	ss,sssave	;
	mov	esp,espsave	;

	mov	ebx,#srvptrp	; Return the server function pointer in the
	mov	[ebx],eax	;     real mode code to the caller.

;	mov	bx,protpsp	; Switch back to the protected mode pgm's PSP
;	mov	ah,51h		;
;	int	21h		;

	cmp	fp387f,FALSE	; Restore the prot mode pgm's 387 state
	je	short #no_87_rst
	frstor	prot_387state
#no_87_rst:

	sub	eax,eax		; Zero the return to indicate a successful
				;    load.

#ret:	pop	es		; Restore the registers and return
	pop	esi		;
	pop	edi		;
	pop	edx		;
	pop	ecx		;
	pop	ebx		;
	pop	ebp		;
	ret			;

#err:	mov	ebp,ebpsave	; Switch back to our normal stack
	mov	ss,sssave	;
	mov	esp,espsave	;
	jmp	#ret		; Go return

realload endp


;
;	realexit - Force the real mode code to exit
;
;	realexit();
;
;	The "realexit" routine is called to force the real mode server to
;	exit.  It must be called by the protected mode code so that the
;	memory allocated to the real mode code is freed and the real mode
;	open files are closed.  
;	
	public	realexit
	public	_realexit
	public	realexit_

realexit proc	near
_realexit:
realexit_:

	push	ebp		; Set-up a stack frame.
	mov	ebp,esp		;
	push	ebx		;
	push	ecx		;
	push	edx		;
	push	edi		;
	push	esi		;
	push	es		;

	mov	ebpsave,ebp	; Save our current EBP and SS:ESP in globals
	mov	espsave,esp	;
	mov	sssave,ss	;

	mov	exitflag,1	; Turn on the flag that says we are telling
				;    the real mode server to exit.

;
; Now "tell" the real mode server to exit.
; We do this by returning from the original call through to protected mode
; (to label "protret") that the server made way back when it first initialized.
; To return from that call, all we have to do is switch back to the stack
; we were on at that point (which we saved in globals), and execute
; a FAR return instruction.
;
	mov	ss,ssexit	; Reload SS:ESP.
	mov	esp,espexit	;
	db	0cbh		; Use a far return instruction to go back
				;    to the real mode code.

;
; When the server terminates, it returns from the original INT 21h func 4Bh
; EXEC that was done in the realload() routine.  That routine then immediately
; jumps to the "exitret" label below.
;
; When we get here, the real mode server and its PSP are gone, and we are
; back on our own PSP.  However, the stack is the temp EXEC stack that we
; switched to just before making the original INT 21h func 4Bh call.
; So we have to switch back to the stack we had when realexit() was called.
;
exitret:

;
; If the real mode server exited before we told it to by returning from
; that initialization time call to "protret" above, then we are hosed,
; because we have no idea where in the protected mode program we are
; executing.
;
	cmp	exitflag,0	; Branch if the exit flag is off. (oops!)
	je	#err		;

	mov	ebp,ebpsave	; Switch back to our regular stack.
	mov	ss,sssave	;
	mov	esp,espsave	;

	mov	exitflag,0	; Clear the exit flag.

	pop	es		; Restore the registers and return.
	pop	esi		;
	pop	edi		;
	pop	edx		;
	pop	ecx		;
	pop	ebx		;
	pop	ebp		;
	ret			;

#err:	mov	edx,offset #errmsg ;  Output error message "Fatal error:
	push	cs		;	Unexpected exit from real mode code.".
	pop	ds		;
	mov	ah,09h		;
	int	21h		;

	mov	ax,4c01h	; Exit ourselves.
	int	21h		;

#errmsg	db	'Fatal error: Unexpected exit from real mode code.'
	db	0dh,0ah,'$'

realexit endp

CODESEG ends

	end
