binary-floating-point helper functions

bfp_* and related functions as needed by c[ft]fpr* from PowerISA v3.1B Book I section 7.6.2.2

def reset_xflags():
    vxsnan_flag <- 0
    vximz_flag <- 0
    vxidi_flag <- 0
    vxisi_flag <- 0
    vxzdz_flag <- 0
    vxsqrt_flag <- 0
    vxcvi_flag <- 0
    vxvc_flag <- 0
    ox_flag <- 0
    ux_flag <- 0
    xx_flag <- 0
    zx_flag <- 0

def bfp_CONVERT_FROM_BFP64(x):
    # x is a binary floating-point value represented in
    # double-precision format.
    exponent <- x[1:11]
    fraction <- x[12:63]

    result <- BFPState()
    result.sign <- 0
    result.exponent <- 0
    result.significand <- 0
    result.class.SNaN <- 0
    result.class.QNaN <- 0
    result.class.Infinity <- 0
    result.class.Zero <- 0
    result.class.Denormal <- 0
    result.class.Normal <- 0
    if (exponent = 2047) & (fraction[0] = 0) & (fraction != 0) then
        # x is a SNaN
        result.class.SNaN <- 1
        result.sign <- x[0]
        result.significand[0] <- 0
        result.significand[1:52] <- fraction
    else if (exponent = 2047) & (fraction[0] != 0) & (fraction != 0) then
        # x is a QNaN
        result.class.QNaN <- 1
        result.sign <- x[0]
        result.significand[0] <- 0
        result.significand[1:52] <- fraction
    else if (exponent = 2047) & (fraction = 0) then
        # is an Infinity
        result.class.Infinity <- 1
        result.sign <- x[0]
    else if (exponent = 0) & (fraction = 0) then
        # x is a Zero
        result.class.Zero <- 1
        result.sign <- x[0]
    else if (exponent = 0) & (fraction != 0) then
        # x is a Denormal
        result.class.Denormal <- 1
        result.sign <- x[0]
        result.exponent <- -1022
        result.significand[0] <- 0
        result.significand[1:52] <- fraction
        do while result.significand[0] != 1
            result.significand <- result.significand * 2
            result.exponent <- result.exponent - 1
    else
        result.class.Normal <- 1
        result.sign <- x[0]
        result.exponent <- exponent
        # have to do the subtraction separately since SelectableInt won't
        # give negative results
        result.exponent <- result.exponent - 1023
        result.significand[0] <- 1
        result.significand[1:52] <- fraction
    return result

def bfp_CONVERT_FROM_BFP32(x):
    # x is a floating-point value represented in single-precision format.
    exponent <- x[1:8]
    fraction <- x[9:31]

    result <- BFPState()
    result.sign <- 0
    result.exponent <- 0
    result.significand <- 0
    result.class.SNaN <- 0
    result.class.QNaN <- 0
    result.class.Infinity <- 0
    result.class.Zero <- 0
    result.class.Denormal <- 0
    result.class.Normal <- 0
    if (exponent = 255) & (fraction[0] = 0) & (fraction != 0) then
        # x is a SNaN
        result.class.SNaN <- 1
        result.sign <- x[0]
        result.significand[0] <- 0
        result.significand[1:23] <- fraction
    else if (exponent = 255) & (fraction[0] != 0) & (fraction != 0) then
        # x is a QNaN
        result.class.QNaN <- 1
        result.sign <- x[0]
        result.significand[0] <- 0
        result.significand[1:23] <- fraction
    else if (exponent = 255) & (fraction = 0) then
        # is an Infinity
        result.class.Infinity <- 1
        result.sign <- x[0]
    else if (exponent = 0) & (fraction = 0) then
        # x is a Zero
        result.class.Zero <- 1
        result.sign <- x[0]
    else if (exponent = 0) & (fraction != 0) then
        # x is a Denormal
        result.class.Denormal <- 1
        result.sign <- x[0]
        result.exponent <- -126
        result.significand[0] <- 0
        result.significand[1:23] <- fraction
        do while result.significand[0] != 1
            result.significand <- result.significand * 2
            result.exponent <- result.exponent - 1
    else
        result.class.Normal <- 1
        result.sign <- x[0]
        result.exponent <- exponent
        # have to do the subtraction separately since SelectableInt won't
        # give negative results
        result.exponent <- result.exponent - 127
        result.significand[0] <- 1
        result.significand[1:23] <- fraction
    return result

def bfp_CONVERT_FROM_SI32(x):
    # x is an integer value represented in signed word integer format.

    result <- BFPState()
    result.sign <- 0
    result.exponent <- 0
    result.significand <- 0
    result.class.SNaN <- 0
    result.class.QNaN <- 0
    result.class.Infinity <- 0
    result.class.Zero <- 0
    result.class.Denormal <- 0
    result.class.Normal <- 0

    if x = 0x0000_0000 then
        result.class.Zero <- 1
    else
        result.class.Normal <- 1
        result.sign <- x[0]
        result.exponent <- 32
        result.significand[0:32] <- EXTS(x)

        if result.significand[0] = 1 then
            result.sign <- 1
            result.significand[0:32] <- -result.significand[0:32]
        do while result.significand[0] = 0
            result.significand <- result.significand * 2
            result.exponent <- result.exponent - 1
    return result

