Source to machdep/i386/fp_emul/fp_arith.s


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

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @[email protected]
 * 
 * "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."
 * 
 * @[email protected]
 */

	.file	"arith.s"

	.ident	"@(#)kern-fp:arith.s	1.1"
//$tt(*80387	emulator	+ + + arithmetic + + +*)
// ************************************************************************
//
//			a r i t h . 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  or disclosed except in accordance with
//    the terms of that agreement.                                  
//	===============================================================
//
//	function:
//		implements add, subtract, multiply, and divide.
//
//	public procedures:
//		move_op_to_result		overflow_response
//		underflow_response		put_max_nan
//		put_indefinite			arith
//
// *************************************************************************
//
//...March 3, 1987...
//
//	.file	*a_mar*
#include	"fp_e80387.h"
//$eject
//...declare status register segment...
//
	.data	//a_msr	segment	rw	public
//	extrn	sr_masks
//a_msr	ends
//
	.text	//a_med	segment	er	public
//	assume	%ds:a_med
//	extrn	addition_normalize,round
//	extrn	gradual_underflow,put_result
//	extrn	pop_free,subadd,mult
//	extrn	divid,set_up_indefinite,test_4w
//	extrn	special_round_test,directed_round_test
//	extrn	get_precision,u_masked_,set_i_masked_
//	extrn	d_masked_,affine_infinity_,i_masked_
//	extrn	o_masked_,set_p_error,set_u_error
//	extrn	set_o_error,set_z_masked_,set_d_masked_
//	extrn	set_stk_u_error,d_error_,get_rnd_control
//
	.globl	put_indefinite
	.globl	move_op_to_result
	.globl	put_max_nan
	.globl	overflow_response
	.globl	underflow_response
	.globl	arith
	.globl	norm_denorm
	.globl put_arith_result
	.globl set_up_nan_return
//$eject
//
//...define some floating point constants...
//
indefinite_pattern:	
	.value	0,0,0,0x0c000,0x0ffff
infinity_pattern:	
	.value	0,0,0,0x8000,0x7fff
zero_pattern:	
	.value	0,0,0,0,0
max_valid_pattern:	
	.value	0x0ffff,0x0ffff,0x0ffff,0x0ffff,0x7ffe
arith_table:	
	.long	subadd,subadd,mult,divid
//
//...the following jump tables refer to the following op1/op2 cases 
//
//   (where v=valid, z=zero, f=infinity, and d=denormal):
//		v/v,v/z,v/f,v/d,z/v,z/z,z/f,z/d,f/v,f/z,f/f,f/d,d/v,d/z,d/f and d/d,
//	 in that order ..
//add_table
special_table:	
	.long	handle_non_special_cases,handle_non_special_cases
	.long	second_operand,only_op2_denormd
	.long	handle_non_special_cases,handle_non_special_cases
	.long	second_operand,only_op2_denormd
	.long	first_operand,first_operand
	.long	add_sub_infinities,derror_with_first_operand
	.long	only_op1_denormd,only_op1_denormd
	.long	derror_with_second_operand,both_ops_denormd
//sub_table
	.long	handle_non_special_cases,handle_non_special_cases
	.long	neg_second_operand,only_op2_denormd
	.long	handle_non_special_cases,handle_non_special_cases
	.long	neg_second_operand,only_op2_denormd
	.long	first_operand,first_operand
	.long	add_sub_infinities,derror_with_first_operand
	.long	only_op1_denormd,only_op1_denormd
	.long	derror_with_neg_second_operand,both_ops_denormd
//mul_table
	.long	handle_non_special_cases,exor_signed_zero
	.long	exor_signed_infinity,only_op2_denormd
	.long	exor_signed_zero,exor_signed_zero
	.long	invalid_error_detected,denormd_exor_signed_zero
	.long	exor_signed_infinity,invalid_error_detected
	.long	exor_signed_infinity,denormd_exor_signed_infinity
	.long	only_op1_denormd,denormd_exor_signed_zero
	.long	denormd_exor_signed_infinity,both_ops_denormd
