diff options
Diffstat (limited to 'c/src/lib/libcpu/m68k/m68040/fpsp/decbin.S')
-rw-r--r-- | c/src/lib/libcpu/m68k/m68040/fpsp/decbin.S | 508 |
1 files changed, 508 insertions, 0 deletions
diff --git a/c/src/lib/libcpu/m68k/m68040/fpsp/decbin.S b/c/src/lib/libcpu/m68k/m68040/fpsp/decbin.S new file mode 100644 index 0000000000..734c1b6961 --- /dev/null +++ b/c/src/lib/libcpu/m68k/m68040/fpsp/decbin.S @@ -0,0 +1,508 @@ +// +// $Id$ +// +// decbin.sa 3.3 12/19/90 +// +// Description: Converts normalized packed bcd value pointed to by +// register A6 to extended-precision value in FP0. +// +// Input: Normalized packed bcd value in ETEMP(a6). +// +// Output: Exact floating-point representation of the packed bcd value. +// +// Saves and Modifies: D2-D5 +// +// Speed: The program decbin takes ??? cycles to execute. +// +// Object Size: +// +// External Reference(s): None. +// +// Algorithm: +// Expected is a normal bcd (i.e. non-exceptional; all inf, zero, +// and NaN operands are dispatched without entering this routine) +// value in 68881/882 format at location ETEMP(A6). +// +// A1. Convert the bcd exponent to binary by successive adds and muls. +// Set the sign according to SE. Subtract 16 to compensate +// for the mantissa which is to be interpreted as 17 integer +// digits, rather than 1 integer and 16 fraction digits. +// Note: this operation can never overflow. +// +// A2. Convert the bcd mantissa to binary by successive +// adds and muls in FP0. Set the sign according to SM. +// The mantissa digits will be converted with the decimal point +// assumed following the least-significant digit. +// Note: this operation can never overflow. +// +// A3. Count the number of leading/trailing zeros in the +// bcd string. If SE is positive, count the leading zeros; +// if negative, count the trailing zeros. Set the adjusted +// exponent equal to the exponent from A1 and the zero count +// added if SM = 1 and subtracted if SM = 0. Scale the +// mantissa the equivalent of forcing in the bcd value: +// +// SM = 0 a non-zero digit in the integer position +// SM = 1 a non-zero digit in Mant0, lsd of the fraction +// +// this will insure that any value, regardless of its +// representation (ex. 0.1E2, 1E1, 10E0, 100E-1), is converted +// consistently. +// +// A4. Calculate the factor 10^exp in FP1 using a table of +// 10^(2^n) values. To reduce the error in forming factors +// greater than 10^27, a directed rounding scheme is used with +// tables rounded to RN, RM, and RP, according to the table +// in the comments of the pwrten section. +// +// A5. Form the final binary number by scaling the mantissa by +// the exponent factor. This is done by multiplying the +// mantissa in FP0 by the factor in FP1 if the adjusted +// exponent sign is positive, and dividing FP0 by FP1 if +// it is negative. +// +// Clean up and return. Check if the final mul or div resulted +// in an inex2 exception. If so, set inex1 in the fpsr and +// check if the inex1 exception is enabled. If so, set d7 upper +// word to $0100. This will signal unimp.sa that an enabled inex1 +// exception occurred. Unimp will fix the stack. +// + +// Copyright (C) Motorola, Inc. 1990 +// All Rights Reserved +// +// THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF MOTOROLA +// The copyright notice above does not evidence any +// actual or intended publication of such source code. + +//DECBIN idnt 2,1 | Motorola 040 Floating Point Software Package + + |section 8 + +#include "fpsp.defs" + +// +// PTENRN, PTENRM, and PTENRP are arrays of powers of 10 rounded +// to nearest, minus, and plus, respectively. The tables include +// 10**{1,2,4,8,16,32,64,128,256,512,1024,2048,4096}. No rounding +// is required until the power is greater than 27, however, all +// tables include the first 5 for ease of indexing. +// + |xref PTENRN + |xref PTENRM + |xref PTENRP + +RTABLE: .byte 0,0,0,0 + .byte 2,3,2,3 + .byte 2,3,3,2 + .byte 3,2,2,3 + + .global decbin + .global calc_e + .global pwrten + .global calc_m + .global norm + .global ap_st_z + .global ap_st_n +// + .set FNIBS,7 + .set FSTRT,0 +// + .set ESTRT,4 + .set EDIGITS,2 // +// +// Constants in single precision +FZERO: .long 0x00000000 +FONE: .long 0x3F800000 +FTEN: .long 0x41200000 + + .set TEN,10 + +// +decbin: + | fmovel #0,FPCR ;clr real fpcr + moveml %d2-%d5,-(%a7) +// +// Calculate exponent: +// 1. Copy bcd value in memory for use as a working copy. +// 2. Calculate absolute value of exponent in d1 by mul and add. +// 3. Correct for exponent sign. +// 4. Subtract 16 to compensate for interpreting the mant as all integer digits. +// (i.e., all digits assumed left of the decimal point.) +// +// Register usage: +// +// calc_e: +// (*) d0: temp digit storage +// (*) d1: accumulator for binary exponent +// (*) d2: digit count +// (*) d3: offset pointer +// ( ) d4: first word of bcd +// ( ) a0: pointer to working bcd value +// ( ) a6: pointer to original bcd value +// (*) FP_SCR1: working copy of original bcd value +// (*) L_SCR1: copy of original exponent word +// +calc_e: + movel #EDIGITS,%d2 //# of nibbles (digits) in fraction part + moveql #ESTRT,%d3 //counter to pick up digits + leal FP_SCR1(%a6),%a0 //load tmp bcd storage address + movel ETEMP(%a6),(%a0) //save input bcd value + movel ETEMP_HI(%a6),4(%a0) //save words 2 and 3 + movel ETEMP_LO(%a6),8(%a0) //and work with these + movel (%a0),%d4 //get first word of bcd + clrl %d1 //zero d1 for accumulator +e_gd: + mulul #TEN,%d1 //mul partial product by one digit place + bfextu %d4{%d3:#4},%d0 //get the digit and zero extend into d0 + addl %d0,%d1 //d1 = d1 + d0 + addqb #4,%d3 //advance d3 to the next digit + dbf %d2,e_gd //if we have used all 3 digits, exit loop + btst #30,%d4 //get SE + beqs e_pos //don't negate if pos + negl %d1 //negate before subtracting +e_pos: + subl #16,%d1 //sub to compensate for shift of mant + bges e_save //if still pos, do not neg + negl %d1 //now negative, make pos and set SE + orl #0x40000000,%d4 //set SE in d4, + orl #0x40000000,(%a0) //and in working bcd +e_save: + movel %d1,L_SCR1(%a6) //save exp in memory +// +// +// Calculate mantissa: +// 1. Calculate absolute value of mantissa in fp0 by mul and add. +// 2. Correct for mantissa sign. +// (i.e., all digits assumed left of the decimal point.) +// +// Register usage: +// +// calc_m: +// (*) d0: temp digit storage +// (*) d1: lword counter +// (*) d2: digit count +// (*) d3: offset pointer +// ( ) d4: words 2 and 3 of bcd +// ( ) a0: pointer to working bcd value +// ( ) a6: pointer to original bcd value +// (*) fp0: mantissa accumulator +// ( ) FP_SCR1: working copy of original bcd value +// ( ) L_SCR1: copy of original exponent word +// +calc_m: + moveql #1,%d1 //word counter, init to 1 + fmoves FZERO,%fp0 //accumulator +// +// +// Since the packed number has a long word between the first & second parts, +// get the integer digit then skip down & get the rest of the +// mantissa. We will unroll the loop once. +// + bfextu (%a0){#28:#4},%d0 //integer part is ls digit in long word + faddb %d0,%fp0 //add digit to sum in fp0 +// +// +// Get the rest of the mantissa. +// +loadlw: + movel (%a0,%d1.L*4),%d4 //load mantissa longword into d4 + moveql #FSTRT,%d3 //counter to pick up digits + moveql #FNIBS,%d2 //reset number of digits per a0 ptr +md2b: + fmuls FTEN,%fp0 //fp0 = fp0 * 10 + bfextu %d4{%d3:#4},%d0 //get the digit and zero extend + faddb %d0,%fp0 //fp0 = fp0 + digit +// +// +// If all the digits (8) in that long word have been converted (d2=0), +// then inc d1 (=2) to point to the next long word and reset d3 to 0 +// to initialize the digit offset, and set d2 to 7 for the digit count; +// else continue with this long word. +// + addqb #4,%d3 //advance d3 to the next digit + dbf %d2,md2b //check for last digit in this lw +nextlw: + addql #1,%d1 //inc lw pointer in mantissa + cmpl #2,%d1 //test for last lw + ble loadlw //if not, get last one + +// +// Check the sign of the mant and make the value in fp0 the same sign. +// +m_sign: + btst #31,(%a0) //test sign of the mantissa + beqs ap_st_z //if clear, go to append/strip zeros + fnegx %fp0 //if set, negate fp0 + +// +// Append/strip zeros: +// +// For adjusted exponents which have an absolute value greater than 27*, +// this routine calculates the amount needed to normalize the mantissa +// for the adjusted exponent. That number is subtracted from the exp +// if the exp was positive, and added if it was negative. The purpose +// of this is to reduce the value of the exponent and the possibility +// of error in calculation of pwrten. +// +// 1. Branch on the sign of the adjusted exponent. +// 2p.(positive exp) +// 2. Check M16 and the digits in lwords 2 and 3 in descending order. +// 3. Add one for each zero encountered until a non-zero digit. +// 4. Subtract the count from the exp. +// 5. Check if the exp has crossed zero in #3 above; make the exp abs +// and set SE. +// 6. Multiply the mantissa by 10**count. +// 2n.(negative exp) +// 2. Check the digits in lwords 3 and 2 in descending order. +// 3. Add one for each zero encountered until a non-zero digit. +// 4. Add the count to the exp. +// 5. Check if the exp has crossed zero in #3 above; clear SE. +// 6. Divide the mantissa by 10**count. +// +// *Why 27? If the adjusted exponent is within -28 < expA < 28, than +// any adjustment due to append/strip zeros will drive the resultant +// exponent towards zero. Since all pwrten constants with a power +// of 27 or less are exact, there is no need to use this routine to +// attempt to lessen the resultant exponent. +// +// Register usage: +// +// ap_st_z: +// (*) d0: temp digit storage +// (*) d1: zero count +// (*) d2: digit count +// (*) d3: offset pointer +// ( ) d4: first word of bcd +// (*) d5: lword counter +// ( ) a0: pointer to working bcd value +// ( ) FP_SCR1: working copy of original bcd value +// ( ) L_SCR1: copy of original exponent word +// +// +// First check the absolute value of the exponent to see if this +// routine is necessary. If so, then check the sign of the exponent +// and do append (+) or strip (-) zeros accordingly. +// This section handles a positive adjusted exponent. +// +ap_st_z: + movel L_SCR1(%a6),%d1 //load expA for range test + cmpl #27,%d1 //test is with 27 + ble pwrten //if abs(expA) <28, skip ap/st zeros + btst #30,(%a0) //check sign of exp + bnes ap_st_n //if neg, go to neg side + clrl %d1 //zero count reg + movel (%a0),%d4 //load lword 1 to d4 + bfextu %d4{#28:#4},%d0 //get M16 in d0 + bnes ap_p_fx //if M16 is non-zero, go fix exp + addql #1,%d1 //inc zero count + moveql #1,%d5 //init lword counter + movel (%a0,%d5.L*4),%d4 //get lword 2 to d4 + bnes ap_p_cl //if lw 2 is zero, skip it + addql #8,%d1 //and inc count by 8 + addql #1,%d5 //inc lword counter + movel (%a0,%d5.L*4),%d4 //get lword 3 to d4 +ap_p_cl: + clrl %d3 //init offset reg + moveql #7,%d2 //init digit counter +ap_p_gd: + bfextu %d4{%d3:#4},%d0 //get digit + bnes ap_p_fx //if non-zero, go to fix exp + addql #4,%d3 //point to next digit + addql #1,%d1 //inc digit counter + dbf %d2,ap_p_gd //get next digit +ap_p_fx: + movel %d1,%d0 //copy counter to d2 + movel L_SCR1(%a6),%d1 //get adjusted exp from memory + subl %d0,%d1 //subtract count from exp + bges ap_p_fm //if still pos, go to pwrten + negl %d1 //now its neg; get abs + movel (%a0),%d4 //load lword 1 to d4 + orl #0x40000000,%d4 // and set SE in d4 + orl #0x40000000,(%a0) // and in memory +// +// Calculate the mantissa multiplier to compensate for the striping of +// zeros from the mantissa. +// +ap_p_fm: + movel #PTENRN,%a1 //get address of power-of-ten table + clrl %d3 //init table index + fmoves FONE,%fp1 //init fp1 to 1 + moveql #3,%d2 //init d2 to count bits in counter +ap_p_el: + asrl #1,%d0 //shift lsb into carry + bccs ap_p_en //if 1, mul fp1 by pwrten factor + fmulx (%a1,%d3),%fp1 //mul by 10**(d3_bit_no) +ap_p_en: + addl #12,%d3 //inc d3 to next rtable entry + tstl %d0 //check if d0 is zero + bnes ap_p_el //if not, get next bit + fmulx %fp1,%fp0 //mul mantissa by 10**(no_bits_shifted) + bras pwrten //go calc pwrten +// +// This section handles a negative adjusted exponent. +// +ap_st_n: + clrl %d1 //clr counter + moveql #2,%d5 //set up d5 to point to lword 3 + movel (%a0,%d5.L*4),%d4 //get lword 3 + bnes ap_n_cl //if not zero, check digits + subl #1,%d5 //dec d5 to point to lword 2 + addql #8,%d1 //inc counter by 8 + movel (%a0,%d5.L*4),%d4 //get lword 2 +ap_n_cl: + movel #28,%d3 //point to last digit + moveql #7,%d2 //init digit counter +ap_n_gd: + bfextu %d4{%d3:#4},%d0 //get digit + bnes ap_n_fx //if non-zero, go to exp fix + subql #4,%d3 //point to previous digit + addql #1,%d1 //inc digit counter + dbf %d2,ap_n_gd //get next digit +ap_n_fx: + movel %d1,%d0 //copy counter to d0 + movel L_SCR1(%a6),%d1 //get adjusted exp from memory + subl %d0,%d1 //subtract count from exp + bgts ap_n_fm //if still pos, go fix mantissa + negl %d1 //take abs of exp and clr SE + movel (%a0),%d4 //load lword 1 to d4 + andl #0xbfffffff,%d4 // and clr SE in d4 + andl #0xbfffffff,(%a0) // and in memory +// +// Calculate the mantissa multiplier to compensate for the appending of +// zeros to the mantissa. +// +ap_n_fm: + movel #PTENRN,%a1 //get address of power-of-ten table + clrl %d3 //init table index + fmoves FONE,%fp1 //init fp1 to 1 + moveql #3,%d2 //init d2 to count bits in counter +ap_n_el: + asrl #1,%d0 //shift lsb into carry + bccs ap_n_en //if 1, mul fp1 by pwrten factor + fmulx (%a1,%d3),%fp1 //mul by 10**(d3_bit_no) +ap_n_en: + addl #12,%d3 //inc d3 to next rtable entry + tstl %d0 //check if d0 is zero + bnes ap_n_el //if not, get next bit + fdivx %fp1,%fp0 //div mantissa by 10**(no_bits_shifted) +// +// +// Calculate power-of-ten factor from adjusted and shifted exponent. +// +// Register usage: +// +// pwrten: +// (*) d0: temp +// ( ) d1: exponent +// (*) d2: {FPCR[6:5],SM,SE} as index in RTABLE; temp +// (*) d3: FPCR work copy +// ( ) d4: first word of bcd +// (*) a1: RTABLE pointer +// calc_p: +// (*) d0: temp +// ( ) d1: exponent +// (*) d3: PWRTxx table index +// ( ) a0: pointer to working copy of bcd +// (*) a1: PWRTxx pointer +// (*) fp1: power-of-ten accumulator +// +// Pwrten calculates the exponent factor in the selected rounding mode +// according to the following table: +// +// Sign of Mant Sign of Exp Rounding Mode PWRTEN Rounding Mode +// +// ANY ANY RN RN +// +// + + RP RP +// - + RP RM +// + - RP RM +// - - RP RP +// +// + + RM RM +// - + RM RP +// + - RM RP +// - - RM RM +// +// + + RZ RM +// - + RZ RM +// + - RZ RP +// - - RZ RP +// +// +pwrten: + movel USER_FPCR(%a6),%d3 //get user's FPCR + bfextu %d3{#26:#2},%d2 //isolate rounding mode bits + movel (%a0),%d4 //reload 1st bcd word to d4 + asll #2,%d2 //format d2 to be + bfextu %d4{#0:#2},%d0 // {FPCR[6],FPCR[5],SM,SE} + addl %d0,%d2 //in d2 as index into RTABLE + leal RTABLE,%a1 //load rtable base + moveb (%a1,%d2),%d0 //load new rounding bits from table + clrl %d3 //clear d3 to force no exc and extended + bfins %d0,%d3{#26:#2} //stuff new rounding bits in FPCR + fmovel %d3,%FPCR //write new FPCR + asrl #1,%d0 //write correct PTENxx table + bccs not_rp //to a1 + leal PTENRP,%a1 //it is RP + bras calc_p //go to init section +not_rp: + asrl #1,%d0 //keep checking + bccs not_rm + leal PTENRM,%a1 //it is RM + bras calc_p //go to init section +not_rm: + leal PTENRN,%a1 //it is RN +calc_p: + movel %d1,%d0 //copy exp to d0;use d0 + bpls no_neg //if exp is negative, + negl %d0 //invert it + orl #0x40000000,(%a0) //and set SE bit +no_neg: + clrl %d3 //table index + fmoves FONE,%fp1 //init fp1 to 1 +e_loop: + asrl #1,%d0 //shift next bit into carry + bccs e_next //if zero, skip the mul + fmulx (%a1,%d3),%fp1 //mul by 10**(d3_bit_no) +e_next: + addl #12,%d3 //inc d3 to next rtable entry + tstl %d0 //check if d0 is zero + bnes e_loop //not zero, continue shifting +// +// +// Check the sign of the adjusted exp and make the value in fp0 the +// same sign. If the exp was pos then multiply fp1*fp0; +// else divide fp0/fp1. +// +// Register Usage: +// norm: +// ( ) a0: pointer to working bcd value +// (*) fp0: mantissa accumulator +// ( ) fp1: scaling factor - 10**(abs(exp)) +// +norm: + btst #30,(%a0) //test the sign of the exponent + beqs mul //if clear, go to multiply +div: + fdivx %fp1,%fp0 //exp is negative, so divide mant by exp + bras end_dec +mul: + fmulx %fp1,%fp0 //exp is positive, so multiply by exp +// +// +// Clean up and return with result in fp0. +// +// If the final mul/div in decbin incurred an inex exception, +// it will be inex2, but will be reported as inex1 by get_op. +// +end_dec: + fmovel %FPSR,%d0 //get status register + bclrl #inex2_bit+8,%d0 //test for inex2 and clear it + fmovel %d0,%FPSR //return status reg w/o inex2 + beqs no_exc //skip this if no exc + orl #inx1a_mask,USER_FPSR(%a6) //set inex1/ainex +no_exc: + moveml (%a7)+,%d2-%d5 + rts + |end |