/* X86.prim.masm (MS assembler)
 *
 * COPYRIGHT (c) 1996 Bell Laboratories, Lucent Technologies
 *
 * derived from X86.prim.asm
 * derived from I386.prim.s, by Mark Leone (mleone@cs.cmu.edu)
 *
 * new version derived from Lal George's completely rewritten
 * X86.prim.asm, by Matthias Blume (blume@cs.uchicago.edu)
 */

#include "ml-base.h"
#include "asm-base.h"
#include "ml-values.h"
#include "tags.h"
#include "ml-request.h"
#include "reg-mask.h"
#include "ml-limits.h"

/*
 *
 * The 386 registers are used as follows:
 *
 * EAX - temp1 (see the code generator, x86/x86.sml)
 * EBX - misc0
 * ECX - misc1
 * EDX - misc2
 * ESI - standard continuation (ml_cont, see ml_state.h)
 * EBP - standard argument (ml_arg)
 * EDI - free space pointer (ml_allocptr)
 * ESP - stack pointer
 * EIP - program counter (ml_pc)
 */


	/* Registers (see x86/x86.sml): */
#define temp		REG(eax)
#define misc0		REG(ebx)
#define misc1		REG(ecx)
#define misc2		REG(edx)
#define stdcont		REG(esi)
#define stdarg		REG(ebp)
#define allocptr	REG(edi)
#define stackptr	REG(esp)


	/* other reg uses */
#define creturn 	REG(eax)

#define REGOFF(o,r)	IND_DW_OFF(r,o)

	/* Stack frame (see x86/x86.sml): */
#define tempmem		REGOFF(0,REG(esp))
#define baseptr		REGOFF(4,REG(esp))
#define exncont		REGOFF(8,REG(esp))
#define limitptr	REGOFF(12,REG(esp))
#define pc		REGOFF(16,REG(esp))
#define unused_1	REGOFF(20,REG(esp))
#define storeptr	REGOFF(24,REG(esp))
#define varptr		REGOFF(28,REG(esp))
#define start_gc	REGOFF(32,REG(esp))
#define unused_2	REGOFF(36,REG(esp))
#define eaxSpill	REGOFF(40,REG(esp)) /* eax=0 */
#define	ecxSpill	REGOFF(44,REG(esp)) /* ecx=1 */
#define	edxSpill	REGOFF(48,REG(esp)) /* edx=2 */
#define	ebxSpill	REGOFF(52,REG(esp)) /* ebx=3 */
#define	espSpill	REGOFF(56,REG(esp)) /* esp=4 */
#define	ebpSpill	REGOFF(60,REG(esp)) /* ebp=5 */
#define	esiSpill	REGOFF(64,REG(esp)) /* esi=6 */
#define	ediSpill	REGOFF(68,REG(esp)) /* edi=7 */
#define stdlink		REGOFF(72,REG(esp))
#define	stdclos		REGOFF(76,REG(esp))
	
#define ML_STATE_OFFSET 176
#define mlstate_ptr	REGOFF(ML_STATE_OFFSET,REG(esp))
#define freg8           184	     /* double word aligned */ 
#define	freg9           192
#define freg31          368          /* 152 + (31-8)*8 */
#define	fpTempMem	376	     /* freg31 + 8 */
#define SpillAreaStart	512	     /* starting offset */	
#define ML_FRAME_SIZE	(8192)

#define	via

.386
.MODEL FLAT

	DATA
	ALIGN4
WORD32(request_w,0)	/* place to put the request code */

	GLOBAL(CSYM(ML_X86Frame))
WORD32(CSYM(ML_X86Frame),0) /* ptr to the ml frame (gives C access to limitptr) */

WORD32(SavedSP,0)      /* Value of stack pointer to restore */
	
#include "mlstate-offsets.h"	/** this file is generated **/

/*
 * 386 function call conventions:  
 *  [true for gcc and dynix3 cc; untested for others]
 *
 * 	Caller save registers: eax, ecx, edx
 * 	Callee save registers: ebx, esi, edi, and ebp. 
 * 	Floating point state is caller-save.
 * 	Arguments passed on stack.  Rightmost argument pushed first.
 * 	Word-sized result returned in %eax.
 */

