All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU
@ 2017-02-07  0:59 Laurent Vivier
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 01/16] softfloat: define 680x0 specific values Laurent Vivier
                   ` (16 more replies)
  0 siblings, 17 replies; 38+ messages in thread
From: Laurent Vivier @ 2017-02-07  0:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Richard Henderson, Laurent Vivier

This series modifies the original ColdFire FPU implementation
to use floatx80 instead of float64 internally as this
is the native datatype for 680x0. I didn't keep the float64
type for ColdFire, but if someone thinks it's required I
can update this series in this way.

The series also adds the FPU status and control registers and
several floating point instructions.

The floatx80 datatype used here is not exactly the same as the
one used by 680x0 for its extended precision data type, because
normally the signaling bit of 680x0 NAN is the MSB of the mantissa
minus one and in floatx80 it is the MSB.

We also add the gdb server parts to read the new FPU registers.
A strange thing happens here: while the gdb client running remotely
from a debian etch-m68k has no issue working with 96bit FPU registers
(the 680x0 extended precision data type), new gdbs (from a debian unstable
and gdb for cross-compiled environment) don't expect this FPU registers
size. But it seems like a bug in gdb, not in this implementation.

After this series is applied, qemu-m68k can run a debian etch-m68k
or a debian unstable chroot.

v3:
    fix fsave opcode
    Add comment to define "unnormalized" number
    Correctly define pickNaN()

v2:
    complete rework of the series
    force single precision in ColdFire mode
    add "forced" precision instructions (fsmove, fdmove, fsadd, ...)
    fixed Fcc.

Laurent Vivier (16):
  softfloat: define 680x0 specific values
  softloat: disable floatx80_invalid_encoding() for m68k
  target-m68k: move FPU helpers to fpu_helper.c
  target-m68k: define ext_opsize
  target-m68k: use floatx80 internally
  target-m68k: add FPCR and FPSR
  target-m68k: manage FPU exceptions
  target-m68k: define 96bit FP registers for gdb on 680x0
  target-m68k: add fmovem
  target-m68k: add fscc.
  target-m68k: add fmovecr
  target-m68k: add fscale, fgetman, fgetexp and fmod
  target-m68k: add fsglmul and fsgldiv
  target-m68k: add explicit single and double precision operations
  target-m68k: add more FPU instructions
  target-m68k: add fsincos

 configure                  |    2 +-
 fpu/softfloat-specialize.h |   34 +-
 fpu/softfloat.c            |   31 ++
 gdb-xml/m68k-fp.xml        |   21 +
 include/fpu/softfloat.h    |   15 -
 target/m68k/Makefile.objs  |    2 +-
 target/m68k/cpu.c          |   13 +-
 target/m68k/cpu.h          |   75 ++-
 target/m68k/fpu_helper.c   |  972 ++++++++++++++++++++++++++++++++
 target/m68k/helper.c       |  165 +++---
 target/m68k/helper.h       |   73 ++-
 target/m68k/qregs.def      |    4 +-
 target/m68k/translate.c    | 1318 +++++++++++++++++++++++++++++++++-----------
 13 files changed, 2254 insertions(+), 471 deletions(-)
 create mode 100644 gdb-xml/m68k-fp.xml
 create mode 100644 target/m68k/fpu_helper.c

-- 
2.9.3

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [Qemu-devel] [PATCH v3 01/16] softfloat: define 680x0 specific values
  2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
@ 2017-02-07  0:59 ` Laurent Vivier
  2017-02-08 21:30   ` Richard Henderson
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 02/16] softloat: disable floatx80_invalid_encoding() for m68k Laurent Vivier
                   ` (15 subsequent siblings)
  16 siblings, 1 reply; 38+ messages in thread
From: Laurent Vivier @ 2017-02-07  0:59 UTC (permalink / raw)
  To: qemu-devel
  Cc: Aurelien Jarno, Richard Henderson, Laurent Vivier, Peter Maydell

CC: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 fpu/softfloat-specialize.h | 34 +++++++++++++++++++++++++++++++---
 1 file changed, 31 insertions(+), 3 deletions(-)

diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h
index 100c8a9..fb70d32 100644
--- a/fpu/softfloat-specialize.h
+++ b/fpu/softfloat-specialize.h
@@ -111,7 +111,7 @@ float16 float16_default_nan(float_status *status)
 *----------------------------------------------------------------------------*/
 float32 float32_default_nan(float_status *status)
 {
-#if defined(TARGET_SPARC)
+#if defined(TARGET_SPARC) || defined(TARGET_M68K)
     return const_float32(0x7FFFFFFF);
 #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \
       defined(TARGET_XTENSA) || defined(TARGET_S390X) || defined(TARGET_TRICORE)
@@ -136,7 +136,7 @@ float32 float32_default_nan(float_status *status)
 *----------------------------------------------------------------------------*/
 float64 float64_default_nan(float_status *status)
 {
-#if defined(TARGET_SPARC)
+#if defined(TARGET_SPARC) || defined(TARGET_M68K)
     return const_float64(LIT64(0x7FFFFFFFFFFFFFFF));
 #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \
       defined(TARGET_S390X)
@@ -162,7 +162,10 @@ float64 float64_default_nan(float_status *status)
 floatx80 floatx80_default_nan(float_status *status)
 {
     floatx80 r;
-
+#if defined(TARGET_M68K)
+    r.low = LIT64(0xFFFFFFFFFFFFFFFF);
+    r.high = 0x7FFF;
+#else
     if (status->snan_bit_is_one) {
         r.low = LIT64(0xBFFFFFFFFFFFFFFF);
         r.high = 0x7FFF;
@@ -170,6 +173,7 @@ floatx80 floatx80_default_nan(float_status *status)
         r.low = LIT64(0xC000000000000000);
         r.high = 0xFFFF;
     }
+#endif
     return r;
 }
 
@@ -502,6 +506,30 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN,
         return 1;
     }
 }
+#elif defined(TARGET_M68K)
+static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN,
+                   flag aIsLargerSignificand)
+{
+    /* M68000 FAMILY PROGRAMMER’S REFERENCE MANUAL
+     * 3.4 FLOATING-POINT INSTRUCTION DETAILS
+     * If either operand, but not both operands, of an operation is a
+     * nonsignaling NaN, then that NaN is returned as the result. If both
+     * operands are nonsignaling NaNs, then the destination operand
+     * nonsignaling NaN is returned as the result.
+     * If either operand to an operation is a signaling NaN (SNaN), then the
+     * SNaN bit is set in the FPSR EXC byte. If the SNaN exception enable bit
+     * is set in the FPCR ENABLE byte, then the exception is taken and the
+     * destination is not modified. If the SNaN exception enable bit is not
+     * set, setting the SNaN bit in the operand to a one converts the SNaN to
+     * a nonsignaling NaN. The operation then continues as described in the
+     * preceding paragraph for nonsignaling NaNs.
+     */
+    if (aIsQNaN || aIsSNaN) { /* a is the destination operand */
+        return 0; /* return the destination operand */
+    } else {
+        return 1; /* return b */
+    }
+}
 #else
 static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN,
                     flag aIsLargerSignificand)
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [Qemu-devel] [PATCH v3 02/16] softloat: disable floatx80_invalid_encoding() for m68k
  2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 01/16] softfloat: define 680x0 specific values Laurent Vivier
@ 2017-02-07  0:59 ` Laurent Vivier
  2017-02-08 21:32   ` Richard Henderson
  2017-02-08 22:58   ` Peter Maydell
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 03/16] target-m68k: move FPU helpers to fpu_helper.c Laurent Vivier
                   ` (14 subsequent siblings)
  16 siblings, 2 replies; 38+ messages in thread
From: Laurent Vivier @ 2017-02-07  0:59 UTC (permalink / raw)
  To: qemu-devel
  Cc: Aurelien Jarno, Richard Henderson, Laurent Vivier, Andreas Schwab

According to the comment, this definition of invalid encoding is given
by intel developer's manual, and doesn't work with the behavior
of 680x0 FPU.

CC: Andreas Schwab <schwab@linux-m68k.org>
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 fpu/softfloat.c         | 31 +++++++++++++++++++++++++++++++
 include/fpu/softfloat.h | 15 ---------------
 2 files changed, 31 insertions(+), 15 deletions(-)

diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index c295f31..f95b19f 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -4799,6 +4799,37 @@ int float64_unordered_quiet(float64 a, float64 b, float_status *status)
 }
 
 /*----------------------------------------------------------------------------
+| Return whether the given value is an invalid floatx80 encoding.
+| Invalid floatx80 encodings arise when the integer bit is not set, but
+| the exponent is not zero. The only times the integer bit is permitted to
+| be zero is in subnormal numbers and the value zero.
+| This includes what the Intel software developer's manual calls pseudo-NaNs,
+| pseudo-infinities and un-normal numbers. It does not include
+| pseudo-denormals, which must still be correctly handled as inputs even
+| if they are never generated as outputs.
+*----------------------------------------------------------------------------*/
+static inline bool floatx80_invalid_encoding(floatx80 a)
+{
+#if defined(TARGET_M68K)
+    /*-------------------------------------------------------------------------
+    |  M68000 FAMILY PROGRAMMER’S REFERENCE MANUAL
+    |  1.6.2 Denormalized Numbers
+    |  Since the extended-precision data format has an explicit integer bit,
+    |  a number can be formatted with a nonzero exponent, less than the maximum
+    |  value, and a zero integer bit.  The IEEE 754 standard does not define a
+    |  zero integer bit. Such a number is an unnormalized number. Hardware does
+    |  not directly support denormalized and unnormalized numbers, but
+    |  implicitly supports them by trapping them as unimplemented data types,
+    |  allowing efficient conversion in software.
+    *------------------------------------------------------------------------*/
+    return 0;
+#else
+    return (a.low & (1ULL << 63)) == 0 && (a.high & 0x7FFF) != 0;
+#endif
+}
+
+
+/*----------------------------------------------------------------------------
 | Returns the result of converting the extended double-precision floating-
 | point value `a' to the 32-bit two's complement integer format.  The
 | conversion is performed according to the IEC/IEEE Standard for Binary
diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h
index 842ec6b..3920c0a 100644
--- a/include/fpu/softfloat.h
+++ b/include/fpu/softfloat.h
@@ -678,21 +678,6 @@ static inline int floatx80_is_any_nan(floatx80 a)
     return ((a.high & 0x7fff) == 0x7fff) && (a.low<<1);
 }
 
-/*----------------------------------------------------------------------------
-| Return whether the given value is an invalid floatx80 encoding.
-| Invalid floatx80 encodings arise when the integer bit is not set, but
-| the exponent is not zero. The only times the integer bit is permitted to
-| be zero is in subnormal numbers and the value zero.
-| This includes what the Intel software developer's manual calls pseudo-NaNs,
-| pseudo-infinities and un-normal numbers. It does not include
-| pseudo-denormals, which must still be correctly handled as inputs even
-| if they are never generated as outputs.
-*----------------------------------------------------------------------------*/
-static inline bool floatx80_invalid_encoding(floatx80 a)
-{
-    return (a.low & (1ULL << 63)) == 0 && (a.high & 0x7FFF) != 0;
-}
-
 #define floatx80_zero make_floatx80(0x0000, 0x0000000000000000LL)
 #define floatx80_one make_floatx80(0x3fff, 0x8000000000000000LL)
 #define floatx80_ln2 make_floatx80(0x3ffe, 0xb17217f7d1cf79acLL)
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [Qemu-devel] [PATCH v3 03/16] target-m68k: move FPU helpers to fpu_helper.c
  2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 01/16] softfloat: define 680x0 specific values Laurent Vivier
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 02/16] softloat: disable floatx80_invalid_encoding() for m68k Laurent Vivier
@ 2017-02-07  0:59 ` Laurent Vivier
  2017-02-08 21:33   ` Richard Henderson
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 04/16] target-m68k: define ext_opsize Laurent Vivier
                   ` (13 subsequent siblings)
  16 siblings, 1 reply; 38+ messages in thread
From: Laurent Vivier @ 2017-02-07  0:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Richard Henderson, Laurent Vivier

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 target/m68k/Makefile.objs |   2 +-
 target/m68k/fpu_helper.c  | 112 ++++++++++++++++++++++++++++++++++++++++++++++
 target/m68k/helper.c      |  88 ------------------------------------
 3 files changed, 113 insertions(+), 89 deletions(-)
 create mode 100644 target/m68k/fpu_helper.c

diff --git a/target/m68k/Makefile.objs b/target/m68k/Makefile.objs
index 02cf616..39141ab 100644
--- a/target/m68k/Makefile.objs
+++ b/target/m68k/Makefile.objs
@@ -1,3 +1,3 @@
 obj-y += m68k-semi.o
-obj-y += translate.o op_helper.o helper.o cpu.o
+obj-y += translate.o op_helper.o helper.o cpu.o fpu_helper.o
 obj-y += gdbstub.o
diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
new file mode 100644
index 0000000..5bf2576
--- /dev/null
+++ b/target/m68k/fpu_helper.c
@@ -0,0 +1,112 @@
+/*
+ *  m68k FPU helpers
+ *
+ *  Copyright (c) 2006-2007 CodeSourcery
+ *  Written by Paul Brook
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "exec/helper-proto.h"
+
+uint32_t HELPER(f64_to_i32)(CPUM68KState *env, float64 val)
+{
+    return float64_to_int32(val, &env->fp_status);
+}
+
+float32 HELPER(f64_to_f32)(CPUM68KState *env, float64 val)
+{
+    return float64_to_float32(val, &env->fp_status);
+}
+
+float64 HELPER(i32_to_f64)(CPUM68KState *env, uint32_t val)
+{
+    return int32_to_float64(val, &env->fp_status);
+}
+
+float64 HELPER(f32_to_f64)(CPUM68KState *env, float32 val)
+{
+    return float32_to_float64(val, &env->fp_status);
+}
+
+float64 HELPER(iround_f64)(CPUM68KState *env, float64 val)
+{
+    return float64_round_to_int(val, &env->fp_status);
+}
+
+float64 HELPER(itrunc_f64)(CPUM68KState *env, float64 val)
+{
+    return float64_trunc_to_int(val, &env->fp_status);
+}
+
+float64 HELPER(sqrt_f64)(CPUM68KState *env, float64 val)
+{
+    return float64_sqrt(val, &env->fp_status);
+}
+
+float64 HELPER(abs_f64)(float64 val)
+{
+    return float64_abs(val);
+}
+
+float64 HELPER(chs_f64)(float64 val)
+{
+    return float64_chs(val);
+}
+
+float64 HELPER(add_f64)(CPUM68KState *env, float64 a, float64 b)
+{
+    return float64_add(a, b, &env->fp_status);
+}
+
+float64 HELPER(sub_f64)(CPUM68KState *env, float64 a, float64 b)
+{
+    return float64_sub(a, b, &env->fp_status);
+}
+
+float64 HELPER(mul_f64)(CPUM68KState *env, float64 a, float64 b)
+{
+    return float64_mul(a, b, &env->fp_status);
+}
+
+float64 HELPER(div_f64)(CPUM68KState *env, float64 a, float64 b)
+{
+    return float64_div(a, b, &env->fp_status);
+}
+
+float64 HELPER(sub_cmp_f64)(CPUM68KState *env, float64 a, float64 b)
+{
+    /* ??? This may incorrectly raise exceptions.  */
+    /* ??? Should flush denormals to zero.  */
+    float64 res;
+    res = float64_sub(a, b, &env->fp_status);
+    if (float64_is_quiet_nan(res, &env->fp_status)) {
+        /* +/-inf compares equal against itself, but sub returns nan.  */
+        if (!float64_is_quiet_nan(a, &env->fp_status)
+            && !float64_is_quiet_nan(b, &env->fp_status)) {
+            res = float64_zero;
+            if (float64_lt_quiet(a, res, &env->fp_status)) {
+                res = float64_chs(res);
+            }
+        }
+    }
+    return res;
+}
+
+uint32_t HELPER(compare_f64)(CPUM68KState *env, float64 val)
+{
+    return float64_compare_quiet(val, float64_zero, &env->fp_status);
+}
diff --git a/target/m68k/helper.c b/target/m68k/helper.c
index f750d3d..5ca9911 100644
--- a/target/m68k/helper.c
+++ b/target/m68k/helper.c
@@ -284,94 +284,6 @@ void HELPER(set_sr)(CPUM68KState *env, uint32_t val)
     m68k_switch_sp(env);
 }
 
-/* FPU helpers.  */
-uint32_t HELPER(f64_to_i32)(CPUM68KState *env, float64 val)
-{
-    return float64_to_int32(val, &env->fp_status);
-}
-
-float32 HELPER(f64_to_f32)(CPUM68KState *env, float64 val)
-{
-    return float64_to_float32(val, &env->fp_status);
-}
-
-float64 HELPER(i32_to_f64)(CPUM68KState *env, uint32_t val)
-{
-    return int32_to_float64(val, &env->fp_status);
-}
-
-float64 HELPER(f32_to_f64)(CPUM68KState *env, float32 val)
-{
-    return float32_to_float64(val, &env->fp_status);
-}
-
-float64 HELPER(iround_f64)(CPUM68KState *env, float64 val)
-{
-    return float64_round_to_int(val, &env->fp_status);
-}
-
-float64 HELPER(itrunc_f64)(CPUM68KState *env, float64 val)
-{
-    return float64_trunc_to_int(val, &env->fp_status);
-}
-
-float64 HELPER(sqrt_f64)(CPUM68KState *env, float64 val)
-{
-    return float64_sqrt(val, &env->fp_status);
-}
-
-float64 HELPER(abs_f64)(float64 val)
-{
-    return float64_abs(val);
-}
-
-float64 HELPER(chs_f64)(float64 val)
-{
-    return float64_chs(val);
-}
-
-float64 HELPER(add_f64)(CPUM68KState *env, float64 a, float64 b)
-{
-    return float64_add(a, b, &env->fp_status);
-}
-
-float64 HELPER(sub_f64)(CPUM68KState *env, float64 a, float64 b)
-{
-    return float64_sub(a, b, &env->fp_status);
-}
-
-float64 HELPER(mul_f64)(CPUM68KState *env, float64 a, float64 b)
-{
-    return float64_mul(a, b, &env->fp_status);
-}
-
-float64 HELPER(div_f64)(CPUM68KState *env, float64 a, float64 b)
-{
-    return float64_div(a, b, &env->fp_status);
-}
-
-float64 HELPER(sub_cmp_f64)(CPUM68KState *env, float64 a, float64 b)
-{
-    /* ??? This may incorrectly raise exceptions.  */
-    /* ??? Should flush denormals to zero.  */
-    float64 res;
-    res = float64_sub(a, b, &env->fp_status);
-    if (float64_is_quiet_nan(res, &env->fp_status)) {
-        /* +/-inf compares equal against itself, but sub returns nan.  */
-        if (!float64_is_quiet_nan(a, &env->fp_status)
-            && !float64_is_quiet_nan(b, &env->fp_status)) {
-            res = float64_zero;
-            if (float64_lt_quiet(a, res, &env->fp_status))
-                res = float64_chs(res);
-        }
-    }
-    return res;
-}
-
-uint32_t HELPER(compare_f64)(CPUM68KState *env, float64 val)
-{
-    return float64_compare_quiet(val, float64_zero, &env->fp_status);
-}
 
 /* MAC unit.  */
 /* FIXME: The MAC unit implementation is a bit of a mess.  Some helpers
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [Qemu-devel] [PATCH v3 04/16] target-m68k: define ext_opsize
  2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
                   ` (2 preceding siblings ...)
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 03/16] target-m68k: move FPU helpers to fpu_helper.c Laurent Vivier
@ 2017-02-07  0:59 ` Laurent Vivier
  2017-02-08 21:33   ` Richard Henderson
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 05/16] target-m68k: use floatx80 internally Laurent Vivier
                   ` (12 subsequent siblings)
  16 siblings, 1 reply; 38+ messages in thread
From: Laurent Vivier @ 2017-02-07  0:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Richard Henderson, Laurent Vivier

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 target/m68k/translate.c | 43 ++++++++++++++++++++++++-------------------
 1 file changed, 24 insertions(+), 19 deletions(-)

diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index 9f60fbc..d9ba735 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -669,6 +669,21 @@ static inline int insn_opsize(int insn)
     }
 }
 
+static inline int ext_opsize(int ext, int pos)
+{
+    switch ((ext >> pos) & 7) {
+    case 0: return OS_LONG;
+    case 1: return OS_SINGLE;
+    case 2: return OS_EXTENDED;
+    case 3: return OS_PACKED;
+    case 4: return OS_WORD;
+    case 5: return OS_DOUBLE;
+    case 6: return OS_BYTE;
+    default:
+        g_assert_not_reached();
+    }
+}
+
 /* Assign value to a register.  If the width is less than the register width
    only the low part of the register is set.  */
 static void gen_partset_reg(int opsize, TCGv reg, TCGv val)
@@ -4101,20 +4116,19 @@ DISAS_INSN(fpu)
         tmp32 = tcg_temp_new_i32();
         /* fmove */
         /* ??? TODO: Proper behavior on overflow.  */
-        switch ((ext >> 10) & 7) {
-        case 0:
-            opsize = OS_LONG;
+
+        opsize = ext_opsize(ext, 10);
+        switch (opsize) {
+        case OS_LONG:
             gen_helper_f64_to_i32(tmp32, cpu_env, src);
             break;
-        case 1:
-            opsize = OS_SINGLE;
+        case OS_SINGLE:
             gen_helper_f64_to_f32(tmp32, cpu_env, src);
             break;
-        case 4:
-            opsize = OS_WORD;
+        case OS_WORD:
             gen_helper_f64_to_i32(tmp32, cpu_env, src);
             break;
-        case 5: /* OS_DOUBLE */
+        case OS_DOUBLE:
             tcg_gen_mov_i32(tmp32, AREG(insn, 0));
             switch ((insn >> 3) & 7) {
             case 2:
@@ -4143,8 +4157,7 @@ DISAS_INSN(fpu)
             }
             tcg_temp_free_i32(tmp32);
             return;
-        case 6:
-            opsize = OS_BYTE;
+        case OS_BYTE:
             gen_helper_f64_to_i32(tmp32, cpu_env, src);
             break;
         default:
@@ -4217,15 +4230,7 @@ DISAS_INSN(fpu)
     }
     if (ext & (1 << 14)) {
         /* Source effective address.  */
-        switch ((ext >> 10) & 7) {
-        case 0: opsize = OS_LONG; break;
-        case 1: opsize = OS_SINGLE; break;
-        case 4: opsize = OS_WORD; break;
-        case 5: opsize = OS_DOUBLE; break;
-        case 6: opsize = OS_BYTE; break;
-        default:
-            goto undef;
-        }
+        opsize = ext_opsize(ext, 10);
         if (opsize == OS_DOUBLE) {
             tmp32 = tcg_temp_new_i32();
             tcg_gen_mov_i32(tmp32, AREG(insn, 0));
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [Qemu-devel] [PATCH v3 05/16] target-m68k: use floatx80 internally
  2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
                   ` (3 preceding siblings ...)
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 04/16] target-m68k: define ext_opsize Laurent Vivier
@ 2017-02-07  0:59 ` Laurent Vivier
  2017-02-15 22:59   ` Richard Henderson
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 06/16] target-m68k: add FPCR and FPSR Laurent Vivier
                   ` (11 subsequent siblings)
  16 siblings, 1 reply; 38+ messages in thread
From: Laurent Vivier @ 2017-02-07  0:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Richard Henderson, Laurent Vivier

Coldfire uses float64, but 680x0 use floatx80.
This patch introduces the use of floatx80 internally
and enables 680x0 80bits FPU.

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 target/m68k/cpu.c        |  13 +-
 target/m68k/cpu.h        |  10 +-
 target/m68k/fpu_helper.c | 202 +++++++++++++---
 target/m68k/helper.c     |  12 +-
 target/m68k/helper.h     |  32 +--
 target/m68k/qregs.def    |   3 +-
 target/m68k/translate.c  | 617 +++++++++++++++++++++++++++++++----------------
 7 files changed, 616 insertions(+), 273 deletions(-)

diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c
index fa10b6e..cedc272 100644
--- a/target/m68k/cpu.c
+++ b/target/m68k/cpu.c
@@ -49,6 +49,8 @@ static void m68k_cpu_reset(CPUState *s)
     M68kCPU *cpu = M68K_CPU(s);
     M68kCPUClass *mcc = M68K_CPU_GET_CLASS(cpu);
     CPUM68KState *env = &cpu->env;
+    floatx80 nan = floatx80_default_nan(NULL);
+    int i;
 
     mcc->parent_reset(s);
 
@@ -57,7 +59,16 @@ static void m68k_cpu_reset(CPUState *s)
     env->sr = 0x2700;
 #endif
     m68k_switch_sp(env);
-    /* ??? FP regs should be initialized to NaN.  */
+    for (i = 0; i < 8; i++) {
+        env->fregs[i].d = nan;
+    }
+    env->fp0h = nan.high;
+    env->fp0l = nan.low;
+    env->fp1h = nan.high;
+    env->fp1l = nan.low;
+    env->fpcr = 0;
+    env->fpsr = 0;
+
     cpu_m68k_set_ccr(env, 0);
     /* TODO: We should set PC from the interrupt vector.  */
     env->pc = 0;
diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h
index 8095822..192a877 100644
--- a/target/m68k/cpu.h
+++ b/target/m68k/cpu.h
@@ -64,6 +64,8 @@
 #define NB_MMU_MODES 2
 #define TARGET_INSN_START_EXTRA_WORDS 1
 
+typedef CPU_LDoubleU FPReg;
+
 typedef struct CPUM68KState {
     uint32_t dregs[8];
     uint32_t aregs[8];
@@ -82,12 +84,16 @@ typedef struct CPUM68KState {
     uint32_t cc_c; /* either 0/1, unused, or computed from cc_n and cc_v */
     uint32_t cc_z; /* == 0 or unused */
 
-    float64 fregs[8];
-    float64 fp_result;
+    FPReg fregs[8];
     uint32_t fpcr;
     uint32_t fpsr;
     float_status fp_status;
 
+    uint32_t fp0h;
+    uint64_t fp0l;
+    uint32_t fp1h;
+    uint64_t fp1l;
+
     uint64_t mactmp;
     /* EMAC Hardware deals with 48-bit values composed of one 32-bit and
        two 8-bit parts.  We store a single 64-bit value and
diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
index 5bf2576..9260e7b 100644
--- a/target/m68k/fpu_helper.c
+++ b/target/m68k/fpu_helper.c
@@ -3,6 +3,7 @@
  *
  *  Copyright (c) 2006-2007 CodeSourcery
  *  Written by Paul Brook
+ *  Copyright (c) 2011-2016 Laurent Vivier
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -21,92 +22,215 @@
 #include "qemu/osdep.h"
 #include "cpu.h"
 #include "exec/helper-proto.h"
+#include "exec/exec-all.h"
 
-uint32_t HELPER(f64_to_i32)(CPUM68KState *env, float64 val)
+static floatx80 FP0_to_floatx80(CPUM68KState *env)
 {
-    return float64_to_int32(val, &env->fp_status);
+    return (floatx80){ .low = env->fp0l, .high = env->fp0h };
 }
 
-float32 HELPER(f64_to_f32)(CPUM68KState *env, float64 val)
+static void floatx80_to_FP0(CPUM68KState *env, floatx80 res)
 {
-    return float64_to_float32(val, &env->fp_status);
+    env->fp0l = res.low;
+    env->fp0h = res.high;
 }
 
-float64 HELPER(i32_to_f64)(CPUM68KState *env, uint32_t val)
+static int32_t FP0_to_int32(CPUM68KState *env)
 {
-    return int32_to_float64(val, &env->fp_status);
+    return env->fp0h;
 }
 
-float64 HELPER(f32_to_f64)(CPUM68KState *env, float32 val)
+static void int32_to_FP0(CPUM68KState *env, int32_t val)
 {
-    return float32_to_float64(val, &env->fp_status);
+    env->fp0h = val;
 }
 
-float64 HELPER(iround_f64)(CPUM68KState *env, float64 val)
+static float32 FP0_to_float32(CPUM68KState *env)
 {
-    return float64_round_to_int(val, &env->fp_status);
+    return *(float32 *)&env->fp0h;
 }
 
-float64 HELPER(itrunc_f64)(CPUM68KState *env, float64 val)
+static void float32_to_FP0(CPUM68KState *env, float32 val)
 {
-    return float64_trunc_to_int(val, &env->fp_status);
+    env->fp0h = *(uint32_t *)&val;
 }
 
-float64 HELPER(sqrt_f64)(CPUM68KState *env, float64 val)
+static float64 FP0_to_float64(CPUM68KState *env)
 {
-    return float64_sqrt(val, &env->fp_status);
+    return *(float64 *)&env->fp0l;
+}
+static void float64_to_FP0(CPUM68KState *env, float64 val)
+{
+    env->fp0l = *(uint64_t *)&val;
 }
 
-float64 HELPER(abs_f64)(float64 val)
+static floatx80 FP1_to_floatx80(CPUM68KState *env)
 {
-    return float64_abs(val);
+    return (floatx80){ .low = env->fp1l, .high = env->fp1h };
 }
 
-float64 HELPER(chs_f64)(float64 val)
+void HELPER(exts32_FP0)(CPUM68KState *env)
 {
-    return float64_chs(val);
+    floatx80 res;
+
+    res = int32_to_floatx80(FP0_to_int32(env), &env->fp_status);
+
+    floatx80_to_FP0(env, res);
 }
 
-float64 HELPER(add_f64)(CPUM68KState *env, float64 a, float64 b)
+void HELPER(extf32_FP0)(CPUM68KState *env)
 {
-    return float64_add(a, b, &env->fp_status);
+    floatx80 res;
+
+    res = float32_to_floatx80(FP0_to_float32(env), &env->fp_status);
+
+    floatx80_to_FP0(env, res);
 }
 
-float64 HELPER(sub_f64)(CPUM68KState *env, float64 a, float64 b)
+void HELPER(extf64_FP0)(CPUM68KState *env)
 {
-    return float64_sub(a, b, &env->fp_status);
+    floatx80 res;
+
+    res = float64_to_floatx80(FP0_to_float64(env), &env->fp_status);
+
+    floatx80_to_FP0(env, res);
 }
 
-float64 HELPER(mul_f64)(CPUM68KState *env, float64 a, float64 b)
+void HELPER(reds32_FP0)(CPUM68KState *env)
 {
-    return float64_mul(a, b, &env->fp_status);
+    int32_t res;
+
+    res = floatx80_to_int32(FP0_to_floatx80(env), &env->fp_status);
+
+    int32_to_FP0(env, res);
 }
 
-float64 HELPER(div_f64)(CPUM68KState *env, float64 a, float64 b)
+void HELPER(redf32_FP0)(CPUM68KState *env)
 {
-    return float64_div(a, b, &env->fp_status);
+    float32 res;
+
+    res = floatx80_to_float32(FP0_to_floatx80(env), &env->fp_status);
+
+    float32_to_FP0(env, res);
 }
 
-float64 HELPER(sub_cmp_f64)(CPUM68KState *env, float64 a, float64 b)
+void HELPER(redf64_FP0)(CPUM68KState *env)
 {
-    /* ??? This may incorrectly raise exceptions.  */
-    /* ??? Should flush denormals to zero.  */
     float64 res;
-    res = float64_sub(a, b, &env->fp_status);
-    if (float64_is_quiet_nan(res, &env->fp_status)) {
+
+    res = floatx80_to_float64(FP0_to_floatx80(env), &env->fp_status);
+
+    float64_to_FP0(env, res);
+}
+
+void HELPER(iround_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res = floatx80_round_to_int(FP0_to_floatx80(env), &env->fp_status);
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(itrunc_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res = floatx80_round_to_int(FP0_to_floatx80(env), &env->fp_status);
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(sqrt_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res = floatx80_sqrt(FP0_to_floatx80(env), &env->fp_status);
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(abs_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res = floatx80_abs(FP0_to_floatx80(env));
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(chs_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res = floatx80_chs(FP0_to_floatx80(env));
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(add_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res = floatx80_add(FP0_to_floatx80(env), FP1_to_floatx80(env),
+                      &env->fp_status);
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(sub_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res = floatx80_sub(FP1_to_floatx80(env), FP0_to_floatx80(env),
+                       &env->fp_status);
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(mul_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res = floatx80_mul(FP0_to_floatx80(env), FP1_to_floatx80(env),
+                       &env->fp_status);
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(div_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res = floatx80_div(FP1_to_floatx80(env), FP0_to_floatx80(env),
+                       &env->fp_status);
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(cmp_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 fp0 = FP0_to_floatx80(env);
+    floatx80 fp1 = FP1_to_floatx80(env);
+    floatx80 res;
+
+    res = floatx80_sub(fp0, fp1, &env->fp_status);
+    if (floatx80_is_quiet_nan(res, &env->fp_status)) {
         /* +/-inf compares equal against itself, but sub returns nan.  */
-        if (!float64_is_quiet_nan(a, &env->fp_status)
-            && !float64_is_quiet_nan(b, &env->fp_status)) {
-            res = float64_zero;
-            if (float64_lt_quiet(a, res, &env->fp_status)) {
-                res = float64_chs(res);
+        if (!floatx80_is_quiet_nan(fp0, &env->fp_status)
+            && !floatx80_is_quiet_nan(fp1, &env->fp_status)) {
+            res = floatx80_zero;
+            if (floatx80_lt_quiet(fp0, res, &env->fp_status)) {
+                res = floatx80_chs(res);
             }
         }
     }
-    return res;
+
+    floatx80_to_FP0(env, res);
 }
 
-uint32_t HELPER(compare_f64)(CPUM68KState *env, float64 val)
+uint32_t HELPER(compare_FP0)(CPUM68KState *env)
 {
-    return float64_compare_quiet(val, float64_zero, &env->fp_status);
+    floatx80 fp0 = FP0_to_floatx80(env);
+    return floatx80_compare_quiet(fp0, floatx80_zero, &env->fp_status);
 }
diff --git a/target/m68k/helper.c b/target/m68k/helper.c
index 5ca9911..8bfc881 100644
--- a/target/m68k/helper.c
+++ b/target/m68k/helper.c
@@ -73,10 +73,11 @@ void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf)
     g_slist_free(list);
 }
 
-static int fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
+static int cf_fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
 {
     if (n < 8) {
-        stfq_p(mem_buf, env->fregs[n]);
+        float_status s;
+        stfq_p(mem_buf, floatx80_to_float64(env->fregs[n].d, &s));
         return 8;
     }
     if (n < 11) {
@@ -87,10 +88,11 @@ static int fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
     return 0;
 }
 
-static int fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
+static int cf_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
 {
     if (n < 8) {
-        env->fregs[n] = ldfq_p(mem_buf);
+        float_status s;
+        env->fregs[n].d = float64_to_floatx80(ldfq_p(mem_buf), &s);
         return 8;
     }
     if (n < 11) {
@@ -126,7 +128,7 @@ void m68k_cpu_init_gdb(M68kCPU *cpu)
     CPUM68KState *env = &cpu->env;
 
     if (m68k_feature(env, M68K_FEATURE_CF_FPU)) {
-        gdb_register_coprocessor(cs, fpu_gdb_get_reg, fpu_gdb_set_reg,
+        gdb_register_coprocessor(cs, cf_fpu_gdb_get_reg, cf_fpu_gdb_set_reg,
                                  11, "cf-fp.xml", 18);
     }
     /* TODO: Add [E]MAC registers.  */
diff --git a/target/m68k/helper.h b/target/m68k/helper.h
index d7a4bf1..d52689b 100644
--- a/target/m68k/helper.h
+++ b/target/m68k/helper.h
@@ -12,21 +12,23 @@ DEF_HELPER_3(movec, void, env, i32, i32)
 DEF_HELPER_4(cas2w, void, env, i32, i32, i32)
 DEF_HELPER_4(cas2l, void, env, i32, i32, i32)
 
-DEF_HELPER_2(f64_to_i32, f32, env, f64)
-DEF_HELPER_2(f64_to_f32, f32, env, f64)
-DEF_HELPER_2(i32_to_f64, f64, env, i32)
-DEF_HELPER_2(f32_to_f64, f64, env, f32)
-DEF_HELPER_2(iround_f64, f64, env, f64)
-DEF_HELPER_2(itrunc_f64, f64, env, f64)
-DEF_HELPER_2(sqrt_f64, f64, env, f64)
-DEF_HELPER_1(abs_f64, f64, f64)
-DEF_HELPER_1(chs_f64, f64, f64)
-DEF_HELPER_3(add_f64, f64, env, f64, f64)
-DEF_HELPER_3(sub_f64, f64, env, f64, f64)
-DEF_HELPER_3(mul_f64, f64, env, f64, f64)
-DEF_HELPER_3(div_f64, f64, env, f64, f64)
-DEF_HELPER_3(sub_cmp_f64, f64, env, f64, f64)
-DEF_HELPER_2(compare_f64, i32, env, f64)
+DEF_HELPER_1(exts32_FP0, void, env)
+DEF_HELPER_1(extf32_FP0, void, env)
+DEF_HELPER_1(extf64_FP0, void, env)
+DEF_HELPER_1(redf32_FP0, void, env)
+DEF_HELPER_1(redf64_FP0, void, env)
+DEF_HELPER_1(reds32_FP0, void, env)
+DEF_HELPER_1(iround_FP0, void, env)
+DEF_HELPER_1(itrunc_FP0, void, env)
+DEF_HELPER_1(sqrt_FP0, void, env)
+DEF_HELPER_1(abs_FP0, void, env)
+DEF_HELPER_1(chs_FP0, void, env)
+DEF_HELPER_1(add_FP0_FP1, void, env)
+DEF_HELPER_1(sub_FP0_FP1, void, env)
+DEF_HELPER_1(mul_FP0_FP1, void, env)
+DEF_HELPER_1(div_FP0_FP1, void, env)
+DEF_HELPER_1(cmp_FP0_FP1, void, env)
+DEF_HELPER_1(compare_FP0, i32, env)
 
 DEF_HELPER_3(mac_move, void, env, i32, i32)
 DEF_HELPER_3(macmulf, i64, env, i32, i32)
diff --git a/target/m68k/qregs.def b/target/m68k/qregs.def
index 51ff43b..b6441d9 100644
--- a/target/m68k/qregs.def
+++ b/target/m68k/qregs.def
@@ -1,4 +1,5 @@
-DEFF64(FP_RESULT, fp_result)
+DEFF96(FP0, fp0)
+DEFF96(FP1, fp1)
 DEFO32(PC, pc)
 DEFO32(SR, sr)
 DEFO32(CC_OP, cc_op)
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index d9ba735..3a56b27 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -32,37 +32,30 @@
 #include "trace-tcg.h"
 #include "exec/log.h"
 
-
 //#define DEBUG_DISPATCH 1
 
-/* Fake floating point.  */
-#define tcg_gen_mov_f64 tcg_gen_mov_i64
-#define tcg_gen_qemu_ldf64 tcg_gen_qemu_ld64
-#define tcg_gen_qemu_stf64 tcg_gen_qemu_st64
-
 #define DEFO32(name, offset) static TCGv QREG_##name;
 #define DEFO64(name, offset) static TCGv_i64 QREG_##name;
-#define DEFF64(name, offset) static TCGv_i64 QREG_##name;
+#define DEFF96(name, offset) static TCGv_i32 QREG_##name##H; \
+                             static TCGv_i64 QREG_##name##L;
 #include "qregs.def"
 #undef DEFO32
 #undef DEFO64
-#undef DEFF64
+#undef DEFF96
 
 static TCGv_i32 cpu_halted;
 static TCGv_i32 cpu_exception_index;
 
 static TCGv_env cpu_env;
 
-static char cpu_reg_names[3*8*3 + 5*4];
+static char cpu_reg_names[2 * 8 * 3 + 5 * 4];
 static TCGv cpu_dregs[8];
 static TCGv cpu_aregs[8];
-static TCGv_i64 cpu_fregs[8];
 static TCGv_i64 cpu_macc[4];
 
 #define REG(insn, pos)  (((insn) >> (pos)) & 7)
 #define DREG(insn, pos) cpu_dregs[REG(insn, pos)]
 #define AREG(insn, pos) get_areg(s, REG(insn, pos))
-#define FREG(insn, pos) cpu_fregs[REG(insn, pos)]
 #define MACREG(acc)     cpu_macc[acc]
 #define QREG_SP         get_areg(s, 7)
 
@@ -87,11 +80,16 @@ void m68k_tcg_init(void)
 #define DEFO64(name, offset) \
     QREG_##name = tcg_global_mem_new_i64(cpu_env, \
         offsetof(CPUM68KState, offset), #name);
-#define DEFF64(name, offset) DEFO64(name, offset)
+#define DEFF96(name,  offset) do { \
+    QREG_##name##H = tcg_global_mem_new_i32(cpu_env,    \
+        offsetof(CPUM68KState, offset##h), #name); \
+    QREG_##name##L = tcg_global_mem_new_i64(cpu_env,    \
+        offsetof(CPUM68KState, offset##l), #name); \
+} while (0);
 #include "qregs.def"
 #undef DEFO32
 #undef DEFO64
-#undef DEFF64
+#undef DEFF96
 
     cpu_halted = tcg_global_mem_new_i32(cpu_env,
                                         -offsetof(M68kCPU, env) +
@@ -111,10 +109,6 @@ void m68k_tcg_init(void)
         cpu_aregs[i] = tcg_global_mem_new(cpu_env,
                                           offsetof(CPUM68KState, aregs[i]), p);
         p += 3;
-        sprintf(p, "F%d", i);
-        cpu_fregs[i] = tcg_global_mem_new_i64(cpu_env,
-                                          offsetof(CPUM68KState, fregs[i]), p);
-        p += 3;
     }
     for (i = 0; i < 4; i++) {
         sprintf(p, "ACC%d", i);
@@ -286,7 +280,6 @@ static inline TCGv gen_load(DisasContext * s, int opsize, TCGv addr, int sign)
             tcg_gen_qemu_ld16u(tmp, addr, index);
         break;
     case OS_LONG:
-    case OS_SINGLE:
         tcg_gen_qemu_ld32u(tmp, addr, index);
         break;
     default:
@@ -296,16 +289,6 @@ static inline TCGv gen_load(DisasContext * s, int opsize, TCGv addr, int sign)
     return tmp;
 }
 
-static inline TCGv_i64 gen_load64(DisasContext * s, TCGv addr)
-{
-    TCGv_i64 tmp;
-    int index = IS_USER(s);
-    tmp = tcg_temp_new_i64();
-    tcg_gen_qemu_ldf64(tmp, addr, index);
-    gen_throws_exception = gen_last_qop;
-    return tmp;
-}
-
 /* Generate a store.  */
 static inline void gen_store(DisasContext *s, int opsize, TCGv addr, TCGv val)
 {
@@ -318,7 +301,6 @@ static inline void gen_store(DisasContext *s, int opsize, TCGv addr, TCGv val)
         tcg_gen_qemu_st16(val, addr, index);
         break;
     case OS_LONG:
-    case OS_SINGLE:
         tcg_gen_qemu_st32(val, addr, index);
         break;
     default:
@@ -327,13 +309,6 @@ static inline void gen_store(DisasContext *s, int opsize, TCGv addr, TCGv val)
     gen_throws_exception = gen_last_qop;
 }
 
-static inline void gen_store64(DisasContext *s, TCGv addr, TCGv_i64 val)
-{
-    int index = IS_USER(s);
-    tcg_gen_qemu_stf64(val, addr, index);
-    gen_throws_exception = gen_last_qop;
-}
-
 typedef enum {
     EA_STORE,
     EA_LOADU,
@@ -377,6 +352,15 @@ static inline uint32_t read_im32(CPUM68KState *env, DisasContext *s)
     return im;
 }
 
+/* Read a 64-bit immediate constant.  */
+static inline uint64_t read_im64(CPUM68KState *env, DisasContext *s)
+{
+    uint64_t im;
+    im = (uint64_t)read_im32(env, s) << 32;
+    im |= (uint64_t)read_im32(env, s);
+    return im;
+}
+
 /* Calculate and address index.  */
 static TCGv gen_addr_index(DisasContext *s, uint16_t ext, TCGv tmp)
 {
@@ -909,6 +893,291 @@ static TCGv gen_ea(CPUM68KState *env, DisasContext *s, uint16_t insn,
     return gen_ea_mode(env, s, mode, reg0, opsize, val, addrp, what);
 }
 
+static void gen_op_load_fpr_FP0(int freg)
+{
+    tcg_gen_ld16u_i32(QREG_FP0H, cpu_env,
+                      offsetof(CPUM68KState, fregs[freg].l.upper));
+    tcg_gen_ld_i64(QREG_FP0L, cpu_env,
+                   offsetof(CPUM68KState, fregs[freg].l.lower));
+}
+
+static void gen_op_store_fpr_FP0(int freg)
+{
+    tcg_gen_st16_i32(QREG_FP0H, cpu_env,
+                     offsetof(CPUM68KState, fregs[freg].l.upper));
+    tcg_gen_st_i64(QREG_FP0L, cpu_env,
+                   offsetof(CPUM68KState, fregs[freg].l.lower));
+}
+
+static void gen_op_load_fpr_FP1(int freg)
+{
+    tcg_gen_ld16u_i32(QREG_FP1H, cpu_env,
+                      offsetof(CPUM68KState, fregs[freg].l.upper));
+    tcg_gen_ld_i64(QREG_FP1L, cpu_env,
+                   offsetof(CPUM68KState, fregs[freg].l.lower));
+}
+
+static void gen_extend_FP0(int opsize)
+{
+    switch (opsize) {
+    case OS_BYTE:
+        tcg_gen_ext8s_i32(QREG_FP0H, QREG_FP0H);
+        gen_helper_exts32_FP0(cpu_env);
+        break;
+    case OS_WORD:
+        tcg_gen_ext16s_i32(QREG_FP0H, QREG_FP0H);
+        gen_helper_exts32_FP0(cpu_env);
+        break;
+    case OS_LONG:
+        gen_helper_exts32_FP0(cpu_env);
+        break;
+    case OS_SINGLE:
+        gen_helper_extf32_FP0(cpu_env);
+        break;
+    case OS_DOUBLE:
+        gen_helper_extf64_FP0(cpu_env);
+        break;
+    case OS_EXTENDED:
+        tcg_gen_shri_i32(QREG_FP0H, QREG_FP0H, 16);
+        break;
+    case OS_PACKED:
+        /* nothing to do */
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static void gen_reduce_FP0(int opsize)
+{
+    switch (opsize) {
+    case OS_BYTE:
+    case OS_WORD:
+    case OS_LONG:
+        gen_helper_reds32_FP0(cpu_env);
+        break;
+    case OS_SINGLE:
+        gen_helper_redf32_FP0(cpu_env);
+        break;
+    case OS_DOUBLE:
+        gen_helper_redf64_FP0(cpu_env);
+        break;
+    case OS_EXTENDED:
+        tcg_gen_shli_i32(QREG_FP0H, QREG_FP0H, 16);
+        break;
+    case OS_PACKED:
+        /* nothing to do */
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static void gen_load_FP0(DisasContext *s, int opsize, TCGv addr)
+{
+    TCGv tmp;
+    int index = IS_USER(s);
+    switch (opsize) {
+    case OS_BYTE:
+        tcg_gen_qemu_ld8s(QREG_FP0H, addr, index);
+        gen_helper_exts32_FP0(cpu_env);
+        break;
+    case OS_WORD:
+        tcg_gen_qemu_ld16s(QREG_FP0H, addr, index);
+        gen_helper_exts32_FP0(cpu_env);
+        break;
+    case OS_LONG:
+        tcg_gen_qemu_ld32u(QREG_FP0H, addr, index);
+        gen_helper_exts32_FP0(cpu_env);
+        break;
+    case OS_SINGLE:
+        tcg_gen_qemu_ld32u(QREG_FP0H, addr, index);
+        gen_helper_extf32_FP0(cpu_env);
+        break;
+    case OS_DOUBLE:
+        tcg_gen_qemu_ld64(QREG_FP0L, addr, index);
+        gen_helper_extf64_FP0(cpu_env);
+        break;
+    case OS_EXTENDED:
+        tcg_gen_qemu_ld32u(QREG_FP0H, addr, index);
+        tcg_gen_shri_i32(QREG_FP0H, QREG_FP0H, 16);
+        tmp = tcg_temp_new();
+        tcg_gen_addi_i32(tmp, addr, 4);
+        tcg_gen_qemu_ld64(QREG_FP0L, tmp, index);
+        tcg_temp_free(tmp);
+        break;
+    case OS_PACKED:
+        tcg_gen_qemu_ld32u(QREG_FP0H, addr, index);
+        tmp = tcg_temp_new();
+        tcg_gen_addi_i32(tmp, addr, 4);
+        tcg_gen_qemu_ld64(QREG_FP0L, tmp, index);
+        tcg_temp_free(tmp);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    gen_throws_exception = gen_last_qop;
+}
+
+static void gen_store_FP0(DisasContext *s, int opsize, TCGv addr)
+{
+    TCGv tmp;
+    int index = IS_USER(s);
+    switch (opsize) {
+    case OS_BYTE:
+        gen_helper_reds32_FP0(cpu_env);
+        tcg_gen_qemu_st8(QREG_FP0H, addr, index);
+        break;
+    case OS_WORD:
+        gen_helper_reds32_FP0(cpu_env);
+        tcg_gen_qemu_st16(QREG_FP0H, addr, index);
+        break;
+    case OS_LONG:
+        gen_helper_reds32_FP0(cpu_env);
+        tcg_gen_qemu_st32(QREG_FP0H, addr, index);
+        break;
+    case OS_SINGLE:
+        gen_helper_redf32_FP0(cpu_env);
+        tcg_gen_qemu_st32(QREG_FP0H, addr, index);
+        break;
+    case OS_DOUBLE:
+        gen_helper_redf64_FP0(cpu_env);
+        tcg_gen_qemu_st64(QREG_FP0L, addr, index);
+        break;
+    case OS_EXTENDED:
+        tcg_gen_shli_i32(QREG_FP0H, QREG_FP0H, 16);
+        tcg_gen_qemu_st32(QREG_FP0H, addr, index);
+        tmp = tcg_temp_new();
+        tcg_gen_addi_i32(tmp, addr, 4);
+        tcg_gen_qemu_st64(QREG_FP0L, tmp, index);
+        tcg_temp_free(tmp);
+        break;
+    case OS_PACKED:
+        tcg_gen_qemu_st32(QREG_FP0H, addr, index);
+        tmp = tcg_temp_new();
+        tcg_gen_addi_i32(tmp, addr, 4);
+        tcg_gen_qemu_st64(QREG_FP0L, tmp, index);
+        tcg_temp_free(tmp);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    gen_throws_exception = gen_last_qop;
+}
+
+static void gen_ldst_FP0(DisasContext *s, int opsize, TCGv addr, ea_what what)
+{
+    if (what == EA_STORE) {
+        gen_store_FP0(s, opsize, addr);
+    } else {
+        gen_load_FP0(s, opsize, addr);
+    }
+}
+
+static int gen_ea_mode_FP0(CPUM68KState *env, DisasContext *s, int mode,
+                           int reg0, int opsize, ea_what what)
+{
+    TCGv reg, addr;
+    uint64_t val64;
+    uint32_t val32;
+
+    switch (mode) {
+    case 0: /* Data register direct.  */
+        reg = cpu_dregs[reg0];
+        if (what == EA_STORE) {
+            gen_reduce_FP0(opsize);
+            tcg_gen_mov_i32(reg, QREG_FP0H);
+            return 0;
+        } else {
+            tcg_gen_mov_i32(QREG_FP0H, reg);
+            gen_extend_FP0(opsize);
+            return 0;
+        }
+    case 1: /* Address register direct.  */
+        return -1;
+    case 2: /* Indirect register */
+        reg = get_areg(s, reg0);
+        gen_ldst_FP0(s, opsize, reg, what);
+        return 0;
+    case 3: /* Indirect postincrement.  */
+        reg = cpu_aregs[reg0];
+        gen_ldst_FP0(s, opsize, reg, what);
+        tcg_gen_addi_i32(reg, reg, opsize_bytes(opsize));
+        return 0;
+    case 4: /* Indirect predecrememnt.  */
+        addr = gen_lea_mode(env, s, mode, reg0, opsize);
+        if (IS_NULL_QREG(addr)) {
+            return -1;
+        }
+        gen_ldst_FP0(s, opsize, addr, what);
+        tcg_gen_mov_i32(cpu_aregs[reg0], addr);
+        return 0;
+    case 5: /* Indirect displacement.  */
+    case 6: /* Indirect index + displacement.  */
+    do_indirect:
+        addr = gen_lea_mode(env, s, mode, reg0, opsize);
+        if (IS_NULL_QREG(addr)) {
+            return -1;
+        }
+        gen_ldst_FP0(s, opsize, addr, what);
+        return 0;
+    case 7: /* Other */
+        switch (reg0) {
+        case 0: /* Absolute short.  */
+        case 1: /* Absolute long.  */
+        case 2: /* pc displacement  */
+        case 3: /* pc index+displacement.  */
+            goto do_indirect;
+        case 4: /* Immediate.  */
+            if (what == EA_STORE) {
+                return -1;
+            }
+            val32 = 0;
+            val64 = 0;
+            switch (opsize) {
+            case OS_BYTE:
+                val32 = read_im8(env, s);
+                break;
+            case OS_WORD:
+                val32 = read_im16(env, s);
+                break;
+            case OS_LONG:
+            case OS_SINGLE:
+                val32 = read_im32(env, s);
+                break;
+            case OS_DOUBLE:
+                val64 = read_im64(env, s);
+                break;
+            case OS_EXTENDED:
+                val32 = read_im32(env, s);
+                val64 = read_im64(env, s);
+                break;
+            case OS_PACKED:
+                val32 = read_im32(env, s);
+                val64 = read_im64(env, s);
+                break;
+            default:
+                g_assert_not_reached();
+            }
+            tcg_gen_movi_i32(QREG_FP0H, val32);
+            tcg_gen_movi_i64(QREG_FP0L, val64);
+            gen_extend_FP0(opsize);
+            return 0;
+        default:
+            return -1;
+        }
+    }
+    return -1;
+}
+
+static int gen_ea_FP0(CPUM68KState *env, DisasContext *s, uint16_t insn,
+                       int opsize, ea_what what)
+{
+    int mode = extract32(insn, 3, 3);
+    int reg0 = REG(insn, 0);
+    return gen_ea_mode_FP0(env, s, mode, reg0, opsize, what);
+}
+
 typedef struct {
     TCGCond tcond;
     bool g1;
@@ -4089,16 +4358,53 @@ DISAS_INSN(trap)
     gen_exception(s, s->pc - 2, EXCP_TRAP0 + (insn & 0xf));
 }
 
+static void gen_op_fmove_fcr(CPUM68KState *env, DisasContext *s,
+                             uint32_t insn, uint32_t ext)
+{
+    int mask = (ext >> 10) & 7;
+    int is_write = (ext >> 13) & 1;
+    TCGv tmp;
+
+    tmp = gen_lea(env, s, insn, OS_LONG);
+    if (IS_NULL_QREG(tmp)) {
+        TCGv val;
+
+        if (is_write) {
+            switch (mask) {
+            case 1: /* FPIAR */
+            case 2: /* FPSR */
+            default:
+                cpu_abort(NULL, "Unimplemented: fmove from control %d", mask);
+                break;
+            case 4: /* FPCR */
+                val = tcg_const_i32(0);
+                DEST_EA(env, insn, OS_LONG, val, NULL);
+                tcg_temp_free(val);
+                break;
+            }
+            return;
+        }
+        switch (mask) {
+        case 1: /* FPIAR */
+        case 2: /* FPSR */
+        default:
+            cpu_abort(NULL, "Unimplemented: fmove to control %d",
+                      mask);
+            break;
+        case 4: /* FPCR */
+            /* Not implemented.  Ignore writes.  */
+            break;
+        }
+        return;
+    }
+}
+
 /* ??? FP exceptions are not implemented.  Most exceptions are deferred until
    immediately before the next FP instruction is executed.  */
 DISAS_INSN(fpu)
 {
     uint16_t ext;
-    int32_t offset;
     int opmode;
-    TCGv_i64 src;
-    TCGv_i64 dest;
-    TCGv_i64 res;
     TCGv tmp32;
     int round;
     int set_dest;
@@ -4112,87 +4418,17 @@ DISAS_INSN(fpu)
     case 1:
         goto undef;
     case 3: /* fmove out */
-        src = FREG(ext, 7);
-        tmp32 = tcg_temp_new_i32();
-        /* fmove */
-        /* ??? TODO: Proper behavior on overflow.  */
-
+        gen_op_load_fpr_FP0(REG(ext, 7));
         opsize = ext_opsize(ext, 10);
-        switch (opsize) {
-        case OS_LONG:
-            gen_helper_f64_to_i32(tmp32, cpu_env, src);
-            break;
-        case OS_SINGLE:
-            gen_helper_f64_to_f32(tmp32, cpu_env, src);
-            break;
-        case OS_WORD:
-            gen_helper_f64_to_i32(tmp32, cpu_env, src);
-            break;
-        case OS_DOUBLE:
-            tcg_gen_mov_i32(tmp32, AREG(insn, 0));
-            switch ((insn >> 3) & 7) {
-            case 2:
-            case 3:
-                break;
-            case 4:
-                tcg_gen_addi_i32(tmp32, tmp32, -8);
-                break;
-            case 5:
-                offset = cpu_ldsw_code(env, s->pc);
-                s->pc += 2;
-                tcg_gen_addi_i32(tmp32, tmp32, offset);
-                break;
-            default:
-                goto undef;
-            }
-            gen_store64(s, tmp32, src);
-            switch ((insn >> 3) & 7) {
-            case 3:
-                tcg_gen_addi_i32(tmp32, tmp32, 8);
-                tcg_gen_mov_i32(AREG(insn, 0), tmp32);
-                break;
-            case 4:
-                tcg_gen_mov_i32(AREG(insn, 0), tmp32);
-                break;
-            }
-            tcg_temp_free_i32(tmp32);
+        if (gen_ea_FP0(env, s, insn, opsize, EA_STORE) == -1) {
+            gen_addr_fault(s);
             return;
-        case OS_BYTE:
-            gen_helper_f64_to_i32(tmp32, cpu_env, src);
-            break;
-        default:
-            goto undef;
         }
-        DEST_EA(env, insn, opsize, tmp32, NULL);
-        tcg_temp_free_i32(tmp32);
         return;
     case 4: /* fmove to control register.  */
-        switch ((ext >> 10) & 7) {
-        case 4: /* FPCR */
-            /* Not implemented.  Ignore writes.  */
-            break;
-        case 1: /* FPIAR */
-        case 2: /* FPSR */
-        default:
-            cpu_abort(NULL, "Unimplemented: fmove to control %d",
-                      (ext >> 10) & 7);
-        }
-        break;
     case 5: /* fmove from control register.  */
-        switch ((ext >> 10) & 7) {
-        case 4: /* FPCR */
-            /* Not implemented.  Always return zero.  */
-            tmp32 = tcg_const_i32(0);
-            break;
-        case 1: /* FPIAR */
-        case 2: /* FPSR */
-        default:
-            cpu_abort(NULL, "Unimplemented: fmove from control %d",
-                      (ext >> 10) & 7);
-            goto undef;
-        }
-        DEST_EA(env, insn, OS_LONG, tmp32, NULL);
-        break;
+        gen_op_fmove_fcr(env, s, insn, ext);
+        return;
     case 6: /* fmovem */
     case 7:
         {
@@ -4211,14 +4447,9 @@ DISAS_INSN(fpu)
             mask = 0x80;
             for (i = 0; i < 8; i++) {
                 if (ext & mask) {
-                    dest = FREG(i, 0);
-                    if (ext & (1 << 13)) {
-                        /* store */
-                        tcg_gen_qemu_stf64(dest, addr, IS_USER(s));
-                    } else {
-                        /* load */
-                        tcg_gen_qemu_ldf64(dest, addr, IS_USER(s));
-                    }
+                    gen_op_load_fpr_FP0(REG(i, 0));
+                    gen_ldst_FP0(s, OS_DOUBLE, addr, (ext & (1 << 13)) ?
+                                                     EA_STORE : EA_LOADS);
                     if (ext & (mask - 1))
                         tcg_gen_addi_i32(addr, addr, 8);
                 }
@@ -4231,114 +4462,66 @@ DISAS_INSN(fpu)
     if (ext & (1 << 14)) {
         /* Source effective address.  */
         opsize = ext_opsize(ext, 10);
-        if (opsize == OS_DOUBLE) {
-            tmp32 = tcg_temp_new_i32();
-            tcg_gen_mov_i32(tmp32, AREG(insn, 0));
-            switch ((insn >> 3) & 7) {
-            case 2:
-            case 3:
-                break;
-            case 4:
-                tcg_gen_addi_i32(tmp32, tmp32, -8);
-                break;
-            case 5:
-                offset = cpu_ldsw_code(env, s->pc);
-                s->pc += 2;
-                tcg_gen_addi_i32(tmp32, tmp32, offset);
-                break;
-            case 7:
-                offset = cpu_ldsw_code(env, s->pc);
-                offset += s->pc - 2;
-                s->pc += 2;
-                tcg_gen_addi_i32(tmp32, tmp32, offset);
-                break;
-            default:
-                goto undef;
-            }
-            src = gen_load64(s, tmp32);
-            switch ((insn >> 3) & 7) {
-            case 3:
-                tcg_gen_addi_i32(tmp32, tmp32, 8);
-                tcg_gen_mov_i32(AREG(insn, 0), tmp32);
-                break;
-            case 4:
-                tcg_gen_mov_i32(AREG(insn, 0), tmp32);
-                break;
-            }
-            tcg_temp_free_i32(tmp32);
-        } else {
-            SRC_EA(env, tmp32, opsize, 1, NULL);
-            src = tcg_temp_new_i64();
-            switch (opsize) {
-            case OS_LONG:
-            case OS_WORD:
-            case OS_BYTE:
-                gen_helper_i32_to_f64(src, cpu_env, tmp32);
-                break;
-            case OS_SINGLE:
-                gen_helper_f32_to_f64(src, cpu_env, tmp32);
-                break;
-            }
+        if (gen_ea_FP0(env, s, insn, opsize, EA_LOADS) == -1) {
+            gen_addr_fault(s);
+            return;
         }
     } else {
         /* Source register.  */
-        src = FREG(ext, 10);
+        opsize = OS_EXTENDED;
+        gen_op_load_fpr_FP0(REG(ext, 10));
     }
-    dest = FREG(ext, 7);
-    res = tcg_temp_new_i64();
-    if (opmode != 0x3a)
-        tcg_gen_mov_f64(res, dest);
     round = 1;
     set_dest = 1;
     switch (opmode) {
     case 0: case 0x40: case 0x44: /* fmove */
-        tcg_gen_mov_f64(res, src);
         break;
     case 1: /* fint */
-        gen_helper_iround_f64(res, cpu_env, src);
+        gen_helper_iround_FP0(cpu_env);
         round = 0;
         break;
     case 3: /* fintrz */
-        gen_helper_itrunc_f64(res, cpu_env, src);
+        gen_helper_itrunc_FP0(cpu_env);
         round = 0;
         break;
     case 4: case 0x41: case 0x45: /* fsqrt */
-        gen_helper_sqrt_f64(res, cpu_env, src);
+        gen_helper_sqrt_FP0(cpu_env);
         break;
     case 0x18: case 0x58: case 0x5c: /* fabs */
-        gen_helper_abs_f64(res, src);
+        gen_helper_abs_FP0(cpu_env);
         break;
     case 0x1a: case 0x5a: case 0x5e: /* fneg */
-        gen_helper_chs_f64(res, src);
+        gen_helper_chs_FP0(cpu_env);
         break;
     case 0x20: case 0x60: case 0x64: /* fdiv */
-        gen_helper_div_f64(res, cpu_env, res, src);
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_div_FP0_FP1(cpu_env);
         break;
     case 0x22: case 0x62: case 0x66: /* fadd */
-        gen_helper_add_f64(res, cpu_env, res, src);
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_add_FP0_FP1(cpu_env);
         break;
     case 0x23: case 0x63: case 0x67: /* fmul */
-        gen_helper_mul_f64(res, cpu_env, res, src);
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_mul_FP0_FP1(cpu_env);
         break;
     case 0x28: case 0x68: case 0x6c: /* fsub */
-        gen_helper_sub_f64(res, cpu_env, res, src);
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_sub_FP0_FP1(cpu_env);
         break;
     case 0x38: /* fcmp */
-        gen_helper_sub_cmp_f64(res, cpu_env, res, src);
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_cmp_FP0_FP1(cpu_env);
         set_dest = 0;
         round = 0;
         break;
     case 0x3a: /* ftst */
-        tcg_gen_mov_f64(res, src);
         set_dest = 0;
         round = 0;
         break;
     default:
         goto undef;
     }
-    if (ext & (1 << 14)) {
-        tcg_temp_free_i64(src);
-    }
     if (round) {
         if (opmode & 0x40) {
             if ((opmode & 0x4) != 0)
@@ -4348,16 +4531,15 @@ DISAS_INSN(fpu)
         }
     }
     if (round) {
-        TCGv tmp = tcg_temp_new_i32();
-        gen_helper_f64_to_f32(tmp, cpu_env, res);
-        gen_helper_f32_to_f64(res, cpu_env, tmp);
-        tcg_temp_free_i32(tmp);
+        gen_helper_redf32_FP0(cpu_env);
+        gen_helper_extf32_FP0(cpu_env);
+    } else {
+        gen_helper_redf64_FP0(cpu_env);
+        gen_helper_extf64_FP0(cpu_env);
     }
-    tcg_gen_mov_f64(QREG_FP_RESULT, res);
     if (set_dest) {
-        tcg_gen_mov_f64(dest, res);
+        gen_op_store_fpr_FP0(REG(ext, 7));
     }
-    tcg_temp_free_i64(res);
     return;
 undef:
     /* FIXME: Is this right for offset addressing modes?  */
@@ -4382,10 +4564,10 @@ DISAS_INSN(fbcc)
     l1 = gen_new_label();
     /* TODO: Raise BSUN exception.  */
     flag = tcg_temp_new();
-    gen_helper_compare_f64(flag, cpu_env, QREG_FP_RESULT);
+    gen_helper_compare_FP0(flag, cpu_env);
     /* Jump to l1 if condition is true.  */
     switch (insn & 0xf) {
-    case 0: /* f */
+    case 0:  /* False */
         break;
     case 1: /* eq (=0) */
         tcg_gen_brcond_i32(TCG_COND_EQ, flag, tcg_const_i32(0), l1);
@@ -5011,11 +5193,15 @@ void register_m68k_insns (CPUM68KState *env)
     INSN(bfop_reg, eec0, fff8, BITFIELD);   /* bfset */
     INSN(bfop_mem, e8c0, ffc0, BITFIELD);   /* bftst */
     INSN(bfop_reg, e8c0, fff8, BITFIELD);   /* bftst */
-    INSN(undef_fpu, f000, f000, CF_ISA_A);
+    BASE(undef_fpu, f000, f000);
     INSN(fpu,       f200, ffc0, CF_FPU);
     INSN(fbcc,      f280, ffc0, CF_FPU);
     INSN(frestore,  f340, ffc0, CF_FPU);
-    INSN(fsave,     f340, ffc0, CF_FPU);
+    INSN(fsave,     f300, ffc0, CF_FPU);
+    INSN(fpu,       f200, ffc0, FPU);
+    INSN(fbcc,      f280, ff80, FPU);
+    INSN(frestore,  f340, ffc0, FPU);
+    INSN(fsave,     f300, ffc0, FPU);
     INSN(intouch,   f340, ffc0, CF_ISA_A);
     INSN(cpushl,    f428, ff38, CF_ISA_A);
     INSN(wddata,    fb00, ff00, CF_ISA_A);
@@ -5141,6 +5327,18 @@ void gen_intermediate_code(CPUM68KState *env, TranslationBlock *tb)
     tb->icount = num_insns;
 }
 
+static double floatx80_to_double(CPUM68KState *env, uint16_t high, uint64_t low)
+{
+    floatx80 a = { .high = high, .low = low };
+    union {
+        float64 f64;
+        double d;
+    } u;
+
+    u.f64 = floatx80_to_float64(a, &env->fp_status);
+    return u.d;
+}
+
 void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
                          int flags)
 {
@@ -5148,20 +5346,19 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
     CPUM68KState *env = &cpu->env;
     int i;
     uint16_t sr;
-    CPU_DoubleU u;
-    for (i = 0; i < 8; i++)
-      {
-        u.d = env->fregs[i];
-        cpu_fprintf(f, "D%d = %08x   A%d = %08x   F%d = %08x%08x (%12g)\n",
+    for (i = 0; i < 8; i++) {
+        cpu_fprintf(f, "D%d = %08x   A%d = %08x   "
+                    "F%d = %04x %016"PRIx64"  (%12g)\n",
                     i, env->dregs[i], i, env->aregs[i],
-                    i, u.l.upper, u.l.lower, *(double *)&u.d);
-      }
+                    i, env->fregs[i].l.upper, env->fregs[i].l.lower,
+                    floatx80_to_double(env, env->fregs[i].l.upper,
+                                       env->fregs[i].l.lower));
+    }
     cpu_fprintf (f, "PC = %08x   ", env->pc);
     sr = env->sr | cpu_m68k_get_ccr(env);
     cpu_fprintf(f, "SR = %04x %c%c%c%c%c ", sr, (sr & CCF_X) ? 'X' : '-',
                 (sr & CCF_N) ? 'N' : '-', (sr & CCF_Z) ? 'Z' : '-',
                 (sr & CCF_V) ? 'V' : '-', (sr & CCF_C) ? 'C' : '-');
-    cpu_fprintf (f, "FPRESULT = %12g\n", *(double *)&env->fp_result);
 }
 
 void restore_state_to_opc(CPUM68KState *env, TranslationBlock *tb,
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [Qemu-devel] [PATCH v3 06/16] target-m68k: add FPCR and FPSR
  2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
                   ` (4 preceding siblings ...)
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 05/16] target-m68k: use floatx80 internally Laurent Vivier
@ 2017-02-07  0:59 ` Laurent Vivier
  2017-02-16  1:10   ` Richard Henderson
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 07/16] target-m68k: manage FPU exceptions Laurent Vivier
                   ` (10 subsequent siblings)
  16 siblings, 1 reply; 38+ messages in thread
From: Laurent Vivier @ 2017-02-07  0:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Richard Henderson, Laurent Vivier

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 target/m68k/cpu.c        |   2 +-
 target/m68k/cpu.h        |  36 +++++-
 target/m68k/fpu_helper.c | 116 +++++++++++++++---
 target/m68k/helper.c     |  20 ++-
 target/m68k/helper.h     |   3 +-
 target/m68k/qregs.def    |   1 +
 target/m68k/translate.c  | 311 +++++++++++++++++++++++++++++++++++------------
 7 files changed, 381 insertions(+), 108 deletions(-)

diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c
index cedc272..9553df4 100644
--- a/target/m68k/cpu.c
+++ b/target/m68k/cpu.c
@@ -66,7 +66,7 @@ static void m68k_cpu_reset(CPUState *s)
     env->fp0l = nan.low;
     env->fp1h = nan.high;
     env->fp1l = nan.low;
-    env->fpcr = 0;
+    cpu_m68k_set_fpcr(env, 0);
     env->fpsr = 0;
 
     cpu_m68k_set_ccr(env, 0);
diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h
index 192a877..6b3cb26 100644
--- a/target/m68k/cpu.h
+++ b/target/m68k/cpu.h
@@ -168,6 +168,7 @@ int cpu_m68k_signal_handler(int host_signum, void *pinfo,
                            void *puc);
 uint32_t cpu_m68k_get_ccr(CPUM68KState *env);
 void cpu_m68k_set_ccr(CPUM68KState *env, uint32_t);
+void cpu_m68k_set_fpcr(CPUM68KState *env, uint32_t val);
 
 
 /* Instead of computing the condition codes after each m68k instruction,
@@ -212,6 +213,36 @@ typedef enum {
 #define M68K_SSP    0
 #define M68K_USP    1
 
+/* Floating-Point Status Register */
+
+/* Condition Code */
+#define FPSR_CC_MASK  0x0f000000
+#define FPSR_CC_A     0x01000000 /* Not-A-Number */
+#define FPSR_CC_I     0x02000000 /* Infinity */
+#define FPSR_CC_Z     0x04000000 /* Zero */
+#define FPSR_CC_N     0x08000000 /* Negative */
+
+/* Quotient */
+
+#define FPSR_QT_MASK  0x00ff0000
+
+/* Floating-Point Control Register */
+/* Rounding mode */
+#define FPCR_RND_MASK   0x0030
+#define FPCR_RND_N      0x0000
+#define FPCR_RND_Z      0x0010
+#define FPCR_RND_M      0x0020
+#define FPCR_RND_P      0x0030
+
+/* Rounding precision */
+#define FPCR_PREC_MASK  0x00c0
+#define FPCR_PREC_X     0x0000
+#define FPCR_PREC_S     0x0040
+#define FPCR_PREC_D     0x0080
+#define FPCR_PREC_U     0x00c0
+
+#define FPCR_EXCP_MASK 0xff00
+
 /* CACR fields are implementation defined, but some bits are common.  */
 #define M68K_CACR_EUSP  0x10
 
@@ -228,8 +259,6 @@ typedef enum {
 void m68k_set_irq_level(M68kCPU *cpu, int level, uint8_t vector);
 void m68k_switch_sp(CPUM68KState *env);
 
-#define M68K_FPCR_PREC (1 << 6)
-
 void do_m68k_semihosting(CPUM68KState *env, int nr);
 
 /* There are 4 ColdFire core ISA revisions: A, A+, B and C.
@@ -306,8 +335,7 @@ static inline void cpu_get_tb_cpu_state(CPUM68KState *env, target_ulong *pc,
 {
     *pc = env->pc;
     *cs_base = 0;
-    *flags = (env->fpcr & M68K_FPCR_PREC)       /* Bit  6 */
-            | (env->sr & SR_S)                  /* Bit  13 */
+    *flags = (env->sr & SR_S)                   /* Bit  13 */
             | ((env->macsr >> 4) & 0xf);        /* Bits 0-3 */
 }
 
diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
index 9260e7b..9d39118 100644
--- a/target/m68k/fpu_helper.c
+++ b/target/m68k/fpu_helper.c
@@ -132,11 +132,75 @@ void HELPER(iround_FP0)(CPUM68KState *env)
     floatx80_to_FP0(env, res);
 }
 
+static void m68k_restore_precision_mode(CPUM68KState *env)
+{
+    switch (env->fpcr & FPCR_PREC_MASK) {
+    case FPCR_PREC_X: /* extended */
+        set_floatx80_rounding_precision(80, &env->fp_status);
+        break;
+    case FPCR_PREC_S: /* single */
+        set_floatx80_rounding_precision(32, &env->fp_status);
+        break;
+    case FPCR_PREC_D: /* double */
+        set_floatx80_rounding_precision(64, &env->fp_status);
+        break;
+    case FPCR_PREC_U: /* undefined */
+    default:
+        break;
+    }
+}
+
+static void cf_restore_precision_mode(CPUM68KState *env)
+{
+    if (env->fpcr & FPCR_PREC_S) { /* single */
+        set_floatx80_rounding_precision(32, &env->fp_status);
+    } else { /* double */
+        set_floatx80_rounding_precision(64, &env->fp_status);
+    }
+}
+
+static void restore_rounding_mode(CPUM68KState *env)
+{
+    switch (env->fpcr & FPCR_RND_MASK) {
+    case FPCR_RND_N: /* round to nearest */
+        set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
+        break;
+    case FPCR_RND_Z: /* round to zero */
+        set_float_rounding_mode(float_round_to_zero, &env->fp_status);
+        break;
+    case FPCR_RND_M: /* round toward minus infinity */
+        set_float_rounding_mode(float_round_down, &env->fp_status);
+        break;
+    case FPCR_RND_P: /* round toward positive infinity */
+        set_float_rounding_mode(float_round_up, &env->fp_status);
+        break;
+    }
+}
+
+void cpu_m68k_set_fpcr(CPUM68KState *env, uint32_t val)
+{
+    env->fpcr = val & 0xffff;
+
+    if (m68k_feature(env, M68K_FEATURE_CF_FPU)) {
+        cf_restore_precision_mode(env);
+    } else {
+        m68k_restore_precision_mode(env);
+    }
+    restore_rounding_mode(env);
+}
+
+void HELPER(set_fpcr)(CPUM68KState *env, uint32_t val)
+{
+    cpu_m68k_set_fpcr(env, val);
+}
+
 void HELPER(itrunc_FP0)(CPUM68KState *env)
 {
     floatx80 res;
 
+    set_float_rounding_mode(float_round_to_zero, &env->fp_status);
     res = floatx80_round_to_int(FP0_to_floatx80(env), &env->fp_status);
+    restore_rounding_mode(env);
 
     floatx80_to_FP0(env, res);
 }
@@ -208,29 +272,47 @@ void HELPER(div_FP0_FP1)(CPUM68KState *env)
     floatx80_to_FP0(env, res);
 }
 
+static int float_comp_to_cc(int float_compare)
+{
+    switch (float_compare) {
+    case float_relation_equal:
+        return FPSR_CC_Z;
+    case float_relation_less:
+        return FPSR_CC_N;
+    case float_relation_unordered:
+        return FPSR_CC_A;
+    case float_relation_greater:
+        return 0;
+    default:
+        g_assert_not_reached();
+    }
+}
+
 void HELPER(cmp_FP0_FP1)(CPUM68KState *env)
 {
     floatx80 fp0 = FP0_to_floatx80(env);
     floatx80 fp1 = FP1_to_floatx80(env);
-    floatx80 res;
+    int float_compare;
 
-    res = floatx80_sub(fp0, fp1, &env->fp_status);
-    if (floatx80_is_quiet_nan(res, &env->fp_status)) {
-        /* +/-inf compares equal against itself, but sub returns nan.  */
-        if (!floatx80_is_quiet_nan(fp0, &env->fp_status)
-            && !floatx80_is_quiet_nan(fp1, &env->fp_status)) {
-            res = floatx80_zero;
-            if (floatx80_lt_quiet(fp0, res, &env->fp_status)) {
-                res = floatx80_chs(res);
-            }
-        }
-    }
-
-    floatx80_to_FP0(env, res);
+    float_compare = floatx80_compare(fp1, fp0, &env->fp_status);
+    env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | float_comp_to_cc(float_compare);
 }
 
-uint32_t HELPER(compare_FP0)(CPUM68KState *env)
+void HELPER(tst_FP0)(CPUM68KState *env)
 {
-    floatx80 fp0 = FP0_to_floatx80(env);
-    return floatx80_compare_quiet(fp0, floatx80_zero, &env->fp_status);
+    uint32_t fpsr = 0;
+    floatx80 val = FP0_to_floatx80(env);
+
+    if (floatx80_is_neg(val)) {
+        fpsr |= FPSR_CC_N;
+    }
+
+    if (floatx80_is_any_nan(val)) {
+        fpsr |= FPSR_CC_A;
+    } else if (floatx80_is_infinity(val)) {
+        fpsr |= FPSR_CC_I;
+    } else if (floatx80_is_zero(val)) {
+        fpsr |= FPSR_CC_Z;
+    }
+    env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | fpsr;
 }
diff --git a/target/m68k/helper.c b/target/m68k/helper.c
index 8bfc881..cff93dc 100644
--- a/target/m68k/helper.c
+++ b/target/m68k/helper.c
@@ -80,8 +80,14 @@ static int cf_fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
         stfq_p(mem_buf, floatx80_to_float64(env->fregs[n].d, &s));
         return 8;
     }
-    if (n < 11) {
-        /* FP control registers (not implemented)  */
+    switch (n) {
+    case 8: /* fpcontrol */
+        stl_be_p(mem_buf, env->fpcr);
+        return 4;
+    case 9: /* fpstatus */
+        stl_be_p(mem_buf, env->fpsr);
+        return 4;
+    case 10: /* fpiar, not implemented */
         memset(mem_buf, 0, 4);
         return 4;
     }
@@ -95,8 +101,14 @@ static int cf_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
         env->fregs[n].d = float64_to_floatx80(ldfq_p(mem_buf), &s);
         return 8;
     }
-    if (n < 11) {
-        /* FP control registers (not implemented)  */
+    switch (n) {
+    case 8: /* fpcontrol */
+        env->fpcr = ldl_p(mem_buf);
+        return 4;
+    case 9: /* fpstatus */
+        env->fpsr = ldl_p(mem_buf);
+        return 4;
+    case 10: /* fpiar, not implemented */
         return 4;
     }
     return 0;
diff --git a/target/m68k/helper.h b/target/m68k/helper.h
index d52689b..03fb268 100644
--- a/target/m68k/helper.h
+++ b/target/m68k/helper.h
@@ -28,7 +28,8 @@ DEF_HELPER_1(sub_FP0_FP1, void, env)
 DEF_HELPER_1(mul_FP0_FP1, void, env)
 DEF_HELPER_1(div_FP0_FP1, void, env)
 DEF_HELPER_1(cmp_FP0_FP1, void, env)
-DEF_HELPER_1(compare_FP0, i32, env)
+DEF_HELPER_2(set_fpcr, void, env, i32)
+DEF_HELPER_1(tst_FP0, void, env)
 
 DEF_HELPER_3(mac_move, void, env, i32, i32)
 DEF_HELPER_3(macmulf, i64, env, i32, i32)
diff --git a/target/m68k/qregs.def b/target/m68k/qregs.def
index b6441d9..b355e0e 100644
--- a/target/m68k/qregs.def
+++ b/target/m68k/qregs.def
@@ -10,3 +10,4 @@ DEFO32(CC_V, cc_v)
 DEFO32(CC_Z, cc_z)
 DEFO32(MACSR, macsr)
 DEFO32(MAC_MASK, mac_mask)
+DEFO32(FPSR, fpsr)
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index 3a56b27..cce8a8f 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -52,6 +52,8 @@ static char cpu_reg_names[2 * 8 * 3 + 5 * 4];
 static TCGv cpu_dregs[8];
 static TCGv cpu_aregs[8];
 static TCGv_i64 cpu_macc[4];
+static TCGv QEMU_FPSR;
+static TCGv QEMU_FPCR;
 
 #define REG(insn, pos)  (((insn) >> (pos)) & 7)
 #define DREG(insn, pos) cpu_dregs[REG(insn, pos)]
@@ -117,6 +119,11 @@ void m68k_tcg_init(void)
         p += 5;
     }
 
+    QEMU_FPSR = tcg_global_mem_new(cpu_env, offsetof(CPUM68KState, fpsr),
+                                   "FPSR");
+    QEMU_FPCR = tcg_global_mem_new(cpu_env, offsetof(CPUM68KState, fpcr),
+                                   "FPCR");
+
     NULL_QREG = tcg_global_mem_new(cpu_env, -4, "NULL");
     store_dummy = tcg_global_mem_new(cpu_env, -8, "NULL");
 }
@@ -130,7 +137,6 @@ typedef struct DisasContext {
     CCOp cc_op; /* Current CC operation */
     int cc_op_synced;
     int user;
-    uint32_t fpcr;
     struct TranslationBlock *tb;
     int singlestep_enabled;
     TCGv_i64 mactmp;
@@ -4358,12 +4364,49 @@ DISAS_INSN(trap)
     gen_exception(s, s->pc - 2, EXCP_TRAP0 + (insn & 0xf));
 }
 
+static void gen_store_fcr(DisasContext *s, TCGv addr, int reg)
+{
+    int index = IS_USER(s);
+
+    switch (reg) {
+    case 0: /* FPSR */
+        tcg_gen_qemu_st32(QEMU_FPSR, addr, index);
+        break;
+    case 1: /* FPIAR */
+        break;
+    case 2: /* FPCR */
+        tcg_gen_qemu_st32(QEMU_FPCR, addr, index);
+        break;
+    }
+}
+
+static void gen_load_fcr(DisasContext *s, TCGv addr, int reg)
+{
+    int index = IS_USER(s);
+    TCGv val;
+
+    switch (reg) {
+    case 0: /* FPSR */
+        tcg_gen_qemu_ld32u(QEMU_FPSR, addr, index);
+        break;
+    case 1: /* FPIAR */
+        break;
+    case 2: /* FPCR */
+        val = tcg_temp_new();
+        tcg_gen_qemu_ld32u(val, addr, index);
+        gen_helper_set_fpcr(cpu_env, val);
+        tcg_temp_free(val);
+        break;
+    }
+}
+
 static void gen_op_fmove_fcr(CPUM68KState *env, DisasContext *s,
                              uint32_t insn, uint32_t ext)
 {
     int mask = (ext >> 10) & 7;
     int is_write = (ext >> 13) & 1;
-    TCGv tmp;
+    int i;
+    TCGv addr, tmp;
 
     tmp = gen_lea(env, s, insn, OS_LONG);
     if (IS_NULL_QREG(tmp)) {
@@ -4372,31 +4415,70 @@ static void gen_op_fmove_fcr(CPUM68KState *env, DisasContext *s,
         if (is_write) {
             switch (mask) {
             case 1: /* FPIAR */
+                break;
             case 2: /* FPSR */
-            default:
-                cpu_abort(NULL, "Unimplemented: fmove from control %d", mask);
+                DEST_EA(env, insn, OS_LONG, QEMU_FPSR, NULL);
                 break;
             case 4: /* FPCR */
-                val = tcg_const_i32(0);
-                DEST_EA(env, insn, OS_LONG, val, NULL);
-                tcg_temp_free(val);
+                DEST_EA(env, insn, OS_LONG, QEMU_FPCR, NULL);
                 break;
             }
             return;
         }
         switch (mask) {
         case 1: /* FPIAR */
+            break;
         case 2: /* FPSR */
-        default:
-            cpu_abort(NULL, "Unimplemented: fmove to control %d",
-                      mask);
+            SRC_EA(env, val, OS_LONG, 0, NULL);
+            tcg_gen_mov_i32(QEMU_FPSR, val);
             break;
         case 4: /* FPCR */
-            /* Not implemented.  Ignore writes.  */
+            SRC_EA(env, val, OS_LONG, 0, NULL);
+            gen_helper_set_fpcr(cpu_env, val);
             break;
         }
         return;
     }
+
+    addr = tcg_temp_new();
+    tcg_gen_mov_i32(addr, tmp);
+
+    /* mask:
+     *
+     * 0b100 Floating-Point Control Register
+     * 0b010 Floating-Point Status Register
+     * 0b001 Floating-Point Instruction Address Register
+     *
+     */
+
+    if (is_write && (insn & 070) == 040) {
+        for (i = 2; i >= 0; i--, mask >>= 1) {
+            if (mask & 1) {
+                gen_store_fcr(s, addr, i);
+                if (mask != 1) {
+                    tcg_gen_subi_i32(addr, addr, opsize_bytes(OS_LONG));
+                }
+            }
+       }
+       tcg_gen_mov_i32(AREG(insn, 0), addr);
+    } else {
+        for (i = 0; i < 3; i++, mask >>= 1) {
+            if (mask & 1) {
+                if (is_write) {
+                    gen_store_fcr(s, addr, i);
+                } else {
+                    gen_load_fcr(s, addr, i);
+                }
+                if (mask != 1 || (insn & 070) == 030) {
+                    tcg_gen_addi_i32(addr, addr, opsize_bytes(OS_LONG));
+                }
+            }
+        }
+        if ((insn & 070) == 030) {
+            tcg_gen_mov_i32(AREG(insn, 0), addr);
+        }
+    }
+    tcg_temp_free_i32(addr);
 }
 
 /* ??? FP exceptions are not implemented.  Most exceptions are deferred until
@@ -4406,8 +4488,6 @@ DISAS_INSN(fpu)
     uint16_t ext;
     int opmode;
     TCGv tmp32;
-    int round;
-    int set_dest;
     int opsize;
 
     ext = read_im16(env, s);
@@ -4424,6 +4504,7 @@ DISAS_INSN(fpu)
             gen_addr_fault(s);
             return;
         }
+        gen_helper_tst_FP0(cpu_env);
         return;
     case 4: /* fmove to control register.  */
     case 5: /* fmove from control register.  */
@@ -4471,18 +4552,14 @@ DISAS_INSN(fpu)
         opsize = OS_EXTENDED;
         gen_op_load_fpr_FP0(REG(ext, 10));
     }
-    round = 1;
-    set_dest = 1;
     switch (opmode) {
     case 0: case 0x40: case 0x44: /* fmove */
         break;
     case 1: /* fint */
         gen_helper_iround_FP0(cpu_env);
-        round = 0;
         break;
     case 3: /* fintrz */
         gen_helper_itrunc_FP0(cpu_env);
-        round = 0;
         break;
     case 4: case 0x41: case 0x45: /* fsqrt */
         gen_helper_sqrt_FP0(cpu_env);
@@ -4512,34 +4589,15 @@ DISAS_INSN(fpu)
     case 0x38: /* fcmp */
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_cmp_FP0_FP1(cpu_env);
-        set_dest = 0;
-        round = 0;
-        break;
+        return;
     case 0x3a: /* ftst */
-        set_dest = 0;
-        round = 0;
-        break;
+        gen_helper_tst_FP0(cpu_env);
+        return;
     default:
         goto undef;
     }
-    if (round) {
-        if (opmode & 0x40) {
-            if ((opmode & 0x4) != 0)
-                round = 0;
-        } else if ((s->fpcr & M68K_FPCR_PREC) == 0) {
-            round = 0;
-        }
-    }
-    if (round) {
-        gen_helper_redf32_FP0(cpu_env);
-        gen_helper_extf32_FP0(cpu_env);
-    } else {
-        gen_helper_redf64_FP0(cpu_env);
-        gen_helper_extf64_FP0(cpu_env);
-    }
-    if (set_dest) {
-        gen_op_store_fpr_FP0(REG(ext, 7));
-    }
+    gen_op_store_fpr_FP0(REG(ext, 7));
+    gen_helper_tst_FP0(cpu_env);
     return;
 undef:
     /* FIXME: Is this right for offset addressing modes?  */
@@ -4551,8 +4609,8 @@ DISAS_INSN(fbcc)
 {
     uint32_t offset;
     uint32_t addr;
-    TCGv flag;
     TCGLabel *l1;
+    TCGv tmp;
 
     addr = s->pc;
     offset = cpu_ldsw_code(env, s->pc);
@@ -4563,57 +4621,117 @@ DISAS_INSN(fbcc)
 
     l1 = gen_new_label();
     /* TODO: Raise BSUN exception.  */
-    flag = tcg_temp_new();
-    gen_helper_compare_FP0(flag, cpu_env);
     /* Jump to l1 if condition is true.  */
-    switch (insn & 0xf) {
+    switch (insn & 0x3f)  {
     case 0:  /* False */
+    case 16: /* Signaling False */
         break;
-    case 1: /* eq (=0) */
-        tcg_gen_brcond_i32(TCG_COND_EQ, flag, tcg_const_i32(0), l1);
-        break;
-    case 2: /* ogt (=1) */
-        tcg_gen_brcond_i32(TCG_COND_EQ, flag, tcg_const_i32(1), l1);
-        break;
-    case 3: /* oge (=0 or =1) */
-        tcg_gen_brcond_i32(TCG_COND_LEU, flag, tcg_const_i32(1), l1);
-        break;
-    case 4: /* olt (=-1) */
-        tcg_gen_brcond_i32(TCG_COND_LT, flag, tcg_const_i32(0), l1);
-        break;
-    case 5: /* ole (=-1 or =0) */
-        tcg_gen_brcond_i32(TCG_COND_LE, flag, tcg_const_i32(0), l1);
+    case 1:  /* EQual Z */
+    case 17: /* Signaling EQual Z */
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR, FPSR_CC_Z);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
         break;
-    case 6: /* ogl (=-1 or =1) */
-        tcg_gen_andi_i32(flag, flag, 1);
-        tcg_gen_brcond_i32(TCG_COND_NE, flag, tcg_const_i32(0), l1);
+    case 2:  /* Ordered Greater Than !(A || Z || N) */
+    case 18: /* Greater Than !(A || Z || N) */
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR,
+                         FPSR_CC_A | FPSR_CC_Z | FPSR_CC_N);
+        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
         break;
-    case 7: /* or (=2) */
-        tcg_gen_brcond_i32(TCG_COND_EQ, flag, tcg_const_i32(2), l1);
+    case 3:  /* Ordered Greater than or Equal Z || !(A || N) */
+    case 19: /* Greater than or Equal Z || !(A || N) */
+        assert(FPSR_CC_A == (FPSR_CC_N >> 3));
+        tmp = tcg_temp_new();
+        tcg_gen_shli_i32(tmp, QREG_FPSR, 3);
+        tcg_gen_or_i32(tmp, tmp, QREG_FPSR);
+        tcg_gen_xori_i32(tmp, tmp, FPSR_CC_N);
+        tcg_gen_andi_i32(tmp, tmp, FPSR_CC_N | FPSR_CC_Z);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        break;
+    case 4:  /* Ordered Less Than !(!N || A || Z); */
+    case 20: /* Less Than !(!N || A || Z); */
+        tmp = tcg_temp_new();
+        tcg_gen_xori_i32(tmp, QREG_FPSR, FPSR_CC_N);
+        tcg_gen_andi_i32(tmp, tmp, FPSR_CC_N | FPSR_CC_A | FPSR_CC_Z);
+        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
         break;
-    case 8: /* un (<2) */
-        tcg_gen_brcond_i32(TCG_COND_LT, flag, tcg_const_i32(2), l1);
+    case 5:  /* Ordered Less than or Equal Z || (N && !A) */
+    case 21: /* Less than or Equal Z || (N && !A) */
+        assert(FPSR_CC_A == (FPSR_CC_N >> 3));
+        tmp = tcg_temp_new();
+        tcg_gen_xori_i32(tmp, QREG_FPSR, FPSR_CC_A);
+        tcg_gen_shli_i32(tmp, tmp, 3);
+        tcg_gen_ori_i32(tmp, tmp, FPSR_CC_Z);
+        tcg_gen_and_i32(tmp, tmp, QREG_FPSR);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        break;
+    case 6:  /* Ordered Greater or Less than !(A || Z) */
+    case 22: /* Greater or Less than !(A || Z) */
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR, FPSR_CC_A | FPSR_CC_Z);
+        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
         break;
-    case 9: /* ueq (=0 or =2) */
-        tcg_gen_andi_i32(flag, flag, 1);
-        tcg_gen_brcond_i32(TCG_COND_EQ, flag, tcg_const_i32(0), l1);
+    case 7:  /* Ordered !A */
+    case 23: /* Greater, Less or Equal !A */
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR, FPSR_CC_A);
+        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
         break;
-    case 10: /* ugt (>0) */
-        tcg_gen_brcond_i32(TCG_COND_GT, flag, tcg_const_i32(0), l1);
+    case 8:  /* Unordered A */
+    case 24: /* Not Greater, Less or Equal A */
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR, FPSR_CC_A);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
         break;
-    case 11: /* uge (>=0) */
-        tcg_gen_brcond_i32(TCG_COND_GE, flag, tcg_const_i32(0), l1);
+    case 9:  /* Unordered or Equal A || Z */
+    case 25: /* Not Greater or Less then A || Z */
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR, FPSR_CC_A | FPSR_CC_Z);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
         break;
-    case 12: /* ult (=-1 or =2) */
-        tcg_gen_brcond_i32(TCG_COND_GEU, flag, tcg_const_i32(2), l1);
+    case 10: /* Unordered or Greater Than A || !(N || Z)) */
+    case 26: /* Not Less or Equal A || !(N || Z)) */
+        assert(FPSR_CC_Z == (FPSR_CC_N >> 1));
+        tmp = tcg_temp_new();
+        tcg_gen_shli_i32(tmp, QREG_FPSR, 1);
+        tcg_gen_or_i32(tmp, tmp, QREG_FPSR);
+        tcg_gen_xori_i32(tmp, tmp, FPSR_CC_N);
+        tcg_gen_andi_i32(tmp, tmp, FPSR_CC_N | FPSR_CC_A);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        break;
+    case 11: /* Unordered or Greater or Equal A || Z || !N */
+    case 27: /* Not Less Than A || Z || !N */
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR, FPSR_CC_A | FPSR_CC_Z | FPSR_CC_N);
+        tcg_gen_xori_i32(tmp, tmp, FPSR_CC_N);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
         break;
-    case 13: /* ule (!=1) */
-        tcg_gen_brcond_i32(TCG_COND_NE, flag, tcg_const_i32(1), l1);
+    case 12: /* Unordered or Less Than A || (N && !Z) */
+    case 28: /* Not Greater than or Equal A || (N && !Z) */
+        assert(FPSR_CC_Z == (FPSR_CC_N >> 1));
+        tmp = tcg_temp_new();
+        tcg_gen_xori_i32(tmp, QREG_FPSR, FPSR_CC_Z);
+        tcg_gen_shli_i32(tmp, tmp, 1);
+        tcg_gen_ori_i32(tmp, tmp, FPSR_CC_A);
+        tcg_gen_and_i32(tmp, tmp, QREG_FPSR);
+        tcg_gen_andi_i32(tmp, tmp, FPSR_CC_A | FPSR_CC_N);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        break;
+    case 13: /* Unordered or Less or Equal A || Z || N */
+    case 29: /* Not Greater Than A || Z || N */
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR, FPSR_CC_A | FPSR_CC_Z | FPSR_CC_N);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
         break;
-    case 14: /* ne (!=0) */
-        tcg_gen_brcond_i32(TCG_COND_NE, flag, tcg_const_i32(0), l1);
+    case 14: /* Not Equal !Z */
+    case 30: /* Signaling Not Equal !Z */
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR, FPSR_CC_Z);
+        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
         break;
-    case 15: /* t */
+    case 15: /* True */
+    case 31: /* Signaling True */
         tcg_gen_br(l1);
         break;
     }
@@ -5240,7 +5358,6 @@ void gen_intermediate_code(CPUM68KState *env, TranslationBlock *tb)
     dc->cc_op = CC_OP_DYNAMIC;
     dc->cc_op_synced = 1;
     dc->singlestep_enabled = cs->singlestep_enabled;
-    dc->fpcr = env->fpcr;
     dc->user = (env->sr & SR_S) == 0;
     dc->done_mac = 0;
     dc->writeback_mask = 0;
@@ -5359,6 +5476,38 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
     cpu_fprintf(f, "SR = %04x %c%c%c%c%c ", sr, (sr & CCF_X) ? 'X' : '-',
                 (sr & CCF_N) ? 'N' : '-', (sr & CCF_Z) ? 'Z' : '-',
                 (sr & CCF_V) ? 'V' : '-', (sr & CCF_C) ? 'C' : '-');
+    cpu_fprintf(f, "FPSR = %08x %c%c%c%c ", env->fpsr,
+                (env->fpsr & FPSR_CC_A) ? 'A' : '-',
+                (env->fpsr & FPSR_CC_I) ? 'I' : '-',
+                (env->fpsr & FPSR_CC_Z) ? 'Z' : '-',
+                (env->fpsr & FPSR_CC_N) ? 'N' : '-');
+    cpu_fprintf(f, "\n                                "
+                   "FPCR =     %04x ", env->fpcr);
+    switch (env->fpcr & FPCR_PREC_MASK) {
+    case FPCR_PREC_X:
+        cpu_fprintf(f, "X ");
+        break;
+    case FPCR_PREC_S:
+        cpu_fprintf(f, "S ");
+        break;
+    case FPCR_PREC_D:
+        cpu_fprintf(f, "D ");
+        break;
+    }
+    switch (env->fpcr & FPCR_RND_MASK) {
+    case FPCR_RND_N:
+        cpu_fprintf(f, "RN ");
+        break;
+    case FPCR_RND_Z:
+        cpu_fprintf(f, "RZ ");
+        break;
+    case FPCR_RND_M:
+        cpu_fprintf(f, "RM ");
+        break;
+    case FPCR_RND_P:
+        cpu_fprintf(f, "RP ");
+        break;
+    }
 }
 
 void restore_state_to_opc(CPUM68KState *env, TranslationBlock *tb,
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [Qemu-devel] [PATCH v3 07/16] target-m68k: manage FPU exceptions
  2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
                   ` (5 preceding siblings ...)
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 06/16] target-m68k: add FPCR and FPSR Laurent Vivier
@ 2017-02-07  0:59 ` Laurent Vivier
  2017-02-16  1:16   ` Richard Henderson
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 08/16] target-m68k: define 96bit FP registers for gdb on 680x0 Laurent Vivier
                   ` (9 subsequent siblings)
  16 siblings, 1 reply; 38+ messages in thread
From: Laurent Vivier @ 2017-02-07  0:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Richard Henderson, Laurent Vivier

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 target/m68k/cpu.h        |  28 +++++++++++++
 target/m68k/fpu_helper.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++-
 target/m68k/helper.h     |   1 +
 target/m68k/translate.c  |  27 ++++++++++++
 4 files changed, 162 insertions(+), 1 deletion(-)

diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h
index 6b3cb26..7985dc3 100644
--- a/target/m68k/cpu.h
+++ b/target/m68k/cpu.h
@@ -57,6 +57,15 @@
 #define EXCP_TRAP15         47   /* User trap #15.  */
 #define EXCP_UNSUPPORTED    61
 #define EXCP_ICE            13
+#define EXCP_FP_BSUN        48 /* Branch Set on Unordered */
+#define EXCP_FP_INEX        49 /* Inexact result */
+#define EXCP_FP_DZ          50 /* Divide by Zero */
+#define EXCP_FP_UNFL        51 /* Underflow */
+#define EXCP_FP_OPERR       52 /* Operand Error */
+#define EXCP_FP_OVFL        53 /* Overflow */
+#define EXCP_FP_SNAN        54 /* Signaling Not-A-Number */
+#define EXCP_FP_UNIMP       55 /* Unimplemented Data type */
+
 
 #define EXCP_RTE            0x100
 #define EXCP_HALT_INSN      0x101
@@ -222,6 +231,25 @@ typedef enum {
 #define FPSR_CC_Z     0x04000000 /* Zero */
 #define FPSR_CC_N     0x08000000 /* Negative */
 
+/* Exception Status */
+#define FPSR_ES_MASK  0x0000ff00
+#define FPSR_ES_BSUN  0x00008000 /* Branch Set on Unordered */
+#define FPSR_ES_SNAN  0x00004000 /* Signaling Not-A-Number */
+#define FPSR_ES_OPERR 0x00002000 /* Operand Error */
+#define FPSR_ES_OVFL  0x00001000 /* Overflow */
+#define FPSR_ES_UNFL  0x00000800 /* Underflow */
+#define FPSR_ES_DZ    0x00000400 /* Divide by Zero */
+#define FPSR_ES_INEX2 0x00000200 /* Inexact operation */
+#define FPSR_ES_INEX  0x00000100 /* Inexact decimal input */
+
+/* Accrued Exception */
+#define FPSR_AE_MASK  0x000000ff
+#define FPSR_AE_IOP   0x00000080 /* Invalid Operation */
+#define FPSR_AE_OVFL  0x00000040 /* Overflow */
+#define FPSR_AE_UNFL  0x00000020 /* Underflow */
+#define FPSR_AE_DZ    0x00000010 /* Divide by Zero */
+#define FPSR_AE_INEX  0x00000008 /* Inexact */
+
 /* Quotient */
 
 #define FPSR_QT_MASK  0x00ff0000
diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
index 9d39118..1e68c41 100644
--- a/target/m68k/fpu_helper.c
+++ b/target/m68k/fpu_helper.c
@@ -177,6 +177,70 @@ static void restore_rounding_mode(CPUM68KState *env)
     }
 }
 
+static void set_fpsr_exception(CPUM68KState *env)
+{
+    uint32_t fpsr = 0;
+    int flags;
+
+    flags = get_float_exception_flags(&env->fp_status);
+    if (flags == 0) {
+        return;
+    }
+    set_float_exception_flags(0, &env->fp_status);
+
+    if (flags & float_flag_invalid) {
+        fpsr |= FPSR_AE_IOP;
+    }
+    if (flags & float_flag_divbyzero) {
+        fpsr |= FPSR_AE_DZ;
+    }
+    if (flags & float_flag_overflow) {
+        fpsr |= FPSR_AE_OVFL;
+    }
+    if (flags & float_flag_underflow) {
+        fpsr |= FPSR_AE_UNFL;
+    }
+    if (flags & float_flag_inexact) {
+        fpsr |= FPSR_AE_INEX;
+    }
+
+    env->fpsr = (env->fpsr & ~FPSR_AE_MASK) | fpsr;
+}
+
+static void fpu_exception(CPUM68KState *env, uint32_t exception)
+{
+    CPUState *cs = CPU(m68k_env_get_cpu(env));
+
+    env->fpsr = (env->fpsr & ~FPSR_ES_MASK) | exception;
+    if (env->fpcr & exception) {
+        switch (exception) {
+        case FPSR_ES_BSUN:
+            cs->exception_index = EXCP_FP_BSUN;
+            break;
+        case FPSR_ES_SNAN:
+            cs->exception_index = EXCP_FP_SNAN;
+            break;
+        case FPSR_ES_OPERR:
+            cs->exception_index = EXCP_FP_OPERR;
+            break;
+        case FPSR_ES_OVFL:
+            cs->exception_index = EXCP_FP_OVFL;
+            break;
+        case FPSR_ES_UNFL:
+            cs->exception_index = EXCP_FP_UNFL;
+            break;
+        case FPSR_ES_DZ:
+            cs->exception_index = EXCP_FP_DZ;
+            break;
+        case FPSR_ES_INEX:
+        case FPSR_ES_INEX2:
+            cs->exception_index = EXCP_FP_INEX;
+            break;
+        }
+        cpu_loop_exit_restore(cs, GETPC());
+    }
+}
+
 void cpu_m68k_set_fpcr(CPUM68KState *env, uint32_t val)
 {
     env->fpcr = val & 0xffff;
@@ -292,10 +356,16 @@ void HELPER(cmp_FP0_FP1)(CPUM68KState *env)
 {
     floatx80 fp0 = FP0_to_floatx80(env);
     floatx80 fp1 = FP1_to_floatx80(env);
-    int float_compare;
+    int flags, float_compare;
 
     float_compare = floatx80_compare(fp1, fp0, &env->fp_status);
     env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | float_comp_to_cc(float_compare);
+
+    flags = get_float_exception_flags(&env->fp_status);
+    if (flags & float_flag_invalid) {
+        fpu_exception(env, FPSR_ES_OPERR);
+   }
+   set_fpsr_exception(env);
 }
 
 void HELPER(tst_FP0)(CPUM68KState *env)
@@ -315,4 +385,39 @@ void HELPER(tst_FP0)(CPUM68KState *env)
         fpsr |= FPSR_CC_Z;
     }
     env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | fpsr;
+
+    set_fpsr_exception(env);
+}
+
+void HELPER(update_fpstatus)(CPUM68KState *env)
+{
+    int flags = get_float_exception_flags(&env->fp_status);
+
+    if (env->fpsr & FPSR_AE_IOP) {
+        flags |= float_flag_invalid;
+    } else {
+        flags &= ~float_flag_invalid;
+    }
+    if (env->fpsr & FPSR_AE_DZ) {
+        flags |= float_flag_divbyzero;
+    } else {
+        flags &= ~float_flag_divbyzero;
+    }
+    if (env->fpsr & FPSR_AE_OVFL) {
+        flags |= float_flag_overflow;
+    } else {
+        flags &= ~float_flag_overflow;
+    }
+    if (env->fpsr & FPSR_AE_UNFL) {
+        flags |= float_flag_underflow;
+    } else {
+        flags &= ~float_flag_underflow;
+    }
+    if (env->fpsr & FPSR_AE_INEX) {
+        flags |= float_flag_inexact;
+    } else {
+        flags &= ~float_flag_inexact;
+    }
+
+    set_float_exception_flags(flags, &env->fp_status);
 }
diff --git a/target/m68k/helper.h b/target/m68k/helper.h
index 03fb268..072a6d0 100644
--- a/target/m68k/helper.h
+++ b/target/m68k/helper.h
@@ -30,6 +30,7 @@ DEF_HELPER_1(div_FP0_FP1, void, env)
 DEF_HELPER_1(cmp_FP0_FP1, void, env)
 DEF_HELPER_2(set_fpcr, void, env, i32)
 DEF_HELPER_1(tst_FP0, void, env)
+DEF_HELPER_1(update_fpstatus, void, env)
 
 DEF_HELPER_3(mac_move, void, env, i32, i32)
 DEF_HELPER_3(macmulf, i64, env, i32, i32)
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index cce8a8f..f9c64ff 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -4388,6 +4388,7 @@ static void gen_load_fcr(DisasContext *s, TCGv addr, int reg)
     switch (reg) {
     case 0: /* FPSR */
         tcg_gen_qemu_ld32u(QEMU_FPSR, addr, index);
+        gen_helper_update_fpstatus(cpu_env);
         break;
     case 1: /* FPIAR */
         break;
@@ -4431,6 +4432,7 @@ static void gen_op_fmove_fcr(CPUM68KState *env, DisasContext *s,
         case 2: /* FPSR */
             SRC_EA(env, val, OS_LONG, 0, NULL);
             tcg_gen_mov_i32(QEMU_FPSR, val);
+            gen_helper_update_fpstatus(cpu_env);
             break;
         case 4: /* FPCR */
             SRC_EA(env, val, OS_LONG, 0, NULL);
@@ -5481,6 +5483,21 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
                 (env->fpsr & FPSR_CC_I) ? 'I' : '-',
                 (env->fpsr & FPSR_CC_Z) ? 'Z' : '-',
                 (env->fpsr & FPSR_CC_N) ? 'N' : '-');
+    cpu_fprintf(f, "%c%c%c%c%c%c%c%c ",
+                (env->fpsr & FPSR_ES_BSUN) ? 'B' : '-',
+                (env->fpsr & FPSR_ES_SNAN) ? 'A' : '-',
+                (env->fpsr & FPSR_ES_OPERR) ? 'O' : '-',
+                (env->fpsr & FPSR_ES_OVFL) ? 'V' : '-',
+                (env->fpsr & FPSR_ES_UNFL) ? 'U' : '-',
+                (env->fpsr & FPSR_ES_DZ) ? 'Z' : '-',
+                (env->fpsr & FPSR_ES_INEX2) ? '2' : '-',
+                (env->fpcr & FPSR_ES_INEX) ? 'I' : '-');
+    cpu_fprintf(f, "%c%c%c%c%c",
+                (env->fpsr & FPSR_AE_IOP) ? 'O' : '-',
+                (env->fpsr & FPSR_AE_OVFL) ? 'V' : '-',
+                (env->fpsr & FPSR_AE_UNFL) ? 'U' : '-',
+                (env->fpsr & FPSR_AE_DZ) ? 'Z' : '-',
+                (env->fpcr & FPSR_ES_INEX) ? 'I' : '-');
     cpu_fprintf(f, "\n                                "
                    "FPCR =     %04x ", env->fpcr);
     switch (env->fpcr & FPCR_PREC_MASK) {
@@ -5508,6 +5525,16 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
         cpu_fprintf(f, "RP ");
         break;
     }
+    /* FPCR exception mask uses the same bitmask as FPSR */
+    cpu_fprintf(f, "%c%c%c%c%c%c%c%c\n",
+                (env->fpcr & FPSR_ES_BSUN) ? 'B' : '-',
+                (env->fpcr & FPSR_ES_SNAN) ? 'A' : '-',
+                (env->fpcr & FPSR_ES_OPERR) ? 'O' : '-',
+                (env->fpcr & FPSR_ES_OVFL) ? 'V' : '-',
+                (env->fpcr & FPSR_ES_UNFL) ? 'U' : '-',
+                (env->fpcr & FPSR_ES_DZ) ? 'Z' : '-',
+                (env->fpcr & FPSR_ES_INEX2) ? '2' : '-',
+                (env->fpcr & FPSR_ES_INEX) ? 'I' : '-');
 }
 
 void restore_state_to_opc(CPUM68KState *env, TranslationBlock *tb,
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [Qemu-devel] [PATCH v3 08/16] target-m68k: define 96bit FP registers for gdb on 680x0
  2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
                   ` (6 preceding siblings ...)
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 07/16] target-m68k: manage FPU exceptions Laurent Vivier
@ 2017-02-07  0:59 ` Laurent Vivier
  2017-02-16  1:17   ` Richard Henderson
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 09/16] target-m68k: add fmovem Laurent Vivier
                   ` (8 subsequent siblings)
  16 siblings, 1 reply; 38+ messages in thread
From: Laurent Vivier @ 2017-02-07  0:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Richard Henderson, Laurent Vivier

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 configure            |  2 +-
 gdb-xml/m68k-fp.xml  | 21 +++++++++++++++++++++
 target/m68k/helper.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 67 insertions(+), 1 deletion(-)
 create mode 100644 gdb-xml/m68k-fp.xml

diff --git a/configure b/configure
index 86fd833..4373ec5 100755
--- a/configure
+++ b/configure
@@ -5912,7 +5912,7 @@ case "$target_name" in
   ;;
   m68k)
     bflt="yes"
-    gdb_xml_files="cf-core.xml cf-fp.xml"
+    gdb_xml_files="cf-core.xml cf-fp.xml m68k-fp.xml"
   ;;
   microblaze|microblazeel)
     TARGET_ARCH=microblaze
diff --git a/gdb-xml/m68k-fp.xml b/gdb-xml/m68k-fp.xml
new file mode 100644
index 0000000..64290d1
--- /dev/null
+++ b/gdb-xml/m68k-fp.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2008 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.coldfire.fp">
+  <reg name="fp0" bitsize="96" type="float" group="float"/>
+  <reg name="fp1" bitsize="96" type="float" group="float"/>
+  <reg name="fp2" bitsize="96" type="float" group="float"/>
+  <reg name="fp3" bitsize="96" type="float" group="float"/>
+  <reg name="fp4" bitsize="96" type="float" group="float"/>
+  <reg name="fp5" bitsize="96" type="float" group="float"/>
+  <reg name="fp6" bitsize="96" type="float" group="float"/>
+  <reg name="fp7" bitsize="96" type="float" group="float"/>
+
+  <reg name="fpcontrol" bitsize="32" group="float"/>
+  <reg name="fpstatus" bitsize="32" group="float"/>,
+  <reg name="fpiaddr" bitsize="32" type="code_ptr" group="float"/>
+</feature>
diff --git a/target/m68k/helper.c b/target/m68k/helper.c
index cff93dc..d93fad9 100644
--- a/target/m68k/helper.c
+++ b/target/m68k/helper.c
@@ -114,6 +114,48 @@ static int cf_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
     return 0;
 }
 
+static int m68k_fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
+{
+    if (n < 8) {
+        stw_be_p(mem_buf, env->fregs[n].l.upper);
+        memset(mem_buf + 2, 0, 2);
+        stq_be_p(mem_buf + 4, env->fregs[n].l.lower);
+        return 12;
+    }
+    switch (n) {
+    case 8: /* fpcontrol */
+        stl_be_p(mem_buf, env->fpcr);
+        return 4;
+    case 9: /* fpstatus */
+        stl_be_p(mem_buf, env->fpsr);
+        return 4;
+    case 10: /* fpiar, not implemented */
+        memset(mem_buf, 0, 4);
+        return 4;
+    }
+    return 0;
+}
+
+static int m68k_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
+{
+    if (n < 8) {
+        env->fregs[n].l.upper = lduw_be_p(mem_buf);
+        env->fregs[n].l.lower = ldq_be_p(mem_buf + 4);
+        return 12;
+    }
+    switch (n) {
+    case 8: /* fpcontrol */
+        env->fpcr = ldl_p(mem_buf);
+        return 4;
+    case 9: /* fpstatus */
+        env->fpsr = ldl_p(mem_buf);
+        return 4;
+    case 10: /* fpiar, not implemented */
+        return 4;
+    }
+    return 0;
+}
+
 M68kCPU *cpu_m68k_init(const char *cpu_model)
 {
     M68kCPU *cpu;
@@ -142,6 +184,9 @@ void m68k_cpu_init_gdb(M68kCPU *cpu)
     if (m68k_feature(env, M68K_FEATURE_CF_FPU)) {
         gdb_register_coprocessor(cs, cf_fpu_gdb_get_reg, cf_fpu_gdb_set_reg,
                                  11, "cf-fp.xml", 18);
+    } else if (m68k_feature(env, M68K_FEATURE_FPU)) {
+        gdb_register_coprocessor(cs, m68k_fpu_gdb_get_reg,
+                                 m68k_fpu_gdb_set_reg, 11, "m68k-fp.xml", 18);
     }
     /* TODO: Add [E]MAC registers.  */
 }
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [Qemu-devel] [PATCH v3 09/16] target-m68k: add fmovem
  2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
                   ` (7 preceding siblings ...)
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 08/16] target-m68k: define 96bit FP registers for gdb on 680x0 Laurent Vivier
@ 2017-02-07  0:59 ` Laurent Vivier
  2017-02-16  1:22   ` Richard Henderson
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 10/16] target-m68k: add fscc Laurent Vivier
                   ` (7 subsequent siblings)
  16 siblings, 1 reply; 38+ messages in thread
From: Laurent Vivier @ 2017-02-07  0:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Richard Henderson, Laurent Vivier

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 target/m68k/fpu_helper.c |  6 +++
 target/m68k/helper.h     |  1 +
 target/m68k/translate.c  | 99 +++++++++++++++++++++++++++++++++++-------------
 3 files changed, 80 insertions(+), 26 deletions(-)

diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
index 1e68c41..aadfc82 100644
--- a/target/m68k/fpu_helper.c
+++ b/target/m68k/fpu_helper.c
@@ -421,3 +421,9 @@ void HELPER(update_fpstatus)(CPUM68KState *env)
 
     set_float_exception_flags(flags, &env->fp_status);
 }
+
+void HELPER(fmovem)(CPUM68KState *env, uint32_t opsize,
+                    uint32_t mode, uint32_t mask)
+{
+    fprintf(stderr, "MISSING HELPER fmovem\n");
+}
diff --git a/target/m68k/helper.h b/target/m68k/helper.h
index 072a6d0..58bc273 100644
--- a/target/m68k/helper.h
+++ b/target/m68k/helper.h
@@ -31,6 +31,7 @@ DEF_HELPER_1(cmp_FP0_FP1, void, env)
 DEF_HELPER_2(set_fpcr, void, env, i32)
 DEF_HELPER_1(tst_FP0, void, env)
 DEF_HELPER_1(update_fpstatus, void, env)
+DEF_HELPER_4(fmovem, void, env, i32, i32, i32)
 
 DEF_HELPER_3(mac_move, void, env, i32, i32)
 DEF_HELPER_3(macmulf, i64, env, i32, i32)
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index f9c64ff..ac60f1a 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -4483,13 +4483,79 @@ static void gen_op_fmove_fcr(CPUM68KState *env, DisasContext *s,
     tcg_temp_free_i32(addr);
 }
 
+static void gen_op_fmovem(CPUM68KState *env, DisasContext *s,
+                          uint32_t insn, uint32_t ext)
+{
+    int opsize;
+    uint16_t mask;
+    int i;
+    uint32_t mode;
+    int32_t incr;
+    TCGv addr, tmp;
+    int is_load;
+
+    if (m68k_feature(s->env, M68K_FEATURE_FPU)) {
+        opsize = OS_EXTENDED;
+    } else {
+        opsize = OS_DOUBLE;  /* FIXME */
+    }
+
+    mode = (ext >> 11) & 0x3;
+    if ((mode & 0x1) == 1) {
+        gen_helper_fmovem(cpu_env, tcg_const_i32(opsize),
+                          tcg_const_i32(mode), DREG(ext, 0));
+        return;
+    }
+
+    tmp = gen_lea(env, s, insn, opsize);
+    if (IS_NULL_QREG(tmp)) {
+        gen_addr_fault(s);
+        return;
+    }
+
+    addr = tcg_temp_new();
+    tcg_gen_mov_i32(addr, tmp);
+    is_load = ((ext & 0x2000) == 0);
+    incr = opsize_bytes(opsize);
+    mask = ext & 0x00FF;
+
+    if (!is_load && (mode & 2) == 0) {
+        for (i = 7; i >= 0; i--, mask <<= 1) {
+            if (mask & 0x80) {
+                gen_op_load_fpr_FP0(i);
+                gen_store_FP0(s, opsize, addr);
+                if ((mask & 0xff) != 0x80) {
+                    tcg_gen_subi_i32(addr, addr, incr);
+                }
+            }
+        }
+        tcg_gen_mov_i32(AREG(insn, 0), addr);
+    } else {
+        for (i = 0; i < 8; i++, mask <<= 1) {
+            if (mask & 0x80) {
+                if (is_load) {
+                    gen_load_FP0(s, opsize, addr);
+                    gen_op_store_fpr_FP0(i);
+                } else {
+                    gen_op_load_fpr_FP0(i);
+                    gen_store_FP0(s, opsize, addr);
+                }
+                tcg_gen_addi_i32(addr, addr, incr);
+            }
+        }
+        if ((insn & 070) == 030) {
+            tcg_gen_mov_i32(AREG(insn, 0), addr);
+        }
+    }
+    tcg_temp_free_i32(addr);
+}
+
 /* ??? FP exceptions are not implemented.  Most exceptions are deferred until
    immediately before the next FP instruction is executed.  */
 DISAS_INSN(fpu)
 {
     uint16_t ext;
     int opmode;
-    TCGv tmp32;
     int opsize;
 
     ext = read_im16(env, s);
@@ -4514,32 +4580,13 @@ DISAS_INSN(fpu)
         return;
     case 6: /* fmovem */
     case 7:
-        {
-            TCGv addr;
-            uint16_t mask;
-            int i;
-            if ((ext & 0x1f00) != 0x1000 || (ext & 0xff) == 0)
-                goto undef;
-            tmp32 = gen_lea(env, s, insn, OS_LONG);
-            if (IS_NULL_QREG(tmp32)) {
-                gen_addr_fault(s);
-                return;
-            }
-            addr = tcg_temp_new_i32();
-            tcg_gen_mov_i32(addr, tmp32);
-            mask = 0x80;
-            for (i = 0; i < 8; i++) {
-                if (ext & mask) {
-                    gen_op_load_fpr_FP0(REG(i, 0));
-                    gen_ldst_FP0(s, OS_DOUBLE, addr, (ext & (1 << 13)) ?
-                                                     EA_STORE : EA_LOADS);
-                    if (ext & (mask - 1))
-                        tcg_gen_addi_i32(addr, addr, 8);
-                }
-                mask >>= 1;
-            }
-            tcg_temp_free_i32(addr);
+        if ((ext & 0xf00) != 0 || (ext & 0xff) == 0) {
+            goto undef;
+        }
+        if ((ext & 0x1000) == 0 && !m68k_feature(s->env, M68K_FEATURE_FPU)) {
+            goto undef;
         }
+        gen_op_fmovem(env, s, insn, ext);
         return;
     }
     if (ext & (1 << 14)) {
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [Qemu-devel] [PATCH v3 10/16] target-m68k: add fscc.
  2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
                   ` (8 preceding siblings ...)
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 09/16] target-m68k: add fmovem Laurent Vivier
@ 2017-02-07  0:59 ` Laurent Vivier
  2017-02-16  1:27   ` Richard Henderson
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 11/16] target-m68k: add fmovecr Laurent Vivier
                   ` (6 subsequent siblings)
  16 siblings, 1 reply; 38+ messages in thread
From: Laurent Vivier @ 2017-02-07  0:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Richard Henderson, Laurent Vivier

use DisasCompare with FPU conditions in fscc and fbcc.

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 target/m68k/translate.c | 228 ++++++++++++++++++++++++++++++++----------------
 1 file changed, 153 insertions(+), 75 deletions(-)

diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index ac60f1a..3fdb672 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -4654,139 +4654,215 @@ undef:
     disas_undef_fpu(env, s, insn);
 }
 
-DISAS_INSN(fbcc)
+static void gen_fcc_cond(DisasCompare *c, DisasContext *s, int cond)
 {
-    uint32_t offset;
-    uint32_t addr;
-    TCGLabel *l1;
-    TCGv tmp;
-
-    addr = s->pc;
-    offset = cpu_ldsw_code(env, s->pc);
-    s->pc += 2;
-    if (insn & (1 << 6)) {
-        offset = (offset << 16) | read_im16(env, s);
-    }
-
-    l1 = gen_new_label();
+    c->g1 = 0;
+    c->v2 = tcg_const_i32(0);
+    c->g2 = 1;
     /* TODO: Raise BSUN exception.  */
-    /* Jump to l1 if condition is true.  */
-    switch (insn & 0x3f)  {
+    switch (cond) {
     case 0:  /* False */
     case 16: /* Signaling False */
+        c->v1 = c->v2;
+        c->tcond = TCG_COND_NEVER;
         break;
     case 1:  /* EQual Z */
     case 17: /* Signaling EQual Z */
-        tmp = tcg_temp_new();
-        tcg_gen_andi_i32(tmp, QREG_FPSR, FPSR_CC_Z);
-        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        c->v1 = tcg_temp_new();
+        c->g1 = 1;
+        tcg_gen_andi_i32(c->v1, QREG_FPSR, FPSR_CC_Z);
+        c->tcond = TCG_COND_NE;
         break;
     case 2:  /* Ordered Greater Than !(A || Z || N) */
     case 18: /* Greater Than !(A || Z || N) */
-        tmp = tcg_temp_new();
-        tcg_gen_andi_i32(tmp, QREG_FPSR,
+        c->v1 = tcg_temp_new();
+        c->g1 = 1;
+        tcg_gen_andi_i32(c->v1, QREG_FPSR,
                          FPSR_CC_A | FPSR_CC_Z | FPSR_CC_N);
-        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
+        c->tcond = TCG_COND_EQ;
         break;
     case 3:  /* Ordered Greater than or Equal Z || !(A || N) */
     case 19: /* Greater than or Equal Z || !(A || N) */
         assert(FPSR_CC_A == (FPSR_CC_N >> 3));
-        tmp = tcg_temp_new();
-        tcg_gen_shli_i32(tmp, QREG_FPSR, 3);
-        tcg_gen_or_i32(tmp, tmp, QREG_FPSR);
-        tcg_gen_xori_i32(tmp, tmp, FPSR_CC_N);
-        tcg_gen_andi_i32(tmp, tmp, FPSR_CC_N | FPSR_CC_Z);
-        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        c->v1 = tcg_temp_new();
+        c->g1 = 1;
+        tcg_gen_shli_i32(c->v1, QREG_FPSR, 3);
+        tcg_gen_or_i32(c->v1, c->v1, QREG_FPSR);
+        tcg_gen_xori_i32(c->v1, c->v1, FPSR_CC_N);
+        tcg_gen_andi_i32(c->v1, c->v1, FPSR_CC_N | FPSR_CC_Z);
+        c->tcond = TCG_COND_NE;
         break;
     case 4:  /* Ordered Less Than !(!N || A || Z); */
     case 20: /* Less Than !(!N || A || Z); */
-        tmp = tcg_temp_new();
-        tcg_gen_xori_i32(tmp, QREG_FPSR, FPSR_CC_N);
-        tcg_gen_andi_i32(tmp, tmp, FPSR_CC_N | FPSR_CC_A | FPSR_CC_Z);
-        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
+        c->v1 = tcg_temp_new();
+        c->g1 = 1;
+        tcg_gen_xori_i32(c->v1, QREG_FPSR, FPSR_CC_N);
+        tcg_gen_andi_i32(c->v1, c->v1, FPSR_CC_N | FPSR_CC_A | FPSR_CC_Z);
+        c->tcond = TCG_COND_EQ;
         break;
     case 5:  /* Ordered Less than or Equal Z || (N && !A) */
     case 21: /* Less than or Equal Z || (N && !A) */
         assert(FPSR_CC_A == (FPSR_CC_N >> 3));
-        tmp = tcg_temp_new();
-        tcg_gen_xori_i32(tmp, QREG_FPSR, FPSR_CC_A);
-        tcg_gen_shli_i32(tmp, tmp, 3);
-        tcg_gen_ori_i32(tmp, tmp, FPSR_CC_Z);
-        tcg_gen_and_i32(tmp, tmp, QREG_FPSR);
-        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        c->v1 = tcg_temp_new();
+        c->g1 = 1;
+        tcg_gen_xori_i32(c->v1, QREG_FPSR, FPSR_CC_A);
+        tcg_gen_shli_i32(c->v1, c->v1, 3);
+        tcg_gen_ori_i32(c->v1, c->v1, FPSR_CC_Z);
+        tcg_gen_and_i32(c->v1, c->v1, QREG_FPSR);
+        c->tcond = TCG_COND_NE;
         break;
     case 6:  /* Ordered Greater or Less than !(A || Z) */
     case 22: /* Greater or Less than !(A || Z) */
-        tmp = tcg_temp_new();
-        tcg_gen_andi_i32(tmp, QREG_FPSR, FPSR_CC_A | FPSR_CC_Z);
-        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
+        c->v1 = tcg_temp_new();
+        c->g1 = 1;
+        tcg_gen_andi_i32(c->v1, QREG_FPSR, FPSR_CC_A | FPSR_CC_Z);
+        c->tcond = TCG_COND_EQ;
         break;
     case 7:  /* Ordered !A */
     case 23: /* Greater, Less or Equal !A */
-        tmp = tcg_temp_new();
-        tcg_gen_andi_i32(tmp, QREG_FPSR, FPSR_CC_A);
-        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
+        c->v1 = tcg_temp_new();
+        c->g1 = 1;
+        tcg_gen_andi_i32(c->v1, QREG_FPSR, FPSR_CC_A);
+        c->tcond = TCG_COND_EQ;
         break;
     case 8:  /* Unordered A */
     case 24: /* Not Greater, Less or Equal A */
-        tmp = tcg_temp_new();
-        tcg_gen_andi_i32(tmp, QREG_FPSR, FPSR_CC_A);
-        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        c->v1 = tcg_temp_new();
+        c->g1 = 1;
+        tcg_gen_andi_i32(c->v1, QREG_FPSR, FPSR_CC_A);
+        c->tcond = TCG_COND_NE;
         break;
     case 9:  /* Unordered or Equal A || Z */
     case 25: /* Not Greater or Less then A || Z */
-        tmp = tcg_temp_new();
-        tcg_gen_andi_i32(tmp, QREG_FPSR, FPSR_CC_A | FPSR_CC_Z);
-        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        c->v1 = tcg_temp_new();
+        c->g1 = 1;
+        tcg_gen_andi_i32(c->v1, QREG_FPSR, FPSR_CC_A | FPSR_CC_Z);
+        c->tcond = TCG_COND_NE;
         break;
     case 10: /* Unordered or Greater Than A || !(N || Z)) */
     case 26: /* Not Less or Equal A || !(N || Z)) */
         assert(FPSR_CC_Z == (FPSR_CC_N >> 1));
-        tmp = tcg_temp_new();
-        tcg_gen_shli_i32(tmp, QREG_FPSR, 1);
-        tcg_gen_or_i32(tmp, tmp, QREG_FPSR);
-        tcg_gen_xori_i32(tmp, tmp, FPSR_CC_N);
-        tcg_gen_andi_i32(tmp, tmp, FPSR_CC_N | FPSR_CC_A);
-        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        c->v1 = tcg_temp_new();
+        c->g1 = 1;
+        tcg_gen_shli_i32(c->v1, QREG_FPSR, 1);
+        tcg_gen_or_i32(c->v1, c->v1, QREG_FPSR);
+        tcg_gen_xori_i32(c->v1, c->v1, FPSR_CC_N);
+        tcg_gen_andi_i32(c->v1, c->v1, FPSR_CC_N | FPSR_CC_A);
+        c->tcond = TCG_COND_NE;
         break;
     case 11: /* Unordered or Greater or Equal A || Z || !N */
     case 27: /* Not Less Than A || Z || !N */
-        tmp = tcg_temp_new();
-        tcg_gen_andi_i32(tmp, QREG_FPSR, FPSR_CC_A | FPSR_CC_Z | FPSR_CC_N);
-        tcg_gen_xori_i32(tmp, tmp, FPSR_CC_N);
-        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        c->v1 = tcg_temp_new();
+        c->g1 = 1;
+        tcg_gen_andi_i32(c->v1, QREG_FPSR, FPSR_CC_A | FPSR_CC_Z | FPSR_CC_N);
+        tcg_gen_xori_i32(c->v1, c->v1, FPSR_CC_N);
+        c->tcond = TCG_COND_NE;
         break;
     case 12: /* Unordered or Less Than A || (N && !Z) */
     case 28: /* Not Greater than or Equal A || (N && !Z) */
         assert(FPSR_CC_Z == (FPSR_CC_N >> 1));
-        tmp = tcg_temp_new();
-        tcg_gen_xori_i32(tmp, QREG_FPSR, FPSR_CC_Z);
-        tcg_gen_shli_i32(tmp, tmp, 1);
-        tcg_gen_ori_i32(tmp, tmp, FPSR_CC_A);
-        tcg_gen_and_i32(tmp, tmp, QREG_FPSR);
-        tcg_gen_andi_i32(tmp, tmp, FPSR_CC_A | FPSR_CC_N);
-        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        c->v1 = tcg_temp_new();
+        c->g1 = 1;
+        tcg_gen_xori_i32(c->v1, QREG_FPSR, FPSR_CC_Z);
+        tcg_gen_shli_i32(c->v1, c->v1, 1);
+        tcg_gen_ori_i32(c->v1, c->v1, FPSR_CC_A);
+        tcg_gen_and_i32(c->v1, c->v1, QREG_FPSR);
+        tcg_gen_andi_i32(c->v1, c->v1, FPSR_CC_A | FPSR_CC_N);
+        c->tcond = TCG_COND_NE;
         break;
     case 13: /* Unordered or Less or Equal A || Z || N */
     case 29: /* Not Greater Than A || Z || N */
-        tmp = tcg_temp_new();
-        tcg_gen_andi_i32(tmp, QREG_FPSR, FPSR_CC_A | FPSR_CC_Z | FPSR_CC_N);
-        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        c->v1 = tcg_temp_new();
+        c->g1 = 1;
+        tcg_gen_andi_i32(c->v1, QREG_FPSR, FPSR_CC_A | FPSR_CC_Z | FPSR_CC_N);
+        c->tcond = TCG_COND_NE;
         break;
     case 14: /* Not Equal !Z */
     case 30: /* Signaling Not Equal !Z */
-        tmp = tcg_temp_new();
-        tcg_gen_andi_i32(tmp, QREG_FPSR, FPSR_CC_Z);
-        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
+        c->v1 = tcg_temp_new();
+        c->g1 = 1;
+        tcg_gen_andi_i32(c->v1, QREG_FPSR, FPSR_CC_Z);
+        c->tcond = TCG_COND_EQ;
         break;
     case 15: /* True */
     case 31: /* Signaling True */
-        tcg_gen_br(l1);
+        c->v1 = c->v2;
+        c->tcond = TCG_COND_ALWAYS;
         break;
     }
+}
+
+static void gen_fjmpcc(DisasContext *s, int cond, TCGLabel *l1)
+{
+    DisasCompare c;
+
+    gen_fcc_cond(&c, s, cond);
+    tcg_gen_brcond_i32(c.tcond, c.v1, c.v2, l1);
+    free_cond(&c);
+}
+
+DISAS_INSN(fbcc)
+{
+    uint32_t offset;
+    uint32_t base;
+    TCGLabel *l1;
+
+    base = s->pc;
+    offset = (int16_t)read_im16(env, s);
+    if (insn & (1 << 6)) {
+        offset = (offset << 16) | read_im16(env, s);
+    }
+
+    l1 = gen_new_label();
+    update_cc_op(s);
+    gen_fjmpcc(s, insn & 0x3f, l1);
     gen_jmp_tb(s, 0, s->pc);
     gen_set_label(l1);
-    gen_jmp_tb(s, 1, addr + offset);
+    gen_jmp_tb(s, 1, base + offset);
+}
+
+DISAS_INSN(fscc_mem)
+{
+    TCGLabel *l1, *l2;
+    TCGv taddr;
+    TCGv addr;
+    uint16_t ext;
+
+    ext = read_im16(env, s);
+
+    taddr = gen_lea(env, s, insn, OS_BYTE);
+    if (IS_NULL_QREG(taddr)) {
+        gen_addr_fault(s);
+        return;
+    }
+    addr = tcg_temp_local_new();
+    tcg_gen_mov_i32(addr, taddr);
+    l1 = gen_new_label();
+    l2 = gen_new_label();
+    gen_fjmpcc(s, ext & 0x3f, l1);
+    gen_store(s, OS_BYTE, addr, tcg_const_i32(0x00));
+    tcg_gen_br(l2);
+    gen_set_label(l1);
+    gen_store(s, OS_BYTE, addr, tcg_const_i32(0xff));
+    gen_set_label(l2);
+    tcg_temp_free(addr);
+}
+
+DISAS_INSN(fscc_reg)
+{
+    TCGLabel *l1;
+    TCGv reg;
+    uint16_t ext;
+
+    ext = read_im16(env, s);
+
+    reg = DREG(insn, 0);
+
+    l1 = gen_new_label();
+    tcg_gen_ori_i32(reg, reg, 0x000000ff);
+    gen_fjmpcc(s, ext & 0x3f, l1);
+    tcg_gen_andi_i32(reg, reg, 0xffffff00);
+    gen_set_label(l1);
 }
 
 DISAS_INSN(frestore)
@@ -5366,6 +5442,8 @@ void register_m68k_insns (CPUM68KState *env)
     INSN(frestore,  f340, ffc0, CF_FPU);
     INSN(fsave,     f300, ffc0, CF_FPU);
     INSN(fpu,       f200, ffc0, FPU);
+    INSN(fscc_mem,  f240, ffc0, FPU);
+    INSN(fscc_reg,  f240, fff8, FPU);
     INSN(fbcc,      f280, ff80, FPU);
     INSN(frestore,  f340, ffc0, FPU);
     INSN(fsave,     f300, ffc0, FPU);
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [Qemu-devel] [PATCH v3 11/16] target-m68k: add fmovecr
  2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
                   ` (9 preceding siblings ...)
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 10/16] target-m68k: add fscc Laurent Vivier
@ 2017-02-07  0:59 ` Laurent Vivier
  2017-02-16  1:28   ` Richard Henderson
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 12/16] target-m68k: add fscale, fgetman, fgetexp and fmod Laurent Vivier
                   ` (5 subsequent siblings)
  16 siblings, 1 reply; 38+ messages in thread
From: Laurent Vivier @ 2017-02-07  0:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Richard Henderson, Laurent Vivier

fmovecr moves a floating point constant from the
FPU ROM to a floating point register.

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 target/m68k/fpu_helper.c | 31 +++++++++++++++++++++++++++++++
 target/m68k/helper.h     |  1 +
 target/m68k/translate.c  | 12 +++++++++++-
 3 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
index aadfc82..d8145e0 100644
--- a/target/m68k/fpu_helper.c
+++ b/target/m68k/fpu_helper.c
@@ -24,6 +24,31 @@
 #include "exec/helper-proto.h"
 #include "exec/exec-all.h"
 
+static const floatx80 fpu_rom[128] = {
+    [0x00] = floatx80_pi,                                   /* Pi */
+    [0x0b] = make_floatx80(0x3ffd, 0x9a209a84fbcff798ULL),  /* Log10(2) */
+    [0x0c] = make_floatx80(0x4000, 0xadf85458a2bb4a9aULL),  /* e        */
+    [0x0d] = make_floatx80(0x3fff, 0xb8aa3b295c17f0bcULL),  /* Log2(e)  */
+    [0x0e] = make_floatx80(0x3ffd, 0xde5bd8a937287195ULL),  /* Log10(e) */
+    [0x0f] = floatx80_zero,                                 /* Zero     */
+    [0x30] = floatx80_ln2,                                  /* ln(2)    */
+    [0x31] = make_floatx80(0x4000, 0x935d8dddaaa8ac17ULL),  /* ln(10)   */
+    [0x32] = floatx80_one,                                  /* 10^0     */
+    [0x33] = make_floatx80(0x4002, 0xa000000000000000ULL),  /* 10^1     */
+    [0x34] = make_floatx80(0x4005, 0xc800000000000000ULL),  /* 10^2     */
+    [0x35] = make_floatx80(0x400c, 0x9c40000000000000ULL),  /* 10^4     */
+    [0x36] = make_floatx80(0x4019, 0xbebc200000000000ULL),  /* 10^8     */
+    [0x37] = make_floatx80(0x4034, 0x8e1bc9bf04000000ULL),  /* 10^16    */
+    [0x38] = make_floatx80(0x4069, 0x9dc5ada82b70b59eULL),  /* 10^32    */
+    [0x39] = make_floatx80(0x40d3, 0xc2781f49ffcfa6d5ULL),  /* 10^64    */
+    [0x3a] = make_floatx80(0x41a8, 0x93ba47c980e98ce0ULL),  /* 10^128   */
+    [0x3b] = make_floatx80(0x4351, 0xaa7eebfb9df9de8eULL),  /* 10^256   */
+    [0x3c] = make_floatx80(0x46a3, 0xe319a0aea60e91c7ULL),  /* 10^512   */
+    [0x3d] = make_floatx80(0x4d48, 0xc976758681750c17ULL),  /* 10^1024  */
+    [0x3e] = make_floatx80(0x5a92, 0x9e8b3b5dc53d5de5ULL),  /* 10^2048  */
+    [0x3f] = make_floatx80(0x7525, 0xc46052028a20979bULL),  /* 10^4096  */
+};
+
 static floatx80 FP0_to_floatx80(CPUM68KState *env)
 {
     return (floatx80){ .low = env->fp0l, .high = env->fp0h };
@@ -427,3 +452,9 @@ void HELPER(fmovem)(CPUM68KState *env, uint32_t opsize,
 {
     fprintf(stderr, "MISSING HELPER fmovem\n");
 }
+
+void HELPER(const_FP0)(CPUM68KState *env, uint32_t offset)
+{
+    env->fp0l = fpu_rom[offset].low;
+    env->fp0h = fpu_rom[offset].high;
+}
diff --git a/target/m68k/helper.h b/target/m68k/helper.h
index 58bc273..4927324 100644
--- a/target/m68k/helper.h
+++ b/target/m68k/helper.h
@@ -32,6 +32,7 @@ DEF_HELPER_2(set_fpcr, void, env, i32)
 DEF_HELPER_1(tst_FP0, void, env)
 DEF_HELPER_1(update_fpstatus, void, env)
 DEF_HELPER_4(fmovem, void, env, i32, i32, i32)
+DEF_HELPER_2(const_FP0, void, env, i32)
 
 DEF_HELPER_3(mac_move, void, env, i32, i32)
 DEF_HELPER_3(macmulf, i64, env, i32, i32)
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index 3fdb672..23eac63 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -4555,16 +4555,26 @@ static void gen_op_fmovem(CPUM68KState *env, DisasContext *s,
 DISAS_INSN(fpu)
 {
     uint16_t ext;
+    uint8_t rom_offset;
     int opmode;
     int opsize;
 
     ext = read_im16(env, s);
     opmode = ext & 0x7f;
     switch ((ext >> 13) & 7) {
-    case 0: case 2:
+    case 0:
         break;
     case 1:
         goto undef;
+    case 2:
+        if (insn == 0xf200 && (ext & 0xfc00) == 0x5c00) {
+            /* fmovecr */
+            rom_offset = ext & 0x7f;
+            gen_helper_const_FP0(cpu_env, tcg_const_i32(rom_offset));
+            gen_op_store_fpr_FP0(REG(ext, 7));
+            return;
+        }
+        break;
     case 3: /* fmove out */
         gen_op_load_fpr_FP0(REG(ext, 7));
         opsize = ext_opsize(ext, 10);
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [Qemu-devel] [PATCH v3 12/16] target-m68k: add fscale, fgetman, fgetexp and fmod
  2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
                   ` (10 preceding siblings ...)
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 11/16] target-m68k: add fmovecr Laurent Vivier
@ 2017-02-07  0:59 ` Laurent Vivier
  2017-02-16  1:34   ` Richard Henderson
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 13/16] target-m68k: add fsglmul and fsgldiv Laurent Vivier
                   ` (4 subsequent siblings)
  16 siblings, 1 reply; 38+ messages in thread
From: Laurent Vivier @ 2017-02-07  0:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Richard Henderson, Laurent Vivier

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 target/m68k/cpu.h        |  1 +
 target/m68k/fpu_helper.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++
 target/m68k/helper.h     |  4 ++++
 target/m68k/translate.c  | 14 ++++++++++++
 4 files changed, 75 insertions(+)

diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h
index 7985dc3..3042ab7 100644
--- a/target/m68k/cpu.h
+++ b/target/m68k/cpu.h
@@ -253,6 +253,7 @@ typedef enum {
 /* Quotient */
 
 #define FPSR_QT_MASK  0x00ff0000
+#define FPSR_QT_SHIFT 16
 
 /* Floating-Point Control Register */
 /* Rounding mode */
diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
index d8145e0..42f5b5c 100644
--- a/target/m68k/fpu_helper.c
+++ b/target/m68k/fpu_helper.c
@@ -458,3 +458,59 @@ void HELPER(const_FP0)(CPUM68KState *env, uint32_t offset)
     env->fp0l = fpu_rom[offset].low;
     env->fp0h = fpu_rom[offset].high;
 }
+
+void HELPER(getexp_FP0)(CPUM68KState *env)
+{
+    int32_t exp;
+    floatx80 res;
+
+    res = FP0_to_floatx80(env);
+    if (floatx80_is_zero_or_denormal(res) || floatx80_is_any_nan(res) ||
+        floatx80_is_infinity(res)) {
+        return;
+    }
+    exp = (env->fp0h & 0x7fff) - 0x3fff;
+
+    res = int32_to_floatx80(exp, &env->fp_status);
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(getman_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    res = int64_to_floatx80(env->fp0l, &env->fp_status);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(scale_FP0_FP1)(CPUM68KState *env)
+{
+    int32_t scale;
+    int32_t exp;
+
+    scale = floatx80_to_int32(FP0_to_floatx80(env), &env->fp_status);
+
+    exp = (env->fp1h & 0x7fff) + scale;
+
+    env->fp0h = (env->fp1h & 0x8000) | (exp & 0x7fff);
+    env->fp0l = env->fp1l;
+}
+
+static void make_quotient(CPUM68KState *env, floatx80 val)
+{
+    uint32_t quotient = floatx80_to_int32(val, &env->fp_status);
+    uint32_t sign = (quotient >> 24) & 0x80;
+    quotient = sign | (quotient & 0x7f);
+    env->fpsr = (env->fpsr & ~FPSR_QT_MASK) | (quotient << FPSR_QT_SHIFT);
+}
+
+void HELPER(mod_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res = floatx80_rem(FP1_to_floatx80(env), FP0_to_floatx80(env),
+                       &env->fp_status);
+    make_quotient(env, res);
+
+    floatx80_to_FP0(env, res);
+}
diff --git a/target/m68k/helper.h b/target/m68k/helper.h
index 4927324..620e707 100644
--- a/target/m68k/helper.h
+++ b/target/m68k/helper.h
@@ -33,6 +33,10 @@ DEF_HELPER_1(tst_FP0, void, env)
 DEF_HELPER_1(update_fpstatus, void, env)
 DEF_HELPER_4(fmovem, void, env, i32, i32, i32)
 DEF_HELPER_2(const_FP0, void, env, i32)
+DEF_HELPER_1(getexp_FP0, void, env)
+DEF_HELPER_1(getman_FP0, void, env)
+DEF_HELPER_1(scale_FP0_FP1, void, env)
+DEF_HELPER_1(mod_FP0_FP1, void, env)
 
 DEF_HELPER_3(mac_move, void, env, i32, i32)
 DEF_HELPER_3(macmulf, i64, env, i32, i32)
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index 23eac63..be18083 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -4629,10 +4629,20 @@ DISAS_INSN(fpu)
     case 0x1a: case 0x5a: case 0x5e: /* fneg */
         gen_helper_chs_FP0(cpu_env);
         break;
+    case 0x1e: /* fgetexp */
+        gen_helper_getexp_FP0(cpu_env);
+        break;
+    case 0x1f: /* fgetman */
+        gen_helper_getman_FP0(cpu_env);
+        break;
     case 0x20: case 0x60: case 0x64: /* fdiv */
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_div_FP0_FP1(cpu_env);
         break;
+    case 0x21: /* fmod */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_mod_FP0_FP1(cpu_env);
+        break;
     case 0x22: case 0x62: case 0x66: /* fadd */
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_add_FP0_FP1(cpu_env);
@@ -4641,6 +4651,10 @@ DISAS_INSN(fpu)
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_mul_FP0_FP1(cpu_env);
         break;
+    case 0x26: /* fscale */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_scale_FP0_FP1(cpu_env);
+        break;
     case 0x28: case 0x68: case 0x6c: /* fsub */
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_sub_FP0_FP1(cpu_env);
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [Qemu-devel] [PATCH v3 13/16] target-m68k: add fsglmul and fsgldiv
  2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
                   ` (11 preceding siblings ...)
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 12/16] target-m68k: add fscale, fgetman, fgetexp and fmod Laurent Vivier
@ 2017-02-07  0:59 ` Laurent Vivier
  2017-02-16  1:36   ` Richard Henderson
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 14/16] target-m68k: add explicit single and double precision operations Laurent Vivier
                   ` (3 subsequent siblings)
  16 siblings, 1 reply; 38+ messages in thread
From: Laurent Vivier @ 2017-02-07  0:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Richard Henderson, Laurent Vivier

fsglmul and fsgldiv truncate data to single precision before computing
results.

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 target/m68k/fpu_helper.c | 22 ++++++++++++++++++++++
 target/m68k/helper.h     |  2 ++
 target/m68k/translate.c  |  8 ++++++++
 3 files changed, 32 insertions(+)

diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
index 42f5b5c..8a3eed3 100644
--- a/target/m68k/fpu_helper.c
+++ b/target/m68k/fpu_helper.c
@@ -351,6 +351,17 @@ void HELPER(mul_FP0_FP1)(CPUM68KState *env)
     floatx80_to_FP0(env, res);
 }
 
+void HELPER(sglmul_FP0_FP1)(CPUM68KState *env)
+{
+    float64 a, b, res;
+
+    a = floatx80_to_float64(FP0_to_floatx80(env), &env->fp_status);
+    b = floatx80_to_float64(FP1_to_floatx80(env), &env->fp_status);
+    res = float64_mul(a, b, &env->fp_status);
+
+    floatx80_to_FP0(env, float64_to_floatx80(res, &env->fp_status));
+}
+
 void HELPER(div_FP0_FP1)(CPUM68KState *env)
 {
     floatx80 res;
@@ -361,6 +372,17 @@ void HELPER(div_FP0_FP1)(CPUM68KState *env)
     floatx80_to_FP0(env, res);
 }
 
+void HELPER(sgldiv_FP0_FP1)(CPUM68KState *env)
+{
+    float64 a, b, res;
+
+    a = floatx80_to_float64(FP1_to_floatx80(env), &env->fp_status);
+    b = floatx80_to_float64(FP0_to_floatx80(env), &env->fp_status);
+    res = float64_div(a, b, &env->fp_status);
+
+    floatx80_to_FP0(env, float64_to_floatx80(res, &env->fp_status));
+}
+
 static int float_comp_to_cc(int float_compare)
 {
     switch (float_compare) {
diff --git a/target/m68k/helper.h b/target/m68k/helper.h
index 620e707..c30e5f7 100644
--- a/target/m68k/helper.h
+++ b/target/m68k/helper.h
@@ -26,7 +26,9 @@ DEF_HELPER_1(chs_FP0, void, env)
 DEF_HELPER_1(add_FP0_FP1, void, env)
 DEF_HELPER_1(sub_FP0_FP1, void, env)
 DEF_HELPER_1(mul_FP0_FP1, void, env)
+DEF_HELPER_1(sglmul_FP0_FP1, void, env)
 DEF_HELPER_1(div_FP0_FP1, void, env)
+DEF_HELPER_1(sgldiv_FP0_FP1, void, env)
 DEF_HELPER_1(cmp_FP0_FP1, void, env)
 DEF_HELPER_2(set_fpcr, void, env, i32)
 DEF_HELPER_1(tst_FP0, void, env)
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index be18083..99f41d3 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -4651,10 +4651,18 @@ DISAS_INSN(fpu)
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_mul_FP0_FP1(cpu_env);
         break;
+    case 0x24: /* fsgldiv */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_sgldiv_FP0_FP1(cpu_env);
+        break;
     case 0x26: /* fscale */
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_scale_FP0_FP1(cpu_env);
         break;
+    case 0x27: /* fsglmul */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_sglmul_FP0_FP1(cpu_env);
+        break;
     case 0x28: case 0x68: case 0x6c: /* fsub */
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_sub_FP0_FP1(cpu_env);
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [Qemu-devel] [PATCH v3 14/16] target-m68k: add explicit single and double precision operations
  2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
                   ` (12 preceding siblings ...)
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 13/16] target-m68k: add fsglmul and fsgldiv Laurent Vivier
@ 2017-02-07  0:59 ` Laurent Vivier
  2017-02-16  1:41   ` Richard Henderson
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 15/16] target-m68k: add more FPU instructions Laurent Vivier
                   ` (2 subsequent siblings)
  16 siblings, 1 reply; 38+ messages in thread
From: Laurent Vivier @ 2017-02-07  0:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Richard Henderson, Laurent Vivier

Add fssqrt, fdsqrt, fsabs, fdabs, fsneg, fdneg, fsadd, fdadd,
fssub, fdsub, fsmul, fdmul, fsdiv, fddiv, fsmove and fdmove.

The precision is managed using set_floatx80_rounding_precision(),
except for fsmove, fdmove, fsneg, fdneg, fsabs and fdabs:
the value is converted manually to the given precision and
converted back to floatx80.

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 target/m68k/fpu_helper.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++-
 target/m68k/helper.h     |  16 ++++-
 target/m68k/translate.c  |  76 +++++++++++++++++---
 3 files changed, 259 insertions(+), 11 deletions(-)

diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
index 8a3eed3..c69efe1 100644
--- a/target/m68k/fpu_helper.c
+++ b/target/m68k/fpu_helper.c
@@ -294,6 +294,16 @@ void HELPER(itrunc_FP0)(CPUM68KState *env)
     floatx80_to_FP0(env, res);
 }
 
+#define PREC_BEGIN(prec)                                        \
+    do {                                                        \
+        int old;                                                \
+        old = get_floatx80_rounding_precision(&env->fp_status); \
+        set_floatx80_rounding_precision(prec, &env->fp_status)  \
+
+#define PREC_END()                                              \
+        set_floatx80_rounding_precision(old, &env->fp_status);  \
+    } while (0)
+
 void HELPER(sqrt_FP0)(CPUM68KState *env)
 {
     floatx80 res;
@@ -303,6 +313,28 @@ void HELPER(sqrt_FP0)(CPUM68KState *env)
     floatx80_to_FP0(env, res);
 }
 
+void HELPER(ssqrt_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    PREC_BEGIN(32);
+    res = floatx80_sqrt(FP0_to_floatx80(env), &env->fp_status);
+    PREC_END();
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(dsqrt_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    PREC_BEGIN(64);
+    res = floatx80_sqrt(FP0_to_floatx80(env), &env->fp_status);
+    PREC_END();
+
+    floatx80_to_FP0(env, res);
+}
+
 void HELPER(abs_FP0)(CPUM68KState *env)
 {
     floatx80 res;
@@ -312,11 +344,59 @@ void HELPER(abs_FP0)(CPUM68KState *env)
     floatx80_to_FP0(env, res);
 }
 
-void HELPER(chs_FP0)(CPUM68KState *env)
+void HELPER(sabs_FP0)(CPUM68KState *env)
 {
     floatx80 res;
+    float32 f32;
+
+    res = floatx80_abs(FP0_to_floatx80(env));
+    f32 = floatx80_to_float32(res, &env->fp_status);
+    res = float32_to_floatx80(f32, &env->fp_status);
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(dabs_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    float64 f64;
+
+    res = floatx80_abs(FP0_to_floatx80(env));
+    f64 = floatx80_to_float64(res, &env->fp_status);
+    res = float64_to_floatx80(f64, &env->fp_status);
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(neg_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res = floatx80_chs(FP0_to_floatx80(env));
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(sneg_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    float32 f32;
+
+    res = floatx80_chs(FP0_to_floatx80(env));
+    f32 = floatx80_to_float32(res, &env->fp_status);
+    res = float32_to_floatx80(f32, &env->fp_status);
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(dneg_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    float64 f64;
 
     res = floatx80_chs(FP0_to_floatx80(env));
+    f64 = floatx80_to_float64(res, &env->fp_status);
+    res = float64_to_floatx80(f64, &env->fp_status);
 
     floatx80_to_FP0(env, res);
 }
@@ -331,6 +411,30 @@ void HELPER(add_FP0_FP1)(CPUM68KState *env)
     floatx80_to_FP0(env, res);
 }
 
+void HELPER(sadd_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    PREC_BEGIN(32);
+    res = floatx80_add(FP0_to_floatx80(env), FP1_to_floatx80(env),
+                      &env->fp_status);
+    PREC_END();
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(dadd_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    PREC_BEGIN(64);
+    res = floatx80_add(FP0_to_floatx80(env), FP1_to_floatx80(env),
+                      &env->fp_status);
+    PREC_END();
+
+    floatx80_to_FP0(env, res);
+}
+
 void HELPER(sub_FP0_FP1)(CPUM68KState *env)
 {
     floatx80 res;
@@ -341,6 +445,30 @@ void HELPER(sub_FP0_FP1)(CPUM68KState *env)
     floatx80_to_FP0(env, res);
 }
 
+void HELPER(ssub_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    PREC_BEGIN(32);
+    res = floatx80_sub(FP1_to_floatx80(env), FP0_to_floatx80(env),
+                       &env->fp_status);
+    PREC_END();
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(dsub_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    PREC_BEGIN(64);
+    res = floatx80_sub(FP1_to_floatx80(env), FP0_to_floatx80(env),
+                       &env->fp_status);
+    PREC_END();
+
+    floatx80_to_FP0(env, res);
+}
+
 void HELPER(mul_FP0_FP1)(CPUM68KState *env)
 {
     floatx80 res;
@@ -351,6 +479,30 @@ void HELPER(mul_FP0_FP1)(CPUM68KState *env)
     floatx80_to_FP0(env, res);
 }
 
+void HELPER(smul_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    PREC_BEGIN(32);
+    res = floatx80_mul(FP0_to_floatx80(env), FP1_to_floatx80(env),
+                       &env->fp_status);
+    PREC_END();
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(dmul_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    PREC_BEGIN(64);
+    res = floatx80_mul(FP0_to_floatx80(env), FP1_to_floatx80(env),
+                       &env->fp_status);
+    PREC_END();
+
+    floatx80_to_FP0(env, res);
+}
+
 void HELPER(sglmul_FP0_FP1)(CPUM68KState *env)
 {
     float64 a, b, res;
@@ -372,6 +524,30 @@ void HELPER(div_FP0_FP1)(CPUM68KState *env)
     floatx80_to_FP0(env, res);
 }
 
+void HELPER(sdiv_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    PREC_BEGIN(32);
+    res = floatx80_div(FP1_to_floatx80(env), FP0_to_floatx80(env),
+                       &env->fp_status);
+    PREC_END();
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(ddiv_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    PREC_BEGIN(64);
+    res = floatx80_div(FP1_to_floatx80(env), FP0_to_floatx80(env),
+                       &env->fp_status);
+    PREC_END();
+
+    floatx80_to_FP0(env, res);
+}
+
 void HELPER(sgldiv_FP0_FP1)(CPUM68KState *env)
 {
     float64 a, b, res;
diff --git a/target/m68k/helper.h b/target/m68k/helper.h
index c30e5f7..07aa04f 100644
--- a/target/m68k/helper.h
+++ b/target/m68k/helper.h
@@ -21,13 +21,27 @@ DEF_HELPER_1(reds32_FP0, void, env)
 DEF_HELPER_1(iround_FP0, void, env)
 DEF_HELPER_1(itrunc_FP0, void, env)
 DEF_HELPER_1(sqrt_FP0, void, env)
+DEF_HELPER_1(ssqrt_FP0, void, env)
+DEF_HELPER_1(dsqrt_FP0, void, env)
 DEF_HELPER_1(abs_FP0, void, env)
-DEF_HELPER_1(chs_FP0, void, env)
+DEF_HELPER_1(sabs_FP0, void, env)
+DEF_HELPER_1(dabs_FP0, void, env)
+DEF_HELPER_1(neg_FP0, void, env)
+DEF_HELPER_1(sneg_FP0, void, env)
+DEF_HELPER_1(dneg_FP0, void, env)
 DEF_HELPER_1(add_FP0_FP1, void, env)
+DEF_HELPER_1(sadd_FP0_FP1, void, env)
+DEF_HELPER_1(dadd_FP0_FP1, void, env)
 DEF_HELPER_1(sub_FP0_FP1, void, env)
+DEF_HELPER_1(ssub_FP0_FP1, void, env)
+DEF_HELPER_1(dsub_FP0_FP1, void, env)
 DEF_HELPER_1(mul_FP0_FP1, void, env)
+DEF_HELPER_1(smul_FP0_FP1, void, env)
+DEF_HELPER_1(dmul_FP0_FP1, void, env)
 DEF_HELPER_1(sglmul_FP0_FP1, void, env)
 DEF_HELPER_1(div_FP0_FP1, void, env)
+DEF_HELPER_1(sdiv_FP0_FP1, void, env)
+DEF_HELPER_1(ddiv_FP0_FP1, void, env)
 DEF_HELPER_1(sgldiv_FP0_FP1, void, env)
 DEF_HELPER_1(cmp_FP0_FP1, void, env)
 DEF_HELPER_2(set_fpcr, void, env, i32)
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index 99f41d3..883f4ff 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -4612,7 +4612,15 @@ DISAS_INSN(fpu)
         gen_op_load_fpr_FP0(REG(ext, 10));
     }
     switch (opmode) {
-    case 0: case 0x40: case 0x44: /* fmove */
+    case 0: /* fmove */
+        break;
+    case 0x40: /* fsmove */
+        gen_helper_redf32_FP0(cpu_env);
+        gen_helper_extf32_FP0(cpu_env);
+        break;
+    case 0x44: /* fdmove */
+        gen_helper_redf64_FP0(cpu_env);
+        gen_helper_extf64_FP0(cpu_env);
         break;
     case 1: /* fint */
         gen_helper_iround_FP0(cpu_env);
@@ -4620,14 +4628,32 @@ DISAS_INSN(fpu)
     case 3: /* fintrz */
         gen_helper_itrunc_FP0(cpu_env);
         break;
-    case 4: case 0x41: case 0x45: /* fsqrt */
+    case 4: /* fsqrt */
         gen_helper_sqrt_FP0(cpu_env);
         break;
-    case 0x18: case 0x58: case 0x5c: /* fabs */
+    case 0x41: /* fssqrt */
+        gen_helper_ssqrt_FP0(cpu_env);
+        break;
+    case 0x45: /* fdsqrt */
+        gen_helper_dsqrt_FP0(cpu_env);
+        break;
+    case 0x18: /* fabs */
         gen_helper_abs_FP0(cpu_env);
         break;
-    case 0x1a: case 0x5a: case 0x5e: /* fneg */
-        gen_helper_chs_FP0(cpu_env);
+    case 0x58: /* fsabs */
+        gen_helper_sabs_FP0(cpu_env);
+        break;
+    case 0x5c: /* fdabs */
+        gen_helper_dabs_FP0(cpu_env);
+        break;
+    case 0x1a: /* fneg */
+        gen_helper_neg_FP0(cpu_env);
+        break;
+    case 0x5a: /* fsneg */
+        gen_helper_sneg_FP0(cpu_env);
+        break;
+    case 0x5e: /* fdneg */
+        gen_helper_dneg_FP0(cpu_env);
         break;
     case 0x1e: /* fgetexp */
         gen_helper_getexp_FP0(cpu_env);
@@ -4635,22 +4661,46 @@ DISAS_INSN(fpu)
     case 0x1f: /* fgetman */
         gen_helper_getman_FP0(cpu_env);
         break;
-    case 0x20: case 0x60: case 0x64: /* fdiv */
+    case 0x20: /* fdiv */
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_div_FP0_FP1(cpu_env);
         break;
+    case 0x60: /* fsdiv */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_sdiv_FP0_FP1(cpu_env);
+        break;
+    case 0x64: /* fddiv */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_ddiv_FP0_FP1(cpu_env);
+        break;
     case 0x21: /* fmod */
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_mod_FP0_FP1(cpu_env);
         break;
-    case 0x22: case 0x62: case 0x66: /* fadd */
+    case 0x22: /* fadd */
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_add_FP0_FP1(cpu_env);
         break;
-    case 0x23: case 0x63: case 0x67: /* fmul */
+    case 0x62: /* fsadd */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_sadd_FP0_FP1(cpu_env);
+        break;
+    case 0x66: /* fdadd */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_dadd_FP0_FP1(cpu_env);
+        break;
+    case 0x23: /* fmul */
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_mul_FP0_FP1(cpu_env);
         break;
+    case 0x63: /* fsmul */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_smul_FP0_FP1(cpu_env);
+        break;
+    case 0x67: /* fdmul */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_dmul_FP0_FP1(cpu_env);
+        break;
     case 0x24: /* fsgldiv */
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_sgldiv_FP0_FP1(cpu_env);
@@ -4663,10 +4713,18 @@ DISAS_INSN(fpu)
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_sglmul_FP0_FP1(cpu_env);
         break;
-    case 0x28: case 0x68: case 0x6c: /* fsub */
+    case 0x28: /* fsub */
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_sub_FP0_FP1(cpu_env);
         break;
+    case 0x68: /* fssub */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_ssub_FP0_FP1(cpu_env);
+        break;
+    case 0x6c: /* fdsub */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_dsub_FP0_FP1(cpu_env);
+        break;
     case 0x38: /* fcmp */
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_cmp_FP0_FP1(cpu_env);
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [Qemu-devel] [PATCH v3 15/16] target-m68k: add more FPU instructions
  2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
                   ` (13 preceding siblings ...)
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 14/16] target-m68k: add explicit single and double precision operations Laurent Vivier
@ 2017-02-07  0:59 ` Laurent Vivier
  2017-02-16  1:46   ` Richard Henderson
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 16/16] target-m68k: add fsincos Laurent Vivier
  2017-02-07  1:25 ` [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU no-reply
  16 siblings, 1 reply; 38+ messages in thread
From: Laurent Vivier @ 2017-02-07  0:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Richard Henderson, Laurent Vivier

Add fsinh, flognp1, ftanh, fatan, fasin, fatanh,
fsin, ftan, fetox, ftwotox, ftentox, flogn, flog10, facos,
fcos.

As softfloat library does not provide these functions,
we us the libm of the host.

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 target/m68k/fpu_helper.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++
 target/m68k/helper.h     |  16 ++++
 target/m68k/translate.c  |  48 ++++++++++
 3 files changed, 301 insertions(+)

diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
index c69efe1..95d5cc4 100644
--- a/target/m68k/fpu_helper.c
+++ b/target/m68k/fpu_helper.c
@@ -23,6 +23,7 @@
 #include "cpu.h"
 #include "exec/helper-proto.h"
 #include "exec/exec-all.h"
+#include <math.h>
 
 static const floatx80 fpu_rom[128] = {
     [0x00] = floatx80_pi,                                   /* Pi */
@@ -712,3 +713,239 @@ void HELPER(mod_FP0_FP1)(CPUM68KState *env)
 
     floatx80_to_FP0(env, res);
 }
+
+static long double floatx80_to_ldouble(floatx80 val)
+{
+    if (floatx80_is_infinity(val)) {
+            if (floatx80_is_neg(val)) {
+                    return -__builtin_infl();
+            }
+            return __builtin_infl();
+    }
+    if (floatx80_is_any_nan(val)) {
+            char low[20];
+            sprintf(low, "0x%016"PRIx64, val.low);
+
+            return nanl(low);
+    }
+
+    return *(long double *)&val;
+}
+
+static floatx80 ldouble_to_floatx80(long double val)
+{
+    floatx80 res;
+
+    if (isinf(val)) {
+            res.high = floatx80_default_nan(NULL).high;
+            res.low = 0;
+    }
+    if (isinf(val) < 0) {
+            res.high |= 0x8000;
+    }
+    if (isnan(val)) {
+            res.high = floatx80_default_nan(NULL).high;
+            res.low = *(uint64_t *)((char *)&val + 4);
+    }
+    return *(floatx80 *)&val;
+}
+
+void HELPER(sinh_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    val = sinhl(floatx80_to_ldouble(FP0_to_floatx80(env)));
+    res = ldouble_to_floatx80(val);
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(lognp1_FP0)(CPUM68KState *env)
+{
+    floatx80 val;
+    long double res;
+
+    val = FP0_to_floatx80(env);
+    res = logl(floatx80_to_ldouble(val) + 1.0);
+
+    floatx80_to_FP0(env, ldouble_to_floatx80(res));
+}
+
+void HELPER(ln_FP0)(CPUM68KState *env)
+{
+    floatx80 val;
+    long double res;
+
+    val = FP0_to_floatx80(env);
+    res = logl(floatx80_to_ldouble(val));
+
+    floatx80_to_FP0(env, ldouble_to_floatx80(res));
+}
+
+void HELPER(log10_FP0)(CPUM68KState *env)
+{
+    floatx80 val;
+    long double res;
+
+    val = FP0_to_floatx80(env);
+    res = log10l(floatx80_to_ldouble(val));
+
+    floatx80_to_FP0(env, ldouble_to_floatx80(res));
+}
+
+void HELPER(atan_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    val = floatx80_to_ldouble(FP0_to_floatx80(env));
+
+    val = atanl(val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(asin_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    val = floatx80_to_ldouble(FP0_to_floatx80(env));
+    if (val < -1.0 || val > 1.0) {
+        floatx80_to_FP0(env, floatx80_default_nan(NULL));
+        return;
+    }
+
+    val = asinl(val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(atanh_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    val = floatx80_to_ldouble(FP0_to_floatx80(env));
+    if (val < -1.0 || val > 1.0) {
+        floatx80_to_FP0(env, floatx80_default_nan(NULL));
+        return;
+    }
+
+    val = atanhl(val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(sin_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    val = floatx80_to_ldouble(FP0_to_floatx80(env));
+
+    val = sinl(val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(tanh_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    val = floatx80_to_ldouble(FP0_to_floatx80(env));
+
+    val = tanhl(val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(tan_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    val = floatx80_to_ldouble(FP0_to_floatx80(env));
+
+    val = tanl(val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(exp_FP0)(CPUM68KState *env)
+{
+    floatx80 f;
+    long double res;
+
+    f = FP0_to_floatx80(env);
+
+    res = expl(floatx80_to_ldouble(f));
+
+    floatx80_to_FP0(env, ldouble_to_floatx80(res));
+}
+
+void HELPER(exp2_FP0)(CPUM68KState *env)
+{
+    floatx80 f;
+    long double res;
+
+    f = FP0_to_floatx80(env);
+
+    res = exp2l(floatx80_to_ldouble(f));
+
+    floatx80_to_FP0(env, ldouble_to_floatx80(res));
+}
+
+void HELPER(exp10_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    val = floatx80_to_ldouble(FP0_to_floatx80(env));
+
+    val = exp10l(val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(cosh_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    val = floatx80_to_ldouble(FP0_to_floatx80(env));
+
+    val = coshl(val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(acos_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    val = floatx80_to_ldouble(FP0_to_floatx80(env));
+    if (val < -1.0 || val > 1.0) {
+        floatx80_to_FP0(env, floatx80_default_nan(NULL));
+        return;
+    }
+
+    val = acosl(val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(cos_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    val = floatx80_to_ldouble(FP0_to_floatx80(env));
+
+    val = cosl(val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
diff --git a/target/m68k/helper.h b/target/m68k/helper.h
index 07aa04f..600a9a6 100644
--- a/target/m68k/helper.h
+++ b/target/m68k/helper.h
@@ -53,6 +53,22 @@ DEF_HELPER_1(getexp_FP0, void, env)
 DEF_HELPER_1(getman_FP0, void, env)
 DEF_HELPER_1(scale_FP0_FP1, void, env)
 DEF_HELPER_1(mod_FP0_FP1, void, env)
+DEF_HELPER_1(sinh_FP0, void, env)
+DEF_HELPER_1(lognp1_FP0, void, env)
+DEF_HELPER_1(atan_FP0, void, env)
+DEF_HELPER_1(asin_FP0, void, env)
+DEF_HELPER_1(atanh_FP0, void, env)
+DEF_HELPER_1(sin_FP0, void, env)
+DEF_HELPER_1(tanh_FP0, void, env)
+DEF_HELPER_1(tan_FP0, void, env)
+DEF_HELPER_1(exp_FP0, void, env)
+DEF_HELPER_1(exp2_FP0, void, env)
+DEF_HELPER_1(exp10_FP0, void, env)
+DEF_HELPER_1(ln_FP0, void, env)
+DEF_HELPER_1(log10_FP0, void, env)
+DEF_HELPER_1(cosh_FP0, void, env)
+DEF_HELPER_1(acos_FP0, void, env)
+DEF_HELPER_1(cos_FP0, void, env)
 
 DEF_HELPER_3(mac_move, void, env, i32, i32)
 DEF_HELPER_3(macmulf, i64, env, i32, i32)
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index 883f4ff..7af88a2 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -4625,6 +4625,9 @@ DISAS_INSN(fpu)
     case 1: /* fint */
         gen_helper_iround_FP0(cpu_env);
         break;
+    case 2: /* fsinh */
+        gen_helper_sinh_FP0(cpu_env);
+        break;
     case 3: /* fintrz */
         gen_helper_itrunc_FP0(cpu_env);
         break;
@@ -4637,6 +4640,42 @@ DISAS_INSN(fpu)
     case 0x45: /* fdsqrt */
         gen_helper_dsqrt_FP0(cpu_env);
         break;
+    case 0x06: /* flognp1 */
+        gen_helper_lognp1_FP0(cpu_env);
+        break;
+    case 0x09: /* ftanh */
+        gen_helper_tanh_FP0(cpu_env);
+        break;
+    case 0x0a: /* fatan */
+        gen_helper_atan_FP0(cpu_env);
+        break;
+    case 0x0c: /* fasin */
+        gen_helper_asin_FP0(cpu_env);
+        break;
+    case 0x0d: /* fatanh */
+        gen_helper_atanh_FP0(cpu_env);
+        break;
+    case 0x0e: /* fsin */
+        gen_helper_sin_FP0(cpu_env);
+        break;
+    case 0x0f: /* ftan */
+        gen_helper_tan_FP0(cpu_env);
+        break;
+    case 0x10: /* fetox */
+        gen_helper_exp_FP0(cpu_env);
+        break;
+    case 0x11: /* ftwotox */
+        gen_helper_exp2_FP0(cpu_env);
+        break;
+    case 0x12: /* ftentox */
+        gen_helper_exp10_FP0(cpu_env);
+        break;
+    case 0x14: /* flogn */
+        gen_helper_ln_FP0(cpu_env);
+        break;
+    case 0x15: /* flog10 */
+        gen_helper_log10_FP0(cpu_env);
+        break;
     case 0x18: /* fabs */
         gen_helper_abs_FP0(cpu_env);
         break;
@@ -4646,6 +4685,9 @@ DISAS_INSN(fpu)
     case 0x5c: /* fdabs */
         gen_helper_dabs_FP0(cpu_env);
         break;
+    case 0x19: /* fcosh */
+        gen_helper_cosh_FP0(cpu_env);
+        break;
     case 0x1a: /* fneg */
         gen_helper_neg_FP0(cpu_env);
         break;
@@ -4655,6 +4697,12 @@ DISAS_INSN(fpu)
     case 0x5e: /* fdneg */
         gen_helper_dneg_FP0(cpu_env);
         break;
+    case 0x1c: /* facos */
+        gen_helper_acos_FP0(cpu_env);
+        break;
+    case 0x1d: /* fcos */
+        gen_helper_cos_FP0(cpu_env);
+        break;
     case 0x1e: /* fgetexp */
         gen_helper_getexp_FP0(cpu_env);
         break;
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [Qemu-devel] [PATCH v3 16/16] target-m68k: add fsincos
  2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
                   ` (14 preceding siblings ...)
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 15/16] target-m68k: add more FPU instructions Laurent Vivier
@ 2017-02-07  0:59 ` Laurent Vivier
  2017-02-07  1:25 ` [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU no-reply
  16 siblings, 0 replies; 38+ messages in thread
From: Laurent Vivier @ 2017-02-07  0:59 UTC (permalink / raw)
  To: qemu-devel; +Cc: Aurelien Jarno, Richard Henderson, Laurent Vivier

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 target/m68k/fpu_helper.c | 21 +++++++++++++++++++++
 target/m68k/helper.h     |  1 +
 target/m68k/translate.c  | 15 +++++++++++++++
 3 files changed, 37 insertions(+)

diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
index 95d5cc4..9b9d9aa 100644
--- a/target/m68k/fpu_helper.c
+++ b/target/m68k/fpu_helper.c
@@ -95,6 +95,12 @@ static floatx80 FP1_to_floatx80(CPUM68KState *env)
     return (floatx80){ .low = env->fp1l, .high = env->fp1h };
 }
 
