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

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

//$tt(*80387	emulator	+ + + r o u n d + + + *)
// **********************************************************************
//
//			r o u n d . 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 all rounding modes for all precisions
//
//	public procedures:
//		round			special_round_test
//		directed_round_test
//
//	internal procedures:
//		prec16_case		prec24_case
//		prec32_case		prec53_case
//		prec64_case
//
// ***************************************************************************
//
//...october 31, 1986...
//
//	.file	*a_mro*
//
//$nolist
#include	"fp_e80387.h"
//$list
//...declare status register segment...
//
	.data	//a_msr	segment	rw	public
//	extrn	%gs:sr_flags
//a_msr	ends
//
//	assume	%ds:a_msr
//
	.text	//a_med	segment	er	public
//
//	extrn	get_rnd_control
//
	.globl	round
	.globl	directed_round_test
	.globl	special_round_test
//
// ***********************************************************************
//			prec16_case:
//			************
//	function:
//		 sets up for rounding to 16 bits.
//
//	inputs:
//		assumes edi points to operand record.
//
//	outputs:
//		sets: dl if round or sticky bit set
//		      dh if guard bit set
//		      ah if lsb set
//		      cl to number of rounded bytes
//		      ch to pattern to add to low byte for rounding
//		and clears the guard, round, and sticky bytes.
//		doesnt affect al.
//
//	data accessed:
//
//	data changed:
//
//	procecures called:
//
// *************************************************************************
	ALIGN
prec16_case:	//proc			; initially, assume all guard,
	xchg	frac80+4(%ebp,%edi),%ecx// round, and sticky info zero
	and	%ecx,%ecx	// examine and clear grst word
	jns	get_round_sticky_16
	notb	%dh	// et guard byte
get_round_sticky_16:
	and	$0x7fffffff,%ecx
	or	frac80(%ebp,%edi),%ecx
	orw	(%ebp,%edi),%cx
	jz	get_lsb_16
	notb	%dl								// set round-sticky byte
get_lsb_16:
	xorb	%ah,%ah			// initially, assume lsb is zero
	testb	$0x01,frac80+8(%ebp,%edi)
	jz	set_rnd_info_16
	notb	%ah				// set lsb byte
	movl	$0,frac80(%ebp,%edi)
	movw	$0,(%ebp,%edi)
set_rnd_info_16:
	mov	$0x0102,%ecx			// the rounded result has 2 bytes
	ret				// the pattern to add is 01h
//prec16_case	endp
//
// *************************************************************************
//			prec24_case:
//			************
//	function:
//		sets up for rounding to 24 bits.
//
//	inputs:
//		assumes edi points to operand record.
//
//	outputs:
//		sets: dl if round or sticky bit set
//		      dh if guard bit set
//		      ah if lsb set
//		      cl to number of rounded bytes
//		      ch to pattern to add to low byte for rounding
//		and clears the guard, round, and sticky bytes.
//		doesnt affect al.
//
//	data accessed:
//
//	data changed:
//
// *************************************************************************
	ALIGN
prec24_case:	//proc			; initially, assume all guard,
	xchgb	frac32(%ebp,%edi),%cl	// round, and sticky info zero
	andb	%cl,%cl			// examine and clear grst word
	jns	get_round_sticky_24
	notb	%dh								// set guard byte
get_round_sticky_24:
	andb	$0x7f,%cl
	or	frac64(%ebp,%edi),%ecx
	or	(%ebp,%edi),%ecx
	jz	get_lsb_24
	notb	%dl								// set round-sticky byte
get_lsb_24:
	xorb	%ah,%ah			// initially, assume lsb is zero
	testb	$0x01,frac32+1(%ebp,%edi)
	jz	clear_sticky_bytes_24
	notb	%ah				// set lsb byte
clear_sticky_bytes_24:
	movl	$0,frac64(%ebp,%edi)
	movl	$0,(%ebp,%edi)
	mov	$0x0103,%ecx			// the rounded result has 3 bytes
	ret					// the pattern to add is 01h
//prec24_case	endp
//
// *****************************************************************************
//			prec32_case:
//			************
//	function:
//		sets up for rounding to 32 bits.
//
//	inputs:
//		assumes edi points to operand record.
//
//	outputs:
//		sets: dl if round or sticky bit set
//		      dh if guard bit set
//		      ah if lsb set
//		      cl to number of rounded bytes
//		      ch to pattern to add to low byte for rounding
//		and clears the guard, round, and sticky bytes.
//		doesnt affect al.
//
//	data accessed:
//
//	data changed:
//
// *************************************************************************
	ALIGN
prec32_case:	//proc			; initially, assume all guard,
	xchg	frac64(%ebp,%edi),%ecx	// round, and sticky info zero
	and	%ecx,%ecx
	jns	get_round_sticky_32
	notb	%dh			// set guard byte
get_round_sticky_32:
	and	$0x7fffffff,%ecx
	or	(%ebp,%edi),%ecx
	jz	get_lsb_32
	notb	%dl			// set round-sticky byte
