📄 fp_util.s
字号:
/* * fp_util.S * * Copyright Roman Zippel, 1997. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * ALTERNATIVELY, this product may be distributed under the terms of * the GNU Public License, in which case the provisions of the GPL are * required INSTEAD OF the above restrictions. (This clause is * necessary due to a potential bad interaction between the GPL and * the restrictions contained in a BSD-style copyright.) * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */#include <linux/config.h>#include "fp_emu.h"/* * Here are lots of conversion and normalization functions mainly * used by fp_scan.S * Note that these functions are optimized for "normal" numbers, * these are handled first and exit as fast as possible, this is * especially important for fp_normalize_ext/fp_conv_ext2ext, as * it's called very often. * The register usage is optimized for fp_scan.S and which register * is currently at that time unused, be careful if you want change * something here. %d0 and %d1 is always usable, sometimes %d2 (or * only the lower half) most function have to return the %a0 * unmodified, so that the caller can immediatly reuse it. */ .globl fp_ill, fp_end | exits from fp_scan: | illegal instructionfp_ill: printf ,"fp_illegal\n" rts | completed instructionfp_end: tst.l (TASK_MM-8,%a2) jmi 1f tst.l (TASK_MM-4,%a2) jmi 1f tst.l (TASK_MM,%a2) jpl 2f1: printf ,"oops:%p,%p,%p\n",3,%a2@(TASK_MM-8),%a2@(TASK_MM-4),%a2@(TASK_MM)2: clr.l %d0 rts .globl fp_conv_long2ext, fp_conv_single2ext .globl fp_conv_double2ext, fp_conv_ext2ext .globl fp_normalize_ext, fp_normalize_double .globl fp_normalize_single, fp_normalize_single_fast .globl fp_conv_ext2double, fp_conv_ext2single .globl fp_conv_ext2long, fp_conv_ext2short .globl fp_conv_ext2byte .globl fp_finalrounding_single, fp_finalrounding_single_fast .globl fp_finalrounding_double .globl fp_finalrounding, fp_finaltest, fp_final/* * First several conversion functions from a source operand * into the extended format. Note, that only fp_conv_ext2ext * normalizes the number and is always called after the other * conversion functions, which only move the information into * fp_ext structure. */ | fp_conv_long2ext: | | args: %d0 = source (32-bit long) | %a0 = destination (ptr to struct fp_ext)fp_conv_long2ext: printf PCONV,"l2e: %p -> %p(",2,%d0,%a0 clr.l %d1 | sign defaults to zero tst.l %d0 jeq fp_l2e_zero | is source zero? jpl 1f | positive? moveq #1,%d1 neg.l %d01: swap %d1 move.w #0x3fff+31,%d1 move.l %d1,(%a0)+ | set sign / exp move.l %d0,(%a0)+ | set mantissa clr.l (%a0) subq.l #8,%a0 | restore %a0 printx PCONV,%a0@ printf PCONV,")\n" rts | source is zerofp_l2e_zero: clr.l (%a0)+ clr.l (%a0)+ clr.l (%a0) subq.l #8,%a0 printx PCONV,%a0@ printf PCONV,")\n" rts | fp_conv_single2ext | args: %d0 = source (single-precision fp value) | %a0 = dest (struct fp_ext *)fp_conv_single2ext: printf PCONV,"s2e: %p -> %p(",2,%d0,%a0 move.l %d0,%d1 lsl.l #8,%d0 | shift mantissa lsr.l #8,%d1 | exponent / sign lsr.l #7,%d1 lsr.w #8,%d1 jeq fp_s2e_small | zero / denormal? cmp.w #0xff,%d1 | NaN / Inf? jeq fp_s2e_large bset #31,%d0 | set explizit bit add.w #0x3fff-0x7f,%d1 | re-bias the exponent.9: move.l %d1,(%a0)+ | fp_ext.sign, fp_ext.exp move.l %d0,(%a0)+ | high lword of fp_ext.mant clr.l (%a0) | low lword = 0 subq.l #8,%a0 printx PCONV,%a0@ printf PCONV,")\n" rts | zeros and denormalizedfp_s2e_small: | exponent is zero, so explizit bit is already zero too tst.l %d0 jeq 9b move.w #0x4000-0x7f,%d1 jra 9b | infinities and NANfp_s2e_large: bclr #31,%d0 | clear explizit bit move.w #0x7fff,%d1 jra 9bfp_conv_double2ext:#ifdef FPU_EMU_DEBUG getuser.l %a1@(0),%d0,fp_err_ua2,%a1 getuser.l %a1@(4),%d1,fp_err_ua2,%a1 printf PCONV,"d2e: %p%p -> %p(",3,%d0,%d1,%a0#endif getuser.l (%a1)+,%d0,fp_err_ua2,%a1 move.l %d0,%d1 lsl.l #8,%d0 | shift high mantissa lsl.l #3,%d0 lsr.l #8,%d1 | exponent / sign lsr.l #7,%d1 lsr.w #5,%d1 jeq fp_d2e_small | zero / denormal? cmp.w #0x7ff,%d1 | NaN / Inf? jeq fp_d2e_large bset #31,%d0 | set explizit bit add.w #0x3fff-0x3ff,%d1 | re-bias the exponent.9: move.l %d1,(%a0)+ | fp_ext.sign, fp_ext.exp move.l %d0,(%a0)+ getuser.l (%a1)+,%d0,fp_err_ua2,%a1 move.l %d0,%d1 lsl.l #8,%d0 lsl.l #3,%d0 move.l %d0,(%a0) moveq #21,%d0 lsr.l %d0,%d1 or.l %d1,-(%a0) subq.l #4,%a0 printx PCONV,%a0@ printf PCONV,")\n" rts | zeros and denormalizedfp_d2e_small: | exponent is zero, so explizit bit is already zero too tst.l %d0 jeq 9b move.w #0x4000-0x3ff,%d1 jra 9b | infinities and NANfp_d2e_large: bclr #31,%d0 | clear explizit bit move.w #0x7fff,%d1 jra 9b | fp_conv_ext2ext: | originally used to get longdouble from userspace, now it's | called before arithmetic operations to make sure the number | is normalized [maybe rename it?]. | args: %a0 = dest (struct fp_ext *) | returns 0 in %d0 for a NaN, otherwise 1fp_conv_ext2ext: printf PCONV,"e2e: %p(",1,%a0 printx PCONV,%a0@ printf PCONV,"), " move.l (%a0)+,%d0 cmp.w #0x7fff,%d0 | Inf / NaN? jeq fp_e2e_large move.l (%a0),%d0 jpl fp_e2e_small | zero / denorm? | The high bit is set, so normalization is irrelevant.fp_e2e_checkround: subq.l #4,%a0#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC move.b (%a0),%d0 jne fp_e2e_round#endif printf PCONV,"%p(",1,%a0 printx PCONV,%a0@ printf PCONV,")\n" moveq #1,%d0 rts#ifdef CONFIG_M68KFPU_EMU_EXTRAPRECfp_e2e_round: fp_set_sr FPSR_EXC_INEX2 clr.b (%a0) move.w (FPD_RND,FPDATA),%d2 jne fp_e2e_roundother | %d2 == 0, round to nearest tst.b %d0 | test guard bit jpl 9f | zero is closer btst #0,(11,%a0) | test lsb bit jne fp_e2e_doroundup | round to infinity lsl.b #1,%d0 | check low bits jeq 9f | round to zerofp_e2e_doroundup: addq.l #1,(8,%a0) jcc 9f addq.l #1,(4,%a0) jcc 9f move.w #0x8000,(4,%a0) addq.w #1,(2,%a0)9: printf PNORM,"%p(",1,%a0 printx PNORM,%a0@ printf PNORM,")\n" rtsfp_e2e_roundother: subq.w #2,%d2 jcs 9b | %d2 < 2, round to zero jhi 1f | %d2 > 2, round to +infinity tst.b (1,%a0) | to -inf jne fp_e2e_doroundup | negative, round to infinity jra 9b | positive, round to zero1: tst.b (1,%a0) | to +inf jeq fp_e2e_doroundup | positive, round to infinity jra 9b | negative, round to zero#endif | zeros and subnormals: | try to normalize these anyway.fp_e2e_small: jne fp_e2e_small1 | high lword zero? move.l (4,%a0),%d0 jne fp_e2e_small2#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC clr.l %d0 move.b (-4,%a0),%d0 jne fp_e2e_small3#endif | Genuine zero. clr.w -(%a0) subq.l #2,%a0 printf PNORM,"%p(",1,%a0 printx PNORM,%a0@ printf PNORM,")\n" moveq #1,%d0 rts | definitely subnormal, need to shift all 64 bitsfp_e2e_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 %d21: move.w %d2,(%a0)+ move.w %d1,%d2 jeq fp_e2e_checkround | fancy 64-bit double-shift begins here 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_e2e_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_e2e_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_e2e_checkround | pathologically small subnormalfp_e2e_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 %d21: move.w %d2,(%a0)+ ext.l %d1 jeq fp_e2e_checkround clr.l (4,%a0) sub.w #32,%d2 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_e2e_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_e2e_checkround#else move.w %d1,%d2 jra fp_e2e_extra1 | These are extremely small numbers, that will mostly end up as zero | anyway, so this is only important for correct rounding.fp_e2e_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_e2e_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_e2e_checkround1: neg.w %d1 bfins %d0,(%a0){%d1,#8} jra fp_e2e_checkround2: lsl.l %d1,%d0 move.b %d0,(-4,%a0) lsr.l #8,%d0 move.b %d0,(7,%a0) jra fp_e2e_checkround#endif1: move.l %d0,%d1 | lower lword is splitted between lsl.l %d2,%d0 | higher and lower lword move.l %d0,(%a0) move.l %d1,%d0 neg.w %d2 add.w #32,%d2 lsr.l %d2,%d0 move.l %d0,-(%a0) jra fp_e2e_checkround | Infinities and NaNsfp_e2e_large: move.l (%a0)+,%d0 jne 3f1: tst.l (%a0) jne 4f moveq #1,%d02: subq.l #8,%a0 printf PCONV,"%p(",1,%a0 printx PCONV,%a0@ printf PCONV,")\n" rts | we have maybe a NaN, shift off the highest bit3: lsl.l #1,%d0 jeq 1b | we have a NaN, clear the return value4: clrl %d0 jra 2b/* * Normalization functions. Call these on the output of general * FP operators, and before any conversion into the destination * formats. fp_normalize_ext has always to be called first, the * following conversion functions expect an already normalized * number. */ | fp_normalize_ext: | normalize an extended in extended (unpacked) format, basically | it does the same as fp_conv_ext2ext, additionally it also does | the necessary postprocessing checks. | args: %a0 (struct fp_ext *) | NOTE: it does _not_ modify %a0/%a1 and the upper word of %d2fp_normalize_ext: printf PNORM,"ne: %p(",1,%a0 printx PNORM,%a0@ printf PNORM,"), " move.l (%a0)+,%d0 cmp.w #0x7fff,%d0 | Inf / NaN? jeq fp_ne_large move.l (%a0),%d0 jpl fp_ne_small | zero / denorm? | The high bit is set, so normalization is irrelevant.fp_ne_checkround: subq.l #4,%a0#ifdef CONFIG_M68KFPU_EMU_EXTRAPREC move.b (%a0),%d0 jne fp_ne_round#endif printf PNORM,"%p(",1,%a0 printx PNORM,%a0@ printf PNORM,")\n" rts#ifdef CONFIG_M68KFPU_EMU_EXTRAPRECfp_ne_round: fp_set_sr FPSR_EXC_INEX2 clr.b (%a0) move.w (FPD_RND,FPDATA),%d2 jne fp_ne_roundother | %d2 == 0, round to nearest tst.b %d0 | test guard bit jpl 9f | zero is closer btst #0,(11,%a0) | test lsb bit jne fp_ne_doroundup | round to infinity lsl.b #1,%d0 | check low bits jeq 9f | round to zerofp_ne_doroundup: addq.l #1,(8,%a0) jcc 9f addq.l #1,(4,%a0) jcc 9f addq.w #1,(2,%a0) move.w #0x8000,(4,%a0)9: printf PNORM,"%p(",1,%a0 printx PNORM,%a0@ printf PNORM,")\n" rtsfp_ne_roundother: subq.w #2,%d2 jcs 9b | %d2 < 2, round to zero jhi 1f | %d2 > 2, round to +infinity tst.b (1,%a0) | to -inf jne fp_ne_doroundup | negative, round to infinity
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -