Source to machdep/i386/fp_emul/fp_subadd.s
/*
* 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 "subadd.s"
.ident "@(#)kern-fp:subadd.s 1.1"
//$tt(*80387 emulator + + + s u b a d d + + + *)
// ************************************************************************
//
// s u b a d 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:
// performs floating point add, subtract, or compare
// of unpacked, non-zero, valid numbers.
//
// public procedures:
// addx subx subadd
// sp_subadd compar mov_neg_frac
//
// **************************************************************************
//
//...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_flags,%gs:sr_errors
//a_msr ends
//
// assume %ds:a_msr
.text //a_med segment er public
//
// extrn sticky_right_shift,right_shift,test_6w
// extrn addition_normalize,subtraction_normalize
// extrn pop_free,clear_6w,get_rnd_control
// extrn set_i_error,affine_infinity_,set_s_bit
// extrn clear_s_bit,set_z_bit,clear_z_bit
// extrn i_error_,set_d_error,set_stk_u_error
//
.globl addx
.globl subx
.globl subadd
.globl sp_subadd
.globl compar
//
// ***********************************************************************
// addx:
// *****
// function:
// fractional add. result_frac <-- frac1 + frac2.
//
// inputs:
// assumes operand records are set up.
//
// outputs:
// al = 1 if there was a carry-out; al=0 otherwise.
//
// data accessed:
// - word_frac1 word_frac2
// - result_word_frac
//
// data changed:
// result_word_frac
//
// ***********************************************************************
ALIGN
addx: //proc
mov dword_frac1(%ebp),%eax //do fractional add
xorw %ax,%ax //clear extra word
add dword_frac2(%ebp),%eax
mov %eax,result_dword_frac(%ebp)
mov dword_frac1+4(%ebp),%eax
adc dword_frac2+4(%ebp),%eax
mov %eax,result_dword_frac+4(%ebp)
mov dword_frac1+8(%ebp),%eax
adc dword_frac2+8(%ebp),%eax
mov %eax,result_dword_frac+8(%ebp)
movb $0,%al
rclb $1,%al //put carry into al
ret
//addx endp
//
// ****************************************************************************
// subx:
// ****
// function:
// 2s complement fractional subtract.
// result_frac <-- frac1 - frac2.
//
// inputs:
// assumes operand variables are set up.
//
// outputs:
// al=1 if borrow; al=0 otherwise.
//
// data accessed:
// - word_frac1 word_frac2
// - result_word_frac
//
// data changed:
// - result_word_frac
//
// ***************************************************************************
ALIGN
subx: //proc
mov dword_frac1(%ebp),%eax // fractional subtract
andl $0x0ffff0000,dword_frac2(%ebp) // clear low word
sub dword_frac2(%ebp),%eax
mov %eax,result_dword_frac(%ebp)
mov dword_frac1+4(%ebp),%eax
sbb dword_frac2+4(%ebp),%eax
mov %eax,result_dword_frac+4(%ebp)
mov dword_frac1+8(%ebp),%eax
sbb dword_frac2+8(%ebp),%eax
mov %eax,result_dword_frac+8(%ebp)
movb $0,%al //set al to borrow
rclb $1,%al
ret
//subx endp
//
// ***************************************************************************
// subadd:
// *******
// function:
// floating point add or subtract.calculates unpacked result.
// sp_subadd is the entry point for log functions.
//
// inputs:
// assumes operands are unpacked, valid, non-zero.
//
// outputs:
//
// data accessed:
// - operation_type sign1
// - expon1 msb_frac
// - offset_operand1 sign2
// - expon2 msb_frac2
// - offset_operand2 offset_result
// - result_sign result_expon
// - result_word_frac
//
// data changed:
// - result_sign result_expon
// - result_word_frac
//
// procedures called:
// sticky_right_shift right_shift
// addition_normlized subtraction_normalize
// addx subx
// test_6w get_rnd_control
//
// ************************************************************************
ALIGN
subadd: //proc
mov $sticky_right_shift,%esi // load shift proc ptr
jmp form_expon_difference // merge with sp_subadd
ALIGN
sp_subadd:
mov $sp_right_shift,%esi // load shift proc ptr
form_expon_difference:
mov dword_expon1,%ecx
sub dword_expon2,%ecx
js expon2_larger
cmp $67,%ecx //set cl to shift amount =
jbe adjust_frac2 //min(67,expon1-expon2)
mov $67,%ecx
adjust_frac2:
movb $0x00,%al //al = bit to inject from left
mov $offset_operand2,%edi
push %esi
call *%esi //shift op2 fraction
pop %esi
mov expon1(%ebp),%eax //set result expon to expon1
mov %eax,result_expon(%ebp)
cmpb special, tag1(%ebp)
je determine_operation_true
testb $0x80,msb_frac1
jnz determine_operation_true // branch if frac1 is normal
jmp frac2_normal_ // test frac2
ALIGN
expon2_larger:
neg %ecx //set cl to shift amount
cmp $67,%ecx
jbe adjust_frac1
mov $67,%ecx
adjust_frac1:
push %ecx //save shift amount
movb $0x00,%al //al = bit to inject
mov $offset_operand1,%edi
call *%esi
mov expon2(%ebp),%eax //set result expon to expon2
mov %eax,result_expon(%ebp)
pop %ecx
frac2_normal_:
cmpb special, tag2(%ebp)
je determine_operation_true
movb false,%al
testb $0x80,msb_frac2 // set al to true if frac2 is
jz determine_operation // normalized
determine_operation_true:
movb true,%al
determine_operation:
cmpb $sub_op,operation_type(%ebp) //if op=sub_op, negate op2
jne add_or_sub_
notb sign2(%ebp)
add_or_sub_:
movb sign1(%ebp),%ah
cmpb sign2(%ebp),%ah
jne do_subtraction
call addx //do fractional add
mov $offset_result,%edi
call addition_normalize //normalize result after add
movb sign1(%ebp),%al
movb %al,result_sign(%ebp) //set result sign to sign1
testb $0x80, msb_result
jnz add_done
mov $offset_result, %edi
call test_6w
jnz norm_result
add_done:
ret
ALIGN
do_subtraction:
push %eax //save normalization indicator
movb sign1(%ebp),%al //initially, set sign to sign1
movb %al,result_sign(%ebp)
call subx //do fractional subtract
mov $offset_result,%edi
cmpb $0,%al
mov $0,%eax
je detect_zero
notl (%ebp,%edi) //form 2s comp of result_frac
add $1,(%ebp,%edi)
notl 4(%ebp,%edi)
adc %eax,4(%ebp,%edi)
notl 8(%ebp,%edi)
adc %eax,8(%ebp,%edi)
notb result_sign(%ebp) //and complement result_sign
detect_zero:
call test_6w //find true zero result
jnz do_norm_ // if not zero, test for denorm
call get_rnd_control // sign is + unless rnd down
xorb rnd_down,%al
decw %ax
cbtw
movb %ah,result_sign(%ebp)
pop %eax //if al=true, then result should
cmpb true,%al // be true zero
jne subtract_done
movw $0,result_expon(%ebp)
ret
ALIGN
do_norm_:
pop %eax //if al=false, dont normalize
cmpb true,%al // the result
jne subtract_done
norm_result:
mov $offset_result,%edi
call subtraction_normalize //normalize the result
subtract_done:
ret
ALIGN
sp_right_shift:
call right_shift
mov (%ebp,%edi),%eax // ss:ebp+edi -> dword_frac after call
and $0x1fffffff,%eax // mask dword fraction
or %eax,%edx // or it to the right_shift output
jz sp_nonsticky // if zero, it must be nonsticky
orl $0x20000000,(%ebp,%edi) // if nonzero, set bit of dword_frac
sp_nonsticky:
andl $0x0e0000000,(%ebp,%edi) // now mask off most of dword_frac
ret
//subadd endp
//
// ***********************************************************************
// compar:
// *******
//
// function:
// main procedure for compare and test instructions.
//
// inputs:
// op1 and op2 operand records.
//
// outputs:
// sets zero and sign status bits to indicate the result
// of the compare. sets the invalid and denorm error bits
// if these errors are detected.
//
// data accessed:
// - operation_type tag1
// - expon1 tag2
// - expon2 result_sign
// - result_word_frac
//
// data changed:
//
// procedures called:
// pop_free subadd
// test_6w clear_6w
// set_s_bit clear_s_bit
// set_z_bit clear_z_bit
//
// ***************************************************************************
ALIGN
compar: //proc
jz no_stack_error // branch if no stack error
andb $~a_mask,%gs:sr_flags // clear a-bit
orb invalid_mask+zero_mask,%gs:sr_errors// clears zf, to 0
jmp invalid_error_signal
ALIGN
no_stack_error:
movl $0,before_error_signals(%ebp)
andb $~(a_mask+c_mask),%gs:sr_flags
cmpb $test_op,operation_type(%ebp) // if not test_op, then
jne set_up_check // its compar_op or ucom_op
movb positive,sign2(%ebp) // for test_op, set operand2
movb special,tag2(%ebp) // to zero
mov $dword_frac2,%edi
call clear_6w // returns with eax = 0
mov %eax,dword_expon2
set_up_check:
movb tag1(%ebp),%al // both operands valid?
orb tag2(%ebp),%al
jz ordered_cases // yes, ops are ordered.
cmpb special,%al // one op is zero, and the
je ordered_cases // other is zero or valid.
testb $0x10,%al // is either op unsupported?
jnz invalid_error_detected
op1_nan_:
movb tag1(%ebp),%al // al = value of tag1
movb tag2(%ebp),%ah // ah = value of tag2
cmpb inv,%al
je op1_snan_
cmpb inv,%ah
jne non_nan_supp_ops
jmp signal_invalid_
ALIGN
op1_snan_:
testb $0x40,msb_frac1
jz invalid_error_detected
op2_also_nan_:
cmpb inv,%ah
jne set_up_nan_return
signal_invalid_:
testb $0x40,msb_frac2
jz invalid_error_detected
set_up_nan_return:
cmpb $ucom_op,operation_type(%ebp) // if ucom_op, then
je set_uncertain // no invalid signal.
jmp invalid_error_detected
ALIGN
non_nan_supp_ops:
cmpb denormd,tag1(%ebp)
jne op2_denormd_
movb true,signal_d_error_
cmp $0,expon1(%ebp)
jne op2_denormd_
movl $1,expon1(%ebp)
op2_denormd_:
cmpb denormd,tag2(%ebp)
jne ordered_cases
movb true,signal_d_error_
cmp $0,expon2(%ebp)
jne ordered_cases
movl $1,expon2(%ebp)
ordered_cases:
movb $sub_op,operation_type(%ebp) // subtract op2 from op1
call subadd
mov $offset_result,%edi // detect a zero fraction
call test_6w // in result
jz operands_equal
andb $~(zero_mask+sign_mask),%gs:sr_flags // set zero to false
testb $0x01,result_sign(%ebp) // set sign to result
jz finish_up
orb $sign_mask,%gs:sr_flags
finish_up:
cmp $0,before_error_signals(%ebp)
jne check_i_error
jmp pop_free
ALIGN
check_i_error:
cmpb false,signal_i_error_ // recall that false = 00h
je set_denorm_error
test_invalid_mask:
testb invalid_mask,%gs:sr_masks
jz unmasked_exit
jmp pop_free
ALIGN
unmasked_exit:
ret
ALIGN
check_d_error:
cmpb false,signal_d_error_ // recall that false = 00h
jne set_denorm_error
jmp pop_free
ALIGN
set_denorm_error:
orb denorm_mask,%gs:sr_errors
test_denorm_mask:
testb denorm_mask,%gs:sr_masks
jz unmasked_exit
jmp pop_free
ALIGN
invalid_error_detected:
orb invalid_mask,%gs:sr_errors
invalid_error_signal:
movl $0x000000ff,before_error_signals(%ebp)
jmp set_uncertain
ALIGN
also_d_error_:
cmpb denormd,tag1(%ebp)
je d_error_also
cmpb denormd,tag2(%ebp)
jne set_uncertain
d_error_also:
movb true, signal_d_error_
set_uncertain:
orb $(sign_mask + c_mask), %gs:sr_flags
jmp zero_true // set zero to true
ALIGN
operands_equal:
andb $~sign_mask,%gs:sr_flags // set sign positive
zero_true:
orb $zero_mask,%gs:sr_flags
jmp finish_up
//compar endp
//
//a_med ends
//
// end