📄 fp_util.s
字号:
jra 9b | positive, round to zero1: tst.b (1,%a0) | to +inf jeq fp_ne_doroundup | positive, round to infinity jra 9b | negative, round to zero#endif | Zeros and subnormal numbers | These are probably merely subnormal, rather than "denormalized" | numbers, so we will try to make them normal again.fp_ne_small: jne fp_ne_small1 | high lword zero? move.l (4,%a0),%d0 jne fp_ne_small2#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC clr.l %d0 move.b (-4,%a0),%d0 jne fp_ne_small3#endif | Genuine zero. clr.w -(%a0) subq.l #2,%a0 printf PNORM,"%p(",1,%a0 printx PNORM,%a0@ printf PNORM,")\n" rts | Subnormal.fp_ne_small1: bfffo %d0{#0,#32},%d1 move.w -(%a0),%d2 sub.w %d1,%d2 jcc 1f | Pathologically small, denormalize. add.w %d2,%d1 clr.w %d2 fp_set_sr FPSR_EXC_UNFL1: move.w %d2,(%a0)+ move.w %d1,%d2 jeq fp_ne_checkround | This is exactly the same 64-bit double shift as seen above. lsl.l %d2,%d0 move.l %d0,(%a0)+ move.l (%a0),%d0 move.l %d0,%d1 lsl.l %d2,%d0 move.l %d0,(%a0) neg.w %d2 and.w #0x1f,%d2 lsr.l %d2,%d1 or.l %d1,-(%a0)#ifdef CONFIG_M68KFPU_EMU_EXTRAPRECfp_ne_extra1: clr.l %d0 move.b (-4,%a0),%d0 neg.w %d2 add.w #24,%d2 jcc 1f clr.b (-4,%a0) lsl.l %d2,%d0 or.l %d0,(4,%a0) jra fp_ne_checkround1: addq.w #8,%d2 lsl.l %d2,%d0 move.b %d0,(-4,%a0) lsr.l #8,%d0 or.l %d0,(4,%a0)#endif jra fp_ne_checkround | May or may not be subnormal, if so, only 32 bits to shift.fp_ne_small2: bfffo %d0{#0,#32},%d1 add.w #32,%d1 move.w -(%a0),%d2 sub.w %d1,%d2 jcc 1f | Beyond pathologically small, denormalize. add.w %d2,%d1 clr.w %d2 fp_set_sr FPSR_EXC_UNFL1: move.w %d2,(%a0)+ ext.l %d1 jeq fp_ne_checkround clr.l (4,%a0) sub.w #32,%d1 jcs 1f lsl.l %d1,%d0 | lower lword needs only to be shifted move.l %d0,(%a0) | into the higher lword#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC clr.l %d0 move.b (-4,%a0),%d0 clr.b (-4,%a0) neg.w %d1 add.w #32,%d1 bfins %d0,(%a0){%d1,#8}#endif jra fp_ne_checkround1: neg.w %d1 | lower lword is splitted between bfins %d0,(%a0){%d1,#32} | higher and lower lword#ifndef CONFIG_M68KFPU_EMU_EXTRAPREC jra fp_ne_checkround#else move.w %d1,%d2 jra fp_ne_extra1 | These are extremely small numbers, that will mostly end up as zero | anyway, so this is only important for correct rounding.fp_ne_small3: bfffo %d0{#24,#8},%d1 add.w #40,%d1 move.w -(%a0),%d2 sub.w %d1,%d2 jcc 1f | Pathologically small, denormalize. add.w %d2,%d1 clr.w %d21: move.w %d2,(%a0)+ ext.l %d1 jeq fp_ne_checkround cmp.w #8,%d1 jcs 2f1: clr.b (-4,%a0) sub.w #64,%d1 jcs 1f add.w #24,%d1 lsl.l %d1,%d0 move.l %d0,(%a0) jra fp_ne_checkround1: neg.w %d1 bfins %d0,(%a0){%d1,#8} jra fp_ne_checkround2: lsl.l %d1,%d0 move.b %d0,(-4,%a0) lsr.l #8,%d0 move.b %d0,(7,%a0) jra fp_ne_checkround#endif | Infinities and NaNs, again, same as above.fp_ne_large: move.l (%a0)+,%d0 jne 3f1: tst.l (%a0) jne 4f2: subq.l #8,%a0 printf PNORM,"%p(",1,%a0 printx PNORM,%a0@ printf PNORM,")\n" rts | we have maybe a NaN, shift off the highest bit3: move.l %d0,%d1 lsl.l #1,%d1 jne 4f clr.l (-4,%a0) jra 1b | we have a NaN, test if it is signaling4: bset #30,%d0 jne 2b fp_set_sr FPSR_EXC_SNAN move.l %d0,(-4,%a0) jra 2b | these next two do rounding as per the IEEE standard. | values for the rounding modes appear to be: | 0: Round to nearest | 1: Round to zero | 2: Round to -Infinity | 3: Round to +Infinity | both functions expect that fp_normalize was already | called (and extended argument is already normalized | as far as possible), these are used if there is different | rounding precision is selected and before converting | into single/double | fp_normalize_double: | normalize an extended with double (52-bit) precision | args: %a0 (struct fp_ext *)fp_normalize_double: printf PNORM,"nd: %p(",1,%a0 printx PNORM,%a0@ printf PNORM,"), " move.l (%a0)+,%d2 tst.w %d2 jeq fp_nd_zero | zero / denormalized cmp.w #0x7fff,%d2 jeq fp_nd_huge | NaN / infinitive. sub.w #0x4000-0x3ff,%d2 | will the exponent fit? jcs fp_nd_small | too small. cmp.w #0x7fe,%d2 jcc fp_nd_large | too big. addq.l #4,%a0 move.l (%a0),%d0 | low lword of mantissa | now, round off the low 11 bits.fp_nd_round: moveq #21,%d1 lsl.l %d1,%d0 | keep 11 low bits. jne fp_nd_checkround | Are they non-zero? | nothing to do here9: subq.l #8,%a0 printf PNORM,"%p(",1,%a0 printx PNORM,%a0@ printf PNORM,")\n" rts | Be careful with the X bit! It contains the lsb | from the shift above, it is needed for round to nearest.fp_nd_checkround: fp_set_sr FPSR_EXC_INEX2 | INEX2 bit and.w #0xf800,(2,%a0) | clear bits 0-10 move.w (FPD_RND,FPDATA),%d2 | rounding mode jne 2f | %d2 == 0, round to nearest tst.l %d0 | test guard bit jpl 9b | zero is closer | here we test the X bit by adding it to %d2 clr.w %d2 | first set z bit, addx only clears it addx.w %d2,%d2 | test lsb bit | IEEE754-specified "round to even" behaviour. If the guard | bit is set, then the number is odd, so rounding works like | in grade-school arithmetic (i.e. 1.5 rounds to 2.0) | Otherwise, an equal distance rounds towards zero, so as not | to produce an odd number. This is strange, but it is what | the standard says. jne fp_nd_doroundup | round to infinity lsl.l #1,%d0 | check low bits jeq 9b | round to zerofp_nd_doroundup: | round (the mantissa, that is) towards infinity add.l #0x800,(%a0) jcc 9b | no overflow, good. addq.l #1,-(%a0) | extend to high lword jcc 1f | no overflow, good. | Yow! we have managed to overflow the mantissa. Since this | only happens when %d1 was 0xfffff800, it is now zero, so | reset the high bit, and increment the exponent. move.w #0x8000,(%a0) addq.w #1,-(%a0) cmp.w #0x43ff,(%a0)+ | exponent now overflown? jeq fp_nd_large | yes, so make it infinity.1: subq.l #4,%a0 printf PNORM,"%p(",1,%a0 printx PNORM,%a0@ printf PNORM,")\n" rts2: subq.w #2,%d2 jcs 9b | %d2 < 2, round to zero jhi 3f | %d2 > 2, round to +infinity | Round to +Inf or -Inf. High word of %d2 contains the | sign of the number, by the way. swap %d2 | to -inf tst.b %d2 jne fp_nd_doroundup | negative, round to infinity jra 9b | positive, round to zero3: swap %d2 | to +inf tst.b %d2 jeq fp_nd_doroundup | positive, round to infinity jra 9b | negative, round to zero | Exponent underflow. Try to make a denormal, and set it to | the smallest possible fraction if this fails.fp_nd_small: fp_set_sr FPSR_EXC_UNFL | set UNFL bit move.w #0x3c01,(-2,%a0) | 2**-1022 neg.w %d2 | degree of underflow cmp.w #32,%d2 | single or double shift? jcc 1f | Again, another 64-bit double shift. move.l (%a0),%d0 move.l %d0,%d1 lsr.l %d2,%d0 move.l %d0,(%a0)+ move.l (%a0),%d0 lsr.l %d2,%d0 neg.w %d2 add.w #32,%d2 lsl.l %d2,%d1 or.l %d1,%d0 move.l (%a0),%d1 move.l %d0,(%a0) | Check to see if we shifted off any significant bits lsl.l %d2,%d1 jeq fp_nd_round | Nope, round. bset #0,%d0 | Yes, so set the "sticky bit". jra fp_nd_round | Now, round. | Another 64-bit single shift and store1: sub.w #32,%d2 cmp.w #32,%d2 | Do we really need to shift? jcc 2f | No, the number is too small. move.l (%a0),%d0 clr.l (%a0)+ move.l %d0,%d1 lsr.l %d2,%d0 neg.w %d2 add.w #32,%d2 | Again, check to see if we shifted off any significant bits. tst.l (%a0) jeq 1f bset #0,%d0 | Sticky bit.1: move.l %d0,(%a0) lsl.l %d2,%d1 jeq fp_nd_round bset #0,%d0 jra fp_nd_round | Sorry, the number is just too small.2: clr.l (%a0)+ clr.l (%a0) moveq #1,%d0 | Smallest possible fraction, jra fp_nd_round | round as desired. | zero and denormalizedfp_nd_zero: tst.l (%a0)+ jne 1f tst.l (%a0) jne 1f subq.l #8,%a0 printf PNORM,"%p(",1,%a0 printx PNORM,%a0@ printf PNORM,")\n" rts | zero. nothing to do. | These are not merely subnormal numbers, but true denormals, | i.e. pathologically small (exponent is 2**-16383) numbers. | It is clearly impossible for even a normal extended number | with that exponent to fit into double precision, so just | write these ones off as "too darn small".1: fp_set_sr FPSR_EXC_UNFL | Set UNFL bit clr.l (%a0) clr.l -(%a0) move.w #0x3c01,-(%a0) | i.e. 2**-1022 addq.l #6,%a0 moveq #1,%d0 jra fp_nd_round | round. | Exponent overflow. Just call it infinity.fp_nd_large: move.w #0x7ff,%d0 and.w (6,%a0),%d0 jeq 1f fp_set_sr FPSR_EXC_INEX21: fp_set_sr FPSR_EXC_OVFL move.w (FPD_RND,FPDATA),%d2 jne 3f | %d2 = 0 round to nearest1: move.w #0x7fff,(-2,%a0) clr.l (%a0)+ clr.l (%a0)2: subq.l #8,%a0 printf PNORM,"%p(",1,%a0 printx PNORM,%a0@ printf PNORM,")\n" rts3: subq.w #2,%d2 jcs 5f | %d2 < 2, round to zero jhi 4f | %d2 > 2, round to +infinity tst.b (-3,%a0) | to -inf jne 1b jra 5f4: tst.b (-3,%a0) | to +inf jeq 1b5: move.w #0x43fe,(-2,%a0) moveq #-1,%d0 move.l %d0,(%a0)+ move.w #0xf800,%d0 move.l %d0,(%a0) jra 2b | Infinities or NaNsfp_nd_huge: subq.l #4,%a0 printf PNORM,"%p(",1,%a0 printx PNORM,%a0@ printf PNORM,")\n" rts | fp_normalize_single: | normalize an extended with single (23-bit) precision | args: %a0 (struct fp_ext *)fp_normalize_single: printf PNORM,"ns: %p(",1,%a0 printx PNORM,%a0@ printf PNORM,") " addq.l #2,%a0 move.w (%a0)+,%d2 jeq fp_ns_zero | zero / denormalized cmp.w #0x7fff,%d2 jeq fp_ns_huge | NaN / infinitive. sub.w #0x4000-0x7f,%d2 | will the exponent fit? jcs fp_ns_small | too small. cmp.w #0xfe,%d2 jcc fp_ns_large | too big. move.l (%a0)+,%d0 | get high lword of mantissafp_ns_round: tst.l (%a0) | check the low lword jeq 1f | Set a sticky bit if it is non-zero. This should only | affect the rounding in what would otherwise be equal- | distance situations, which is what we want it to do. bset #0,%d01: clr.l (%a0) | zap it from memory. | now, round off the low 8 bits of the hi lword. tst.b %d0 | 8 low bits. jne fp_ns_checkround | Are they non-zero? | nothing to do here subq.l #8,%a0 printf PNORM,"%p(",1,%a0 printx PNORM,%a0@ printf PNORM,")\n" rtsfp_ns_checkround: fp_set_sr FPSR_EXC_INEX2 | INEX2 bit clr.b -(%a0) | clear low byte of high lword subq.l #3,%a0 move.w (FPD_RND,FPDATA),%d2 | rounding mode jne 2f | %d2 == 0, round to nearest tst.b %d0 | test guard bit jpl 9f | zero is closer btst #8,%d0 | test lsb bit | round to even behaviour, see above. jne fp_ns_doroundup | round to infinity lsl.b #1,%d0 | check low bits jeq 9f | round to zerofp_ns_doroundup: | round (the mantissa, that is) towards infinity add.l #0x100,(%a0) jcc 9f | no overflow, good. | Overflow. This means that the %d1 was 0xffffff00, so it | is now zero. We will set the mantissa to reflect this, and | increment the exponent (checking for overflow there too) move.w #0x8000,(%a0) addq.w #1,-(%a0) cmp.w #0x407f,(%a0)+ | exponent now overflown? jeq fp_ns_large | yes, so make it infinity.9: subq.l #4,%a0 printf PNORM,"%p(",1,%a0 printx PNORM,%a0@ printf PNORM,")\n" rts | check nondefault rounding modes2: subq.w #2,%d2 jcs 9b | %d2 < 2, round to zero jhi 3f | %d2 > 2, round to +infinity tst.b (-3,%a0) | to -inf jne fp_ns_doroundup | negative, round to infinity jra 9b | positive, round to zero3: tst.b (-3,%a0) | to +inf jeq fp_ns_doroundup | positive, round to infinity jra 9b | negative, round to zero | Exponent underflow. Try to make a denormal, and set it to | the smallest possible fraction if this fails.fp_ns_small: fp_set_sr FPSR_EXC_UNFL | set UNFL bit move.w #0x3f81,(-2,%a0) | 2**-126 neg.w %d2 | degree of underflow cmp.w #32,%d2 | single or double shift? jcc 2f | a 32-bit shift. move.l (%a0),%d0 move.l %d0,%d1 lsr.l %d2,%d0 move.l %d0,(%a0)+ | Check to see if we shifted off any significant bits. neg.w %d2 add.w #32,%d2 lsl.l %d2,%d1 jeq 1f bset #0,%d0 | Sticky bit. | Check the lower lword1: tst.l (%a0) jeq fp_ns_round clr (%a0) bset #0,%d0 | Sticky bit. jra fp_ns_round | Sorry, the number is just too small.2: clr.l (%a0)+ clr.l (%a0) moveq #1,%d0 | Smallest possible fraction, jra fp_ns_round | round as desired. | Exponent overflow. Just call it infinity.fp_ns_large: tst.b (3,%a0) jeq 1f fp_set_sr FPSR_EXC_INEX21: fp_set_sr FPSR_EXC_OVFL move.w (FPD_RND,FPDATA),%d2 jne 3f | %d2 = 0 round to nearest1: move.w #0x7fff,(-2,%a0) clr.l (%a0)+ clr.l (%a0)2: subq.l #8,%a0 printf PNORM,"%p(",1,%a0 printx PNORM,%a0@ printf PNORM,")\n" rts3: subq.w #2,%d2 jcs 5f | %d2 < 2, round to zero jhi 4f | %d2 > 2, round to +infinity
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -