Source to machdep/i386/fp_emul/fp_store.s


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

	.file "store.s"

	.ident	"@(#)kern-fp:store.s	1.1"

//$tt(*80387	emulator+++s	t	o	r	e+++*)
// ***************************************************************************
//
//			s t o r e . m o d 
//			==================
//
//	=============================================================
//               intel corporation proprietary information
//    this software  is  supplied  under  the  terms  of  a  license
//    agreement  or non-disclosure agreement  with intel corporation
//    and  may not be copied nor disclosed except in accordance with
//    the terms of that agreement.                                  
//	===============================================================
//
//	function:
//		implements 80387 store, fix, and storex instructions.
//
//	public procedures:
//		store	fix16	fix32	fix64	move_op_to_op
//
//	internal procedures:
//		extended_store		bcd_store
//		single_real_store	double_real_store
//		store_valid		int64_store
//		int16_store		int32_store
//
// ************************************************************************
//
//...december 12, 1986..
//
//$nolist
#include	"fp_e80387.h"
//$list
	.data	//a_msr	segment	rw	public
//	extrn	%gs:sr_masks,%gs:sr_flags,%gs:sr_errors,%gs:sr_controls
//a_msr	ends
//
//	assume	%ds:a_msr
//
	.text	//a_med	segment	er	public
//
//	extrn	pop_free,clear_6w,test_3w,subadd
//	extrn	gradual_underflow,right_shift_frac2_cl
//	extrn	special_round_test,directed_round_test
//	extrn	right_shift_result_cl,set_up_indefinite
//	extrn	right_shift_frac1_cl,sticky_right_shift
//	extrn	left_shift_frac1_cl,left_shift_result_cl
//	extrn	put_op1_result,divid,addition_normalize
//	extrn	round,get_precision,get_rnd_control
//	extrn	store_precision,set_i_error,i_masked_
//	extrn	set_o_error,o_masked_,set_p_error
//	extrn	set_u_error,test_4w,subtraction_normalize
//	extrn	p_error_,clear_p_error,add_to_frac
//	extrn	move_constant,set_stk_u_error,norm_denorm
//	extrn	%gs:sr_mem_offset
//	--------to be added for unix
//..		extrn	fpfulong:far,fpfushort:far,fpsulong:far,fpsushort:far
//
	.globl	store
	.globl	fix16
	.globl	fix32
	.globl	fix64
	.globl	move_op_to_op
//
store_routine:	
	.long	single_real_store,double_real_store,int16_store
	.long	int32_store,int64_store,extended_store
	.long	int16_store,int16_store,bcd_store
//
// *************************************************************************
//			store:
//			******
//	function:
//		implements all store instructions .
//
//	inputs:
//		operand1; format of result.
//
//	outputs:
//		operand in *result_format* stored in memory
//		destination pointed to by es:di.
//
//	data accessed:
//		- result_rec_offset	result_format
//		- offset_operand1
//
//	data changed:
//		- result record
//
//	procedures called:
//		set_up_indefinite	store routine (result format)
//		i_masked?
//
// ****************************************************************************
	ALIGN
store:	//proc
	jz	no_unmasked_stack_error		// branch if no stack error
	call	set_stk_u_error			// stack underflow occurred
	testb	invalid_mask,%gs:sr_masks	// if unmasked, just exit
	jz	return				// else, store indef set up
no_unmasked_stack_error:			// in operand1 by fetch_an_op
	andb	$~a_mask,%gs:sr_flags	// clear a-bit
get_result_loc:
	cmpb	$reg,result_location(%ebp)
	jne	store_to_memory				// branch if memop
	call	put_op1_result				// give opnd1 as result
	jmp	pop_free
	ALIGN
store_to_memory:
	movzbl	result_format(%ebp),%ebx// call a different rtn
	shll	$2,%ebx					// for each of the
	cmp	$8,%ebx
	jge	go_to_storer			// do store of temporary real
	cmpb	unsupp,tag1(%ebp)		// detect unsupported pattern
	jne	go_to_storer
	orb	invalid_mask,%gs:sr_errors	// set i_error
	testb	invalid_mask,%gs:sr_masks	// if unmasked, just exit
	jz	return					// else, set up indef in
	mov	$offset_operand1,%edi		// operand1. 
	call	set_up_indefinite
go_to_storer:
	jmp	*%cs:store_routine(%ebx)			// output formats
//store	endp
//
// ****************************************************************************
//			move_op_to_op:
//
//	function:
//		moves operand record (op1,op2,result_op,result2_op) to
//		another op.
//
//	inputs:
//		ss:esi points to the source, and
//		ss:edi points to the destination
//	outputs:
//		operand record in destination
//
//	data accessed:
//		operand or result variables
//
//	data changed:
//		operand or result variables
//
// *****************************************************************************
	ALIGN
move_op_to_op:	//proc
//	push	%ds			// save a?msr
//	push	%ss
//	pop	%ds
	push	%ss
	pop	%es
	mov	$((sign2-sign1)>> 2),%ecx / set record length

	/* FAST_MOVSL */
	push	%eax
1:
	movl	%ss:0(%esi),%eax
	movl	%eax,%es:0(%edi)
	addl	$4,%esi	
	addl	$4,%edi	
	decl	%ecx	
	testl	%ecx,%ecx
	jg	1b

	pop	%eax
//	pop	%ds			// reload a?msr
return:
	ret
//move_op_to_op	endp
//
// ***************************************************************************
//			extended_store:
//			***************
//	function:
//		stores into double extended format.
//
//	inputs:
//		operand1
//
//	outputs:
//		double extended operand in memory.
//		stack popped if indicated
//
//	data accessed:
//		- mem_operand_pointer		sign1
//		- expon1			word_frac1
//
//	data changed:
//
//	procedures called:
//		pop_free
//
// *****************************************************************************
	ALIGN
extended_store:	//proc
	les	mem_operand_pointer(%ebp),%edi	//move frac to memory
//	push	%ds				// save a?msr
//	push	%ss
//	pop	%ds				// set up source pointer
	lea	2+word_frac1,%esi
	
	/* FAST_MOVSL */
	movl	%ss:0(%esi),%ecx
	movl	%ecx,%es:0(%edi)
	movl	%ss:4(%esi),%ecx
	movl	%ecx,%es:4(%edi)
//	addl	$8,%esi
//	addl	$8,%edi
//	movl	$0,%ecx

//	pop	%ds						// reload a?msr
	movb	sign1(%ebp),%ah			// merge sign & exponent
	andw	$0x8000,%ax				// and move to memory
	orw	expon1(%ebp),%ax
	movw	%ax,%es:8(%edi)

//..
//..		pushad
//..		push	es
//..		push	edi
//..		push	ax
//..		call	fpsushort
//..		add	esp,12
//..		popad
//..		cld
	jmp	pop_free					// pop stack, if needed
//extended_store	endp
//
// ****************************************************************************
//			bcd_store:
//			**********
//	function:
//		stores into bcd format
//
//	inputs:
//		operand1
//
//	outputs:
//		bcd operand in memory; stack popped
//
//	data accessed:
//		- mem_operand_pointer		operation_type
//		- offset_operand1		sign1
//		- tag1				word_frac1
//		- msb_frac1			offset_operand1
//		- offset_operand2		sign2
//		- expon2			word_frac2
//		- offset_operand2		offset_result
//		- result_expon			result_word_frac
//		- msb_result			offset_result
//
//	data changed:
//		- operation_type		word_frac1
//		- msb_frac1			sign2
//		- expon2			word_frac2
//		- result_word_frac		msb_result
//
//	procedures called:
//		move_op_to_op			right_shift_frac1_cl
//		right_shift_frac2_cl	right_shift_result_cl
//		left_shift_frac1_cl		left_shift_result_cl
//		divid					clear_6w
//		pop_free				test_4w
//		add_to_frac				move_constant
//
// **************************************************************************
ten_18:	
	.value	0x0000,0x0000,0x0000,0x7640,0x6b3a,0x0de0b// frac for 10**18
//
	ALIGN
bcd_store:	//proc
	push	%gs:sr_masks				// save current precision
	orb	prec64,%gs:sr_controls			// and set to 64-bit
	cmpb	special,tag1(%ebp)			// separate bcd cases
	jne	finite_value_
	jmp	store_signed_bcd
	ALIGN
finite_value_:
	cmpl	$0x07fff,dword_expon1            // is it a finite number?
	jne	check_bcd_denorm			// yes, branch to valid
invalid_bcd:
	orb	invalid_mask,%gs:sr_errors	// set i_error
	testb	invalid_mask,%gs:sr_masks	// is i_error unmasked?
	jnz	store_bcd_indef
	jmp	bcd_store_done			// no, so exit at once
	ALIGN
store_bcd_indef:				// yes, store bcd_indef
	movl	$0, dword_frac1+frac80(%ebp)	// clear low frac
	movl	$0x80000000, dword_frac1+6(%ebp)// set only ms bit of hi frac
	movw	$0x0ffff, 8+word_frac1		//set msw to all ones,
	jmp	store_bcd_result		// and give as answer
	ALIGN
check_bcd_denorm:
	cmpb	denormd,tag1(%ebp)			// separate bcd cases
	jne	round_to_int
	movl	$0x0001,dword_expon1	// if masked d-error, make valid
	movb	valid,tag1(%ebp)
round_to_int:
	mov	$0x403e,%eax
	cmp	%eax,dword_expon1		// if expon >= 63, then number
	jge	invalid_bcd			// is already too big for bcd
	mov	$offset_operand1,%edi	// gradual uflow until expon=63
	push	%edi
	call	gradual_underflow
	pop	%edi				// round to precision 64
	movb	prec64,%dl
	movb	false,%al
	call	round
detect_zero:
	xor	%eax,%eax
	mov	$dword_frac1+4,%edi	// if fraction = 0, result = 0
	call	test_4w
	jnz	below_10_to_18_
	mov	%eax,dword_expon1		// set result to true zero
	movb	special,tag1(%ebp)
	jmp	store_bcd_round_data
	ALIGN