def bfp_CONVERT_FROM_SI64(x):
    # x is an integer value represented in signed double-word integer
    # format.

    result <- BFPState()
    result.sign <- 0
    result.exponent <- 0
    result.significand <- 0
    result.class.SNaN <- 0
    result.class.QNaN <- 0
    result.class.Infinity <- 0
    result.class.Zero <- 0
    result.class.Denormal <- 0
    result.class.Normal <- 0

    if x = 0x0000_0000_0000_0000 then
        result.class.Zero <- 1
    else
        result.class.Normal <- 1
        result.sign <- x[0]
        result.exponent <- 64
        result.significand[0:64] <- EXTS(x)

        if result.significand[0] = 1 then
            result.sign <- 1
            result.significand[0:64] <- -result.significand[0:64]
        do while result.significand[0] = 0
            result.significand <- result.significand * 2
            result.exponent <- result.exponent - 1
    return result

def bfp_CONVERT_FROM_SI128(x):
    # x is a 128-bit signed integer value.

    result <- BFPState()
    result.sign <- 0
    result.exponent <- 0
    result.significand <- 0
    result.class.SNaN <- 0
    result.class.QNaN <- 0
    result.class.Infinity <- 0
    result.class.Zero <- 0
    result.class.Denormal <- 0
    result.class.Normal <- 0

    if x = 0x0000_0000_0000_0000_0000_0000_0000_0000 then
        result.class.Zero <- 1
    else
        result.class.Normal <- 1
        result.sign <- x[0]
        result.exponent <- 128
        result.significand[0:128] <- EXTS(x)

        if result.significand[0] = 1 then
            result.sign <- 1
            result.significand[0:128] <- -result.significand[0:128]
        do while result.significand[0] = 0
            result.significand <- result.significand * 2
            result.exponent <- result.exponent - 1
    return result

def bfp_CONVERT_FROM_UI32(x):
    # x is an integer value represented in unsigned word integer
    # format.

    result <- BFPState()
    result.sign <- 0
    result.exponent <- 0
    result.significand <- 0
    result.class.SNaN <- 0
    result.class.QNaN <- 0
    result.class.Infinity <- 0
    result.class.Zero <- 0
    result.class.Denormal <- 0
    result.class.Normal <- 0

    if x = 0x0000_0000 then
        result.class.Zero <- 1
    else
        result.class.Normal <- 1
        result.sign <- 0
        result.exponent <- 32
        result.significand[0:32] <- 0b0 || x
        do while result.significand[0] = 0
            result.significand <- result.significand * 2
            result.exponent <- result.exponent - 1
    return result

def bfp_CONVERT_FROM_UI64(x):
    # x is an integer value represented in unsigned double-word integer
    # format.

    result <- BFPState()
    result.sign <- 0
    result.exponent <- 0
    result.significand <- 0
    result.class.SNaN <- 0
    result.class.QNaN <- 0
    result.class.Infinity <- 0
    result.class.Zero <- 0
    result.class.Denormal <- 0
    result.class.Normal <- 0

    if x = 0x0000_0000_0000_0000 then
        result.class.Zero <- 1
    else
        result.class.Normal <- 1
        result.sign <- 0
        result.exponent <- 64
        result.significand[0:64] <- 0b0 || x
        do while result.significand[0] = 0
            result.significand <- result.significand * 2
            result.exponent <- result.exponent - 1
    return result

def bfp_CONVERT_FROM_UI128(x):
    # x is a 128-bit unsigned integer value.

    result <- BFPState()
    result.sign <- 0
    result.exponent <- 0
    result.significand <- 0
    result.class.SNaN <- 0
    result.class.QNaN <- 0
    result.class.Infinity <- 0
    result.class.Zero <- 0
    result.class.Denormal <- 0
    result.class.Normal <- 0

    if x = 0x0000_0000_0000_0000_0000_0000_0000_0000 then
        result.class.Zero <- 1
    else
        result.class.Normal <- 1
        result.sign <- 0
        result.exponent <- 128
        result.significand[0:128] <- 0b0 || x
        do while result.significand[0] = 0
            result.significand <- result.significand * 2
            result.exponent <- result.exponent - 1
    return result

def bfp_ROUND_TO_INTEGER(rmode, x):
    # x is a binary floating-point value that is represented in the
    # binary floating-point working format and has
    # unbounded exponent range and significand precision.

    if IsSNaN(x) then vxsnan_flag <- 1
    if IsNaN(x) & ¬IsSNaN(x) then return x
    if IsSNaN(x) then
        result <- x
        result.class.SNaN <- 0
        result.class.QNaN <- 1
        return result
    if IsInf(x) | IsZero(x) then return x
    result <- x
    result.class.Denormal <- 0
    result.class.Normal <- 0
    exact <- 0b0
    halfway <- 0b0
    more_than_halfway <- 0b0
    even <- 0b0
    if result.exponent < -1 then
        # all values have magnitude < 0.5
        result.significand <- 0
        result.exponent <- 0
        even <- 0b1
    else if result.exponent = -1 then
        if result.significand[0] = 1 then
            result.significand[0] <- 0
            if result.significand = 0 then halfway <- 0b1
            else more_than_halfway <- 0b1
        result.significand <- 0
        result.exponent <- 0
        even <- 0b1
    else
        result.significand <- 0
        int_part <- x.significand[0:x.exponent]
        result.significand[0:x.exponent] <- int_part
        even <- ¬int_part[x.exponent]
        temp <- x.significand
        temp[0:x.exponent] <- 0
        if temp = 0 then exact <- 0b1
        if temp[x.exponent + 1] = 1 then
            temp[x.exponent + 1] <- 0
            if temp = 0 then halfway <- 0b1
            else more_than_halfway <- 0b1
    if rmode = 0b000 then  # Round to Nearest Even
        round_up <- (¬even & halfway) | more_than_halfway
    if rmode = 0b001 then  # Round towards Zero
        round_up <- 0b0
    if rmode = 0b010 then  # Round towards +Infinity
        round_up <- (x.sign = 0) & ¬exact
    if rmode = 0b011 then  # Round towards -Infinity
        round_up <- (x.sign = 1) & ¬exact
    if rmode = 0b100 then  # Round to Nearest Away
        round_up <- halfway | more_than_halfway
    if round_up then
        inc_flag <- 1
        temp <- BFPState()
        temp.significand <- 0
        temp.significand[result.exponent] <- 1
        result.significand <- result.significand + temp.significand
        if result.significand >= 2 then
            result.significand <- truediv(result.significand, 2)
            result.exponent <- result.exponent + 1
    else inc_flag <- 0  # TODO: does the spec specify this?
    if result.significand = 0 then result.class.Zero <- 1
    else result.class.Normal <- 1
    if ¬bfp_COMPARE_EQ(result, x) then xx_flag <- 1
    else xx_flag <- 0  # TODO: does the spec specify this?
    return result

