Module Name:    src
Committed By:   rin
Date:           Sun Sep  4 13:14:57 UTC 2022

Modified Files:
        src/sys/arch/powerpc/fpu: fpu_compare.c fpu_emu.c fpu_emu.h
            fpu_implode.c

Log Message:
Fix logic for FI, FR, and FPRF fields of FPSCR.

They are not sticky bits and updated by arithmetic and round
insns at the same time.

Comparison insns update only FPCC sub-field of FPRF.

For other insns, these field are left untouched.

Also, for single-precision insns, exception bits should be set by
the first fpu_implode(), which rounds the value to float.


To generate a diff of this commit:
cvs rdiff -u -r1.5 -r1.6 src/sys/arch/powerpc/fpu/fpu_compare.c
cvs rdiff -u -r1.49 -r1.50 src/sys/arch/powerpc/fpu/fpu_emu.c
cvs rdiff -u -r1.8 -r1.9 src/sys/arch/powerpc/fpu/fpu_emu.h
cvs rdiff -u -r1.20 -r1.21 src/sys/arch/powerpc/fpu/fpu_implode.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/powerpc/fpu/fpu_compare.c
diff -u src/sys/arch/powerpc/fpu/fpu_compare.c:1.5 src/sys/arch/powerpc/fpu/fpu_compare.c:1.6
--- src/sys/arch/powerpc/fpu/fpu_compare.c:1.5	Sat Jun 27 03:07:57 2020
+++ src/sys/arch/powerpc/fpu/fpu_compare.c	Sun Sep  4 13:14:57 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: fpu_compare.c,v 1.5 2020/06/27 03:07:57 rin Exp $ */
+/*	$NetBSD: fpu_compare.c,v 1.6 2022/09/04 13:14:57 rin Exp $ */
 
 /*
  * Copyright (c) 1992, 1993
@@ -48,7 +48,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: fpu_compare.c,v 1.5 2020/06/27 03:07:57 rin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: fpu_compare.c,v 1.6 2022/09/04 13:14:57 rin Exp $");
 
 #include <sys/types.h>
 
@@ -79,6 +79,8 @@ fpu_compare(struct fpemu *fe, int ordere
 	struct fpn *a, *b, *r;
 	int cc;
 
+	fe->fe_fpscr &= ~FPSCR_FPCC;
+
 	a = &fe->fe_f1;
 	b = &fe->fe_f2;
 	r = &fe->fe_f3;

Index: src/sys/arch/powerpc/fpu/fpu_emu.c
diff -u src/sys/arch/powerpc/fpu/fpu_emu.c:1.49 src/sys/arch/powerpc/fpu/fpu_emu.c:1.50
--- src/sys/arch/powerpc/fpu/fpu_emu.c:1.49	Sat Sep  3 04:54:47 2022
+++ src/sys/arch/powerpc/fpu/fpu_emu.c	Sun Sep  4 13:14:57 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: fpu_emu.c,v 1.49 2022/09/03 04:54:47 rin Exp $ */
+/*	$NetBSD: fpu_emu.c,v 1.50 2022/09/04 13:14:57 rin Exp $ */
 
 /*
  * Copyright 2001 Wasabi Systems, Inc.
@@ -76,7 +76,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: fpu_emu.c,v 1.49 2022/09/03 04:54:47 rin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: fpu_emu.c,v 1.50 2022/09/04 13:14:57 rin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ddb.h"
@@ -297,7 +297,7 @@ fpu_execute(struct trapframe *tf, struct
 	int ra, rb, rc, rt, type, mask, fsr, cx, bf, setcr, cond;
 	u_int bits;
 	struct fpreg *fs;
-	int i, mtfsb1 = 0;
+	int i;
 
 	/* Setup work. */
 	fp = NULL;