//div_table
	.long	handle_non_special_cases,divide_by_zero
	.long	exor_signed_zero,only_op2_denormd
	.long	exor_signed_zero,invalid_error_detected
	.long	exor_signed_zero,denormd_exor_signed_zero
	.long	exor_signed_infinity,exor_signed_infinity
	.long	invalid_error_detected,denormd_exor_signed_infinity
	.long	only_op1_denormd,divide_by_zero
	.long	denormd_exor_signed_zero,both_ops_denormd
//
//$eject
//			put_indefinite:
//
//	function:
//		sets unpacked result to indefinite.
//
//	inputs:
//
//	outputs:
//		result set to indefinite; tag set to invalid.
//
//	data accessed:
//		- result_sign			result_tag
//
//	data changed:
//		- result
//
	ALIGN
put_indefinite:	//proc
	mov	$indefinite_pattern,%esi
	movb	negative,result_sign(%ebp)
// note that entry points below do not affect result_sign.
put_inv_tag:
	movb	inv,result_tag(%ebp)	// fall into set_constant_result
set_constant_result:
	lea	offset_result+4(%ebp),%edi
//	push	%ds                       // save a?msr
//	push	%cs
//	pop	%ds
//	push	%ss
//	pop	%es

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

	mov	%ecx,result_dword_frac(%ebp)        // clear ls dword
	mov	%cs:8(%esi),%eax			// move exponent
	movw	%ax,result_expon(%ebp)		// to result
//	pop	%ds				// reload a?msr
	ret
//put_indefinite	endp
//$eject
//			move_op_to_result:
//
//	function:
//		moves operand to result.
//
//	inputs:
//		assumes ss:(ebp).esi points to op1 or op2
//
//	outputs:
//		result in result
//
//	data accessed:
//		- expon1			offset_result
//		- result_expon
//
//	data changed:
//		- result
//
	ALIGN
move_op_to_result:	//proc
//	push	%ds			                        // save a?msr
	add	%ebp,%esi
//	push	%ss
//	pop	%ds
//	push	%ss
//	pop	%es
	lea	offset_result(%ebp),%edi
	mov	$((offset_operand2-offset_operand1)>>2),%ecx

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

//	pop	%ds	                        // reload a?msr
	ret
//move_op_to_result	endp
//$eject
//			overflow_response:
//
//	function:
//		responds to detected overflow.
//
//	inputs:
//
//	outputs:
//		overflow error indication(s) set when appropriate
//		correct masked or unmasked result
//
//	data accessed:
//		- result_sign			result_tag
//		- result_expon			msb_result
//
//	data changed:
//		- result
//
//	procedures called:
//		special_round_test		set_o_error
//		o_masked?			    set_p_error
//
//
	ALIGN
overflow_response:	//proc
	orb	overflow_mask, %gs:sr_errors	//set overflow error
	testb	overflow_mask, %gs:sr_masks
	jnz	masked_overflow		   // branch if ovflw masked
// note that p_error may have been set by the first rounding of the result,
// and that rounded result will now have its exponents bias adjusted:
reduce_exponent:
	subl	wrap_around_constant,dword_result_expon	// subtract
	cmpl	$0x7fff, dword_result_expon	//wrap around constant
	jge	masked_overflow	            // bad! should give qnan indefinite
unmasked_ov_un_rounds:
	cmpb	true, rnd1_inexact	//detect inexact result
	jne	give_valid_o_result	// if exact, branch to replace source
	orb	$inexact_mask, %gs:sr_errors	// else, set p_error
	cmpb	true, added_one	//was rounding done by adding one
	jne	give_valid_o_result	// if not branch to replace source
	orb	$a_mask, %gs:sr_flags	// else set A_bit
	jmp	give_valid_o_result
	ALIGN
masked_overflow:
	orb	$inexact_mask, %gs:sr_errors	//set p_error
	call	get_rnd_control
	cmpb	rnd_to_zero,%al
	je	put_max_valid
	movb	result_sign(%ebp),%al	        // check for special
	call	special_round_test		            // rounding case
	jnz	put_max_valid
	orb	$a_mask, %gs:sr_flags	//set a_bit
put_infinity:
	mov	$infinity_pattern,%esi
	jmp	put_inv_tag
	ALIGN
put_max_valid:
//	mov	    (ebp).result_tag,   valid
	movl	$0x00007ffe,dword_result_expon
	movl	$0,result_dword_frac(%ebp)
	movl	$0x0ffffffff,result_dword_frac+frac64(%ebp)
	movl	$0x0ffffffff,result_dword_frac+frac32(%ebp)
	cmpb	$add_op,operation_type(%ebp)
	jl	give_valid_o_result
	cmpb	$div_op,operation_type(%ebp)
	jg	give_valid_o_result
	movb	$precision_mask,%dl	// load precision field mask
	andb	%gs:sr_controls,%dl		// mask in precision control
	cmpb	prec53,%dl
	jg	give_valid_o_result
	je	purge_11_bits
	movl	$0,result_dword_frac+frac64(%ebp)
	movl	$0x0ffffff00,result_dword_frac+frac32(%ebp)
	jmp	give_valid_o_result
	ALIGN
purge_11_bits:
	andl	$0x0fffff800,result_dword_frac+frac64(%ebp)

//	mov	    esi,    offset max_valid_pattern	; set result to max
//	jmp	    set_constant_result		            ; valid number

give_valid_o_result:
	movb	valid,result_tag(%ebp)
	ret
//overflow_response	endp
//$eject
//			underflow_response:
//
//	function:
//		responds to a detected underflow 
//
//	inputs:
//
//	outputs:
//		sets underflow error indication(s) if appropiate
//		correct masked or unmasked results in result
//
//	data accessed:
//		- offset_result			result_sign
//		- result_tag			result_expon
//		- result_word_frac
//
//	data changed:
//		- result
//
//	procedures called:
//		directed_round_test		gradual_underflow
//		round				    test_4w
//		set_u_error			    u_masked?
//		get_precision
//
	ALIGN
underflow_response:	//proc
	testb	underflow_mask, %gs:sr_masks	//is underflow masked
	jnz	do_grad_underflow		// yes, dont set flag
	orb	underflow_mask, %gs:sr_errors	//no, set error flag
increase_exponent:
	addl	wrap_around_constant,dword_result_expon // add wrap-around
	cmp	$0, dword_result_expon	//wrap_around constant
	jg	unmasked_ov_un_rounds	// bad! shuld give qnan indef
	subl	wrap_around_constant, dword_result_expon // subtract
do_grad_underflow:
	mov	$offset_result,%edi                  // do gradual underflow
	mov	$0x0001,%eax			// minimum expon is 0001
	call	gradual_underflow
	call	get_precision
	movb	true,%al
	mov	$offset_result,%edi		// do second round
	call	round
	cmpw	$0, rnd_history		
	je	prepare_zero_expon
	orb	underflow_mask + inexact_mask, %gs:sr_errors //set both errors
	cmpb	true, added_one		// was rounding done by adding one
	jne	prepare_zero_expon	//if not, branch for zero expon
	orb	$a_mask, %gs:sr_flags	//else, set a_bit & if incrementing
	testb	$0x80, msb_result	//The significand caused the most
	jz	prepare_zero_expon	//significant bit of result_frac to
	movb	valid, result_tag(%ebp) // be set, tag result *valid* & return
	ret
	ALIGN
prepare_zero_expon:
	xor	%eax, %eax
clear_expon:
	mov	%eax,dword_result_expon		// set exponent to zero
	mov	$result_dword_frac+4,%edi
	call	test_4w				// if fraction nonzero,
	movb	denormd,result_tag(%ebp)
	jnz	accept_tag			// tag as denormal
	movb	special,result_tag(%ebp)	// else, tag as zero
accept_tag:
	ret
//underflow_response	endp
//$eject
//			norm_denorm:
//
//	function:
//		normalizes an unpacked operand presumed to have been tagged
//		*denormd*.  includes case in which operand is so tagged because
//		it was a single or double format denormal, which has already
//       been normalized in the course of extending it to unpacked format.
//		also includes case in which operand is a pseudo-denormal.
//
//	inputs:  edi points to the offset of the record to be normalized.
//
//	outputs:
//		8 bytes beginning at (ebp + edi + frac64) are left shifted until
//		the bit in position b63 is set.
//		the 32 bit exponent field is 1 - shift_count
//
//	data accessed:
//		- dword ptr (ebp + edi + expon)
//		- byte ptr (ebp + edi + msb)
//		- dword ptr (ebp + edi + frac32),	dword ptr (ebp + edi + frac64)
//
//	data changed:
//		- dword ptr (ebp + edi + expon)
//		- byte ptr (ebp + edi + msb)
//		- dword ptr (ebp + edi + frac32),	dword ptr (ebp + edi + frac64)
//
//	procedures called:
//
//
	ALIGN
