root/sys/arch/mips/mips/fp.S
/*      $NetBSD: fp.S,v 1.60 2025/05/03 02:00:46 riastradh Exp $        */

/*
 * Copyright (c) 1992, 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Ralph Campbell.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      @(#)fp.s        8.1 (Berkeley) 6/10/93
 */

#ifdef _KERNEL_OPT
#include "opt_cputype.h"
#endif

#include <sys/cdefs.h>

#include <mips/asm.h>
#include <mips/trap.h>
#include <mips/cpuregs.h>

#include <mips/locore.h>

#include "assym.h"

#define SEXP_INF        0xff
#define DEXP_INF        0x7ff
#define SEXP_BIAS       127
#define DEXP_BIAS       1023
#define SEXP_MIN        -126
#define DEXP_MIN        -1022
#define SEXP_MAX        127
#define DEXP_MAX        1023
#define WEXP_MAX        30              /* maximum unbiased exponent for int */
#define WEXP_MIN        -1              /* minimum unbiased exponent for int */
#define SFRAC_BITS      23
#define DFRAC_BITS      52
#define SIMPL_ONE       0x00800000
#define DIMPL_ONE       0x00100000
#define SLEAD_ZEROS     31 - 23
#define DLEAD_ZEROS     31 - 20
#define STICKYBIT       1
#define GUARDBIT        0x80000000
#define SSIGNAL_NAN     0x00400000
#define DSIGNAL_NAN     0x00080000
#define SQUIET_NAN      0x003fffff
#define DQUIET_NAN0     0x0007ffff
#define DQUIET_NAN1     0xffffffff
#define INT_MIN         0x80000000
#define INT_MAX         0x7fffffff

#define COND_UNORDERED  0x1
#define COND_EQUAL      0x2
#define COND_LESS       0x4
#define COND_SIGNAL     0x8

#if defined(FPEMUL)
#if defined(__mips_o32) || defined(__mips_o64)
#define FPX_L                   INT_L
#define FPX_S                   INT_S
#define FPX_SCALESHIFT          INT_SCALESHIFT
#else
#define FPX_L                   LONG_L
#define FPX_S                   LONG_S
#define FPX_SCALESHIFT          LONG_SCALESHIFT
#define DFPX_L                  REG_L
#define DFPX_S                  REG_S
#define DFPX_SCALESHIFT         REG_SCALESHIFT
#define SZDFREG                 SZREG
#define DFPX_REGMASK            (0x1F << DFPX_SCALESHIFT)
#define DFPX_REGEVENMASK        (0x1E << DFPX_SCALESHIFT)
#endif
#define SZFREG                  (1 << FPX_SCALESHIFT)
#define FPX_REGMASK             (0x1F << FPX_SCALESHIFT)
#define FPX_REGEVENMASK         (0x1E << FPX_SCALESHIFT)
#define REG_REGMASK             (0x1F << REG_SCALESHIFT)
#endif

/* insns are reordered in the way as MIPS architecture imposes */
        .set    reorder

/*----------------------------------------------------------------------------
 *
 * mips_emul_fp --
 *
 *      Emulate unimplemented floating point operations.
 *      This routine should only be called by mips_fpu_intr().
 *
 *      mips_emul_fp(uint32_t instr, struct trapframe *tf, uint32_t cause)
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Floating point registers are modified according to instruction.
 *
 *----------------------------------------------------------------------------
 */
#if defined(__mips_o32) || defined(__mips_o64)
#define CALLFRAME_FRAME         (CALLFRAME_SIZ + 1*SZREG)       /* a1 slot */
#define CALLFRAME_CAUSE         (CALLFRAME_SIZ + 2*SZREG)       /* a2 slot */
#endif
#if defined(__mips_n32) || defined(__mips_n64)
#define CALLFRAME_FRAME         (1*SZREG)
#define CALLFRAME_CAUSE         (2*SZREG)
#if CALLFRAME_RA == CALLFRAME_FRAME || CALLFRAME_RA == CALLFRAME_CAUSE
#error N32/N64 ABI callframe error
#endif
#endif
NESTED(mips_emul_fp, CALLFRAME_SIZ, ra)
        PTR_SUBU sp, CALLFRAME_SIZ
        REG_S   ra, CALLFRAME_RA(sp)
        REG_S   a1, CALLFRAME_FRAME(sp)
        REG_S   a2, CALLFRAME_CAUSE(sp)
/*
 * Decode the FMT field (bits 25-21) and FUNCTION field (bits 5-0).
 */
        srl     v0, a0, 21 - PTR_SCALESHIFT     # get FMT field
        andi    v0, v0, 0x1F << PTR_SCALESHIFT  # mask FMT field
#ifdef FPEMUL
        PTR_L   t0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        PTR_L   a3, fmt_tbl(v0)                 # switch on FUNC & FMT
        INT_L   a2, PCB_FPREGS+FRAME_FSR(t0)
#else
        cfc1    a2, MIPS_FCSR                   # get exception register
        PTR_L   a3, fmt_tbl(v0)                 # switch on FUNC & FMT
        and     a2, a2, ~MIPS_FCSR_CAUSE        # clear exception
        ctc1    a2, MIPS_FCSR
#endif
        j       a3

single_op:
        andi    v0, a0, 0x3F            # get FUNC field
        sll     v0, v0, PTR_SCALESHIFT
        PTR_L   t9, func_single_tbl(v0)
        j       t9
double_op:
        andi    v0, a0, 0x3F            # get FUNC field
        sll     v0, v0, PTR_SCALESHIFT
        PTR_L   t9, func_double_tbl(v0)
        j       t9
single_fixed_op:
        andi    v0, a0, 0x3F            # get FUNC field
        sll     v0, v0, PTR_SCALESHIFT
        PTR_L   t9, func_single_fixed_tbl(v0)
        j       t9
long_fixed_op:
        andi    v0, a0, 0x3F            # get FUNC field
        sll     v0, v0, PTR_SCALESHIFT
        PTR_L   t9, func_long_fixed_tbl(v0)
        j       t9
#if (defined(__mips_n32) || defined(__mips_n64)) && 0
paired_single_op:
        andi    v0, a0, 0x3F            # get FUNC field
        sll     v0, v0, PTR_SCALESHIFT
        PTR_L   t9, func_paired_single_tbl(v0)
        j       t9
#else
#define paired_single_op        ill
#endif

#ifndef FPEMUL
#define mfromc1         ill
#define dmfromc1        ill
#define cfromc1         ill
#define mtoc1           ill
#define dmtoc1          ill
#define ctoc1           ill
#define branchc1        ill
#elif !(defined(__mips_n32) || defined(__mips_n64))
#define dmfromc1        ill
#define dmtoc1          ill
#endif
#define branchc1any2    ill
#define branchc1any4    ill
#define pairedsingle_op ill

/*
 * Single Precisions functions
 */
#define recip_s         ill
#define recip1_s        ill
#define recip2_s        ill
#define round_l_s       ill
#define trunc_l_s       ill
#define ceil_l_s        ill
#define floor_l_s       ill
#define cvt_l_s         ill
#define cvt_ps_s        ill
#define movcf_s         ill
#define movn_s          ill
#define movz_s          ill
#define rsqrt_s         ill
#define rsqrt1_s        ill
#define rsqrt2_s        ill
#ifndef MIPS3_PLUS
#define sqrt_s          ill
#define round_w_s       ill
#define trunc_w_s       ill
#define ceil_w_s        ill
#define floor_w_s       ill
#endif

/*
 * Double Precisions functions
 */
#ifndef MIPS3_PLUS
#define sqrt_d          ill
#define round_w_d       ill
#define trunc_w_d       ill
#define ceil_w_d        ill
#define floor_w_d       ill
#endif
#define round_l_d       ill
#define ceil_l_d        ill
#define trunc_l_d       ill
#define floor_l_d       ill
#define recip_d         ill
#define recip1_d        ill
#define recip2_d        ill
#define cvt_l_d         ill
#define movcf_d         ill
#define movz_d          ill
#define movn_d          ill
#define recip_d         ill
#define rsqrt_d         ill
#define rsqrt1_d        ill
#define rsqrt2_d        ill

/*
 * Long Fixed functions
 */
#define cvt_s_l         ill
#define cvt_d_l         ill
#define cvt_s_pu        ill

/*
 * Paired Single functions
 */
#define addr_ps         ill
#define abs_ps          ill
#define add_ps          ill
#define cmp_ps          ill
#define cvt_ps_pl       ill
#define cvt_ps_pw       ill
#define movcf_ps        ill
#define movz_ps         ill
#define movn_ps         ill
#define mov_ps          ill
#define mul_ps          ill
#define mulr_ps         ill
#define neg_ps          ill
#define recip1_ps       ill
#define recip2_ps       ill
#define rsqrt1_ps       ill
#define rsqrt2_ps       ill
#define sub_ps          ill

        .rdata
fmt_tbl:
        PTR_WORD mfromc1        # sub 0         mfc1
        PTR_WORD dmfromc1       # sub 1         dmfc1
        PTR_WORD cfromc1        # sub 2         cfc1
        PTR_WORD ill            # sub 3         mfhc1           MIPS32r2
        PTR_WORD mtoc1          # sub 4         mtc1
        PTR_WORD dmtoc1         # sub 5         dmtc1
        PTR_WORD ctoc1          # sub 6         ctc1
        PTR_WORD ill            # sub 7         mthc1           MIPS32r2
        PTR_WORD branchc1       # sub 8         bc1
        PTR_WORD branchc1any2   # sub 9         bc1any2         MIPS-3D ASE
        PTR_WORD branchc1any4   # sub 10        bc1any4         MIPS-3D ASE
        PTR_WORD ill            # sub 11
        PTR_WORD ill            # sub 12
        PTR_WORD ill            # sub 13
        PTR_WORD ill            # sub 14
        PTR_WORD ill            # sub 15
        PTR_WORD single_op      # sub 16        S
        PTR_WORD double_op      # sub 17        D
        PTR_WORD ill            # sub 18
        PTR_WORD ill            # sub 19
        PTR_WORD single_fixed_op # sub 20       W
        PTR_WORD long_fixed_op  # sub 21        L
        PTR_WORD paired_single_op # sub 22      PS
        PTR_WORD ill            # sub 23
        PTR_WORD ill            # sub 24
        PTR_WORD ill            # sub 25
        PTR_WORD ill            # sub 26
        PTR_WORD ill            # sub 27
        PTR_WORD ill            # sub 28
        PTR_WORD ill            # sub 29
        PTR_WORD ill            # sub 30
        PTR_WORD ill            # sub 31

func_single_tbl:
        PTR_WORD add_s          # func  0 00    ADD.S
        PTR_WORD sub_s          # func  1 01    SUB.S
        PTR_WORD mul_s          # func  2 02    MUL.S
        PTR_WORD div_s          # func  3 03    DIV.S
        PTR_WORD sqrt_s         # func  4 04    SQRT.S
        PTR_WORD abs_s          # func  5 05    ABS.S
        PTR_WORD mov_s          # func  6 06    MOV.S
        PTR_WORD neg_s          # func  7 07    NEG.S 
        PTR_WORD round_l_s      # func  8 10    ROUND.L.S       MIPS3/MIPS64
        PTR_WORD trunc_l_s      # func  9 11    TRUNC.L.S       MIPS3/MIPS64
        PTR_WORD ceil_l_s       # func 10 12    CEIL.L.S        MIPS3/MIPS64
        PTR_WORD floor_l_s      # func 11 13    FLOOR.L.S       MIPS3/MIPS64
        PTR_WORD round_w_s      # func 12 14    ROUND.W.S
        PTR_WORD trunc_w_s      # func 13 15    TRUNC.W.S
        PTR_WORD ceil_w_s       # func 14 16    CEIL.W.S
        PTR_WORD floor_w_s      # func 15 17    FLOOR.W.S
        PTR_WORD ill            # func 16 20
        PTR_WORD movcf_s        # func 17 21    MOVCF.S         MIPS32
        PTR_WORD movz_s         # func 18 22    MOVZ.S          MIPS32
        PTR_WORD movn_s         # func 19 23    MOVN.S          MIPS32
        PTR_WORD ill            # func 20 24
        PTR_WORD recip_s        # func 21 25    RECIP.S         MIPS32r2
        PTR_WORD rsqrt_s        # func 22 26    RSQRT.S         MIPS32r2
        PTR_WORD ill            # func 23 27
        PTR_WORD ill            # func 24 30
        PTR_WORD ill            # func 25 31
        PTR_WORD ill            # func 26 32
        PTR_WORD ill            # func 27 33
        PTR_WORD recip2_s       # func 28 34    RECIP2.S        MIPS-3D ASE
        PTR_WORD recip1_s       # func 29 35    RECIP1.S        MIPS-3D ASE
        PTR_WORD rsqrt1_s       # func 30 36    RSQRT1.S        MIPS-3D ASE
        PTR_WORD rsqrt2_s       # func 31 37    RSQRT2.S        MIPS-3D ASE
        PTR_WORD ill            # func 32 40
        PTR_WORD cvt_d_s        # func 33 41    CVT.D.S
        PTR_WORD ill            # func 34 42
        PTR_WORD ill            # func 35 43
        PTR_WORD cvt_w_s        # func 36 44    CVT.W.S
        PTR_WORD cvt_l_s        # func 37 45    CVT.L.S         MIPS3/MIPS64
        PTR_WORD cvt_ps_s       # func 38 46    CVT.PS.S        MIPS32r2
        PTR_WORD ill            # func 39 47
        PTR_WORD ill            # func 40 50
        PTR_WORD ill            # func 41 51
        PTR_WORD ill            # func 42 52
        PTR_WORD ill            # func 43 53
        PTR_WORD ill            # func 44 54
        PTR_WORD ill            # func 45 55
        PTR_WORD ill            # func 46 56
        PTR_WORD ill            # func 47 57
        PTR_WORD cmp_s          # func 48 60    C.F.S
        PTR_WORD cmp_s          # func 49 61    C.UN.S
        PTR_WORD cmp_s          # func 50 62    C.EQ.S
        PTR_WORD cmp_s          # func 51 63    C.UEQ.S
        PTR_WORD cmp_s          # func 52 64    C.OLT.S
        PTR_WORD cmp_s          # func 53 65    C.ULT.S
        PTR_WORD cmp_s          # func 54 66    C.OLE.S
        PTR_WORD cmp_s          # func 55 67    C.ULE.S
        PTR_WORD cmp_s          # func 56 70    C.SF.S
        PTR_WORD cmp_s          # func 57 71    C.NGLE.S
        PTR_WORD cmp_s          # func 58 72    C.SEQ.S
        PTR_WORD cmp_s          # func 59 73    C.NGL.S
        PTR_WORD cmp_s          # func 60 74    C.LT.S
        PTR_WORD cmp_s          # func 61 75    C.NGE.S
        PTR_WORD cmp_s          # func 62 76    C.LE.S
        PTR_WORD cmp_s          # func 63 77    C.NGT.S

func_double_tbl:
        PTR_WORD add_d          # func  0 00    ADD.D
        PTR_WORD sub_d          # func  1 01    SUB.D
        PTR_WORD mul_d          # func  2 02    MUL.D
        PTR_WORD div_d          # func  3 03    DIV.D
        PTR_WORD sqrt_d         # func  4 04    SQRT.D
        PTR_WORD abs_d          # func  5 05    ABS.D
        PTR_WORD mov_d          # func  6 06    MOV.D
        PTR_WORD neg_d          # func  7 07    NEG.D 
        PTR_WORD round_l_d      # func  8 10    ROUND.L.D       MIPS3/MIPS64
        PTR_WORD trunc_l_d      # func  9 11    TRUNC.L.D       MIPS3/MIPS64
        PTR_WORD ceil_l_d       # func 10 12    CEIL.L.D        MIPS3/MIPS64
        PTR_WORD floor_l_d      # func 11 13    FLOOR.L.D       MIPS3/MIPS64
        PTR_WORD round_w_d      # func 12 14    ROUND.W.D
        PTR_WORD trunc_w_d      # func 13 15    TRUNC.W.D
        PTR_WORD ceil_w_d       # func 14 16    CEIL.W.D
        PTR_WORD floor_w_d      # func 15 17    FLOOR.W.D
        PTR_WORD ill            # func 16 20
        PTR_WORD movcf_d        # func 17 21    MOVCF.D         MIPS32
        PTR_WORD movz_d         # func 18 22    MOVZ.D          MIPS32
        PTR_WORD movn_d         # func 19 23    MOVN.D          MIPS32
        PTR_WORD ill            # func 20 24
        PTR_WORD recip_d        # func 21 25    RECIP.D         MIPS32r2
        PTR_WORD rsqrt_d        # func 22 26    RSQRT.D         MIPS32r2
        PTR_WORD ill            # func 23 27
        PTR_WORD ill            # func 24 30
        PTR_WORD ill            # func 25 31
        PTR_WORD ill            # func 26 32
        PTR_WORD ill            # func 27 33
        PTR_WORD recip2_d       # func 28 34    RECIP2.D        MIPS-3D ASE
        PTR_WORD recip2_d       # func 29 35    RECIP1.D        MIPS-3D ASE
        PTR_WORD rsqrt1_d       # func 30 36    RSQRT1.D        MIPS-3D ASE
        PTR_WORD rsqrt2_d       # func 31 37    RSQRT2.D        MIPS-3D ASE
        PTR_WORD cvt_s_d        # func 32 40    CVT.S.D
        PTR_WORD ill            # func 33 41
        PTR_WORD ill            # func 34 42
        PTR_WORD ill            # func 35 43
        PTR_WORD cvt_w_d        # func 36 44    CVT.W.D
        PTR_WORD cvt_l_d        # func 37 45    CVT.L.D         MIPS3/MIPS64
        PTR_WORD ill            # func 38 46
        PTR_WORD ill            # func 39 47
        PTR_WORD ill            # func 40 50
        PTR_WORD ill            # func 41 51
        PTR_WORD ill            # func 42 52
        PTR_WORD ill            # func 43 53
        PTR_WORD ill            # func 44 54
        PTR_WORD ill            # func 45 55
        PTR_WORD ill            # func 46 56
        PTR_WORD ill            # func 47 57
        PTR_WORD cmp_d          # func 48 60    C.F.D
        PTR_WORD cmp_d          # func 49 61    C.UN.D
        PTR_WORD cmp_d          # func 50 62    C.EQ.D
        PTR_WORD cmp_d          # func 51 63    C.UEQ.D
        PTR_WORD cmp_d          # func 52 64    C.OLT.D
        PTR_WORD cmp_d          # func 53 65    C.ULT.D
        PTR_WORD cmp_d          # func 54 66    C.OLE.D
        PTR_WORD cmp_d          # func 55 67    C.ULE.D
        PTR_WORD cmp_d          # func 56 70    C.SF.D
        PTR_WORD cmp_d          # func 57 71    C.NGLE.D
        PTR_WORD cmp_d          # func 58 72    C.SEQ.D
        PTR_WORD cmp_d          # func 59 73    C.NGL.D
        PTR_WORD cmp_d          # func 60 74    C.LT.D
        PTR_WORD cmp_d          # func 61 75    C.NGE.D
        PTR_WORD cmp_d          # func 62 76    C.LE.D
        PTR_WORD cmp_d          # func 63 77    C.NGT.D

func_single_fixed_tbl:
        PTR_WORD ill            # func  0 00
        PTR_WORD ill            # func  1 01
        PTR_WORD ill            # func  2 02
        PTR_WORD ill            # func  3 03
        PTR_WORD ill            # func  4 04
        PTR_WORD ill            # func  5 05
        PTR_WORD ill            # func  6 06
        PTR_WORD ill            # func  7 07
        PTR_WORD ill            # func  8 10
        PTR_WORD ill            # func  9 11
        PTR_WORD ill            # func 10 12
        PTR_WORD ill            # func 11 13
        PTR_WORD ill            # func 12 14
        PTR_WORD ill            # func 13 15
        PTR_WORD ill            # func 14 16
        PTR_WORD ill            # func 15 17
        PTR_WORD ill            # func 16 20
        PTR_WORD ill            # func 17 21
        PTR_WORD ill            # func 18 22
        PTR_WORD ill            # func 19 23
        PTR_WORD ill            # func 20 24
        PTR_WORD ill            # func 21 25
        PTR_WORD ill            # func 22 26
        PTR_WORD ill            # func 23 27
        PTR_WORD ill            # func 24 30
        PTR_WORD ill            # func 25 31
        PTR_WORD ill            # func 26 32
        PTR_WORD ill            # func 27 33
        PTR_WORD ill            # func 28 34
        PTR_WORD ill            # func 29 35
        PTR_WORD ill            # func 30 36
        PTR_WORD ill            # func 31 37
        PTR_WORD cvt_s_w        # func 32 40    CVT.S.W
        PTR_WORD cvt_d_w        # func 33 41    CVT.D.W
        PTR_WORD ill            # func 34 42
        PTR_WORD ill            # func 35 43
        PTR_WORD ill            # func 36 44
        PTR_WORD ill            # func 37 45
        PTR_WORD cvt_ps_pw      # func 38 46    CVT.PS.PW       MIPS-3D ASE
        PTR_WORD ill            # func 39 47
        PTR_WORD ill            # func 40 50
        PTR_WORD ill            # func 41 51
        PTR_WORD ill            # func 42 52
        PTR_WORD ill            # func 43 53
        PTR_WORD ill            # func 44 54
        PTR_WORD ill            # func 45 55
        PTR_WORD ill            # func 46 56
        PTR_WORD ill            # func 47 57
        PTR_WORD ill            # func 48 60
        PTR_WORD ill            # func 49 61
        PTR_WORD ill            # func 50 62
        PTR_WORD ill            # func 51 63
        PTR_WORD ill            # func 52 64
        PTR_WORD ill            # func 53 65
        PTR_WORD ill            # func 54 66
        PTR_WORD ill            # func 55 67
        PTR_WORD ill            # func 56 70
        PTR_WORD ill            # func 57 71
        PTR_WORD ill            # func 58 72
        PTR_WORD ill            # func 59 73
        PTR_WORD ill            # func 60 74
        PTR_WORD ill            # func 61 75
        PTR_WORD ill            # func 62 76
        PTR_WORD ill            # func 63 77

func_long_fixed_tbl:
        PTR_WORD ill            # func  0 00
        PTR_WORD ill            # func  1 01
        PTR_WORD ill            # func  2 02
        PTR_WORD ill            # func  3 03
        PTR_WORD ill            # func  4 04
        PTR_WORD ill            # func  5 05
        PTR_WORD ill            # func  6 06
        PTR_WORD ill            # func  7 07
        PTR_WORD ill            # func  8 10
        PTR_WORD ill            # func  9 11
        PTR_WORD ill            # func 10 12
        PTR_WORD ill            # func 11 13
        PTR_WORD ill            # func 12 14
        PTR_WORD ill            # func 13 15
        PTR_WORD ill            # func 14 16
        PTR_WORD ill            # func 15 17
        PTR_WORD ill            # func 16 20
        PTR_WORD ill            # func 17 21
        PTR_WORD ill            # func 18 22
        PTR_WORD ill            # func 19 23
        PTR_WORD ill            # func 20 24
        PTR_WORD ill            # func 21 25
        PTR_WORD ill            # func 22 26
        PTR_WORD ill            # func 23 27
        PTR_WORD ill            # func 24 30
        PTR_WORD ill            # func 25 31
        PTR_WORD ill            # func 26 32
        PTR_WORD ill            # func 27 33
        PTR_WORD ill            # func 28 34
        PTR_WORD ill            # func 29 35
        PTR_WORD ill            # func 30 36
        PTR_WORD ill            # func 31 37
        PTR_WORD cvt_s_l        # func 32 40    CVT.S.L         MIPS3/MIPS64
        PTR_WORD cvt_d_l        # func 33 41    CVT.D.L         MIPS3/MIPS64
        PTR_WORD ill            # func 34 42
        PTR_WORD ill            # func 35 43
        PTR_WORD ill            # func 36 44
        PTR_WORD ill            # func 37 45
        PTR_WORD cvt_ps_pl      # func 38 46    CVT.PS.PL       MIPS-3D ASE
        PTR_WORD ill            # func 39 47
        PTR_WORD ill            # func 40 50
        PTR_WORD ill            # func 41 51
        PTR_WORD ill            # func 42 52
        PTR_WORD ill            # func 43 53
        PTR_WORD ill            # func 44 54
        PTR_WORD ill            # func 45 55
        PTR_WORD ill            # func 46 56
        PTR_WORD ill            # func 47 57
        PTR_WORD ill            # func 48 60
        PTR_WORD ill            # func 49 61
        PTR_WORD ill            # func 50 62
        PTR_WORD ill            # func 51 63
        PTR_WORD ill            # func 52 64
        PTR_WORD ill            # func 53 65
        PTR_WORD ill            # func 54 66
        PTR_WORD ill            # func 55 67
        PTR_WORD ill            # func 56 70
        PTR_WORD ill            # func 57 71
        PTR_WORD ill            # func 58 72
        PTR_WORD ill            # func 59 73
        PTR_WORD ill            # func 60 74
        PTR_WORD ill            # func 61 75
        PTR_WORD ill            # func 62 76
        PTR_WORD ill            # func 63 77

#if defined(MIPS3_PLUS) && 0
func_paired_single_tbl:
        PTR_WORD add_ps         # func  0 00    ADD.PS
        PTR_WORD sub_ps         # func  1 01    SUB.PS
        PTR_WORD mul_ps         # func  2 02    MUL.PS
        PTR_WORD ill            # func  3 03
        PTR_WORD ill            # func  4 04
        PTR_WORD abs_ps         # func  5 05    ABS.PS
        PTR_WORD mov_ps         # func  6 06    MOV.PS
        PTR_WORD neg_ps         # func  7 07    NEG.PS 
        PTR_WORD ill            # func  8 10
        PTR_WORD ill            # func  9 11
        PTR_WORD ill            # func 10 12
        PTR_WORD ill            # func 11 13
        PTR_WORD ill            # func 12 14
        PTR_WORD ill            # func 13 15
        PTR_WORD ill            # func 14 16
        PTR_WORD ill            # func 15 17
        PTR_WORD ill            # func 16 20
        PTR_WORD movcf_ps       # func 17 21    MOVCF.PS        MIPS32
        PTR_WORD movz_ps        # func 18 22    MOVZ.PS         MIPS32
        PTR_WORD movn_ps        # func 19 23    MOVN.PS         MIPS32
        PTR_WORD ill            # func 20 24
        PTR_WORD ill            # func 21 25
        PTR_WORD ill            # func 22 26
        PTR_WORD ill            # func 23 27
        PTR_WORD addr_ps        # func 24 30    ADDR.PS         MIPS-3D ASE
        PTR_WORD ill            # func 25 31
        PTR_WORD mulr_ps        # func 26 32    MULR.PS         MIPS-3D ASE
        PTR_WORD ill            # func 27 33
        PTR_WORD recip2_ps      # func 28 34    RECIP2.PS       MIPS-3D ASE
        PTR_WORD recip1_ps      # func 29 35    RECIP1.PS       MIPS-3D ASE
        PTR_WORD rsqrt1_ps      # func 30 36    RSQRT1.PS       MIPS-3D ASE
        PTR_WORD rsqrt2_ps      # func 31 37    RSQRT2.PS       MIPS-3D ASE
        PTR_WORD cvt_s_pu       # func 32 40    CVT.S.PU
        PTR_WORD ill            # func 33 41
        PTR_WORD ill            # func 34 42
        PTR_WORD ill            # func 35 43
        PTR_WORD ill            # func 36 44    CVT.PW.PS
        PTR_WORD ill            # func 37 45
        PTR_WORD ill            # func 38 46
        PTR_WORD ill            # func 39 47
        PTR_WORD ill            # func 40 50    CVT.S.PL
        PTR_WORD ill            # func 41 51
        PTR_WORD ill            # func 42 52
        PTR_WORD ill            # func 43 53
        PTR_WORD ill            # func 44 54    PLL.PS
        PTR_WORD ill            # func 45 55    PLU.PS
        PTR_WORD ill            # func 46 56    PUL.PS
        PTR_WORD ill            # func 47 57    PUU.PS
        PTR_WORD cmp_ps         # func 48 60    C.F
        PTR_WORD cmp_ps         # func 49 61    C.UN
        PTR_WORD cmp_ps         # func 50 62    C.EQ
        PTR_WORD cmp_ps         # func 51 63    C.UEQ
        PTR_WORD cmp_ps         # func 52 64    C.OLT
        PTR_WORD cmp_ps         # func 53 65    C.ULT
        PTR_WORD cmp_ps         # func 54 66    C.OLE
        PTR_WORD cmp_ps         # func 55 67    C.ULE
        PTR_WORD cmp_ps         # func 56 70    C.SF
        PTR_WORD cmp_ps         # func 57 71    C.NGLE
        PTR_WORD cmp_ps         # func 58 72    C.SEQ
        PTR_WORD cmp_ps         # func 59 73    C.NGL
        PTR_WORD cmp_ps         # func 60 74    C.LT
        PTR_WORD cmp_ps         # func 61 75    C.NGE
        PTR_WORD cmp_ps         # func 62 76    C.LE
        PTR_WORD cmp_ps         # func 63 77    C.NGT
#endif

        .text

#ifdef FPEMUL
mfromc1:
        srl     t1, a0, 11-FPX_SCALESHIFT       # fs is in bits 15:11
        PTR_L   t0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        andi    t1, t1, FPX_REGMASK
        PTR_ADDU t0, t0, t1

        FPX_L   v0, PCB_FPREGS+FRAME_FP0(t0)

        srl     t0, a0, 16-REG_SCALESHIFT
        andi    t0, t0, REG_REGMASK
        PTR_ADDU t0, t0, a1

        REG_PROLOGUE
        REG_S   v0, TF_REG_ZERO(t0)
        REG_EPILOGUE

        b       done

mtoc1:
        REG_PROLOGUE
        REG_S   zero, TF_REG_ZERO(a1)           # ensure zero has value 0
        srl     t0, a0, 16-REG_SCALESHIFT
        andi    t0, t0, REG_REGMASK
        PTR_ADDU v0, a1, t0
        REG_L   v0, TF_REG_ZERO(v0)
        REG_EPILOGUE

        srl     t1, a0, 11-FPX_SCALESHIFT
        PTR_L   t0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        andi    t1, t1, FPX_REGMASK
        PTR_ADDU t0, t0, t1

        FPX_S   v0, PCB_FPREGS+FRAME_FP0(t0)

        b       done

#if defined(FPEMUL) && (defined(__mips_n32) || defined(__mips_n64))
dmfromc1:
        srl     t1, a0, 11-DFPX_SCALESHIFT      # fs is in bits 15:11
        PTR_L   t0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        andi    t1, t1,  DFPX_REGMASK
        PTR_ADDU t0, t0, t1

        DFPX_L  v0, PCB_FPREGS+FRAME_FP0(t0)

        srl     t0, a0, 16-REG_SCALESHIFT
        andi    t0, t0, REG_REGMASK
        PTR_ADDU t0, t0, a1

        REG_PROLOGUE
        REG_S   v0, TF_REG_ZERO(t0)
        REG_EPILOGUE

        b       done

dmtoc1:
        REG_PROLOGUE
        REG_S   zero, TF_REG_ZERO(a1)           # ensure zero has value 0
        srl     t0, a0, 16-REG_SCALESHIFT
        andi    t0, t0, REG_REGMASK
        PTR_ADDU v0, a1, t0
        REG_L   v0, TF_REG_ZERO(v0)
        REG_EPILOGUE

        srl     t1, a0, 11-DFPX_SCALESHIFT
        PTR_L   t0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        andi    t1, t1, DFPX_REGMASK
        PTR_ADDU t0, t0, t1

        DFPX_S  v0, PCB_FPREGS+FRAME_FP0(t0)

        b       done
#endif /* FPEMUL && (__mips_n32 || __mips_n64) */

cfromc1:
        srl     t1, a0, 11
        PTR_L   t0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        andi    t1, t1, 0x001F
        li      t2, 0x1F
        move    v0, zero
        bne     t1, t2, cfinvalid

        INT_L   v0, PCB_FPREGS+FRAME_FSR(t0)

cfinvalid:

        srl     t0, a0, 16-REG_SCALESHIFT
        andi    t0, t0, REG_REGMASK
        PTR_ADDU t0, t0, a1

        REG_PROLOGUE
        REG_S   v0, TF_REG_ZERO(t0)
        REG_EPILOGUE

        b       done

ctoc1:
        REG_PROLOGUE
        REG_S   zero, TF_REG_ZERO(a1)           # ensure zero has value 0
        REG_EPILOGUE

        srl     t0, a0, 11
        andi    t0, t0, 0x001F
        li      t1, 0x1F
        bne     t0, t1, done

        srl     t0, a0, 16-REG_SCALESHIFT
        andi    t0, t0, REG_REGMASK
        PTR_ADDU v0, a1, t0
        REG_PROLOGUE
        REG_L   v0, TF_REG_ZERO(v0)
        REG_EPILOGUE
        PTR_L   t0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        #nop
        INT_S   v0, PCB_FPREGS+FRAME_FSR(t0)

        b       done

branchc1:
        srl     v0, a0, 16 - PTR_SCALESHIFT
        andi    v0, v0, 0x1f << PTR_SCALESHIFT
        PTR_L   t9, branchc1_tbl(v0)
        j       t9

        .rdata
branchc1_tbl:
        PTR_WORD bcfalse        # br 0
        PTR_WORD bctrue         # br 1
        PTR_WORD bcfalse_l      # br 2
        PTR_WORD bctrue_l       # br 3
        PTR_WORD ill            # br 4
        PTR_WORD ill            # br 5
        PTR_WORD ill            # br 6
        PTR_WORD ill            # br 7
        PTR_WORD ill            # br 8
        PTR_WORD ill            # br 9
        PTR_WORD ill            # br 10
        PTR_WORD ill            # br 11
        PTR_WORD ill            # br 12
        PTR_WORD ill            # br 13
        PTR_WORD ill            # br 14
        PTR_WORD ill            # br 15
        PTR_WORD ill            # br 16
        PTR_WORD ill            # br 17
        PTR_WORD ill            # br 18
        PTR_WORD ill            # br 19
        PTR_WORD ill            # br 20
        PTR_WORD ill            # br 21
        PTR_WORD ill            # br 22
        PTR_WORD ill            # br 23
        PTR_WORD ill            # br 24
        PTR_WORD ill            # br 25
        PTR_WORD ill            # br 26
        PTR_WORD ill            # br 27
        PTR_WORD ill            # br 28
        PTR_WORD ill            # br 29
        PTR_WORD ill            # br 30
        PTR_WORD ill            # br 31

        .text

bcfalse:
        li      v0, MIPS_FCSR_FCC0
        and     v0, v0, a2
        beq     v0, zero, bcemul_branch
        b       done
bctrue:
        li      v0, MIPS_FCSR_FCC0
        and     v0, v0, a2
        bne     v0, zero, bcemul_branch
        b       done
bcfalse_l:
        li      v0, MIPS_FCSR_FCC0
        and     v0, v0, a2
        beq     v0, zero, bcemul_branch
        REG_PROLOGUE
        REG_L   v0, TF_REG_EPC(a1)
        addiu   v0, v0, 4
        REG_S   v0, TF_REG_EPC(a1)
        REG_EPILOGUE
        b       done
bctrue_l:
        li      v0, MIPS_FCSR_FCC0
        and     v0, v0, a2
        bne     v0, zero, bcemul_branch
        REG_PROLOGUE
        REG_L   v0, TF_REG_EPC(a1)
        addiu   v0, v0, 4
        REG_S   v0, TF_REG_EPC(a1)
        REG_EPILOGUE
        b       done

bcemul_branch:
        /* Fetch delay slot instruction */
        REG_L   a1, CALLFRAME_FRAME(sp)
        REG_PROLOGUE
        REG_L   a0, TF_REG_EPC(a1)
        REG_EPILOGUE
        PTR_ADDU a0, 4
        jal     _C_LABEL(mips_ufetch32)

        move    a0, v0
        REG_L   a1, CALLFRAME_FRAME(sp)
        REG_L   a2, CALLFRAME_CAUSE(sp)

        /* Update cause */
        li      t0, MIPS_CR_BR_DELAY
        or      a2, a2, t0

        /* Free mips_emul_fp call frame */
        REG_L   ra, CALLFRAME_RA(sp)
        PTR_ADDU sp, CALLFRAME_SIZ

        j       _C_LABEL(mips_emul_branchdelayslot)
#endif

/*
 * Single precision subtract.
 */
sub_s:
        jal     _C_LABEL(get_ft_fs_s)
        xor     ta0, ta0, 1                     # negate FT sign bit
        b       add_sub_s
/*
 * Single precision add.
 */
add_s:
        jal     _C_LABEL(get_ft_fs_s)
add_sub_s:
        bne     t1, SEXP_INF, 1f                # is FS an infinity?
        bne     ta1, SEXP_INF, result_fs_s      # if FT is not inf, result=FS
        bne     t2, zero, result_fs_s           # if FS is NAN, result is FS
        bne     ta2, zero, result_ft_s          # if FT is NAN, result is FT
        bne     t0, ta0, invalid_s              # both infinities same sign?
        b       result_fs_s                     # result is in FS
1:
        beq     ta1, SEXP_INF, result_ft_s      # if FT is inf, result=FT
        bne     t1, zero, 4f                    # is FS a denormalized num?
        beq     t2, zero, 3f                    # is FS zero?
        bne     ta1, zero, 2f                   # is FT a denormalized num?
        beq     ta2, zero, result_fs_s          # FT is zero, result=FS
        jal     _C_LABEL(renorm_fs_s)
        jal     _C_LABEL(renorm_ft_s)
        b       5f
2:
        jal     _C_LABEL(renorm_fs_s)
        subu    ta1, ta1, SEXP_BIAS             # unbias FT exponent
        or      ta2, ta2, SIMPL_ONE             # set implied one bit
        b       5f
3:
        bne     ta1, zero, result_ft_s          # if FT != 0, result=FT
        bne     ta2, zero, result_ft_s
        and     v0, a2, MIPS_FCSR_RM            # get rounding mode
        bne     v0, MIPS_FCSR_RM_RM, 1f         # round to -infinity?
        or      t0, t0, ta0                     # compute result sign
        b       result_fs_s
1:
        and     t0, t0, ta0                     # compute result sign
        b       result_fs_s
4:
        bne     ta1, zero, 2f                   # is FT a denormalized num?
        beq     ta2, zero, result_fs_s          # FT is zero, result=FS
        subu    t1, t1, SEXP_BIAS               # unbias FS exponent
        or      t2, t2, SIMPL_ONE               # set implied one bit
        jal     _C_LABEL(renorm_ft_s)
        b       5f
2:
        subu    t1, t1, SEXP_BIAS               # unbias FS exponent
        or      t2, t2, SIMPL_ONE               # set implied one bit
        subu    ta1, ta1, SEXP_BIAS             # unbias FT exponent
        or      ta2, ta2, SIMPL_ONE             # set implied one bit
/*
 * Perform the addition.
 */
5:
        move    t9, zero                        # no shifted bits (sticky reg)
        beq     t1, ta1, 4f                     # no shift needed
        subu    v0, t1, ta1                     # v0 = difference of exponents
        move    v1, v0                          # v1 = abs(difference)
        bge     v0, zero, 1f
        negu    v1
1:
        ble     v1, SFRAC_BITS+2, 2f            # is difference too great?
        li      t9, STICKYBIT                   # set the sticky bit
        bge     v0, zero, 1f                    # check which exp is larger
        move    t1, ta1                         # result exp is FTs
        move    t2, zero                        # FSs fraction shifted is zero
        b       4f
1:
        move    ta2, zero                       # FTs fraction shifted is zero
        b       4f
2:
        li      t9, 32                          # compute 32 - abs(exp diff)
        subu    t9, t9, v1
        bgt     v0, zero, 3f                    # if FS > FT, shift FTs frac
        move    t1, ta1                         # FT > FS, result exp is FTs
        sll     t9, t2, t9                      # save bits shifted out
        srl     t2, t2, v1                      # shift FSs fraction
        b       4f
3:
        sll     t9, ta2, t9                     # save bits shifted out
        srl     ta2, ta2, v1                    # shift FTs fraction
4:
        bne     t0, ta0, 1f                     # if signs differ, subtract
        addu    t2, t2, ta2                     # add fractions
        b       norm_s
1:
        blt     t2, ta2, 3f                     # subtract larger from smaller
        bne     t2, ta2, 2f                     # if same, result=0
        move    t1, zero                        # result=0
        move    t2, zero
        and     v0, a2, MIPS_FCSR_RM            # get rounding mode
        bne     v0, MIPS_FCSR_RM_RM, 1f         # round to -infinity?
        or      t0, t0, ta0                     # compute result sign
        b       result_fs_s
1:
        and     t0, t0, ta0                     # compute result sign
        b       result_fs_s
2:
        sltu    v0, zero, t9                    # compute t2:zero - ta2:t9
        subu    t9, zero, t9
        subu    t2, t2, ta2                     # subtract fractions
        subu    t2, t2, v0                      # subtract barrow
        b       norm_s
3:
        move    t0, ta0                         # sign of result = FTs
        sltu    v0, zero, t9                    # compute ta2:zero - t2:t9
        subu    t9, zero, t9
        subu    t2, ta2, t2                     # subtract fractions
        subu    t2, t2, v0                      # subtract barrow
        b       norm_s

/*
 * Double precision subtract.
 */
sub_d:
        jal     _C_LABEL(get_ft_fs_d)
        xor     ta0, ta0, 1                     # negate sign bit
        b       add_sub_d
/*
 * Double precision add.
 */
add_d:
        jal     _C_LABEL(get_ft_fs_d)
add_sub_d:
        bne     t1, DEXP_INF, 1f                # is FS an infinity?
        bne     ta1, DEXP_INF, result_fs_d      # if FT is not inf, result=FS
        bne     t2, zero, result_fs_d           # if FS is NAN, result is FS
        bne     t3, zero, result_fs_d
        bne     ta2, zero, result_ft_d          # if FT is NAN, result is FT
        bne     ta3, zero, result_ft_d
        bne     t0, ta0, invalid_d              # both infinities same sign?
        b       result_fs_d                     # result is in FS
1:
        beq     ta1, DEXP_INF, result_ft_d      # if FT is inf, result=FT
        bne     t1, zero, 4f                    # is FS a denormalized num?
        bne     t2, zero, 1f                    # is FS zero?
        beq     t3, zero, 3f
1:
        bne     ta1, zero, 2f                   # is FT a denormalized num?
        bne     ta2, zero, 1f
        beq     ta3, zero, result_fs_d          # FT is zero, result=FS
1:
        jal     _C_LABEL(renorm_fs_d)
        jal     _C_LABEL(renorm_ft_d)
        b       5f
2:
        jal     _C_LABEL(renorm_fs_d)
        subu    ta1, ta1, DEXP_BIAS             # unbias FT exponent
        or      ta2, ta2, DIMPL_ONE             # set implied one bit
        b       5f
