File indexing completed on 2025-05-11 08:23:48
0001 #include "fpsp-namespace.h"
0002 //
0003 //
0004 // decbin.sa 3.3 12/19/90
0005 //
0006 // Description: Converts normalized packed bcd value pointed to by
0007 // register A6 to extended-precision value in FP0.
0008 //
0009 // Input: Normalized packed bcd value in ETEMP(a6).
0010 //
0011 // Output: Exact floating-point representation of the packed bcd value.
0012 //
0013 // Saves and Modifies: D2-D5
0014 //
0015 // Speed: The program decbin takes ??? cycles to execute.
0016 //
0017 // Object Size:
0018 //
0019 // External Reference(s): None.
0020 //
0021 // Algorithm:
0022 // Expected is a normal bcd (i.e. non-exceptional; all inf, zero,
0023 // and NaN operands are dispatched without entering this routine)
0024 // value in 68881/882 format at location ETEMP(A6).
0025 //
0026 // A1. Convert the bcd exponent to binary by successive adds and muls.
0027 // Set the sign according to SE. Subtract 16 to compensate
0028 // for the mantissa which is to be interpreted as 17 integer
0029 // digits, rather than 1 integer and 16 fraction digits.
0030 // Note: this operation can never overflow.
0031 //
0032 // A2. Convert the bcd mantissa to binary by successive
0033 // adds and muls in FP0. Set the sign according to SM.
0034 // The mantissa digits will be converted with the decimal point
0035 // assumed following the least-significant digit.
0036 // Note: this operation can never overflow.
0037 //
0038 // A3. Count the number of leading/trailing zeros in the
0039 // bcd string. If SE is positive, count the leading zeros;
0040 // if negative, count the trailing zeros. Set the adjusted
0041 // exponent equal to the exponent from A1 and the zero count
0042 // added if SM = 1 and subtracted if SM = 0. Scale the
0043 // mantissa the equivalent of forcing in the bcd value:
0044 //
0045 // SM = 0 a non-zero digit in the integer position
0046 // SM = 1 a non-zero digit in Mant0, lsd of the fraction
0047 //
0048 // this will insure that any value, regardless of its
0049 // representation (ex. 0.1E2, 1E1, 10E0, 100E-1), is converted
0050 // consistently.
0051 //
0052 // A4. Calculate the factor 10^exp in FP1 using a table of
0053 // 10^(2^n) values. To reduce the error in forming factors
0054 // greater than 10^27, a directed rounding scheme is used with
0055 // tables rounded to RN, RM, and RP, according to the table
0056 // in the comments of the pwrten section.
0057 //
0058 // A5. Form the final binary number by scaling the mantissa by
0059 // the exponent factor. This is done by multiplying the
0060 // mantissa in FP0 by the factor in FP1 if the adjusted
0061 // exponent sign is positive, and dividing FP0 by FP1 if
0062 // it is negative.
0063 //
0064 // Clean up and return. Check if the final mul or div resulted
0065 // in an inex2 exception. If so, set inex1 in the fpsr and
0066 // check if the inex1 exception is enabled. If so, set d7 upper
0067 // word to $0100. This will signal unimp.sa that an enabled inex1
0068 // exception occurred. Unimp will fix the stack.
0069 //
0070
0071 // Copyright (C) Motorola, Inc. 1990
0072 // All Rights Reserved
0073 //
0074 // THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF MOTOROLA
0075 // The copyright notice above does not evidence any
0076 // actual or intended publication of such source code.
0077
0078 //DECBIN idnt 2,1 | Motorola 040 Floating Point Software Package
0079
0080 |section 8
0081
0082 #include "fpsp.defs"
0083
0084 //
0085 // PTENRN, PTENRM, and PTENRP are arrays of powers of 10 rounded
0086 // to nearest, minus, and plus, respectively. The tables include
0087 // 10**{1,2,4,8,16,32,64,128,256,512,1024,2048,4096}. No rounding
0088 // is required until the power is greater than 27, however, all
0089 // tables include the first 5 for ease of indexing.
0090 //
0091 |xref PTENRN
0092 |xref PTENRM
0093 |xref PTENRP
0094
0095 RTABLE: .byte 0,0,0,0
0096 .byte 2,3,2,3
0097 .byte 2,3,3,2
0098 .byte 3,2,2,3
0099
0100 .global decbin
0101 .global calc_e
0102 .global pwrten
0103 .global calc_m
0104 .global norm
0105 .global ap_st_z
0106 .global ap_st_n
0107 //
0108 .set FNIBS,7
0109 .set FSTRT,0
0110 //
0111 .set ESTRT,4
0112 .set EDIGITS,2 //
0113 //
0114 // Constants in single precision
0115 FZERO: .long 0x00000000
0116 FONE: .long 0x3F800000
0117 FTEN: .long 0x41200000
0118
0119 .set TEN,10
0120
0121 //
0122 decbin:
0123 | fmovel #0,FPCR ;clr real fpcr
0124 moveml %d2-%d5,-(%a7)
0125 //
0126 // Calculate exponent:
0127 // 1. Copy bcd value in memory for use as a working copy.
0128 // 2. Calculate absolute value of exponent in d1 by mul and add.
0129 // 3. Correct for exponent sign.
0130 // 4. Subtract 16 to compensate for interpreting the mant as all integer digits.
0131 // (i.e., all digits assumed left of the decimal point.)
0132 //
0133 // Register usage:
0134 //
0135 // calc_e:
0136 // (*) d0: temp digit storage
0137 // (*) d1: accumulator for binary exponent
0138 // (*) d2: digit count
0139 // (*) d3: offset pointer
0140 // ( ) d4: first word of bcd
0141 // ( ) a0: pointer to working bcd value
0142 // ( ) a6: pointer to original bcd value
0143 // (*) FP_SCR1: working copy of original bcd value
0144 // (*) L_SCR1: copy of original exponent word
0145 //
0146 calc_e:
0147 movel #EDIGITS,%d2 //# of nibbles (digits) in fraction part
0148 moveql #ESTRT,%d3 //counter to pick up digits
0149 leal FP_SCR1(%a6),%a0 //load tmp bcd storage address
0150 movel ETEMP(%a6),(%a0) //save input bcd value
0151 movel ETEMP_HI(%a6),4(%a0) //save words 2 and 3
0152 movel ETEMP_LO(%a6),8(%a0) //and work with these
0153 movel (%a0),%d4 //get first word of bcd
0154 clrl %d1 //zero d1 for accumulator
0155 e_gd:
0156 mulul #TEN,%d1 //mul partial product by one digit place
0157 bfextu %d4{%d3:#4},%d0 //get the digit and zero extend into d0
0158 addl %d0,%d1 //d1 = d1 + d0
0159 addqb #4,%d3 //advance d3 to the next digit
0160 dbf %d2,e_gd //if we have used all 3 digits, exit loop
0161 btst #30,%d4 //get SE
0162 beqs e_pos //don't negate if pos
0163 negl %d1 //negate before subtracting
0164 e_pos:
0165 subl #16,%d1 //sub to compensate for shift of mant
0166 bges e_save //if still pos, do not neg
0167 negl %d1 //now negative, make pos and set SE
0168 orl #0x40000000,%d4 //set SE in d4,
0169 orl #0x40000000,(%a0) //and in working bcd
0170 e_save:
0171 movel %d1,L_SCR1(%a6) //save exp in memory
0172 //
0173 //
0174 // Calculate mantissa:
0175 // 1. Calculate absolute value of mantissa in fp0 by mul and add.
0176 // 2. Correct for mantissa sign.
0177 // (i.e., all digits assumed left of the decimal point.)
0178 //
0179 // Register usage:
0180 //
0181 // calc_m:
0182 // (*) d0: temp digit storage
0183 // (*) d1: lword counter
0184 // (*) d2: digit count
0185 // (*) d3: offset pointer
0186 // ( ) d4: words 2 and 3 of bcd
0187 // ( ) a0: pointer to working bcd value
0188 // ( ) a6: pointer to original bcd value
0189 // (*) fp0: mantissa accumulator
0190 // ( ) FP_SCR1: working copy of original bcd value
0191 // ( ) L_SCR1: copy of original exponent word
0192 //
0193 calc_m:
0194 moveql #1,%d1 //word counter, init to 1
0195 fmoves FZERO,%fp0 //accumulator
0196 //
0197 //
0198 // Since the packed number has a long word between the first & second parts,
0199 // get the integer digit then skip down & get the rest of the
0200 // mantissa. We will unroll the loop once.
0201 //
0202 bfextu (%a0){#28:#4},%d0 //integer part is ls digit in long word
0203 faddb %d0,%fp0 //add digit to sum in fp0
0204 //
0205 //
0206 // Get the rest of the mantissa.
0207 //
0208 loadlw:
0209 movel (%a0,%d1.L*4),%d4 //load mantissa longword into d4
0210 moveql #FSTRT,%d3 //counter to pick up digits
0211 moveql #FNIBS,%d2 //reset number of digits per a0 ptr
0212 md2b:
0213 fmuls FTEN,%fp0 //fp0 = fp0 * 10
0214 bfextu %d4{%d3:#4},%d0 //get the digit and zero extend
0215 faddb %d0,%fp0 //fp0 = fp0 + digit
0216 //
0217 //
0218 // If all the digits (8) in that long word have been converted (d2=0),
0219 // then inc d1 (=2) to point to the next long word and reset d3 to 0
0220 // to initialize the digit offset, and set d2 to 7 for the digit count;
0221 // else continue with this long word.
0222 //
0223 addqb #4,%d3 //advance d3 to the next digit
0224 dbf %d2,md2b //check for last digit in this lw
0225 nextlw:
0226 addql #1,%d1 //inc lw pointer in mantissa
0227 cmpl #2,%d1 //test for last lw
0228 ble loadlw //if not, get last one
0229
0230 //
0231 // Check the sign of the mant and make the value in fp0 the same sign.
0232 //
0233 m_sign:
0234 btst #31,(%a0) //test sign of the mantissa
0235 beq ap_st_z //if clear, go to append/strip zeros
0236 fnegx %fp0 //if set, negate fp0
0237
0238 //
0239 // Append/strip zeros:
0240 //
0241 // For adjusted exponents which have an absolute value greater than 27*,
0242 // this routine calculates the amount needed to normalize the mantissa
0243 // for the adjusted exponent. That number is subtracted from the exp
0244 // if the exp was positive, and added if it was negative. The purpose
0245 // of this is to reduce the value of the exponent and the possibility
0246 // of error in calculation of pwrten.
0247 //
0248 // 1. Branch on the sign of the adjusted exponent.
0249 // 2p.(positive exp)
0250 // 2. Check M16 and the digits in lwords 2 and 3 in descending order.
0251 // 3. Add one for each zero encountered until a non-zero digit.
0252 // 4. Subtract the count from the exp.
0253 // 5. Check if the exp has crossed zero in #3 above; make the exp abs
0254 // and set SE.
0255 // 6. Multiply the mantissa by 10**count.
0256 // 2n.(negative exp)
0257 // 2. Check the digits in lwords 3 and 2 in descending order.
0258 // 3. Add one for each zero encountered until a non-zero digit.
0259 // 4. Add the count to the exp.
0260 // 5. Check if the exp has crossed zero in #3 above; clear SE.
0261 // 6. Divide the mantissa by 10**count.
0262 //
0263 // *Why 27? If the adjusted exponent is within -28 < expA < 28, than
0264 // any adjustment due to append/strip zeros will drive the resultant
0265 // exponent towards zero. Since all pwrten constants with a power
0266 // of 27 or less are exact, there is no need to use this routine to
0267 // attempt to lessen the resultant exponent.
0268 //
0269 // Register usage:
0270 //
0271 // ap_st_z:
0272 // (*) d0: temp digit storage
0273 // (*) d1: zero count
0274 // (*) d2: digit count
0275 // (*) d3: offset pointer
0276 // ( ) d4: first word of bcd
0277 // (*) d5: lword counter
0278 // ( ) a0: pointer to working bcd value
0279 // ( ) FP_SCR1: working copy of original bcd value
0280 // ( ) L_SCR1: copy of original exponent word
0281 //
0282 //
0283 // First check the absolute value of the exponent to see if this
0284 // routine is necessary. If so, then check the sign of the exponent
0285 // and do append (+) or strip (-) zeros accordingly.
0286 // This section handles a positive adjusted exponent.
0287 //
0288 ap_st_z:
0289 movel L_SCR1(%a6),%d1 //load expA for range test
0290 cmpl #27,%d1 //test is with 27
0291 ble pwrten //if abs(expA) <28, skip ap/st zeros
0292 btst #30,(%a0) //check sign of exp
0293 bne ap_st_n //if neg, go to neg side
0294 clrl %d1 //zero count reg
0295 movel (%a0),%d4 //load lword 1 to d4
0296 bfextu %d4{#28:#4},%d0 //get M16 in d0
0297 bnes ap_p_fx //if M16 is non-zero, go fix exp
0298 addql #1,%d1 //inc zero count
0299 moveql #1,%d5 //init lword counter
0300 movel (%a0,%d5.L*4),%d4 //get lword 2 to d4
0301 bnes ap_p_cl //if lw 2 is zero, skip it
0302 addql #8,%d1 //and inc count by 8
0303 addql #1,%d5 //inc lword counter
0304 movel (%a0,%d5.L*4),%d4 //get lword 3 to d4
0305 ap_p_cl:
0306 clrl %d3 //init offset reg
0307 moveql #7,%d2 //init digit counter
0308 ap_p_gd:
0309 bfextu %d4{%d3:#4},%d0 //get digit
0310 bnes ap_p_fx //if non-zero, go to fix exp
0311 addql #4,%d3 //point to next digit
0312 addql #1,%d1 //inc digit counter
0313 dbf %d2,ap_p_gd //get next digit
0314 ap_p_fx:
0315 movel %d1,%d0 //copy counter to d2
0316 movel L_SCR1(%a6),%d1 //get adjusted exp from memory
0317 subl %d0,%d1 //subtract count from exp
0318 bges ap_p_fm //if still pos, go to pwrten
0319 negl %d1 //now its neg; get abs
0320 movel (%a0),%d4 //load lword 1 to d4
0321 orl #0x40000000,%d4 // and set SE in d4
0322 orl #0x40000000,(%a0) // and in memory
0323 //
0324 // Calculate the mantissa multiplier to compensate for the striping of
0325 // zeros from the mantissa.
0326 //
0327 ap_p_fm:
0328 movel #PTENRN,%a1 //get address of power-of-ten table
0329 clrl %d3 //init table index
0330 fmoves FONE,%fp1 //init fp1 to 1
0331 moveql #3,%d2 //init d2 to count bits in counter
0332 ap_p_el:
0333 asrl #1,%d0 //shift lsb into carry
0334 bccs ap_p_en //if 1, mul fp1 by pwrten factor
0335 fmulx (%a1,%d3),%fp1 //mul by 10**(d3_bit_no)
0336 ap_p_en:
0337 addl #12,%d3 //inc d3 to next rtable entry
0338 tstl %d0 //check if d0 is zero
0339 bnes ap_p_el //if not, get next bit
0340 fmulx %fp1,%fp0 //mul mantissa by 10**(no_bits_shifted)
0341 bra pwrten //go calc pwrten
0342 //
0343 // This section handles a negative adjusted exponent.
0344 //
0345 ap_st_n:
0346 clrl %d1 //clr counter
0347 moveql #2,%d5 //set up d5 to point to lword 3
0348 movel (%a0,%d5.L*4),%d4 //get lword 3
0349 bnes ap_n_cl //if not zero, check digits
0350 subl #1,%d5 //dec d5 to point to lword 2
0351 addql #8,%d1 //inc counter by 8
0352 movel (%a0,%d5.L*4),%d4 //get lword 2
0353 ap_n_cl:
0354 movel #28,%d3 //point to last digit
0355 moveql #7,%d2 //init digit counter
0356 ap_n_gd:
0357 bfextu %d4{%d3:#4},%d0 //get digit
0358 bnes ap_n_fx //if non-zero, go to exp fix
0359 subql #4,%d3 //point to previous digit
0360 addql #1,%d1 //inc digit counter
0361 dbf %d2,ap_n_gd //get next digit
0362 ap_n_fx:
0363 movel %d1,%d0 //copy counter to d0
0364 movel L_SCR1(%a6),%d1 //get adjusted exp from memory
0365 subl %d0,%d1 //subtract count from exp
0366 bgts ap_n_fm //if still pos, go fix mantissa
0367 negl %d1 //take abs of exp and clr SE
0368 movel (%a0),%d4 //load lword 1 to d4
0369 andl #0xbfffffff,%d4 // and clr SE in d4
0370 andl #0xbfffffff,(%a0) // and in memory
0371 //
0372 // Calculate the mantissa multiplier to compensate for the appending of
0373 // zeros to the mantissa.
0374 //
0375 ap_n_fm:
0376 movel #PTENRN,%a1 //get address of power-of-ten table
0377 clrl %d3 //init table index
0378 fmoves FONE,%fp1 //init fp1 to 1
0379 moveql #3,%d2 //init d2 to count bits in counter
0380 ap_n_el:
0381 asrl #1,%d0 //shift lsb into carry
0382 bccs ap_n_en //if 1, mul fp1 by pwrten factor
0383 fmulx (%a1,%d3),%fp1 //mul by 10**(d3_bit_no)
0384 ap_n_en:
0385 addl #12,%d3 //inc d3 to next rtable entry
0386 tstl %d0 //check if d0 is zero
0387 bnes ap_n_el //if not, get next bit
0388 fdivx %fp1,%fp0 //div mantissa by 10**(no_bits_shifted)
0389 //
0390 //
0391 // Calculate power-of-ten factor from adjusted and shifted exponent.
0392 //
0393 // Register usage:
0394 //
0395 // pwrten:
0396 // (*) d0: temp
0397 // ( ) d1: exponent
0398 // (*) d2: {FPCR[6:5],SM,SE} as index in RTABLE; temp
0399 // (*) d3: FPCR work copy
0400 // ( ) d4: first word of bcd
0401 // (*) a1: RTABLE pointer
0402 // calc_p:
0403 // (*) d0: temp
0404 // ( ) d1: exponent
0405 // (*) d3: PWRTxx table index
0406 // ( ) a0: pointer to working copy of bcd
0407 // (*) a1: PWRTxx pointer
0408 // (*) fp1: power-of-ten accumulator
0409 //
0410 // Pwrten calculates the exponent factor in the selected rounding mode
0411 // according to the following table:
0412 //
0413 // Sign of Mant Sign of Exp Rounding Mode PWRTEN Rounding Mode
0414 //
0415 // ANY ANY RN RN
0416 //
0417 // + + RP RP
0418 // - + RP RM
0419 // + - RP RM
0420 // - - RP RP
0421 //
0422 // + + RM RM
0423 // - + RM RP
0424 // + - RM RP
0425 // - - RM RM
0426 //
0427 // + + RZ RM
0428 // - + RZ RM
0429 // + - RZ RP
0430 // - - RZ RP
0431 //
0432 //
0433 pwrten:
0434 movel USER_FPCR(%a6),%d3 //get user's FPCR
0435 bfextu %d3{#26:#2},%d2 //isolate rounding mode bits
0436 movel (%a0),%d4 //reload 1st bcd word to d4
0437 asll #2,%d2 //format d2 to be
0438 bfextu %d4{#0:#2},%d0 // {FPCR[6],FPCR[5],SM,SE}
0439 addl %d0,%d2 //in d2 as index into RTABLE
0440 leal RTABLE,%a1 //load rtable base
0441 moveb (%a1,%d2),%d0 //load new rounding bits from table
0442 clrl %d3 //clear d3 to force no exc and extended
0443 bfins %d0,%d3{#26:#2} //stuff new rounding bits in FPCR
0444 fmovel %d3,%FPCR //write new FPCR
0445 asrl #1,%d0 //write correct PTENxx table
0446 bccs not_rp //to a1
0447 leal PTENRP,%a1 //it is RP
0448 bras calc_p //go to init section
0449 not_rp:
0450 asrl #1,%d0 //keep checking
0451 bccs not_rm
0452 leal PTENRM,%a1 //it is RM
0453 bras calc_p //go to init section
0454 not_rm:
0455 leal PTENRN,%a1 //it is RN
0456 calc_p:
0457 movel %d1,%d0 //copy exp to d0;use d0
0458 bpls no_neg //if exp is negative,
0459 negl %d0 //invert it
0460 orl #0x40000000,(%a0) //and set SE bit
0461 no_neg:
0462 clrl %d3 //table index
0463 fmoves FONE,%fp1 //init fp1 to 1
0464 e_loop:
0465 asrl #1,%d0 //shift next bit into carry
0466 bccs e_next //if zero, skip the mul
0467 fmulx (%a1,%d3),%fp1 //mul by 10**(d3_bit_no)
0468 e_next:
0469 addl #12,%d3 //inc d3 to next rtable entry
0470 tstl %d0 //check if d0 is zero
0471 bnes e_loop //not zero, continue shifting
0472 //
0473 //
0474 // Check the sign of the adjusted exp and make the value in fp0 the
0475 // same sign. If the exp was pos then multiply fp1*fp0;
0476 // else divide fp0/fp1.
0477 //
0478 // Register Usage:
0479 // norm:
0480 // ( ) a0: pointer to working bcd value
0481 // (*) fp0: mantissa accumulator
0482 // ( ) fp1: scaling factor - 10**(abs(exp))
0483 //
0484 norm:
0485 btst #30,(%a0) //test the sign of the exponent
0486 beqs mul //if clear, go to multiply
0487 div:
0488 fdivx %fp1,%fp0 //exp is negative, so divide mant by exp
0489 bras end_dec
0490 mul:
0491 fmulx %fp1,%fp0 //exp is positive, so multiply by exp
0492 //
0493 //
0494 // Clean up and return with result in fp0.
0495 //
0496 // If the final mul/div in decbin incurred an inex exception,
0497 // it will be inex2, but will be reported as inex1 by get_op.
0498 //
0499 end_dec:
0500 fmovel %FPSR,%d0 //get status register
0501 bclrl #inex2_bit+8,%d0 //test for inex2 and clear it
0502 fmovel %d0,%FPSR //return status reg w/o inex2
0503 beqs no_exc //skip this if no exc
0504 orl #inx1a_mask,USER_FPSR(%a6) //set inex1/ainex
0505 no_exc:
0506 moveml (%a7)+,%d2-%d5
0507 rts
0508 |end