norm_denorm:	//proc
	cmp 	$0,expon(%ebp,%edi)
	jne	norm_denorm_done
	movl 	$1,expon(%ebp,%edi)
	testb	$0x80,msb(%ebp,%edi)
	jnz	norm_denorm_done
norm_64_bits:
	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)
	shll	%cl,frac64(%ebp,%edi)
adjust_dword_expon:
	sub	%ecx,expon(%ebp,%edi)	// adjust exponent
norm_denorm_done:
	ret
	ALIGN
top_32_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	adjust_dword_expon
//norm_denorm	endp
//$eject
//
//			arith:
//
//	function:
//		main procedure for implementation of 80387 add, subtract,
//		multiply, and divide instructions
//
//	inputs:
//		assumes operation type, operand(s), and unpacked
//		status variables are set up
//
//	outputs
//		result of operation in result
//
//	data accessed:
//		- operation_type		offset_operand1
//		- sign1				tag1
//		- expon1			offset_operand2
//		- sign2				tag2
//		- expon2			offset_result
//		- result_sign			result_tag
//		- result_expon			result_word_frac
//
//	data changed:
//		- result
//
//	procedures called:
//		put_indefinite		affine_infinity?
//		put_max_valid		move_op_to_result
//		overflow_response	underflow_response
//		put_max_nan		divid
//		set_up_indefinite	round
//		addition_normalized	put_result
//		pop_free		subadd
//		mult			test_4w
//		i_masked?		d_error?
//		d_masked?		i_error?
//		get_precision		set_i_masked?
//		set_z_masked?		set_d_masked?
//
//
	ALIGN
arith:	//proc
//	assume	%ds:a_msr
	jz	weed_out_special_cases	// branch if no stack error
	call	set_stk_u_error			// stack underflow occurred
	testb	invalid_mask,%gs:sr_masks
	jz	go_home					// if unmasked, return
	call	put_indefinite			// otherwise, result = indefinite
	jmp	finish_up
	ALIGN
weed_out_special_cases:
	andb	$~a_mask, %gs:sr_flags		//clear a_bit initially
	movb	tag1(%ebp),%al			// both operands valid?
	orb	tag2(%ebp),%al
	jnz	special_cases				// no, special case
handle_non_special_cases:
	movzbl	operation_type(%ebp),%ebx// call sub/add/mul/div
	subb	$add_op,%bl				// to do the operation
	shlw	$2,%bx
	call	*%cs:arith_table(%ebx)
// the following three instructions are residue of warning mode arithmetic:
//		call	i_error?		; invalid if attempted
//		jz		no_invalid_error; division by denormal
//		jmp		invalid_error_detected
no_invalid_error:
	mov	$offset_result,%edi	// round result
	call	get_precision				// (indicate first, not
	movb	false,%al			// second, round)
	call	round
	mov	$offset_result,%edi	// re-normalize
//	movb	$4,%ah				/ (num words in frac=4)
	call	addition_normalize			// (al contains overflow
	mov	dword_result_expon,%eax	// indication from round)
	cmp	$0x7ffe,%eax		// overflow iff
	jg	overflow_happened			// expon > 7ffeh
	cmp	$0,%eax			// check underflow if
	jl	underflow_happened			// expon < 0
	call	give_valid_o_result			// set tag to valid
	and	%eax,%eax				// check underflow if
	jnz	report_rounds		// expon = 0 and frac <> 0
	mov	$result_dword_frac+4,%edi	//expon=0
	call	test_4w
	jnz	underflow_happened
	movb	special,result_tag(%ebp)	// result is truly zero
report_rounds:
	cmpb	true, rnd1_inexact	//detect inexact result
	jne	finish_up		//if exact, branch
	orb	$inexact_mask, %gs:sr_errors  //else, set p_error
	cmpb	true, added_one		//was rounding done by adding one
	jne	finish_up		//if not, branch
	orb	$a_mask, %gs:sr_flags	// else, set a_bit