3:
        bne     ta1, zero, result_ft_d          # if FT != 0, result=FT
        bne     ta2, zero, result_ft_d
        bne     ta3, zero, result_ft_d
        and     v0, a2, MIPS_FCSR_RM            # get rounding mode
        bne     v0, MIPS_FCSR_RM_RM, 1f         # round to -infinity?
        or      t0, t0, ta0                     # compute result sign
        b       result_fs_d
1:
        and     t0, t0, ta0                     # compute result sign
        b       result_fs_d
4:
        bne     ta1, zero, 2f                   # is FT a denormalized num?
        bne     ta2, zero, 1f
        beq     ta3, zero, result_fs_d          # FT is zero, result=FS
1:
        subu    t1, t1, DEXP_BIAS               # unbias FS exponent
        or      t2, t2, DIMPL_ONE               # set implied one bit
        jal     _C_LABEL(renorm_ft_d)
        b       5f
2:
        subu    t1, t1, DEXP_BIAS               # unbias FS exponent
        or      t2, t2, DIMPL_ONE               # set implied one bit
        subu    ta1, ta1, DEXP_BIAS             # unbias FT exponent
        or      ta2, ta2, DIMPL_ONE             # set implied one bit
/*
 * Perform the addition.
 */
5:
        move    t9, zero                        # no shifted bits (sticky reg)
        beq     t1, ta1, 4f                     # no shift needed
        subu    v0, t1, ta1                     # v0 = difference of exponents
        move    v1, v0                          # v1 = abs(difference)
        bge     v0, zero, 1f
        negu    v1
1:
        ble     v1, DFRAC_BITS+2, 2f            # is difference too great?
        li      t9, STICKYBIT                   # set the sticky bit
        bge     v0, zero, 1f                    # check which exp is larger
        move    t1, ta1                         # result exp is FTs
        move    t2, zero                        # FSs fraction shifted is zero
        move    t3, zero
        b       4f
1:
        move    ta2, zero                       # FTs fraction shifted is zero
        move    ta3, zero
        b       4f
2:
        li      t9, 32
        bge     v0, zero, 3f                    # if FS > FT, shift FTs frac
        move    t1, ta1                         # FT > FS, result exp is FTs
        blt     v1, t9, 1f                      # shift right by < 32?
        subu    v1, v1, t9
        subu    t9, t9, v1
        sll     v0, t2, t9                      # save bits shifted out
        sltu    t9, zero, t3                    # dont lose any one bits
        or      t9, t9, v0                      # save sticky bit
        srl     t3, t2, v1                      # shift FSs fraction
        move    t2, zero
        b       4f
1:
        subu    v0, t9, v1
        sll     t9, t3, v0                      # save bits shifted out
        srl     t3, t3, v1                      # shift FSs fraction
        sll     v0, t2, v0                      # save bits shifted out of t2
        or      t3, t3, v0                      # and put into t3
        srl     t2, t2, v1
        b       4f
3:
        blt     v1, t9, 1f                      # shift right by < 32?
        subu    v1, v1, t9
        subu    v0, t9, v1
        sll     t9, ta2, v0                     # save bits shifted out
        srl     ta3, ta2, v1                    # shift FTs fraction
        move    ta2, zero
        b       4f
1:
        subu    v0, t9, v1
        sll     t9, ta3, v0                     # save bits shifted out
        srl     ta3, ta3, v1                    # shift FTs fraction
        sll     v0, ta2, v0                     # save bits shifted out of t2
        or      ta3, ta3, v0                    # and put into t3
        srl     ta2, ta2, v1
4:
        bne     t0, ta0, 1f                     # if signs differ, subtract
        addu    t3, t3, ta3                     # add fractions
        sltu    v0, t3, ta3                     # compute carry
        addu    t2, t2, ta2                     # add fractions
        addu    t2, t2, v0                      # add carry
        b       norm_d
1:
        blt     t2, ta2, 3f                     # subtract larger from smaller
        bne     t2, ta2, 2f
        bltu    t3, ta3, 3f
        bne     t3, ta3, 2f                     # if same, result=0
        move    t1, zero                        # result=0
        move    t2, zero
        move    t3, zero
        and     v0, a2, MIPS_FCSR_RM            # get rounding mode
        bne     v0, MIPS_FCSR_RM_RM, 1f         # round to -infinity?
        or      t0, t0, ta0                     # compute result sign
        b       result_fs_d
1:
        and     t0, t0, ta0                     # compute result sign
        b       result_fs_d
2:
        beq     t9, zero, 1f                    # compute t2:t3:zero - ta2:ta3:t9
        subu    t9, zero, t9
        sltu    v0, t3, 1                       # compute barrow out
        subu    t3, t3, 1                       # subtract barrow
        subu    t2, t2, v0
1:
        sltu    v0, t3, ta3
        subu    t3, t3, ta3                     # subtract fractions
        subu    t2, t2, ta2                     # subtract fractions
        subu    t2, t2, v0                      # subtract barrow
        b       norm_d
3:
        move    t0, ta0                         # sign of result = FTs
        beq     t9, zero, 1f                    # compute ta2:ta3:zero - t2:t3:t9
        subu    t9, zero, t9
        sltu    v0, ta3, 1                      # compute barrow out
        subu    ta3, ta3, 1                     # subtract barrow
        subu    ta2, ta2, v0
1:
        sltu    v0, ta3, t3
        subu    t3, ta3, t3                     # subtract fractions
        subu    t2, ta2, t2                     # subtract fractions
        subu    t2, t2, v0                      # subtract barrow
        b       norm_d

/*
 * Single precision multiply.
 */
mul_s:
        jal     _C_LABEL(get_ft_fs_s)
        xor     t0, t0, ta0                     # compute sign of result
        move    ta0, t0
        bne     t1, SEXP_INF, 2f                # is FS an infinity?
        bne     t2, zero, result_fs_s           # if FS is a NAN, result=FS
        bne     ta1, SEXP_INF, 1f               # FS is inf, is FT an infinity?
        bne     ta2, zero, result_ft_s          # if FT is a NAN, result=FT
        b       result_fs_s                     # result is infinity
1:
        bne     ta1, zero, result_fs_s          # inf * zero? if no, result=FS
        bne     ta2, zero, result_fs_s
        b       invalid_s                       # infinity * zero is invalid
2:
        bne     ta1, SEXP_INF, 1f               # FS != inf, is FT an infinity?
        bne     t1, zero, result_ft_s           # zero * inf? if no, result=FT
        bne     t2, zero, result_ft_s
        bne     ta2, zero, result_ft_s          # if FT is a NAN, result=FT
        b       invalid_s                       # zero * infinity is invalid
1:
        bne     t1, zero, 1f                    # is FS zero?
        beq     t2, zero, result_fs_s           # result is zero
        jal     _C_LABEL(renorm_fs_s)
        b       2f
1:
        subu    t1, t1, SEXP_BIAS               # unbias FS exponent
        or      t2, t2, SIMPL_ONE               # set implied one bit
2:
        bne     ta1, zero, 1f                   # is FT zero?
        beq     ta2, zero, result_ft_s          # result is zero
        jal     _C_LABEL(renorm_ft_s)
        b       2f
1:
        subu    ta1, ta1, SEXP_BIAS             # unbias FT exponent
        or      ta2, ta2, SIMPL_ONE             # set implied one bit
2:
        addu    t1, t1, ta1                     # compute result exponent
        addu    t1, t1, 9                       # account for binary point
        multu   t2, ta2                         # multiply fractions
        mflo    t9
        mfhi    t2
        b       norm_s

/*
 * Double precision multiply.
 */
mul_d:
        jal     _C_LABEL(get_ft_fs_d)
        xor     t0, t0, ta0                     # compute sign of result
        move    ta0, t0
        bne     t1, DEXP_INF, 2f                # is FS an infinity?
        bne     t2, zero, result_fs_d           # if FS is a NAN, result=FS
        bne     t3, zero, result_fs_d
        bne     ta1, DEXP_INF, 1f               # FS is inf, is FT an infinity?
        bne     ta2, zero, result_ft_d          # if FT is a NAN, result=FT
        bne     ta3, zero, result_ft_d
        b       result_fs_d                     # result is infinity
1:
        bne     ta1, zero, result_fs_d          # inf * zero? if no, result=FS
        bne     ta2, zero, result_fs_d
        bne     ta3, zero, result_fs_d
        b       invalid_d                       # infinity * zero is invalid
2:
        bne     ta1, DEXP_INF, 1f               # FS != inf, is FT an infinity?
        bne     t1, zero, result_ft_d           # zero * inf? if no, result=FT
        bne     t2, zero, result_ft_d           # if FS is a NAN, result=FS
        bne     t3, zero, result_ft_d
        bne     ta2, zero, result_ft_d          # if FT is a NAN, result=FT
        bne     ta3, zero, result_ft_d
        b       invalid_d                       # zero * infinity is invalid
1:
        bne     t1, zero, 2f                    # is FS zero?
        bne     t2, zero, 1f
        beq     t3, zero, result_fs_d           # result is zero
1:
        jal     _C_LABEL(renorm_fs_d)
        b       3f
2:
        subu    t1, t1, DEXP_BIAS               # unbias FS exponent
        or      t2, t2, DIMPL_ONE               # set implied one bit
3:
        bne     ta1, zero, 2f                   # is FT zero?
        bne     ta2, zero, 1f
        beq     ta3, zero, result_ft_d          # result is zero
1:
        jal     _C_LABEL(renorm_ft_d)
        b       3f
2:
        subu    ta1, ta1, DEXP_BIAS             # unbias FT exponent
        or      ta2, ta2, DIMPL_ONE             # set implied one bit
3:
        addu    t1, t1, ta1                     # compute result exponent
        addu    t1, t1, 12                      # ???
        multu   t3, ta3                         # multiply fractions (low * low)
        move    ta0, t2                         # free up t2,t3 for result
        move    ta1, t3
        mflo    a3                              # save low order bits
        mfhi    t9
        not     v0, t9
        multu   ta0, ta3                                # multiply FS(high) * FT(low)
        mflo    v1
        mfhi    t3                              # init low result
        sltu    v0, v0, v1                      # compute carry
        addu    t9, v1
        multu   ta1, ta2                                # multiply FS(low) * FT(high)
        addu    t3, t3, v0                      # add carry
        not     v0, t9
        mflo    v1
        mfhi    t2
        sltu    v0, v0, v1
        addu    t9, v1
        multu   ta0, ta2                                # multiply FS(high) * FT(high)
        addu    t3, v0
        not     v1, t3
        sltu    v1, v1, t2
        addu    t3, t2
        not     v0, t3
        mfhi    t2
        addu    t2, v1
        mflo    v1
        sltu    v0, v0, v1
        addu    t2, v0
        addu    t3, v1
        sltu    a3, zero, a3                    # reduce t9,a3 to just t9
        or      t9, a3
        b       norm_d

/*
 * Single precision divide.
 */
div_s:
        jal     _C_LABEL(get_ft_fs_s)
        xor     t0, t0, ta0                     # compute sign of result
        move    ta0, t0
        bne     t1, SEXP_INF, 1f                # is FS an infinity?
        bne     t2, zero, result_fs_s           # if FS is NAN, result is FS
        bne     ta1, SEXP_INF, result_fs_s      # is FT an infinity?
        bne     ta2, zero, result_ft_s          # if FT is NAN, result is FT
        b       invalid_s                       # infinity/infinity is invalid
1:
        bne     ta1, SEXP_INF, 1f               # is FT an infinity?
        bne     ta2, zero, result_ft_s          # if FT is NAN, result is FT
        move    t1, zero                        # x / infinity is zero
        move    t2, zero
        b       result_fs_s
1:
        bne     t1, zero, 2f                    # is FS zero?
        bne     t2, zero, 1f
        bne     ta1, zero, result_fs_s          # FS=zero, is FT zero?
        beq     ta2, zero, invalid_s            # 0 / 0
        b       result_fs_s                     # result = zero
1:
        jal     _C_LABEL(renorm_fs_s)
        b       3f
2:
        subu    t1, t1, SEXP_BIAS               # unbias FS exponent
        or      t2, t2, SIMPL_ONE               # set implied one bit
3:
        bne     ta1, zero, 2f                   # is FT zero?
        bne     ta2, zero, 1f
        or      a2, a2, MIPS_FCSR_CAUSE_Z | MIPS_FCSR_FLAGS_Z
        and     v0, a2, MIPS_FCSR_ENABLES_Z     # trap enabled?
        bne     v0, zero, fpe_trap
#ifdef FPEMUL
        PTR_L   t1, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        #nop
        INT_S   a2, PCB_FPREGS+FRAME_FSR(t1)
#else
        ctc1    a2, MIPS_FCSR                   # save exceptions
#endif
        li      t1, SEXP_INF                    # result is infinity
        move    t2, zero
        b       result_fs_s
1:
        jal     _C_LABEL(renorm_ft_s)
        b       3f
2:
        subu    ta1, ta1, SEXP_BIAS             # unbias FT exponent
        or      ta2, ta2, SIMPL_ONE             # set implied one bit
3:
        subu    t1, t1, ta1                     # compute exponent
        subu    t1, t1, 3                       # compensate for result position
        li      v0, SFRAC_BITS+3                # number of bits to divide
        move    t9, t2                          # init dividend
        move    t2, zero                        # init result
1:
        bltu    t9, ta2, 3f                     # is dividend >= divisor?
2:
        subu    t9, t9, ta2                     # subtract divisor from dividend
        or      t2, t2, 1                       # remember that we did
        bne     t9, zero, 3f                    # if not done, continue
        sll     t2, t2, v0                      # shift result to final position
        b       norm_s
3:
        sll     t9, t9, 1                       # shift dividend
        sll     t2, t2, 1                       # shift result
        subu    v0, v0, 1                       # are we done?
        bne     v0, zero, 1b                    # no, continue
        b       norm_s

/*
 * Double precision divide.
 */
div_d:
        jal     _C_LABEL(get_ft_fs_d)
        xor     t0, t0, ta0                     # compute sign of result
        move    ta0, t0
        bne     t1, DEXP_INF, 1f                # is FS an infinity?
        bne     t2, zero, result_fs_d           # if FS is NAN, result is FS
        bne     t3, zero, result_fs_d
        bne     ta1, DEXP_INF, result_fs_d      # is FT an infinity?
        bne     ta2, zero, result_ft_d          # if FT is NAN, result is FT
        bne     ta3, zero, result_ft_d
        b       invalid_d                       # infinity/infinity is invalid
1:
        bne     ta1, DEXP_INF, 1f               # is FT an infinity?
        bne     ta2, zero, result_ft_d          # if FT is NAN, result is FT
        bne     ta3, zero, result_ft_d
        move    t1, zero                        # x / infinity is zero
        move    t2, zero
        move    t3, zero
        b       result_fs_d
1:
        bne     t1, zero, 2f                    # is FS zero?
        bne     t2, zero, 1f
        bne     t3, zero, 1f
        bne     ta1, zero, result_fs_d          # FS=zero, is FT zero?
        bne     ta2, zero, result_fs_d
        beq     ta3, zero, invalid_d            # 0 / 0
        b       result_fs_d                     # result = zero
1:
        jal     _C_LABEL(renorm_fs_d)
        b       3f
2:
        subu    t1, t1, DEXP_BIAS               # unbias FS exponent
        or      t2, t2, DIMPL_ONE               # set implied one bit
3:
        bne     ta1, zero, 2f                   # is FT zero?
        bne     ta2, zero, 1f
        bne     ta3, zero, 1f
        or      a2, a2, MIPS_FCSR_CAUSE_Z | MIPS_FCSR_FLAGS_Z
        and     v0, a2, MIPS_FCSR_ENABLES_Z     # trap enabled?
        bne     v0, zero, fpe_trap
#ifdef FPEMUL
        PTR_L   t1, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        #nop
        INT_S   a2, PCB_FPREGS+FRAME_FSR(t1)
#else
        ctc1    a2, MIPS_FCSR                   # save exceptions
#endif
        li      t1, DEXP_INF                    # result is infinity
        move    t2, zero
        move    t3, zero
        b       result_fs_d
1:
        jal     _C_LABEL(renorm_ft_d)
        b       3f
2:
        subu    ta1, ta1, DEXP_BIAS             # unbias FT exponent
        or      ta2, ta2, DIMPL_ONE             # set implied one bit
3:
        subu    t1, t1, ta1                     # compute exponent
        subu    t1, t1, 3                       # compensate for result position
        li      v0, DFRAC_BITS+3                # number of bits to divide
        move    t9, t2                          # init dividend
        move    v1, t3
        move    t2, zero                        # init result
        move    t3, zero
1:
        bltu    t9, ta2, 3f                     # is dividend >= divisor?
        bne     t9, ta2, 2f
        bltu    v1, ta3, 3f
2:
        .set    noat
        sltu    AT, v1, ta3                     # subtract divisor from dividend
        subu    v1, v1, ta3
        subu    t9, t9, ta2
        subu    t9, t9, AT
        .set    at
        or      t3, t3, 1                       # remember that we did
        bne     t9, zero, 3f                    # if not done, continue
        bne     v1, zero, 3f
        li      v1, 32                          # shift result to final position
        blt     v0, v1, 2f                      # shift < 32 bits?
        subu    v0, v0, v1                      # shift by > 32 bits
        sll     t2, t3, v0                      # shift upper part
        move    t3, zero
        b       norm_d
2:
        .set    noat
        subu    v1, v1, v0                      # shift by < 32 bits
        sll     t2, t2, v0                      # shift upper part
        srl     AT, t3, v1                      # save bits shifted out
        or      t2, t2, AT                      # and put into upper part
        sll     t3, t3, v0
        b       norm_d
        .set    at
3:
        .set    noat
        sll     t9, t9, 1                       # shift dividend
        srl     AT, v1, 31                      # save bit shifted out
        or      t9, t9, AT                      # and put into upper part
        sll     v1, v1, 1
        sll     t2, t2, 1                       # shift result
        srl     AT, t3, 31                      # save bit shifted out
        or      t2, t2, AT                      # and put into upper part
        sll     t3, t3, 1
        subu    v0, v0, 1                       # are we done?
        bne     v0, zero, 1b                    # no, continue
        sltu    v0, zero, v1                    # be sure to save any one bits
        or      t9, t9, v0                      # from the lower remainder
        b       norm_d
        .set    at

#ifdef MIPS3_PLUS
sqrt_s:
        jal     _C_LABEL(get_fs_s)

        /* Take care of zero, negative, inf, and NaN special cases */
        or      v0, t1, t2                      # sqrt(+-0) == +-0
        beq     v0, zero, result_fs_s           # ...
        bne     t0, zero, 1f                    # sqrt(-val) == sNaN
        bne     t1, SEXP_INF, 2f                # skip forward if not infinity
        b       result_fs_s                     # sqrt(NaN,+inf) == itself
1:      move    t0, zero                        # result is a quiet NAN
        li      t1, SEXP_INF                    # sqrt(-inf,-val) == sNaN
        li      t2, SQUIET_NAN
        b       result_fs_s
2:
        /* normalize FS if needed */
        bne     t1, zero, 2f
        jal     _C_LABEL(renorm_fs_s)
2:      and     t2, t2, (SIMPL_ONE-1)           # ix &= 0x007fffff;
        or      t2, t2, SIMPL_ONE               # ix |= 0x00800000;
        and     v0, t1, 1                       # if (m & 1)
        beq     v0, zero, 1f                    # ...
        add     t2, t2, t2                      #       ix += ix;
1:      sra     t1, t1, 1                       # m = m / 2;

        /* generate sqrt(FS) bit by bit */
        add     t2, t2, t2                      # ix += ix;
        move    ta0, zero                       # q = 0; (result)
        li      t9, SIMPL_ONE<<1                # r = 0x01000000;
        move    ta2, zero                       # s = 0;
1:      beq     t9, zero, 3f                    # while (r != 0) {
        add     v0, ta2, t9                     #       t = s + r;
        bgt     v0, t2, 2f                      #       if (t <= ix)
        add     ta2, v0, t9                     #               s = t + r;
        sub     t2, t2, v0                      #               ix -= t;
        add     ta0, ta0, t9                    #               q += r;
2:      add     t2, t2, t2                      #       ix += ix;
        srl     t9, t9, 1                       #       r >>= 1;
        b       1b                              # }
3:
        /* rounding -- all mips rounding modes use the same rounding here */
        beq     t2, zero, 1f                    # if (ix != 0)
        and     v0, ta0, 1                      # q += q&1;
        add     ta0, ta0, v0                    # ...

        /* calculate result */
1:      srl     t2, ta0, 1                      # ix = (q >> 1);
        add     t1, t1, SEXP_BIAS               # m += 127; (re-bias)
        li      v1, SIMPL_ONE
        and     v0, t2, v1                      # keep extra exponent bit
        bne     v0, zero, 1f                    # if it is there.
        sub     t1, t1, 1                       # ...
1:
        nor     v1, v1, v1                      # ~SIMP_ONE
        and     t2, t2, v1                      # ix &= ~SIMPL_ONE
        b       result_fs_s                     # store result (already normal)

sqrt_d:
        jal     _C_LABEL(get_fs_d)

        /* Take care of zero, negative, inf, and NaN special cases */
        or      v0, t1, t2                      # sqrt(+-0) == +- 0
        or      v0, v0, t3                      # ...
        beq     v0, zero, result_fs_d           # ...
        bne     t0, zero, 1f                    # sqrt(-val) == sNaN
        bne     t1, DEXP_INF, 2f                # skip forward if not infinity
        b       result_fs_d                     # sqrt(NaN,+inf) == itself
1:      move    t0, zero                        # sqrt(-inf,-val) == sNaN
        li      t1, DEXP_INF
        li      t2, DQUIET_NAN0
        li      t3, DQUIET_NAN1
        b       result_fs_d