#define cresult	REG(eax)

CALLEE_SAVE_M MACRO
	PUSHL	REG(ebx)
	PUSHL	REG(esi)
	PUSHL	REG(edi)
	PUSHL	REG(ebp)	
ENDM
#define CALLEE_SAVE CALLEE_SAVE_M

CALLEE_RESTORE_M MACRO
	POPL	REG(ebp)
	POPL	REG(edi)
	POPL	REG(esi)
	POPL	REG(ebx)
ENDM 
#define CALLEE_RESTORE CALLEE_RESTORE_M

/* MOVE copies one memory location to another, using a specified temporary. */

EXCHANGE_M MACRO src,tmp,dest
	MOVL	(src, tmp)
	MOVL	(tmp, dest)
ENDM
#define MOVE(a,b,c) EXCHANGE_M a, b, c

CONTINUE_M MACRO
	JMP     via stdcont
ENDM
#define CONTINUE CONTINUE_M

CHECKLIMIT_M MACRO
 ANON_LAB:
	MOVE(stdlink, temp, pc)
	CMPL(limitptr, allocptr)
	jb	FLAB_ANON
	CALL	CSYM(saveregs)
	JMP	BLAB_ANON
 ANON_LAB:
ENDM
#define CHECKLIMIT CHECKLIMIT_M

ENTRY_M MACRO id
	GLOBAL(CSYM(&id))
	LABEL(CSYM(&id))
ENDM
#define ENTRY(id) ENTRY_M id

ML_CODE_HDR_M MACRO name
	GLOBAL(CSYM(&name))
	ALIGN4
	LABEL(CSYM(&name))
ENDM
#define ML_CODE_HDR(name) ML_CODE_HDR_M name


/**********************************************************************/
	TEXT
	ALIGN4

ML_CODE_HDR(sigh_return_a)
	MOVL	(IMMED(ML_unit),stdlink)
	MOVL	(IMMED(ML_unit),stdclos)
	MOVL	(IMMED(ML_unit),pc)
	MOVL	(IMMED(REQ_SIG_RETURN),request_w)
	JMP	CSYM(set_request)

/* sigh_resume:
 * Resume execution at the point at which a handler trap occurred.  This is a
 * standard two-argument function, thus the closure is in ml_cont.
 */

ENTRY(sigh_resume)
	MOVL	(IMMED(REQ_SIG_RESUME),request_w)
	JMP	CSYM(set_request)

/* pollh_return_a:
 * The return continuation for the ML poll handler.
 */
ML_CODE_HDR(pollh_return_a)
	MOVL	(IMMED(REQ_POLL_RETURN),request_w)
	MOVL	(IMMED(ML_unit),stdlink)
	MOVL	(IMMED(ML_unit),stdclos)
	MOVL	(IMMED(ML_unit),pc)
	JMP	CSYM(set_request)
	
/* pollh_resume:
 * Resume execution at the point at which a poll event occurred.
 */
ENTRY(pollh_resume)
	MOVL	(IMMED(REQ_POLL_RESUME),request_w)
	JMP	CSYM(set_request)

ML_CODE_HDR(handle_a)
	MOVL	(IMMED(REQ_EXN),request_w)
	MOVE	(stdlink,temp,pc)
	JMP	CSYM(set_request)

ML_CODE_HDR(return_a)
	MOVL	(IMMED(REQ_RETURN),request_w)
	MOVL	(IMMED(ML_unit),stdlink)
	MOVL	(IMMED(ML_unit),stdclos)
	MOVL	(IMMED(ML_unit),pc)
	JMP	CSYM(set_request)

/* Request a fault.  The floating point coprocessor must be reset
 * (thus trashing the FP registers) since we don't know whether a 
 * value has been pushed into the temporary "register".	 This is OK 
 * because no floating point registers will be live at the start of 
 * the exception handler.
 */