finish_up:
	mov	$offset_result,%edi
	mov	offset_result_rec,%esi
	jmp	put_arith_result
	ALIGN
//
both_ops_denormd:
	orb	denorm_mask, %gs:sr_errors 
	testb	denorm_mask, %gs:sr_masks
	jz	go_home
	mov	$offset_operand1,%edi
	call	norm_denorm
op2_denormd:
	mov	$offset_operand2,%edi
	call	norm_denorm
	jmp	handle_non_special_cases
	ALIGN
only_op1_denormd:
	orb	denorm_mask, %gs:sr_errors 
	testb	denorm_mask, %gs:sr_masks
	jz	go_home
	mov	$offset_operand1,%edi
	call	norm_denorm
	jmp	handle_non_special_cases
	ALIGN
only_op2_denormd:
	orb	denorm_mask, %gs:sr_errors 
	testb	denorm_mask, %gs:sr_masks
	jz	go_home
	jmp	op2_denormd
	ALIGN
//
overflow_happened:
	call	overflow_response	// here, overflow is certain.
	jmp	finish_up
	ALIGN
go_home:
	ret
underflow_happened:
	call	underflow_response
	jmp	finish_up
	ALIGN
special_cases:
	testb	$0x10,%al		// al contains (ebp).tag1 or (ebp).tag2
	jz	op1_nan_
	orb	invalid_mask, %gs:sr_errors 
	testb	invalid_mask, %gs:sr_masks
	jz	go_home
	mov	$offset_operand1,%edi
	call	set_up_indefinite
	mov	offset_result_rec,%esi
	jmp	put_arith_result
	ALIGN
op1_nan_:				// here, neither op is unsupported.
	cmpb	inv,tag1(%ebp)
	je	op1_snan_
	cmpb	inv,tag2(%ebp)
	jne	non_nan_supp_ops
	jmp	signal_invalid_
	ALIGN
op1_snan_:
	testb	$0x40,msb_frac1
	jz	invalid_operand
op2_also_nan_:
	cmpb	inv,tag2(%ebp)
	jne	set_up_nan_return
signal_invalid_:
	testb	$0x40,msb_frac2
	jnz	set_up_nan_return
invalid_operand:
	orb	invalid_mask, %gs:sr_errors 
	testb	invalid_mask, %gs:sr_masks
	jz	go_home					// i-error, if unmasked
set_up_nan_return:
	mov	offset_result_rec,%esi	// result=max(nan1,nan2)
	cmpb	inv, tag1(%ebp)
	je	check_tag2
	mov	$offset_operand2, %edi
	jmp	install_hi_bits
	ALIGN
check_tag2:
	cmpb	inv, tag2(%ebp)
	je	put_max_nan
	mov	$offset_operand1, %edi
	jmp	install_hi_bits
	ALIGN
put_max_nan:
	movb	sign1(%ebp),%al			// set result to larger
	movb	sign2(%ebp),%ah			// magnitude result
	push	%eax				// save signs
	andb	$0x7f,msb_frac1
	andb	$0x7f,msb_frac2
subtract_fracs:
	movb	positive,sign1(%ebp)		// subtract the absolute
	movb	positive,sign2(%ebp)		// values of the two ops
	movb	$sub_op,operation_type(%ebp)
	push	%esi
	call	subadd
	pop	%esi
	pop	%eax
	movb	%al,sign1(%ebp)				// restore sign1 and give
	mov	$offset_operand1,%edi	// operand1 as answer
	cmpb	positive,result_sign(%ebp)		// if difference is +
	je	install_hi_bits
	movb	%ah,sign2(%ebp)				// restore sign2 and give
	mov	$offset_operand2,%edi	// operand2 as answer
install_hi_bits:
	orb	$0x0c0,msb(%ebp,%edi)
put_arith_result:
	call	put_result
	jmp	pop_free
	ALIGN
non_nan_supp_ops:
	movb	tag2(%ebp),%bl
	movb	tag1(%ebp),%bh
	testb	$0x04,%bh
	jz	op2_denorm_
	movb	$3,%bh				// indicate op1 is denorm