+static void floatx80_to_FP1(CPUM68KState *env, floatx80 res)
+{
+    env->fp1l = res.low;
+    env->fp1h = res.high;
+}
+
 void HELPER(exts32_FP0)(CPUM68KState *env)
 {
     floatx80 res;
@@ -949,3 +955,18 @@ void HELPER(cos_FP0)(CPUM68KState *env)
     res = ldouble_to_floatx80(val);
     floatx80_to_FP0(env, res);
 }
+
+void HELPER(sincos_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val, valsin, valcos;
+
+    val = floatx80_to_ldouble(FP0_to_floatx80(env));
+
+    sincosl(val, &valsin, &valcos);
+    res = ldouble_to_floatx80(valsin);
+    floatx80_to_FP0(env, res);
+    res = ldouble_to_floatx80(valcos);
+    floatx80_to_FP1(env, res);
+
+}
diff --git a/target/m68k/helper.h b/target/m68k/helper.h
index 600a9a6..16f3370 100644
--- a/target/m68k/helper.h
+++ b/target/m68k/helper.h
@@ -69,6 +69,7 @@ DEF_HELPER_1(log10_FP0, void, env)
 DEF_HELPER_1(cosh_FP0, void, env)
 DEF_HELPER_1(acos_FP0, void, env)
 DEF_HELPER_1(cos_FP0, void, env)
+DEF_HELPER_1(sincos_FP0_FP1, void, env)
 
 DEF_HELPER_3(mac_move, void, env, i32, i32)
 DEF_HELPER_3(macmulf, i64, env, i32, i32)
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index 7af88a2..cb4d9ce 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -923,6 +923,14 @@ static void gen_op_load_fpr_FP1(int freg)
                    offsetof(CPUM68KState, fregs[freg].l.lower));
 }
 
+static void gen_op_store_fpr_FP1(int freg)
+{
+    tcg_gen_st16_i32(QREG_FP1H, cpu_env,
+                     offsetof(CPUM68KState, fregs[freg].l.upper));
+    tcg_gen_st_i64(QREG_FP1L, cpu_env,
+                   offsetof(CPUM68KState, fregs[freg].l.lower));
+}
+
 static void gen_extend_FP0(int opsize)
 {
     switch (opsize) {
@@ -4773,6 +4781,13 @@ DISAS_INSN(fpu)
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_dsub_FP0_FP1(cpu_env);
         break;
+    case 0x30: case 0x31: case 0x32:
+    case 0x33: case 0x34: case 0x35:
+    case 0x36: case 0x37:
+        gen_helper_sincos_FP0_FP1(cpu_env);
+        gen_op_store_fpr_FP0(REG(ext, 7)); /* sin */
+        gen_op_store_fpr_FP1(REG(ext, 0)); /* cos */
+        break;
     case 0x38: /* fcmp */
         gen_op_load_fpr_FP1(REG(ext, 7));
         gen_helper_cmp_FP0_FP1(cpu_env);
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU
  2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
                   ` (15 preceding siblings ...)
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 16/16] target-m68k: add fsincos Laurent Vivier
@ 2017-02-07  1:25 ` no-reply
  16 siblings, 0 replies; 38+ messages in thread
From: no-reply @ 2017-02-07  1:25 UTC (permalink / raw)
  To: laurent; +Cc: famz, qemu-devel, aurelien, rth

Hi,

Your series seems to have some coding style problems. See output below for
more information:

Type: series
Subject: [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU
Message-id: 20170207005930.28327-1-laurent@vivier.eu

=== TEST SCRIPT BEGIN ===
#!/bin/bash

BASE=base
n=1
total=$(git log --oneline $BASE.. | wc -l)
failed=0

# Useful git options
git config --local diff.renamelimit 0
git config --local diff.renames True

commits="$(git log --format=%H --reverse $BASE..)"
for c in $commits; do
    echo "Checking PATCH $n/$total: $(git log -n 1 --format=%s $c)..."
    if ! git show $c --format=email | ./scripts/checkpatch.pl --mailback -; then
        failed=1
        echo
    fi
    n=$((n+1))
done

exit $failed
=== TEST SCRIPT END ===

Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
From https://github.com/patchew-project/qemu
 * [new tag]         patchew/20170207005930.28327-1-laurent@vivier.eu -> patchew/20170207005930.28327-1-laurent@vivier.eu
Switched to a new branch 'test'
24fef09 target-m68k: add fsincos
31936e6 target-m68k: add more FPU instructions
04f5a29 target-m68k: add explicit single and double precision operations
8117b9d target-m68k: add fsglmul and fsgldiv
a11400e target-m68k: add fscale, fgetman, fgetexp and fmod
22a8624 target-m68k: add fmovecr
85d9833 target-m68k: add fscc.
403aa87 target-m68k: add fmovem
f53725e target-m68k: define 96bit FP registers for gdb on 680x0
0a01215 target-m68k: manage FPU exceptions
194d954 target-m68k: add FPCR and FPSR
2ae3010 target-m68k: use floatx80 internally
7833d24 target-m68k: define ext_opsize
971c3fd target-m68k: move FPU helpers to fpu_helper.c
214af1e softloat: disable floatx80_invalid_encoding() for m68k
71a3a80 softfloat: define 680x0 specific values

=== OUTPUT BEGIN ===
Checking PATCH 1/16: softfloat: define 680x0 specific values...
Checking PATCH 2/16: softloat: disable floatx80_invalid_encoding() for m68k...
Checking PATCH 3/16: target-m68k: move FPU helpers to fpu_helper.c...
Checking PATCH 4/16: target-m68k: define ext_opsize...
Checking PATCH 5/16: target-m68k: use floatx80 internally...
ERROR: storage class should be at the beginning of the declaration
#457: FILE: target/m68k/translate.c:39:
+#define DEFF96(name, offset) static TCGv_i32 QREG_##name##H; \

total: 1 errors, 0 warnings, 1204 lines checked

Your patch has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

Checking PATCH 6/16: target-m68k: add FPCR and FPSR...
Checking PATCH 7/16: target-m68k: manage FPU exceptions...
Checking PATCH 8/16: target-m68k: define 96bit FP registers for gdb on 680x0...
Checking PATCH 9/16: target-m68k: add fmovem...
Checking PATCH 10/16: target-m68k: add fscc....
Checking PATCH 11/16: target-m68k: add fmovecr...
Checking PATCH 12/16: target-m68k: add fscale, fgetman, fgetexp and fmod...
Checking PATCH 13/16: target-m68k: add fsglmul and fsgldiv...
Checking PATCH 14/16: target-m68k: add explicit single and double precision operations...
Checking PATCH 15/16: target-m68k: add more FPU instructions...
Checking PATCH 16/16: target-m68k: add fsincos...
=== OUTPUT END ===

Test command exited with code: 1


---
Email generated automatically by Patchew [http://patchew.org/].
Please send your feedback to patchew-devel@freelists.org

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 01/16] softfloat: define 680x0 specific values
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 01/16] softfloat: define 680x0 specific values Laurent Vivier
@ 2017-02-08 21:30   ` Richard Henderson
  0 siblings, 0 replies; 38+ messages in thread
From: Richard Henderson @ 2017-02-08 21:30 UTC (permalink / raw)
  To: Laurent Vivier, qemu-devel; +Cc: Peter Maydell, Aurelien Jarno

On 02/06/2017 04:59 PM, Laurent Vivier wrote:
> CC: Peter Maydell <peter.maydell@linaro.org>
> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
> ---
>  fpu/softfloat-specialize.h | 34 +++++++++++++++++++++++++++++++---
>  1 file changed, 31 insertions(+), 3 deletions(-)

Reviewed-by: Richard Henderson <rth@twiddle.net>


r~

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 02/16] softloat: disable floatx80_invalid_encoding() for m68k
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 02/16] softloat: disable floatx80_invalid_encoding() for m68k Laurent Vivier
@ 2017-02-08 21:32   ` Richard Henderson
  2017-02-08 22:58   ` Peter Maydell
  1 sibling, 0 replies; 38+ messages in thread
From: Richard Henderson @ 2017-02-08 21:32 UTC (permalink / raw)
  To: Laurent Vivier, qemu-devel; +Cc: Andreas Schwab, Aurelien Jarno

On 02/06/2017 04:59 PM, Laurent Vivier wrote:
> According to the comment, this definition of invalid encoding is given
> by intel developer's manual, and doesn't work with the behavior
> of 680x0 FPU.
>
> CC: Andreas Schwab <schwab@linux-m68k.org>
> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
> ---

Reviewed-by: Richard Henderson <rth@twiddle.net>


r~

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 03/16] target-m68k: move FPU helpers to fpu_helper.c
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 03/16] target-m68k: move FPU helpers to fpu_helper.c Laurent Vivier
@ 2017-02-08 21:33   ` Richard Henderson
  0 siblings, 0 replies; 38+ messages in thread
From: Richard Henderson @ 2017-02-08 21:33 UTC (permalink / raw)
  To: Laurent Vivier, qemu-devel; +Cc: Aurelien Jarno

On 02/06/2017 04:59 PM, Laurent Vivier wrote:
> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
> ---
>  target/m68k/Makefile.objs |   2 +-
>  target/m68k/fpu_helper.c  | 112 ++++++++++++++++++++++++++++++++++++++++++++++
>  target/m68k/helper.c      |  88 ------------------------------------
>  3 files changed, 113 insertions(+), 89 deletions(-)
>  create mode 100644 target/m68k/fpu_helper.c

Reviewed-by: Richard Henderson <rth@twiddle.net>


r~

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 04/16] target-m68k: define ext_opsize
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 04/16] target-m68k: define ext_opsize Laurent Vivier
@ 2017-02-08 21:33   ` Richard Henderson
  0 siblings, 0 replies; 38+ messages in thread
From: Richard Henderson @ 2017-02-08 21:33 UTC (permalink / raw)
  To: Laurent Vivier, qemu-devel; +Cc: Aurelien Jarno

On 02/06/2017 04:59 PM, Laurent Vivier wrote:
> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
> ---
>  target/m68k/translate.c | 43 ++++++++++++++++++++++++-------------------
>  1 file changed, 24 insertions(+), 19 deletions(-)

Reviewed-by: Richard Henderson <rth@twiddle.net>


r~

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 02/16] softloat: disable floatx80_invalid_encoding() for m68k
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 02/16] softloat: disable floatx80_invalid_encoding() for m68k Laurent Vivier
  2017-02-08 21:32   ` Richard Henderson
