executor/src/floatnext.c

221 lines
5.3 KiB
C

/* Copyright 1996 by Abacus Research and
* Development, Inc. All rights reserved.
*/
#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_floatnext[] =
"$Id: floatnext.c 63 2004-12-24 18:19:43Z ctm $";
#endif
#include "rsys/common.h"
#include "SANE.h"
#include "rsys/float.h"
#include "rsys/floatconv.h"
#if defined (NEXT)
#define INLINE_MATH
#endif
/* Subtracts one from the given multi-byte big endian unsigned number. */
static void
mp_decrement_big_endian (uint8 *bytes, int num_bytes)
{
boolean_t borrow_p;
uint8 *b;
borrow_p = TRUE;
for (b = bytes + num_bytes; borrow_p && b > bytes; )
{
const uint8 v = *--b;
borrow_p = (v == 0);
*b = v - 1;
}
}
/* Adds one to the given multi-byte big endian unsigned number. */
static void
mp_increment_big_endian (uint8 *bytes, int num_bytes)
{
boolean_t carry_p;
uint8 *b;
carry_p = TRUE;
for (b = bytes + num_bytes; carry_p && b > bytes; )
{
/* Equivalent to "carry_p = !++*--b;" Heh. */
const uint8 v = *--b + 1;
carry_p = (v == 0);
*b = v;
}
}
/* Replaces x with the nearest number to x in the direction of y.
*
* This implementation is not exactly right, but it should be good
* enough for Executor 2. In particular, it doesn't handle
* denormalized numbers well and it doesn't signal exceptions.
*
* Note that most SANE traps replace the dst operand with the
* result. This one replaces the src operand (really!)
*/
P_SAVED0D1A0A1_3 (PUBLIC pascal trap, void, ROMlib_FnextX,
uint8 *, x, uint8 *, y, unsigned short, sel)
{
CCRElement saved_ccc, saved_ccn, saved_ccv, saved_ccnz, saved_ccx;
INTEGER x_class, y_class, x_class_swapped, y_class_swapped;
x80_t x80_x;
int byte_size;
int x_sign, y_sign;
boolean_t normalize_x80_p;
ieee_t xv, yv;
saved_ccnz = cpu_state.ccnz;
saved_ccn = cpu_state.ccn;
saved_ccc = cpu_state.ccc;
saved_ccv = cpu_state.ccv;
saved_ccx = cpu_state.ccx;
/* Determine the classes of both X and Y. */
ROMlib_Fclassx (x, &x_class_swapped, sel);
x_class = CW (x_class_swapped);
ROMlib_Fclassx (y, &y_class_swapped, sel);
y_class = CW (y_class_swapped);
normalize_x80_p = FALSE; /* default, avoid gcc warnings. */
switch (sel & OPCODE_MASK)
{
case FX_OPERAND:
xv = x80_to_ieee ((const x80_t *) x);
yv = x80_to_ieee ((const x80_t *) y);
normalize_x80_p = ((x[2] & 0x80) != 0);
byte_size = 10;
break;
case FD_OPERAND:
xv = f64_to_ieee ((const f64_t *) x);
yv = f64_to_ieee ((const f64_t *) y);
byte_size = 8;
break;
case FS_OPERAND:
xv = f32_to_ieee ((const f32_t *) x);
yv = f32_to_ieee ((const f32_t *) y);
byte_size = 4;
break;
case FI_OPERAND:
xv = CW (*(short *)x);
yv = CW (*(short *)y);
byte_size = 2;
break;
case FL_OPERAND:
xv = CL(*(long *)x);
yv = CL(*(long *)y);
byte_size = 4;
break;
case FC_OPERAND:
xv = comp_to_ieee ((const comp_t *) x);
yv = comp_to_ieee ((const comp_t *) y);
byte_size = 8;
break;
default:
gui_abort ();
xv = yv = 0; /* avoid gcc warnings */
goto done;
}
/* If either number is a NaN, the result is a NaN. Technically it
* should be a quiet NaN and we should signal if a signaling
* NaN made it here, but we don't do that yet.
*/
if (y_class == SNaN || y_class == QNaN)
{
memcpy (x, y, byte_size);
goto done;
}
if (x_class == SNaN || x_class == QNaN)
goto done;
/* Compare x and y. */
ROMlib_Fx2X (x, &x80_x, sel & OPCODE_MASK);
ROMlib_Fcmpx (y, &x80_x, sel & OPCODE_MASK);
/* If x == y, just return x unchanged. */
if (!cpu_state.ccnz)
goto done;
/* Fetch the sign of x and y. */
x_sign = x[0] & 0x80;
y_sign = y[0] & 0x80;
if (x_class == ZeroNum)
{
memset (x, 0, byte_size);
x[0] = y_sign;
x[byte_size - 1] |= 1; /* smallest normalized number. */
}
else /* x != 0 */
{
if (!cpu_state.ccn ^ !x_sign)
mp_increment_big_endian (x, byte_size);
else
{
if (normalize_x80_p)
x[2] &= 0x7F; /* make sure borrow goes through normalize bit */
mp_decrement_big_endian (x, byte_size);
}
/* Restore the sign in case of carry. */
x[0] = (x[0] & 0x7F) | x_sign;
}
/* Restore normalize bit, which may have gotten carried through or
* borrowed through, etc.
*/
if (normalize_x80_p)
x[2] |= 0x80;
done:
#if ERROR_SUPPORTED_P (ERROR_FLOATING_POINT)
{
ieee_t result;
switch (sel & OPCODE_MASK)
{
case FX_OPERAND:
result = x80_to_ieee ((const x80_t *) x);
break;
case FD_OPERAND:
result = f64_to_ieee ((const f64_t *) x);
break;
case FS_OPERAND:
result = f32_to_ieee ((const f32_t *) x);
break;
case FI_OPERAND:
result = CW (*(short *)x);
break;
case FL_OPERAND:
result = CL (*(long *)x);
break;
case FC_OPERAND:
result = comp_to_ieee ((const comp_t *) x);
break;
default:
gui_abort ();
result = 0;
}
warning_floating_point ("nextafter(%.30f, %.30f) == %.30f",
(double) xv, (double) yv, (double) result);
}
#endif /* ERROR_SUPPORTED_P (ERROR_FLOATING_POINT) */
cpu_state.ccnz = saved_ccnz;
cpu_state.ccn = saved_ccn;
cpu_state.ccc = saved_ccc;
cpu_state.ccv = saved_ccv;
cpu_state.ccx = saved_ccx;
}