2:
        /* normalize FS if needed */
        bne     t1, zero, 2f
        jal     _C_LABEL(renorm_fs_d)
2:      and     t2, t2, (DIMPL_ONE-1)           # ix0 &= 0x000fffff
        or      t2, t2, DIMPL_ONE               # ix0 |= 0x00100000
        and     v0, t1, 1                       # if (m & 1)
        beq     v0, zero, 1f                    # ...
        add     t2, t2, t2                      # ix0 += ix0
        srl     v0, t3, 31                      # ix0 += (ix1&sign)>>31)
        and     v0, v0, 1                       # ...
        add     t2, t2, v0                      # ...
        addu    t3, t3, t3                      # ix1 += ix1;
1:      sra     t1, t1, 1                       # m = m / 2;

        /* generate sqrt(FS) bit by bit -- first upper */
        addu    t2, t2, t2                      # ix0 += ix0;
        srl     v0, t3, 31                      # ix0 += (ix1&sign)>>31)
        and     v0, v0, 1                       # ...
        add     t2, t2, v0                      # ...
        addu    t3, t3, t3                      # ix1 += ix1;

        move    ta0, zero                       # q = 0;        (result)
        move    ta1, zero                       # q1 = 0;       (result)
        move    ta2, zero                       # s0 = 0;
        move    ta3, zero                       # s1 = 0;
        li      t9, DIMPL_ONE<<1                # t = 0x00200000;
1:      beq     t9, zero, 3f                    # while (r != 0) {
        add     v0, ta2, t9                     #       t = s0+r;
        bgt     v0, t2, 2f                      #       if (t <= ix0)
        add     ta2, v0, t9                     #               s0 = t + r;
        sub     t2, t2, v0                      #               ix0 -= t;
        add     ta0, ta0, t9                    #               q += r;
2:      add     t2, t2, t2                      #       ix0 += ix0;
        srl     v0, t3, 31                      #       ix0 += (ix1&sign)>>31)
        and     v0, v0, 1                       #       ...
        add     t2, t2, v0                      #       ...
        addu    t3, t3, t3                      #       ix1 += ix1;
        srl     t9, t9, 1                       #       r >>= 1;
        b       1b                              # }
3:
        /* then lower bits */
        li      t9, 1<<31                       # r = sign;
1:      beq     t9, zero, 4f                    # while (r != 0) {
        addu    v1, ta3, t9                     #    t1 = s1 + r;
        move    v0, ta2                         #    t = s0;
        blt     v0, t2, 2f                      #    if ( (t<ix0) ||
        bne     v0, t2, 3f                      #         ((t == ix0) &&
        bgtu    v1, t3, 3f                      #          (t1 <= ix1)))
2:      addu    ta3, v1, t9                     #       s1 = t1 + r;
        .set    noat
        srl     AT, v1, 31                      #       if (((t1&sign)==sign) &&
        and     AT, AT, 1                       #       ...
        beq     AT, zero, 2f                    #       ...
        srl     AT, ta3, 31                     #           (s1&sign) == 0)
        and     AT, AT, 1                       #           ...
        bne     AT, zero, 2f                    #           ...
        add     ta2, ta2, 1                     #           s0 += 1;
        .set    at
2:      sub     t2, t2, v0                      #       ix0 -= t;
        bgeu    t3, v1, 2f                      #       if (ix1 < t1)
        sub     t2, t2, 1                       #           ix0 -= 1;
2:      subu    t3, t3, v1                      #       ix1 -= t1;
        addu    ta1, ta1, t9                    #       q1 += r;
3:      add     t2, t2, t2                      #    ix0 += ix0;
        srl     v0, t3, 31                      #    ix0 += (ix1&sign)>>31)
        and     v0, v0, 1                       #    ...
        add     t2, t2, v0                      #    ...
        addu    t3, t3, t3                      #    ix1 += ix1;
        srl     t9, t9, 1                       #    r >>= 1;
        b       1b                              # }
4:

        /* rounding -- all mips rounding modes use the same rounding here */
        or      v0, t2, t3                      # if (ix0 | ix1)
        beq     v0, zero, 2f                    # ...
        li      v0, 0xffffffff                  #    if (q1 == 0xffffffff)
        and     v1, t2, v0                      #    ...
        bne     v1, v0, 1f                      #    ...
        move    ta1, zero                       #       q1 = 0;
        add     ta0, ta0, 1                     #       q += 1;
        b       2f                              #    else
1:      and     v0, ta1, 1                      #       q1 += q1 & 1;
        addu    ta1, ta1, v0                    #       ...

        /* calculate result */
2:      srl     t2, ta0, 1                      # ix0 = q >> 1;
        srl     t3, ta1, 1                      # ix1 = q1 >> 1;
        and     v0, ta0, 1                      # if ((q & 1) == 1)
        beq     v0, zero, 1f                    # ...
        or      t3, (1<<31)                     #       ix1 |= sign;
1:      add     t1, t1, DEXP_BIAS               # m += 1023;
        li      v1, DIMPL_ONE
        and     v0, t2, v1                      # keep extra exponent bit
        bne     v0, zero, 1f                    # if it is there.
        sub     t1, t1, 1                       # ...
1:
        nor     v1, v1, v1                      # ~DIMPL_ONE
        and     t2, t2, v1                      # ix0 &= ~DIMPL_ONE
        b       result_fs_d                     # store result (already normal)
#endif  /* MIPS3_PLUS */

/*
 * Single precision absolute value.
 */
abs_s:
        jal     _C_LABEL(get_fs_s)
        move    t0, zero                        # set sign positive
        b       result_fs_s

/*
 * Double precision absolute value.
 */
abs_d:
        jal     _C_LABEL(get_fs_d)
        move    t0, zero                        # set sign positive
        b       result_fs_d

/*
 * Single precision move.
 */
mov_s:
        jal     _C_LABEL(get_fs_s)
        b       result_fs_s

/*
 * Double precision move.
 */
mov_d:
        jal     _C_LABEL(get_fs_d)
        b       result_fs_d

/*
 * Single precision negate.
 */
neg_s:
        jal     _C_LABEL(get_fs_s)
        xor     t0, t0, 1                       # reverse sign
        b       result_fs_s

/*
 * Double precision negate.
 */
neg_d:
        jal     _C_LABEL(get_fs_d)
        xor     t0, t0, 1                       # reverse sign
        b       result_fs_d

#ifdef MIPS3_PLUS
/*
 * Single precision mips2 rounding.  Explicit case of cvt_w_s.
 */
round_w_s:
        li      v1,0
        b       _cvt_w_s
trunc_w_s:
        li      v1,1
        b       _cvt_w_s
ceil_w_s:
        li      v1,2
        b       _cvt_w_s
floor_w_s:
        li      v1,3
        b       _cvt_w_s

/*
 * Double precision mips2 rounding.  Explicit case of cvt_w_d.
 */
round_w_d:
        li      v1,0
        b       _cvt_w_d
trunc_w_d:
        li      v1,1
        b       _cvt_w_d
ceil_w_d:
        li      v1,2
        b       _cvt_w_d
floor_w_d:
        li      v1,3
        b       _cvt_w_d
#endif /* MIPS3_PLUS */

/*
 * Convert double to single.
 */
cvt_s_d:
        jal     _C_LABEL(get_fs_d)
        bne     t1, DEXP_INF, 1f                # is FS an infinity?
        li      t1, SEXP_INF                    # convert to single
        sll     t2, t2, 3                       # convert D fraction to S
        srl     t9, t3, 32 - 3
        or      t2, t2, t9
        b       result_fs_s
1:
        bne     t1, zero, 2f                    # is FS zero?
        bne     t2, zero, 1f
        beq     t3, zero, result_fs_s           # result=0
1:
        jal     _C_LABEL(renorm_fs_d)
        subu    t1, t1, 3                       # correct exp for shift below
        b       3f
2:
        subu    t1, t1, DEXP_BIAS               # unbias exponent
        or      t2, t2, DIMPL_ONE               # add implied one bit
3:
        sll     t2, t2, 3                       # convert D fraction to S
        srl     t9, t3, 32 - 3
        or      t2, t2, t9
        sll     t9, t3, 3
        b       norm_noshift_s

/*
 * Convert integer to single.
 */
cvt_s_w:
        jal     _C_LABEL(get_fs_int)
        bne     t2, zero, .Lcvtswnot0           # check for zero
        move    t1, zero
        b       result_fs_s
/*
 * Find out how many leading zero bits are in t2 and put in v1.
 */
.Lcvtswnot0:
#if __mips == 32 || __mips == 64
        clz     v1, t2
#else
        .set    noat

        move    v0, t2
        move    v1, zero
        srl     AT, v0, 16
        bne     AT, zero, 1f
        addu    v1, 16
        sll     v0, 16
1:
        srl     AT, v0, 24
        bne     AT, zero, 1f
        addu    v1, 8
        sll     v0, 8
1:
        srl     AT, v0, 28
        bne     AT, zero, 1f
        addu    v1, 4
        sll     v0, 4
1:
        srl     AT, v0, 30
        bne     AT, zero, 1f
        addu    v1, 2
        sll     v0, 2
1:
        srl     AT, v0, 31
        bne     AT, zero, 1f
        addu    v1, 1
        .set    at
#endif /* __mips == 32 || __mips == 64 */
/*
 * Now shift t2 the correct number of bits.
 */
1:
        subu    v1, v1, SLEAD_ZEROS             # dont count leading zeros
        li      t1, 23                          # init exponent
        subu    t1, t1, v1                      # compute exponent
        beq     v1, zero, 1f
        li      v0, 32
        blt     v1, zero, 2f                    # if shift < 0, shift right
        subu    v0, v0, v1
        sll     t2, t2, v1                      # shift left
1:
        add     t1, t1, SEXP_BIAS               # bias exponent
        and     t2, t2, ~SIMPL_ONE              # clear implied one bit
        b       result_fs_s
2:
        negu    v1                              # shift right by v1
        subu    v0, v0, v1
        sll     t9, t2, v0                      # save bits shifted out
        srl     t2, t2, v1
        b       norm_noshift_s

/*
 * Convert single to double.
 */
cvt_d_s:
        jal     _C_LABEL(get_fs_s)
        move    t3, zero
        bne     t1, SEXP_INF, 1f                # is FS an infinity?
        li      t1, DEXP_INF                    # convert to double
        b       result_fs_d
1:
        bne     t1, zero, 2f                    # is FS denormalized or zero?
        beq     t2, zero, result_fs_d           # is FS zero?
        jal     _C_LABEL(renorm_fs_s)
        move    t9, zero
        sll     t3, t2, 32 - 3                  # convert S fraction to D
        srl     t2, t2, 3
        b       norm_d
2:
        addu    t1, t1, DEXP_BIAS - SEXP_BIAS   # bias exponent correctly
        sll     t3, t2, 32 - 3                  # convert S fraction to D
        srl     t2, t2, 3
        b       result_fs_d

/*
 * Convert integer to double.
 */
cvt_d_w:
        jal     _C_LABEL(get_fs_int)
        bne     t2, zero, .Lcvtdwnot0           # check for zero
        move    t1, zero                        # result=0
        move    t3, zero
        b       result_fs_d
/*
 * Find out how many leading zero bits are in t2 and put in v1.
 */
.Lcvtdwnot0:
#if __mips == 32 || __mips == 64
        clz     v1, t2
#else /* __mips == 32 || __mips == 64 */
        .set    noat

        move    v0, t2
        move    v1, zero
        srl     AT, v0, 16
        bne     AT, zero, 1f
        addu    v1, 16
        sll     v0, 16
1:
        srl     AT, v0, 24
        bne     AT, zero, 1f
        addu    v1, 8
        sll     v0, 8
1:
        srl     AT, v0, 28
        bne     AT, zero, 1f
        addu    v1, 4
        sll     v0, 4
1:
        srl     AT, v0, 30
        bne     AT, zero, 1f
        addu    v1, 2
        sll     v0, 2
1:
        srl     AT, v0, 31
        bne     AT, zero, 1f
        addu    v1, 1
1:
        .set    at
#endif /* __mips == 32 || __mips == 64 */
/*
 * Now shift t2 the correct number of bits.
 */
        subu    v1, v1, DLEAD_ZEROS             # dont count leading zeros
        li      t1, DEXP_BIAS + 20              # init exponent
        subu    t1, t1, v1                      # compute exponent
        beq     v1, zero, 1f
        li      v0, 32
        blt     v1, zero, 2f                    # if shift < 0, shift right
        subu    v0, v0, v1
        sll     t2, t2, v1                      # shift left
1:
        and     t2, t2, ~DIMPL_ONE              # clear implied one bit
        move    t3, zero
        b       result_fs_d
2:
        negu    v1                              # shift right by v1
        subu    v0, v0, v1
        sll     t3, t2, v0
        srl     t2, t2, v1
        and     t2, t2, ~DIMPL_ONE              # clear implied one bit
        b       result_fs_d

/*
 * Convert single to integer.
 */
cvt_w_s:
        and     v1, a2, MIPS_FCSR_RM            # get rounding mode
_cvt_w_s:
        jal     _C_LABEL(get_fs_s)
        bne     t1, SEXP_INF, 1f                # is FS an infinity?
        bne     t2, zero, invalid_w             # invalid conversion
1:
        bne     t1, zero, 1f                    # is FS zero?
        beq     t2, zero, result_fs_w           # result is zero
        move    t2, zero                        # result is an inexact zero
        b       inexact_w
1:
        subu    t1, t1, SEXP_BIAS               # unbias exponent
        or      t2, t2, SIMPL_ONE               # add implied one bit
        sll     t3, t2, 32 - 3                  # convert S fraction to D
        srl     t2, t2, 3
        b       cvt_w

/*
 * Convert double to integer.
 */
cvt_w_d:
        and     v1, a2, MIPS_FCSR_RM            # get rounding mode
_cvt_w_d:
        jal     _C_LABEL(get_fs_d)
        bne     t1, DEXP_INF, 1f                # is FS an infinity?
        bne     t2, zero, invalid_w             # invalid conversion
        bne     t3, zero, invalid_w             # invalid conversion
1:
        bne     t1, zero, 2f                    # is FS zero?
        bne     t2, zero, 1f
        beq     t3, zero, result_fs_w           # result is zero
1:
        move    t2, zero                        # result is an inexact zero
        b       inexact_w
2:
        subu    t1, t1, DEXP_BIAS               # unbias exponent
        or      t2, t2, DIMPL_ONE               # add implied one bit
cvt_w:
#if 0
        blt     t1, WEXP_MIN, underflow_w       # is exponent too small?
#else
        bge     t1, WEXP_MIN, 3f                # is exponent too small?
        beq     v1, MIPS_FCSR_RM_RP, 1f         # round to +infinity
        beq     v1, MIPS_FCSR_RM_RM, 2f         # round to -infinity

        move    t2, zero
        b       result_fs_w
1:
        xori    t2, t0, 1
        b       result_fs_w
2:
        sll     t2, t0, 31
        sra     t2, t2, 31
        b       result_fs_w

3:
#endif
        li      v0, WEXP_MAX+1
        bgt     t1, v0, overflow_w              # is exponent too large?
        bne     t1, v0, 1f                      # special check for INT_MIN
        beq     t0, zero, overflow_w            # if positive, overflow
        bne     t2, DIMPL_ONE, overflow_w
        bne     t3, zero, overflow_w
        li      t2, INT_MIN                     # result is INT_MIN
        b       result_fs_w
1:
        subu    v0, t1, 20                      # compute amount to shift
        beq     v0, zero, 2f                    # is shift needed?
        li      v1, 32
        blt     v0, zero, 1f                    # if shift < 0, shift right
        subu    v1, v1, v0                      # shift left
        sll     t2, t2, v0
        srl     v1, t3, v1                      # save bits shifted out of t3
        or      t2, t2, v1                      # and put into t2
        sll     t3, t3, v0                      # shift FSs fraction
        b       2f
1:
        negu    v0                              # shift right by v0
        subu    v1, v1, v0
        sll     t9, t3, v1                      # save bits shifted out
        sltu    t9, zero, t9                    # dont lose any ones
        srl     t3, t3, v0                      # shift FSs fraction
        or      t3, t3, t9
        sll     v1, t2, v1                      # save bits shifted out of t2
        or      t3, t3, v1                      # and put into t3
        srl     t2, t2, v0
/*
 * round result (t0 is sign, t2 is integer part, t3 is fractional part).
 */
2:
        and     v0, a2, MIPS_FCSR_RM            # get rounding mode
        beq     v0, MIPS_FCSR_RM_RN, 3f         # round to nearest
        beq     v0, MIPS_FCSR_RM_RZ, 5f         # round to zero (truncate)
        beq     v0, MIPS_FCSR_RM_RP, 1f         # round to +infinity
        beq     t0, zero, 5f                    # if sign is positive, truncate
        b       2f
1:
        bne     t0, zero, 5f                    # if sign is negative, truncate
2:
        beq     t3, zero, 5f                    # if no fraction bits, continue
        addu    t2, t2, 1                       # add rounding bit
        blt     t2, zero, overflow_w            # overflow?
        b       5f
3:
        li      v0, GUARDBIT                    # load guard bit for rounding
        addu    v0, v0, t3                      # add remainder
        sltu    v1, v0, t3                      # compute carry out
        beq     v1, zero, 4f                    # if no carry, continue
        addu    t2, t2, 1                       # add carry to result
        blt     t2, zero, overflow_w            # overflow?
4:
        bne     v0, zero, 5f                    # if rounded remainder is zero
        and     t2, t2, ~1                      #  clear LSB (round to nearest)
5:
        beq     t0, zero, 1f                    # result positive?
        negu    t2                              # convert to negative integer
1:
        beq     t3, zero, result_fs_w           # is result exact?
/*
 * Handle inexact exception.
 */
inexact_w:
        or      a2, a2, MIPS_FCSR_CAUSE_I | MIPS_FCSR_FLAGS_I
        and     v0, a2, MIPS_FCSR_ENABLES_I
        bne     v0, zero, fpe_trap
#ifdef FPEMUL
        PTR_L   v0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        #nop
        INT_S   a2, PCB_FPREGS+FRAME_FSR(v0)
#else
        ctc1    a2, MIPS_FCSR                   # save exceptions
#endif
        b       result_fs_w

/*
 * Conversions to integer which overflow will trap (if enabled),
 * or generate an inexact trap (if enabled),
 * or generate an invalid exception.
 */
overflow_w:
        or      a2, a2, MIPS_FCSR_CAUSE_O | MIPS_FCSR_FLAGS_O
        and     v0, a2, MIPS_FCSR_ENABLES_O
        bne     v0, zero, fpe_trap
        and     v0, a2, MIPS_FCSR_ENABLES_I
        bne     v0, zero, inexact_w             # inexact traps enabled?
        b       invalid_w

/*
 * Conversions to integer which underflow will trap (if enabled),
 * or generate an inexact trap (if enabled),
 * or generate an invalid exception.
 */
underflow_w:
        or      a2, a2, MIPS_FCSR_CAUSE_U | MIPS_FCSR_FLAGS_U
        and     v0, a2, MIPS_FCSR_ENABLES_U
        bne     v0, zero, fpe_trap
        and     v0, a2, MIPS_FCSR_ENABLES_I
        bne     v0, zero, inexact_w             # inexact traps enabled?
        b       invalid_w

/*
 * Compare single.
 */
cmp_s:
        jal     _C_LABEL(get_cmp_s)
        bne     t1, SEXP_INF, 1f                # is FS an infinity?
        bne     t2, zero, unordered             # FS is a NAN
1:
        bne     ta1, SEXP_INF, 2f               # is FT an infinity?
        bne     ta2, zero, unordered            # FT is a NAN
2:
        sll     t1, t1, 23                      # reassemble exp & frac
        or      t1, t1, t2
        sll     ta1, ta1, 23                    # reassemble exp & frac
        or      ta1, ta1, ta2
        beq     t0, zero, 1f                    # is FS positive?
        negu    t1
1:
        beq     ta0, zero, 1f                   # is FT positive?
        negu    ta1
1:
        li      v0, COND_LESS
        blt     t1, ta1, test_cond              # is FS < FT?
        li      v0, COND_EQUAL
        beq     t1, ta1, test_cond              # is FS == FT?
        move    v0, zero                        # FS > FT
        b       test_cond

/*
 * Compare double.
 */
cmp_d:
        jal     _C_LABEL(get_cmp_d)
        bne     t1, DEXP_INF, 1f                # is FS an infinity?
        bne     t2, zero, unordered
        bne     t3, zero, unordered             # FS is a NAN
1:
        bne     ta1, DEXP_INF, 2f               # is FT an infinity?
        bne     ta2, zero, unordered
        bne     ta3, zero, unordered            # FT is a NAN
2:
        sll     t1, t1, 20                      # reassemble exp & frac
        or      t1, t1, t2
        sll     ta1, ta1, 20                    # reassemble exp & frac
        or      ta1, ta1, ta2
        beq     t0, zero, 1f                    # is FS positive?
        not     t3                              # negate t1,t3
        not     t1
        addu    t3, t3, 1
        seq     v0, t3, zero                    # compute carry
        addu    t1, t1, v0
1:
        beq     ta0, zero, 1f                   # is FT positive?
        not     ta3                             # negate ta1,ta3
        not     ta1
        addu    ta3, ta3, 1
        seq     v0, ta3, zero                   # compute carry
        addu    ta1, ta1, v0
1:
        li      v0, COND_LESS
        blt     t1, ta1, test_cond              # is FS(MSW) < FT(MSW)?
        move    v0, zero
        bne     t1, ta1, test_cond              # is FS(MSW) > FT(MSW)?
        li      v0, COND_LESS
        bltu    t3, ta3, test_cond              # is FS(LSW) < FT(LSW)?
        li      v0, COND_EQUAL
        beq     t3, ta3, test_cond              # is FS(LSW) == FT(LSW)?
        move    v0, zero                        # FS > FT
test_cond:
        and     v0, v0, a0                      # condition match instruction?
set_cond:
        bne     v0, zero, 1f
        and     a2, a2, ~MIPS_FCSR_FCC0         # clear condition bit
        b       2f
1:
        or      a2, a2, MIPS_FCSR_FCC0          # set condition bit
2:
#ifdef FPEMUL
        PTR_L   v0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        #nop
        INT_S   a2, PCB_FPREGS+FRAME_FSR(v0)
#else
        ctc1    a2, MIPS_FCSR                   # save condition bit
#endif
        b       done

unordered:
        and     v0, a0, COND_UNORDERED          # this cmp match unordered?
        bne     v0, zero, 1f
        and     a2, a2, ~MIPS_FCSR_FCC0         # clear condition bit
        b       2f
1:
        or      a2, a2, MIPS_FCSR_FCC0          # set condition bit
2:
        and     v0, a0, COND_SIGNAL
        beq     v0, zero, 1f                    # is this a signaling cmp?
        or      a2, a2, MIPS_FCSR_CAUSE_V | MIPS_FCSR_FLAGS_V
        and     v0, a2, MIPS_FCSR_ENABLES_V
        bne     v0, zero, fpe_trap
1:
#ifdef FPEMUL
        PTR_L   v0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        #nop
        INT_S   a2, PCB_FPREGS+FRAME_FSR(v0)
#else
        ctc1    a2, MIPS_FCSR                   # save condition bit
#endif
        b       done

/*
 * Determine the amount to shift the fraction in order to restore the
 * normalized position. After that, round and handle exceptions.
 */
norm_s:
#if __mips == 32 || __mips == 64
#ifdef __mips_o32
        bne     t2, zero, 1f
        clz     v1, t9
        addu    v1, 32
        b       2f
1:
        clz     v1, t2
2:
#elif __mips_isa_rev == 2
        move    v0, t9
        dins    v0, t2, 32, 32
        dclz    v1, v0
#else
        dsll    v0, t9, 32
        dsrl    v0, v0, 32
        dsll    v1, t2, 32
        or      v0, v1
        dclz    v1, v0
#endif
#else
        .set    noat
        move    v0, t2                          # MSW
        move    v1, zero                        # v1 = num of leading zeros
        bne     t2, zero, 1f
        move    v0, t9                          # LSW
        addu    v1, 32
1:
        srl     AT, v0, 16
        bne     AT, zero, 1f
        addu    v1, 16
        sll     v0, 16
1:
        srl     AT, v0, 24
        bne     AT, zero, 1f
        addu    v1, 8
        sll     v0, 8
1:
        srl     AT, v0, 28
        bne     AT, zero, 1f
        addu    v1, 4
        sll     v0, 4
1:
        srl     AT, v0, 30
        bne     AT, zero, 1f
        addu    v1, 2
        sll     v0, 2
1:
        srl     AT, v0, 31
        bne     AT, zero, 1f
        addu    v1, 1
2:
        .set    at
#endif  /* __mips == 32 || __mips == 64 */
/*
 * Now shift t2,t9 the correct number of bits.
 */
        subu    v1, v1, SLEAD_ZEROS             # dont count leading zeros
        subu    t1, t1, v1                      # adjust the exponent
        beq     v1, zero, norm_noshift_s
        li      ta1, 32
        blt     v1, zero, 1f                    # if shift < 0, shift right
        subu    ta1, ta1, v1
        sll     t2, t2, v1                      # shift t2,t9 left
        srl     v0, t9, ta1                     # save bits shifted out
        or      t2, t2, v0
        sll     t9, t9, v1
        b       norm_noshift_s
1:
        negu    v1                              # shift t2,t9 right by at
        subu    ta1, ta1, v1
        sll     v0, t9, ta1                     # save bits shifted out
        sltu    v0, zero, v0                    # be sure to save any one bits
        srl     t9, t9, v1
        or      t9, t9, v0
        sll     v0, t2, ta1                     # save bits shifted out
        or      t9, t9, v0
        srl     t2, t2, v1
norm_noshift_s:
        move    ta1, t1                         # save unrounded exponent
        move    ta2, t2                         # save unrounded fraction
        and     v0, a2, MIPS_FCSR_RM            # get rounding mode
        beq     v0, MIPS_FCSR_RM_RN, 3f         # round to nearest
        beq     v0, MIPS_FCSR_RM_RZ, 5f         # round to zero (truncate)
        beq     v0, MIPS_FCSR_RM_RP, 1f         # round to +infinity
        beq     t0, zero, 5f                    # if sign is positive, truncate
        b       2f
1:
        bne     t0, zero, 5f                    # if sign is negative, truncate
2:
        beq     t9, zero, 5f                    # if exact, continue
        addu    t2, t2, 1                       # add rounding bit
        bne     t2, SIMPL_ONE<<1, 5f            # need to adjust exponent?
        addu    t1, t1, 1                       # adjust exponent
        srl     t2, t2, 1                       # renormalize fraction
        b       5f
3:
        li      v0, GUARDBIT                    # load guard bit for rounding
        addu    v0, v0, t9                      # add remainder
        sltu    v1, v0, t9                      # compute carry out
        beq     v1, zero, 4f                    # if no carry, continue
        addu    t2, t2, 1                       # add carry to result
        bne     t2, SIMPL_ONE<<1, 4f            # need to adjust exponent?
        addu    t1, t1, 1                       # adjust exponent
        srl     t2, t2, 1                       # renormalize fraction
4:
        bne     v0, zero, 5f                    # if rounded remainder is zero
        and     t2, t2, ~1                      #  clear LSB (round to nearest)
5:
        bgt     t1, SEXP_MAX, overflow_s        # overflow?
        blt     t1, SEXP_MIN, underflow_s       # underflow?
        bne     t9, zero, inexact_s             # is result inexact?
        addu    t1, t1, SEXP_BIAS               # bias exponent
        and     t2, t2, ~SIMPL_ONE              # clear implied one bit
        b       result_fs_s

/*
 * Handle inexact exception.
 */
inexact_s:
        addu    t1, t1, SEXP_BIAS               # bias exponent
        and     t2, t2, ~SIMPL_ONE              # clear implied one bit
inexact_nobias_s:
        jal     _C_LABEL(set_fd_s)              # save result
        or      a2, a2, MIPS_FCSR_CAUSE_I | MIPS_FCSR_FLAGS_I
        and     v0, a2, MIPS_FCSR_ENABLES_I
        bne     v0, zero, fpe_trap
#ifdef FPEMUL
        PTR_L   v0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        #nop
        INT_S   a2, PCB_FPREGS+FRAME_FSR(v0)
#else
        ctc1    a2, MIPS_FCSR                   # save exceptions
#endif
        b       done

/*
 * Overflow will trap (if enabled),
 * or generate an inexact trap (if enabled),
 * or generate an infinity.
 */
overflow_s:
        or      a2, a2, MIPS_FCSR_CAUSE_O | MIPS_FCSR_FLAGS_O
        and     v0, a2, MIPS_FCSR_ENABLES_O
        beq     v0, zero, 1f
        subu    t1, t1, 192                     # bias exponent
        and     t2, t2, ~SIMPL_ONE              # clear implied one bit
        jal     _C_LABEL(set_fd_s)              # save result
        b       fpe_trap
1:
        and     v0, a2, MIPS_FCSR_RM            # get rounding mode
        beq     v0, MIPS_FCSR_RM_RN, 3f         # round to nearest
        beq     v0, MIPS_FCSR_RM_RZ, 1f         # round to zero (truncate)
        beq     v0, MIPS_FCSR_RM_RP, 2f         # round to +infinity
        bne     t0, zero, 3f
1:
        li      t1, SEXP_MAX                    # result is max finite
        li      t2, 0x007fffff
        b       inexact_s
2:
        bne     t0, zero, 1b
3:
        li      t1, SEXP_MAX + 1                # result is infinity
        move    t2, zero
        b       inexact_s

/*
 * In this implementation, "tininess" is detected "after rounding" and
 * "loss of accuracy" is detected as "an inexact result".
 */
underflow_s:
        and     v0, a2, MIPS_FCSR_ENABLES_U
        beq     v0, zero, 1f
/*
 * Underflow is enabled so compute the result and trap.
 */
        addu    t1, t1, 192                     # bias exponent
        and     t2, t2, ~SIMPL_ONE              # clear implied one bit
        jal     _C_LABEL(set_fd_s)              # save result
        or      a2, a2, MIPS_FCSR_CAUSE_U | MIPS_FCSR_FLAGS_U
        b       fpe_trap
/*
 * Underflow is not enabled so compute the result,
 * signal inexact result (if it is) and trap (if enabled).
 */
1:
        move    t1, ta1                         # get unrounded exponent
        move    t2, ta2                         # get unrounded fraction
        li      v0, SEXP_MIN                    # compute shift amount
        subu    v0, v0, t1                      # shift t2,t9 right by at
        blt     v0, SFRAC_BITS+2, 3f            # shift all the bits out?
        move    t1, zero                        # result is inexact zero
        move    t2, zero
        or      a2, a2, MIPS_FCSR_CAUSE_U | MIPS_FCSR_FLAGS_U
/*
 * Now round the zero result.
 * Only need to worry about rounding to +- infinity when the sign matches.
 */
        and     v0, a2, MIPS_FCSR_RM            # get rounding mode
        beq     v0, MIPS_FCSR_RM_RN, inexact_nobias_s   # round to nearest
        beq     v0, MIPS_FCSR_RM_RZ, inexact_nobias_s   # round to zero
        beq     v0, MIPS_FCSR_RM_RP, 1f         # round to +infinity
        beq     t0, zero, inexact_nobias_s      # if sign is positive, truncate
        b       2f
1:
        bne     t0, zero, inexact_nobias_s      # if sign is negative, truncate
2:
        addu    t2, t2, 1                       # add rounding bit
        b       inexact_nobias_s
3:
        .set    noat
        li      v1, 32
        subu    v1, v1, v0
        sltu    AT, zero, t9                    # be sure to save any one bits
        sll     t9, t2, v1                      # save bits shifted out
        or      t9, t9, AT                      # include sticky bits
        srl     t2, t2, v0
        .set    at
/*
 * Now round the denormalized result.
 */
        and     v0, a2, MIPS_FCSR_RM            # get rounding mode
        beq     v0, MIPS_FCSR_RM_RN, 3f         # round to nearest
        beq     v0, MIPS_FCSR_RM_RZ, 5f         # round to zero (truncate)
        beq     v0, MIPS_FCSR_RM_RP, 1f         # round to +infinity
        beq     t0, zero, 5f                    # if sign is positive, truncate
        b       2f
1:
        bne     t0, zero, 5f                    # if sign is negative, truncate
2:
        beq     t9, zero, 5f                    # if exact, continue
        addu    t2, t2, 1                       # add rounding bit
        b       5f
3:
        li      v0, GUARDBIT                    # load guard bit for rounding
        addu    v0, v0, t9                      # add remainder
        sltu    v1, v0, t9                      # compute carry out
        beq     v1, zero, 4f                    # if no carry, continue
        addu    t2, t2, 1                       # add carry to result
4:
        bne     v0, zero, 5f                    # if rounded remainder is zero
        and     t2, t2, ~1                      #  clear LSB (round to nearest)
5:
        move    t1, zero                        # denorm or zero exponent
        jal     _C_LABEL(set_fd_s)              # save result
        beq     t9, zero, done                  # check for exact result
        or      a2, a2, MIPS_FCSR_CAUSE_U | MIPS_FCSR_FLAGS_U
        or      a2, a2, MIPS_FCSR_CAUSE_I | MIPS_FCSR_FLAGS_I
        and     v0, a2, MIPS_FCSR_ENABLES_I
        bne     v0, zero, fpe_trap
#ifdef FPEMUL
        PTR_L   v0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        #nop
        INT_S   a2, PCB_FPREGS+FRAME_FSR(v0)
#else
        ctc1    a2, MIPS_FCSR                   # save exceptions
#endif
        b       done

/*
 * Determine the amount to shift the fraction in order to restore the
 * normalized position. After that, round and handle exceptions.
 */
norm_d:
#if __mips == 32 || __mips == 64
        bne     t2, zero, 2f
        bne     t3, zero, 1f
        clz     v1, t9
        addu    v1, 64
        b       3f
1:
        clz     v1, t3
        addu    v1, 32
        b       3f
2:
        clz     v1, t2
3:
#else
        .set    noat
        move    v0, t2
        move    v1, zero                        # v1 = num of leading zeros
        bne     t2, zero, 1f
        move    v0, t3
        addu    v1, 32
        bne     t3, zero, 1f
        move    v0, t9
        addu    v1, 32
1:
        srl     AT, v0, 16
        bne     AT, zero, 1f
        addu    v1, 16
        sll     v0, 16
1:
        srl     AT, v0, 24
        bne     AT, zero, 1f
        addu    v1, 8
        sll     v0, 8
1:
        srl     AT, v0, 28
        bne     AT, zero, 1f
        addu    v1, 4
        sll     v0, 4
1:
        srl     AT, v0, 30
        bne     AT, zero, 1f
        addu    v1, 2
        sll     v0, 2
1:
        srl     AT, v0, 31
        bne     AT, zero, 1f
        addu    v1, 1
1:
        .set    at
#endif /* __mips_isa == 32 || __mips_isa == 64 */
/*
 * Now shift t2,t3,t9 the correct number of bits.
 */
        subu    v1, v1, DLEAD_ZEROS             # dont count leading zeros
        subu    t1, t1, v1                      # adjust the exponent
        beq     v1, zero, norm_noshift_d

        li      ta1, 32
        blt     v1, zero, 2f                    # if shift < 0, shift right
        blt     v1, ta1, 1f                     # shift by < 32?
        subu    v1, v1, ta1                     # shift by >= 32
        subu    ta1, ta1, v1
        sll     t2, t3, v1                      # shift left by v1
        srl     v0, t9, ta1                     # save bits shifted out
        or      t2, t2, v0
        sll     t3, t9, v1
        move    t9, zero
        b       norm_noshift_d
1:
        subu    ta1, ta1, v1
        sll     t2, t2, v1                      # shift left by v1
        srl     v0, t3, ta1                     # save bits shifted out
        or      t2, t2, v0
        sll     t3, t3, v1
        srl     v0, t9, ta1                     # save bits shifted out
        or      t3, t3, v0
        sll     t9, t9, v1
        b       norm_noshift_d
2:
        negu    v1                              # shift right by at
        subu    ta1, ta1, v1                    #  (known to be < 32 bits)
        sll     v0, t9, ta1                     # save bits shifted out
        sltu    v0, zero, v0                    # be sure to save any one bits
        srl     t9, t9, v1
        or      t9, t9, v0
        sll     v0, t3, ta1                     # save bits shifted out
        or      t9, t9, v0
        srl     t3, t3, v1
        sll     v0, t2, ta1                     # save bits shifted out
        or      t3, t3, v0
        srl     t2, t2, v1
norm_noshift_d:
        move    ta1, t1                         # save unrounded exponent
        move    ta2, t2                         # save unrounded fraction (MS)
        move    ta3, t3                         # save unrounded fraction (LS)
        and     v0, a2, MIPS_FCSR_RM            # get rounding mode
        beq     v0, MIPS_FCSR_RM_RN, 3f         # round to nearest
        beq     v0, MIPS_FCSR_RM_RZ, 5f         # round to zero (truncate)
        beq     v0, MIPS_FCSR_RM_RP, 1f         # round to +infinity
        beq     t0, zero, 5f                    # if sign is positive, truncate
        b       2f
1:
        bne     t0, zero, 5f                    # if sign is negative, truncate
2:
        beq     t9, zero, 5f                    # if exact, continue
        addu    t3, t3, 1                       # add rounding bit
        bne     t3, zero, 5f                    # branch if no carry
        addu    t2, t2, 1                       # add carry
        bne     t2, DIMPL_ONE<<1, 5f            # need to adjust exponent?
        addu    t1, t1, 1                       # adjust exponent
        srl     t2, t2, 1                       # renormalize fraction
        b       5f
3:
        li      v0, GUARDBIT                    # load guard bit for rounding
        addu    v0, v0, t9                      # add remainder
        sltu    v1, v0, t9                      # compute carry out
        beq     v1, zero, 4f                    # branch if no carry
        addu    t3, t3, 1                       # add carry
        bne     t3, zero, 4f                    # branch if no carry
        addu    t2, t2, 1                       # add carry to result
        bne     t2, DIMPL_ONE<<1, 4f            # need to adjust exponent?
        addu    t1, t1, 1                       # adjust exponent
        srl     t2, t2, 1                       # renormalize fraction
4:
        bne     v0, zero, 5f                    # if rounded remainder is zero
        and     t3, t3, ~1                      #  clear LSB (round to nearest)
5:
        bgt     t1, DEXP_MAX, overflow_d        # overflow?
        blt     t1, DEXP_MIN, underflow_d       # underflow?
        bne     t9, zero, inexact_d             # is result inexact?
        addu    t1, t1, DEXP_BIAS               # bias exponent
        and     t2, t2, ~DIMPL_ONE              # clear implied one bit
        b       result_fs_d

/*
 * Handle inexact exception.
 */
inexact_d:
        addu    t1, t1, DEXP_BIAS               # bias exponent
        and     t2, t2, ~DIMPL_ONE              # clear implied one bit
inexact_nobias_d:
        jal     _C_LABEL(set_fd_d)              # save result
        or      a2, a2, MIPS_FCSR_CAUSE_I | MIPS_FCSR_FLAGS_I
        and     v0, a2, MIPS_FCSR_ENABLES_I
        bne     v0, zero, fpe_trap
#ifdef FPEMUL
        PTR_L   v0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        #nop
        INT_S   a2, PCB_FPREGS+FRAME_FSR(v0)
#else
        ctc1    a2, MIPS_FCSR                   # save exceptions
#endif
        b       done

/*
 * Overflow will trap (if enabled),
 * or generate an inexact trap (if enabled),
 * or generate an infinity.
 */
overflow_d:
        or      a2, a2, MIPS_FCSR_CAUSE_O | MIPS_FCSR_FLAGS_O
        and     v0, a2, MIPS_FCSR_ENABLES_O
        beq     v0, zero, 1f
        subu    t1, t1, 1536                    # bias exponent
        and     t2, t2, ~DIMPL_ONE              # clear implied one bit
        jal     _C_LABEL(set_fd_d)              # save result
        b       fpe_trap
1:
        and     v0, a2, MIPS_FCSR_RM            # get rounding mode
        beq     v0, MIPS_FCSR_RM_RN, 3f         # round to nearest
        beq     v0, MIPS_FCSR_RM_RZ, 1f         # round to zero (truncate)
        beq     v0, MIPS_FCSR_RM_RP, 2f         # round to +infinity
        bne     t0, zero, 3f
1:
        li      t1, DEXP_MAX                    # result is max finite
        li      t2, 0x000fffff
        li      t3, 0xffffffff
        b       inexact_d
2:
        bne     t0, zero, 1b
3:
        li      t1, DEXP_MAX + 1                # result is infinity
        move    t2, zero
        move    t3, zero
        b       inexact_d

/*
 * In this implementation, "tininess" is detected "after rounding" and
 * "loss of accuracy" is detected as "an inexact result".
 */
underflow_d:
        and     v0, a2, MIPS_FCSR_ENABLES_U
        beq     v0, zero, 1f
/*
 * Underflow is enabled so compute the result and trap.
 */
        addu    t1, t1, 1536                    # bias exponent
        and     t2, t2, ~DIMPL_ONE              # clear implied one bit
        jal     _C_LABEL(set_fd_d)              # save result
        or      a2, a2, MIPS_FCSR_CAUSE_U | MIPS_FCSR_FLAGS_U
        b       fpe_trap
/*
 * Underflow is not enabled so compute the result,
 * signal inexact result (if it is) and trap (if enabled).
 */
1:
        move    t1, ta1                         # get unrounded exponent
        move    t2, ta2                         # get unrounded fraction (MS)
        move    t3, ta3                         # get unrounded fraction (LS)
        li      v0, DEXP_MIN                    # compute shift amount
        subu    v0, v0, t1                      # shift t2,t9 right by at
        blt     v0, DFRAC_BITS+2, 3f            # shift all the bits out?
        move    t1, zero                        # result is inexact zero
        move    t2, zero
        move    t3, zero
        or      a2, a2, MIPS_FCSR_CAUSE_U | MIPS_FCSR_FLAGS_U
/*
 * Now round the zero result.
 * Only need to worry about rounding to +- infinity when the sign matches.
 */
        and     v0, a2, MIPS_FCSR_RM            # get rounding mode
        beq     v0, MIPS_FCSR_RM_RN, inexact_nobias_d   # round to nearest
        beq     v0, MIPS_FCSR_RM_RZ, inexact_nobias_d   # round to zero
        beq     v0, MIPS_FCSR_RM_RP, 1f         # round to +infinity
        beq     t0, zero, inexact_nobias_d      # if sign is positive, truncate
        b       2f
1:
        bne     t0, zero, inexact_nobias_d      # if sign is negative, truncate
2:
        addu    t3, t3, 1                       # add rounding bit
        b       inexact_nobias_d
3:
        li      v1, 32
        blt     v0, v1, 1f                      # shift by < 32?
        subu    v0, v0, v1                      # shift right by >= 32
        subu    v1, v1, v0
        .set    noat
        sltu    AT, zero, t9                    # be sure to save any one bits
        sll     t9, t2, v1                      # save bits shifted out
        or      t9, t9, AT                      # include sticky bits
        srl     t3, t2, v0
        move    t2, zero
        .set    at
        b       2f