def bfp_ROUND_TO_INTEGER_NEAR_EVEN(x):
    return bfp_ROUND_TO_INTEGER(0b000, x)

def bfp_ROUND_TO_INTEGER_TRUNC(x):
    return bfp_ROUND_TO_INTEGER(0b001, x)

def bfp_ROUND_TO_INTEGER_CEIL(x):
    return bfp_ROUND_TO_INTEGER(0b010, x)

def bfp_ROUND_TO_INTEGER_FLOOR(x):
    return bfp_ROUND_TO_INTEGER(0b011, x)

def bfp_ROUND_TO_INTEGER_NEAR_AWAY(x):
    return bfp_ROUND_TO_INTEGER(0b100, x)

def bfp_COMPARE_EQ(x, y):
    # x is a binary floating-point value represented in the
    # binary floating-point working format.
    # y is a binary floating-point value represented in the
    # binary floating-point working format.

    if IsNaN(x) | IsNaN(y) then
        return 0b0
    if IsZero(x) & IsZero(y) then
        return 0b1

    if IsInf(x) & IsInf(y) then
        return x.sign = y.sign
    if IsInf(x) | IsInf(y) then
        return 0b0
    if IsZero(x) | IsZero(y) then
        return 0b0
    if x.sign != y.sign then
        return 0b0
    if x.exponent > 0 then xs <- x.significand * pow(2, x.exponent)
    else xs <- truediv(x.significand, pow(2, -x.exponent))
    if y.exponent > 0 then ys <- y.significand * pow(2, y.exponent)
    else ys <- truediv(y.significand, pow(2, -y.exponent))
    return xs = ys

def bfp_COMPARE_GT(x, y):
    # x is a binary floating-point value represented in the
    # binary floating-point working format.
    # y is a binary floating-point value represented in the
    # binary floating-point working format.

    if IsNaN(x) | IsNaN(y) then
        return 0b0
    if IsZero(x) & IsZero(y) then
        return 0b0

    if IsInf(x) & IsInf(y) then
        return ¬IsNeg(x) & IsNeg(y)
    if IsInf(x) then
        return ¬IsNeg(x)
    if IsInf(y) then
        return IsNeg(y)
    if IsZero(x) then
        return IsNeg(y)
    if IsZero(y) then
        return ¬IsNeg(x)
    if x.sign != y.sign then
        return IsNeg(y)
    if x.exponent > 0 then xs <- x.significand * pow(2, x.exponent)
    else xs <- truediv(x.significand, pow(2, -x.exponent))
    if y.exponent > 0 then ys <- y.significand * pow(2, y.exponent)
    else ys <- truediv(y.significand, pow(2, -y.exponent))
    if x.sign = 1 then return xs < ys
    return xs > ys

def bfp_COMPARE_LT(x, y):
    # x is a binary floating-point value represented in the
    # binary floating-point working format.
    # y is a binary floating-point value represented in the
    # binary floating-point working format.

    if IsNaN(x) | IsNaN(y) then
        return 0b0
    if IsZero(x) & IsZero(y) then
        return 0b0

    if IsInf(x) & IsInf(y) then
        return IsNeg(x) & ¬IsNeg(y)
    if IsInf(x) then
        return IsNeg(x)
    if IsInf(y) then
        return ¬IsNeg(y)
    if IsZero(x) then
        return ¬IsNeg(y)
    if IsZero(y) then
        return IsNeg(x)
    if x.sign != y.sign then
        return IsNeg(x)
    if x.exponent > 0 then xs <- x.significand * pow(2, x.exponent)
    else xs <- truediv(x.significand, pow(2, -x.exponent))
    if y.exponent > 0 then ys <- y.significand * pow(2, y.exponent)
    else ys <- truediv(y.significand, pow(2, -y.exponent))
    if x.sign = 1 then return xs > ys
    return xs < ys

def bfp_ABSOLUTE(x):
    # x is a binary floating-point value represented in the
    # binary floating-point working format.
    result <- x
    result.sign <- 0
    return result

