patch-2.1.126 linux/arch/mips/kernel/softfp.S

Next file: linux/arch/mips/kernel/syscall.c
Previous file: linux/arch/mips/kernel/signal.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.125/linux/arch/mips/kernel/softfp.S linux/arch/mips/kernel/softfp.S
@@ -0,0 +1,667 @@
+/* $Id: softfp.S,v 1.1 1998/07/14 09:33:48 ralf Exp $
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1998 by Ralf Baechle
+ *
+ * For now it's just a crude hack good enough to run certain fp programs like
+ * Mozilla.
+ * XXX: Handle MIPS II/III/IV/V enhancements, exceptions, ...
+ */
+#include <asm/regdef.h>
+#include <asm/asm.h>
+
+#ifndef __KERNEL__
+#define printk printf
+#endif
+
+#define LOCK_KERNEL
+#define UNLOCK_KERNEL
+
+/*
+ * This duplicates definitions from <linux/kernel.h>.
+ */
+#define KERN_EMERG      "<0>"   /* system is unusable                   */
+#define KERN_ALERT      "<1>"   /* action must be taken immediately     */
+#define KERN_CRIT       "<2>"   /* critical conditions                  */
+#define KERN_ERR        "<3>"   /* error conditions                     */
+#define KERN_WARNING    "<4>"   /* warning conditions                   */
+#define KERN_NOTICE     "<5>"   /* normal but significant condition     */
+#define KERN_INFO       "<6>"   /* informational                        */
+#define KERN_DEBUG      "<7>"   /* debug-level messages                 */
+
+/*
+ * This duplicates definitions from <asm/signal.h>
+ */
+#define SIGILL           4      /* Illegal instruction (ANSI).  */
+
+/*
+ * Definitions about the instruction format
+ */
+#define fd_shift	6
+#define fr_shift	21
+#define fs_shift	11
+#define ft_shift	16
+
+/*
+ * NaNs as use by the MIPS architecture
+ */
+#define S_QNaN		0x7fbfffff
+#define D_QNaN		0x7ff7ffffffffffff
+#define W_QNaN		0x7fffffff
+#define L_QNaN		0x7fffffffffffffff
+
+/*
+ * Checking for NaNs
+ */
+#define S_is_QNaN(reg,res)						\
+	sll	res, reg, S_F_size - S_F_bits
+#define D_is_QNaN(reg1,reg2,res)					\
+	sll	res, reg1, (D_F_size - 32) - (D_F_bits - 32);		\
+	or	res, reg2
+
+/*
+ * Checking for Denorms
+ */
+#define S_is_Denorm(reg,res)						\
+	li	res, 1 << (S_F_bits - 1);				\
+	and	reg, res
+
+/*
+ * Some constants that define the properties of single precission numbers.
+ */
+#define S_M_prec	24
+#define S_E_max		127
+#define S_E_min		-126
+#define S_E_bias	127
+#define S_E_bits	8
+#define S_F_bits	23
+#define S_F_size	32
+
+/* Set temp0, if exponent of reg is S_E_max + 1.  */
+#define S_is_E_max(reg,temp0,temp1)					\
+	li	temp0, (S_E_max + 1 + S_E_bias) << S_F_bits;		\
+	and	temp1, temp0, reg;					\
+	seq	temp0, temp1			/* temp0 != 0 if NaN */
+
+/* Clear temp0, if exponent of reg is S_E_min - 1.  */
+#define S_is_E_min(reg,temp0)						\
+	li	temp0, (S_E_min - 1 + S_E_bias) << S_F_bits;		\
+	and	temp0, reg	/* temp0 == 0 if denorm or zero */
+
+/* Set temp0 if reg is a NaN assuming S_is_E_max is true  */
+#define S_get_F(reg,temp0)						\
+	li	temp0, (1 << S_F_bits) - 1;				\
+	and	temp0, reg			/* temp0 != 0 if NaN */
+
+/* Set res if fraction of reg is != 0.  */
+#define S_is_Inf(reg,res)						\
+	li	res, (1 << S_F_bits) - 1;				\
+	and	res, reg			/* temp0 == 0 if Inf */
+
+
+/*
+ * Some constants that define the properties of double precission numbers.
+ */
+#define D_M_prec	53
+#define D_E_max		1023
+#define D_E_min		-1022
+#define D_E_bias	1023
+#define D_E_bits	8
+#define D_F_bits	52
+#define D_F_size	64
+
+/* Set temp0, if exponent of reg1/reg2 is D_E_max.  */
+#define D_is_E_max(reg1,reg2,temp0,temp1)				\
+	li	temp0, (D_E_max + 1 + D_E_bias) << (D_F_bits - 32);	\
+	and	temp1, temp0, reg1;					\
+	seq	temp0, temp1			/* temp0 != 0 if NaN */
+
+/* Clear temp0, if exponent of reg is D_E_min.  */
+#define D_is_E_min(reg1,reg2,res)					\
+	li	res, (D_E_min + 1 + D_E_bias) << (D_F_bits - 32);	\
+	and	res, reg1	/* temp0 == 0 if NaN or zero */
+
+/* Set res if reg is a NaN assuming S_is_E_max is true  */
+#define D_get_F(reg1,reg2,res)						\
+	li	res, (1 << (D_F_bits - 32)) - 1;			\
+	and	res, reg1			/* temp0 != 0 if NaN */
+
+/* Set temp0 if reg1/reg2 is a NaN  */
+#define D_is_NAN(reg1,reg2,temp0,temp1)					\
+	li	temp0, (1 << (D_F_bits - 32) - 1;			\
+	and	temp0, reg1;						\
+	or	temp0, reg2;						\
+	sne	temp0, zero, temp0		/* temp0 != 0 if NaN */
+
+/* Set res if fraction of reg1/reg2 is != 0.  */
+#define D_is_Inf(reg1,reg2,res)						\
+	li	res, (1 << (D_F_bits - 32)) - 1;			\
+	and	res, reg1;						\
+	or	res, reg2			/* temp0 == 0 if Inf */
+
+/* Complain about yet unhandled instruction.  */
+#define BITCH(insn)							\
+insn:	LOCK_KERNEL;							\
+	la	a1, 8f;							\
+	TEXT(#insn);							\
+	la	a1, nosim;						\
+	UNLOCK_KERNEL;							\
+	j	done
+
+	.data
+nosim: .asciz	KERN_DEBUG "Don't know how to simulate %s instruction\n"
+	.previous
+
+/*
+ * When we come here, we've saved some of the integer registers and
+ * reenabled interrupts.
+ */
+LEAF(simfp)
+	.set	noreorder
+	.cpload	$25
+	.set	reorder
+
+	subu	sp, 16
+	.cprestore 20
+	sw	ra, 16(sp)
+
+	/* For now we assume that we get the opcode to simulate passed in as
+	   an argument.  */
+	move	t0, a0
+
+	/*
+	 * First table lookup using insn[5:0]
+	 */
+	la	t1, lowtab
+	andi	t2, t0, 0x3f
+	sll	t2, t2, 2
+	addu	t1, t2
+	lw	t1, (t1)
+	jr	t1
+	END(simfp)
+
+/*
+ * We only decode the lower 3 of the 5 bit in the fmt field.  That way we
+ * can keep the jump table significantly shorter.
+ */
+#define FMT_switch(insn,opc,temp0,temp1)				\
+insn:	srl	temp0, opc, 19;						\
+	andi	temp0, 0x1c;						\
+	la	temp1, insn ## .tab;					\
+	addu	temp0, temp1;						\
+	lw	temp0, (temp0);						\
+	jr	temp0;							\
+									\
+	.data;								\
+insn ## .tab:								\
+	.word	insn ## .s, insn ## .d, unimp, unimp;			\
+	.word	insn ## .w, insn ## .l, unimp, unimp;			\
+	.previous
+
+	BITCH(add)
+	BITCH(sub)
+	BITCH(mul)
+	BITCH(div)
+	BITCH(sqrt)
+	BITCH(abs)
+	BITCH(mov)
+	BITCH(neg)
+	BITCH(round.l)
+	BITCH(trunc.l)
+	BITCH(ceil.l)
+	BITCH(floor.l)
+	BITCH(round.w)
+	BITCH(trunc.w)
+	BITCH(ceil.w)
+	BITCH(floor.w)
+	BITCH(cvt.s)
+	BITCH(cvt.d)
+
+/* ------------------------------------------------------------------------ */
+
+FMT_switch(cvt.w,t0,t1,t2)
+
+/* Convert a single fp to a fixed point integer.  */
+cvt.w.s:
+	srl	t1, t0, fs_shift	# Get source register
+	andi	t1, 31
+	jal	s_get_fpreg
+
+	S_is_E_max(t1,t2,t3)
+	beqz	t2, 3f
+	/* Might be a NaN or Inf.  */
+	S_get_F(t1,t2)
+	beqz	t2, 2f
+
+	/* It's a NaN.  IEEE says undefined.  */
+	/* Is it a QNaN?  Then the result is a QNaN as well.  */
+	S_is_QNaN(t1,t2)
+	bltz	t2, 1f
+
+	/* XXX Ok, it's a SNaN.  Signal invalid exception, if enabled.
+	   For now we don't signal and supply a QNaN for result.  */
+
+1:	li	t2, W_QNaN
+	srl	t1, t0, fd_shift	# Put result register
+	andi	t1, 31
+	jal	s_put_fpreg
+	j	done
+2:
+
+	S_is_Inf(t1,t2)
+	bnez	t2, 2f
+
+	/* It's +/- Inf.  Set register to +/- max. integer.  */
+	/* XXX Send invalid operation exception instead, if enabled.  */
+	srl	t1, t1, 31		# Extract sign bit
+	li	t2, 0x7fffffff
+	addu	t2, t1
+
+	srl	t1, t0, fd_shift	# Put result register
+	andi	t1, 31
+	jal	s_put_fpreg
+	j	done
+2:
+3:	
+
+	/* But then it might be a denorm or zero?  */
+	S_is_E_min(t1,t2)
+	bnez	t2, 2f
+
+	/* Ok, it's a denorm or zero.  */
+	S_get_F(t1,t2)
+	beqz	t2, 1f
+
+	/* It's a denorm.  */
+	/* XXX Should be signaling inexact exception, if enabled.  */
+	/* Fall through.  */
+1:
+	/* Yes, it is a denorm or zero.  Supply a zero as result.  */
+	move	t2, zero
+	srl	t1, t0, fd_shift	# Put result register
+	andi	t1, 31
+	jal	s_put_fpreg
+	j	done
+2:
+
+	/* XXX Ok, it's a normal number.  We don't handle that case yet.
+	   If we have fp hardware this case is unreached.  Add this for
+	   full fp simulation.  */
+
+	/* Done, return.  */
+	lw	ra, 16(sp)
+	addu	sp, 16
+	jr	ra
+
+/* Convert a double fp to a fixed point integer.  */
+cvt.w.d:
+	srl	t1, t0, fs_shift	# Get source register
+	andi	t1, 31
+	jal	d_get_fpreg
+
+	D_is_E_max(t1,t2,t3,t4)
+	beqz	t3, 3f
+
+	/* Might be a NaN or Inf.  */
+	D_get_F(t1,t2,t3)
+	or	t3, t2
+	beqz	t3, 2f
+
+	/* It's a NaN.  IEEE says undefined.  */
+	/* Is it a QNaN?  Then the result is a QNaN as well.  */
+	D_is_QNaN(t1,t2,t3)
+	bltz	t3, 1f
+
+	/* XXX Ok, it's a SNaN.  Signal invalid exception, if enabled.
+	   For now we don't signal and supply a QNaN for result.  */
+
+1:	li	t2, W_QNaN
+	srl	t1, t0, fd_shift	# Put result register
+	andi	t1, 31
+	jal	s_put_fpreg
+	j	done
+2:
+
+	D_is_Inf(t1,t2,t3)
+	bnez	t3, 2f
+
+	/* It's +/- Inf.  Set register to +/- max. integer.  */
+	/* XXX Send invalid operation exception instead, if enabled.  */
+	srl	t1, t1, 31		# Extract sign bit
+	li	t2, 0x7fffffff
+	addu	t2, t1
+
+	srl	t1, t0, fd_shift	# Put result register
+	andi	t1, 31
+	jal	s_put_fpreg
+	j	done
+2:
+3:	
+
+	/* But then it might be a denorm or zero?  */
+	D_is_E_min(t1,t2,t3)
+	bnez	t3, 2f
+
+	/* Ok, it's a denorm or zero.  */
+	D_get_F(t1,t2,t3)
+	or	t3, t2
+	beqz	t3, 1f
+
+	/* It's a denorm.  */
+	/* XXX Should be signaling inexact exception, if enabled.  */
+	/* Fall through.  */
+1:
+	/* Yes, it is a denorm or zero.  Supply a zero as result.  */
+	move	t2, zero
+	srl	t1, t0, fd_shift	# Put result register
+	andi	t1, 31
+	jal	s_put_fpreg
+	j	done
+2:
+
+	/* XXX Ok, it's a normal number.  We don't handle that case yet.
+	   If we have fp hardware this case is only reached if the value
+	   of the source register exceeds the range which is representable
+	   in a single precission register.  For now we kludge by returning
+	   +/- maxint and don't signal overflow. */
+
+	srl	t1, t1, 31		# Extract sign bit
+	li	t2, 0x7fffffff
+	addu	t2, t1
+
+	srl	t1, t0, fd_shift	# Put result register
+	andi	t1, 31
+	jal	s_put_fpreg
+
+	/* Done, return.  */
+	lw	ra, 16(sp)
+	addu	sp, 16
+	jr	ra
+
+cvt.w.w = unimp				# undefined result
+cvt.w.l = unimp				# undefined result
+
+/* MIPS III extension, no need to handle for 32bit OS.  */
+cvt.l = unimp
+
+/* ------------------------------------------------------------------------ */
+
+	BITCH(c.f)
+	BITCH(c.un)
+	BITCH(c.eq)
+	BITCH(c.ueq)
+	BITCH(c.olt)
+	BITCH(c.ult)
+	BITCH(c.ole)
+	BITCH(c.ule)
+	BITCH(c.sf)
+	BITCH(c.ngle)
+	BITCH(c.seq)
+	BITCH(c.ngl)
+	BITCH(c.lt)
+	BITCH(c.nge)
+	BITCH(c.le)
+	BITCH(c.ngt)
+
+/* Get the single precission register which's number is in t1.  */
+s_get_fpreg:
+	.set	noat
+	sll	AT, t1, 2
+	sll	t1, 3
+	addu	t1, AT
+	la	AT, 1f
+	addu	AT, t1
+	jr	AT
+	.set	at
+
+1:	mfc1	t1, $0
+	jr	ra
+	mfc1	t1, $1
+	jr	ra
+	mfc1	t1, $2
+	jr	ra
+	mfc1	t1, $3
+	jr	ra
+	mfc1	t1, $4
+	jr	ra
+	mfc1	t1, $5
+	jr	ra
+	mfc1	t1, $6
+	jr	ra
+	mfc1	t1, $7
+	jr	ra
+	mfc1	t1, $8
+	jr	ra
+	mfc1	t1, $9
+	jr	ra
+	mfc1	t1, $10
+	jr	ra
+	mfc1	t1, $11
+	jr	ra
+	mfc1	t1, $12
+	jr	ra
+	mfc1	t1, $13
+	jr	ra
+	mfc1	t1, $14
+	jr	ra
+	mfc1	t1, $15
+	jr	ra
+	mfc1	t1, $16
+	jr	ra
+	mfc1	t1, $17
+	jr	ra
+	mfc1	t1, $18
+	jr	ra
+	mfc1	t1, $19
+	jr	ra
+	mfc1	t1, $20
+	jr	ra
+	mfc1	t1, $21
+	jr	ra
+	mfc1	t1, $22
+	jr	ra
+	mfc1	t1, $23
+	jr	ra
+	mfc1	t1, $24
+	jr	ra
+	mfc1	t1, $25
+	jr	ra
+	mfc1	t1, $26
+	jr	ra
+	mfc1	t1, $27
+	jr	ra
+	mfc1	t1, $28
+	jr	ra
+	mfc1	t1, $29
+	jr	ra
+	mfc1	t1, $30
+	jr	ra
+	mfc1	t1, $31
+	jr	ra
+
+/*
+ * Put the value in t2 into the single precission register which's number
+ * is in t1.
+ */
+s_put_fpreg:
+	.set	noat
+	sll	AT, t1, 2
+	sll	t1, 3
+	addu	t1, AT
+	la	AT, 1f
+	addu	AT, t1
+	jr	AT
+	.set	at
+	
+1:	mtc1	t2, $0
+	jr	ra
+	mtc1	t2, $1
+	jr	ra
+	mtc1	t2, $2
+	jr	ra
+	mtc1	t2, $3
+	jr	ra
+	mtc1	t2, $4
+	jr	ra
+	mtc1	t2, $5
+	jr	ra
+	mtc1	t2, $6
+	jr	ra
+	mtc1	t2, $7
+	jr	ra
+	mtc1	t2, $8
+	jr	ra
+	mtc1	t2, $9
+	jr	ra
+	mtc1	t2, $10
+	jr	ra
+	mtc1	t2, $11
+	jr	ra
+	mtc1	t2, $12
+	jr	ra
+	mtc1	t2, $13
+	jr	ra
+	mtc1	t2, $14
+	jr	ra
+	mtc1	t2, $15
+	jr	ra
+	mtc1	t2, $16
+	jr	ra
+	mtc1	t2, $17
+	jr	ra
+	mtc1	t2, $18
+	jr	ra
+	mtc1	t2, $19
+	jr	ra
+	mtc1	t2, $20
+	jr	ra
+	mtc1	t2, $21
+	jr	ra
+	mtc1	t2, $22
+	jr	ra
+	mtc1	t2, $23
+	jr	ra
+	mtc1	t2, $24
+	jr	ra
+	mtc1	t2, $25
+	jr	ra
+	mtc1	t2, $26
+	jr	ra
+	mtc1	t2, $27
+	jr	ra
+	mtc1	t2, $28
+	jr	ra
+	mtc1	t2, $29
+	jr	ra
+	mtc1	t2, $30
+	jr	ra
+	mtc1	t2, $31
+	jr	ra
+
+/* Get the double precission register which's number is in t1 into t1/t2.  */
+d_get_fpreg:
+	.set	noat
+	sll	t1, 3
+	la	AT, 1f
+	addu	AT, t1
+	jr	AT
+	.set	at
+
+1:	mfc1	t1, $0
+	mfc1	t2, $1
+	jr	ra
+	mfc1	t1, $2
+	mfc1	t2, $3
+	jr	ra
+	mfc1	t1, $4
+	mfc1	t2, $5
+	jr	ra
+	mfc1	t1, $6
+	mfc1	t2, $7
+	jr	ra
+	mfc1	t1, $8
+	mfc1	t2, $9
+	jr	ra
+	mfc1	t1, $10
+	mfc1	t2, $11
+	jr	ra
+	mfc1	t1, $12
+	mfc1	t2, $13
+	jr	ra
+	mfc1	t1, $14
+	mfc1	t2, $15
+	jr	ra
+	mfc1	t1, $16
+	mfc1	t2, $17
+	jr	ra
+	mfc1	t1, $18
+	mfc1	t2, $19
+	jr	ra
+	mfc1	t1, $20
+	mfc1	t2, $21
+	jr	ra
+	mfc1	t1, $22
+	mfc1	t2, $23
+	jr	ra
+	mfc1	t1, $24
+	mfc1	t2, $25
+	jr	ra
+	mfc1	t1, $26
+	mfc1	t2, $27
+	jr	ra
+	mfc1	t1, $28
+	mfc1	t2, $29
+	jr	ra
+	mfc1	t1, $30
+	mfc1	t2, $31
+	jr	ra
+
+/*
+ * Send an invalid operation exception.
+ */
+invalid:
+	lw	ra, 16(sp)
+	addu	sp, 16
+	jr	ra
+
+/*
+ * Done, just skip over the current instruction
+ */
+done:
+	lw	ra, 16(sp)
+	addu	sp, 16
+	jr	ra
+
+unimp:
+	/* We've run into an yet unknown instruction.  This happens either
+	   on new, yet unsupported CPU types or when the faulting instruction
+	   is being executed for cache but has been overwritten in memory.  */
+	LOCK_KERNEL
+	move	a0, t0
+	PRINT(KERN_DEBUG "FP support: unknown fp op %08lx, ")
+	PRINT("please mail to ralf@gnu.org.\n")
+
+	li	a0, SIGILL			# Die, sucker ...
+	move	a1, $28
+	jal	force_sig
+	UNLOCK_KERNEL
+
+	lw	ra, 16(sp)
+	addu	sp, 16
+	jr	ra
+
+/*
+ * Jump table for the lowest 6 bits of a cp1 instruction.
+ */
+	.data
+lowtab:	.word	add,   sub,   mul,   div,   sqrt,  abs,   mov,   neg
+	.word	round.l,trunc.l,ceil.l,floor.l,round.w,trunc.w,ceil.w,floor.w
+	.word	unimp, unimp, unimp, unimp, unimp, unimp, unimp, unimp
+	.word	unimp, unimp, unimp, unimp, unimp, unimp, unimp, unimp
+	.word	cvt.s, cvt.d, unimp, unimp, cvt.w, cvt.l, unimp, unimp
+	.word	unimp, unimp, unimp, unimp, unimp, unimp, unimp, unimp
+	.word	c.f,   c.un,  c.eq,  c.ueq, c.olt, c.ult, c.ole, c.ule
+	.word	c.sf,  c.ngle,c.seq, c.ngl, c.lt,  c.nge, c.le, c.ngt

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov