Source to machdep/i386/fp_emul/fp_reg.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	"reg.s"

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

// ****************************************************************************
//
//			r e g . 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:
//		operation cluster for the 80387 register stack.
//
//	public procedures:
//		put_result	stack_full?	float_int16
//		save_regs	restore_regs	set_up_indefinite
//		getx		fetch_an_op	exchange	exam
//		fxtrac		abs_value	chsign		put_op1_result
//		put_si_result	do_exchange
//
//	internal procedures:
//		extend_single	extend_double	get_bcd		fetch_an_op
//		float16		float32		float64
//
// *****************************************************************************
//
//...March 3, 1987...
//
//$nolist
#include	"fp_e80387.h"
//$list
//...declare status register segment...
//
	.data	//a_msr	segment	rw	public
//	extrn	%gs:sr_masks,%gs:sr_errors,%gs:sr_flags,%gs:sr_regstack
//a_msr	ends
//
//	assume	%ds:a_msr
//
	.text	//a_med	segment	er	public
//
//	extrn	store_reg_tag,decr_top,left_shift
//	extrn	subtraction_normalize,clear_6w,test_3w
//	extrn	move_op_to_result,save_status,set_z_bit
//	extrn	restore_status,init,test_4w,test_6w
//	extrn	left_shift_frac2_cl,left_shift_frac1_1
//	extrn	left_shift_result_cl,get_top,set_i_error
//	extrn	get_reg_tag,set_s_bit,set_d_error
//	extrn	i_masked_,clear_c_bit,clear_cond_bits
//	extrn	set_c_bit,set_a_bit,set_i_masked_
//	extrn	move_op_to_op,set_stk_u_error,set_stk_o_error
//	extrn	norm_denorm,correct_tag_word
//	--------to be added for unix
//..		extrn	fpfulong:far,fpfushort:far,fpsulong:far,fpsushort:far
//
	.globl	getx
	.globl	fetch_an_op
	.globl	put_result
	.globl	stack_full_
	.globl	fxtrac
	.globl	float_int16
	.globl	save
	.globl	save_regs
	.globl	restore_regs
	.globl	restore
	.globl	set_up_indefinite
	.globl	exchange
	.globl	exam
	.globl	decompose
	.globl	load
	.globl	abs_value
	.globl	chsign
	.globl	put_op1_result
	.globl	do_exchange
	.globl	put_si_result
	.globl	do_exchange_leave_a_bit
//
// *****************************************************************************
//			extend_single:
//			**************
//	function:
//		retrieves a single real operand from memory,
//		extends its parts, and puts them in the operand
//
//	inputs:
//		assumes mem_operand_pointer set up
//		edi points to operand1 or operand2
//
//	outputs:
//		extended operand in operand
//
//	data accessed:
//		- mem_operand_pointer
//
//	data changed:
//		- operands
//
//	procedures called:
//		set_d_error
//
// ***************************************************************************
	ALIGN
extend_single:	//proc
	les	mem_operand_pointer(%ebp),%ebx
	xor	%eax,%eax
	mov	%eax,frac64(%ebp,%edi) 		//clear low 5 bytes of
	movb	%al,frac32(%ebp,%edi)	//fraction to 0
//	---- replace following line for unix
	mov	%es:(%ebx),%eax				// move 3 fraction

//..		
//..		pushad
//..		push	es
//..		push	ebx
//..		call	fpfulong
//..		add	esp,8
//..		popad
//..		cld
//..
	mov	%eax,frac32+1(%ebp,%edi)	// bytes from memory
	sar	$23,%eax		// get short real exponent in al
	movb	%ah,sign(%ebp,%edi) 	// transfer sign to operand
	and	$0x000000ff,%eax	// strip sign bits (here, b8-b31)
	movl	$0,before_error_signals(%ebp)	// initialize all to false
	jnz	single_invalid_
	movb	frac64+7(%ebp,%edi),%al	// zero or denormalized?
	orw	frac64+5(%ebp,%edi),%ax
	jnz	single_denormalized
zero_return:
	movb	special,tag(%ebp,%edi)	// set tag to special,
	mov	%eax,expon(%ebp,%edi)			// expon to 0
	ret
	ALIGN
single_denormalized:
	movb	denormd,tag(%ebp,%edi)	// set tag to denormd
	movb	true,signal_d_error_
	testb	denorm_mask,%gs:sr_masks		// short cut for unmasked d-error
	jnz	sngl_d_error_masked
	ret
	ALIGN
sngl_d_error_masked:
	movl	single_exp_offset+1,expon(%ebp,%edi)
	bsrl	frac32(%ebp,%edi),%ecx
	sub	$31,%ecx
	neg	%ecx
	shll	%cl,frac32(%ebp,%edi)
	sub	%ecx,expon(%ebp,%edi)	// adjust exponent
	ret
	ALIGN
single_invalid_:
	cmpb	true,%al
	jne	single_operand_is_valid
	movl	$0x7fff,expon(%ebp,%edi)	//operand is inv/inf
	testl	$0x7fffffff,frac32(%ebp,%edi)
	movb	infinty,tag(%ebp,%edi)
	jz	sngl_exit
	movb	inv,tag(%ebp,%edi)		//invalid operand
	testb	$0x40,msb(%ebp,%edi)
	jnz	sngl_qnan
	movb	true,signal_i_error_
sngl_qnan:
sngl_exit:
	ret													//set tag to invalid
	ALIGN
single_operand_is_valid:
	movb	valid,tag(%ebp,%edi)		//set tag to valid
	add	single_exp_offset,%eax		//single_exp_offset,
	mov	%eax,expon(%ebp,%edi)		//implicit bit to 1
	orb	$0x80,frac64+7(%ebp,%edi)
	ret
	ALIGN
//extend_single	endp
//
// ***********************************************************************
//			extend_double:
//			**************
//	function:
//		retrieves a double real operand from memory,
//		extends its parts, and puts them in the operand
//
//	inputs:
//		assumes mem_operand_pointer is set up
//		edi points to operand1 or operand2
//
//	outputs:
//		expanded operand in operand
//
//	data accessed:
//		- mem_operand_pointer
//
//	data changed:
//		- operand
//
//	procedures called:
//
//
// *********************************************************************
	ALIGN
extend_double:	//proc
//	push	%ds											// save a?msr
	les	mem_operand_pointer(%ebp),%ebx
	movb	$0,frac64(%ebp,%edi)			//clear low byte of
	mov	%es:(%ebx),%eax		//fraction to 0

//..
//..		pushad
//..		push	ds
//..		push	ebx
//..		call	fpfulong
//..		pop		ebx
//..
	mov	%eax,frac64+1(%ebp,%edi)		//move 7 fraction bytes
	mov	%es:4(%ebx),%eax		// from memory
//..
//..		push	ebx+4
//..		call	fpfulong
//..		add		esp,	8
//..		popad
//..		cld

//	pop	%ds									// reload a?msr
	mov	%eax,frac32+1(%ebp,%edi)
	sar	$20,%eax
	cwtd
	movb	%dl,sign(%ebp,%edi) 	// transfer sign to operand
	and	$0x000007ff,%eax	// strip sign bits (here,
					// b11 - b31)
	movl	$0,before_error_signals(%ebp)	// initialize both to false
	jnz	double_invalid_
	mov	frac64(%ebp,%edi),%eax		// exp=0 : is fraction 0?
	or	frac32(%ebp,%edi),%eax
	jnz	double_denormalized
	jmp	zero_return
	ALIGN
double_denormalized:
	movb	denormd,tag(%ebp,%edi)	// set tag to denormd
	movb	true,signal_d_error_
	testb	denorm_mask,%gs:sr_masks		// short cut for unmasked d-error
	jnz	dubl_d_error_masked
	ret
	ALIGN
dubl_d_error_masked:
	movl	double_exp_offset+1+3,expon(%ebp,%edi)
	bsrl	frac32(%ebp,%edi),%ecx
	jz	top_28_zero
	mov	frac64(%ebp,%edi),%eax
	sub	$31,%ecx
	neg	%ecx
	shldl	%cl,%eax,frac32(%ebp,%edi)
	shll	%cl,frac64(%ebp,%edi)
dubl_denorm_expon:
	sub	%ecx,expon(%ebp,%edi)	// adjust exponent
	ret
	ALIGN
top_28_zero:
	mov	frac64(%ebp,%edi),%eax
	bsrl	%eax,%ecx
	sub	$31,%ecx
	neg	%ecx
	shll	%cl,%eax
	mov	%eax,frac32(%ebp,%edi)
	movl	$0,frac64(%ebp,%edi)
	add	$32,%ecx
	jmp	dubl_denorm_expon
	ALIGN
double_invalid_:
	cmpw	$0x07ff,%ax
	jne	double_operand_is_valid
	movl	$0x7fff,expon(%ebp,%edi)	  // operand is nan or
	andl	$0x0fffffff,frac32(%ebp,%edi) // inf. set exponent.
	mov	frac32(%ebp,%edi),%eax	  // mask expon bits
	or	frac64(%ebp,%edi),%eax
	movb	infinty,tag(%ebp,%edi)
	jz	dubl_msb_set
	movb	inv,tag(%ebp,%edi)		// invalid operand
	testb	$0x08,msb(%ebp,%edi)
	jnz	dubl_qnan
	movb	true,signal_i_error_
	testb	invalid_mask,%gs:sr_masks
	jz	dubl_exit
dubl_qnan:
	jmp	norm_dubl_sgnfcnd
	ALIGN
double_operand_is_valid:
	add	double_exp_offset,%eax		// double_exp_offset
	mov	%eax,expon(%ebp,%edi)
	movb	valid,tag(%ebp,%edi)		// set operand tag
norm_dubl_sgnfcnd:
	mov	frac64(%ebp,%edi),%eax
	.byte	0x0f,0xa4,0x44,0x3d,0x08,0x03  //implements following inst.
//	shldl	$3,%eax,frac32(%ebp,%edi)
	shll	$3,frac64(%ebp,%edi)
dubl_msb_set:
	orb	$0x80,frac64+7(%ebp,%edi)	//set leading bit
dubl_exit:
	ret
//extend_double	endp
//
// ****************************************************************************
//			getx:
//			*****
//	function:
//		fetches extended-format fp number from memory, unpacks it,
//		and sets its tag
//
//	inputs:
//		assumes mem_operand_pointer is set up
//		edi points to operand1 or operand2
//
//	outputs:
//		if number is valid, then exponent and msb set to all ones
//
//	data accessed:
//		- mem_operand_pointer		extra_word_reg
//
//	data changed:
//		- operand			extra_word_reg
//
//	procedures called:
//		test_4w				test_3w
//		set_d_error
//
// ****************************************************************************
	ALIGN
getx:	//proc
//	push	%ds									// save a?msr
	les	mem_operand_pointer(%ebp),%esi	// ds:esi ==> memop
//
getx_stackreg:
//	push	%ss
//	pop	%es			// es:edi ==> operand

	add	$frac64,%edi		// bump di to frac64
	push	%edi			// save operand offset
	lea	(%ebp,%edi),%edi
//	mov	$0x0002,%ecx		// move first four words
	/* FAST_MOVSL */
	
	movl	%es:0(%esi),%ecx
	movl	%ecx,%ss:0(%edi)
	movl	%es:4(%esi),%ecx
	movl	%ecx,%ss:4(%edi)
//	addl	$8,%esi
//	movl	$0,%ecx

	movw	%es:8(%esi),%ax			// get sign/expon word
//..		pushad
//..		push	ds
//..rep_loop:	
//..		push	esi
//..		call	fpfulong
//..		pop	esi
//..		stosd
//..		add	esi,4
//..		loop	rep_loop
//..		push	esi
//..		call	fpfushort
//..		add	esp,8
//..		popad
//..		cld
//..
	pop	%edi		// reload operand offset
//	pop	%ds		// reload a?msr
	cwtd			// transmit sign to dx
	movb	%dl,sign-frac64(%ebp,%edi)// and to operand
	and	$0x7fff,%eax		// strip off sign bit
	mov	%eax,expon-frac64(%ebp,%edi)		// store to operand
	movl	$0,before_error_signals(%ebp)	// initialize both to false
	jz	expon_zero			// branch if exponent 0
	testb	$0x80,msb-frac64(%ebp,%edi)
	jnz	max_expon_
	movb	unsupp,%dl				// we have an
	jmp	getx_set_tag			// unsupported format
	ALIGN
max_expon_:
	cmpw	$0x7fff,%ax			// check for invalid
	je	not_validx			// or infinity
	movb	valid,%dl		 	// operand is valid
getx_set_tag:
	movb	%dl,tag-frac64(%ebp,%edi) // set operand tag
	xor	%eax,%eax			// (for stackreg return)
	ret
	ALIGN
not_validx:
	movb	infinty,%dl
	mov	frac32-frac64(%ebp,%edi),%eax
	and	$0x07fffffff,%eax
	or	(%ebp,%edi),%eax
	jz	getx_set_tag
	movb	inv,%dl				// set tag to invalid
	jmp	getx_set_tag
	ALIGN
expon_zero:
	mov	frac32-frac64(%ebp,%edi),%eax
	or	(%ebp,%edi),%eax
	movb	special,%dl			// set tag to special
	jz	getx_set_tag			// if number is +/- 0
	movb	denormd,%dl			// set tag to denormd
	jmp	getx_set_tag
	ALIGN
//getx	endp
//
// ****************************************************************************
//			fetch_an_op:
//			***********
//	function:
//		fetches one operand(op1 or op2) and puts its parts
// 		in operand1 or operand2
//
//	inputs:
//		location of 	op info in esi
//		location of the operand in edi
//
//	outputs:
//		operand parts in operand1 or operand2
//
//	procedures called:
//		mem_fetch_routine		set_up_indefinite
//		get_top
//		getx_stackreg
//
// ***************************************************************************
fetch_location:	
	.long	from_memory,from_stack_top,from_stack_minus_1
	.long	from_stack_plus_1,from_reg
mem_fetch_routine:	
	.long	extend_single,extend_double,float16,float32
	.long	float64,getx,float16,float16,get_bcd
	ALIGN
fetch_an_op:	//proc
	cmpb	$null,format(%ebp,%esi)// if format = null,
	je	no_stack_error_on_fetch		// then no operand to get
	xor	%ebx,%ebx
	mov	%ebx,(%ebp,%edi)		// set low order dword of
	movb	location(%ebp,%esi),%bl	// fraction to zero
	shl	$2,%ebx				// handle each operand
	jmp	*%cs:fetch_location(%ebx)		// location case separate
	ALIGN
from_memory:
	movb	format(%ebp,%esi),%bl	// form table offset
	shl	$2,%ebx
	call	*%cs:mem_fetch_routine(%ebx)	// call routine to fetch
no_stack_error_on_fetch:
	xor	%eax,%eax			// eax = 00000000h
	ret
	ALIGN
from_stack_top:
	call	get_top			// get reg num in al
unpack_reg:
	call	reg_full_		// zf=1 (empty) cx=top
	jz	from_stack_plus_1	// error if reg empty
	mov	%ebx,%esi		// reg_full returns ebx as pointer
					// to stackoffset =
					//  (10*regnum) + offset regstack
					// thus, ds:esi ==> regstack
//
//	push	%ds			// stack a copy of a?msr
	push	%gs			// set ds to address register stack
	pop	%es
	jmp	getx_stackreg		// unpack register
	ALIGN
from_stack_minus_1:
	call	get_top
	incw	%ax
	jmp	unpack_reg
	ALIGN
from_reg:
	call	get_top
	addb	reg_num(%ebp),%al
	jmp	unpack_reg
	ALIGN
from_stack_plus_1:
	call	set_up_indefinite		// store indefinite opr
	incw	%ax				// indicate stack error on return
	ret

//fetch_an_op	endp
//
// *************************************************************************
//			get_bcd:
//			********
//	function:
//		fetches a bcd number and converts it to an unpacked 
//		tempreal in operand1.
//	inputs:
//		assumes mem_operand_pointer is set up.
//		ignores di and uses operand2 and result fields.
//
//	outputs:
//		unpacked operand in operand1
//
//	data accessed:
//		- mem_operand_pointer		expon1
//		- offset_operand1		sign1
//		- tag1				word_frac1
//		- offset_operand1			word_frac2
//		- lsb__frac2			offset_operand2
//		- result_word_frac		offset_result
//
//	data changed:
//		- sign1				tag1
//		- expon1			word_frac1
//		- word_frac2			result_word_frac
//
//	procedures called:
//		left_shift			move_op_to_result
//		subtraction_normalize		test_6w
//
// ******************************************************************************
	ALIGN
get_bcd:	//proc
//	push	%ds			// save a?msr
	les	mem_operand_pointer(%ebp),%esi
//	push	%ss			// set up destination pointer
//	pop	%es
	lea	word_frac2,%edi		// es:di ==> word_frac2
//	mov	$0x0005,%ecx
	
	/* FAST_MOVSW */
	movl	%es:0(%esi),%ecx
	movl	%ecx,%ss:0(%edi)
	movl	%es:4(%esi),%ecx
	movl	%ecx,%ss:4(%edi)
	movw	%es:8(%esi),%cx
	movw	%cx,%ss:8(%edi)
//	addl	$10,%esi
//	movl	$0,%ecx