@ 2017-02-08 22:58   ` Peter Maydell
  2017-02-09  8:07     ` Laurent Vivier
  1 sibling, 1 reply; 38+ messages in thread
From: Peter Maydell @ 2017-02-08 22:58 UTC (permalink / raw)
  To: Laurent Vivier
  Cc: QEMU Developers, Andreas Schwab, Aurelien Jarno, Richard Henderson

On 7 February 2017 at 00:59, Laurent Vivier <laurent@vivier.eu> wrote:
> According to the comment, this definition of invalid encoding is given
> by intel developer's manual, and doesn't work with the behavior
> of 680x0 FPU.
>
> CC: Andreas Schwab <schwab@linux-m68k.org>
> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
> ---
>  fpu/softfloat.c         | 31 +++++++++++++++++++++++++++++++
>  include/fpu/softfloat.h | 15 ---------------
>  2 files changed, 31 insertions(+), 15 deletions(-)
>
> diff --git a/fpu/softfloat.c b/fpu/softfloat.c
> index c295f31..f95b19f 100644
> --- a/fpu/softfloat.c
> +++ b/fpu/softfloat.c
> @@ -4799,6 +4799,37 @@ int float64_unordered_quiet(float64 a, float64 b, float_status *status)
>  }
>
>  /*----------------------------------------------------------------------------
> +| Return whether the given value is an invalid floatx80 encoding.
> +| Invalid floatx80 encodings arise when the integer bit is not set, but
> +| the exponent is not zero. The only times the integer bit is permitted to
> +| be zero is in subnormal numbers and the value zero.
> +| This includes what the Intel software developer's manual calls pseudo-NaNs,
> +| pseudo-infinities and un-normal numbers. It does not include
> +| pseudo-denormals, which must still be correctly handled as inputs even
> +| if they are never generated as outputs.
> +*----------------------------------------------------------------------------*/
> +static inline bool floatx80_invalid_encoding(floatx80 a)
> +{
> +#if defined(TARGET_M68K)
> +    /*-------------------------------------------------------------------------
> +    |  M68000 FAMILY PROGRAMMER’S REFERENCE MANUAL
> +    |  1.6.2 Denormalized Numbers
> +    |  Since the extended-precision data format has an explicit integer bit,
> +    |  a number can be formatted with a nonzero exponent, less than the maximum
> +    |  value, and a zero integer bit.  The IEEE 754 standard does not define a
> +    |  zero integer bit. Such a number is an unnormalized number. Hardware does
> +    |  not directly support denormalized and unnormalized numbers, but
> +    |  implicitly supports them by trapping them as unimplemented data types,
> +    |  allowing efficient conversion in software.
> +    *------------------------------------------------------------------------*/

This comment says that numbers of the form that this function
identifies (zero integer bit and nonzero exponent) are
supposed to trap on m68k. That suggests that the target/m68k code
is going to need to catch this case before it calls softfloat
code. So (a) we don't care what the softfloat code does
with these representations because they'll never get to it
and (b) the m68k code could probably use floatx80_invalid_encoding()
as a function to identify where it needs to trap, which it
can't do if it's moved out of softfloat.h.

Either way this change doesn't seem right given what the
comment describes...

thanks
-- PMM

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 02/16] softloat: disable floatx80_invalid_encoding() for m68k
  2017-02-08 22:58   ` Peter Maydell
@ 2017-02-09  8:07     ` Laurent Vivier
  0 siblings, 0 replies; 38+ messages in thread