below_10_to_18_:
	mov	4(%ebp,%edi),%edx
	cmp	$0x0de0b6b3,%edx
	ja	invalid_bcd
	mov	(%ebp,%edi),%eax
	jb	divide_by_10_to_9
	cmp	$0x0a7640000,%eax
	jae	invalid_bcd
divide_by_10_to_9:
	movl	$0,4(%ebp,%edi)	// clear high dword of frac1
	movl	$0,(%ebp,%edi)// clear  low dword of 8 byte frac1 
	sub	$2,%edi			// make edi point to 10 byte frac1
	mov	$0x3b9aca00,%ebx		// ten to the 9th, as hex ordinal
	div	%ebx
	push	%eax						// save quotient
	mov	$4,%ecx
	mov	$100,%ebx
	FALLSTHRU
next_pair_nibbles:
	mov	%edx,%eax
	xor	%edx,%edx
	div	%ebx
	xchgl	%edx,%eax
	aam
second_half_loop_start:
	shlb	$4,%ah
	orb	%ah,%al
	movb	%al,(%ebp,%edi)
	inc	%edi
	LOOP(next_pair_nibbles)
do_second_half_:
	pop	%eax
	or	%eax,%eax					// is eax 0?
	movb	%dl,(%ebp,%edi)
	jz	store_bcd_round_data
	push	%edx
	xor	%edx,%edx
	mov	$10,%ecx
	div	%ecx
	xchgl	%edx,%eax
	pop	%ecx
	movb	%al, %ah
	movb	%cl, %al
	push	$0
	mov	$5, %ecx
	jmp	second_half_loop_start
	ALIGN
store_bcd_round_data:
	cmpb	true,rnd1_inexact
	jne	store_signed_bcd
	orb	$inexact_mask,%gs:sr_errors
	cmpb	true,added_one
	jne	store_signed_bcd
	orb	$a_mask,%gs:sr_flags
store_signed_bcd:
	movb	sign1(%ebp),%ah			// set sign in bcd number
	andb	$0x080,%ah
	movb	%ah,msb_frac1
store_bcd_result:
	les	%gs:sr_mem_offset,%edi	//move frac1 to memory
//	push	%ds				// save a?msr
//	push	%ss
//	pop	%ds
	lea	word_frac1,%esi

	/* FAST_MOVSL */
	movl	%ss:0(%esi),%ecx
	movl	%ecx,%es:0(%edi)
	movl	%ss:4(%esi),%ecx
	movl	%ecx,%es:4(%edi)
	movw	%ss:8(%esi),%cx
	movw	%cx,%es:8(%edi)
//	addl	$10,%esi
//	addl	$10,%edi
//	movl	$0,%ecx

//	pop	%ds					// reload a?msr
	call	pop_free
bcd_store_done:
	pop	%gs:sr_masks				// restore old setting
	ret
//bcd_store	endp
//
// *****************************************************************************
//			single_real_store:
//			******************
//	function:
//		implements store to single-precision real format
//
//	inputs:
//		operand1
//
//	outputs:
//		single_precision real in memory;
//		stack popped if indicated.
//
//	data accessed:
//		- mem_operand_pointer		offset_operand1
//		- sign1				tag1
//		- expon1			word_frac
//		- msb_frac1
//
//	data changed:
//
//	procedures called:
//		round				store_valid
//		addition_normalized		special_round_test
//		directed_round_test		gradual_underflow
//		pop_free			test_3w
//		u_masked?			set_o_error
//		o_masked?			set_p_error
//		set_u_error			set_i_masked?
//		store_denormd
//
// ************************************************************************
	ALIGN
single_real_store:	//proc
	push	$pop_free				// return to pop_free
	les	mem_operand_pointer(%ebp),%edi	//set es:di to memop
	mov	low_extended_expon_for_single,%esi
	movb	prec24,%dl 				// 24-bit precision
	movb	tag1(%ebp),%al			// load op1 tag
	cmpb	valid,%al
	je	single_valid				// detect valid, infinity,
	cmpb	denormd,%al				// denormd, invalid, and
	je	single_denormd				// special cases
	cmpb	inv,%al					// check whether op1 is nan
	jne	single_special				// branch unless op1 is nan
	testb	$0x40,msb_frac1				// check whether nan is quiet
	jnz	single_special				// branch if nan is quiet
	orb	invalid_mask,%gs:sr_errors	// set i_error for snan
	testb	invalid_mask,%gs:sr_masks	// if unmasked, just exit
	jz	special_return				// else, change to qnan
	orb	$0x40,msb_frac1
single_special:
	mov	dword_frac1+frac32+1(%ebp),%ecx
	and	$0x807fffff,%ecx
	movzbl	expon1(%ebp),%eax
	shl	$23,%eax
	or	%ecx,%eax
	mov	%eax,%es:(%edi)
//..
//..		pushad
//..		push	es
//..		push	edi
//..		push	eax
//..		call	fpsulong
//..		add		esp,	12
//..		popad
//..		cld
//..
special_return:
	ret
	ALIGN

single_denormd:
	mov	$offset_operand1,%edi	// op1 is denormal or pseudo-denormal
	call	norm_denorm		// dont set d_error; just normalize
single_valid:
	mov	high_extended_expon_for_single,%edi // load parameter
	call	store_valid
	les	mem_operand_pointer(%ebp),%edi	// es:edi --> memop
	jz	single_infinity		// zf = 1 => store infinity
	js	single_max			// sf = 1 => store max
	jc	single_qnan			// cf = 1 => store qnan
single_store:
	mov	dword_frac1+frac32+1(%ebp),%eax
	and	$0x80ffffff,%eax
	test	$0x00ffffff,%eax
	jz	single_store_it
	and	$0x807fffff,%eax
	movzbl	expon1(%ebp),%ecx
	addb	$0x80,%cl
	shl	$23,%ecx
	or	%ecx,%eax
single_store_it:
	mov	%eax,%es:(%edi)
//..
//..		pushad
//..		push	es
//..		push	edi
//..		push	eax
//..		call	fpsulong
//..		add	esp,12
//..		popad
//..		cld
//..
	ret					//do needed pop or free
	ALIGN

single_max:
	mov	$0x7fffff7f,%eax
	jmp	single_sign_on
	ALIGN
single_infinity:
	mov	$0x8000007f,%eax
single_sign_on:
	orb	sign1(%ebp),%al
	ror	$8,%eax
	jmp	single_store_it
	ALIGN
single_qnan:
	movl	$0xffffffff,%es:(%edi)
	pop	%eax				// skip pop or free
	ret					//   since this happens on error
	ALIGN

store_valid:
	push	%edi				// stack high_expon
	push	%esi				// stack low_expon
	push	%edx				// stack prec control
	movb	false,%al			// not second rounding
	mov	$offset_operand1,%edi	// round to prec (dl)
//	push	edi					; round preserves edi
	call	round
//		pop		edi
	call	addition_normalize		// and renormalize
	pop	%edx				// unstack prec control
	pop	%esi				// unstack low_expon
	pop	%edi				// unstack high_expon
	cmp	%esi,dword_expon1		// is underflow possible?
	jl	store_underflow_	// < low_expon, so underflow possible
	je	decrement_exponent		// no, = low_expon?
	cmp	%edi,dword_expon1		// no, > high_expon?
	jle	do_store_valid			// no, store number
	orb	overflow_mask,%gs:sr_errors	// set o_error
	testb	overflow_mask,%gs:sr_masks	// if unmasked, violate ieee standard
	jnz	masked_overflow		// and abort without rounding source
unmasked_ov_underflow:
//
// the commentized instructions immediately following are appropriate for
// unmasked over/underflow response in accord with the ieee standard.  on the
// 80387, unmasked over/underflows on conversion leave for the trap handler the
// source operand, unmodified by rounding, exponent rebiasing, or anything else.
// therefore, to report that preliminary rounding was inexact or achieved by
// increasing the source seems inconsistent with the 80387s philosophy
// of entering an over/underflow trap handler with the 80387s state changed
// only insofar as the over/underflow flag has been set, so skip that report.
//
//	cmp	rnd1_inexact,	true		; detect inexact result
//	jne	move_op1_to_87_tos  		; if exact, branch to replace source
//	or	%gs:sr_errors,	inexact_mask	; else, set i_error
//	cmp	added_one,	true		; was rounding done by adding one?
//	jne	move_op1_to_87_tos		; if not, branch to replace source
//	or	%gs:sr_flags,	a_mask		; else, set a_bit
//move_op1_to_87_tos:
//	mov	(ebp).result_location,	stack_top
//	pop	%edi		; discard callers return address
//	pop	%edi		; discard pop_free address
//	jmp	put_op1_result
//
// Instead, we return a special error condition (sf=0 zf=0 cf=1)
// to our caller, to cause a quiet NaN to be generated, as the 80387 does.
	movb	$1, %ah
	sahf
	ret
	ALIGN

masked_overflow:
	orb	$inexact_mask,%gs:sr_errors	// set inexact_error
	movb	%gs:sr_controls,%al			// load control byte
	andb	$rnd_control_mask,%al	// mask in rounding control
	movb	rnd_up,%ah
	cmpb	%al,%ah
	js	store_valid_done		// branch if zf = 0 & sf = 1
	movb	sign1(%ebp),%al				// get sign of op1
	call	special_round_test			// after round test, cf=0
	pushf				//  (zf=0 sf=1)/(zf=1 sf=0)
	js	pop_store_valid_done		// sf = 1 => store max
	orb	$a_mask,%gs:sr_flags			// set a_bit
pop_store_valid_done:
	popf						// reload flags
store_valid_done:
	ret									// exit
	ALIGN
store_underflow_:
	testb	underflow_mask,%gs:sr_masks	// if underflow masked,
	jnz	check_masked_underflow	// then branch to check for inexact
	orb	underflow_mask,%gs:sr_errors// else, set u_error
//
// after the underflow exception has been set, the response to unmasked
// underflow ought to be the same as that to unmasked overflow.
	jmp	unmasked_ov_underflow
	ALIGN

check_masked_underflow:
	mov	$offset_operand1,%edi		// load op1 offset
	mov	%esi,%eax				// load low exponent
	push	%edi					// save op1 offset
	push	%edx					// save prec control
	call	gradual_underflow			// do gradual underflow
	pop	%edx					// reload prec control
	pop	%edi					// reload op1 offset
	movb	true,%al				// perform second round
	call	round
	cmpw	$0,rnd_history			// signal_u_error?
	je	decrement_exponent
	orb	underflow_mask+inexact_mask,%gs:sr_errors
decrement_exponent:
	testb	$0x80,msb_frac1				// test msb of fraction
	jnz	do_store_valid		// dont decrement if 800..0h
	decw	expon1(%ebp)				// else, decrement expon
do_store_valid:
	cmpw	$0,rnd_history			// signal_p_error?
	je	adjust_flags
	orb	$inexact_mask,%gs:sr_errors	// signal_p_error.
	cmpb	true,added_one		// was rounding done by adding one?
	jne	adjust_flags		// if not, branch to adjust flags
	orb	$a_mask,%gs:sr_flags		// else, set a_bit
adjust_flags:
	xor	%eax,%eax		// cf = 0, sf = 0, zf = 0 for
	inc	%eax			//  store valid exit
	ret

//single_real_store	endp
//
// ************************************************************************
//			double_real_store:
//			******************
//	function:
//		implements store to double-precision real format
//
//	inputs:
//		operand1
//
//	outputs:
//		double precision real in memory;
//		stack popped if indicated.
//
//	data accessed:
//		- mem_operand_pointer		offset_operand1
//		- sign1				tag1
//		- expon1			word_frac1
//		- msb_frac1			offset_operand1
//
//	data changed:
//		- sign1				word_frac1
//
//	procedures called:
//		test_3w				store_valid
//		right_shift_frac1_cl		pop_free
//		double_zero			double_fill
//		store_denormd			double_frac_mem
//
// ***************************************************************************
	ALIGN
double_real_store:	//proc
	push	$pop_free				// return to pop_free
	mov	low_extended_expon_for_double,%esi
	movb	prec53,%dl 				// 53-bit precision
	movb	tag1(%ebp),%al			// separate special cases
	cmpb	valid,%al
	je	double_valid
	cmpb	denormd,%al
	je	double_denormd
	cmpb	special,%al
	jne	inv_or_inf
	movw	$0x3c00,expon1(%ebp)
	jmp	double_frac_mem
	ALIGN
inv_or_inf:
	cmpb	inv,%al
	jne	double_special
	testb	$0x40,msb_frac1			// check whether nan is quiet
	jnz	double_special				// branch if nan is quiet
	orb	invalid_mask,%gs:sr_errors	// set i_error for snan
	testb	invalid_mask,%gs:sr_masks	// if unmasked, just exit
	jnz	make_dbl_qnan				// else, change to qnan
dbl_special_return:
	ret
	ALIGN
make_dbl_qnan:
	orb	$0x40,msb_frac1
double_special:
	movw	$0x3c00+0x07ff,expon1(%ebp)
	jmp	double_frac_mem			// store frac to memory
	ALIGN
double_denormd:
	mov	$offset_operand1,%edi	// op1 is denormal or pseudo-denormal
	call	norm_denorm		// dont set d_error; just normalize
double_valid:
	mov	high_extended_expon_for_double,%edi
	call	store_valid
	jz	double_infinity			// zf = 1 ==> store infinity
	js	double_max				// sf = 1 ==> store max
	jc	double_qnan				// cf = 1 ==> store qnan
double_store:
	movb	3+lsb_frac1,%al	// else store double
	cbtw
	mov	$dword_frac1+6,%edi
	call	test_3w
	jz	double_zero				// store signed zero
double_frac_mem:
	movb	$3,%cl					// start by shifting
	call	right_shift_frac1_cl		// fraction right 3 bits
	les	mem_operand_pointer(%ebp),%edi	// es:edi points to memop
//	push	%ds				// save a?msr
//	push	%ss
//	pop	%ds				// set up source pointer
	lea	3+word_frac1,%esi		// move frac to memory

	/* FAST_MOVSL */
	movl	%ss:0(%esi),%ecx
	movl	%ecx,%es:0(%edi)
	movw	%ss:4(%esi),%ecx
	movw	%ecx,%es:4(%edi)
//	addl	$6,%esi
//	addl	$6,%edi
//	movl	$0,%ecx

//	pop	%ds					// reload a?msr
	movw	expon1(%ebp),%ax		// fetch high-order word,
	subw	$0x3c00,%ax				// with expon, sign, and
	shlw	$4,%ax				// top 4 bits of fraction
	andb	$0x0f,msb_frac1
	orb	msb_frac1,%al
	andb	$0x80,sign1(%ebp)
	orb	sign1(%ebp),%ah
	movw	%ax,%es:6(%edi)				// es:di points to expon