1:
        .set    noat
        subu    v1, v1, v0                      # shift right by at
        sltu    AT, zero, t9                    # be sure to save any one bits
        sll     t9, t3, v1                      # save bits shifted out
        or      t9, t9, AT                      # include sticky bits
        srl     t3, t3, v0
        sll     AT, t2, v1                      # save bits shifted out
        or      t3, t3, AT
        srl     t2, t2, v0
        .set    at
/*
 * Now round the denormalized result.
 */
2:
        and     v0, a2, MIPS_FCSR_RM            # get rounding mode
        beq     v0, MIPS_FCSR_RM_RN, 3f         # round to nearest
        beq     v0, MIPS_FCSR_RM_RZ, 5f         # round to zero (truncate)
        beq     v0, MIPS_FCSR_RM_RP, 1f         # round to +infinity
        beq     t0, zero, 5f                    # if sign is positive, truncate
        b       2f
1:
        bne     t0, zero, 5f                    # if sign is negative, truncate
2:
        beq     t9, zero, 5f                    # if exact, continue
        addu    t3, t3, 1                       # add rounding bit
        bne     t3, zero, 5f                    # if no carry, continue
        addu    t2, t2, 1                       # add carry
        b       5f
3:
        li      v0, GUARDBIT                    # load guard bit for rounding
        addu    v0, v0, t9                      # add remainder
        sltu    v1, v0, t9                      # compute carry out
        beq     v1, zero, 4f                    # if no carry, continue
        addu    t3, t3, 1                       # add rounding bit
        bne     t3, zero, 4f                    # if no carry, continue
        addu    t2, t2, 1                       # add carry
4:
        bne     v0, zero, 5f                    # if rounded remainder is zero
        and     t3, t3, ~1                      #  clear LSB (round to nearest)
5:
        move    t1, zero                        # denorm or zero exponent
        jal     _C_LABEL(set_fd_d)              # save result
        beq     t9, zero, done                  # check for exact result
        or      a2, a2, MIPS_FCSR_CAUSE_U | MIPS_FCSR_FLAGS_U
        or      a2, a2, MIPS_FCSR_CAUSE_I | MIPS_FCSR_FLAGS_I
        and     v0, a2, MIPS_FCSR_ENABLES_I
        bne     v0, zero, fpe_trap
#ifdef FPEMUL
        PTR_L   v0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        #nop
        INT_S   a2, PCB_FPREGS+FRAME_FSR(v0)
#else
        ctc1    a2, MIPS_FCSR                   # save exceptions
#endif
        b       done

/*
 * Signal an invalid operation if the trap is enabled; otherwise,
 * the result is a quiet NAN.
 */
invalid_s:                                      # trap invalid operation
        or      a2, a2, MIPS_FCSR_CAUSE_V | MIPS_FCSR_FLAGS_V
        and     v0, a2, MIPS_FCSR_ENABLES_V
        bne     v0, zero, fpe_trap
#ifdef FPEMUL
        PTR_L   v0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        #nop
        INT_S   a2, PCB_FPREGS+FRAME_FSR(v0)
#else
        ctc1    a2, MIPS_FCSR                   # save exceptions
#endif
        move    t0, zero                        # result is a quiet NAN
        li      t1, SEXP_INF
        li      t2, SQUIET_NAN
        jal     _C_LABEL(set_fd_s)              # save result (in t0,t1,t2)
        b       done

/*
 * Signal an invalid operation if the trap is enabled; otherwise,
 * the result is a quiet NAN.
 */
invalid_d:                                      # trap invalid operation
        or      a2, a2, MIPS_FCSR_CAUSE_V | MIPS_FCSR_FLAGS_V
        and     v0, a2, MIPS_FCSR_ENABLES_V
        bne     v0, zero, fpe_trap
#ifdef FPEMUL
        PTR_L   v0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        #nop
        INT_S   a2, PCB_FPREGS+FRAME_FSR(v0)
#else
        ctc1    a2, MIPS_FCSR                   # save exceptions
#endif
        move    t0, zero                        # result is a quiet NAN
        li      t1, DEXP_INF
        li      t2, DQUIET_NAN0
        li      t3, DQUIET_NAN1
        jal     _C_LABEL(set_fd_d)              # save result (in t0,t1,t2,t3)
        b       done

/*
 * Signal an invalid operation if the trap is enabled; otherwise,
 * the result is INT_MAX or INT_MIN.
 */
invalid_w:                                      # trap invalid operation
        or      a2, a2, MIPS_FCSR_CAUSE_V | MIPS_FCSR_FLAGS_V
        and     v0, a2, MIPS_FCSR_ENABLES_V
        bne     v0, zero, fpe_trap
#ifdef FPEMUL
        PTR_L   v0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        #nop
        INT_S   a2, PCB_FPREGS+FRAME_FSR(v0)
#else
        ctc1    a2, MIPS_FCSR                   # save exceptions
#endif
        bne     t0, zero, 1f
        li      t2, INT_MAX                     # result is INT_MAX
        b       result_fs_w
1:
        li      t2, INT_MIN                     # result is INT_MIN
        b       result_fs_w

/*
 * Trap if the hardware should have handled this case.
 */
fpe_trap:
#ifdef FPEMUL
        PTR_L   v0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        #nop
        INT_S   a2, PCB_FPREGS+FRAME_FSR(v0)
#else
        /*
         * ctc1 with fpe bits set causes FPE in kernel mode panic on 5231.
         */
        REG_S   a2, CALLFRAME_SIZ + 3*SZREG(sp)
        move    a0, MIPS_CURLWP                 # get current lwp
        jal     _C_LABEL(fpu_save)              # on RM5231

        REG_L   a2, CALLFRAME_SIZ + 3*SZREG(sp)

        PTR_L   v0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        #nop
        INT_S   a2, PCB_FPREGS+FRAME_FSR(v0)
#endif
        move    a3, a2                          # fpustat
        REG_L   a1, CALLFRAME_FRAME(sp)         # frame
        REG_L   a2, CALLFRAME_CAUSE(sp)         # cause
        REG_L   ra, CALLFRAME_RA(sp)
        PTR_ADDU sp, CALLFRAME_SIZ
        j       _C_LABEL(fpemul_sigfpe)

/*
 * Send an illegal instruction signal to the current lwp.
 */
ill:
#ifdef FPEMUL
        PTR_L   v0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        #nop
        INT_S   a2, PCB_FPREGS+FRAME_FSR(v0)
#else
        ctc1    a2, MIPS_FCSR                   # save exceptions
#endif
        REG_L   a1, CALLFRAME_FRAME(sp)         # frame
        REG_L   a2, CALLFRAME_CAUSE(sp)         # cause
        REG_L   ra, CALLFRAME_RA(sp)
        PTR_ADDU sp, CALLFRAME_SIZ
        j       _C_LABEL(fpemul_sigill)

result_ft_s:
        move    t0, ta0                         # result is FT
        move    t1, ta1
        move    t2, ta2
result_fs_s:                                    # result is FS
        jal     _C_LABEL(set_fd_s)              # save result (in t0,t1,t2)
        b       done

result_fs_w:
        jal     _C_LABEL(set_fd_word)           # save result (in t2)
        b       done

result_ft_d:
        move    t0, ta0                         # result is FT
        move    t1, ta1
        move    t2, ta2
        move    t3, ta3
result_fs_d:                                    # result is FS
        jal     _C_LABEL(set_fd_d)              # save result (in t0,t1,t2,t3)

done:
/*
 * Succeeded to emulate instruction with no error
 * so compute the next PC.
 */
        REG_L   t0, CALLFRAME_CAUSE(sp)
        REG_PROLOGUE
        REG_L   v0, TF_REG_EPC(a1)
        REG_EPILOGUE
        bgez    t0, 1f                          # Check the branch delay bit.
/*
 * The instruction is in the branch delay slot so the branch will have to
 * be emulated to get the resulting PC.
 */
        REG_S   a1, CALLFRAME_FRAME(sp)
        move    a0, a1                          # 1st arg is p. to trapframe
        move    a1, v0                          # 2nd arg is instruction PC
                                                # 3rd arg is FP CSR
        move    a3, zero                        # 4th arg is FALSE
        jal     _C_LABEL(mips_emul_branch)      # compute PC after branch

        REG_L   a1, CALLFRAME_FRAME(sp)
        b       2f
/*
 * This is not in the branch delay slot so calculate the resulting
 * PC (epc + 4) into v0.
 */
1:
        addiu   v0, v0, 4                       # v0 = next pc
2:
        REG_PROLOGUE
        REG_S   v0, TF_REG_EPC(a1)              # save new pc
        REG_EPILOGUE

        REG_L   ra, CALLFRAME_RA(sp)
        PTR_ADDU sp, CALLFRAME_SIZ
        j       ra
END(mips_emul_fp)

/*----------------------------------------------------------------------------
 * get_fs_int --
 *
 *      Read (integer) the FS register (bits 15-11).
 *      This is an internal routine used by mips_emul_fp only.
 *
 * Results:
 *      t0      contains the sign
 *      t2      contains the fraction
 *
 *----------------------------------------------------------------------------
 */
STATIC_LEAF(get_fs_int)
#ifdef FPEMUL
        srl     t2, a0, 11 - FPX_SCALESHIFT
        PTR_L   t0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        andi    t2, t2, FPX_REGEVENMASK         # Even regs only
        PTR_ADDU t0, t0, t2

        lw      t2, PCB_FPREGS+FRAME_FP0(t0)

        srl     t0, t2, 31              # init the sign bit
        bge     t2, zero, 1f
        negu    t2
1:
        j       ra
#else
        srl     a3, a0, 11 - (PTR_SCALESHIFT-1) # get FS field (even regs only)
        and     a3, a3, 0xf << PTR_SCALESHIFT   # mask FS field
        PTR_L   a3, get_fs_int_tbl(a3)          # switch on register number
        j       a3

        .rdata
get_fs_int_tbl:
        PTR_WORD get_fs_int_f0
        PTR_WORD get_fs_int_f2
        PTR_WORD get_fs_int_f4
        PTR_WORD get_fs_int_f6
        PTR_WORD get_fs_int_f8
        PTR_WORD get_fs_int_f10
        PTR_WORD get_fs_int_f12
        PTR_WORD get_fs_int_f14
        PTR_WORD get_fs_int_f16
        PTR_WORD get_fs_int_f18
        PTR_WORD get_fs_int_f20
        PTR_WORD get_fs_int_f22
        PTR_WORD get_fs_int_f24
        PTR_WORD get_fs_int_f26
        PTR_WORD get_fs_int_f28
        PTR_WORD get_fs_int_f30
        .text

get_fs_int_f0:
        mfc1    t2, $f0
        b       get_fs_int_done
get_fs_int_f2:
        mfc1    t2, $f2
        b       get_fs_int_done
get_fs_int_f4:
        mfc1    t2, $f4
        b       get_fs_int_done
get_fs_int_f6:
        mfc1    t2, $f6
        b       get_fs_int_done
get_fs_int_f8:
        mfc1    t2, $f8
        b       get_fs_int_done
get_fs_int_f10:
        mfc1    t2, $f10
        b       get_fs_int_done
get_fs_int_f12:
        mfc1    t2, $f12
        b       get_fs_int_done
get_fs_int_f14:
        mfc1    t2, $f14
        b       get_fs_int_done
get_fs_int_f16:
        mfc1    t2, $f16
        b       get_fs_int_done
get_fs_int_f18:
        mfc1    t2, $f18
        b       get_fs_int_done
get_fs_int_f20:
        mfc1    t2, $f20
        b       get_fs_int_done
get_fs_int_f22:
        mfc1    t2, $f22
        b       get_fs_int_done
get_fs_int_f24:
        mfc1    t2, $f24
        b       get_fs_int_done
get_fs_int_f26:
        mfc1    t2, $f26
        b       get_fs_int_done
get_fs_int_f28:
        mfc1    t2, $f28
        b       get_fs_int_done
get_fs_int_f30:
        mfc1    t2, $f30
get_fs_int_done:
        srl     t0, t2, 31              # init the sign bit
        bge     t2, zero, 1f
        negu    t2
1:
        j       ra
#endif
END(get_fs_int)

/*----------------------------------------------------------------------------
 * get_ft_fs_s --
 *
 *      Read (single precision) the FT register (bits 20-16) and
 *      the FS register (bits 15-11) and break up into fields.
 *      This is an internal routine used by mips_emul_fp only.
 *
 * Results:
 *      t0      contains the FS sign
 *      t1      contains the FS (biased) exponent
 *      t2      contains the FS fraction
 *      ta0     contains the FT sign
 *      ta1     contains the FT (biased) exponent
 *      ta2     contains the FT fraction
 *
 *----------------------------------------------------------------------------
 */
STATIC_LEAF(get_ft_fs_s)
#ifdef FPEMUL
        srl     ta0, a0, 16 - FPX_SCALESHIFT
        PTR_L   ta1, L_PCB(MIPS_CURLWP)         # get pcb of current lwp
        andi    ta0, ta0, FPX_REGEVENMASK       # Even regs only
        PTR_ADDU ta1, ta1, ta0

        lw      ta0, PCB_FPREGS+FRAME_FP0(ta1)

        srl     ta1, ta0, 23                    # get exponent
        and     ta1, ta1, 0xFF
        and     ta2, ta0, 0x7FFFFF              # get fraction
        srl     ta0, ta0, 31                    # get sign
        bne     ta1, SEXP_INF, 1f               # is it a signaling NAN?
        and     v0, ta2, SSIGNAL_NAN
        bne     v0, zero, invalid_s
1:
        /* fall through to get FS */
#else
        srl     a3, a0, 16 - (PTR_SCALESHIFT - 1)# get FT field (even regs only)
        and     a3, a3, 0xF << PTR_SCALESHIFT   # mask FT field
        PTR_L   a3, get_ft_s_tbl(a3)            # switch on register number
        j       a3

        .rdata
get_ft_s_tbl:
        PTR_WORD get_ft_s_f0
        PTR_WORD get_ft_s_f2
        PTR_WORD get_ft_s_f4
        PTR_WORD get_ft_s_f6
        PTR_WORD get_ft_s_f8
        PTR_WORD get_ft_s_f10
        PTR_WORD get_ft_s_f12
        PTR_WORD get_ft_s_f14
        PTR_WORD get_ft_s_f16
        PTR_WORD get_ft_s_f18
        PTR_WORD get_ft_s_f20
        PTR_WORD get_ft_s_f22
        PTR_WORD get_ft_s_f24
        PTR_WORD get_ft_s_f26
        PTR_WORD get_ft_s_f28
        PTR_WORD get_ft_s_f30
        .text

get_ft_s_f0:
        mfc1    ta0, $f0
        b       get_ft_s_done
get_ft_s_f2:
        mfc1    ta0, $f2
        b       get_ft_s_done
get_ft_s_f4:
        mfc1    ta0, $f4
        b       get_ft_s_done
get_ft_s_f6:
        mfc1    ta0, $f6
        b       get_ft_s_done
get_ft_s_f8:
        mfc1    ta0, $f8
        b       get_ft_s_done
get_ft_s_f10:
        mfc1    ta0, $f10
        b       get_ft_s_done
get_ft_s_f12:
        mfc1    ta0, $f12
        b       get_ft_s_done
get_ft_s_f14:
        mfc1    ta0, $f14
        b       get_ft_s_done
get_ft_s_f16:
        mfc1    ta0, $f16
        b       get_ft_s_done
get_ft_s_f18:
        mfc1    ta0, $f18
        b       get_ft_s_done
get_ft_s_f20:
        mfc1    ta0, $f20
        b       get_ft_s_done
get_ft_s_f22:
        mfc1    ta0, $f22
        b       get_ft_s_done
get_ft_s_f24:
        mfc1    ta0, $f24
        b       get_ft_s_done
get_ft_s_f26:
        mfc1    ta0, $f26
        b       get_ft_s_done
get_ft_s_f28:
        mfc1    ta0, $f28
        b       get_ft_s_done
get_ft_s_f30:
        mfc1    ta0, $f30
get_ft_s_done:
        srl     ta1, ta0, 23                    # get exponent
        and     ta1, ta1, 0xFF
        and     ta2, ta0, 0x7FFFFF              # get fraction
        srl     ta0, ta0, 31                    # get sign
        bne     ta1, SEXP_INF, 1f               # is it a signaling NAN?
        and     v0, ta2, SSIGNAL_NAN
        bne     v0, zero, invalid_s
1:
        /* fall through to get FS */
#endif

/*----------------------------------------------------------------------------
 * get_fs_s --
 *
 *      Read (single precision) the FS register (bits 15-11) and
 *      break up into fields.
 *      This is an internal routine used by mips_emul_fp only.
 *
 * Results:
 *      t0      contains the sign
 *      t1      contains the (biased) exponent
 *      t2      contains the fraction
 *
 *----------------------------------------------------------------------------
 */
STATIC_XLEAF(get_fs_s)
#ifdef FPEMUL
        srl     t0, a0, 11 - FPX_SCALESHIFT
        PTR_L   t1, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        andi    t0, t0, FPX_REGEVENMASK         # Even regs only
        PTR_ADDU t1, t1, t0

        lw      t0, PCB_FPREGS+FRAME_FP0(t1)

        srl     t1, t0, 23                      # get exponent
        and     t1, t1, 0xFF
        and     t2, t0, 0x7FFFFF                # get fraction
        srl     t0, t0, 31                      # get sign
        bne     t1, SEXP_INF, 1f                # is it a signaling NAN?
        and     v0, t2, SSIGNAL_NAN
        bne     v0, zero, invalid_s
1:
        j       ra
#else
        srl     a3, a0, 11 - (PTR_SCALESHIFT-1) # get FS field (even regs only)
        and     a3, a3, 0xF << PTR_SCALESHIFT   # mask FS field
        PTR_L   a3, get_fs_s_tbl(a3)            # switch on register number
        j       a3

        .rdata
get_fs_s_tbl:
        PTR_WORD get_fs_s_f0
        PTR_WORD get_fs_s_f2
        PTR_WORD get_fs_s_f4
        PTR_WORD get_fs_s_f6
        PTR_WORD get_fs_s_f8
        PTR_WORD get_fs_s_f10
        PTR_WORD get_fs_s_f12
        PTR_WORD get_fs_s_f14
        PTR_WORD get_fs_s_f16
        PTR_WORD get_fs_s_f18
        PTR_WORD get_fs_s_f20
        PTR_WORD get_fs_s_f22
        PTR_WORD get_fs_s_f24
        PTR_WORD get_fs_s_f26
        PTR_WORD get_fs_s_f28
        PTR_WORD get_fs_s_f30
        .text

get_fs_s_f0:
        mfc1    t0, $f0
        b       get_fs_s_done
get_fs_s_f2:
        mfc1    t0, $f2
        b       get_fs_s_done
get_fs_s_f4:
        mfc1    t0, $f4
        b       get_fs_s_done
get_fs_s_f6:
        mfc1    t0, $f6
        b       get_fs_s_done
get_fs_s_f8:
        mfc1    t0, $f8
        b       get_fs_s_done
get_fs_s_f10:
        mfc1    t0, $f10
        b       get_fs_s_done
get_fs_s_f12:
        mfc1    t0, $f12
        b       get_fs_s_done
get_fs_s_f14:
        mfc1    t0, $f14
        b       get_fs_s_done
get_fs_s_f16:
        mfc1    t0, $f16
        b       get_fs_s_done
get_fs_s_f18:
        mfc1    t0, $f18
        b       get_fs_s_done
get_fs_s_f20:
        mfc1    t0, $f20
        b       get_fs_s_done
get_fs_s_f22:
        mfc1    t0, $f22
        b       get_fs_s_done
get_fs_s_f24:
        mfc1    t0, $f24
        b       get_fs_s_done
get_fs_s_f26:
        mfc1    t0, $f26
        b       get_fs_s_done
get_fs_s_f28:
        mfc1    t0, $f28
        b       get_fs_s_done
get_fs_s_f30:
        mfc1    t0, $f30
get_fs_s_done:
        srl     t1, t0, 23                      # get exponent
        and     t1, t1, 0xFF
        and     t2, t0, 0x7FFFFF                # get fraction
        srl     t0, t0, 31                      # get sign
        bne     t1, SEXP_INF, 1f                # is it a signaling NAN?
        and     v0, t2, SSIGNAL_NAN
        bne     v0, zero, invalid_s
1:
        j       ra
#endif
END(get_ft_fs_s)

/*----------------------------------------------------------------------------
 * get_ft_fs_d --
 *
 *      Read (double precision) the FT register (bits 20-16) and
 *      the FS register (bits 15-11) and break up into fields.
 *      This is an internal routine used by mips_emul_fp only.
 *
 * Results:
 *      t0      contains the FS sign
 *      t1      contains the FS (biased) exponent
 *      t2      contains the FS fraction
 *      t3      contains the FS remaining fraction
 *      ta0     contains the FT sign
 *      ta1     contains the FT (biased) exponent
 *      ta2     contains the FT fraction
 *      ta3     contains the FT remaining fraction
 *
 *----------------------------------------------------------------------------
 */
STATIC_LEAF(get_ft_fs_d)
#ifdef FPEMUL
        srl     ta3, a0, 16 - FPX_SCALESHIFT
        PTR_L   ta0, L_PCB(MIPS_CURLWP)         # get pcb of current lwp
        andi    ta3, ta3, FPX_REGEVENMASK       # Even regs only
        PTR_ADDU ta0, ta3