ENTRY(request_fault)
	CALL	CSYM(FPEEnable)
	MOVL	(IMMED(REQ_FAULT),request_w)
	MOVE	(stdlink,temp,pc)
	JMP	CSYM(set_request)

/* bind_cfun : (string * string) -> c_function
 */
ML_CODE_HDR(bind_cfun_a)
	CHECKLIMIT
	MOVL	(IMMED(REQ_BIND_CFUN),request_w)
	JMP	CSYM(set_request)

ML_CODE_HDR(build_literals_a)
	CHECKLIMIT
	MOVL	(IMMED(REQ_BUILD_LITERALS),request_w)
	JMP	CSYM(set_request)

ML_CODE_HDR(callc_a)
	CHECKLIMIT
	MOVL	(IMMED(REQ_CALLC),request_w)
	JMP	CSYM(set_request)

ENTRY(saveregs)
	POPL	pc
	MOVL	(IMMED(REQ_GC),request_w)
	/* fall into set_request */
	
ENTRY(set_request)
	/* temp holds mlstate_ptr, valid request in request_w  */
	/* Save registers */
	MOVL	(mlstate_ptr, temp)
	MOVL	(allocptr, REGOFF(AllocPtrOffMSP,temp))
	MOVL	(stdarg, REGOFF(StdArgOffMSP,temp))
	MOVL	(stdcont, REGOFF(StdContOffMSP,temp))
	
#define	temp2 allocptr
	/* note that we have left ML code */
	MOVL	(REGOFF(VProcOffMSP,temp),temp2)
	MOVL	(IMMED(0), REGOFF(InMLOffVSP,temp2))
	
	MOVL	(misc0, REGOFF(Misc0OffMSP,temp))
	MOVL	(misc1, REGOFF(Misc1OffMSP,temp))
	MOVL	(misc2, REGOFF(Misc2OffMSP,temp))

	/* Save vregs before stack frame is popped. (?? - Blume) */
	MOVE	(limitptr,temp2, REGOFF(LimitPtrOffMSP,temp))
	MOVE	(exncont, temp2, REGOFF(ExnPtrOffMSP,temp)) 
	MOVE	(stdclos, temp2, REGOFF(StdClosOffMSP,temp))
	MOVE	(stdlink, temp2, REGOFF(LinkRegOffMSP,temp))
	MOVE	(pc, temp2, REGOFF(PCOffMSP,temp))
	MOVE	(storeptr,temp2, REGOFF(StorePtrOffMSP,temp))
	MOVE	(varptr,  temp2, REGOFF(VarPtrOffMSP,temp))
#undef	temp2	
	
	/* return val of function is request code */
	MOVL	(request_w,creturn)

	/* Pop the stack frame and return to run_ml(). */
	MOVL	(SavedSP, REG(esp))
	CALLEE_RESTORE
	RET

	TEXT
	ALIGN4

ENTRY(asm_restoreregs)
	MOVL	(REGOFF(4,REG(esp)), temp) /* get argument (MLState ptr) */
	CALLEE_SAVE

	MOVL	(REG(esp), SavedSP)	/* save stack pointer */

	/* Align on 8 byte boundary. Assumes that the stack starts
	 * out being at least word aligned. But who knows... */

	ORL	(IMMED(4),REG(esp))
	SUBL	(IMMED(4),REG(esp))

#define temp2 REG(ebx)
	/* Allocate and initialize the ML stack frame. */
	SUBL	(IMMED(ML_FRAME_SIZE), REG(esp))
	MOVE	(REGOFF(ExnPtrOffMSP,temp),   temp2, exncont)
	MOVE	(REGOFF(LimitPtrOffMSP,temp), temp2, limitptr)
	MOVE	(REGOFF(StorePtrOffMSP,temp), temp2, storeptr)
	MOVE	(REGOFF(VarPtrOffMSP,temp),   temp2, varptr)
	LEA	(CSYM(saveregs),temp2)
	MOVL	(temp2,start_gc)
	MOVL	(temp,mlstate_ptr)

	/* vregs */
	MOVE	(REGOFF(LinkRegOffMSP,temp),temp2,stdlink)
	MOVE	(REGOFF(StdClosOffMSP,temp),temp2,stdclos)

	/* PC */
	MOVE	(REGOFF(PCOffMSP,temp),temp2,pc)