//..
//..		pushad
//..		push	ds
//..getbcd_loop:
//..		push	esi
//..		call	fpfushort
//..		pop	esi
//..		stosw
//..		add	esi,2
//..		loop	getbcd_loop
//..		pop	ds
//..		popad
//..		cld
//..
//	pop	%ds			// reload a?msr
	cmpw	$0x0ffff,dword_frac1+10(%ebp)
	jne	accept_bcd
	mov	$offset_operand1,%edi
	call	set_up_indefinite
	ret
	ALIGN
accept_bcd:
	movb	msb_frac2,%al	// get most significant byte
	cbtw				//  and transmit sign to ah
	movb	%ah,sign1(%ebp)		// store sign into operand
	mov	$offset_operand1,%edi	// set frac1 to 0
	call	clear_6w
	movb	$18,%cl			//set loop count to 18 digits
	FALLSTHRU
convert_loop:
	push	%ecx
	movb	$4,%cl			//shift frac2 left 4 bits to
	call	left_shift_frac2_cl	// the next digit
	and	$0x0f,msb_frac2	//clear previous digit
	call	left_shift_frac1_1	// frac1 <= 10*frac1 + frac2(9):
	mov	$offset_operand1,%esi	// 1) frac1 <= frac1 * 2
	call	move_op_to_result	// 2) result_frac <= frac1 * 4
	movb	$2,%cl
	call	left_shift_result_cl
//		mov	ax,(ebp).result_word_frac ; 3) frac1 <-- frac1 +
//		add	word_frac1,ax		;	result_frac
//		mov	ax,(ebp).result_word_frac + 2
//		adc	(ebp).word_frac1 + 2,ax
//		mov	ax,(ebp).result_word_frac + 4
//		adc	(ebp).word_frac1 + 4,ax
//		mov	ax,(ebp).result_word_frac + 6
//		adc	(ebp).word_frac1 + 6,ax
//		mov	ax,(ebp).result_word_frac + 8
//		adc	(ebp).word_frac1 + 8,ax
//
	mov	result_dword_frac(%ebp),%eax // 3) frac1 <-- frac1 +
	xorw	%ax,%ax
	add	%eax,dword_frac1(%ebp)		//	result_frac
	mov	result_dword_frac+4(%ebp),%eax
	adc	%eax,dword_frac1+4(%ebp)
	mov	result_dword_frac+8(%ebp),%eax
	adc	%eax,dword_frac1+8(%ebp)
	xor	%eax,%eax			// 4) frac1 <-- frac1 + frac(9)
	movb	msb_frac2,%al
	addw	%ax,word_frac1
	movb	$0,%al
	adc	%eax,dword_frac1+4(%ebp)
	adc	%eax,dword_frac1+8(%ebp)
	pop	%ecx			//and loop
	LOOP(convert_loop)
	mov	$dword_frac1,%edi
	call	test_6w			//frac1 = 0?
	jnz	make_floating_point
	mov	%eax,dword_expon1 		//set expon and tag for zero
	movb	special,tag1(%ebp)
	ret
	ALIGN
make_floating_point:
	movl	$0x404e,dword_expon1	//set expon1 and normalize
	mov	$offset_operand1,%edi
	call	subtraction_normalize
	movb	valid,tag1(%ebp)		//set tag to valid
	ret
//get_bcd	endp
//
// ******************************************************************************
//			put_result:
//
//	function:
//		puts result on 80387 stack or in 80387 register.
//		implements 80387 load instruction.
//
//	inputs:
//		address of data parts  (in edi register)
//		address of result info (in esi).
//		(ebp).before_error_signals
//	outputs:
//		stack error indication (in al register).
//
//	data accessed:
//
//	data changed:
//
//	procedures called:
//		decr_top			stack_full?
//		set_up_indefinite	store_reg_tag
//		get_top
//
// ***************************************************************************
location_case:	
	.long	push_onto_stack
	.long	put_in_stack_top
	.long	put_in_stack_top_minus_1
	.long	push_onto_stack
	.long	put_in_reg
//
	ALIGN
load:	//proc
	jz	put_zf_in_ah			// if zf set, no stack error
	call	set_stk_u_error
	testb	invalid_mask,%gs:sr_masks	// else, stack underflow error
	jnz	put_zf_in_ah			// if i_error masked, continue
	ret					// else, return
	ALIGN
put_zf_in_ah:				// if zf is set here, no stack
	lahf				// underflow occurred.  however,
					// if zf is clear, stack underflow
					// occurred and invalid is masked.
	jnz	put_op1_result
common_clear_a_bit:
	andb	$~a_mask,%gs:sr_flags
put_op1_result:
	mov	$offset_operand1,%edi		// edi points to result record
put_si_result:
	mov	offset_result_rec,%esi	// merge with put_result
put_result:
	movzbl	location(%ebp,%esi),%ebx	//handle cases separately
	shl	$2,%ebx
	jmp	*%cs:location_case(%ebx)
	ALIGN
put_in_stack_top_minus_1:
	call	get_top				// put reg num of top-1 in al
	incw	%ax
	jmp	mask_top
	ALIGN
push_onto_stack:
	sahf			// zf clear iff stack underflow occurred with
	jnz	push_value	//   invalid exception masked 
	call	stack_full_			// is top - 1 full?
	jz	other_before_error_	// no, so detect other before errors.
	call	set_stk_o_error		// set invalid error, stack bit, and
					// c1 (a) bit
	testb	invalid_mask,%gs:sr_masks
	jz	exit_load			// unmasked, so done with load
	call	set_up_indefinite	// push indefinite as the result
	jmp	push_value
	ALIGN
other_before_error_:
	mov	before_error_signals(%ebp),%eax
	or	%eax,%eax
	jz	push_value
	orb	%al,%al
	jz	check_z_error
	orb	invalid_mask,%gs:sr_errors
	testb	invalid_mask,%gs:sr_masks
	jz	exit_load			// unmasked, so done with load
	orb	$0x40,msb(%ebp,%edi)
	jmp	push_value
	ALIGN
