⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fp_util.s

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 S
📖 第 1 页 / 共 3 页
字号:
/* * 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 + -