#undef temp2

	/* Load ML registers */
	MOVL	(REGOFF(AllocPtrOffMSP,temp),allocptr)
	MOVL	(REGOFF(StdContOffMSP,temp),stdcont)
	MOVL	(REGOFF(StdArgOffMSP,temp),stdarg)
	MOVL	(REGOFF(Misc0OffMSP,temp),misc0)
	MOVL	(REGOFF(Misc1OffMSP,temp),misc1)
	MOVL	(REGOFF(Misc2OffMSP,temp),misc2)

	MOVL(REG(esp),CSYM(ML_X86Frame))    /* frame ptr for signal handler. */

	PUSHL	misc2			/* free up a register */
	PUSHL	temp			/* save msp temporarily */

#define tmpreg misc2
	/* note that we're entering ML */
	MOVL	(REGOFF(VProcOffMSP,temp),temp)	/* temp is now vsp */
#define vsp temp
	MOVL	(IMMED(1),REGOFF(InMLOffVSP,vsp))

	/* handle signals */
	MOVL	  (REGOFF(SigsRecvOffVSP,vsp),tmpreg)
	CMPL	  (REGOFF(SigsHandledOffVSP,vsp),tmpreg)
#undef tmpreg

	JNE	pending

restore_and_jmp_ml:
	POPL	temp			/* restore temp to msp */
	POPL	misc2

jmp_ml:
	CMPL	(limitptr,allocptr)
	JMP	(REGOFF(PCOffMSP,temp))	/* jump to ML code */

pending:
	CMPL	(IMMED(0),REGOFF(InSigHandlerOffVSP,vsp))
	JNE	restore_and_jmp_ml

	MOVL	(IMMED(1),REGOFF(HandlerPendingOffVSP,vsp))

	/* must restore here because limitptr is on stack */
	POPL	temp			/* restore temp to msp */
	POPL	misc2

	MOVL	(allocptr,limitptr)
	JMP	jmp_ml
#undef vsp


/* ----------------------------------------------------------------------
 * array : (int * 'a) -> 'a array
 * Allocate and initialize a new array.	 This can cause GC.
 */

ML_CODE_HDR(array_a)
	CHECKLIMIT
	MOVL	(REGOFF(0,stdarg),temp)		/* temp := length in words */
	SARL	(IMMED(1),temp)			/* temp := length untagged */
	CMPL	(IMMED(SMALL_OBJ_SZW),temp)	/* small object? */
	JGE	FLAB(ARRAY_A_LARGE)

#define temp1 misc0
#define temp2 misc1
	PUSHL	misc0			/* free up misc0 */
	PUSHL	misc1			/* free up misc1 */

	MOVL	(temp,temp1)
	SALL	(IMMED(TAG_SHIFTW),temp1)	/* build descriptor */
	ORL	(IMMED(MAKE_TAG(DTAG_arr_data)),temp1)
	MOVL	(temp1,REGOFF(0,allocptr))	/* store descriptor */
	ADDL	(IMMED(4),allocptr)		/* allocptr++ */
	MOVL	(allocptr,temp1)		/* temp1 := array data ptr */
	MOVL	(REGOFF(4,stdarg),temp2)	/* temp2 := initial value */
ANON_LAB:
	MOVL	(temp2,REGOFF(0,allocptr))	/* init array */
	ADDL	(IMMED(4),allocptr)
	SUBL	(IMMED(1),temp)
	JNE	BLAB_ANON

	/* Allocate array header */
	MOVL	(IMMED(DESC_polyarr),REGOFF(0,allocptr)) /* descriptor */
	ADDL	(IMMED(4),allocptr)
	MOVL	(REGOFF(0,stdarg),temp)		/* temp := length */
	MOVL	(allocptr, stdarg)		/* result := header addr */
	MOVL	(temp1, REGOFF(0,allocptr))	/* store pointer to data */
	MOVL	(temp, REGOFF(4,allocptr))	/* store length */
	ADDL	(IMMED(8),allocptr)

	POPL	misc1
	POPL	misc0
	CONTINUE
