//      TITLE("Alpha AXP floor")
//++
//
// Copyright (c) 1993, 1994  Digital Equipment Corporation
//
// Module Name:
//
//    floor.s
//
// Abstract:
//
//    This module implements a high-performance Alpha AXP specific routine
//    for IEEE double format floor.
//
// Author:
//
//    Bill Gray
//
// Environment:
//
//    User mode.
//
// Revision History:
//
//    Thomas Van Baak (tvb) 15-Apr-1994
//
//        Adapted for NT.
//
//--

#include "ksalpha.h"


//
// Define stack frame.
//

        .struct 0
Temp:   .space  8                       // save argument
        .space  8                       // for 16-byte stack alignment
FrameLength:

//
// Define lower and upper 32-bit parts of 64-bit double.
//

#define LowPart 0x0
#define HighPart 0x4

        SBTTL("floor")

//++
//
// double
// floor (
//    IN double x
//    )
//
// Routine Description:
//
//    This function returns the floor of the given double argument.
//
// Arguments:
//
//    x (f16) - Supplies the argument value.
//
// Return Value:
//
//    The double floor result is returned as the function value in f0.
//
//--

        NESTED_ENTRY(floor, FrameLength, ra)

        lda     sp, -FrameLength(sp)    // allocate stack frame

        PROLOGUE_END

        ldah    t0, 0x7ff0(zero)        // exp mask
        ldt     f0, two_to_52           // get big
        ldah    t1, 0x10(zero)          // one in exp field
        stt     f16, Temp(sp)
        ldl     v0, Temp + HighPart(sp)
        cpys    f16, f0, f1             // fix sign of big
        cpys    f16, f16, f0
        and     v0, t0, t0
        subl    t0, t1, t0
        ldt     f10, neg_one
        ldah    t1, 0x4320(zero)        // cutoff value
        cmpult  t0, t1, t0
        beq     t0, quick_out

// Add big, sub big to round to int.

        addt    f16, f1, f11
        subt    f11, f1, f1
        cmptlt  f0, f1, f0
        fbeq    f0, it_rounded_down

// It rounded up so subtract one.

        ldt     f10, one
        subt    f1, f10, f1

it_rounded_down:
        cpys    f1, f1, f0
        br      zero, done


// Value is abnormal (or too big).
// If it is zero or denorm, figure out
// whether to return 0.0 or -1.0 -- if
// value is too big, just return it.

quick_out:
        ldah    t1, 0x7ff0(zero)
        ldah    t2, -0x8000(zero)
        and     v0, t1, t0
        and     v0, t2, v0
        bne     t0, ret_arg
        ldah    t0, 0x10(zero)
        beq     v0, ret_zero
        stt     f16, Temp(sp)
        ldl     v0, Temp(sp)
        lda     t0, -1(t0)
        ldl     t2, Temp + HighPart(sp)
        cpys    f10, f10, f16
        and     t2, t0, t0
        bis     t0, v0, v0
        and     t2, t1, t1
        cmpult  zero, v0, v0
        cmpeq   t1, zero, t1
        beq     t1, ret_zero
        and     t1, v0, t1
        beq     t1, ret_zero
        br      zero, ret_arg

ret_zero:
        cpys    f31, f31, f16

ret_arg:
        cpys    f16, f16, f0

done:
        lda     sp, FrameLength(sp)     // deallocate stack frame

        ret     zero, (ra)

        .end   floor

        .align  3
        .rdata

one:
        .quad   0x3ff0000000000000      // 1.0

neg_one:
        .quad   0xbff0000000000000      // -(1.0)

two_to_52:
        .quad   0x4330000000000000      // 2^52 (4503599627370496.0)
