/ nextaft.s (emx+gcc) -- Copyright (c) 1996-1998 by Eberhard Mattes

#include <emx/asm386.h>

#define FUNC    MATHSUFFIX2(nextafter)

        .globl  LABEL(FUNC)

        .text

        ALIGN

#if defined (LONG_DOUBLE)

/ long double nextafterl (long double x, long double y)

#define x(i)    4+i(%esp)
#define y(i)    16+i(%esp)

LABEL(FUNC):
        PROFILE_NOFRAME
        fldt    x(0)
        fldt    y(0)
        fucompp
        fstsww  %ax
        testb   $0x04, %ah
        jnz     Lunordered              /* Unordered (at least one NaN) */
        testb   $0x40, %ah
        jnz     Leq                     /* x==y */
        movb    x(9), %al
        shrb    $7, %al                 /* signbit(x) -> bit 0 of %al */
        xorb    %ah, %al                /* Note: bit 0 of %ah is 1 iff x>y */
        testb   $0x01, %al
        movw    x(8), %ax               /* Fetch exponent */
        jnz     Ldown                   /* Decrement mantissa */

/* Increment mantissa. */

Lup:    andl    $0x7fff, %eax           /* Clear sign bit */
        cmpl    $0x7fff, %eax           /* Infinity? */
        je      Lreturn_x               /* Yes => return x */
        addl    $1, x(0)                /* Increment lower word */
        adcl    $0, x(4)                /* Propagate carry into upper word */
        jc      Lup_of                  /* Overflow */
        testl   %eax, %eax              /* Denormal? */
        jnz     Lreturn_x               /* No => return x */
        testb   $0x80, x(7)             /* Pseudo-denormal? */
        jz      Lreturn_x_exception     /* No => return x, raise exceptions */
        incw    x(8)                    /* Turn it into a normal */
        jmp     Lreturn_x

/* Return x and raise appropriate exceptions (inexact and
   underflow/overflow). */

        ALIGN
Lreturn_x_exception:
        fldt    x(0)
        fstl    y(0)                    /* This raises the exceptions */
        jmp     Lreturn                 /* (we store as 64-bit number!) */

/* Mantissa overflow.  Increment exponent. */

        ALIGN
Lup_of: cmpl    $0x7ffe, %eax           /* Exponent overflow? */
        je      Lup_make_inf            /* Yes => return infinity */
        movb    $0x80, x(7)             /* Set mantissa to 100...000 */
        incw    x(8)                    /* Increment exponent */
        jmp     Lreturn_x               /* Return x */

/* x is LDBL_MAX, raise overflow and inexact exceptions,
   return infinity. */

        ALIGN
Lup_make_inf:
        movl    $0xffffffff, x(0)       /* Restore mantissa of x */
        movl    $0xffffffff, x(4)
        fldt    x(0)                    /* Compute x+x */
        fadd    %st
        jmp     Lreturn

/* Decrement mantissa. */

        ALIGN
Ldown:  andl    $0x7fff, %eax           /* Clear sign bit */
        cmpl    $0x7fff, %eax           /* Infinity? */
        je      Ldown_is_inf            /* Yes => return biggest normal */
        subl    $1, x(0)                /* Decrement lower word */
        sbbl    $0, x(4)                /* Propagate carry into upper word */
        jc      Ldown_uf0               /* Underflow (integer bit was 0) */
        jo      Ldown_uf1               /* Underflow (integer bit was 1) */
        test    %eax, %eax              /* Is the exponent zero? */
        jnz     Lreturn_x               /* No => return x */
        jmp     Lreturn_x_exception     /* Yes => maybe raise exceptions */

/* Underflow (mantissa was 100...000 and is now 011...111).  Decrement
   the exponent.  Return the biggest denormal if the exponent becomes 0. */

        ALIGN
Ldown_uf1:
        decw    x(8)                    /* Decrement exponent */
        cmp     $1, %eax                /* Exponent now zero? */
        je      Lreturn_x_exception     /* Yes => return x (mantissa OK) */

/* It's still a normal.  Set the mantissa to 111...111. */

        orb     $0x80, x(7)
        jmp     Lreturn_x

/* Underflow (mantissa was 000...000 and is now 111...111.  Return
   the smallest denormal with y's sign. */

        ALIGN
Ldown_uf0:
        movl    $1, y(0)
        movl    $0, y(4)
        andw    $0x8000, y(8)
        fldt    y(0)
        fstl    y(0)                    /* This raises the exceptions */
        jmp     Lreturn

/* x is infinite.  Return the biggest normal with x's sign. */

        ALIGN
Ldown_is_inf:
        movl    $0xffffffff, x(0)
        movl    $0xffffffff, x(4)
        andw    $0x8000, x(8)
        orw     $0x7ffe, x(8)
        jmp     Lreturn_x

/* Unordered: return one of the NaNs. */

        ALIGN
Lunordered:
        fldt    x(0)
        _xam
        j_nan   Lreturn                 /* x is a Nan => return x */
        fstp    %st(0)                  /* Remove x */

/* Fall through to return y */

/* x==y: return y. */

        ALIGN
Leq:    fldt    y(0)
        jmp     Lreturn

        ALIGN
Lreturn_x:
        fldt    x(0)

        ALIGN