#undef temp1
#undef temp2

LABEL(ARRAY_A_LARGE)
	MOVL	(IMMED(REQ_ALLOC_ARRAY),request_w)
	MOVE	(stdlink,temp,pc)
	JMP	CSYM(set_request)


/* create_r : int -> realarray */
ML_CODE_HDR(create_r_a)
	CHECKLIMIT
#define temp1 misc0
	PUSHL	misc0			/* free temp1 */
	MOVL	(stdarg,temp)		/* temp := length */
	SARL	(IMMED(1),temp)		/* temp := untagged length */
	SHLL	(IMMED(1),temp)		/* temp := length in words */
	CMPL	(IMMED(SMALL_OBJ_SZW),temp)
	JGE	FLAB_ANON

	ORL	(IMMED(4),allocptr)	/* align allocptr */

	/* allocate the data object */
	MOVL	(temp,temp1)
	SHLL	(IMMED(TAG_SHIFTW),temp1)	/* temp1 := descriptor */
	ORL	(IMMED(MAKE_TAG(DTAG_raw64)),temp1)
	MOVL	(temp1,REGOFF(0,allocptr))	/* store descriptor */
	ADDL	(IMMED(4),allocptr)		/* allocptr++ */
	MOVL	(allocptr,temp1)		/* temp1 := data object */
	SHLL	(IMMED(2),temp)			/* temp := length in bytes */
	ADDL	(temp,allocptr)			/* allocptr += length */

	/* allocate the header object */
	MOVL	(IMMED(DESC_real64arr),REGOFF(0,allocptr))
	ADDL	(IMMED(4),allocptr)		/* allocptr++ */
	MOVL	(temp1,REGOFF(0,allocptr))	/* header data */
	MOVL	(stdarg,REGOFF(4,allocptr))	/* header length */
	MOVL	(allocptr,stdarg)		/* stdarg := header obj */
	ADDL	(IMMED(8),allocptr)		/* allocptr += 2 */

	POPL	misc0
	CONTINUE

ANON_LAB:
	POPL	misc0
	MOVL	(IMMED(REQ_ALLOC_REALDARRAY),request_w)
	MOVE	(stdlink,temp,pc)
	JMP	CSYM(set_request)
#undef temp1


/* create_b : int -> bytearray */
ML_CODE_HDR(create_b_a)
	CHECKLIMIT
	MOVL	(stdarg,temp)		/* temp is tagged length */
	SARL	(IMMED(1),temp)		/* temp is untagged length */
	ADDL	(IMMED(3),temp)
	SARL	(IMMED(2),temp)		/* temp is length in words */
	CMPL	(IMMED(SMALL_OBJ_SZW),temp)
	JMP	FLAB_ANON
	JGE	FLAB_ANON		/* XXXXX */

#define temp1 misc0
	PUSHL	misc0

	/* allocate the data object */
	MOVL	(temp,temp1)
	SHLL	(IMMED(TAG_SHIFTW),temp1)
	ORL	(IMMED(MAKE_TAG(DTAG_raw32)),temp1)
	MOVL	(temp1,REGOFF(0,allocptr))	/* store descriptor */
	ADDL	(IMMED(4),allocptr)
	MOVL	(allocptr,temp1)		/* temp1 is data object */
	SHLL	(IMMED(2),temp)			/* temp is size in bytes */
	ADDL	(temp,allocptr)			/* allocptr += length */

	/* allocate the header object */
	MOVL	(IMMED(DESC_word8arr),REGOFF(0,allocptr))
	ADDL	(IMMED(4),allocptr)
	MOVL	(temp1,REGOFF(0,allocptr))
	MOVL	(stdarg,REGOFF(4,allocptr))
	MOVL	(allocptr,stdarg)		/* stdarg := header */
	ADDL	(IMMED(8),allocptr)		/* allocptr += 2 */
	POPL	misc0
	CONTINUE