def si32_CONVERT_FROM_BFP(x):
    # x is an integer value represented in the
    # binary floating-point working format.

    if IsNaN(x) then
        vxcvi_flag <- 1
        if IsSNaN(x) then vxsnan_flag <- 1
        return 0x8000_0000
    else
        temp_xx <- xx_flag
        temp_inc <- inc_flag
        rnd <- bfp_ROUND_TO_INTEGER_TRUNC(x)
        # TODO: does the spec say these are preserved?
        xx_flag <- temp_xx
        inc_flag <- temp_inc
        exponent <- rnd.exponent
        significand <- rnd.significand
        si32max <- bfp_CONVERT_FROM_SI32(0b0 || [1] * 31)
        si32min <- bfp_CONVERT_FROM_SI32(0b1 || [0] * 31)
        if bfp_COMPARE_GT(rnd, si32max) then
            vxcvi_flag <- 1
            return 0x7FFF_FFFF
        if bfp_COMPARE_LT(rnd, si32min) then
            vxcvi_flag <- 1
            return 0x8000_0000
        else
            if ¬bfp_COMPARE_EQ(rnd, x) then xx_flag <- 1
            else xx_flag <- 0  # TODO: does the spec specify this?
            inc_flag <- 0
            # TODO: spec says this is logical shift right:
            significand <- significand[0:31] / pow(2, 31 - exponent)
            if IsNeg(rnd) then significand <- -significand
            return significand[0:31]

def si64_CONVERT_FROM_BFP(x):
    # x is an integer value represented in the
    # binary floating-point working format.

    if IsNaN(x) then
        vxcvi_flag <- 1
        if IsSNaN(x) then vxsnan_flag <- 1
        return 0x8000_0000_0000_0000
    else
        temp_xx <- xx_flag
        temp_inc <- inc_flag
        rnd <- bfp_ROUND_TO_INTEGER_TRUNC(x)
        # TODO: does the spec say these are preserved?
        xx_flag <- temp_xx
        inc_flag <- temp_inc
        exponent <- rnd.exponent
        significand <- rnd.significand
        si64max <- bfp_CONVERT_FROM_SI64(0b0 || [1] * 63)
        si64min <- bfp_CONVERT_FROM_SI64(0b1 || [0] * 63)
        if bfp_COMPARE_GT(rnd, si64max) then
            vxcvi_flag <- 1
            return 0x7FFF_FFFF_FFFF_FFFF
        if bfp_COMPARE_LT(rnd, si64min) then
            vxcvi_flag <- 1
            return 0x8000_0000_0000_0000
        else
            if ¬bfp_COMPARE_EQ(rnd, x) then xx_flag <- 1
            else xx_flag <- 0  # TODO: does the spec specify this?
            inc_flag <- 0
            # TODO: spec says this is logical shift right:
            significand <- significand[0:63] / pow(2, 63 - exponent)
            if IsNeg(rnd) then significand <- -significand
            return significand[0:63]

def si128_CONVERT_FROM_BFP(x):
    # x is an integer value represented in the
    # binary floating-point working format.

    if IsNaN(x) then
        vxcvi_flag <- 1
        if IsSNaN(x) then vxsnan_flag <- 1
        return 0x8000_0000_0000_0000_0000_0000_0000_0000
    else
        temp_xx <- xx_flag
        temp_inc <- inc_flag
        rnd <- bfp_ROUND_TO_INTEGER_TRUNC(x)
        # TODO: does the spec say these are preserved?
        xx_flag <- temp_xx
        inc_flag <- temp_inc
        exponent <- rnd.exponent
        significand <- rnd.significand
        si128max <- bfp_CONVERT_FROM_SI128(0b0 || [1] * 127)
        si128min <- bfp_CONVERT_FROM_SI128(0b1 || [0] * 127)
        if bfp_COMPARE_GT(rnd, si128max) then
            vxcvi_flag <- 1
            return 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
        if bfp_COMPARE_LT(rnd, si128min) then
            vxcvi_flag <- 1
            return 0x8000_0000_0000_0000_0000_0000_0000_0000
        else
            if ¬bfp_COMPARE_EQ(rnd, x) then xx_flag <- 1
            else xx_flag <- 0  # TODO: does the spec specify this?
            inc_flag <- 0
            # TODO: spec says this is logical shift right:
            significand <- significand[0:127] / pow(2, 127 - exponent)
            if IsNeg(rnd) then significand <- -significand
            return significand[0:127]

def ui32_CONVERT_FROM_BFP(x):
    # x is an integer value represented in the
    # binary floating-point working format.

    if IsNaN(x) then
        vxcvi_flag <- 1
        if IsSNaN(x) then vxsnan_flag <- 1
        return 0x0000_0000
    else
        temp_xx <- xx_flag
        temp_inc <- inc_flag
        rnd <- bfp_ROUND_TO_INTEGER_TRUNC(x)
        # TODO: does the spec say these are preserved?
        xx_flag <- temp_xx
        inc_flag <- temp_inc
        exponent <- rnd.exponent
        significand <- rnd.significand
        ui32max <- bfp_CONVERT_FROM_UI32([1] * 32)
        if bfp_COMPARE_GT(rnd, ui32max) then
            vxcvi_flag <- 1
            return 0xFFFF_FFFF
        if IsNeg(rnd) then
            vxcvi_flag <- 1
            return 0x0000_0000
        else
            if ¬bfp_COMPARE_EQ(rnd, x) then xx_flag <- 1
            else xx_flag <- 0  # TODO: does the spec specify this?
            inc_flag <- 0
            # TODO: spec says this is logical shift right:
            significand <- significand[0:31] / pow(2, 31 - exponent)
            return significand[0:31]

def ui64_CONVERT_FROM_BFP(x):
    # x is an integer value represented in the
    # binary floating-point working format.

    if IsNaN(x) then
        vxcvi_flag <- 1
        if IsSNaN(x) then vxsnan_flag <- 1
        return 0x0000_0000_0000_0000
    else
        temp_xx <- xx_flag
        temp_inc <- inc_flag
        rnd <- bfp_ROUND_TO_INTEGER_TRUNC(x)
        # TODO: does the spec say these are preserved?
        xx_flag <- temp_xx
        inc_flag <- temp_inc
        exponent <- rnd.exponent
        significand <- rnd.significand
        ui64max <- bfp_CONVERT_FROM_UI64([1] * 64)
        if bfp_COMPARE_GT(rnd, ui64max) then
            vxcvi_flag <- 1
            return 0xFFFF_FFFF_FFFF_FFFF
        if IsNeg(rnd) then
            vxcvi_flag <- 1
            return 0x0000_0000_0000_0000
        else
            if ¬bfp_COMPARE_EQ(rnd, x) then xx_flag <- 1
            else xx_flag <- 0  # TODO: does the spec specify this?
            inc_flag <- 0
            # TODO: spec says this is logical shift right:
            significand <- significand[0:63] / pow(2, 63 - exponent)
            return significand[0:63]

def ui128_CONVERT_FROM_BFP(x):
    # x is an integer value represented in the
    # binary floating-point working format.

    if IsNaN(x) then
        vxcvi_flag <- 1
        if IsSNaN(x) then vxsnan_flag <- 1
        return 0x0000_0000_0000_0000_0000_0000_0000_0000
    else
        temp_xx <- xx_flag
        temp_inc <- inc_flag
        rnd <- bfp_ROUND_TO_INTEGER_TRUNC(x)
        # TODO: does the spec say these are preserved?
        xx_flag <- temp_xx
        inc_flag <- temp_inc
        exponent <- rnd.exponent
        significand <- rnd.significand
        ui128max <- bfp_CONVERT_FROM_UI128([1] * 128)
        if bfp_COMPARE_GT(rnd, ui128max) then
            vxcvi_flag <- 1
            return 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
        if IsNeg(rnd) then
            vxcvi_flag <- 1
            return 0x0000_0000_0000_0000_0000_0000_0000_0000
        else
            if ¬bfp_COMPARE_EQ(rnd, x) then xx_flag <- 1
            else xx_flag <- 0  # TODO: does the spec specify this?
            inc_flag <- 0
            # TODO: spec says this is logical shift right:
            significand <- significand[0:127] / pow(2, 127 - exponent)
            return significand[0:127]

def bfp64_CONVERT_FROM_BFP(x):
    # x is a floating-point value represented in the binary floating-point
    # working format.

    result <- [0] * 64
    if x.class.QNaN = 1 then
        result[0] <- x.sign
        result[1:11] <- 0b111_1111_1111
        result[12:63] <- x.significand[1:52]
    else if x.class.Infinity = 1 then
        result[0] <- x.sign
        result[1:11] <- 0b111_1111_1111
        result[12:63] <- 0
    else if x.class.Zero = 1 then
        result[0] <- x.sign
        result[1:63] <- 0
    else if (x.exponent < -1022) & (FPSCR.UE = 0) then
        result[0] <- x.sign
        sh_cnt <- -1022 - x.exponent
        result[1:11] <- 0b000_0000_0000
        # TODO: spec says this is shift right
        result[12:63] <- x.significand[1:52] / pow(2, sh_cnt)
    else if (x.exponent < -1022) & (FPSCR.UE = 1) then
        result[0:63] <- undefined(0)  # TODO: which undefined value to use?
    else if (x.exponent > 1023) & (FPSCR.OE = 1) then
        result[0:63] <- undefined(0)  # TODO: which undefined value to use?
    else
        result[0] <- x.sign
        result[1:11] <- x.exponent + 1023
        result[12:63] <- x.significand[1:52]
    return result

def bfp32_CONVERT_FROM_BFP(x):
    # x is a floating-point value represented in the binary floating-point
    # working format.

    result <- [0] * 32
    if x.class.QNaN = 1 then
        result[0] <- x.sign
        result[1:8] <- 0b1111_1111
        result[9:31] <- x.significand[1:23]
    else if x.class.Infinity = 1 then
        result[0] <- x.sign
        result[1:9] <- 0b1111_1111
        result[9:31] <- 0
    else if x.class.Zero = 1 then
        result[0] <- x.sign
        result[1:31] <- 0
    else if (x.exponent < -126) & (FPSCR.UE = 0) then
        result[0] <- x.sign
        sh_cnt <- -126 - x.exponent
        result[1:8] <- 0b0000_0000
        # TODO: spec says this is shift right
        result[9:31] <- x.significand[1:23] / pow(2, sh_cnt)
    else if (x.exponent < -126) & (FPSCR.UE = 1) then
        result[0:31] <- undefined(0)  # TODO: which undefined value to use?
    else if (x.exponent > 127) & (FPSCR.OE = 1) then
        result[0:31] <- undefined(0)  # TODO: which undefined value to use?
    else
        result[0] <- x.sign
        result[1:8] <- x.exponent + 127
        result[9:31] <- x.significand[1:23]
    return result

