@@ -349,6 +349,25 @@ typedef enum __attribute__((__packed__)) {
* and using them as inputs to a float op will raise Invalid.
*/
floatx80_unnormal_valid = 8,
+
+ /*
+ * If the exponent is 0 and the Integer bit is set, Intel call
+ * this a "pseudo-denormal"; x86 supports that only on input
+ * (treating them as denormals by ignoring the Integer bit).
+ * For m68k, the integer bit is considered validly part of the
+ * input value when the exponent is 0, and may be 0 or 1,
+ * giving extra range. They may also be generated as outputs.
+ * (The m68k manual actually calls these values part of the
+ * normalized number range, not the denormalized number range.)
+ *
+ * By default you get the Intel behaviour where the Integer
+ * bit is ignored; if this is set then the Integer bit value
+ * is honoured, m68k-style.
+ *
+ * Either way, floatx80_invalid_encoding() will always accept
+ * pseudo-denormals.
+ */
+ floatx80_pseudo_denormal_valid = 16,
} FloatX80Behaviour;
/*
@@ -537,7 +537,8 @@ typedef struct {
* round_mask: bits below lsb which must be rounded
* The following optional modifiers are available:
* arm_althp: handle ARM Alternative Half Precision
- * m68k_denormal: explicit integer bit for extended precision may be 1
+ * has_explicit_bit: has an explicit integer bit; this affects whether
+ * the float_status floatx80_behaviour handling applies
*/
typedef struct {
int exp_size;
@@ -547,7 +548,7 @@ typedef struct {
int frac_size;
int frac_shift;
bool arm_althp;
- bool m68k_denormal;
+ bool has_explicit_bit;
uint64_t round_mask;
} FloatFmt;
@@ -600,9 +601,7 @@ static const FloatFmt floatx80_params[3] = {
[floatx80_precision_d] = { FLOATX80_PARAMS(52) },
[floatx80_precision_x] = {
FLOATX80_PARAMS(64),
-#ifdef TARGET_M68K
- .m68k_denormal = true,
-#endif
+ .has_explicit_bit = true,
},
};
@@ -139,7 +139,8 @@ static void m68k_cpu_reset_hold(Object *obj, ResetType type)
set_floatx80_behaviour(floatx80_default_inf_int_bit_is_zero |
floatx80_pseudo_inf_valid |
floatx80_pseudo_nan_valid |
- floatx80_unnormal_valid,
+ floatx80_unnormal_valid |
+ floatx80_pseudo_denormal_valid,
&env->fp_status);
nan = floatx80_default_nan(&env->fp_status);
@@ -195,6 +195,25 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b,
static void partsN(canonicalize)(FloatPartsN *p, float_status *status,
const FloatFmt *fmt)
{
+ /*
+ * It's target-dependent how to handle the case of exponent 0
+ * and Integer bit set. Intel calls these "pseudodenormals",
+ * and treats them as if the integer bit was 0, and never
+ * produces them on output. This is the default behaviour for QEMU.
+ * For m68k, the integer bit is considered validly part of the
+ * input value when the exponent is 0, and may be 0 or 1,
+ * giving extra range. They may also be generated as outputs.
+ * (The m68k manual actually calls these values part of the
+ * normalized number range, not the denormalized number range,
+ * but that distinction is not important for us, because
+ * m68k doesn't care about the input_denormal_used status flag.)
+ * floatx80_pseudo_denormal_valid selects the m68k behaviour,
+ * which changes both how we canonicalize such a value and
+ * how we uncanonicalize results.
+ */
+ bool has_pseudo_denormals = fmt->has_explicit_bit &&
+ (status->floatx80_behaviour & floatx80_pseudo_denormal_valid);
+
if (unlikely(p->exp == 0)) {
if (likely(frac_eqz(p))) {
p->cls = float_class_zero;
@@ -206,7 +225,7 @@ static void partsN(canonicalize)(FloatPartsN *p, float_status *status,
int shift = frac_normalize(p);
p->cls = float_class_denormal;
p->exp = fmt->frac_shift - fmt->exp_bias
- - shift + !fmt->m68k_denormal;
+ - shift + !has_pseudo_denormals;
}
} else if (likely(p->exp < fmt->exp_max) || fmt->arm_althp) {
p->cls = float_class_normal;
@@ -342,13 +361,15 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
frac_clear(p);
} else {
bool is_tiny = s->tininess_before_rounding || exp < 0;
+ bool has_pseudo_denormals = fmt->has_explicit_bit &&
+ (s->floatx80_behaviour & floatx80_pseudo_denormal_valid);
if (!is_tiny) {
FloatPartsN discard;
is_tiny = !frac_addi(&discard, p, inc);
}
- frac_shrjam(p, !fmt->m68k_denormal - exp);
+ frac_shrjam(p, !has_pseudo_denormals - exp);
if (p->frac_lo & round_mask) {
/* Need to recompute round-to-even/round-to-odd. */
@@ -379,7 +400,7 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
p->frac_lo &= ~round_mask;
}
- exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) && !fmt->m68k_denormal;
+ exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) && !has_pseudo_denormals;
frac_shr(p, frac_shift);
if (is_tiny) {