//..
//..		pushad
//..		push	es
//..		push	edi
//..		push	ax
//..		call	fpsushort
//..		add	esp,12
//..		popad
//..		cld
//..

	ret					// do pop or free
	ALIGN

double_zero:
	xor	%eax,%eax			// store +/- 0 to memory
double_fill:
	les	mem_operand_pointer(%ebp),%edi	// es:edi points to memop
	mov	%eax,%es:(%edi)
//..
//..		pushad
//..		push	es
//..		push	edi
//..		push	eax
//..		call	fpsulong
//..		add	esp,12
//..

	movw	%ax,%es:4(%edi)
//..
//..		push	es
//..		push	edi+4
//..		push	ax
//..		call	fpsushort
//..		add	esp,12
//..		popad
//..		cld
//..
	movb	sign1(%ebp),%ah
	andb	$0x80,%ah
double_expon:
	movw	%ax,%es:6(%edi)			// store the exponent
//..
//..		pushad
//..		push	es
//..		push	edi+12
//..		push	ax
//..		call	fpsushort
//..		add	esp,12
//..		popad
//..		cld
//..
	ret
	ALIGN

double_max:
	mov	$0x0ffffffff,%eax			// set fraction to ff..f
	call	double_fill
	movw	$0x7fef,%ax				// set exponent to 7feh
	orb	sign1(%ebp),%ah			// or sign bit
	jmp	double_expon
	ALIGN
double_infinity:
	call	double_zero			// first clear the memop
	orw	$0x7ff0,%es:6(%edi)	// es:di -> memop
//..
//..		pushad
//..		push	es
//..		push	edi+6
//..		call	fpfushort
//..		or	ax,7ff0h
//..		push	ax
//..		call	fpsushort
//..		add	esp,12
//..		popad
//..		cld
//..
	ret
	ALIGN
double_qnan:
	mov	$0xffffffff,%eax		// set fraction to ff..f
	call	double_fill
	movw	$0xffff,%es:6(%edi)	// es:di -> memop (set exp to ff..f)
	pop	%eax				// skip pop or free
	ret					//   since this happens on error
//double_real_store	endp
//
// ***********************************************************************
//		fix16:
//
//	function:
//		 fixes valid number to a 16-bit integer.
//
//	inputs:
//		 operand1
//
//	outputs:
//		16-bit integer in word_frac1 + 8;
//		overflow indication in zf (zf:1 => no overflow)
//
//	data accessed:
//		- offset_operand1		sign1
//		- expon1			word_frac1
//		- offset_operand1
//
//	data changed:
//		- word_frac1			extra_word_reg
//
//	procedures called:
//		sticky_right_shift		round
//		careful_round			p_error?
//
// ***************************************************************************
	ALIGN
fix16:	//proc
	mov	high_int16_exponent,%ecx		// calc shift amount
	sub	dword_expon1,%ecx			// if expon1 > high expon
	js	no_fix16_ovf			// then overflow (zf=0)
	cmp	max_int16_shift,%ecx			// dont let shift amount
	jbe	do_shift_16			// exceed max_int16_shift
	mov	max_int16_shift,%ecx
do_shift_16:
	mov	$offset_operand1,%edi		// shift frac1 by cl
	xorb	%al,%al
	call	sticky_right_shift
	mov	$offset_operand1,%edi		// round to 16-bit prec
	movb	prec16,%dl
	movb	false,%al
	call	round
	cmpb	false,%al				// overflow if al=true
	jnz	no_fix16_ovf				// round cy out (zf=1)
	cmpw	$0x8000,8+word_frac1
	jb	check_sign_16				// no overflow if < 8000h
	ja	no_fix16_ovf				// else, overflow (zf=0)
	cmpb	negative,sign1(%ebp)	// oflow if = 8000h and
	jnz	no_fix16_ovf				// sign1 = pos (zf=0)
check_sign_16:
	movb	positive,%al			// al is overflow flag
	cmpb	sign1(%ebp),%al			// negate integer if
	jz	no_fix16_ovf				// sign is neg (zf=1)
	negw	8+word_frac1
	xorb	%al,%al					// no overflow (zf=1)
no_fix16_ovf:
	ret
//fix16	endp
//
// ***********************************************************************
//			int16_store:
//
//	function:
//		stores into 16-bit integer format
//
//	inputs:
//		operand1
//
//	outputs:
//		16-bit integer in memory operand location
//
//	data accessed:
//		- mem_operand_pointer		tag1
//		- word_frac1			msb_frac1
//		- offset_operand1		extra_word_reg
//
//	data changed:
//
//	procedures called:
//		fix16				pop_free
//		invalid_or_special		get_rnd_control
//		set_i_error			i_masked?
//		set_p_error			clear_p_error
//
// ************************************************************************
	ALIGN
int16_store:	//proc
//
	cmpb	valid,tag1(%ebp)
	je	valid_16
	cmpb	denormd,tag1(%ebp)
	jne	special_16