def bfp_ROUND_TO_BFP64(ro, rmode, x):
    # x is a normalized binary floating-point value that is represented in
    # the binary floating-point working format and has unbounded exponent
    # range and significand precision.
    # ro is a 1-bit unsigned integer and rmode is a 2-bit unsigned integer,
    # together specifying one of five rounding modes to be used in
    # rounding x.
    #
    # ro=0 rmode=0b00  Round to Nearest Even
    # ro=0 rmode=0b01  Round towards Zero
    # ro=0 rmode=0b10  Round towards +Infinity
    # ro=0 rmode=0b11  Round towards -Infinity
    # ro=1             Round to Odd
    #
    # Return the value x rounded to double-precision under control of the
    # specified rounding mode.

    if x.class.QNaN then return x
    if x.class.Infinity then return x
    if x.class.Zero then return x
    if bfp_COMPARE_LT(bfp_ABSOLUTE(x), bfp_NMIN_BFP64()) then
        if FPSCR.UE=0 then
            x <- bfp_DENORM(-1022, x)
            if (ro=0) & (rmode=0b00) then r <- bfp_ROUND_NEAR_EVEN(53, x)
            if (ro=0) & (rmode=0b01) then r <- bfp_ROUND_TRUNC(53, x)
            if (ro=0) & (rmode=0b10) then r <- bfp_ROUND_CEIL(53, x)
            if (ro=0) & (rmode=0b11) then r <- bfp_ROUND_FLOOR(53, x)
            if  ro=1                 then r <- bfp_ROUND_ODD(53, x)
            ux_flag <- xx_flag
            return r
        else
            x.exponent <- x.exponent + 1536
            ux_flag <- 1
    if (ro=0) & (rmode=0b00) then r <- bfp_ROUND_NEAR_EVEN(53, x)
    if (ro=0) & (rmode=0b01) then r <- bfp_ROUND_TRUNC(53, x)
    if (ro=0) & (rmode=0b10) then r <- bfp_ROUND_CEIL(53, x)
    if (ro=0) & (rmode=0b11) then r <- bfp_ROUND_FLOOR(53, x)
    if  ro=1                 then r <- bfp_ROUND_ODD(53, x)
    if bfp_COMPARE_GT(bfp_ABSOLUTE(r), bfp_NMAX_BFP64()) then
        if FPSCR.OE=0 then
            if (ro=0) & (rmode=0b00) then r <- x.sign ? bfp_INFINITY()   : bfp_INFINITY()
            if (ro=0) & (rmode=0b01) then r <- x.sign ? bfp_NMAX_BFP64() : bfp_NMAX_BFP64()
            if (ro=0) & (rmode=0b10) then r <- x.sign ? bfp_NMAX_BFP64() : bfp_INFINITY()
            if (ro=0) & (rmode=0b11) then r <- x.sign ? bfp_INFINITY()   : bfp_NMAX_BFP64()
            if  ro=1                 then r <- x.sign ? bfp_NMAX_BFP64() : bfp_NMAX_BFP64()
            r.sign <- x.sign
            ox_flag <- 0b1
            xx_flag <- 0b1
            inc_flag <- undefined(0)  # TODO: which undefined value to use?
            return r
        else
            r.exponent <- r.exponent - 1536
            ox_flag <- 1
    return r

def bfp_ROUND_TO_BFP32(rmode,x):
    # x is a normalized binary floating-point value that is represented in
    # the binary floating-point working format and has unbounded exponent
    # range and significand precision.
    #
    # rmode is a 2-bit integer value specifying one of four rounding modes.
    #
    # rmode=0b00 Round to Nearest Even
    # rmode=0b01 Round towards Zero
    # rmode=0b10 Round towards +Infinity
    # rmode=0b11 Round towards - Infinity
    #
    # If x is a QNaN, Infinity, or Zero, return x. Otherwise, if x is an
    # SNaN, set vxsnan_flag to 1 and return the corresponding QNaN
    # representation of x. Otherwise, return the value x rounded to
    # single-precision format’s exponent range and significand precision
    # represented in the floating-point working format using the rounding
    # mode specified by rmode.

    if x.class.SNaN then
        vxsnan_flag <- 1
        return bfp_QUIET(x)

    if x.class.QNaN then return x
    if x.class.Infinity then return x
    if x.class.Zero then return x

    if bfp_COMPARE_LT(bfp_ABSOLUTE(x), bfp_NMIN_BFP32()) then
        x <- bfp_DENORM(-126,x)
        if rmode = 0b00 then r <- bfp_ROUND_NEAR_EVEN(24, x)
        if rmode = 0b01 then r <- bfp_ROUND_TRUNC(24, x)
        if rmode = 0b10 then r <- bfp_ROUND_CEIL(24, x)
        if rmode = 0b11 then r <- bfp_ROUND_FLOOR(24, x)
        if FPSCR.UE = 0 then
            ux_flag <- xx_flag
            return r
        else
            x.exponent <- x.exponent + 192
            ux_flag <- 0b1

    if rmode = 0b00 then r <- bfp_ROUND_NEAR_EVEN(24, x)
    if rmode = 0b01 then r <- bfp_ROUND_TRUNC(24, x)
    if rmode = 0b10 then r <- bfp_ROUND_CEIL(24, x)
    if rmode = 0b11 then r <- bfp_ROUND_FLOOR(24, x)

    if bfp_COMPARE_GT(bfp_ABSOLUTE(r), bfp_NMAX_BFP32()) then
        if FPSCR.OE = 0 then
            if rmode=0b00 then r <- x.sign ? bfp_INFINITY()   : bfp_INFINITY()
            if rmode=0b01 then r <- x.sign ? bfp_NMAX_BFP32() : bfp_NMAX_BFP32()
            if rmode=0b10 then r <- x.sign ? bfp_NMAX_BFP32() : bfp_INFINITY()
            if rmode=0b11 then r <- x.sign ? bfp_INFINITY()   : bfp_NMAX_BFP32()
            r.sign <- x.sign
            ox_flag <- 0b1
            xx_flag <- 0b1
            inc_flag <- undefined(0)  # TODO: which undefined value to use?
            return r
        else
            r.exponent <- r.exponent - 192
            ox_flag <- 0b1
    return r