#if defined(__mips_n32) || defined(__mips_n64)
        FPX_L   ta3, PCB_FPREGS+FRAME_FP0(ta0)
        dsrl    ta0, ta3, 32
        srl     ta3, ta3, 0
#else
        lw      ta3, PCB_FPREGS+FRAME_FP0(ta0)
        lw      ta0, PCB_FPREGS+FRAME_FP0+SZFPREG(ta0)
#endif

        srl     ta1, ta0, 20                    # get exponent
        and     ta1, ta1, 0x7FF
        and     ta2, ta0, 0xFFFFF               # get fraction
        srl     ta0, ta0, 31                    # get sign
        bne     ta1, DEXP_INF, 1f               # is it a signaling NAN?
        and     v0, ta2, DSIGNAL_NAN
        bne     v0, zero, invalid_d
1:
        /* fall through to get FS */
#else
        srl     a3, a0, 16 - (PTR_SCALESHIFT-1) # get FT field (even regs only)
        and     a3, a3, 0xF << PTR_SCALESHIFT   # mask FT field
        PTR_L   a3, get_ft_d_tbl(a3)            # switch on register number
        j       a3

        .rdata
get_ft_d_tbl:
        PTR_WORD get_ft_d_f0
        PTR_WORD get_ft_d_f2
        PTR_WORD get_ft_d_f4
        PTR_WORD get_ft_d_f6
        PTR_WORD get_ft_d_f8
        PTR_WORD get_ft_d_f10
        PTR_WORD get_ft_d_f12
        PTR_WORD get_ft_d_f14
        PTR_WORD get_ft_d_f16
        PTR_WORD get_ft_d_f18
        PTR_WORD get_ft_d_f20
        PTR_WORD get_ft_d_f22
        PTR_WORD get_ft_d_f24
        PTR_WORD get_ft_d_f26
        PTR_WORD get_ft_d_f28
        PTR_WORD get_ft_d_f30
        .text

get_ft_d_f0:
        mfc1    ta3, $f0
        mfc1    ta0, $f1
        b       get_ft_d_done
get_ft_d_f2:
        mfc1    ta3, $f2
        mfc1    ta0, $f3
        b       get_ft_d_done
get_ft_d_f4:
        mfc1    ta3, $f4
        mfc1    ta0, $f5
        b       get_ft_d_done
get_ft_d_f6:
        mfc1    ta3, $f6
        mfc1    ta0, $f7
        b       get_ft_d_done
get_ft_d_f8:
        mfc1    ta3, $f8
        mfc1    ta0, $f9
        b       get_ft_d_done
get_ft_d_f10:
        mfc1    ta3, $f10
        mfc1    ta0, $f11
        b       get_ft_d_done
get_ft_d_f12:
        mfc1    ta3, $f12
        mfc1    ta0, $f13
        b       get_ft_d_done
get_ft_d_f14:
        mfc1    ta3, $f14
        mfc1    ta0, $f15
        b       get_ft_d_done
get_ft_d_f16:
        mfc1    ta3, $f16
        mfc1    ta0, $f17
        b       get_ft_d_done
get_ft_d_f18:
        mfc1    ta3, $f18
        mfc1    ta0, $f19
        b       get_ft_d_done
get_ft_d_f20:
        mfc1    ta3, $f20
        mfc1    ta0, $f21
        b       get_ft_d_done
get_ft_d_f22:
        mfc1    ta3, $f22
        mfc1    ta0, $f23
        b       get_ft_d_done
get_ft_d_f24:
        mfc1    ta3, $f24
        mfc1    ta0, $f25
        b       get_ft_d_done
get_ft_d_f26:
        mfc1    ta3, $f26
        mfc1    ta0, $f27
        b       get_ft_d_done
get_ft_d_f28:
        mfc1    ta3, $f28
        mfc1    ta0, $f29
        b       get_ft_d_done
get_ft_d_f30:
        mfc1    ta3, $f30
        mfc1    ta0, $f31
get_ft_d_done:
        srl     ta1, ta0, 20                    # get exponent
        and     ta1, ta1, 0x7FF
        and     ta2, ta0, 0xFFFFF                       # get fraction
        srl     ta0, ta0, 31                    # get sign
        bne     ta1, DEXP_INF, 1f               # is it a signaling NAN?
        and     v0, ta2, DSIGNAL_NAN
        bne     v0, zero, invalid_d
1:
        /* fall through to get FS */
#endif

/*----------------------------------------------------------------------------
 * get_fs_d --
 *
 *      Read (double precision) the FS register (bits 15-11) and
 *      break up into fields.
 *      This is an internal routine used by mips_emul_fp only.
 *
 * Results:
 *      t0      contains the sign
 *      t1      contains the (biased) exponent
 *      t2      contains the fraction
 *      t3      contains the remaining fraction
 *
 *----------------------------------------------------------------------------
 */
STATIC_XLEAF(get_fs_d)
#ifdef FPEMUL
        srl     t3, a0, 11 - FPX_SCALESHIFT
        PTR_L   t0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        andi    t3, t3, FPX_REGEVENMASK         # Even regs only
        PTR_ADDU t0, t3

#if defined(__mips_n32) || defined(__mips_n64)
        FPX_L   t3, PCB_FPREGS+FRAME_FP0(t0)
        dsrl    t0, t3, 32
        srl     t3, t3, 0
#else
        lw      t3, PCB_FPREGS+FRAME_FP0(t0)
        lw      t0, PCB_FPREGS+FRAME_FP0+SZFPREG(t0)
#endif

        srl     t1, t0, 20                      # get exponent
        and     t1, t1, 0x7FF
        and     t2, t0, 0xFFFFF                 # get fraction
        srl     t0, t0, 31                      # get sign
        bne     t1, DEXP_INF, 1f                # is it a signaling NAN?
        and     v0, t2, DSIGNAL_NAN
        bne     v0, zero, invalid_d
1:
        j       ra
#else
        srl     a3, a0, 11 - (PTR_SCALESHIFT-1) # get FS field (even regs only)
        and     a3, a3, 0xF << PTR_SCALESHIFT   # mask FS field
        PTR_L   a3, get_fs_d_tbl(a3)            # switch on register number
        j       a3

        .rdata
get_fs_d_tbl:
        PTR_WORD get_fs_d_f0
        PTR_WORD get_fs_d_f2
        PTR_WORD get_fs_d_f4
        PTR_WORD get_fs_d_f6
        PTR_WORD get_fs_d_f8
        PTR_WORD get_fs_d_f10
        PTR_WORD get_fs_d_f12
        PTR_WORD get_fs_d_f14
        PTR_WORD get_fs_d_f16
        PTR_WORD get_fs_d_f18
        PTR_WORD get_fs_d_f20
        PTR_WORD get_fs_d_f22
        PTR_WORD get_fs_d_f24
        PTR_WORD get_fs_d_f26
        PTR_WORD get_fs_d_f28
        PTR_WORD get_fs_d_f30
        .text

get_fs_d_f0:
        mfc1    t3, $f0
        mfc1    t0, $f1
        b       get_fs_d_done
get_fs_d_f2:
        mfc1    t3, $f2
        mfc1    t0, $f3
        b       get_fs_d_done
get_fs_d_f4:
        mfc1    t3, $f4
        mfc1    t0, $f5
        b       get_fs_d_done
get_fs_d_f6:
        mfc1    t3, $f6
        mfc1    t0, $f7
        b       get_fs_d_done
get_fs_d_f8:
        mfc1    t3, $f8
        mfc1    t0, $f9
        b       get_fs_d_done
get_fs_d_f10:
        mfc1    t3, $f10
        mfc1    t0, $f11
        b       get_fs_d_done
get_fs_d_f12:
        mfc1    t3, $f12
        mfc1    t0, $f13
        b       get_fs_d_done
get_fs_d_f14:
        mfc1    t3, $f14
        mfc1    t0, $f15
        b       get_fs_d_done
get_fs_d_f16:
        mfc1    t3, $f16
        mfc1    t0, $f17
        b       get_fs_d_done
get_fs_d_f18:
        mfc1    t3, $f18
        mfc1    t0, $f19
        b       get_fs_d_done
get_fs_d_f20:
        mfc1    t3, $f20
        mfc1    t0, $f21
        b       get_fs_d_done
get_fs_d_f22:
        mfc1    t3, $f22
        mfc1    t0, $f23
        b       get_fs_d_done
get_fs_d_f24:
        mfc1    t3, $f24
        mfc1    t0, $f25
        b       get_fs_d_done
get_fs_d_f26:
        mfc1    t3, $f26
        mfc1    t0, $f27
        b       get_fs_d_done
get_fs_d_f28:
        mfc1    t3, $f28
        mfc1    t0, $f29
        b       get_fs_d_done
get_fs_d_f30:
        mfc1    t3, $f30
        mfc1    t0, $f31
get_fs_d_done:
        srl     t1, t0, 20                      # get exponent
        and     t1, t1, 0x7FF
        and     t2, t0, 0xFFFFF                 # get fraction
        srl     t0, t0, 31                      # get sign
        bne     t1, DEXP_INF, 1f                # is it a signaling NAN?
        and     v0, t2, DSIGNAL_NAN
        bne     v0, zero, invalid_d
1:
        j       ra
#endif
END(get_ft_fs_d)

/*----------------------------------------------------------------------------
 * get_cmp_s --
 *
 *      Read (single precision) the FS register (bits 15-11) and
 *      the FT register (bits 20-16) and break up into fields.
 *      This is an internal routine used by mips_emul_fp only.
 *
 * Results:
 *      t0      contains the sign
 *      t1      contains the (biased) exponent
 *      t2      contains the fraction
 *      ta0     contains the sign
 *      ta1     contains the (biased) exponent
 *      ta2     contains the fraction
 *
 *----------------------------------------------------------------------------
 */
STATIC_LEAF(get_cmp_s)
#ifdef FPEMUL
        srl     t1, a0, 11 - FPX_SCALESHIFT
        PTR_L   ta2, L_PCB(MIPS_CURLWP)         # get pcb of current lwp
        andi    t1, t1, FPX_REGEVENMASK                 # Even regs only
        PTR_ADDU t0, ta2, t1

        lw      t0, PCB_FPREGS+FRAME_FP0(t0)

        srl     t1, t0, 23                      # get exponent
        and     t1, t1, 0xFF
        and     t2, t0, 0x7FFFFF                # get fraction
        srl     t0, t0, 31                      # get sign

        srl     ta0, a0, 16 - FPX_SCALESHIFT
        andi    ta0, ta0, FPX_REGEVENMASK                       # Even regs only
        PTR_ADDU ta2, ta0

        lw      ta0, PCB_FPREGS+FRAME_FP0(ta2)

        srl     ta1, ta0, 23                    # get exponent
        and     ta1, ta1, 0xFF
        and     ta2, ta0, 0x7FFFFF              # get fraction
        srl     ta0, ta0, 31                    # get sign
        j       ra
#else
        srl     a3, a0, 11 - (PTR_SCALESHIFT-1) # get FS field (even regs only)
        and     a3, a3, 0xF << PTR_SCALESHIFT   # mask FS field
        PTR_L   a3, cmp_fs_s_tbl(a3)            # switch on register number
        j       a3

        .rdata
cmp_fs_s_tbl:
        PTR_WORD cmp_fs_s_f0
        PTR_WORD cmp_fs_s_f2
        PTR_WORD cmp_fs_s_f4
        PTR_WORD cmp_fs_s_f6
        PTR_WORD cmp_fs_s_f8
        PTR_WORD cmp_fs_s_f10
        PTR_WORD cmp_fs_s_f12
        PTR_WORD cmp_fs_s_f14
        PTR_WORD cmp_fs_s_f16
        PTR_WORD cmp_fs_s_f18
        PTR_WORD cmp_fs_s_f20
        PTR_WORD cmp_fs_s_f22
        PTR_WORD cmp_fs_s_f24
        PTR_WORD cmp_fs_s_f26
        PTR_WORD cmp_fs_s_f28
        PTR_WORD cmp_fs_s_f30
        .text

cmp_fs_s_f0:
        mfc1    t0, $f0
        b       cmp_fs_s_done
cmp_fs_s_f2:
        mfc1    t0, $f2
        b       cmp_fs_s_done
cmp_fs_s_f4:
        mfc1    t0, $f4
        b       cmp_fs_s_done
cmp_fs_s_f6:
        mfc1    t0, $f6
        b       cmp_fs_s_done
cmp_fs_s_f8:
        mfc1    t0, $f8
        b       cmp_fs_s_done
cmp_fs_s_f10:
        mfc1    t0, $f10
        b       cmp_fs_s_done
cmp_fs_s_f12:
        mfc1    t0, $f12
        b       cmp_fs_s_done
cmp_fs_s_f14:
        mfc1    t0, $f14
        b       cmp_fs_s_done
cmp_fs_s_f16:
        mfc1    t0, $f16
        b       cmp_fs_s_done
cmp_fs_s_f18:
        mfc1    t0, $f18
        b       cmp_fs_s_done
cmp_fs_s_f20:
        mfc1    t0, $f20
        b       cmp_fs_s_done
cmp_fs_s_f22:
        mfc1    t0, $f22
        b       cmp_fs_s_done
cmp_fs_s_f24:
        mfc1    t0, $f24
        b       cmp_fs_s_done
cmp_fs_s_f26:
        mfc1    t0, $f26
        b       cmp_fs_s_done
cmp_fs_s_f28:
        mfc1    t0, $f28
        b       cmp_fs_s_done
cmp_fs_s_f30:
        mfc1    t0, $f30
cmp_fs_s_done:
        srl     t1, t0, 23                      # get exponent
        and     t1, t1, 0xFF
        and     t2, t0, 0x7FFFFF                # get fraction
        srl     t0, t0, 31                      # get sign

        srl     a3, a0, 17 - PTR_SCALESHIFT                     # get FT field (even regs only)
        and     a3, a3, 0xF << PTR_SCALESHIFT           # mask FT field
        PTR_L   a3, cmp_ft_s_tbl(a3)            # switch on register number
        j       a3

        .rdata
cmp_ft_s_tbl:
        PTR_WORD cmp_ft_s_f0
        PTR_WORD cmp_ft_s_f2
        PTR_WORD cmp_ft_s_f4
        PTR_WORD cmp_ft_s_f6
        PTR_WORD cmp_ft_s_f8
        PTR_WORD cmp_ft_s_f10
        PTR_WORD cmp_ft_s_f12
        PTR_WORD cmp_ft_s_f14
        PTR_WORD cmp_ft_s_f16
        PTR_WORD cmp_ft_s_f18
        PTR_WORD cmp_ft_s_f20
        PTR_WORD cmp_ft_s_f22
        PTR_WORD cmp_ft_s_f24
        PTR_WORD cmp_ft_s_f26
        PTR_WORD cmp_ft_s_f28
        PTR_WORD cmp_ft_s_f30
        .text

cmp_ft_s_f0:
        mfc1    ta0, $f0
        b       cmp_ft_s_done
cmp_ft_s_f2:
        mfc1    ta0, $f2
        b       cmp_ft_s_done
cmp_ft_s_f4:
        mfc1    ta0, $f4
        b       cmp_ft_s_done
cmp_ft_s_f6:
        mfc1    ta0, $f6
        b       cmp_ft_s_done
cmp_ft_s_f8:
        mfc1    ta0, $f8
        b       cmp_ft_s_done
cmp_ft_s_f10:
        mfc1    ta0, $f10
        b       cmp_ft_s_done
cmp_ft_s_f12:
        mfc1    ta0, $f12
        b       cmp_ft_s_done
cmp_ft_s_f14:
        mfc1    ta0, $f14
        b       cmp_ft_s_done
cmp_ft_s_f16:
        mfc1    ta0, $f16
        b       cmp_ft_s_done
cmp_ft_s_f18:
        mfc1    ta0, $f18
        b       cmp_ft_s_done
cmp_ft_s_f20:
        mfc1    ta0, $f20
        b       cmp_ft_s_done
cmp_ft_s_f22:
        mfc1    ta0, $f22
        b       cmp_ft_s_done
cmp_ft_s_f24:
        mfc1    ta0, $f24
        b       cmp_ft_s_done
cmp_ft_s_f26:
        mfc1    ta0, $f26
        b       cmp_ft_s_done
cmp_ft_s_f28:
        mfc1    ta0, $f28
        b       cmp_ft_s_done
cmp_ft_s_f30:
        mfc1    ta0, $f30
cmp_ft_s_done:
        srl     ta1, ta0, 23                    # get exponent
        and     ta1, ta1, 0xFF
        and     ta2, ta0, 0x7FFFFF              # get fraction
        srl     ta0, ta0, 31                    # get sign
        j       ra
#endif
END(get_cmp_s)

/*----------------------------------------------------------------------------
 * get_cmp_d --
 *
 *      Read (double precision) the FS register (bits 15-11) and
 *      the FT register (bits 20-16) and break up into fields.
 *      This is an internal routine used by mips_emul_fp only.
 *
 * Results:
 *      t0      contains the sign
 *      t1      contains the (biased) exponent
 *      t2      contains the fraction
 *      t3      contains the remaining fraction
 *      ta0     contains the sign
 *      ta1     contains the (biased) exponent
 *      ta2     contains the fraction
 *      ta3     contains the remaining fraction
 *
 *----------------------------------------------------------------------------
 */
STATIC_LEAF(get_cmp_d)
#ifdef FPEMUL
        srl     t1, a0, 11-FPX_SCALESHIFT
        PTR_L   ta2, L_PCB(MIPS_CURLWP)         # get pcb of current lwp
        andi    t1, t1, FPX_REGEVENMASK                 # Even regs only
        PTR_ADDU t0, ta2, t1

#if defined(__mips_n32) || defined(__mips_n64)
        FPX_L   t3, PCB_FPREGS+FRAME_FP0(t0)
        dsrl    t0, t3, 32
        srl     t3, t3, 0
#else
        FPX_L   t3, PCB_FPREGS+FRAME_FP0(t0)
        FPX_L   t0, PCB_FPREGS+FRAME_FP0+SZFPREG(t0)
#endif

        srl     t1, t0, 20                      # get exponent
        and     t1, t1, 0x7FF
        and     t2, t0, 0xFFFFF                 # get fraction
        srl     t0, t0, 31                      # get sign

        srl     ta0, a0, 16 - FPX_SCALESHIFT
        andi    ta0, ta0, FPX_REGEVENMASK               # Even regs only
        PTR_ADDU ta2, ta2, ta0

#if defined(__mips_n32) || defined(__mips_n64)
        FPX_L   ta3, PCB_FPREGS+FRAME_FP0(ta2)
        dsrl    ta0, ta3, 32
        srl     ta3, ta3, 0
#else
        lw      ta3, PCB_FPREGS+FRAME_FP0(ta2)
        lw      ta0, PCB_FPREGS+FRAME_FP0+SZFPREG(ta2)
#endif

        srl     ta1, ta0, 20                    # get exponent
        and     ta1, ta1, 0x7FF
        and     ta2, ta0, 0xFFFFF                       # get fraction
        srl     ta0, ta0, 31                    # get sign
        j       ra
#else
        srl     a3, a0, 12 - PTR_SCALESHIFT     # get FS field
        and     a3, a3, 0xF << PTR_SCALESHIFT   # mask FS field (even regs only)
        PTR_L   a3, cmp_fs_d_tbl(a3)            # switch on register number
        j       a3

        .rdata
cmp_fs_d_tbl:
        PTR_WORD cmp_fs_d_f0
        PTR_WORD cmp_fs_d_f2
        PTR_WORD cmp_fs_d_f4
        PTR_WORD cmp_fs_d_f6
        PTR_WORD cmp_fs_d_f8
        PTR_WORD cmp_fs_d_f10
        PTR_WORD cmp_fs_d_f12
        PTR_WORD cmp_fs_d_f14
        PTR_WORD cmp_fs_d_f16
        PTR_WORD cmp_fs_d_f18
        PTR_WORD cmp_fs_d_f20
        PTR_WORD cmp_fs_d_f22
        PTR_WORD cmp_fs_d_f24
        PTR_WORD cmp_fs_d_f26
        PTR_WORD cmp_fs_d_f28
        PTR_WORD cmp_fs_d_f30
        .text

cmp_fs_d_f0:
        mfc1    t3, $f0
        mfc1    t0, $f1
        b       cmp_fs_d_done
cmp_fs_d_f2:
        mfc1    t3, $f2
        mfc1    t0, $f3
        b       cmp_fs_d_done
cmp_fs_d_f4:
        mfc1    t3, $f4
        mfc1    t0, $f5
        b       cmp_fs_d_done
cmp_fs_d_f6:
        mfc1    t3, $f6
        mfc1    t0, $f7
        b       cmp_fs_d_done
cmp_fs_d_f8:
        mfc1    t3, $f8
        mfc1    t0, $f9
        b       cmp_fs_d_done
cmp_fs_d_f10:
        mfc1    t3, $f10
        mfc1    t0, $f11
        b       cmp_fs_d_done
cmp_fs_d_f12:
        mfc1    t3, $f12
        mfc1    t0, $f13
        b       cmp_fs_d_done
cmp_fs_d_f14:
        mfc1    t3, $f14
        mfc1    t0, $f15
        b       cmp_fs_d_done
cmp_fs_d_f16:
        mfc1    t3, $f16
        mfc1    t0, $f17
        b       cmp_fs_d_done
cmp_fs_d_f18:
        mfc1    t3, $f18
        mfc1    t0, $f19
        b       cmp_fs_d_done
cmp_fs_d_f20:
        mfc1    t3, $f20
        mfc1    t0, $f21
        b       cmp_fs_d_done
cmp_fs_d_f22:
        mfc1    t3, $f22
        mfc1    t0, $f23
        b       cmp_fs_d_done