@@ -499,7 +499,7 @@ fpu_execute(struct trapframe *tf, struct
 				fpu_implode(fe, fp, FTYPE_SNG, &FR(rt));
 				fpu_explode(fe, fp = &fe->fe_f1, FTYPE_SNG,
 				    FR(rt));
-				type = FTYPE_DBL | FTYPE_FPRF;
+				type = FTYPE_DBL | FTYPE_FPSCR;
 				break;
 			case	OPC63_FCTIW:
 			case	OPC63_FCTIWZ:
@@ -526,7 +526,6 @@ fpu_execute(struct trapframe *tf, struct
 			case	OPC63_MTFSB1:
 				FPU_EMU_EVCNT_INCR(mtfsb1);
 				DPRINTF(FPE_INSN, ("fpu_execute: MTFSB1\n"));
-				mtfsb1 = 1;
 				fe->fe_cx = (1 << (31 - rt)) &
 				    ~(FPSCR_FEX | FPSCR_VX);
 				break;
@@ -627,7 +626,7 @@ fpu_execute(struct trapframe *tf, struct
 				DPRINTF(FPE_INSN, ("fpu_execute: FCFID\n"));
 				type = FTYPE_LNG;
 				fpu_explode(fe, fp = &fe->fe_f1, type, FR(rb));
-				type = FTYPE_DBL | FTYPE_FPRF;
+				type = FTYPE_DBL | FTYPE_FPSCR;
 				break;
 			default:
 				return (NOTFPU);
@@ -763,12 +762,12 @@ fpu_execute(struct trapframe *tf, struct
 
 			/* If the instruction was single precision, round */
 			if (!(instr.i_any.i_opcd & 0x4)) {
-				fpu_implode(fe, fp, FTYPE_SNG | FTYPE_FPRF,
+				fpu_implode(fe, fp, FTYPE_SNG | FTYPE_FPSCR,
 				    &FR(rt));
 				fpu_explode(fe, fp = &fe->fe_f1, FTYPE_SNG,
 				    FR(rt));
 			} else
-				type |= FTYPE_FPRF;
+				type |= FTYPE_FPSCR;
 		}
 	} else {
 		return (NOTFPU);
@@ -785,10 +784,6 @@ fpu_execute(struct trapframe *tf, struct
 	cx = fe->fe_cx;
 	fsr = fe->fe_fpscr & ~(FPSCR_FEX|FPSCR_VX);
 	if (cx != 0) {
-		if (mtfsb1 == 0 && (cx & FPSCR_FPRF) != 0) {
-			/* Need to replace CC */
-			fsr &= ~FPSCR_FPRF;
-		}
 		fsr |= cx;
 		DPRINTF(FPE_INSN, ("fpu_execute: cx %x, fsr %x\n", cx, fsr));
 	}

Index: src/sys/arch/powerpc/fpu/fpu_emu.h
diff -u src/sys/arch/powerpc/fpu/fpu_emu.h:1.8 src/sys/arch/powerpc/fpu/fpu_emu.h:1.9
--- src/sys/arch/powerpc/fpu/fpu_emu.h:1.8	Fri Sep  2 12:40:49 2022
+++ src/sys/arch/powerpc/fpu/fpu_emu.h	Sun Sep  4 13:14:57 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: fpu_emu.h,v 1.8 2022/09/02 12:40:49 rin Exp $ */
+/*	$NetBSD: fpu_emu.h,v 1.9 2022/09/04 13:14:57 rin Exp $ */
 
 /*
  * Copyright (c) 1992, 1993
@@ -141,8 +141,8 @@ struct fpn {
 #define	FTYPE_SNG	0x02	/* data = 32-bit float */
 #define	FTYPE_DBL	0x04	/* data = 64-bit double */
 #define	FTYPE_RD_RZ	0x08
-#define	FTYPE_FPRF	0x10
-#define	FTYPE_FLAG_MASK	(FTYPE_RD_RZ | FTYPE_FPRF)
+#define	FTYPE_FPSCR	0x10
+#define	FTYPE_FLAG_MASK	(FTYPE_RD_RZ | FTYPE_FPSCR)
 
 /*
  * Emulator state.

Index: src/sys/arch/powerpc/fpu/fpu_implode.c
diff -u src/sys/arch/powerpc/fpu/fpu_implode.c:1.20 src/sys/arch/powerpc/fpu/fpu_implode.c:1.21
--- src/sys/arch/powerpc/fpu/fpu_implode.c:1.20	Sun Sep  4 09:23:07 2022
+++ src/sys/arch/powerpc/fpu/fpu_implode.c	Sun Sep  4 13:14:57 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: fpu_implode.c,v 1.20 2022/09/04 09:23:07 rin Exp $ */
+/*	$NetBSD: fpu_implode.c,v 1.21 2022/09/04 13:14:57 rin Exp $ */
 
 /*
  * Copyright (c) 1992, 1993
@@ -46,7 +46,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: fpu_implode.c,v 1.20 2022/09/04 09:23:07 rin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: fpu_implode.c,v 1.21 2022/09/04 13:14:57 rin Exp $");
 
 #include <sys/types.h>
 #include <sys/systm.h>
@@ -60,14 +60,14 @@ __KERNEL_RCSID(0, "$NetBSD: fpu_implode.
 #include <powerpc/fpu/fpu_emu.h>
 #include <powerpc/fpu/fpu_extern.h>
 
-static int round(struct fpemu *, struct fpn *);
+static int round(struct fpemu *, struct fpn *, int *);
 static int toinf(struct fpemu *, int);
 static int round_int(struct fpn *, int *, int, int, int);
 
-static u_int fpu_ftoi(struct fpemu *, struct fpn *, int);
-static uint64_t fpu_ftox(struct fpemu *, struct fpn *, int);
-static u_int fpu_ftos(struct fpemu *, struct fpn *, bool);
-static uint64_t fpu_ftod(struct fpemu *, struct fpn *, bool);
+static u_int fpu_ftoi(struct fpemu *, struct fpn *, int *, int);
+static uint64_t fpu_ftox(struct fpemu *, struct fpn *, int *, int);
+static u_int fpu_ftos(struct fpemu *, struct fpn *, int *);
+static uint64_t fpu_ftod(struct fpemu *, struct fpn *, int *);
 
 /*
  * Round a number (algorithm from Motorola MC68882 manual, modified for
@@ -82,7 +82,7 @@ static uint64_t fpu_ftod(struct fpemu *,
  * responsibility to fix this if necessary.
  */
 static int
-round(struct fpemu *fe, struct fpn *fp)
+round(struct fpemu *fe, struct fpn *fp, int *cx)
 {
 	u_int m0, m1, m2, m3;
 	int gr, s;
@@ -104,7 +104,7 @@ round(struct fpemu *fe, struct fpn *fp)
 	if ((gr | s) == 0)	/* result is exact: no rounding needed */
 		goto rounddown;
 
-	fe->fe_cx |= FPSCR_XX|FPSCR_FI;	/* inexact */
+	*cx |= FPSCR_FI;	/* inexact */
 
 	/* Go to rounddown to round down; break to round up. */
 	switch ((fe->fe_fpscr) & FPSCR_RN) {
@@ -140,7 +140,7 @@ round(struct fpemu *fe, struct fpn *fp)
 	}
 
 	/* Bump low bit of mantissa, with carry. */
-	fe->fe_cx |= FPSCR_FR;
+	*cx |= FPSCR_FR;
 
 	FPU_ADDS(m3, m3, 1);
 	FPU_ADDCS(m2, m2, 0);
@@ -190,8 +190,6 @@ toinf(struct fpemu *fe, int sign)
 		inf = sign;
 		break;
 	}
-	if (inf)
-		fe->fe_cx |= FPSCR_OX;
 	return (inf);
 }
 
@@ -206,7 +204,7 @@ round_int(struct fpn *fp, int *cx, int r
 	if ((g | rs) == 0)
 		return 0;	/* exact */
 
-	*cx |= FPSCR_XX | FPSCR_FI;
+	*cx |= FPSCR_FI;
 
 	switch (rn) {
 	case FSR_RD_RN:
@@ -233,16 +231,15 @@ round_int(struct fpn *fp, int *cx, int r
  * fpn -> int (int value returned as return value).
  */
 static u_int
-fpu_ftoi(struct fpemu *fe, struct fpn *fp, int rn)
+fpu_ftoi(struct fpemu *fe, struct fpn *fp, int *cx, int rn)
 {
 	u_int i;
-	int sign, exp, cx;
+	int sign, exp, tmp_cx;
 
 	sign = fp->fp_sign;
-	cx = 0;
 	switch (fp->fp_class) {
 	case FPC_SNAN:
-		fe->fe_cx |= FPSCR_VXSNAN;
+		*cx |= FPSCR_VXSNAN;
 		/* FALLTHROUGH */
 	case FPC_QNAN:
 		sign = 1;
@@ -267,17 +264,18 @@ fpu_ftoi(struct fpemu *fe, struct fpn *f
 		/* NB: the following includes exp < 0 cases */
 		(void)fpu_shr(fp, FP_NMANT - 32 - 1 - exp);
 		i = fp->fp_mant[2];
-		i += round_int(fp, &cx, rn, sign, i & 1);
+		tmp_cx = 0;
+		i += round_int(fp, &tmp_cx, rn, sign, i & 1);
 		if (i >= ((u_int)0x80000000 + sign))
 			break;
-		fe->fe_cx |= cx;
+		*cx |= tmp_cx;
 		return (sign ? -i : i);
 
 	case FPC_INF:
 		break;
 	}
 	/* overflow: replace any inexact exception with invalid */
-	fe->fe_cx |= FPSCR_VXCVI;
+	*cx |= FPSCR_VXCVI;
 	return (0x7fffffff + sign);
 }
 
@@ -285,16 +283,15 @@ fpu_ftoi(struct fpemu *fe, struct fpn *f
  * fpn -> extended int (high bits of int value returned as return value).
  */
 static uint64_t
-fpu_ftox(struct fpemu *fe, struct fpn *fp, int rn)
+fpu_ftox(struct fpemu *fe, struct fpn *fp, int *cx, int rn)
 {
 	uint64_t i;
-	int sign, exp, cx;
+	int sign, exp, tmp_cx;
 
 	sign = fp->fp_sign;
-	cx = 0;
 	switch (fp->fp_class) {
 	case FPC_SNAN:
-		fe->fe_cx |= FPSCR_VXSNAN;
+		*cx |= FPSCR_VXSNAN;
 		/* FALLTHROUGH */
 	case FPC_QNAN:
 		sign = 1;
@@ -319,17 +316,18 @@ fpu_ftox(struct fpemu *fe, struct fpn *f
 		/* NB: the following includes exp < 0 cases */
 		(void)fpu_shr(fp, FP_NMANT - 32 - 1 - exp);
 		i = ((uint64_t)fp->fp_mant[1] << 32) | fp->fp_mant[2];
-		i += round_int(fp, &cx, rn, sign, i & 1);
+		tmp_cx = 0;
+		i += round_int(fp, &tmp_cx, rn, sign, i & 1);
 		if (i >= ((uint64_t)0x8000000000000000LL + sign))
 			break;
-		fe->fe_cx |= cx;
+		*cx |= tmp_cx;
 		return (sign ? -i : i);
 
 	case FPC_INF:
 		break;
 	}
 	/* overflow: replace any inexact exception with invalid */
-	fe->fe_cx |= FPSCR_VXCVI;
+	*cx |= FPSCR_VXCVI;
 	return (0x7fffffffffffffffLL + sign);
 }
 
@@ -340,7 +338,7 @@ fpu_ftox(struct fpemu *fe, struct fpn *f
  * We assume <= 29 bits in a single-precision fraction (1.f part).
  */
 static u_int
-fpu_ftos(struct fpemu *fe, struct fpn *fp, bool fprf)
+fpu_ftos(struct fpemu *fe, struct fpn *fp, int *cx)
 {
 	u_int sign = fp->fp_sign << 31;
 	int exp;
@@ -350,8 +348,7 @@ fpu_ftos(struct fpemu *fe, struct fpn *f
 
 	/* Take care of non-numbers first. */
 	if (ISNAN(fp)) {
-		if (fprf)
-			fe->fe_cx |= FPSCR_C | FPSCR_FU;
+		*cx |= FPSCR_C | FPSCR_FU;
 		/*
 		 * Preserve upper bits of NaN, per SPARC V8 appendix N.
 		 * Note that fp->fp_mant[0] has the quiet bit set,
@@ -362,16 +359,13 @@ fpu_ftos(struct fpemu *fe, struct fpn *f
 		goto done;
 	}
 	if (ISINF(fp)) {
-		if (fprf)
-			fe->fe_cx |= FPRF_SIGN(sign) | FPSCR_FU;
+		*cx |= FPRF_SIGN(sign) | FPSCR_FU;
 		return (sign | SNG_EXP(SNG_EXP_INFNAN));
 	}
 	if (ISZERO(fp)) {
-		if (fprf) {
-			fe->fe_cx |= FPSCR_FE;
-			if (sign)
-				fe->fe_cx |= FPSCR_C;
-		}
+		*cx |= FPSCR_FE;
+		if (sign)
+			*cx |= FPSCR_C;
 		return (sign);
 	}
 
@@ -399,16 +393,14 @@ fpu_ftos(struct fpemu *fe, struct fpn *f
 	if ((exp = fp->fp_exp + SNG_EXP_BIAS) <= 0) {	/* subnormal */
 		/* -NG for g,r; -SNG_FRACBITS-exp for fraction */
 		(void) fpu_shr(fp, FP_NMANT - FP_NG - SNG_FRACBITS - exp);
-		if (round(fe, fp) && fp->fp_mant[3] == SNG_EXP(1)) {
-			if (fprf)
-				fe->fe_cx |= FPRF_SIGN(sign);
+		if (round(fe, fp, cx) && fp->fp_mant[3] == SNG_EXP(1)) {
+			*cx |= FPRF_SIGN(sign);
 			return (sign | SNG_EXP(1) | 0);
 		}
-		if (fprf)
-			fe->fe_cx |= FPSCR_C | FPRF_SIGN(sign);
-		if ((fe->fe_cx & FPSCR_FI) ||
+		*cx |= FPSCR_C | FPRF_SIGN(sign);
+		if (((fe->fe_cx | *cx) & FPSCR_FI) ||
 		    (fe->fe_fpscr & FPSCR_UX))
-			fe->fe_cx |= FPSCR_UX;
+			*cx |= FPSCR_UX;
 		return (sign | SNG_EXP(0) | fp->fp_mant[3]);
 	}
 	/* -FP_NG for g,r; -1 for implied 1; -SNG_FRACBITS for fraction */
@@ -417,21 +409,19 @@ fpu_ftos(struct fpemu *fe, struct fpn *f
 	if ((fp->fp_mant[3] & SNG_EXP(1 << FP_NG)) == 0)
 		panic("fpu_ftos");
 #endif
-	if (round(fe, fp) && fp->fp_mant[3] == SNG_EXP(2))
+	if (round(fe, fp, cx) && fp->fp_mant[3] == SNG_EXP(2))
 		exp++;
 	if (exp >= SNG_EXP_INFNAN) {
+		*cx |= FPSCR_OX;
 		/* overflow to inf or to max single */
 		if (toinf(fe, sign)) {
-			if (fprf)
-				fe->fe_cx |= FPRF_SIGN(sign) | FPSCR_FU;
+			*cx |= FPRF_SIGN(sign) | FPSCR_FU;
 			return (sign | SNG_EXP(SNG_EXP_INFNAN));
 		}
-		if (fprf)
-			fe->fe_cx |= FPRF_SIGN(sign);
+		*cx |= FPRF_SIGN(sign);
 		return (sign | SNG_EXP(SNG_EXP_INFNAN - 1) | SNG_MASK);
 	}
-	if (fprf)
-		fe->fe_cx |= FPRF_SIGN(sign);
+	*cx |= FPRF_SIGN(sign);
 done:
 	/* phew, made it */
 	return (sign | SNG_EXP(exp) | (fp->fp_mant[3] & SNG_MASK));
@@ -443,7 +433,7 @@ done:
  * This code mimics fpu_ftos; see it for comments.
  */
 static uint64_t
-fpu_ftod(struct fpemu *fe, struct fpn *fp, bool fprf)
+fpu_ftod(struct fpemu *fe, struct fpn *fp, int *cx)
 {
 	u_int sign = fp->fp_sign << 31;
 	int exp;
@@ -454,59 +444,51 @@ fpu_ftod(struct fpemu *fe, struct fpn *f
 #define	LO_WORD(i)	((uint32_t)(i))
 
 	if (ISNAN(fp)) {
-		if (fprf)
-			fe->fe_cx |= FPSCR_C | FPSCR_FU;
+		*cx |= FPSCR_C | FPSCR_FU;
 		(void) fpu_shr(fp, FP_NMANT - 1 - DBL_FRACBITS);
 		exp = DBL_EXP_INFNAN;
 		goto done;
 	}
 	if (ISINF(fp)) {
-		if (fprf)
-			fe->fe_cx |= FPRF_SIGN(sign) | FPSCR_FU;
+		*cx |= FPRF_SIGN(sign) | FPSCR_FU;
 		sign |= DBL_EXP(DBL_EXP_INFNAN);
 		goto zero;
 	}
 	if (ISZERO(fp)) {
-		if (fprf) {
-			fe->fe_cx |= FPSCR_FE;
-			if (sign)
-				fe->fe_cx |= FPSCR_C;
-		}
+		*cx |= FPSCR_FE;
+		if (sign)
+			*cx |= FPSCR_C;
 zero:		return HI_WORD(sign);
 	}
 
 	if ((exp = fp->fp_exp + DBL_EXP_BIAS) <= 0) {
 		(void) fpu_shr(fp, FP_NMANT - FP_NG - DBL_FRACBITS - exp);
-		if (round(fe, fp) && fp->fp_mant[2] == DBL_EXP(1)) {
-			if (fprf)
-				fe->fe_cx |= FPRF_SIGN(sign);
+		if (round(fe, fp, cx) && fp->fp_mant[2] == DBL_EXP(1)) {
+			*cx |= FPRF_SIGN(sign);
 			return HI_WORD(sign | DBL_EXP(1) | 0);
 		}
-		if (fprf)
-			fe->fe_cx |= FPSCR_C | FPRF_SIGN(sign);
-		if ((fe->fe_cx & FPSCR_FI) ||
+		*cx |= FPSCR_C | FPRF_SIGN(sign);
+		if (((fe->fe_cx | *cx) & FPSCR_FI) ||
 		    (fe->fe_fpscr & FPSCR_UX))
-			fe->fe_cx |= FPSCR_UX;
+			*cx |= FPSCR_UX;
 		exp = 0;
 		goto done;
 	}
 	(void) fpu_shr(fp, FP_NMANT - FP_NG - 1 - DBL_FRACBITS);
-	if (round(fe, fp) && fp->fp_mant[2] == DBL_EXP(2))
+	if (round(fe, fp, cx) && fp->fp_mant[2] == DBL_EXP(2))
 		exp++;
 	if (exp >= DBL_EXP_INFNAN) {
-		fe->fe_cx |= FPSCR_OX;
+		*cx |= FPSCR_OX;
+		/* overflow to inf or to max double */
 		if (toinf(fe, sign)) {
-			if (fprf)
-				fe->fe_cx |= FPRF_SIGN(sign) | FPSCR_FU;
+			*cx |= FPRF_SIGN(sign) | FPSCR_FU;
 			return HI_WORD(sign | DBL_EXP(DBL_EXP_INFNAN) | 0);
 		}
-		if (fprf)
-			fe->fe_cx |= FPRF_SIGN(sign);
+		*cx |= FPRF_SIGN(sign);
 		return HI_WORD(sign | DBL_EXP(DBL_EXP_INFNAN - 1) | DBL_MASK) |
 		       LO_WORD(~0);
 	}
-	if (fprf)
-		fe->fe_cx |= FPRF_SIGN(sign);
+	*cx |= FPRF_SIGN(sign);
 done:
 	return HI_WORD(sign | DBL_EXP(exp) | (fp->fp_mant[2] & DBL_MASK)) |
 	       LO_WORD(fp->fp_mant[3]);
@@ -519,8 +501,8 @@ void
 fpu_implode(struct fpemu *fe, struct fpn *fp, int type, uint64_t *p)
 {
 	u_int *hi, *lo;
-	int rn;
-	bool fprf;
+	int cx, rn;
+	bool fpscr;
 
 	hi = (u_int *)p;
 	lo = hi + 1;
@@ -529,36 +511,44 @@ fpu_implode(struct fpemu *fe, struct fpn
 		rn = FSR_RD_RZ;
 	else
 		rn = fe->fe_fpscr & FPSCR_RN;
-	fprf = type & FTYPE_FPRF;
+	fpscr = type & FTYPE_FPSCR;
 	type &= ~FTYPE_FLAG_MASK;
 
+	cx = 0;
 	switch (type) {
 
 	case FTYPE_LNG:
 		/* FPRF is undefined. */
-		*p = fpu_ftox(fe, fp, rn);
+		*p = fpu_ftox(fe, fp, &cx, rn);
 		DPRINTF(FPE_REG, ("fpu_implode: long %x %x\n", *hi, *lo));
 		break;
 
 	case FTYPE_INT:
 		/* FPRF is undefined. */
 		*hi = 0;
-		*lo = fpu_ftoi(fe, fp, rn);
+		*lo = fpu_ftoi(fe, fp, &cx, rn);
 		DPRINTF(FPE_REG, ("fpu_implode: int %x\n", *lo));
 		break;
 
 	case FTYPE_SNG:
-		*hi = fpu_ftos(fe, fp, fprf);
+		*hi = fpu_ftos(fe, fp, &cx);
 		*lo = 0;
 		DPRINTF(FPE_REG, ("fpu_implode: single %x\n", *hi));
 		break;
 
 	case FTYPE_DBL:
-		*p = fpu_ftod(fe, fp, fprf);
+		*p = fpu_ftod(fe, fp, &cx);
 		DPRINTF(FPE_REG, ("fpu_implode: double %x %x\n", *hi, *lo));
 		break;
 
 	default:
 		panic("fpu_implode: invalid type %d", type);
 	}
+
+	if (fpscr) {
+		fe->fe_fpscr &= ~(FPSCR_FR | FPSCR_FI | FPSCR_FPRF);
+		fe->fe_cx |= cx;
+		if (cx & FPSCR_FI)
+			fe->fe_cx |= FPSCR_XX;
+	}
 }

Reply via email to