check_z_error:
	orb	%ah,%ah
	jz	signal_d_error
	orb	zero_divide_mask,%gs:sr_errors
	testb	zero_divide_mask,%gs:sr_masks
	jz	exit_load			// unmasked, so done with load
	jmp	push_value
	ALIGN
signal_d_error:
	orb	denorm_mask,%gs:sr_errors
	testb	denorm_mask,%gs:sr_masks
	jz	exit_load			// unmasked, so done with load
//	the following seven instructions were added to cope with a load from
//	memory to (new) 87 stack top of a short_real or long_real operand.
//	otherwise, such loads of a denormal source operand while the denormal
//	exception is masked would incorrectly tag the loaded, normalized
//	temporary_real operand as invalid, instead of valid.  the first four
//	of these seven instructions have been commented out, because the
//	executive procedure e80387 in the module *dcode* always initializes
//	the op1_format field, even when its contents are deducible from the
//	operation_type and op1_location fields, so it is enough to check
//	whether the format field is single_real or double_real.
//		cmp		(ebp).operation_type,	load_op
//		jne		push value
//		cmp		(ebp).op1_location,	memory_opnd
//		jne		push_value
	cmpb	$double_real,op1_format(%ebp)
	ja	push_value
	movb	valid,tag(%ebp,%edi)
push_value:
	call	decr_top			// decrement top for push
put_in_stack_top:
	call	get_top				// put reg num of stack top in al
	jmp	mask_top
	ALIGN
put_in_reg:
	call	get_top				// put reg num specified in
	addb	reg_num(%ebp),%al	// instruction in al
mask_top:
	and	$0x0007,%eax
	mov	%eax,%ecx		// save reg num word in cx
	movb	tag(%ebp,%edi),%al	// put new tag in al
	call	store_reg_tag
	lea	frac64(%ebp,%edi),%esi// ds:esi ==> operand frac64
	mov	$sr_regstack,%edi	// form offset within %gs:sr_regstack
	add	%ecx,%edi		// for result (10 * reg num)
	shl	$2,%ecx
	add	%ecx,%edi		// es:edi ==>  treal st(reg num)
//	mov	$0x02,%ecx			// count = 2 dwords
//	push	%gs				// set segment registers
//	pop	%es
//	push	%ds
//	push	%ss
//	pop	%ds

	/* FAST_MOVSL */
	movl	%ss:0(%esi),%ecx
	movl	%ecx,%gs:0(%edi)
	movl	%ss:4(%esi),%ecx
	movl	%ecx,%gs:4(%edi)
//	addl	$8,%esi
//	addl	$8,%edi
//	movl	$0,%ecx
	
	movb	%ss:8(%esi),%ah			// ah = sign of operand
	andw	$0x8000,%ax		// form tempreal exponent
	orw	%ss:12(%esi),%ax
//	pop	%ds			// reload a?msr into ds
	movw	%ax,%gs:8(%edi)		// exponent => %gs:sr_regstack
exit_load:
	ret
//load	endp
//
// *****************************************************************************
//			stack_full?:
//
//	function:
//		checks stack to see if an attempted push will
//		generate a stack error.
//
//	inputs:
//		tag for register on top of stack
//
//	outputs:
//		zf = true if stack is empty; zf = false if stack is full.
//
//	procedures called:
//		get_top			get_reg_tag
//
// *****************************************************************************
	ALIGN
stack_full_:	//proc
	call	get_top			// determine reg no. of (top-1)
	decw	%ax
reg_full_:
	andb	$0x07,%al
	call	get_reg_tag
	cmpb	$empty,%al	// if (top-1) is not empty,
return:
	ret					// then stack is full
//stack_full_	endp
//
// ************************************************************************
//			decompose:
//
//	function:
//		decomposes op1 into two parts
//
//	inputs:
//		assumes op1 is set up, with tag1 /= unsupp and tag1 /= inv
//
//	outputs:
//		puts the exponent, floated in op1; the fraction in op2
//
//	data accessed:
//		- offset_operand		sign1
//		- tag1				expon1
//		- offset_operand2		sign2
//		- tag2				expon2
//
//	data changed:
//		- sign2				expon2
//
//	procedures called:
//		float_int32			move_op_to_op
//
// ****************************************************************************
	ALIGN
decompose:	//proc
	lea	dword_frac1(%ebp),%esi	// move op1 to op2
	lea	dword_frac2(%ebp),%edi
	call	move_op_to_op
	cmpb	valid,tag1(%ebp)
	jne	is_op1_denorm_
put_sig_in_op2:
	movl	$exponent_bias,dword_expon2	// set op2 exponent=bias
put_exp_in_op1:
	mov	dword_expon1,%eax	// float exp into op1
unbias_expon:
	sub	$exponent_bias,%eax	// first unbias it
	mov	$offset_operand1,%edi
	jmp	float_int32
	ALIGN
is_op1_denorm_:
	cmpb	denormd,tag1(%ebp)
	jne	op1_zero_or_inf
	movb	true,signal_d_error_
	testb	denorm_mask,%gs:sr_masks		// short cut if unmasked d-error
	jz	interim_exit
	mov	$offset_operand2,%edi
	call	norm_denorm
	mov	dword_expon2,%eax
	movl	$exponent_bias,dword_expon2	// set op2 exponent=bias
	jmp	unbias_expon
	ALIGN
op1_zero_or_inf:
	cmpb	special,tag1(%ebp)
	je	op1_zero			// op1 and op2 are infinite
	movb	positive,sign1(%ebp)
interim_exit:
	ret
	ALIGN
op1_zero:
	movb	true,signal_z_error_		// if number=0, signal z-error
	testb	zero_divide_mask,%gs:sr_masks	// if z_error is unmasked
	jz	interim_exit			// then just return
make_op1_negative_inf:
	movl	$0x07fff,dword_expon1		// else, make op1 -infinity
	movb	$0x80,msb_frac1
	movl	infinty*0x10000+NEGATIVE,sign1(%ebp)
	ret