valid_16:
	call	fix16				// make op1 a 16-bit int
	jnz	special_16			// if zf=0, oflow on fix
	cmpw	$0,rnd_history
	je	store_16
	orb	$inexact_mask,%gs:sr_errors
	cmpb	true,added_one
	jne	store_16
	orb	$a_mask,%gs:sr_flags
store_16:
	movw	8+word_frac1,%ax	// move integer to memory
pointer_16:
	les	mem_operand_pointer(%ebp),%edi	//set es:di to location
finish_16:
	movw	%ax,%es:(%edi)		// store integer to mem
//..
//..		pushad
//..		push	es
//..		push	edi
//..		push	ax
//..		call	fpsushort
//..		add	esp,12
//..		popad
//..		cld
//..
	jmp	pop_free			// pop stack and go home
	ALIGN
special_16:
	xorw	%ax,%ax
	cmpb	special,tag1(%ebp)		// is operand tagged special?
	je	pointer_16				// yes, load memop ptr and exit
	orb	invalid_mask,%gs:sr_errors// no, set invalid error
	movw	$0x8000,%ax			// load inv constant into ax
	testb	invalid_mask,%gs:sr_masks// is invalid error masked?
	jnz	pointer_16				// yes, store invalid constant
	ret
//int16_store	endp
//
// ***********************************************************************
//		fix32:
//
//	function:
//		 fixes valid number to a 32-bit integer.
//
//	inputs:
//		 operand1
//
//	outputs:
//		32-bit integer in word_frac1 + 6;
//		overflow indication in zf (zf:1 => no overflow)
//
//	data accessed:
//		- offset_operand1		sign1
//		- dword_expon1			word_frac1
//		- offset_operand1
//
//	data changed:
//		- word_frac1
//
//	procedures called:
//		sticky_right_shift		round
//
// ***************************************************************************
	ALIGN
fix32:	//proc
	mov	high_int32_exponent,%ecx		// calc shift amount
	sub	dword_expon1,%ecx			// if expon1 > high expon
	js	no_fix32_ovf					// then overflow (zf=0)
	cmp	max_int32_shift,%ecx			// dont let shift amount
	jbe	do_shift_32						// exceed max_int32_shift
	mov	max_int32_shift,%ecx
do_shift_32:
	mov	$offset_operand1,%edi		// shift frac1 by cl
	xorb	%al,%al
	call	sticky_right_shift
	mov	$offset_operand1,%edi		// round to 16-bit prec
	movb	prec32,%dl
	movb	false,%al
	call	round
	cmpb	false,%al				// overflow if al=true
	jnz	no_fix32_ovf				// round cy out (zf=1)
	cmpl	$0x080000000,dword_frac1+frac32(%ebp)
	jb	check_sign_32				// no overflow if < 80000000h
	ja	no_fix32_ovf				// else, overflow (zf=0)
	cmpb	negative,sign1(%ebp)	// oflow if = 80000000h and
	jnz	no_fix32_ovf				// sign1 = pos (zf=0)
check_sign_32:
	movb	positive,%al			// al is overflow flag
	cmpb	sign1(%ebp),%al			// negate integer if
	jz	no_fix32_ovf				// sign is neg (zf=1)
	negl	dword_frac1+frac32(%ebp)
	xorb	%al,%al					// no overflow (zf=1)
no_fix32_ovf:
	ret
//fix32	endp
//
// **************************************************************************
//		int32_store:
//
//	function:
//		stores into 32-bit integer format
//
//	input:
//		operand1
//
//	output:
//		32-bit integer in location pointed to by es:di.
//
//	data accessed:
//		- mem_operand_pointer		offset_operand1
//		- sign1				tag1
//		- expon1			word_frac1
//		- msb_frac1			offset_operand1
//
//	data changed:
//		- word_frac1
//
//	procedures called:
//		pop_free
//		fix32
//
// **************************************************************************
	ALIGN
int32_store:	//proc
	cmpb	valid,tag1(%ebp)
	je	valid_32
	cmpb	denormd,tag1(%ebp)
	jne	special_32
valid_32:
	call	fix32				// make op1 a 32-bit int
	jnz	special_32			// if zf=0, oflow on fix
	cmpw	$0,rnd_history
	je	store_32
	orb	$inexact_mask,%gs:sr_errors
	cmpb	true,added_one
	jne	store_32
	orb	$a_mask,%gs:sr_flags
store_32:
	mov	dword_frac1+frac32(%ebp),%eax	// move integer to eax
pointer_32:
	les	mem_operand_pointer(%ebp),%edi	//set es:di to location
finish_32:
	mov	%eax,%es:(%edi)		// store integer to mem
//..
//..		pushad
//..		push	es
//..		push	edi
//..		push	eax
//..		call	fpsulong
//..		add	esp,12
//..		popad
//..		cld
//..
	jmp	pop_free			// pop stack and go home
	ALIGN
