Initial softfloat port from Bochs to 86box, currently selectable only on QT.

This commit is contained in:
TC1995
2023-04-29 18:56:57 +02:00
parent 071c05e65f
commit 7a53e1de45
44 changed files with 16934 additions and 115 deletions

View File

@@ -15,6 +15,7 @@
#include "x86_ops.h"
#include "x87.h"
#include "386_common.h"
#include "softfloat/softfloat-specialize.h"
uint32_t x87_pc_off, x87_op_off;
uint16_t x87_pc_seg, x87_op_seg;
@@ -37,11 +38,6 @@ fpu_log(const char *fmt, ...)
# define fpu_log(fmt, ...)
#endif
#define X87_TAG_VALID 0
#define X87_TAG_ZERO 1
#define X87_TAG_INVALID 2
#define X87_TAG_EMPTY 3
#ifdef USE_NEW_DYNAREC
uint16_t
x87_gettag(void)
@@ -110,6 +106,340 @@ x87_settag(uint16_t new_tag)
}
#endif
static floatx80
FPU_handle_NaN32_Func(floatx80 a, int aIsNaN, float32 b32, int bIsNaN, struct float_status_t *status)
{
int aIsSignalingNaN = floatx80_is_signaling_nan(a);
int bIsSignalingNaN = float32_is_signaling_nan(b32);
if (aIsSignalingNaN | bIsSignalingNaN)
float_raise(status, float_flag_invalid);
// propagate QNaN to SNaN
a = propagateFloatx80NaNOne(a, status);
if (aIsNaN & !bIsNaN) return a;
// float32 is NaN so conversion will propagate SNaN to QNaN and raise
// appropriate exception flags
floatx80 b = float32_to_floatx80(b32, status);
if (aIsSignalingNaN) {
if (bIsSignalingNaN) goto returnLargerSignificand;
return bIsNaN ? b : a;
}
else if (aIsNaN) {
if (bIsSignalingNaN) return a;
returnLargerSignificand:
if (a.fraction < b.fraction) return b;
if (b.fraction < a.fraction) return a;
return (a.exp < b.exp) ? a : b;
}
else {
return b;
}
}
int
FPU_handle_NaN32(floatx80 a, float32 b, floatx80 *r, struct float_status_t *status)
{
const floatx80 floatx80_default_nan = packFloatx80(0, floatx80_default_nan_exp, floatx80_default_nan_fraction);
if (floatx80_is_unsupported(a)) {
float_raise(status, float_flag_invalid);
*r = floatx80_default_nan;
return 1;
}
int aIsNaN = floatx80_is_nan(a), bIsNaN = float32_is_nan(b);
if (aIsNaN | bIsNaN) {
*r = FPU_handle_NaN32_Func(a, aIsNaN, b, bIsNaN, status);
return 1;
}
return 0;
}
static floatx80
FPU_handle_NaN64_Func(floatx80 a, int aIsNaN, float64 b64, int bIsNaN, struct float_status_t *status)
{
int aIsSignalingNaN = floatx80_is_signaling_nan(a);
int bIsSignalingNaN = float64_is_signaling_nan(b64);
if (aIsSignalingNaN | bIsSignalingNaN)
float_raise(status, float_flag_invalid);
// propagate QNaN to SNaN
a = propagateFloatx80NaNOne(a, status);
if (aIsNaN & !bIsNaN) return a;
// float64 is NaN so conversion will propagate SNaN to QNaN and raise
// appropriate exception flags
floatx80 b = float64_to_floatx80(b64, status);
if (aIsSignalingNaN) {
if (bIsSignalingNaN) goto returnLargerSignificand;
return bIsNaN ? b : a;
}
else if (aIsNaN) {
if (bIsSignalingNaN) return a;
returnLargerSignificand:
if (a.fraction < b.fraction) return b;
if (b.fraction < a.fraction) return a;
return (a.exp < b.exp) ? a : b;
}
else {
return b;
}
}
int
FPU_handle_NaN64(floatx80 a, float64 b, floatx80 *r, struct float_status_t *status)
{
const floatx80 floatx80_default_nan = packFloatx80(0, floatx80_default_nan_exp, floatx80_default_nan_fraction);
if (floatx80_is_unsupported(a)) {
float_raise(status, float_flag_invalid);
*r = floatx80_default_nan;
return 1;
}
int aIsNaN = floatx80_is_nan(a), bIsNaN = float64_is_nan(b);
if (aIsNaN | bIsNaN) {
*r = FPU_handle_NaN64_Func(a, aIsNaN, b, bIsNaN, status);
return 1;
}
return 0;
}
struct float_status_t
i387cw_to_softfloat_status_word(uint16_t control_word)
{
struct float_status_t status;
int precision = control_word & FPU_CW_PC;
switch (precision) {
case FPU_PR_32_BITS:
status.float_rounding_precision = 32;
break;
case FPU_PR_64_BITS:
status.float_rounding_precision = 64;
break;
case FPU_PR_80_BITS:
status.float_rounding_precision = 80;
break;
default:
/* With the precision control bits set to 01 "(reserved)", a
real CPU behaves as if the precision control bits were
set to 11 "80 bits" */
status.float_rounding_precision = 80;
break;
}
status.float_exception_flags = 0; // clear exceptions before execution
status.float_nan_handling_mode = float_first_operand_nan;
status.float_rounding_mode = (control_word & FPU_CW_RC) >> 10;
status.flush_underflow_to_zero = 0;
status.float_suppress_exception = 0;
status.float_exception_masks = control_word & FPU_CW_Exceptions_Mask;
status.denormals_are_zeros = 0;
return status;
}
int
FPU_status_word_flags_fpu_compare(int float_relation)
{
switch (float_relation) {
case float_relation_unordered:
return (C0 | C2 | C3);
case float_relation_greater:
return (0);
case float_relation_less:
return (C0);
case float_relation_equal:
return (C3);
}
return (-1); // should never get here
}
void
FPU_write_eflags_fpu_compare(int float_relation)
{
switch (float_relation) {
case float_relation_unordered:
cpu_state.flags |= (Z_FLAG | P_FLAG | C_FLAG);
break;
case float_relation_greater:
break;
case float_relation_less:
cpu_state.flags |= (C_FLAG);
break;
case float_relation_equal:
cpu_state.flags |= (Z_FLAG);
break;
default:
break;
}
}
uint16_t
FPU_exception(uint32_t fetchdat, uint16_t exceptions, int store)
{
uint16_t status;
uint16_t unmasked;
/* Extract only the bits which we use to set the status word */
exceptions &= FPU_SW_Exceptions_Mask;
status = fpu_state.swd;
unmasked = (exceptions & ~fpu_state.cwd) & FPU_CW_Exceptions_Mask;
// if IE or DZ exception happen nothing else will be reported
if (exceptions & (FPU_EX_Invalid | FPU_EX_Zero_Div)) {
unmasked &= (FPU_EX_Invalid | FPU_EX_Zero_Div);
}
/* Set summary bits if exception isn't masked */
if (unmasked) {
fpu_state.swd |= (FPU_SW_Summary | FPU_SW_Backward);
}
if (exceptions & FPU_EX_Invalid) {
// FPU_EX_Invalid cannot come with any other exception but x87 stack fault
fpu_state.swd |= exceptions;
if (exceptions & FPU_SW_Stack_Fault) {
if (!(exceptions & C1)) {
/* This bit distinguishes over- from underflow for a stack fault,
and roundup from round-down for precision loss. */
fpu_state.swd &= ~C1;
}
}
return unmasked;
}
if (exceptions & FPU_EX_Zero_Div) {
fpu_state.swd |= FPU_EX_Zero_Div;
if (!(fpu_state.cwd & FPU_EX_Zero_Div)) {
#ifdef FPU_8087
if (!(fpu_state.cwd & FPU_SW_Summary)) {
fpu_state.cwd |= FPU_SW_Summary;
nmi = 1;
}
#else
picint(1 << 13);
#endif // FPU_8087
}
return unmasked;
}
if (exceptions & FPU_EX_Denormal) {
fpu_state.swd |= FPU_EX_Denormal;
if (unmasked & FPU_EX_Denormal) {
return (unmasked & FPU_EX_Denormal);
}
}
/* Set the corresponding exception bits */
fpu_state.swd |= exceptions;
if (exceptions & FPU_EX_Precision) {
if (!(exceptions & C1)) {
/* This bit distinguishes over- from underflow for a stack fault,
and roundup from round-down for precision loss. */
fpu_state.swd &= ~C1;
}
}
// If #P unmasked exception occurred the result still has to be
// written to the destination.
unmasked &= ~FPU_EX_Precision;
if (unmasked & (FPU_EX_Underflow | FPU_EX_Overflow)) {
// If unmasked over- or underflow occurs and dest is a memory location:
// - the TOS and destination operands remain unchanged
// - the inexact-result condition is not reported and C1 flag is cleared
// - no result is stored in the memory
// If the destination is in the register stack, adjusted resulting value
// is stored in the destination operand.
if (!store)
unmasked &= ~(FPU_EX_Underflow | FPU_EX_Overflow);
else {
fpu_state.swd &= ~C1;
if (!(status & FPU_EX_Precision))
fpu_state.swd &= ~FPU_EX_Precision;
}
}
return unmasked;
}
void
FPU_stack_overflow(uint32_t fetchdat)
{
const floatx80 floatx80_default_nan = packFloatx80(0, floatx80_default_nan_exp, floatx80_default_nan_fraction);
/* The masked response */
if (is_IA_masked()) {
FPU_push();
FPU_save_regi(floatx80_default_nan, 0);
}
FPU_exception(fetchdat, FPU_EX_Stack_Overflow, 0);
}
void
FPU_stack_underflow(uint32_t fetchdat, int stnr, int pop_stack)
{
const floatx80 floatx80_default_nan = packFloatx80(0, floatx80_default_nan_exp, floatx80_default_nan_fraction);
/* The masked response */
if (is_IA_masked()) {
FPU_save_regi(floatx80_default_nan, stnr);
if (pop_stack)
FPU_pop();
}
FPU_exception(fetchdat, FPU_EX_Stack_Underflow, 0);
}
/* -----------------------------------------------------------
* Slimmed down version used to compile against a CPU simulator
* rather than a kernel (ported by Kevin Lawton)
* ------------------------------------------------------------ */
int
FPU_tagof(const floatx80 reg)
{
int32_t exp = floatx80_exp(reg);
if (exp == 0) {
if (!floatx80_fraction(reg))
return X87_TAG_ZERO;
/* The number is a de-normal or pseudodenormal. */
return X87_TAG_INVALID;
}
if (exp == 0x7fff) {
/* Is an Infinity, a NaN, or an unsupported data type. */
return X87_TAG_INVALID;
}
if (!(reg.fraction & BX_CONST64(0x8000000000000000))) {
/* Unsupported data type. */
/* Valid numbers have the ms bit set to 1. */
return X87_TAG_INVALID;
}
return X87_TAG_VALID;
}
#ifdef ENABLE_808X_LOG
void
x87_dumpregs(void)