get_lsb_32:
	xorb	%ah,%ah	// initially, assume lsb is zero
	testb	$0x01,frac32(%ebp,%edi)
	jz	clear_sticky_bytes_32
not_ah_clear_32:
	notb	%ah			// set lsb byte
clear_sticky_bytes_32:
	movl	$0,(%ebp,%edi)
	mov	$0x0104,%ecx// the rounded result has 4 bytes
	ret					// the pattern to add is 01h
//prec32_case	endp
//
// ***********************************************************************
//			prec53_case:
//			***********
//	function:
//		sets up for rounding to 53 bits.
//
//	inputs:
//		assumes edi points to operand record.
//
//	outputs:
//		sets: dl if round or sticky bit set
//		      dh if guard bit set
//		      ah if lsb set
//		      cl to number of rounded bytes
//		      ch to pattern to add to low byte for rounding
//		and clears the guard, round, and sticky bytes.
//		doesnt affect al.
//
//	data accessed:
//
//	data changed:
//
// ***************************************************************************
	ALIGN
prec53_case:	//proc			; initially, assume all guard,
	mov	frac64(%ebp,%edi),%ecx	// round, and sticky info zero
	testb	$0x04,%ch	// examine guard bit
	jz	get_round_sticky_53
	notb	%dh			// set guard byte
get_round_sticky_53:
	and	$0x03ff,%ecx
	or	(%ebp,%edi),%ecx
	jz	get_lsb_53
	notb	%dl			// set round-sticky byte
get_lsb_53:
	xorb	%ah,%ah	// initially, assume lsb is zero
	testb	$0x08,frac64+1(%ebp,%edi)
	jz	clear_grst_bits_53
	notb	%ah			// set lsb byte
clear_grst_bits_53:
	movl	$0,(%ebp,%edi)
	andw	$0x0f800,frac64(%ebp,%edi)
	mov	$0x0807,%ecx// the rounded result has 7 bytes
	ret					// the pattern to add is 08h
//prec53_case	endp
//
// ****************************************************************************
//			prec64_case:
//			************
//	function:
//		sets up for rounding to 64 bits.
//
//	inputs:
//		assumes edi points to operand record.
//
//	outputs:
//		sets: dl if round or sticky bit set
//		      dh if guard bit set
//		      ah if lsb set
//		      cl to number of rounded bytes
//		      ch to pattern to add to low byte for rounding
//		and clears the guard, round, and sticky bytes.
//		doesnt affect al.
//
//	data accessed:
//
//	data changed:
//
// *************************************************************************
	ALIGN
prec64_case:	//proc			; initially assume all guard,
	xchg	(%ebp,%edi),%ecx	// round, and sticky info zero
	and	%ecx,%ecx		// examine and clear grst word
	jns	get_round_sticky_64
	notb	%dh				// set guard byte
get_round_sticky_64:
	and	$0x7fffffff,%ecx
	jz	get_lsb_64
	notb	%dl								// set round-sticky byte
get_lsb_64:
	xorb	%ah,%ah			// initially, assume lsb is zero
	testb	$0x01,frac64(%ebp,%edi)
	jz	set_round_info_64
	notb	%ah				// set lsb byte
set_round_info_64:
	mov	$0x0108,%ecx			// rounded result has 8 bytes
common_return:
	ret					// the pattern to add is 01h
//prec64_case	endp
//
// **********************************************************************
//			round:
//			*****
//	function:
//		implements rounding.
//
//	inputs:
//		assumes: edi points to record to round, 
//		          dl contains rounding precision,
//			al is true if second rounding, false if first.
//
//	outputs:
//		on return, al is true if there was an high-bit carry-out
//		as a result of incrementing the significand to do the rounding.
//
//	data accessed:
//		added_one
//
//	data changed:
//		added_one
//
//	procedures called:
//		prec16_case			prec24_case
//		prec32_case			prec53_case
//		prec64_case			get_rnd_control
//
// ************************************************************************
precn_case:	
	.long	prec24_case,prec32_case,prec53_case,prec64_case,prec16_case
//
rnd_case:	
	.long	case_rnd_to_even,case_rnd_down,case_rnd_up,case_rnd_to_zero
//
	ALIGN
round:	//proc				; access unpacked status info
	xorb	%dh,%dh				// set to zero the following:
	movswl	%dx,%esi			// cl to num bytes in rnd frac
	shl	$2,%esi				// ch to pattern to add to low
	xorw	%dx,%dx				// byte in rounding
	xor	%ecx,%ecx			// dl if round or sticky set
// 		mov	(ebp+edi),dx		;*** zeroing low word
	call	*%cs:precn_case(%esi)	// dh if grd set, ah if lsb set
	andw	%dx,%dx				// if dl and dh both zero, then
	jnz	inexact_case		// result is exact
	cmpb	true,%al
	movb	%dl,%al				// no carry out from rounding
	jne	exact_1st_rnd
	movb	%al,rnd2_inexact
	ret
	ALIGN
exact_1st_rnd:
	movw	%dx,rnd_history
	movb	%al,added_one	// clear flag, no overflow
	ret
	ALIGN