From: Laurent Vivier @ 2017-02-09  8:07 UTC (permalink / raw)
  To: Peter Maydell
  Cc: QEMU Developers, Andreas Schwab, Aurelien Jarno, Richard Henderson

Le 08/02/2017 à 23:58, Peter Maydell a écrit :
> On 7 February 2017 at 00:59, Laurent Vivier <laurent@vivier.eu> wrote:
>> According to the comment, this definition of invalid encoding is given
>> by intel developer's manual, and doesn't work with the behavior
>> of 680x0 FPU.
>>
>> CC: Andreas Schwab <schwab@linux-m68k.org>
>> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
>> ---
>>  fpu/softfloat.c         | 31 +++++++++++++++++++++++++++++++
>>  include/fpu/softfloat.h | 15 ---------------
>>  2 files changed, 31 insertions(+), 15 deletions(-)
>>
>> diff --git a/fpu/softfloat.c b/fpu/softfloat.c
>> index c295f31..f95b19f 100644
>> --- a/fpu/softfloat.c
>> +++ b/fpu/softfloat.c
>> @@ -4799,6 +4799,37 @@ int float64_unordered_quiet(float64 a, float64 b, float_status *status)
>>  }
>>
>>  /*----------------------------------------------------------------------------
>> +| Return whether the given value is an invalid floatx80 encoding.
>> +| Invalid floatx80 encodings arise when the integer bit is not set, but
>> +| the exponent is not zero. The only times the integer bit is permitted to
>> +| be zero is in subnormal numbers and the value zero.
>> +| This includes what the Intel software developer's manual calls pseudo-NaNs,
>> +| pseudo-infinities and un-normal numbers. It does not include
>> +| pseudo-denormals, which must still be correctly handled as inputs even
>> +| if they are never generated as outputs.
>> +*----------------------------------------------------------------------------*/
>> +static inline bool floatx80_invalid_encoding(floatx80 a)
>> +{
>> +#if defined(TARGET_M68K)
>> +    /*-------------------------------------------------------------------------
>> +    |  M68000 FAMILY PROGRAMMER’S REFERENCE MANUAL
>> +    |  1.6.2 Denormalized Numbers
>> +    |  Since the extended-precision data format has an explicit integer bit,
>> +    |  a number can be formatted with a nonzero exponent, less than the maximum
>> +    |  value, and a zero integer bit.  The IEEE 754 standard does not define a
>> +    |  zero integer bit. Such a number is an unnormalized number. Hardware does
>> +    |  not directly support denormalized and unnormalized numbers, but
>> +    |  implicitly supports them by trapping them as unimplemented data types,
>> +    |  allowing efficient conversion in software.
>> +    *------------------------------------------------------------------------*/
> 
> This comment says that numbers of the form that this function
> identifies (zero integer bit and nonzero exponent) are
> supposed to trap on m68k. That suggests that the target/m68k code
> is going to need to catch this case before it calls softfloat
> code. So (a) we don't care what the softfloat code does
> with these representations because they'll never get to it
> and (b) the m68k code could probably use floatx80_invalid_encoding()
> as a function to identify where it needs to trap, which it
> can't do if it's moved out of softfloat.h.
> 
> Either way this change doesn't seem right given what the
> comment describes...

Ok, so I think this functionality needs more work inside the 680x0 FPU
emulation. As the emulation can work without it, I'm going to remove it
from this series to rework it later.

Thanks,
Laurent

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 05/16] target-m68k: use floatx80 internally
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 05/16] target-m68k: use floatx80 internally Laurent Vivier
@ 2017-02-15 22:59   ` Richard Henderson
  0 siblings, 0 replies; 38+ messages in thread
From: Richard Henderson @ 2017-02-15 22:59 UTC (permalink / raw)
  To: Laurent Vivier, qemu-devel; +Cc: Aurelien Jarno

On 02/07/2017 11:59 AM, Laurent Vivier wrote:
> +    uint32_t fp0h;
> +    uint64_t fp0l;
> +    uint32_t fp1h;
> +    uint64_t fp1l;

I'm not especially keen on these temporaries.

Wouldn't it be better to pass pointers to FPReg to the helpers, so that e.g.

   fadd.x  fp0, fp1

puts the result in fp1 directly, without having to copy from this FP0 location?
I can see that you would need a proper FPReg temporary to deal with e.g.

   fadd.d  a0@, fp1

such that the memory source gets converted before being used as input to the 
addition.

> +static float32 FP0_to_float32(CPUM68KState *env)
>  {
> +    return *(float32 *)&env->fp0h;
>  }
...
> +static void float32_to_FP0(CPUM68KState *env, float32 val)
>  {
> +    env->fp0h = *(uint32_t *)&val;
>  }

I don't like this type-punning.  I also don't see what good it does to store 
these truncated values in portions of FP0, when you could simply pass or return 
them by value from the relevant helpers, e.g.

> +void HELPER(reds32_FP0)(CPUM68KState *env)
>  {
> +    int32_t res;
> +
> +    res = floatx80_to_int32(FP0_to_floatx80(env), &env->fp_status);
> +
> +    int32_to_FP0(env, res);
>  }

could easily be

int32_t HELPER(reds32)(CPUM68KState *env, FPReg *val)
{
   return floatx80_to_int32(*val, &env->fp_status);
}


r~

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 06/16] target-m68k: add FPCR and FPSR
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 06/16] target-m68k: add FPCR and FPSR Laurent Vivier
@ 2017-02-16  1:10   ` Richard Henderson
  0 siblings, 0 replies; 38+ messages in thread