//decompose	endp
//
// **************************************************************************
//			float16:
//
//	function:
//		retrieves a 16-bit, 2s complement integer from memory,
//		and fills in the operand
//
//	inputs:
//		assumes mem_operand_pointer is set up
//		edi points to operand1 or operand2
//
//	outputs:
//		operand
//
//	data accessed:
//		- mem_operand_pointer
//
//	data changed:
//		- operand
//
//	procedures called:
//		float_int32
//
// ***************************************************************************
	ALIGN
float16:	//proc
	les	mem_operand_pointer(%ebp),%ebx	// fetch the
						// 16-bit integer
	movw	%es:(%ebx),%ax
//..
//..		pushad
//..		push	es
//..		push	ebx
//..		call	fpfushort
//..		add	esp,8
//..		popad
//..		cld
//..
float_int16:
	movswl	%ax,%eax
	jmp	float_int32
	ALIGN
//
//
//
// *****************************************************************************
//			float32:
//
//	function:
//		retrieves a 32-bit, 2s complement integer from memory
//		and fills in the operand
//
//	inputs:
//		assumes mem_operand_pointer is set up
//		edi points to operand1 or operand2
//
//	outputs:
//		operand
//
//	data accessed:
//		- mem_operand_pointer
//
//	data changed:
//		- operand
//
//	procedures called:
//
//
// *****************************************************************************
float32:
	les	mem_operand_pointer(%ebp),%ebx

	mov	%es:(%ebx),%eax			// move from memory to eax

//..
//..		pushad
//..		push	es
//..		push	ebx
//..		call	fpfulong
//..		add	esp,8
//..		popad
//..		cld

float_int32:
	mov	%eax,%edx			// save integer in edx
	xor	%eax,%eax			// clear low 8 bytes
	mov	%eax,(%ebp,%edi)
	mov	%eax,frac64(%ebp,%edi)
	or	%edx,%eax			// if integer is zero,
	jnz	get_sign_and_mag_32		// then load +0
	mov	%eax,frac32(%ebp,%edi)
	movb	%al,sign(%ebp,%edi)
	movb	special,tag(%ebp,%edi)
	mov	%eax,expon(%ebp,%edi)
	ret
	ALIGN
get_sign_and_mag_32:
	cltd					// transmit sign to edx
	movb	%dl,sign(%ebp,%edi)		// and store to operand
	jns	positive_int				// branch if positive
	neg	%eax					// negate if negative
positive_int:
	bsrl	%eax,%ecx
	sub	$31,%ecx
	neg	%ecx
	shl 	%cl,%eax
	mov	%eax,frac32(%ebp,%edi)	// store normalized{abs(int)}
	mov	$0x401e,%eax				// load valid exponent
	sub	%ecx,%eax
	mov	%eax,expon(%ebp,%edi)
	movb	valid,tag(%ebp,%edi)
	ret
	ALIGN
//float16	endp
//
// ******************************************************************************
//			float64:
//
//	function:
//		retrieves a 64-bit, 2s complement integer from memory
//		and fills in the operand
//
//	inputs:
//		assumes mem_operand_pointer is set up
//		edi points to operand1 or operand2
//
//	outputs:
//		operand
//
//	data accessed:
//		- mem_operand_pointer
//
//	data changed:
//		- operand
//
//	procedures called:
//
//
// ****************************************************************************
	ALIGN
float64:	//proc
//	push	%ds					// save a?msr
	les	mem_operand_pointer(%ebp),%esi
//	push	%ss
//	pop	%es				// es:edi ==> operand
	push	%edi				// save op? offset
	lea	frac64(%ebp,%edi),%edi	// set operand offset
//	mov	$0x0002,%ecx		// move fraction from

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

//..
//..		pushad
//..		push	ds
//..float64_loop:
//..		push	esi
//..		call	fpfulong
//..		pop	esi
//..		add	esi,4
//..		stosd
//..		loop	float64_loop
//..		pop	ds
//..		popad
//..		cld
//..
	pop	%edi			// reload operand offset
//	pop	%ds					// reload a?msr
	mov	frac32(%ebp,%edi),%eax		// get long integer sign
	cltd			// transmit sign to edx
	movb	%dl,sign(%ebp,%edi)	// and store to operand
	inc	%edx			// test for '-' fract
	jnz	detect_zero_64
	notl	frac64(%ebp,%edi)		// '-', so form 2s
	add	$1,frac64(%ebp,%edi)	// complement
	notl	frac32(%ebp,%edi)
	adc	$0,frac32(%ebp,%edi)
	jmp	set_expon_64
	ALIGN
detect_zero_64:
	or	frac64(%ebp,%edi),%eax
	jnz	set_expon_64
	mov	%eax,expon(%ebp,%edi)			// set expon = zero
	movb	special,tag(%ebp,%edi)
	ret
	ALIGN
set_expon_64:
	movl	$0x403e,expon(%ebp,%edi)	//set expon = 63
	bsrl	frac32(%ebp,%edi),%ecx
	jz	top_32_zero
	mov	frac64(%ebp,%edi),%eax
	sub	$31,%ecx
	neg	%ecx
	shldl	%cl,%eax,frac32(%ebp,%edi)
	shl 	%cl,frac64(%ebp,%edi)
adjust_norm_expon:
	sub	%ecx,expon(%ebp,%edi)	// adjust exponent
	ret
	ALIGN
top_32_zero:
	mov	frac64(%ebp,%edi),%eax
	bsrl	%eax,%ecx
	sub	$31,%ecx
	neg	%ecx
	shl 	%cl,%eax
	mov	%eax,frac32(%ebp,%edi)
	movl	$0,frac64(%ebp,%edi)
	add	$32,%ecx
	jmp	adjust_norm_expon
	ALIGN