#undef temp1

ANON_LAB:
	MOVL	(IMMED(REQ_ALLOC_BYTEARRAY),request_w)
	MOVE	(stdlink,temp,pc)
	JMP	CSYM(set_request)

	
/* create_s : int -> string */
ML_CODE_HDR(create_s_a)
	CHECKLIMIT
	MOVL	(stdarg,temp)
	SARL	(IMMED(1),temp)		/* untag */
	ADDL	(IMMED(4),temp)		/* 3 + extra byte */
	SARL	(IMMED(2),temp)		/* length in words */
	CMPL	(IMMED(SMALL_OBJ_SZW),temp)
	JGE	FLAB_ANON

	PUSHL	misc0
#define temp1 misc0

	MOVL	(temp,temp1)
	SHLL	(IMMED(TAG_SHIFTW),temp1)
	ORL	(IMMED(MAKE_TAG(DTAG_raw32)),temp1)
	MOVL	(temp1,REGOFF(0,allocptr))	/* store descriptor */
	ADDL	(IMMED(4),allocptr)

	MOVL	(allocptr,temp1)		/* temp1 is data obj */
	SHLL	(IMMED(2),temp)			/* bytes len */
	ADDL	(temp,allocptr)			/* allocptr += length */
	MOVL	(IMMED(0),REGOFF((-4),allocptr)) /* zero out last word */

	/* allocate header obj */
	MOVL	(IMMED(DESC_string),temp)	/* hdr descr */
	MOVL	(temp,REGOFF(0,allocptr))
	ADDL	(IMMED(4),allocptr)
	MOVL	(temp1,REGOFF(0,allocptr))	/* hdr data */
	MOVL	(stdarg,REGOFF(4,allocptr))	/* hdr length */
	MOVL	(allocptr, stdarg)		/* stdarg is hdr obj */
	ADDL	(IMMED(8),allocptr)		/* allocptr += 2 */

	POPL	misc0
#undef temp1
	CONTINUE

ANON_LAB:
	MOVL	(IMMED(REQ_ALLOC_STRING),request_w)
	MOVE	(stdlink, temp, pc)
	JMP	CSYM(set_request)


/* create_v_a : int * 'a list -> 'a vector
 *	creates a vector with elements taken from a list.
 *	n.b. The frontend ensures that list cannot be nil.
 */
ML_CODE_HDR(create_v_a)
	CHECKLIMIT
	PUSHL	misc0
	PUSHL	misc1
#define temp1 misc0
#define temp2 misc1
	MOVL	(REGOFF(0,stdarg),temp)		/* len tagged */
	MOVL	(temp,temp1)
	SARL	(IMMED(1),temp1)		/* untag */
	CMPL	(IMMED(SMALL_OBJ_SZW),temp1)
	JGE	FLAB(CREATE_V_A_LARGE)

	SHLL	(IMMED(TAG_SHIFTW),temp1)
	ORL	(IMMED(MAKE_TAG(DTAG_vec_data)),temp1)
	MOVL	(temp1,REGOFF(0,allocptr))
	ADDL	(IMMED(4),allocptr)
	MOVL	(REGOFF(4,stdarg),temp1)	/* temp1 is list */
	MOVL	(allocptr,stdarg)		/* stdarg is vector */

ANON_LAB:
	MOVL	(REGOFF(0,temp1),temp2)		/* hd */
	MOVL	(temp2,REGOFF(0,allocptr))	/* store into vector */
	ADDL	(IMMED(4),allocptr)
	MOVL	(REGOFF(4,temp1),temp1)		/* tl */
	CMPL	(IMMED(ML_nil),temp1)		/* isNull */
	JNE	BLAB_ANON

	/* allocate header object */
	MOVL	(IMMED(DESC_polyvec),temp1)
	MOVL	(temp1,REGOFF(0,allocptr))
	ADDL	(IMMED(4),allocptr)
	MOVL	(stdarg,REGOFF(0,allocptr))	/* data */
	MOVL	(temp,REGOFF(4,allocptr))	/* len */
	MOVL	(allocptr,stdarg)		/* result */
	ADDL	(IMMED(8),allocptr)		/* allocptr += 2 */

	POPL	misc1
	POPL	misc0
	CONTINUE
	