special_32:
	xor	%eax,%eax
	cmpb	special,tag1(%ebp)		// is operand tagged special?
	je	pointer_32				// yes, load memop ptr and exit
	orb	invalid_mask,%gs:sr_errors// no, set invalid error
	mov	$0x080000000,%eax	// load inv constant into ax
	testb	invalid_mask,%gs:sr_masks// is invalid error masked?
	jnz	pointer_32				// yes, store invalid constant
	ret
//int32_store	endp
//
// ***********************************************************************
//		fix64:
//
//	function:
//		 fixes valid number to a 64-bit integer.
//
//	inputs:
//		 operand1
//
//	outputs:
//		64-bit integer in word_frac1 + 2;
//		overflow indication in zf (zf:1 => no overflow)
//
//	data accessed:
//		- offset_operand1		sign1
//		- dword_expon1			word_frac1
//		- offset_operand1
//
//	data changed:
//		- word_frac1
//
//	procedures called:
//		sticky_right_shift		round
//
// ***************************************************************************
	ALIGN
fix64:	//proc
	mov	high_int64_exponent,%ecx		// calc shift amount
	sub	dword_expon1,%ecx			// if expon1 > high expon
	js	no_fix64_ovf				// then overflow (zf=0)
	cmp	max_int64_shift,%ecx			// dont let shift amount
	jbe	do_shift_64				// exceed max_int64_shift
	mov	max_int64_shift,%ecx
do_shift_64:
	mov	$offset_operand1,%edi		// shift frac1 by cl
	xorb	%al,%al
	call	sticky_right_shift
	mov	$offset_operand1,%edi		// round to 64-bit prec
	movb	prec64,%dl
	movb	false,%al
	call	round
	cmpb	false,%al				// overflow if al=true
	jnz	no_fix64_ovf				// round cy out (zf=1)
	cmpl	$0x080000000,dword_frac1+frac32(%ebp)
	jb	check_sign_64			// no overflow if < 80000000h
	ja	no_fix64_ovf				// else, overflow (zf=0)
	cmp	$0,dword_frac1+frac64(%ebp)
	jnz	no_fix64_ovf
	cmpb	negative,sign1(%ebp)	// oflow if = 800..0h and
	jnz	no_fix64_ovf				// sign1 = pos (zf=0)
check_sign_64:
	movb	positive,%al			// al is overflow flag
	cmpb	sign1(%ebp),%al			// negate integer if
	jz	no_fix64_ovf				// sign is neg (zf=1)
	xor	%eax,%eax
	negl	dword_frac1+frac64(%ebp)
	sbb	dword_frac1+frac32(%ebp),%eax
	mov	%eax,dword_frac1+frac32(%ebp)
	xorb	%al,%al					// no overflow (zf=1)
no_fix64_ovf:
	ret
//fix64	endp
//
// *****************************************************************************
//			int64_store:
//			************
//	function:
//		stores into 64-bit integer format
//
//	input:
//		operand1
//
//	output:
//		64-bit integer in memory location pointed to by es:edi.
//
//	data accessed:
//		- mem_operand_pointer		offset_operand1
//		- sign1				tag1
//		- expon1			word_frac1
//		- msb_frac1			offset_operand1
//
//	data changed:
//		- word_frac1
//
//	procedures called:
//		pop_free
//
// ***************************************************************************
	ALIGN
int64_store:	//proc
	cmpb	valid,tag1(%ebp)
	je	valid_64
	cmpb	denormd,tag1(%ebp)
	jne	special_64
valid_64:
	call	fix64				// make op1 a 64-bit int
	jnz	special_64			// if zf=0, oflow on fix
	cmpw	$0,rnd_history
	je	store_64
	orb	$inexact_mask,%gs:sr_errors
	cmpb	true,added_one
	jne	store_64
	orb	$a_mask,%gs:sr_flags
store_64:
	mov	dword_frac1+frac64(%ebp),%eax	// move integer to edx:eax
	mov	dword_frac1+frac32(%ebp),%edx	// move integer to edx:eax
pointer_64:
	les	mem_operand_pointer(%ebp),%edi	//set es:edi to location
finish_64:
	mov	%eax,%es:(%edi)		// store low integer to low mem
//..
//..		pushad
//..		push	es
//..		push	edi
//..		push	eax
//..		call	fpsulong
//..		add	esp,12
//..		popad
//..		cld
//..
	mov	%edx,%es:4(%edi)		// store high integer to high mem
//..
//..		pushad
//..		push	es
//..		push	edi
//..		push	edx
//..		call	fpsulong
//..		add	esp,12
//..		popad
//..		cld
//..
	jmp	pop_free			// pop stack and go home
	ALIGN
special_64:
	xor	%eax,%eax
	cltd
	cmpb	special,tag1(%ebp)		// is operand tagged special?
	je	pointer_64				// yes, load memop ptr and exit
	orb	invalid_mask,%gs:sr_errors// no, set invalid error
	mov	$0x080000000,%edx	// load inv constant into ax
	testb	invalid_mask,%gs:sr_masks// is invalid error masked?
	jnz	pointer_64		// yes, store invalid constant
	ret

//int64_store	endp
//
//a_med	ends
//
//	end