|
|
Darwin 0.1 Mach+BSD kernel
/* * 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.1 (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 "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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.