def bfp_INFINITY():
    # The value +Infinity represented in the binary floating-point working
    # format.
    r <- BFPState()
    r.class.Infinity <- 1
    return r

def bfp_NMAX_BFP32():
    # Return the largest finite single-precision floating-point value
    # (i.e., 2^128 - 2^(128-24)) in the binary floating-point working
    # format.
    return bfp_CONVERT_FROM_BFP32(0x7F7F_FFFF)

def bfp_NMAX_BFP64():
    # Return the largest finite double-precision floating-point value
    # (i.e., 2^1024 - 2^(1024-53)) in the binary floating-point working
    # format.
    return bfp_CONVERT_FROM_BFP64(0x7FEF_FFFF_FFFF_FFFF)

def bfp_NMIN_BFP32():
    # Return the smallest positive normalized single-precision
    # floating-point value, 2^-126, represented in the binary
    # floating-point working format.
    return bfp_CONVERT_FROM_BFP32(0x0080_0000)

def bfp_NMIN_BFP64():
    # Return the smallest positive normalized double-precision
    # floating-point value, 2^-1022, represented in the binary
    # floating-point working format.
    return bfp_CONVERT_FROM_BFP64(0x0010_0000_0000_0000)

def bfp_ROUND_HELPER(p, ro, rmode, x):
    # not part of the PowerISA v3.1B specification.
    # helper function for the bfp_ROUND_* functions.
    # doesn't set inc_flag or xx_flag.
    #
    # x is a binary floating-point value that is represented in the binary
    # floating-point working format and has unbounded exponent range and
    # significand precision. x must be rounded as presented, without
    # prenormalization.
    #
    # p is an integer value specifying the precision (i.e., number of bits)
    # the significand is rounded to.
    #
    # ro is a 1-bit unsigned integer and rmode is a 3-bit unsigned integer,
    # together specifying one of six rounding modes to be used in
    # rounding x.
    #
    # ro=0 rmode=0b000  Round to Nearest Even
    # ro=0 rmode=0b001  Round towards Zero
    # ro=0 rmode=0b010  Round towards +Infinity
    # ro=0 rmode=0b011  Round towards -Infinity
    # ro=0 rmode=0b100  Round to Nearest Away
    # ro=1              Round to Odd

    if IsInf(x) | IsNaN(x) | IsZero(x) then
        inc_flag <- 0  # TODO: does the spec specify this?
        xx_flag <- 0  # TODO: does the spec specify this?
        return x

    result <- x
    result.significand <- 0
    result.significand[0:p - 1] <- x.significand[0:p - 1]
    exact <- x.significand = result.significand
    halfway <- 0b0
    more_than_half <- 0b0
    if x.significand[p] then
        t <- x
        t.significand <- 0
        t.significand[0:p] <- x.significand[0:p]
        if t.significand = x.significand then halfway <- 0b1
        else more_than_half <- 0b1
    even <- ¬result.significand[p - 1]

    if (ro=0) & (rmode=0b000) then  # Round to Nearest Even
        round_up <- (halfway & ¬even) | more_than_half
    if (ro=0) & (rmode=0b001) then  # Round towards Zero
        round_up <- 0b0
    if (ro=0) & (rmode=0b010) then  # Round towards +Infinity
        round_up <- (x.sign = 0) & ¬exact
    if (ro=0) & (rmode=0b011) then  # Round towards -Infinity
        round_up <- (x.sign = 1) & ¬exact
    if (ro=0) & (rmode=0b100) then  # Round to Nearest Away
        round_up <- halfway | more_than_half
    if  ro=1                  then  # Round to Odd
        round_up <- ¬exact & even

    if round_up then
        result.significand[0:p-1] <- result.significand[0:p-1] + 1
        if result.significand[0:p-1] = 0 then
            result.significand[0] <- 1
            result.exponent <- result.exponent + 1

    if result.significand != 0 then
        do while result.significand[0] != 1
            result.significand <- result.significand * 2
            result.exponent <- result.exponent - 1
        result.class.Normal <- 1
        result.class.Denormal <- 0
        result.class.Zero <- 0
    else
        result.class.Normal <- 0
        result.class.Denormal <- 0
        result.class.Zero <- 1

    return result

def bfp_ROUND_NEAR_EVEN(p, x):
    # x is a binary floating-point value that is represented in the binary
    # floating-point working format and has unbounded exponent range and
    # significand precision. x must be rounded as presented, without
    # prenormalization.
    #
    # p is an integer value specifying the precision (i.e., number of bits)
    # the significand is rounded to.

    result <- bfp_ROUND_HELPER(p, 0b0, 0b000, x)

    if bfp_COMPARE_GT(bfp_ABSOLUTE(result), bfp_ABSOLUTE(x)) then
        inc_flag <- 1
    else inc_flag <- 0  # TODO: does the spec specify this?
    if ¬bfp_COMPARE_EQ(result, x) then xx_flag <- 1
    else xx_flag <- 0  # TODO: does the spec specify this?

    return result