cmp_fs_d_f24:
        mfc1    t3, $f24
        mfc1    t0, $f25
        b       cmp_fs_d_done
cmp_fs_d_f26:
        mfc1    t3, $f26
        mfc1    t0, $f27
        b       cmp_fs_d_done
cmp_fs_d_f28:
        mfc1    t3, $f28
        mfc1    t0, $f29
        b       cmp_fs_d_done
cmp_fs_d_f30:
        mfc1    t3, $f30
        mfc1    t0, $f31
cmp_fs_d_done:
        srl     t1, t0, 20                      # get exponent
        and     t1, t1, 0x7FF
        and     t2, t0, 0xFFFFF                 # get fraction
        srl     t0, t0, 31                      # get sign

        srl     a3, a0, 17 - PTR_SCALESHIFT     # get FT field (even regs only)
        and     a3, a3, 0xF << PTR_SCALESHIFT   # mask FT field
        PTR_L   a3, cmp_ft_d_tbl(a3)            # switch on register number
        j       a3

        .rdata
cmp_ft_d_tbl:
        PTR_WORD cmp_ft_d_f0
        PTR_WORD cmp_ft_d_f2
        PTR_WORD cmp_ft_d_f4
        PTR_WORD cmp_ft_d_f6
        PTR_WORD cmp_ft_d_f8
        PTR_WORD cmp_ft_d_f10
        PTR_WORD cmp_ft_d_f12
        PTR_WORD cmp_ft_d_f14
        PTR_WORD cmp_ft_d_f16
        PTR_WORD cmp_ft_d_f18
        PTR_WORD cmp_ft_d_f20
        PTR_WORD cmp_ft_d_f22
        PTR_WORD cmp_ft_d_f24
        PTR_WORD cmp_ft_d_f26
        PTR_WORD cmp_ft_d_f28
        PTR_WORD cmp_ft_d_f30
        .text

cmp_ft_d_f0:
        mfc1    ta3, $f0
        mfc1    ta0, $f1
        b       cmp_ft_d_done
cmp_ft_d_f2:
        mfc1    ta3, $f2
        mfc1    ta0, $f3
        b       cmp_ft_d_done
cmp_ft_d_f4:
        mfc1    ta3, $f4
        mfc1    ta0, $f5
        b       cmp_ft_d_done
cmp_ft_d_f6:
        mfc1    ta3, $f6
        mfc1    ta0, $f7
        b       cmp_ft_d_done
cmp_ft_d_f8:
        mfc1    ta3, $f8
        mfc1    ta0, $f9
        b       cmp_ft_d_done
cmp_ft_d_f10:
        mfc1    ta3, $f10
        mfc1    ta0, $f11
        b       cmp_ft_d_done
cmp_ft_d_f12:
        mfc1    ta3, $f12
        mfc1    ta0, $f13
        b       cmp_ft_d_done
cmp_ft_d_f14:
        mfc1    ta3, $f14
        mfc1    ta0, $f15
        b       cmp_ft_d_done
cmp_ft_d_f16:
        mfc1    ta3, $f16
        mfc1    ta0, $f17
        b       cmp_ft_d_done
cmp_ft_d_f18:
        mfc1    ta3, $f18
        mfc1    ta0, $f19
        b       cmp_ft_d_done
cmp_ft_d_f20:
        mfc1    ta3, $f20
        mfc1    ta0, $f21
        b       cmp_ft_d_done
cmp_ft_d_f22:
        mfc1    ta3, $f22
        mfc1    ta0, $f23
        b       cmp_ft_d_done
cmp_ft_d_f24:
        mfc1    ta3, $f24
        mfc1    ta0, $f25
        b       cmp_ft_d_done
cmp_ft_d_f26:
        mfc1    ta3, $f26
        mfc1    ta0, $f27
        b       cmp_ft_d_done
cmp_ft_d_f28:
        mfc1    ta3, $f28
        mfc1    ta0, $f29
        b       cmp_ft_d_done
cmp_ft_d_f30:
        mfc1    ta3, $f30
        mfc1    ta0, $f31
cmp_ft_d_done:
        srl     ta1, ta0, 20                    # get exponent
        and     ta1, ta1, 0x7FF
        and     ta2, ta0, 0xFFFFF                       # get fraction
        srl     ta0, ta0, 31                    # get sign
        j       ra
#endif
END(get_cmp_d)

/*----------------------------------------------------------------------------
 * set_fd_s --
 *
 *      Write (single precision) the FD register (bits 10-6).
 *      This is an internal routine used by mips_emul_fp only.
 *
 * Arguments:
 *      a0      contains the FP instruction
 *      t0      contains the sign
 *      t1      contains the (biased) exponent
 *      t2      contains the fraction
 *
 * set_fd_word --
 *
 *      Write (integer) the FD register (bits 10-6).
 *      This is an internal routine used by mips_emul_fp only.
 *
 * Arguments:
 *      a0      contains the FP instruction
 *      t2      contains the integer
 *
 *----------------------------------------------------------------------------
 */
STATIC_LEAF(set_fd_s)
        sll     t0, t0, 31                      # position sign
        sll     t1, t1, 23                      # position exponent
        or      t2, t2, t0
        or      t2, t2, t1
STATIC_XLEAF(set_fd_word)
#ifdef FPEMUL
        srl     t1, a0, 6 - FPX_SCALESHIFT
        PTR_L   t0, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        andi    t1, t1, FPX_REGEVENMASK         # Even regs only
        PTR_ADDU t0, t0, t1

        FPX_S   t2, PCB_FPREGS+FRAME_FP0(t0)
        j       ra
#else
        srl     a3, a0, 6 + 1 - PTR_SCALESHIFT  # get FD field (even regs only)
        and     a3, a3, 0xF << PTR_SCALESHIFT   # mask FT field
        PTR_L   a3, set_fd_s_tbl(a3)            # switch on register number
        j       a3

        .rdata
set_fd_s_tbl:
        PTR_WORD set_fd_s_f0
        PTR_WORD set_fd_s_f2
        PTR_WORD set_fd_s_f4
        PTR_WORD set_fd_s_f6
        PTR_WORD set_fd_s_f8
        PTR_WORD set_fd_s_f10
        PTR_WORD set_fd_s_f12
        PTR_WORD set_fd_s_f14
        PTR_WORD set_fd_s_f16
        PTR_WORD set_fd_s_f18
        PTR_WORD set_fd_s_f20
        PTR_WORD set_fd_s_f22
        PTR_WORD set_fd_s_f24
        PTR_WORD set_fd_s_f26
        PTR_WORD set_fd_s_f28
        PTR_WORD set_fd_s_f30
        .text

set_fd_s_f0:
        mtc1    t2, $f0
        j       ra
set_fd_s_f2:
        mtc1    t2, $f2
        j       ra
set_fd_s_f4:
        mtc1    t2, $f4
        j       ra
set_fd_s_f6:
        mtc1    t2, $f6
        j       ra
set_fd_s_f8:
        mtc1    t2, $f8
        j       ra
set_fd_s_f10:
        mtc1    t2, $f10
        j       ra
set_fd_s_f12:
        mtc1    t2, $f12
        j       ra
set_fd_s_f14:
        mtc1    t2, $f14
        j       ra
set_fd_s_f16:
        mtc1    t2, $f16
        j       ra
set_fd_s_f18:
        mtc1    t2, $f18
        j       ra
set_fd_s_f20:
        mtc1    t2, $f20
        j       ra
set_fd_s_f22:
        mtc1    t2, $f22
        j       ra
set_fd_s_f24:
        mtc1    t2, $f24
        j       ra
set_fd_s_f26:
        mtc1    t2, $f26
        j       ra
set_fd_s_f28:
        mtc1    t2, $f28
        j       ra
set_fd_s_f30:
        mtc1    t2, $f30
        j       ra
#endif
END(set_fd_s)

/*----------------------------------------------------------------------------
 * set_fd_d --
 *
 *      Write (double precision) the FT register (bits 10-6).
 *      This is an internal routine used by mips_emul_fp only.
 *
 * Arguments:
 *      a0      contains the FP instruction
 *      t0      contains the sign
 *      t1      contains the (biased) exponent
 *      t2      contains the fraction
 *      t3      contains the remaining fraction
 *
 *----------------------------------------------------------------------------
 */
STATIC_LEAF(set_fd_d)
#ifdef FPEMUL
        sll     t0, t0, 31                      # set sign
        sll     t1, t1, 20                      # set exponent
        or      t0, t0, t1
        or      t0, t0, t2                      # set fraction

        srl     t1, a0, 6-FPX_SCALESHIFT
        PTR_L   t2, L_PCB(MIPS_CURLWP)          # get pcb of current lwp
        andi    t1, t1, FPX_REGEVENMASK
        PTR_ADDU t2, t2, t1

#if defined(__mips_n32) || defined(__mips_n64)
        dsll    t0, t0, 32
        or      t0, t0, t3
        FPX_S   t0, PCB_FPREGS+FRAME_FP0(t2)
#else
        FPX_S   t3, PCB_FPREGS+FRAME_FP0(t2)
        FPX_S   t0, PCB_FPREGS+FRAME_FP0+4(t2)
#endif
        j       ra
#else
        sll     t0, t0, 31                      # set sign
        sll     t1, t1, 20                      # set exponent
        or      t0, t0, t1
        or      t0, t0, t2                      # set fraction
        srl     a3, a0, 7 - PTR_SCALESHIFT      # get FD field (even regs only)
        and     a3, a3, 0xF << PTR_SCALESHIFT   # mask FD field
        PTR_L   a3, set_fd_d_tbl(a3)            # switch on register number
        j       a3

        .rdata
set_fd_d_tbl:
        PTR_WORD set_fd_d_f0
        PTR_WORD set_fd_d_f2
        PTR_WORD set_fd_d_f4
        PTR_WORD set_fd_d_f6
        PTR_WORD set_fd_d_f8
        PTR_WORD set_fd_d_f10
        PTR_WORD set_fd_d_f12
        PTR_WORD set_fd_d_f14
        PTR_WORD set_fd_d_f16
        PTR_WORD set_fd_d_f18
        PTR_WORD set_fd_d_f20
        PTR_WORD set_fd_d_f22
        PTR_WORD set_fd_d_f24
        PTR_WORD set_fd_d_f26
        PTR_WORD set_fd_d_f28
        PTR_WORD set_fd_d_f30
        .text

set_fd_d_f0:
        mtc1    t3, $f0
        mtc1    t0, $f1
        j       ra
set_fd_d_f2:
        mtc1    t3, $f2
        mtc1    t0, $f3
        j       ra
set_fd_d_f4:
        mtc1    t3, $f4
        mtc1    t0, $f5
        j       ra
set_fd_d_f6:
        mtc1    t3, $f6
        mtc1    t0, $f7
        j       ra
set_fd_d_f8:
        mtc1    t3, $f8
        mtc1    t0, $f9
        j       ra
set_fd_d_f10:
        mtc1    t3, $f10
        mtc1    t0, $f11
        j       ra
set_fd_d_f12:
        mtc1    t3, $f12
        mtc1    t0, $f13
        j       ra
set_fd_d_f14:
        mtc1    t3, $f14
        mtc1    t0, $f15
        j       ra
set_fd_d_f16:
        mtc1    t3, $f16
        mtc1    t0, $f17
        j       ra
set_fd_d_f18:
        mtc1    t3, $f18
        mtc1    t0, $f19
        j       ra
set_fd_d_f20:
        mtc1    t3, $f20
        mtc1    t0, $f21
        j       ra
set_fd_d_f22:
        mtc1    t3, $f22
        mtc1    t0, $f23
        j       ra
set_fd_d_f24:
        mtc1    t3, $f24
        mtc1    t0, $f25
        j       ra
set_fd_d_f26:
        mtc1    t3, $f26
        mtc1    t0, $f27
        j       ra
set_fd_d_f28:
        mtc1    t3, $f28
        mtc1    t0, $f29
        j       ra
set_fd_d_f30:
        mtc1    t3, $f30
        mtc1    t0, $f31
        j       ra
#endif
END(set_fd_d)

/*----------------------------------------------------------------------------
 * renorm_fs_s --
 *
 * Results:
 *      t1      unbiased exponent
 *      t2      normalized fraction
 *
 *----------------------------------------------------------------------------
 */
STATIC_LEAF(renorm_fs_s)
/*
 * Find out how many leading zero bits are in t2 and put in at.
 */
#if __mips == 32 || __mips == 64
        clz     v1, t2
#else
        .set    noat
        move    v0, t2
        move    v1, zero
        srl     AT, v0, 16
        bne     AT, zero, 1f
        addu    v1, 16
        sll     v0, 16
1:
        srl     AT, v0, 24
        bne     AT, zero, 1f
        addu    v1, 8
        sll     v0, 8
1:
        srl     AT, v0, 28
        bne     AT, zero, 1f
        addu    v1, 4
        sll     v0, 4
1:
        srl     AT, v0, 30
        bne     AT, zero, 1f
        addu    v1, 2
        sll     v0, 2
1:
        srl     AT, v0, 31
        bne     AT, zero, 1f
        addu    v1, 1
1:
        .set    at
#endif /* __mips == 32 || __mips == 64 */
/*
 * Now shift t2 the correct number of bits.
 */
        subu    v1, v1, SLEAD_ZEROS     # dont count normal leading zeros
        li      t1, SEXP_MIN
        subu    t1, t1, v1              # adjust exponent
        sll     t2, t2, v1
        j       ra
END(renorm_fs_s)

/*----------------------------------------------------------------------------
 * renorm_fs_d --
 *
 * Results:
 *      t1      unbiased exponent
 *      t2,t3   normalized fraction
 *
 *----------------------------------------------------------------------------
 */
STATIC_LEAF(renorm_fs_d)
/*
 * Find out how many leading zero bits are in t2,t3 and put in v1.
 */
#if __mips == 32 || __mips == 64
#ifdef __mips_o32
        bne     ta2, zero, 1f
        clz     v1, ta3
        addu    v1, 32
        b       2f
1:
        clz     v1, ta2
2:
#elif __mips_isa_rev == 2
        move    v0, ta3
        dins    v0, ta2, 32, 32
        dclz    v1, v0
#else
        dsll    v0, ta3, 32
        dsrl    v0, v0, 32
        dsll    v1, ta2, 32
        or      v0, v1
        dclz    v1, v0
#endif /* __mips_o32 */
#else
        .set    noat
        move    v0, t2
        move    v1, zero
        bne     t2, zero, 1f
        move    v0, t3
        addu    v1, 32
1:
        srl     AT, v0, 16
        bne     AT, zero, 1f
        addu    v1, 16
        sll     v0, 16
1:
        srl     AT, v0, 24
        bne     AT, zero, 1f
        addu    v1, 8
        sll     v0, 8
1:
        srl     AT, v0, 28
        bne     AT, zero, 1f
        addu    v1, 4
        sll     v0, 4
1:
        srl     AT, v0, 30
        bne     AT, zero, 1f
        addu    v1, 2
        sll     v0, 2
1:
        srl     AT, v0, 31
        bne     AT, zero, 1f
        addu    v1, 1
1:
        .set    at
#endif /* __mips == 32 || __mips == 64 */
/*
 * Now shift t2,t3 the correct number of bits.
 */
        subu    v1, v1, DLEAD_ZEROS     # dont count normal leading zeros
        li      t1, DEXP_MIN
        subu    t1, t1, v1              # adjust exponent
#ifdef __mips_o32
        li      v0, 32
        blt     v1, v0, 1f
        subu    v1, v1, v0              # shift fraction left >= 32 bits
        sll     t2, t3, v1
        move    t3, zero
        j       ra
1:
        subu    v0, v0, v1              # shift fraction left < 32 bits
        sll     t2, t2, v1
        srl     v0, t3, v0
        or      t2, t2, v0
        sll     t3, t3, v1
        j       ra
#else
        dsll    v0, v0, t1
        dsrl    t2, v0, 32              # MSW
        sll     t3, v0, 0               # LSW
        j       ra
#endif
END(renorm_fs_d)

/*----------------------------------------------------------------------------
 * renorm_ft_s --
 *
 * Results:
 *      ta1     unbiased exponent
 *      ta2     normalized fraction
 *
 *----------------------------------------------------------------------------
 */
STATIC_LEAF(renorm_ft_s)
/*
 * Find out how many leading zero bits are in ta2 and put in v1.
 */
#if __mips == 32 || __mips == 64
        clz     v1, ta2
#else
        .set    noat

        move    v0, ta2
        move    v1, zero
        srl     AT, v0, 16
        bne     AT, zero, 1f
        addu    v1, 16
        sll     v0, 16
1:
        srl     AT, v0, 24
        bne     AT, zero, 1f
        addu    v1, 8
        sll     v0, 8
1:
        srl     AT, v0, 28
        bne     AT, zero, 1f
        addu    v1, 4
        sll     v0, 4
1:
        srl     AT, v0, 30
        bne     AT, zero, 1f
        addu    v1, 2
        sll     v0, 2
1:
        srl     AT, v0, 31
        bne     AT, zero, 1f
        addu    v1, 1
1:
        .set    at
#endif /* __mips == 32 || __mips == 64 */
/*
 * Now shift ta2 the correct number of bits.
 */
        subu    v1, v1, SLEAD_ZEROS     # dont count normal leading zeros
        li      ta1, SEXP_MIN
        subu    ta1, ta1, v1            # adjust exponent
        sll     ta2, ta2, v1
        j       ra
END(renorm_ft_s)

/*----------------------------------------------------------------------------
 * renorm_ft_d --
 *
 * Results:
 *      ta1     unbiased exponent
 *      ta2,ta3 normalized fraction
 *
 *----------------------------------------------------------------------------
 */
STATIC_LEAF(renorm_ft_d)
/*
 * Find out how many leading zero bits are in ta2,ta3 and put in at.
 */
#if __mips == 32 || __mips == 64
#ifdef __mips_o32
        bne     ta2, zero, 1f
        clz     v1, ta3
        addu    v1, 32
        b       2f
1:
        clz     v1, ta2
2:
#elif __mips_isa_rev == 2
        move    v0, ta3
        dins    v0, ta2, 32, 32
        dclz    v1, v0
#else
        dsll    v0, ta3, 32
        dsrl    v0, v0, 32
        dsll    v1, ta2, 32
        or      v0, v1
        dclz    v1, v0
#endif /* __mips_o32 */
#else
        .set    noat
        move    v0, ta2
        move    v1, zero
        bne     ta2, zero, 1f
        move    v0, ta3
        addu    v1, 32
1:
        srl     AT, v0, 16
        bne     AT, zero, 1f
        addu    v1, 16
        sll     v0, 16
1:
        srl     AT, v0, 24
        bne     AT, zero, 1f
        addu    v1, 8
        sll     v0, 8
1:
        srl     AT, v0, 28
        bne     AT, zero, 1f
        addu    v1, 4
        sll     v0, 4
1:
        srl     AT, v0, 30
        bne     AT, zero, 1f
        addu    v1, 2
        sll     v0, 2
1:
        srl     AT, v0, 31
        bne     AT, zero, 1f
        addu    v1, 1
1:
        .set    at
#endif /* __mips == 32 || __mips == 64 */
/*
 * Now shift ta2,ta3 the correct number of bits.
 */
        subu    v1, v1, DLEAD_ZEROS     # dont count normal leading zeros
        li      ta1, DEXP_MIN
        subu    ta1, ta1, v1            # adjust exponent
#ifdef __mips_o32
        li      v0, 32
        blt     v1, v0, 1f
        subu    v1, v1, v0              # shift fraction left >= 32 bits
        sll     ta2, ta3, v1
        move    ta3, zero
        j       ra
1:
        subu    v0, v0, v1              # shift fraction left < 32 bits
        sll     ta2, ta2, v1
        srl     v0, ta3, v0
        or      ta2, ta2, v0
        sll     ta3, ta3, v1
        j       ra
#else
        dsll    v0, v0, t1
        dsrl    ta2, v0, 32
        sll     ta3, v0, 0
        j       ra
#endif  /* __mips_o32 */
END(renorm_ft_d)

/*
 * Send SIGILL, SIGFPE.
 * Args are same as mips_emul_fp.
 */
STATIC_LEAF(fpemul_sigill)
        li      t0, 0xFFFFFF00
        and     a2, a2, t0
        ori     a2, a2, T_RES_INST << MIPS_CR_EXC_CODE_SHIFT
        REG_PROLOGUE
        REG_S   a2, TF_REG_CAUSE(a1)
        REG_EPILOGUE

        move    a1, a0                          # code = instruction
        move    a0, MIPS_CURLWP                 # get current lwp
        j       _C_LABEL(mips_fpuillinst)
END(fpemul_sigill)

STATIC_LEAF(fpemul_sigfpe)
        li      t0, 0xFFFFFF00
        and     a2, a2, t0
        ori     a2, a2, T_FPE << MIPS_CR_EXC_CODE_SHIFT
        REG_PROLOGUE
        REG_S   a2, TF_REG_CAUSE(a1)
        REG_EPILOGUE

        move    a1, a3                          # fpustat
        move    a0, MIPS_CURLWP                 # get current lwp
        j       _C_LABEL(mips_fpuexcept)
END(fpemul_sigfpe)

#ifdef FPEMUL
STATIC_LEAF(mips_emul_sigfpe)
        li      t0, 0xFFFFFF00
        and     a2, a2, t0
        ori     a2, a2, T_OVFLOW << MIPS_CR_EXC_CODE_SHIFT
        REG_PROLOGUE
        REG_S   a2, TF_REG_CAUSE(a1)
        REG_EPILOGUE

        move    a1, a3                          # fpustat
        move    a0, MIPS_CURLWP                 # get current lwp
        j       _C_LABEL(mips_fpuexcept)
END(mips_emul_sigfpe)
#endif