From: Richard Henderson @ 2017-02-16  1:10 UTC (permalink / raw)
  To: Laurent Vivier, qemu-devel; +Cc: Aurelien Jarno

On 02/07/2017 11:59 AM, Laurent Vivier wrote:
>  void HELPER(itrunc_FP0)(CPUM68KState *env)
>  {
>      floatx80 res;
>
> +    set_float_rounding_mode(float_round_to_zero, &env->fp_status);
>      res = floatx80_round_to_int(FP0_to_floatx80(env), &env->fp_status);
> +    restore_rounding_mode(env);

It would be better to save/restore the current rounding mode as opposed to 
recomputing from the fpcr.

>  void HELPER(cmp_FP0_FP1)(CPUM68KState *env)
>  {
>      floatx80 fp0 = FP0_to_floatx80(env);
>      floatx80 fp1 = FP1_to_floatx80(env);
> -    floatx80 res;
> +    int float_compare;
>
> -    res = floatx80_sub(fp0, fp1, &env->fp_status);
> -    if (floatx80_is_quiet_nan(res, &env->fp_status)) {
> -        /* +/-inf compares equal against itself, but sub returns nan.  */
> -        if (!floatx80_is_quiet_nan(fp0, &env->fp_status)
> -            && !floatx80_is_quiet_nan(fp1, &env->fp_status)) {
> -            res = floatx80_zero;
> -            if (floatx80_lt_quiet(fp0, res, &env->fp_status)) {
> -                res = floatx80_chs(res);
> -            }
> -        }
> -    }
> -
> -    floatx80_to_FP0(env, res);
> +    float_compare = floatx80_compare(fp1, fp0, &env->fp_status);
> +    env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | float_comp_to_cc(float_compare);
>  }
>
> -uint32_t HELPER(compare_FP0)(CPUM68KState *env)
> +void HELPER(tst_FP0)(CPUM68KState *env)
>  {
> -    floatx80 fp0 = FP0_to_floatx80(env);
> -    return floatx80_compare_quiet(fp0, floatx80_zero, &env->fp_status);
> +    uint32_t fpsr = 0;
> +    floatx80 val = FP0_to_floatx80(env);
> +
> +    if (floatx80_is_neg(val)) {
> +        fpsr |= FPSR_CC_N;
> +    }
> +
> +    if (floatx80_is_any_nan(val)) {
> +        fpsr |= FPSR_CC_A;
> +    } else if (floatx80_is_infinity(val)) {
> +        fpsr |= FPSR_CC_I;
> +    } else if (floatx80_is_zero(val)) {
> +        fpsr |= FPSR_CC_Z;
> +    }
> +    env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | fpsr;
>  }

It would be better to pass in the old FPSR value, returning the new FPSR value, 
so that the helper can be marked TCG_CALL_NO_RWG -- not reading or modifying 
TCG globals.

> +        gen_helper_tst_FP0(cpu_env);

Making this, e.g. gen_helper_tst_FP0(QREG_FPSR, cpu_env).  Which will also 
happen to leave the FPSR in a host register where it can be used if the next 
insn is a fp branch.


r~

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 07/16] target-m68k: manage FPU exceptions
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 07/16] target-m68k: manage FPU exceptions Laurent Vivier
@ 2017-02-16  1:16   ` Richard Henderson
  0 siblings, 0 replies; 38+ messages in thread
From: Richard Henderson @ 2017-02-16  1:16 UTC (permalink / raw)
  To: Laurent Vivier, qemu-devel; +Cc: Aurelien Jarno

On 02/07/2017 11:59 AM, Laurent Vivier wrote:
> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
> ---
>  target/m68k/cpu.h        |  28 +++++++++++++
>  target/m68k/fpu_helper.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++-
>  target/m68k/helper.h     |   1 +
>  target/m68k/translate.c  |  27 ++++++++++++
>  4 files changed, 162 insertions(+), 1 deletion(-)
>
> diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h
> index 6b3cb26..7985dc3 100644
> --- a/target/m68k/cpu.h
> +++ b/target/m68k/cpu.h
> @@ -57,6 +57,15 @@
>  #define EXCP_TRAP15         47   /* User trap #15.  */
>  #define EXCP_UNSUPPORTED    61
>  #define EXCP_ICE            13
> +#define EXCP_FP_BSUN        48 /* Branch Set on Unordered */
> +#define EXCP_FP_INEX        49 /* Inexact result */
> +#define EXCP_FP_DZ          50 /* Divide by Zero */
> +#define EXCP_FP_UNFL        51 /* Underflow */
> +#define EXCP_FP_OPERR       52 /* Operand Error */
> +#define EXCP_FP_OVFL        53 /* Overflow */
> +#define EXCP_FP_SNAN        54 /* Signaling Not-A-Number */
> +#define EXCP_FP_UNIMP       55 /* Unimplemented Data type */
> +
>
>  #define EXCP_RTE            0x100
>  #define EXCP_HALT_INSN      0x101
> @@ -222,6 +231,25 @@ typedef enum {
>  #define FPSR_CC_Z     0x04000000 /* Zero */
>  #define FPSR_CC_N     0x08000000 /* Negative */
>
> +/* Exception Status */
> +#define FPSR_ES_MASK  0x0000ff00
> +#define FPSR_ES_BSUN  0x00008000 /* Branch Set on Unordered */
> +#define FPSR_ES_SNAN  0x00004000 /* Signaling Not-A-Number */
> +#define FPSR_ES_OPERR 0x00002000 /* Operand Error */
> +#define FPSR_ES_OVFL  0x00001000 /* Overflow */
> +#define FPSR_ES_UNFL  0x00000800 /* Underflow */
> +#define FPSR_ES_DZ    0x00000400 /* Divide by Zero */
> +#define FPSR_ES_INEX2 0x00000200 /* Inexact operation */
> +#define FPSR_ES_INEX  0x00000100 /* Inexact decimal input */
> +
> +/* Accrued Exception */
> +#define FPSR_AE_MASK  0x000000ff
> +#define FPSR_AE_IOP   0x00000080 /* Invalid Operation */
> +#define FPSR_AE_OVFL  0x00000040 /* Overflow */
> +#define FPSR_AE_UNFL  0x00000020 /* Underflow */
> +#define FPSR_AE_DZ    0x00000010 /* Divide by Zero */
> +#define FPSR_AE_INEX  0x00000008 /* Inexact */
> +
>  /* Quotient */
>
>  #define FPSR_QT_MASK  0x00ff0000
> diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
> index 9d39118..1e68c41 100644
> --- a/target/m68k/fpu_helper.c
> +++ b/target/m68k/fpu_helper.c
> @@ -177,6 +177,70 @@ static void restore_rounding_mode(CPUM68KState *env)
>      }
>  }
>
> +static void set_fpsr_exception(CPUM68KState *env)
> +{
> +    uint32_t fpsr = 0;
> +    int flags;
> +
> +    flags = get_float_exception_flags(&env->fp_status);
> +    if (flags == 0) {
> +        return;
> +    }
> +    set_float_exception_flags(0, &env->fp_status);
> +
> +    if (flags & float_flag_invalid) {
> +        fpsr |= FPSR_AE_IOP;
> +    }
> +    if (flags & float_flag_divbyzero) {
> +        fpsr |= FPSR_AE_DZ;
> +    }
> +    if (flags & float_flag_overflow) {
> +        fpsr |= FPSR_AE_OVFL;
> +    }
> +    if (flags & float_flag_underflow) {
> +        fpsr |= FPSR_AE_UNFL;
> +    }
> +    if (flags & float_flag_inexact) {
> +        fpsr |= FPSR_AE_INEX;
> +    }
> +
> +    env->fpsr = (env->fpsr & ~FPSR_AE_MASK) | fpsr;
> +}
> +
> +static void fpu_exception(CPUM68KState *env, uint32_t exception)
> +{
> +    CPUState *cs = CPU(m68k_env_get_cpu(env));
> +
> +    env->fpsr = (env->fpsr & ~FPSR_ES_MASK) | exception;
> +    if (env->fpcr & exception) {

What are you trying to do here?  This test is obviously true if exception != 0.

> +        switch (exception) {
> +        case FPSR_ES_BSUN:
> +            cs->exception_index = EXCP_FP_BSUN;
> +            break;
> +        case FPSR_ES_SNAN:
> +            cs->exception_index = EXCP_FP_SNAN;
> +            break;
> +        case FPSR_ES_OPERR:
> +            cs->exception_index = EXCP_FP_OPERR;
> +            break;
> +        case FPSR_ES_OVFL:
> +            cs->exception_index = EXCP_FP_OVFL;
> +            break;
> +        case FPSR_ES_UNFL:
> +            cs->exception_index = EXCP_FP_UNFL;
> +            break;
> +        case FPSR_ES_DZ:
> +            cs->exception_index = EXCP_FP_DZ;
> +            break;
> +        case FPSR_ES_INEX:
> +        case FPSR_ES_INEX2:
> +            cs->exception_index = EXCP_FP_INEX;
> +            break;
> +        }
> +        cpu_loop_exit_restore(cs, GETPC());

GETPC must be invoked from the outer-most handler.  You need to pass this in 
from the callers.

> +    }
> +}
> +
>  void cpu_m68k_set_fpcr(CPUM68KState *env, uint32_t val)
>  {
>      env->fpcr = val & 0xffff;
> @@ -292,10 +356,16 @@ void HELPER(cmp_FP0_FP1)(CPUM68KState *env)
>  {
>      floatx80 fp0 = FP0_to_floatx80(env);
>      floatx80 fp1 = FP1_to_floatx80(env);
> -    int float_compare;
> +    int flags, float_compare;
>
>      float_compare = floatx80_compare(fp1, fp0, &env->fp_status);
>      env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | float_comp_to_cc(float_compare);
> +
> +    flags = get_float_exception_flags(&env->fp_status);
> +    if (flags & float_flag_invalid) {
> +        fpu_exception(env, FPSR_ES_OPERR);
> +   }
> +   set_fpsr_exception(env);
>  }
>
>  void HELPER(tst_FP0)(CPUM68KState *env)
> @@ -315,4 +385,39 @@ void HELPER(tst_FP0)(CPUM68KState *env)
>          fpsr |= FPSR_CC_Z;
>      }
>      env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | fpsr;
> +
> +    set_fpsr_exception(env);
> +}
> +
> +void HELPER(update_fpstatus)(CPUM68KState *env)
> +{
> +    int flags = get_float_exception_flags(&env->fp_status);
> +
> +    if (env->fpsr & FPSR_AE_IOP) {
> +        flags |= float_flag_invalid;
> +    } else {
> +        flags &= ~float_flag_invalid;
> +    }
> +    if (env->fpsr & FPSR_AE_DZ) {
> +        flags |= float_flag_divbyzero;
> +    } else {
> +        flags &= ~float_flag_divbyzero;
> +    }
> +    if (env->fpsr & FPSR_AE_OVFL) {
> +        flags |= float_flag_overflow;
> +    } else {
> +        flags &= ~float_flag_overflow;
> +    }
> +    if (env->fpsr & FPSR_AE_UNFL) {
> +        flags |= float_flag_underflow;
> +    } else {
> +        flags &= ~float_flag_underflow;
> +    }
> +    if (env->fpsr & FPSR_AE_INEX) {
> +        flags |= float_flag_inexact;
> +    } else {
> +        flags &= ~float_flag_inexact;
> +    }
> +
> +    set_float_exception_flags(flags, &env->fp_status);
>  }

What are you doing here?  This seems backward...


r~

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 08/16] target-m68k: define 96bit FP registers for gdb on 680x0
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 08/16] target-m68k: define 96bit FP registers for gdb on 680x0 Laurent Vivier
@ 2017-02-16  1:17   ` Richard Henderson
  0 siblings, 0 replies; 38+ messages in thread