op2_denorm_:
	testb	$0x04,%bl
	jz	get_index
	movb	$3,%bl				// indicate op2 is denorm
get_index:
	and	$0x0303,%ebx	// form index to special operation table
				// bx=4*(4*masked_tag1 + masked_tag2),
				// where masked_tag =	0 for valid,
	shlb	$2,%bh		//	1 for zero,
				//2 for infinity
	addb	%bh,%bl		//	3 for denormd
	xorb	%bh,%bh
	shl	$2,%ebx
	mov	$0x40fb,%eax		// 64 byte/table -add_op
	addb	operation_type(%ebp),%al	// al = normalized type
	mulb	%ah			// (e)ax = operation offset
	add	%eax,%ebx		// (e)bx = case offset
	jmp	*%cs:special_table(%ebx)			// jump to special case
	ALIGN
exit_arith:
	ret
derror_with_first_operand:
	orb	denorm_mask, %gs:sr_errors 
	testb	denorm_mask, %gs:sr_masks
	jz	exit_arith			// if unmasked, return
first_operand:
	mov	$offset_operand1,%esi		// give first operand as
	jmp	set_result_to_operand	// result
	ALIGN
derror_with_neg_second_operand:
	orb	denorm_mask, %gs:sr_errors 
	testb	denorm_mask, %gs:sr_masks
	jz	exit_arith			// if unmasked, return
neg_second_operand:
	notb	sign2(%ebp)			// negate second operand
	jmp	second_operand			// result
	ALIGN
derror_with_second_operand:
	orb	denorm_mask, %gs:sr_errors 
	testb	denorm_mask, %gs:sr_masks
	jz	exit_arith			// if unmasked, return
second_operand:
	mov	$offset_operand2,%esi		//give second operand
set_result_to_operand:
	call	move_op_to_result
	jmp	go_to_finish_up
	ALIGN
//divide_into_zero:	; this section, for pseudo-zeroes, was
//		xor	ax,ax		; commented out when they became unsupp.
//		mov	edi,offset word_frac2 + 2	; if frac2 = 0, invalid
//		call	test_4w		; else, zero and xor
//		jz	invalid_error_detected		; signs as the result
denormd_exor_signed_zero:
	orb	denorm_mask, %gs:sr_errors 
	testb	denorm_mask, %gs:sr_masks
	jz	exit_arith				// if unmasked, return
exor_signed_zero:
	mov	$zero_pattern,%esi
	call	set_constant_result
	movb	special,result_tag(%ebp)
	jmp	set_exor_sign
	ALIGN
denormd_exor_signed_infinity:
	orb	denorm_mask, %gs:sr_errors 
	testb	denorm_mask, %gs:sr_masks
	jz	exit_arith				// if unmasked, return
	jmp	exor_signed_infinity
	ALIGN
divide_by_zero:
	orb	zero_divide_mask, %gs:sr_errors 
	testb	zero_divide_mask, %gs:sr_masks
	jz	exit_arith				// if unmasked, return
exor_signed_infinity:
	call	put_infinity			// else, give infinity
set_exor_sign:
	movb	positive,result_sign(%ebp)	// set sign to exclusive
	movb	sign1(%ebp),%ah	// or of operand signs
	cmpb	sign2(%ebp),%ah
	je	go_to_finish_up
	notb	result_sign(%ebp)
go_to_finish_up:
	jmp	finish_up
	ALIGN
add_sub_infinities:
	movb	sign2(%ebp),%ah	// add or sub magnitude?
	cmpb	$sub_op,operation_type(%ebp)		// add mag if add_op and
	jne	add_or_sub_mag_			// signs same, else sub
	notb	%ah				// comp sign2 if sub
add_or_sub_mag_:
	cmpb	sign1(%ebp),%ah
	je	first_operand		// first op res if add_mag
invalid_error_detected:
	orb	invalid_mask, %gs:sr_errors 
	testb	invalid_mask, %gs:sr_masks
	jz	exit_arith
	call	put_indefinite			// if masked, indefinite
	jmp	finish_up			// otherwise, just return
	ALIGN
//
//arith	endp
//
//a_med	ends
//
//	end