LABEL(CREATE_V_A_LARGE)
	POPL	misc1
	POPL	misc0
	MOVL	(IMMED(REQ_ALLOC_VECTOR),request_w)
	MOVE	(stdlink, temp, pc)
	JMP	CSYM(set_request)
#undef temp1
#undef temp2
	
	
/* try_lock: spin_lock -> bool. 
 * low-level test-and-set style primitive for mutual-exclusion among 
 * processors.	For now, we only provide a uni-processor trivial version.
 */
ML_CODE_HDR(try_lock_a)
#if (MAX_PROCS > 1)
#  error multiple processors not supported
#else /* (MAX_PROCS == 1) */
	MOVL	((stdarg), temp)	/* Get old value of lock. */
	MOVL	(IMMED(1), (stdarg))	/* Set the lock to ML_false. */
	MOVL	(temp, stdarg)		/* Return old value of lock. */
	CONTINUE
#endif

/* unlock : releases a spin lock 
 */
ML_CODE_HDR(unlock_a)
#if (MAX_PROCS > 1)
#  error multiple processors not supported
#else /* (MAX_PROCS == 1) */
	MOVL	(IMMED(3), (stdarg))	/* Store ML_true into lock. */
	MOVL	(IMMED(1), stdarg)	/* Return unit. */
	CONTINUE
#endif


/********************* Floating point functions. *********************/

#define FPOP	fstp FP_REG(st)	/* Pop the floating point register stack. */

/* Temporary storage for the old and new floating point control
   word.  We don't use the stack to for this, since doing so would 
   change the offsets of the pseudo-registers. */
	DATA
	ALIGN4
GLOBAL(old_controlwd)
WORD16(old_controlwd,0)
GLOBAL(new_controlwd)
WORD16(new_controlwd,0)

	TEXT
	ALIGN4

/*
 * Initialize the 80387 floating point coprocessor.  First, the floating
 * point control word is initialized (undefined fields are left
 * unchanged).	Rounding control is set to "nearest" (although floor_a
 * needs "toward negative infinity").  Precision control is set to
 * "double".  The precision, underflow, denormal 
 * overflow, zero divide, and invalid operation exceptions
 * are masked.  Next, seven of the eight available entries on the
 * floating point register stack are claimed (see x86/x86.sml).
 *
 * NB: this cannot trash any registers because it's called from request_fault.
 */
ENTRY(FPEEnable)
	finit
	/* Temp space.Keep stack aligned. */
	SUBL	(IMMED(4), REG(esp))   
	/* Store FP control word. */
	fstcw	IND_W_OFF(REG(esp),0)  
	/* Keep undefined fields, clear others. */
	ANDW	(IMMED(HEXLIT(f0c0)), REGOFF(0,REG(esp)))
	/* Set fields (see above). */
	ORW	(IMMED(HEXLIT(023f)), REGOFF(0,REG(esp)))	
	fldcw	IND_W_OFF(REG(esp),0) /* Install new control word. */
	ADDL	(IMMED(4), REG(esp))
	RET

ENTRY(fegetround)
      SUBL    (IMMED(4),REG(esp))         /* allocate temporary space */
      FSTCW   IND_W_OFF(REG(esp),0)          /* store fp control word */
	/* rounding mode is at bit 10 and 11 */
      SARL    (IMMED(10),REGOFF(0,REG(esp))) 
      ANDL    (IMMED(3),REGOFF(0,REG(esp)))  /* mask two bits */
      MOVL    (REGOFF(0,REG(esp)),REG(eax))  /* return rounding mode */
      ADDL    (IMMED(4),REG(esp))                /* deallocate space */
      RET

ENTRY(fesetround)
      SUBL    (IMMED(4),REG(esp))	/* allocate temporary space */
      FSTCW   IND_W_OFF(REG(esp),0)	/* store fp control word */
	/* Clear rounding field. */
      ANDW    (IMMED(HEXLIT(f3ff)),REGOFF(0,REG(esp)))
      MOVL    (REGOFF(8,REG(esp)),REG(eax))	/* new rounding mode */
      SALL    (IMMED(10),REG(eax))		/* move to right place */
      ORL     (REG(eax),REGOFF(0,REG(esp)))	/* new control word */
      FLDCW   IND_W_OFF(REG(esp),0)		/* load new control word */
      ADDL    (IMMED(4),REG(esp))		/* deallocate space */
      RET


/* floor : real -> int
   Return the nearest integer that is less or equal to the argument.
   Caller's responsibility to make argument in range. */

ML_CODE_HDR(floor_a)
	/* Get FP control word. */
	fstcw	old_controlwd
	MOVW	(old_controlwd,REG(ax))
	/* Clear rounding field. */
	ANDW	(IMMED(HEXLIT(f3ff)), REG(ax))
	/* Round towards neg. infinity. */
	ORW	(IMMED(HEXLIT(0400)), REG(ax))
	MOVW	(REG(ax), new_controlwd)
	fldcw	new_controlwd		/* Install new control word. */

	fld	REAL8 PTR 0 [stdarg]
	SUBL	(IMMED(4),REG(esp))
	FISTPL	REGOFF(0,REG(esp))
	POPL	stdarg
	SALL	(IMMED(1),stdarg)
	INCL	stdarg

	FLDCW	old_controlwd
	CONTINUE


/* logb : real -> int
 * Extract the unbiased exponent pointed to by stdarg.
 * Note: Using fxtract, and fistl does not work for inf's and nan's.
 */
ML_CODE_HDR(logb_a)
	MOVL    (REGOFF(4,stdarg),temp)	/* msb for little endian arch */
	SARL	(IMMED(20),temp)	/* throw out 20 bits */
	ANDL	(IMMED(HEXLIT(7ff)),temp)	/* clear all but 11 low bits */
	SUBL	(IMMED(1023),temp)		/* unbias */
	SALL	(IMMED(1),temp)			/* room for tag bit */
	ADDL	(IMMED(1),temp)			/* tag bit */
	MOVL	(temp,stdarg)
	CONTINUE


/* scalb : (real * int) -> real
 * Scale the first argument by 2 raised to the second argument.	 Raise
 * Float("underflow") or Float("overflow") as appropriate.
 * NB: We assume the first floating point "register" is
 * caller-save, so we can use it here (see x86/x86.sml). */

ML_CODE_HDR(scalb_a)
	CHECKLIMIT
	PUSHL	REGOFF(4,stdarg)		/* Get copy of scalar. */
	SARL	(IMMED(1),REGOFF(0,REG(esp)))	/* Untag it. */
	FILDL	REGOFF(0,REG(esp))			/* Load it ... */
/*	fstp	FP_REG(st)(1) */		/* ... into 1st FP reg. */
/*	ADDL	(IMMED(4), REG(esp)) */		/* Discard copy of scalar. */

	MOVL	(REGOFF(0,stdarg), temp)	/* Get pointer to real. */
	fld	REAL8 PTR 0 [temp]		/* Load it into temp. */

	fscale				/* Multiply exponent by scalar. */
	MOVL	(IMMED(DESC_reald), REGOFF(0,allocptr))
	fstp	REAL8 PTR 4 [allocptr]	/* Store resulting float. */
	ADDL	(IMMED(4),allocptr)	/* Allocate word for tag. */
	MOVL	(allocptr, stdarg)	/* Return a pointer to the float. */
	ADDL	(IMMED(8), allocptr)	/* Allocate room for float. */
	fstp	REAL8 PTR 0 [esp]	/* ?? */
	ADDL	(IMMED(4),REG(esp))	/* discard copy of scalar */
	CONTINUE

END

/* end of X86.prim.masm (MS assembler) */