From: Richard Henderson @ 2017-02-16  1:17 UTC (permalink / raw)
  To: Laurent Vivier, qemu-devel; +Cc: Aurelien Jarno

On 02/07/2017 11:59 AM, Laurent Vivier wrote:
> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
> ---
>  configure            |  2 +-
>  gdb-xml/m68k-fp.xml  | 21 +++++++++++++++++++++
>  target/m68k/helper.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 67 insertions(+), 1 deletion(-)
>  create mode 100644 gdb-xml/m68k-fp.xml

Reviewed-by: Richard Henderson <rth@twiddle.net>


r~

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 09/16] target-m68k: add fmovem
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 09/16] target-m68k: add fmovem Laurent Vivier
@ 2017-02-16  1:22   ` Richard Henderson
  0 siblings, 0 replies; 38+ messages in thread
From: Richard Henderson @ 2017-02-16  1:22 UTC (permalink / raw)
  To: Laurent Vivier, qemu-devel; +Cc: Aurelien Jarno

On 02/07/2017 11:59 AM, Laurent Vivier wrote:
> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
> ---
>  target/m68k/fpu_helper.c |  6 +++
>  target/m68k/helper.h     |  1 +
>  target/m68k/translate.c  | 99 +++++++++++++++++++++++++++++++++++-------------
>  3 files changed, 80 insertions(+), 26 deletions(-)
>
> diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
> index 1e68c41..aadfc82 100644
> --- a/target/m68k/fpu_helper.c
> +++ b/target/m68k/fpu_helper.c
> @@ -421,3 +421,9 @@ void HELPER(update_fpstatus)(CPUM68KState *env)
>
>      set_float_exception_flags(flags, &env->fp_status);
>  }
> +
> +void HELPER(fmovem)(CPUM68KState *env, uint32_t opsize,
> +                    uint32_t mode, uint32_t mask)
> +{
> +    fprintf(stderr, "MISSING HELPER fmovem\n");
> +}

Um... no.


> diff --git a/target/m68k/helper.h b/target/m68k/helper.h
> index 072a6d0..58bc273 100644
> --- a/target/m68k/helper.h
> +++ b/target/m68k/helper.h
> @@ -31,6 +31,7 @@ DEF_HELPER_1(cmp_FP0_FP1, void, env)
>  DEF_HELPER_2(set_fpcr, void, env, i32)
>  DEF_HELPER_1(tst_FP0, void, env)
>  DEF_HELPER_1(update_fpstatus, void, env)
> +DEF_HELPER_4(fmovem, void, env, i32, i32, i32)
>
>  DEF_HELPER_3(mac_move, void, env, i32, i32)
>  DEF_HELPER_3(macmulf, i64, env, i32, i32)
> diff --git a/target/m68k/translate.c b/target/m68k/translate.c
> index f9c64ff..ac60f1a 100644
> --- a/target/m68k/translate.c
> +++ b/target/m68k/translate.c
> @@ -4483,13 +4483,79 @@ static void gen_op_fmove_fcr(CPUM68KState *env, DisasContext *s,
>      tcg_temp_free_i32(addr);
>  }
>
> +static void gen_op_fmovem(CPUM68KState *env, DisasContext *s,
> +                          uint32_t insn, uint32_t ext)
> +{
> +    int opsize;
> +    uint16_t mask;
> +    int i;
> +    uint32_t mode;
> +    int32_t incr;
> +    TCGv addr, tmp;
> +    int is_load;
> +
> +    if (m68k_feature(s->env, M68K_FEATURE_FPU)) {
> +        opsize = OS_EXTENDED;
> +    } else {
> +        opsize = OS_DOUBLE;  /* FIXME */
> +    }
> +
> +    mode = (ext >> 11) & 0x3;
> +    if ((mode & 0x1) == 1) {
> +        gen_helper_fmovem(cpu_env, tcg_const_i32(opsize),
> +                          tcg_const_i32(mode), DREG(ext, 0));

... why not just raise illegal opcode here instead of fprintf.
You should also add a comment about not supporting the dynamic set.

That said... it almost seems easier to support fmovem as a helper than it does 
inline.  So perhaps just always implement it out of line?


r~

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 10/16] target-m68k: add fscc.
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 10/16] target-m68k: add fscc Laurent Vivier
@ 2017-02-16  1:27   ` Richard Henderson
  0 siblings, 0 replies; 38+ messages in thread
From: Richard Henderson @ 2017-02-16  1:27 UTC (permalink / raw)
  To: Laurent Vivier, qemu-devel; +Cc: Aurelien Jarno

On 02/07/2017 11:59 AM, Laurent Vivier wrote:
> +    addr = tcg_temp_local_new();
> +    tcg_gen_mov_i32(addr, taddr);
> +    l1 = gen_new_label();
> +    l2 = gen_new_label();
> +    gen_fjmpcc(s, ext & 0x3f, l1);
> +    gen_store(s, OS_BYTE, addr, tcg_const_i32(0x00));
> +    tcg_gen_br(l2);
> +    gen_set_label(l1);
> +    gen_store(s, OS_BYTE, addr, tcg_const_i32(0xff));
> +    gen_set_label(l2);
> +    tcg_temp_free(addr);

Use tcg_gen_setcond, like in scc.

> +    l1 = gen_new_label();
> +    tcg_gen_ori_i32(reg, reg, 0x000000ff);
> +    gen_fjmpcc(s, ext & 0x3f, l1);
> +    tcg_gen_andi_i32(reg, reg, 0xffffff00);
> +    gen_set_label(l1);

Likewise.


r~

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 11/16] target-m68k: add fmovecr
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 11/16] target-m68k: add fmovecr Laurent Vivier
@ 2017-02-16  1:28   ` Richard Henderson
  0 siblings, 0 replies; 38+ messages in thread
From: Richard Henderson @ 2017-02-16  1:28 UTC (permalink / raw)
  To: Laurent Vivier, qemu-devel; +Cc: Aurelien Jarno

On 02/07/2017 11:59 AM, Laurent Vivier wrote:
> fmovecr moves a floating point constant from the
> FPU ROM to a floating point register.
>
> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
> ---
>  target/m68k/fpu_helper.c | 31 +++++++++++++++++++++++++++++++
>  target/m68k/helper.h     |  1 +
>  target/m68k/translate.c  | 12 +++++++++++-
>  3 files changed, 43 insertions(+), 1 deletion(-)

Reviewed-by: Richard Henderson <rth@twiddle.net>


r~

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 12/16] target-m68k: add fscale, fgetman, fgetexp and fmod
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 12/16] target-m68k: add fscale, fgetman, fgetexp and fmod Laurent Vivier
@ 2017-02-16  1:34   ` Richard Henderson
  0 siblings, 0 replies; 38+ messages in thread
From: Richard Henderson @ 2017-02-16  1:34 UTC (permalink / raw)
  To: Laurent Vivier, qemu-devel; +Cc: Aurelien Jarno

On 02/07/2017 11:59 AM, Laurent Vivier wrote:
> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
> ---
>  target/m68k/cpu.h        |  1 +
>  target/m68k/fpu_helper.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++
>  target/m68k/helper.h     |  4 ++++
>  target/m68k/translate.c  | 14 ++++++++++++
>  4 files changed, 75 insertions(+)
>
> diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h
> index 7985dc3..3042ab7 100644
> --- a/target/m68k/cpu.h
> +++ b/target/m68k/cpu.h
> @@ -253,6 +253,7 @@ typedef enum {
>  /* Quotient */
>
>  #define FPSR_QT_MASK  0x00ff0000
> +#define FPSR_QT_SHIFT 16
>
>  /* Floating-Point Control Register */
>  /* Rounding mode */
> diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
> index d8145e0..42f5b5c 100644
> --- a/target/m68k/fpu_helper.c
> +++ b/target/m68k/fpu_helper.c
> @@ -458,3 +458,59 @@ void HELPER(const_FP0)(CPUM68KState *env, uint32_t offset)
>      env->fp0l = fpu_rom[offset].low;
>      env->fp0h = fpu_rom[offset].high;
>  }
> +
> +void HELPER(getexp_FP0)(CPUM68KState *env)
> +{
> +    int32_t exp;
> +    floatx80 res;
> +
> +    res = FP0_to_floatx80(env);
> +    if (floatx80_is_zero_or_denormal(res) || floatx80_is_any_nan(res) ||
> +        floatx80_is_infinity(res)) {
> +        return;
> +    }
> +    exp = (env->fp0h & 0x7fff) - 0x3fff;
> +
> +    res = int32_to_floatx80(exp, &env->fp_status);
> +
> +    floatx80_to_FP0(env, res);

Failure to raise OPERR for infinities?

> +void HELPER(getman_FP0)(CPUM68KState *env)
> +{
> +    floatx80 res;
> +    res = int64_to_floatx80(env->fp0l, &env->fp_status);
> +    floatx80_to_FP0(env, res);
> +}

This seems completely wrong.  (1) NaN gets returned, (2) Inf raises OPERR, (3) 
Normal values return something in the range [1.0 ... 2.0).  Which means you 
should just force the exponent rather than convert the low part.

> +
> +void HELPER(scale_FP0_FP1)(CPUM68KState *env)
> +{
> +    int32_t scale;
> +    int32_t exp;
> +
> +    scale = floatx80_to_int32(FP0_to_floatx80(env), &env->fp_status);
> +
> +    exp = (env->fp1h & 0x7fff) + scale;
> +
> +    env->fp0h = (env->fp1h & 0x8000) | (exp & 0x7fff);
> +    env->fp0l = env->fp1l;
> +}

Missing handling for NaN, Inf, 0, denormal.


r~

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 13/16] target-m68k: add fsglmul and fsgldiv
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 13/16] target-m68k: add fsglmul and fsgldiv Laurent Vivier
@ 2017-02-16  1:36   ` Richard Henderson
  0 siblings, 0 replies; 38+ messages in thread
From: Richard Henderson @ 2017-02-16  1:36 UTC (permalink / raw)
  To: Laurent Vivier, qemu-devel; +Cc: Aurelien Jarno

On 02/07/2017 11:59 AM, Laurent Vivier wrote:
> fsglmul and fsgldiv truncate data to single precision before computing
> results.
>
> Signed-off-by: Laurent Vivier <laurent@vivier.eu>
> ---
>  target/m68k/fpu_helper.c | 22 ++++++++++++++++++++++
>  target/m68k/helper.h     |  2 ++
>  target/m68k/translate.c  |  8 ++++++++
>  3 files changed, 32 insertions(+)
>
> diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
> index 42f5b5c..8a3eed3 100644
> --- a/target/m68k/fpu_helper.c
> +++ b/target/m68k/fpu_helper.c
> @@ -351,6 +351,17 @@ void HELPER(mul_FP0_FP1)(CPUM68KState *env)
>      floatx80_to_FP0(env, res);
>  }
>
> +void HELPER(sglmul_FP0_FP1)(CPUM68KState *env)
> +{
> +    float64 a, b, res;
> +
> +    a = floatx80_to_float64(FP0_to_floatx80(env), &env->fp_status);
> +    b = floatx80_to_float64(FP1_to_floatx80(env), &env->fp_status);

s/float64/float32/g

Kinda sorta, probably close enough.  The manual says the resulting exponent may 
be out of range.  Which means this will produce +Inf in cases HW won't.


r~

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 14/16] target-m68k: add explicit single and double precision operations
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 14/16] target-m68k: add explicit single and double precision operations Laurent Vivier
@ 2017-02-16  1:41   ` Richard Henderson
  0 siblings, 0 replies; 38+ messages in thread
From: Richard Henderson @ 2017-02-16  1:41 UTC (permalink / raw)
  To: Laurent Vivier, qemu-devel; +Cc: Aurelien Jarno

On 02/07/2017 11:59 AM, Laurent Vivier wrote:
> +    case 0: /* fmove */
> +        break;
> +    case 0x40: /* fsmove */
> +        gen_helper_redf32_FP0(cpu_env);
> +        gen_helper_extf32_FP0(cpu_env);
> +        break;
> +    case 0x44: /* fdmove */
> +        gen_helper_redf64_FP0(cpu_env);
> +        gen_helper_extf64_FP0(cpu_env);
>          break;

This is going to produce double-rounding errors.  Better to properly set the 
rounding precision first and convert once.


r~

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 15/16] target-m68k: add more FPU instructions
  2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 15/16] target-m68k: add more FPU instructions Laurent Vivier
@ 2017-02-16  1:46   ` Richard Henderson
  2017-02-16 10:18     ` Andreas Schwab
  0 siblings, 1 reply; 38+ messages in thread
From: Richard Henderson @ 2017-02-16  1:46 UTC (permalink / raw)
  To: Laurent Vivier, qemu-devel; +Cc: Aurelien Jarno

On 02/07/2017 11:59 AM, Laurent Vivier wrote:
> +static long double floatx80_to_ldouble(floatx80 val)
> +{
> +    if (floatx80_is_infinity(val)) {
> +            if (floatx80_is_neg(val)) {
> +                    return -__builtin_infl();
> +            }
> +            return __builtin_infl();
> +    }
> +    if (floatx80_is_any_nan(val)) {
> +            char low[20];
> +            sprintf(low, "0x%016"PRIx64, val.low);
> +
> +            return nanl(low);
> +    }
> +
> +    return *(long double *)&val;
> +}

This doesn't work except for x86 host.

You ought to extract the mantissa, convert the 64-bit value to long-double, and 
use ldexpl to scale the result for the exponent.

Similarly converting the other way use frexpl and ldexpl.


r~

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 15/16] target-m68k: add more FPU instructions
  2017-02-16  1:46   ` Richard Henderson
@ 2017-02-16 10:18     ` Andreas Schwab
  2017-02-16 21:01       ` Richard Henderson
  0 siblings, 1 reply; 38+ messages in thread
From: Andreas Schwab @ 2017-02-16 10:18 UTC (permalink / raw)
  To: Richard Henderson; +Cc: Laurent Vivier, qemu-devel, Aurelien Jarno

On Feb 16 2017, Richard Henderson <rth@twiddle.net> wrote:

> On 02/07/2017 11:59 AM, Laurent Vivier wrote:
>> +static long double floatx80_to_ldouble(floatx80 val)
>> +{
>> +    if (floatx80_is_infinity(val)) {
>> +            if (floatx80_is_neg(val)) {
>> +                    return -__builtin_infl();
>> +            }
>> +            return __builtin_infl();
>> +    }
>> +    if (floatx80_is_any_nan(val)) {
>> +            char low[20];
>> +            sprintf(low, "0x%016"PRIx64, val.low);
>> +
>> +            return nanl(low);
>> +    }
>> +
>> +    return *(long double *)&val;
>> +}
>
> This doesn't work except for x86 host.

Not even then.

> You ought to extract the mantissa, convert the 64-bit value to
> long-double, and use ldexpl to scale the result for the exponent.
>
> Similarly converting the other way use frexpl and ldexpl.

There is no guarantee that the host long double has the same range and
precision as floatx80.

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 15/16] target-m68k: add more FPU instructions
  2017-02-16 10:18     ` Andreas Schwab
@ 2017-02-16 21:01       ` Richard Henderson
  2017-02-17  9:06         ` Andreas Schwab
  0 siblings, 1 reply; 38+ messages in thread
From: Richard Henderson @ 2017-02-16 21:01 UTC (permalink / raw)
  To: Andreas Schwab; +Cc: Laurent Vivier, qemu-devel, Aurelien Jarno

On 02/16/2017 09:18 PM, Andreas Schwab wrote:
> There is no guarantee that the host long double has the same range and
> precision as floatx80.

Indeed not.  However, do you have another plan for implementing the 
trancendentals?  I'm quite happy using the host long double libm as an 
estimate.  Which is better than at least BasiliskII, which uses the host double.


r~

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [Qemu-devel] [PATCH v3 15/16] target-m68k: add more FPU instructions
  2017-02-16 21:01       ` Richard Henderson
@ 2017-02-17  9:06         ` Andreas Schwab
  0 siblings, 0 replies; 38+ messages in thread
From: Andreas Schwab @ 2017-02-17  9:06 UTC (permalink / raw)
  To: Richard Henderson; +Cc: Laurent Vivier, qemu-devel, Aurelien Jarno

On Feb 17 2017, Richard Henderson <rth@twiddle.net> wrote:

> On 02/16/2017 09:18 PM, Andreas Schwab wrote:
>> There is no guarantee that the host long double has the same range and
>> precision as floatx80.
>
> Indeed not.  However, do you have another plan for implementing the
> trancendentals?  I'm quite happy using the host long double libm as an
> estimate.  Which is better than at least BasiliskII, which uses the host
> double.

In ARAnyM, I implemented a software emulation using mpfr (but it is
still incomplete in many ways).  I don't know if something like this
would be an option for qemu.

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."

^ permalink raw reply	[flat|nested] 38+ messages in thread

end of thread, other threads:[~2017-02-17  9:06 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-07  0:59 [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU Laurent Vivier
2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 01/16] softfloat: define 680x0 specific values Laurent Vivier
2017-02-08 21:30   ` Richard Henderson
2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 02/16] softloat: disable floatx80_invalid_encoding() for m68k Laurent Vivier
2017-02-08 21:32   ` Richard Henderson
2017-02-08 22:58   ` Peter Maydell
2017-02-09  8:07     ` Laurent Vivier
2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 03/16] target-m68k: move FPU helpers to fpu_helper.c Laurent Vivier
2017-02-08 21:33   ` Richard Henderson
2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 04/16] target-m68k: define ext_opsize Laurent Vivier
2017-02-08 21:33   ` Richard Henderson
2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 05/16] target-m68k: use floatx80 internally Laurent Vivier
2017-02-15 22:59   ` Richard Henderson
2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 06/16] target-m68k: add FPCR and FPSR Laurent Vivier
2017-02-16  1:10   ` Richard Henderson
2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 07/16] target-m68k: manage FPU exceptions Laurent Vivier
2017-02-16  1:16   ` Richard Henderson
2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 08/16] target-m68k: define 96bit FP registers for gdb on 680x0 Laurent Vivier
2017-02-16  1:17   ` Richard Henderson
2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 09/16] target-m68k: add fmovem Laurent Vivier
2017-02-16  1:22   ` Richard Henderson
2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 10/16] target-m68k: add fscc Laurent Vivier
2017-02-16  1:27   ` Richard Henderson
2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 11/16] target-m68k: add fmovecr Laurent Vivier
2017-02-16  1:28   ` Richard Henderson
2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 12/16] target-m68k: add fscale, fgetman, fgetexp and fmod Laurent Vivier
2017-02-16  1:34   ` Richard Henderson
2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 13/16] target-m68k: add fsglmul and fsgldiv Laurent Vivier
2017-02-16  1:36   ` Richard Henderson
2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 14/16] target-m68k: add explicit single and double precision operations Laurent Vivier
2017-02-16  1:41   ` Richard Henderson
2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 15/16] target-m68k: add more FPU instructions Laurent Vivier
2017-02-16  1:46   ` Richard Henderson
2017-02-16 10:18     ` Andreas Schwab
2017-02-16 21:01       ` Richard Henderson
2017-02-17  9:06         ` Andreas Schwab
2017-02-07  0:59 ` [Qemu-devel] [PATCH v3 16/16] target-m68k: add fsincos Laurent Vivier
2017-02-07  1:25 ` [Qemu-devel] [PATCH v3 00/16] target-m68k: implement 680x0 FPU no-reply

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.