//
inexact_case:
	cmpb	true,%al
	jne	inexact_1st_rnd
	movb	%al,rnd2_inexact
	jmp	get_rnd_case
	ALIGN
inexact_1st_rnd:
	movw	true,rnd_history// makes rnd1_inexact true, rnd2_inexact false
get_rnd_case:
	push	%eax				// save lsb/2nd rounding flags
	call	get_rnd_control	// round different rnd_control
	movzbl	%al,%ebx		// cases separately
	pop	%eax			// reload lsb/2nd rounding flags
	jmp	*%cs:rnd_case(%ebx)
	ALIGN
case_rnd_up:
	notb	%bh			//if + increment, else truncate
case_rnd_down:
	cmpb	%bh,sign(%ebp,%edi) //increment if neg,
	jne	do_increment		// truncate if pos
	jmp	case_rnd_to_zero
	ALIGN
case_rnd_to_even:
	movb	%dl,%bh				// save round-sticky info in bh
	orb	%ah,%dl				// if guard and (round or sticky
	cmpb	true,%al			// or lsb), set dx to round up
	jne	do_add_				// if 2nd round, if ((lsb=added)
//		mov	bl,	added_one	; and not (round or sticky)
//		cmp	al, rnd1_inexact	; and ((added=1) or last round
//		jne	p_error_0		; inexact)), round in opposite
//		mov	bl,	al	; al is true (0ffh), hence so is bl
// the preceding four instructions are equivalent to the next one
	movb	rnd1_inexact,%bl
p_error_0:
	cmpw	$0x00ff,%bx		// unless first round was inexact and
	jne	do_add_			// current round-sticky bits are clear,
					// decide how to round as for first round
	cmpb	added_one,%ah	// else, dh (guard) must be true here because
				// exact second rounds were ruled out earlier
	jne	do_add_			// branch if ah <> added_one
	incw	%dx			// here, dh = guard, dl = lsb
	jz	case_rnd_to_zero	// truncate
	jmp	do_increment
	ALIGN
do_add_:
	incw	%dx
	jz	do_increment
case_rnd_to_zero:
	movb	false,%al			// clear flag, indicate nothing
	movb	%al,added_one		// added in round (al = false)
//		and	%gs:sr_flags,not a_mask	; clear the a-bit 			---> defer
	jmp	set_inexact
	ALIGN
do_increment:
//		or	%gs:sr_flags,a_mask		; set the a-bit				---> defer
	movb	true,added_one		// set flag for 1 added in round
	movb	%ch,%dl				// put pattern to add in dl
	xorb	%ch,%ch				// ecx = num bytes in rounded res
	mov	$12,%ebx
	sub	%ecx,%ebx			// ebx =  num of low byte
	add	%ebx,%edi			// edi = true offset
	xorb	%al,%al
	addb	%dl,(%ebp,%edi)	// add pattern to low byte and
	inc	%edi					// propogate carries
	dec	%ecx
	FALLSTHRU
inc_loop:
	adcb	%al,(%ebp,%edi)
	inc	%edi
	LOOP(inc_loop)
	sbbb	%al,%al				// set al if carry set
	sub	$12,%edi			// restore edi to entry value
set_inexact:
//	jmp	set_p_error				---> defer
	ret
//round	endp
//
// ****************************************************************************
//			special_round_test:
//			******************
//	function:
//		test for rounding toward zero.
//
//	inputs:
//		assumes sign to be in al upon entry.
//
//	outputs:
//		returns true (in al) and clears zf if:
//		  (rnd_down and sign is positive) or
//		  (rnd_up and sign is negative);
//       else returns false (in al) and sets zf.
//
//	data accessed:
//
//	data changed:
//
//	procedures called:
//		get_rnd_control
//
// **************************************************************************
	ALIGN
special_round_test:	//proc
	movb	%al,%ah				// save sign
	call	get_rnd_control			//get rounding mode
	cmpb	rnd_down,%al
	jne	round_up_			// branch if not down
	andb	%ah,%ah
	jz	is_true				// true if +rnd_down
is_false:
	xorb	%al,%al				// sets zf.
	ret
	ALIGN
round_up_:
	cmpb	rnd_up,%al
	jne	is_false			// branch if not up
	andb	%ah,%ah
	jz	is_false			// branch if +rnd_up
is_true:
	orb	true,%al				// clears zf.
exit_round_test:
	ret
//special_round_test	endp
//
// **************************************************************************
//			directed_round_test:
//			*******************
//	function:
//		test for round control (up or down).
//
//	inputs:
//		none
//
//	outputs:
//		returns true (in al) and clears zf if the round control 
//		is rnd_up or rnd_down.  otherwise, returns false and sets zf.
//
//	data accessed:
//
//	data changed:
//
//	procedures called:
//		get_rnd_conrol
//
// *************************************************************************
	ALIGN
directed_round_test:	//proc
	call	get_rnd_control
	cmpb	rnd_down,%al
	je	is_true
	cmpb	rnd_up,%al
	je	is_true
	jmp	is_false
//directed_round_test	endp
//
//a_med	ends
//
//	end