Lreturn:
        EPILOGUE(FUNC)

#elif defined (FLOAT)

/ float nextafterf (float x, float y)

#define x       4(%esp)
#define y       8(%esp)

LABEL(FUNC):
        PROFILE_NOFRAME
        flds    x
        flds    y
        fucompp
        fstsww  %ax
        testb   $0x04, %ah
        jnz     Lunordered              /* Unordered (at least one NaN) */
        testb   $0x40, %ah
        jnz     Leq                     /* x==y */
        movl    x, %edx
        shrl    $31, %edx               /* signbit(x) -> bit 0 of %dl */
        xorb    %ah, %dl                /* Note: bit 0 of %ah is 1 iff x>y */
        testb   $0x01, %dl
        movl    $1, %ecx                /* Increment */
        jz      Lcommon
        movl    $-1, %ecx               /* TODO: Do this without jump */
        ALIGN
Lcommon:movl    x, %eax
        andl    $0x7fffffff, %eax       /* Clear sign bit */
        jz      Lx_zero                 /* x = 0.0 => smallest denormal */
        addl    %ecx, x
        movl    x, %edx
        andl    $0x7fffffff, %edx
        jz      Lreturn_x               /* 0.0 => no underflow */
        cmpl    $0x7f800000, %edx
        je      Loverflow
        cmpl    $0x00800000, %edx
        jb      Lunderflow
        ALIGN
Lreturn_x:
        flds    x
        ALIGN
Lreturn:
        EPILOGUE(FUNC)

        ALIGN
Lx_zero:
        movl    y, %eax
        andl    $0x80000000, %eax       /* Extract sign bit of y */
        orl     $0x00000001, %eax       /* Smallest denormal */
        movl    %eax, x
        ALIGN
Lunderflow:
        fldl    Lsmall
        fstps   y
        jmp     Lreturn_x

        ALIGN
Loverflow:
        fldl    Lbig
        fstps   y                       /* Raise overflow and inexact */
        jmp     Lreturn_x

        ALIGN
Lunordered:
        movl    x, %eax
        andl    $0x7fffffff, %eax
        cmpl    $0x7f800000, %eax
        ja      Lreturn_x               /* x is a NaN => return x */

/* Fall through to return y (which is a NaN) */

        ALIGN
Leq:    flds    y
        jmp     Lreturn

Lsmall: .long   0x00000001, 0x00000000
Lbig:   .long   0xffffffff, 0x7fefffff

#else

/ double nextafter (double x, double y)

#define x(i)    4+i(%esp)
#define y(i)    12+i(%esp)

LABEL(FUNC):
        PROFILE_NOFRAME
        fldl    x(0)
        fldl    y(0)
        fucompp
        fstsww  %ax
        testb   $0x04, %ah
        jnz     Lunordered              /* Unordered (at least one NaN) */
        testb   $0x40, %ah
        jnz     Leq                     /* x==y */

        movb    x(7), %dl
        shrb    $7, %dl                 /* signbit(x) -> bit 0 of %dl */
        xorb    %ah, %dl                /* Note: bit 0 of %ah is 1 iff x>y */

        movl    x(4), %eax
        andl    $0x7fffffff, %eax       /* Clear sign bit */
        orl     x(0), %eax              /* x == 0.0? */
        jz      Lx_zero                 /* Yes => smallest denormal */
        testb   $0x01, %dl
        jnz     Ldec
        addl    $1, x(0)
        adcl    $0, x(4)
        ALIGN
Lcommon:movl    x(4), %eax
        andl    $0x7fffffff, %eax       /* Clear sign bit */
        orl     x(0), %eax
        jz      Lreturn_x               /* 0.0 => no underflow */
        movl    x(4), %eax
        andl    $0x7ff00000, %eax       /* Extract exponent */
        cmpl    $0x7ff00000, %eax
        je      Loverflow
        cmpl    $0x00100000, %eax
        jb      Lunderflow
        ALIGN
Lreturn_x:
        fldl    x(0)
        ALIGN
Lreturn:
        EPILOGUE(FUNC)

        ALIGN
Ldec:   subl    $1, x(0)
        sbbl    $0, x(4)
        jmp     Lcommon

        ALIGN
Lx_zero:
        andl    $0x80000000, y(4)       /* Keep sign bit of y */
        movl    $0x00000001, y(0)       /* Smallest denormal */
        fldl    Lsmall
        fstps   x(0)
        fldl    y(0)
        jmp     Lreturn

        ALIGN
Lunderflow:
        fldl    Lsmall
        fstps   y(0)
        jmp     Lreturn_x

        ALIGN
Loverflow:
        fldl    Lbig
        fstps   y(0)                    /* Raise overflow and inexact */
        jmp     Lreturn_x

        ALIGN
Lunordered:
        movl    x(4), %eax
        andl    $0x7fffffff, %eax
        cmpl    $0x7ff80000, %eax
        jae     Lreturn_x               /* x is a NaN => return x */

/* Fall through to return y (which is a NaN) */

        ALIGN
Leq:    fldl    y(0)
        jmp     Lreturn

Lsmall: .long   0x00000001, 0x00000000
Lbig:   .long   0xffffffff, 0x7fefffff

#endif