def bfp_ROUND_TRUNC(p, x):
    # x is a binary floating-point value that is represented in the binary
    # floating-point working format and has unbounded exponent range and
    # significand precision. x must be rounded as presented, without
    # prenormalization.
    #
    # p is an integer value specifying the precision (i.e., number of bits)
    # the significand is rounded to.

    result <- bfp_ROUND_HELPER(p, 0b0, 0b001, x)

    if bfp_COMPARE_GT(bfp_ABSOLUTE(result), bfp_ABSOLUTE(x)) then
        inc_flag <- 1
    else inc_flag <- 0  # TODO: does the spec specify this?
    if ¬bfp_COMPARE_EQ(result, x) then xx_flag <- 1
    else xx_flag <- 0  # TODO: does the spec specify this?

    return result

def bfp_ROUND_CEIL(p, x):
    # x is a binary floating-point value that is represented in the binary
    # floating-point working format and has unbounded exponent range and
    # significand precision. x must be rounded as presented, without
    # prenormalization.
    #
    # p is an integer value specifying the precision (i.e., number of bits)
    # the significand is rounded to.

    result <- bfp_ROUND_HELPER(p, 0b0, 0b010, x)

    if bfp_COMPARE_GT(bfp_ABSOLUTE(result), bfp_ABSOLUTE(x)) then
        inc_flag <- 1
    else inc_flag <- 0  # TODO: does the spec specify this?
    if ¬bfp_COMPARE_EQ(result, x) then xx_flag <- 1
    else xx_flag <- 0  # TODO: does the spec specify this?

    return result

def bfp_ROUND_FLOOR(p, x):
    # x is a binary floating-point value that is represented in the binary
    # floating-point working format and has unbounded exponent range and
    # significand precision. x must be rounded as presented, without
    # prenormalization.
    #
    # p is an integer value specifying the precision (i.e., number of bits)
    # the significand is rounded to.

    result <- bfp_ROUND_HELPER(p, 0b0, 0b011, x)

    if bfp_COMPARE_GT(bfp_ABSOLUTE(result), bfp_ABSOLUTE(x)) then
        inc_flag <- 1
    else inc_flag <- 0  # TODO: does the spec specify this?
    if ¬bfp_COMPARE_EQ(result, x) then xx_flag <- 1
    else xx_flag <- 0  # TODO: does the spec specify this?

    return result

def bfp_ROUND_NEAR_AWAY(p, x):
    # not part of the PowerISA v3.1B specification.
    #
    # x is a binary floating-point value that is represented in the binary
    # floating-point working format and has unbounded exponent range and
    # significand precision. x must be rounded as presented, without
    # prenormalization.
    #
    # p is an integer value specifying the precision (i.e., number of bits)
    # the significand is rounded to.

    result <- bfp_ROUND_HELPER(p, 0b0, 0b100, x)

    if bfp_COMPARE_GT(bfp_ABSOLUTE(result), bfp_ABSOLUTE(x)) then
        inc_flag <- 1
    else inc_flag <- 0  # TODO: does the spec specify this?
    if ¬bfp_COMPARE_EQ(result, x) then xx_flag <- 1
    else xx_flag <- 0  # TODO: does the spec specify this?

    return result

def bfp_ROUND_ODD(p, x):
    # x is a binary floating-point value that is represented in the binary
    # floating-point working format and has unbounded exponent range and
    # significand precision. x must be rounded as presented, without
    # prenormalization.
    #
    # p is an integer value specifying the precision (i.e., number of bits)
    # the significand is rounded to.

    result <- bfp_ROUND_HELPER(p, 0b1, 0b000, x)

    if bfp_COMPARE_GT(bfp_ABSOLUTE(result), bfp_ABSOLUTE(x)) then
        inc_flag <- 1
    else inc_flag <- 0  # TODO: does the spec specify this?
    if ¬bfp_COMPARE_EQ(result, x) then xx_flag <- 1
    else xx_flag <- 0  # TODO: does the spec specify this?

    return result

def fprf_CLASS_BFP64(x):
    # x is a floating-point value represented in double-precision format.
    #
    # Return the 5-bit code that specifies the sign and class of x.

    v <- bfp_CONVERT_FROM_BFP64(x)
    if v.class.QNaN then return 0b10001
    if (v.sign = 1) & v.class.Infinity then return 0b01001
    if (v.sign = 0) & v.class.Infinity then return 0b00101
    if (v.sign = 1) & v.class.Zero then return 0b10010
    if (v.sign = 0) & v.class.Zero then return 0b00010
    if (v.sign = 1) & v.class.Denormal then return 0b11000
    if (v.sign = 0) & v.class.Denormal then return 0b10100
    if (v.sign = 1) & v.class.Normal then return 0b01000
    if (v.sign = 0) & v.class.Normal then return 0b00100

def fprf_CLASS_BFP32(x):
    # x is a floating-point value represented in single-precision format.
    #
    # Return the 5-bit code that specifies the sign and class of x.

    v <- bfp_CONVERT_FROM_BFP32(x)
    if v.class.QNaN then return 0b10001
    if (v.sign = 1) & v.class.Infinity then return 0b01001
    if (v.sign = 0) & v.class.Infinity then return 0b00101
    if (v.sign = 1) & v.class.Zero then return 0b10010
    if (v.sign = 0) & v.class.Zero then return 0b00010
    if (v.sign = 1) & v.class.Denormal then return 0b11000
    if (v.sign = 0) & v.class.Denormal then return 0b10100
    if (v.sign = 1) & v.class.Normal then return 0b01000
    if (v.sign = 0) & v.class.Normal then return 0b00100