//float64	endp
//
// ***************************************************************************
//			save:
//
//	function:
//		implements 80387 save instruction
//
//	inputs:
//
//	outputs:
//
//	data accessed:
//		- mem_operand_pointer		result_rec_offset
//
//	data changed:
//
//	procedures called:
//		save_status		save_regs	init
//
// **************************************************************************
	ALIGN
save:	//proc
	call	save_status
	call	save_regs
	jmp	init			// initialize environment
//save	endp
//
// *************************************************************************
//			save_regs:
//
//	function:
//		saves all registers in contiguous memory locations.
//
//	inputs:
//		memory operand pointer (in es:(di))
//
//	outputs:
//
//	data accessed:
//
//	data changed:
//
//	procedures called:
//		get_top
//
// ***************************************************************************
	ALIGN
save_regs:	//proc
	movb	$5,%cl				// 5 words per register
	call	get_top				// ax = top * 5
	push	%ds				// set source seg register
	push	%gs
	pop	%ds
	mulb	%cl
	movw	$40,%cx				// number words to move
	subw	%ax,%cx				// from top of stack =
	mov	$sr_regstack,%esi		//  40 - top*5
	push	%esi				// starting address =
	cwtl
	add	%eax,%esi			//  %gs:sr_regstack + top*5
	add	%eax,%esi			//  *2 bytes/word
	movzwl	%cx,%ecx
	FAST_MOVSW
	movw	%ax,%cx				// move (top * 5) words
	pop	%esi				// load starting address
	FAST_MOVSW
	pop	%ds				// restore %ds
	ret
//save_regs	endp
//
// *****************************************************************************
//			restore_regs:
//
//	function:
//		loads all registers from contiguous memory locations
//		implements 80387 restore instruction
//
//	inputs:
//		mem_operand_pointer
//
//	outputs:
//		loads the %gs:sr_regstack area of a?msr from memory
//
//	data accessed:
//
//	data changed:
//		- operand
//
//	procedures called:
//		restore_status			get_top
//
// *************************************************************************
	ALIGN
restore:	//proc
	call	restore_status			// this call defines es
restore_regs:
	movb	$5,%cl				// 5 words per register
	call	get_top
	push	%ds				// save a?msr
//	pop	%es
//	push	%ds
	mulb	%cl				// ax = top*5
	mov	$40,%ecx				// number words to move
	subw	%ax,%cx				// from top of stack =
	mov	$sr_regstack,%edi		//  40 - top*5
	push	%edi				// starting address =
	cwtl
	add	%eax,%edi				//  %gs:sr_regstack + top*5
	add	%eax,%edi				//  *2 bytes/word
	lds	mem_operand_pointer(%ebp),%esi	// source is in memory
	add	$14,%esi
	cmpb	$1,oprnd_siz32(%ebp)	//is it a 32 bit operand
	jne	status16		//no restore 16 bit status
	add	$14,%esi				// skip the environment
//	--------replace following line for unix
status16:
	FAST_MOVSW

//..
//..		pushad
//..		push	ds
//..restor_loop:
//..		push	esi
//..		call	fpfushort
//..		pop	esi
//..		stosw
//..		add	esi,2
//..		loop	restor_loop
//..		pop	ds
//..		popad
//..		cld
//..
	movw	%ax,%cx
	pop	%edi				// load starting address
	FAST_MOVSW
	pop	%ds				// reload a?msr
	jmp	correct_tag_word

//	ret
//restore	endp
//
// *****************************************************************************
//			set_up_indefinite:
//
//	function:
//		puts indefinite in the operand pointed to by edi
//
//	input:
//		edi points to operand
//
//	output:
//		indefinite in operand,	eax = 0, zf set (zf = 1)
//
//	data accessed:
//		- operand
//
//	data changed:
//		- operand
//
// ****************************************************************************
	ALIGN
set_up_indefinite:	//proc
	xor	%eax,%eax
	mov	%eax,frac64(%ebp,%edi) 	//set up indef fraction
	movl	$0x0c0000000,frac32(%ebp,%edi)
	movb	negative,sign(%ebp,%edi) //set sign negative
	movl	$0x7fff,expon(%ebp,%edi)	 //set exponent to 7fffh
	movb	inv,tag(%ebp,%edi)			 //set tag to invalid
common_return:
	ret
	ALIGN
//set_up_indefinite	endp
//
// ***********************************************************************
//			exchange
//
//
//	function:
//		implements 80387 exchange instruction
//
//	inputs:
//		operand(s) in operands
//
//	outputs:
//		result(s) on stack
//
//	data accessed:
//		- result_rec_offset		offset_operand1
//
//	data changed:
//
//	procedures called:
//		put_result			set_stk_u_error
//
// *************************************************************************
	ALIGN
exchange:	//proc
	jz	do_exchange		// if zf clear, then stack error
	call	set_stk_u_error
	testb	invalid_mask,%gs:sr_masks
	jz	common_return			// if unmasked error, do nothing
do_exchange:
//	call	common_clear_a_bit		/ put op1(top) in result(reg)
	and	$~a_mask,%gs:sr_flags
do_exchange_leave_a_bit:
	call	put_op1_result
set_zf_put_second_result:
	xor	%eax,%eax				// set 86 zf
	lahf					// save set zf in ah
put_second_result:
	mov	$offset_operand2,%edi	// put op2(reg) in res2(top)
	mov	offset_result2_rec,%esi
	jmp	put_result
//exchange	endp
//
// ***********************************************************************
//			exam:
//
//	function:
//		implements 80387 fxam instruction
//
//	inputs:
//		operand1
//
//	outputs:
//		operand type indicators set
//
//	data accessed:
//		- tag1				sign1
//		- msb_frac1
//
//	data changed:
//
//	procedures called:
//
// ***********************************************************************
	ALIGN
exam:	//proc
	jz	something_on_stack	// branch on no stack error
	orb	$(zero_mask+sign_mask),%gs:sr_flags	// set s and z bits
	andb	$~(a_mask+c_mask),%gs:sr_flags	// clear a and c bits
	jmp	check_a_bit
	ALIGN
something_on_stack:
	andb	$~(zero_mask+c_mask+a_mask+sign_mask),%gs:sr_flags
					// clear condition code bits
	movb	tag1(%ebp),%al
	cmpb	unsupp,%al
	je	check_a_bit
	cmpb	inv,%al				// remove special cases
	je	check_z_s_a_bits
	cmpb	infinty,%al
	je	set_c_to_one
	cmpb	denormd,%al
	jne	valid_or_zero
	orb	$(zero_mask+c_mask),%gs:sr_flags	// set s, z, c to 0, 1, 1
					// for denormalized op
	jmp	check_a_bit
	ALIGN
valid_or_zero:
	testb	$0x80,msb_frac1		// for valid or zero,
	jz	check_z_s_a_bits	// set c to msb of frac
set_c_to_one:
	orb	$c_mask,%gs:sr_flags
check_z_s_a_bits:
	rorb	$1,%al				// set z-bit to lsb of tag1
	jnc	check_s_a_bits
	orb	$zero_mask,%gs:sr_flags
check_s_a_bits:
	rorb	$1,%al				// set s-bit to lsb-1 of tag1
	jnc	check_a_bit
	orb	$sign_mask,%gs:sr_flags
check_a_bit:
	testb	$0x01,sign1(%ebp)	// set a-bit to sign bit
	jz	exchange_return
	orb	$a_mask,%gs:sr_flags	// set_a_bit
exchange_return:
	ret
//exam	endp
//
// ***********************************************************************
//			fxtrac:
//
//	function:
//		implements the 80387 fxtrac instruction
//
//	inputs:
//
//	outputs:
//		decomposed results in results
//
//	data accessed:
//		- result_rec_offset		result2_rec_offset
//		- offset_operand1		tag1
//		- offset_operand2
//
//	data changed:
//
//	procedures called:
//		set_up_indefinite		decompose
//		do_exchange	
//
// ***********************************************************************
	ALIGN
fxtrac:	//proc
	jz	detect_i_error	// branch if no stack error
	call	set_stk_u_error	// set invalid error and stack bit, but
						// clear c1 (a) bit
	testb	invalid_mask,%gs:sr_masks
	jz	quick_out			// branch if error unmasked
	mov	$offset_operand2,%edi		// for masked invalid error,
	call	set_up_indefinite			// set op2 to indefinite
	orb	true,%al			// clear zf to indicate that
	lahf					// stack underflow occurred.
	jmp	put_second_result
	ALIGN
detect_i_error:
	call	stack_full_
	jz	other_i_error_
	jmp	set_zf_put_second_result
	ALIGN
other_i_error_:
	andb	$~a_mask,%gs:sr_flags	// clear a-bit
	movl	$0,before_error_signals(%ebp)	// initialize all to zero
	movb	tag1(%ebp),%al				// load operand tag
	cmpb	unsupp,%al
	jne	is_op1_nan_
	testb	invalid_mask,%gs:sr_masks	// short cut if unmasked i-error
	jnz	put_indef_in_op1
unmasked_i_error:
	orb	invalid_mask, %gs:sr_errors
quick_out:
	ret			// return if unmasked error
	ALIGN
put_indef_in_op1:
	mov	$offset_operand1, %edi
	call	set_up_indefinite
	jmp	masked_i_error
	ALIGN
is_op1_nan_:
	cmpb	inv,%al
	jne	do_decompose
	testb	$0x40,msb_frac1			// is op1 a signaling nan?
	jnz	op1_is_qnan
	testb	invalid_mask,%gs:sr_masks
	jz	unmasked_i_error
	orb	$0x40,msb_frac1				// make op1 a quiet nan
masked_i_error:
	movb	true, signal_i_error_
op1_is_qnan:
	lea	dword_frac1(%ebp),%esi	// move op1 to op2
	lea	dword_frac2(%ebp),%edi
	call	move_op_to_op
	call	put_op1_result				// no push occurs here
	jmp	set_zf_put_second_result		// put both results
	ALIGN
do_decompose:
	call	decompose
	jmp	do_exchange		// put both results
	ALIGN
//fxtrac	endp
//
// ***********************************************************************
//			abs_value:
//
//	function:
//		implements the 80387 absolute value instruction
//
//	inputs:
//		assumes operand is set up
//
//	outputs:
//		absolute value of operand in result
//
//	procedures called:
//		set_stk_u_error
//
// ***************************************************************************
	ALIGN
abs_value:	//proc
	jz	do_abs_value			// branch if no stack error
	call	set_stk_u_error		// set invalid error and stack bit,
					// but clear c1 (a) bit
	testb	invalid_mask,%gs:sr_masks
	jnz	abs_value_result	// masked error, so give indef already
common_exit:				// set up in operand1 by fetch_an_op.
	ret				// unmasked error
	ALIGN
do_abs_value:
	movb	positive,sign1(%ebp)// set sign to positive
abs_value_result:
	jmp	common_clear_a_bit		// put result on stack
//abs_value	endp
//
// ***********************************************************************
//			chsign:
//
//	function:
//		implement the 80387 chsign instruction
//
//	inputs:
//		operand1
//
//	outputs:
//		result <==  -(operand1)
//
//	procedures called:
//		set_stk_u_error
//
// ************************************************************************
	ALIGN
chsign:	//proc
	jz	do_chsign			// branch if no stack error
	call	set_stk_u_error	// set invalid error and stack bit, but
						// clear c1 (a) bit
	testb	invalid_mask,%gs:sr_masks
	jnz	chsign_result		// put op1, set up as qnan indefinite
	ret				// by fetch_an_op, into result
	ALIGN
do_chsign:
	notb	sign1(%ebp)			// complement sign
chsign_result:
	jmp	common_clear_a_bit		// put op1 into result
	ALIGN
//chsign	endp
//
//a_med	ends
//
//	end