*       $NetBSD: res_func.sa,v 1.6 2021/12/07 21:37:36 andvar Exp $

*       MOTOROLA MICROPROCESSOR & MEMORY TECHNOLOGY GROUP
*       M68000 Hi-Performance Microprocessor Division
*       M68040 Software Package
*
*       M68040 Software Package Copyright (c) 1993, 1994 Motorola Inc.
*       All rights reserved.
*
*       THE SOFTWARE is provided on an "AS IS" basis and without warranty.
*       To the maximum extent permitted by applicable law,
*       MOTOROLA DISCLAIMS ALL WARRANTIES WHETHER EXPRESS OR IMPLIED,
*       INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
*       PARTICULAR PURPOSE and any warranty against infringement with
*       regard to the SOFTWARE (INCLUDING ANY MODIFIED VERSIONS THEREOF)
*       and any accompanying written materials.
*
*       To the maximum extent permitted by applicable law,
*       IN NO EVENT SHALL MOTOROLA BE LIABLE FOR ANY DAMAGES WHATSOEVER
*       (INCLUDING WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS
*       PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR
*       OTHER PECUNIARY LOSS) ARISING OF THE USE OR INABILITY TO USE THE
*       SOFTWARE.  Motorola assumes no responsibility for the maintenance
*       and support of the SOFTWARE.
*
*       You are hereby granted a copyright license to use, modify, and
*       distribute the SOFTWARE so long as this entire notice is retained
*       without alteration in any modified and/or redistributed versions,
*       and that such modified versions are clearly identified as such.
*       No licenses are granted by implication, estoppel or otherwise
*       under any patents or trademarks of Motorola, Inc.

*
*       res_func.sa 3.9 7/29/91
*
* Normalizes denormalized numbers if necessary and updates the
* stack frame.  The function is then restored back into the
* machine and the 040 completes the operation.  This routine
* is only used by the unsupported data type/format handler.
* (Exception vector 55).
*
* For packed move out (fmove.p fpm,<ea>) the operation is
* completed here; data is packed and moved to user memory.
* The stack is restored to the 040 only in the case of a
* reportable exception in the conversion.
*

RES_FUNC    IDNT    2,1 Motorola 040 Floating Point Software Package

       section 8

       include fpsp.h

sp_bnds:        dc.w    $3f81,$407e
               dc.w    $3f6a,$0000
dp_bnds:        dc.w    $3c01,$43fe
               dc.w    $3bcd,$0000

       xref    mem_write
       xref    bindec
       xref    get_fline
       xref    round
       xref    denorm
       xref    dest_ext
       xref    dest_dbl
       xref    dest_sgl
       xref    unf_sub
       xref    nrm_set
       xref    dnrm_lp
       xref    ovf_res
       xref    reg_dest
       xref    t_ovfl
       xref    t_unfl

       xdef    res_func
       xdef    p_move

res_func:
       clr.b   DNRM_FLG(a6)
       clr.b   RES_FLG(a6)
       clr.b   CU_ONLY(a6)
       tst.b   DY_MO_FLG(a6)
       beq.b   monadic
dyadic:
       btst.b  #7,DTAG(a6)     ;if dop = norm=000, zero=001,
*                               ;inf=010 or nan=011
       beq.b   monadic         ;then branch
*                               ;else denorm
* HANDLE DESTINATION DENORM HERE
*                               ;set dtag to norm
*                               ;write the tag & fpte15 to the fstack
       lea.l   FPTEMP(a6),a0

       bclr.b  #sign_bit,LOCAL_EX(a0)
       sne     LOCAL_SGN(a0)

       bsr     nrm_set         ;normalize number (exp will go negative)
       bclr.b  #sign_bit,LOCAL_EX(a0) ;get rid of false sign
       bfclr   LOCAL_SGN(a0){0:8}      ;change back to IEEE ext format
       beq.b   dpos
       bset.b  #sign_bit,LOCAL_EX(a0)
dpos:
       bfclr   DTAG(a6){0:4}   ;set tag to normalized, FPTE15 = 0
       bset.b  #4,DTAG(a6)     ;set FPTE15
       or.b    #$0f,DNRM_FLG(a6)
monadic:
       lea.l   ETEMP(a6),a0
       btst.b  #direction_bit,CMDREG1B(a6)     ;check direction
       bne.w   opclass3                        ;it is a mv out
*
* At this point, only oplcass 0 and 2 possible
*
       btst.b  #7,STAG(a6)     ;if sop = norm=000, zero=001,
*                               ;inf=010 or nan=011
       bne.w   mon_dnrm        ;else denorm
       tst.b   DY_MO_FLG(a6)   ;all cases of dyadic instructions would
       bne.w   normal          ;require normalization of denorm

* At this point:
*       monadic instructions:   fabs  = $18  fneg   = $1a  ftst   = $3a
*                               fmove = $00  fsmove = $40  fdmove = $44
*                               fsqrt = $05* fssqrt = $41  fdsqrt = $45
*                               (*fsqrt reencoded to $05)
*
       move.w  CMDREG1B(a6),d0 ;get command register
       andi.l  #$7f,d0                 ;strip to only command word
*
* At this point, fabs, fneg, fsmove, fdmove, ftst, fsqrt, fssqrt, and
* fdsqrt are possible.
* For cases fabs, fneg, fsmove, and fdmove goto spos (do not normalize)
* For cases fsqrt, fssqrt, and fdsqrt goto nrm_src (do normalize)
*
       btst.l  #0,d0
       bne.w   normal                  ;weed out fsqrt instructions
*
* cu_norm handles fmove in instructions with normalized inputs.
* The routine round is used to correctly round the input for the
* destination precision and mode.
*
cu_norm:
       st      CU_ONLY(a6)             ;set cu-only inst flag
       move.w  CMDREG1B(a6),d0
       andi.b  #$3b,d0         ;isolate bits to select inst
       tst.b   d0
       beq.l   cu_nmove        ;if zero, it is an fmove
       cmpi.b  #$18,d0
       beq.l   cu_nabs         ;if $18, it is fabs
       cmpi.b  #$1a,d0
       beq.l   cu_nneg         ;if $1a, it is fneg
*
* Inst is ftst.  Check the source operand and set the cc's accordingly.
* No write is done, so simply rts.
*
cu_ntst:
       move.w  LOCAL_EX(a0),d0
       bclr.l  #15,d0
       sne     LOCAL_SGN(a0)
       beq.b   cu_ntpo
       or.l    #neg_mask,USER_FPSR(a6) ;set N
cu_ntpo:
       cmpi.w  #$7fff,d0       ;test for inf/nan
       bne.b   cu_ntcz
       tst.l   LOCAL_HI(a0)
       bne.b   cu_ntn
       tst.l   LOCAL_LO(a0)
       bne.b   cu_ntn
       or.l    #inf_mask,USER_FPSR(a6)
       rts
cu_ntn:
       or.l    #nan_mask,USER_FPSR(a6)
       move.l  ETEMP_EX(a6),FPTEMP_EX(a6)      ;set up fptemp sign for
*                                               ;snan handler

       rts
cu_ntcz:
       tst.l   LOCAL_HI(a0)
       bne.l   cu_ntsx
       tst.l   LOCAL_LO(a0)
       bne.l   cu_ntsx
       or.l    #z_mask,USER_FPSR(a6)
cu_ntsx:
       rts
*
* Inst is fabs.  Execute the absolute value function on the input.
* Branch to the fmove code.  If the operand is NaN, do nothing.
*
cu_nabs:
       move.b  STAG(a6),d0
       btst.l  #5,d0                   ;test for NaN or zero
       bne     wr_etemp                ;if either, simply write it
       bclr.b  #7,LOCAL_EX(a0)         ;do abs
       bra.b   cu_nmove                ;fmove code will finish
*
* Inst is fneg.  Execute the negate value function on the input.
* Fall though to the fmove code.  If the operand is NaN, do nothing.
*
cu_nneg:
       move.b  STAG(a6),d0
       btst.l  #5,d0                   ;test for NaN or zero
       bne     wr_etemp                ;if either, simply write it
       bchg.b  #7,LOCAL_EX(a0)         ;do neg
*
* Inst is fmove.  This code also handles all result writes.
* If bit 2 is set, round is forced to double.  If it is clear,
* and bit 6 is set, round is forced to single.  If both are clear,
* the round precision is found in the fpcr.  If the rounding precision
* is double or single, round the result before the write.
*
cu_nmove:
       move.b  STAG(a6),d0
       andi.b  #$e0,d0                 ;isolate stag bits
       bne     wr_etemp                ;if not norm, simply write it
       btst.b  #2,CMDREG1B+1(a6)       ;check for rd
       bne     cu_nmrd
       btst.b  #6,CMDREG1B+1(a6)       ;check for rs
       bne     cu_nmrs
*
* The move or operation is not with forced precision.  Test for
* nan or inf as the input; if so, simply write it to FPn.  Use the
* FPCR_MODE byte to get rounding on norms and zeros.
*
cu_nmnr:
       bfextu  FPCR_MODE(a6){0:2},d0
       tst.b   d0                      ;check for extended
       beq     cu_wrexn                ;if so, just write result
       cmpi.b  #1,d0                   ;check for single
       beq     cu_nmrs                 ;fall through to double
*
* The move is fdmove or round precision is double.
*
cu_nmrd:
       move.l  #2,d0                   ;set up the size for denorm
       move.w  LOCAL_EX(a0),d1         ;compare exponent to double threshold
       and.w   #$7fff,d1
       cmp.w   #$3c01,d1
       bls     cu_nunfl
       bfextu  FPCR_MODE(a6){2:2},d1   ;get rmode
       or.l    #$00020000,d1           ;or in rprec (double)
       clr.l   d0                      ;clear g,r,s for round
       bclr.b  #sign_bit,LOCAL_EX(a0)  ;convert to internal format
       sne     LOCAL_SGN(a0)
       bsr.l   round
       bfclr   LOCAL_SGN(a0){0:8}
       beq.b   cu_nmrdc
       bset.b  #sign_bit,LOCAL_EX(a0)
cu_nmrdc:
       move.w  LOCAL_EX(a0),d1         ;check for overflow
       and.w   #$7fff,d1
       cmp.w   #$43ff,d1
       bge     cu_novfl                ;take care of overflow case
       bra.w   cu_wrexn
*
* The move is fsmove or round precision is single.
*
cu_nmrs:
       move.l  #1,d0
       move.w  LOCAL_EX(a0),d1
       and.w   #$7fff,d1
       cmp.w   #$3f81,d1
       bls     cu_nunfl
       bfextu  FPCR_MODE(a6){2:2},d1
       or.l    #$00010000,d1
       clr.l   d0
       bclr.b  #sign_bit,LOCAL_EX(a0)
       sne     LOCAL_SGN(a0)
       bsr.l   round
       bfclr   LOCAL_SGN(a0){0:8}
       beq.b   cu_nmrsc
       bset.b  #sign_bit,LOCAL_EX(a0)
cu_nmrsc:
       move.w  LOCAL_EX(a0),d1
       and.w   #$7FFF,d1
       cmp.w   #$407f,d1
       blt     cu_wrexn
*
* The operand is above precision boundaries.  Use t_ovfl to
* generate the correct value.
*
cu_novfl:
       bsr     t_ovfl
       bra     cu_wrexn
*
* The operand is below precision boundaries.  Use denorm to
* generate the correct value.
*
cu_nunfl:
       bclr.b  #sign_bit,LOCAL_EX(a0)
       sne     LOCAL_SGN(a0)
       bsr     denorm
       bfclr   LOCAL_SGN(a0){0:8}      ;change back to IEEE ext format
       beq.b   cu_nucont
       bset.b  #sign_bit,LOCAL_EX(a0)
cu_nucont:
       bfextu  FPCR_MODE(a6){2:2},d1
       btst.b  #2,CMDREG1B+1(a6)       ;check for rd
       bne     inst_d
       btst.b  #6,CMDREG1B+1(a6)       ;check for rs
       bne     inst_s
       swap    d1
       move.b  FPCR_MODE(a6),d1
       lsr.b   #6,d1
       swap    d1
       bra     inst_sd
inst_d:
       or.l    #$00020000,d1
       bra     inst_sd
inst_s:
       or.l    #$00010000,d1
inst_sd:
       bclr.b  #sign_bit,LOCAL_EX(a0)
       sne     LOCAL_SGN(a0)
       bsr.l   round
       bfclr   LOCAL_SGN(a0){0:8}
       beq.b   cu_nuflp
       bset.b  #sign_bit,LOCAL_EX(a0)
cu_nuflp:
       btst.b  #inex2_bit,FPSR_EXCEPT(a6)
       beq.b   cu_nuninx
       or.l    #aunfl_mask,USER_FPSR(a6) ;if the round was inex, set AUNFL
cu_nuninx:
       tst.l   LOCAL_HI(a0)            ;test for zero
       bne.b   cu_nunzro
       tst.l   LOCAL_LO(a0)
       bne.b   cu_nunzro
*
* The mantissa is zero from the denorm loop.  Check sign and rmode
* to see if rounding should have occurred which would leave the lsb.
*
       move.l  USER_FPCR(a6),d0
       andi.l  #$30,d0         ;isolate rmode
       cmpi.l  #$20,d0
       blt.b   cu_nzro
       bne.b   cu_nrp
cu_nrm:
       tst.w   LOCAL_EX(a0)    ;if positive, set lsb
       bge.b   cu_nzro
       btst.b  #7,FPCR_MODE(a6) ;check for double
       beq.b   cu_nincs
       bra.b   cu_nincd
cu_nrp:
       tst.w   LOCAL_EX(a0)    ;if positive, set lsb
       blt.b   cu_nzro
       btst.b  #7,FPCR_MODE(a6) ;check for double
       beq.b   cu_nincs
cu_nincd:
       or.l    #$800,LOCAL_LO(a0) ;inc for double
       bra     cu_nunzro
cu_nincs:
       or.l    #$100,LOCAL_HI(a0) ;inc for single
       bra     cu_nunzro
cu_nzro:
       or.l    #z_mask,USER_FPSR(a6)
       move.b  STAG(a6),d0
       andi.b  #$e0,d0
       cmpi.b  #$40,d0         ;check if input was tagged zero
       beq.b   cu_numv
cu_nunzro:
       or.l    #unfl_mask,USER_FPSR(a6) ;set unfl
cu_numv:
       move.l  (a0),ETEMP(a6)
       move.l  4(a0),ETEMP_HI(a6)
       move.l  8(a0),ETEMP_LO(a6)
*
* Write the result to memory, setting the fpsr cc bits.  NaN and Inf
* bypass cu_wrexn.
*
cu_wrexn:
       tst.w   LOCAL_EX(a0)            ;test for zero
       beq.b   cu_wrzero
       cmp.w   #$8000,LOCAL_EX(a0)     ;test for zero
       bne.b   cu_wreon
cu_wrzero:
       or.l    #z_mask,USER_FPSR(a6)   ;set Z bit
cu_wreon:
       tst.w   LOCAL_EX(a0)
       bpl     wr_etemp
       or.l    #neg_mask,USER_FPSR(a6)
       bra     wr_etemp

*
* HANDLE SOURCE DENORM HERE
*
*                               ;clear denorm stag to norm
*                               ;write the new tag & ete15 to the fstack
mon_dnrm:
*
* At this point, check for the cases in which normalizing the
* denorm produces incorrect results.
*
       tst.b   DY_MO_FLG(a6)   ;all cases of dyadic instructions would
       bne.b   nrm_src         ;require normalization of denorm

* At this point:
*       monadic instructions:   fabs  = $18  fneg   = $1a  ftst   = $3a
*                               fmove = $00  fsmove = $40  fdmove = $44
*                               fsqrt = $05* fssqrt = $41  fdsqrt = $45
*                               (*fsqrt reencoded to $05)
*
       move.w  CMDREG1B(a6),d0 ;get command register
       andi.l  #$7f,d0                 ;strip to only command word
*
* At this point, fabs, fneg, fsmove, fdmove, ftst, fsqrt, fssqrt, and
* fdsqrt are possible.
* For cases fabs, fneg, fsmove, and fdmove goto spos (do not normalize)
* For cases fsqrt, fssqrt, and fdsqrt goto nrm_src (do normalize)
*
       btst.l  #0,d0
       bne.b   nrm_src         ;weed out fsqrt instructions
       st      CU_ONLY(a6)     ;set cu-only inst flag
       bra     cu_dnrm         ;fmove, fabs, fneg, ftst
*                               ;cases go to cu_dnrm
nrm_src:
       bclr.b  #sign_bit,LOCAL_EX(a0)
       sne     LOCAL_SGN(a0)
       bsr     nrm_set         ;normalize number (exponent will go
*                               ; negative)
       bclr.b  #sign_bit,LOCAL_EX(a0) ;get rid of false sign

       bfclr   LOCAL_SGN(a0){0:8}      ;change back to IEEE ext format
       beq.b   spos
       bset.b  #sign_bit,LOCAL_EX(a0)
spos:
       bfclr   STAG(a6){0:4}   ;set tag to normalized, FPTE15 = 0
       bset.b  #4,STAG(a6)     ;set ETE15
       or.b    #$f0,DNRM_FLG(a6)
normal:
       tst.b   DNRM_FLG(a6)    ;check if any of the ops were denorms
       bne     ck_wrap         ;if so, check if it is a potential
*                               ;wrap-around case
fix_stk:
       move.b  #$fe,CU_SAVEPC(a6)
       bclr.b  #E1,E_BYTE(a6)

       clr.w   NMNEXC(a6)

       st.b    RES_FLG(a6)     ;indicate that a restore is needed
       rts

*
* cu_dnrm handles all cu-only instructions (fmove, fabs, fneg, and
* ftst) completely in software without an frestore to the 040.
*
cu_dnrm:
       st.b    CU_ONLY(a6)
       move.w  CMDREG1B(a6),d0
       andi.b  #$3b,d0         ;isolate bits to select inst
       tst.b   d0
       beq.l   cu_dmove        ;if zero, it is an fmove
       cmpi.b  #$18,d0
       beq.l   cu_dabs         ;if $18, it is fabs
       cmpi.b  #$1a,d0
       beq.l   cu_dneg         ;if $1a, it is fneg
*
* Inst is ftst.  Check the source operand and set the cc's accordingly.
* No write is done, so simply rts.
*
cu_dtst:
       move.w  LOCAL_EX(a0),d0
       bclr.l  #15,d0
       sne     LOCAL_SGN(a0)
       beq.b   cu_dtpo
       or.l    #neg_mask,USER_FPSR(a6) ;set N
cu_dtpo:
       cmpi.w  #$7fff,d0       ;test for inf/nan
       bne.b   cu_dtcz
       tst.l   LOCAL_HI(a0)
       bne.b   cu_dtn
       tst.l   LOCAL_LO(a0)
       bne.b   cu_dtn
       or.l    #inf_mask,USER_FPSR(a6)
       rts
cu_dtn:
       or.l    #nan_mask,USER_FPSR(a6)
       move.l  ETEMP_EX(a6),FPTEMP_EX(a6)      ;set up fptemp sign for
*                                               ;snan handler
       rts
cu_dtcz:
       tst.l   LOCAL_HI(a0)
       bne.l   cu_dtsx
       tst.l   LOCAL_LO(a0)
       bne.l   cu_dtsx
       or.l    #z_mask,USER_FPSR(a6)
cu_dtsx:
       rts
*
* Inst is fabs.  Execute the absolute value function on the input.
* Branch to the fmove code.
*
cu_dabs:
       bclr.b  #7,LOCAL_EX(a0)         ;do abs
       bra.b   cu_dmove                ;fmove code will finish
*
* Inst is fneg.  Execute the negate value function on the input.
* Fall though to the fmove code.
*
cu_dneg:
       bchg.b  #7,LOCAL_EX(a0)         ;do neg
*
* Inst is fmove.  This code also handles all result writes.
* If bit 2 is set, round is forced to double.  If it is clear,
* and bit 6 is set, round is forced to single.  If both are clear,
* the round precision is found in the fpcr.  If the rounding precision
* is double or single, the result is zero, and the mode is checked
* to determine if the lsb of the result should be set.
*
cu_dmove:
       btst.b  #2,CMDREG1B+1(a6)       ;check for rd
       bne     cu_dmrd
       btst.b  #6,CMDREG1B+1(a6)       ;check for rs
       bne     cu_dmrs
*
* The move or operation is not with forced precision.  Use the
* FPCR_MODE byte to get rounding.
*
cu_dmnr:
       bfextu  FPCR_MODE(a6){0:2},d0
       tst.b   d0                      ;check for extended
       beq     cu_wrexd                ;if so, just write result
       cmpi.b  #1,d0                   ;check for single
       beq     cu_dmrs                 ;fall through to double
*
* The move is fdmove or round precision is double.  Result is zero.
* Check rmode for rp or rm and set lsb accordingly.
*
cu_dmrd:
       bfextu  FPCR_MODE(a6){2:2},d1   ;get rmode
       tst.w   LOCAL_EX(a0)            ;check sign
       blt.b   cu_dmdn
       cmpi.b  #3,d1                   ;check for rp
       bne     cu_dpd                  ;load double pos zero
       bra     cu_dpdr                 ;load double pos zero w/lsb
cu_dmdn:
       cmpi.b  #2,d1                   ;check for rm
       bne     cu_dnd                  ;load double neg zero
       bra     cu_dndr                 ;load double neg zero w/lsb
*
* The move is fsmove or round precision is single.  Result is zero.
* Check for rp or rm and set lsb accordingly.
*
cu_dmrs:
       bfextu  FPCR_MODE(a6){2:2},d1   ;get rmode
       tst.w   LOCAL_EX(a0)            ;check sign
       blt.b   cu_dmsn
       cmpi.b  #3,d1                   ;check for rp
       bne     cu_spd                  ;load single pos zero
       bra     cu_spdr                 ;load single pos zero w/lsb
cu_dmsn:
       cmpi.b  #2,d1                   ;check for rm
       bne     cu_snd                  ;load single neg zero
       bra     cu_sndr                 ;load single neg zero w/lsb
*
* The precision is extended, so the result in etemp is correct.
* Simply set unfl (not inex2 or aunfl) and write the result to
* the correct fp register.
cu_wrexd:
       or.l    #unfl_mask,USER_FPSR(a6)
       tst.w   LOCAL_EX(a0)
       beq     wr_etemp
       or.l    #neg_mask,USER_FPSR(a6)
       bra     wr_etemp
*
* These routines write +/- zero in double format.  The routines
* cu_dpdr and cu_dndr set the double lsb.
*
cu_dpd:
       move.l  #$3c010000,LOCAL_EX(a0) ;force pos double zero
       clr.l   LOCAL_HI(a0)
       clr.l   LOCAL_LO(a0)
       or.l    #z_mask,USER_FPSR(a6)
       or.l    #unfinx_mask,USER_FPSR(a6)
       bra     wr_etemp
cu_dpdr:
       move.l  #$3c010000,LOCAL_EX(a0) ;force pos double zero
       clr.l   LOCAL_HI(a0)
       move.l  #$800,LOCAL_LO(a0)      ;with lsb set
       or.l    #unfinx_mask,USER_FPSR(a6)
       bra     wr_etemp
cu_dnd:
       move.l  #$bc010000,LOCAL_EX(a0) ;force pos double zero
       clr.l   LOCAL_HI(a0)
       clr.l   LOCAL_LO(a0)
       or.l    #z_mask,USER_FPSR(a6)
       or.l    #neg_mask,USER_FPSR(a6)
       or.l    #unfinx_mask,USER_FPSR(a6)
       bra     wr_etemp
cu_dndr:
       move.l  #$bc010000,LOCAL_EX(a0) ;force pos double zero
       clr.l   LOCAL_HI(a0)
       move.l  #$800,LOCAL_LO(a0)      ;with lsb set
       or.l    #neg_mask,USER_FPSR(a6)
       or.l    #unfinx_mask,USER_FPSR(a6)
       bra     wr_etemp
*
* These routines write +/- zero in single format.  The routines
* cu_dpdr and cu_dndr set the single lsb.
*
cu_spd:
       move.l  #$3f810000,LOCAL_EX(a0) ;force pos single zero
       clr.l   LOCAL_HI(a0)
       clr.l   LOCAL_LO(a0)
       or.l    #z_mask,USER_FPSR(a6)
       or.l    #unfinx_mask,USER_FPSR(a6)
       bra     wr_etemp
cu_spdr:
       move.l  #$3f810000,LOCAL_EX(a0) ;force pos single zero
       move.l  #$100,LOCAL_HI(a0)      ;with lsb set
       clr.l   LOCAL_LO(a0)
       or.l    #unfinx_mask,USER_FPSR(a6)
       bra     wr_etemp
cu_snd:
       move.l  #$bf810000,LOCAL_EX(a0) ;force pos single zero
       clr.l   LOCAL_HI(a0)
       clr.l   LOCAL_LO(a0)
       or.l    #z_mask,USER_FPSR(a6)
       or.l    #neg_mask,USER_FPSR(a6)
       or.l    #unfinx_mask,USER_FPSR(a6)
       bra     wr_etemp
cu_sndr:
       move.l  #$bf810000,LOCAL_EX(a0) ;force pos single zero
       move.l  #$100,LOCAL_HI(a0)      ;with lsb set
       clr.l   LOCAL_LO(a0)
       or.l    #neg_mask,USER_FPSR(a6)
       or.l    #unfinx_mask,USER_FPSR(a6)
       bra     wr_etemp

*
* This code checks for 16-bit overflow conditions on dyadic
* operations which are not restorable into the floating-point
* unit and must be completed in software.  Basically, this
* condition exists with a very large norm and a denorm.  One
* of the operands must be denormalized to enter this code.
*
* Flags used:
*       DY_MO_FLG contains 0 for monadic op, $ff for dyadic
*       DNRM_FLG contains $00 for neither op denormalized
*                         $0f for the destination op denormalized
*                         $f0 for the source op denormalized
*                         $ff for both ops denormalzed
*
* The wrap-around condition occurs for add, sub, div, and cmp
* when
*
*       abs(dest_exp - src_exp) >= $8000
*
* and for mul when
*
*       (dest_exp + src_exp) < $0
*
* we must process the operation here if this case is true.
*
* The rts following the frcfpn routine is the exit from res_func
* for this condition.  The restore flag (RES_FLG) is left clear.
* No frestore is done unless an exception is to be reported.
*
* For fadd:
*       if(sign_of(dest) != sign_of(src))
*               replace exponent of src with $3fff (keep sign)
*               use fpu to perform dest+new_src (user's rmode and X)
*               clr sticky
*       else
*               set sticky
*       call round with user's precision and mode
*       move result to fpn and wbtemp
*
* For fsub:
*       if(sign_of(dest) == sign_of(src))
*               replace exponent of src with $3fff (keep sign)
*               use fpu to perform dest+new_src (user's rmode and X)
*               clr sticky
*       else
*               set sticky
*       call round with user's precision and mode
*       move result to fpn and wbtemp
*
* For fdiv/fsgldiv:
*       if(both operands are denorm)
*               restore_to_fpu;
*       if(dest is norm)
*               force_ovf;
*       else(dest is denorm)
*               force_unf:
*
* For fcmp:
*       if(dest is norm)
*               N = sign_of(dest);
*       else(dest is denorm)
*               N = sign_of(src);
*
* For fmul:
*       if(both operands are denorm)
*               force_unf;
*       if((dest_exp + src_exp) < 0)
*               force_unf:
*       else
*               restore_to_fpu;
*
* local equates:
addcode equ     $22
subcode equ     $28
mulcode equ     $23
divcode equ     $20
cmpcode equ     $38
ck_wrap:
       tst.b   DY_MO_FLG(a6)   ;check for fsqrt
       beq     fix_stk         ;if zero, it is fsqrt
       move.w  CMDREG1B(a6),d0
       andi.w  #$3b,d0         ;strip to command bits
       cmpi.w  #addcode,d0
       beq     wrap_add
       cmpi.w  #subcode,d0
       beq     wrap_sub
       cmpi.w  #mulcode,d0
       beq     wrap_mul
       cmpi.w  #cmpcode,d0
       beq     wrap_cmp
*
* Inst is fdiv.
*
wrap_div:
       cmp.b   #$ff,DNRM_FLG(a6) ;if both ops denorm,
       beq     fix_stk          ;restore to fpu
*
* One of the ops is denormalized.  Test for wrap condition
* and force the result.
*
       cmp.b   #$0f,DNRM_FLG(a6) ;check for dest denorm
       bne.b   div_srcd
div_destd:
       bsr.l   ckinf_ns
       bne     fix_stk
       bfextu  ETEMP_EX(a6){1:15},d0   ;get src exp (always pos)
       bfexts  FPTEMP_EX(a6){1:15},d1  ;get dest exp (always neg)
       sub.l   d1,d0                   ;subtract dest from src
       cmp.l   #$7fff,d0
       blt     fix_stk                 ;if less, not wrap case
       clr.b   WBTEMP_SGN(a6)
       move.w  ETEMP_EX(a6),d0         ;find the sign of the result
       move.w  FPTEMP_EX(a6),d1
       eor.w   d1,d0
       andi.w  #$8000,d0
       beq     force_unf
       st.b    WBTEMP_SGN(a6)
       bra     force_unf

ckinf_ns:
       move.b  STAG(a6),d0             ;check source tag for inf or nan
       bra     ck_in_com
ckinf_nd:
       move.b  DTAG(a6),d0             ;check destination tag for inf or nan
ck_in_com:
       andi.b  #$60,d0                 ;isolate tag bits
       cmp.b   #$40,d0                 ;is it inf?
       beq     nan_or_inf              ;not wrap case
       cmp.b   #$60,d0                 ;is it nan?
       beq     nan_or_inf              ;yes, not wrap case?
       cmp.b   #$20,d0                 ;is it a zero?
       beq     nan_or_inf              ;yes
       clr.l   d0
       rts                             ;then it is either a zero of norm,
*                                       ;check wrap case
nan_or_inf:
       moveq.l #-1,d0
       rts



div_srcd:
       bsr.l   ckinf_nd
       bne     fix_stk
       bfextu  FPTEMP_EX(a6){1:15},d0  ;get dest exp (always pos)
       bfexts  ETEMP_EX(a6){1:15},d1   ;get src exp (always neg)
       sub.l   d1,d0                   ;subtract src from dest
       cmp.l   #$8000,d0
       blt     fix_stk                 ;if less, not wrap case
       clr.b   WBTEMP_SGN(a6)
       move.w  ETEMP_EX(a6),d0         ;find the sign of the result
       move.w  FPTEMP_EX(a6),d1
       eor.w   d1,d0
       andi.w  #$8000,d0
       beq.b   force_ovf
       st.b    WBTEMP_SGN(a6)
*
* This code handles the case of the instruction resulting in
* an overflow condition.
*
force_ovf:
       bclr.b  #E1,E_BYTE(a6)
       or.l    #ovfl_inx_mask,USER_FPSR(a6)
       clr.w   NMNEXC(a6)
       lea.l   WBTEMP(a6),a0           ;point a0 to memory location
       move.w  CMDREG1B(a6),d0
       btst.l  #6,d0                   ;test for forced precision
       beq.b   frcovf_fpcr
       btst.l  #2,d0                   ;check for double
       bne.b   frcovf_dbl
       move.l  #$1,d0                  ;inst is forced single
       bra.b   frcovf_rnd
frcovf_dbl:
       move.l  #$2,d0                  ;inst is forced double
       bra.b   frcovf_rnd
frcovf_fpcr:
       bfextu  FPCR_MODE(a6){0:2},d0   ;inst not forced - use fpcr prec
frcovf_rnd:

* The 881/882 does not set inex2 for the following case, so the
* line is commented out to be compatible with 881/882
*       tst.b   d0
*       beq.b   frcovf_x
*       or.l    #inex2_mask,USER_FPSR(a6) ;if prec is s or d, set inex2

*frcovf_x:
       bsr.l   ovf_res                 ;get correct result based on
*                                       ;round precision/mode.  This
*                                       ;sets FPSR_CC correctly
*                                       ;returns in external format
       bfclr   WBTEMP_SGN(a6){0:8}
       beq     frcfpn
       bset.b  #sign_bit,WBTEMP_EX(a6)
       bra     frcfpn
*
* Inst is fadd.
*
wrap_add:
       cmp.b   #$ff,DNRM_FLG(a6) ;if both ops denorm,
       beq     fix_stk          ;restore to fpu
*
* One of the ops is denormalized.  Test for wrap condition
* and complete the instruction.
*
       cmp.b   #$0f,DNRM_FLG(a6) ;check for dest denorm
       bne.b   add_srcd
add_destd:
       bsr.l   ckinf_ns
       bne     fix_stk
       bfextu  ETEMP_EX(a6){1:15},d0   ;get src exp (always pos)
       bfexts  FPTEMP_EX(a6){1:15},d1  ;get dest exp (always neg)
       sub.l   d1,d0                   ;subtract dest from src
       cmp.l   #$8000,d0
       blt     fix_stk                 ;if less, not wrap case
       bra     add_wrap
add_srcd:
       bsr.l   ckinf_nd
       bne     fix_stk
       bfextu  FPTEMP_EX(a6){1:15},d0  ;get dest exp (always pos)
       bfexts  ETEMP_EX(a6){1:15},d1   ;get src exp (always neg)
       sub.l   d1,d0                   ;subtract src from dest
       cmp.l   #$8000,d0
       blt     fix_stk                 ;if less, not wrap case
*
* Check the signs of the operands.  If they are unlike, the fpu
* can be used to add the norm and 1.0 with the sign of the
* denorm and it will correctly generate the result in extended
* precision.  We can then call round with no sticky and the result
* will be correct for the user's rounding mode and precision.  If
* the signs are the same, we call round with the sticky bit set
* and the result will be correctfor the user's rounding mode and
* precision.
*
add_wrap:
       move.w  ETEMP_EX(a6),d0
       move.w  FPTEMP_EX(a6),d1
       eor.w   d1,d0
       andi.w  #$8000,d0
       beq     add_same
*
* The signs are unlike.
*
       cmp.b   #$0f,DNRM_FLG(a6) ;is dest the denorm?
       bne.b   add_u_srcd
       move.w  FPTEMP_EX(a6),d0
       andi.w  #$8000,d0
       or.w    #$3fff,d0       ;force the exponent to +/- 1
       move.w  d0,FPTEMP_EX(a6) ;in the denorm
       move.l  USER_FPCR(a6),d0
       andi.l  #$30,d0
       fmove.l d0,fpcr         ;set up users rmode and X
       fmove.x ETEMP(a6),fp0
       fadd.x  FPTEMP(a6),fp0
       lea.l   WBTEMP(a6),a0   ;point a0 to wbtemp in frame
       fmove.l fpsr,d1
       or.l    d1,USER_FPSR(a6) ;capture cc's and inex from fadd
       fmove.x fp0,WBTEMP(a6)  ;write result to memory
       lsr.l   #4,d0           ;put rmode in lower 2 bits
       move.l  USER_FPCR(a6),d1
       andi.l  #$c0,d1
       lsr.l   #6,d1           ;put precision in upper word
       swap    d1
       or.l    d0,d1           ;set up for round call
       clr.l   d0              ;force sticky to zero
       bclr.b  #sign_bit,WBTEMP_EX(a6)
       sne     WBTEMP_SGN(a6)
       bsr.l   round           ;round result to users rmode & prec
       bfclr   WBTEMP_SGN(a6){0:8}     ;convert back to IEEE ext format
       beq     frcfpnr
       bset.b  #sign_bit,WBTEMP_EX(a6)
       bra     frcfpnr
add_u_srcd:
       move.w  ETEMP_EX(a6),d0
       andi.w  #$8000,d0
       or.w    #$3fff,d0       ;force the exponent to +/- 1
       move.w  d0,ETEMP_EX(a6) ;in the denorm
       move.l  USER_FPCR(a6),d0
       andi.l  #$30,d0
       fmove.l d0,fpcr         ;set up users rmode and X
       fmove.x ETEMP(a6),fp0
       fadd.x  FPTEMP(a6),fp0
       fmove.l fpsr,d1
       or.l    d1,USER_FPSR(a6) ;capture cc's and inex from fadd
       lea.l   WBTEMP(a6),a0   ;point a0 to wbtemp in frame
       fmove.x fp0,WBTEMP(a6)  ;write result to memory
       lsr.l   #4,d0           ;put rmode in lower 2 bits
       move.l  USER_FPCR(a6),d1
       andi.l  #$c0,d1
       lsr.l   #6,d1           ;put precision in upper word
       swap    d1
       or.l    d0,d1           ;set up for round call
       clr.l   d0              ;force sticky to zero
       bclr.b  #sign_bit,WBTEMP_EX(a6)
       sne     WBTEMP_SGN(a6)  ;use internal format for round
       bsr.l   round           ;round result to users rmode & prec
       bfclr   WBTEMP_SGN(a6){0:8}     ;convert back to IEEE ext format
       beq     frcfpnr
       bset.b  #sign_bit,WBTEMP_EX(a6)
       bra     frcfpnr
*
* Signs are alike:
*
add_same:
       cmp.b   #$0f,DNRM_FLG(a6) ;is dest the denorm?
       bne.b   add_s_srcd
add_s_destd:
       lea.l   ETEMP(a6),a0
       move.l  USER_FPCR(a6),d0
       andi.l  #$30,d0
       lsr.l   #4,d0           ;put rmode in lower 2 bits
       move.l  USER_FPCR(a6),d1
       andi.l  #$c0,d1
       lsr.l   #6,d1           ;put precision in upper word
       swap    d1
       or.l    d0,d1           ;set up for round call
       move.l  #$20000000,d0   ;set sticky for round
       bclr.b  #sign_bit,ETEMP_EX(a6)
       sne     ETEMP_SGN(a6)
       bsr.l   round           ;round result to users rmode & prec
       bfclr   ETEMP_SGN(a6){0:8}      ;convert back to IEEE ext format
       beq.b   add_s_dclr
       bset.b  #sign_bit,ETEMP_EX(a6)
add_s_dclr:
       lea.l   WBTEMP(a6),a0
       move.l  ETEMP(a6),(a0)  ;write result to wbtemp
       move.l  ETEMP_HI(a6),4(a0)
       move.l  ETEMP_LO(a6),8(a0)
       tst.w   ETEMP_EX(a6)
       bgt     add_ckovf
       or.l    #neg_mask,USER_FPSR(a6)
       bra     add_ckovf
add_s_srcd:
       lea.l   FPTEMP(a6),a0
       move.l  USER_FPCR(a6),d0
       andi.l  #$30,d0
       lsr.l   #4,d0           ;put rmode in lower 2 bits
       move.l  USER_FPCR(a6),d1
       andi.l  #$c0,d1
       lsr.l   #6,d1           ;put precision in upper word
       swap    d1
       or.l    d0,d1           ;set up for round call
       move.l  #$20000000,d0   ;set sticky for round
       bclr.b  #sign_bit,FPTEMP_EX(a6)
       sne     FPTEMP_SGN(a6)
       bsr.l   round           ;round result to users rmode & prec
       bfclr   FPTEMP_SGN(a6){0:8}     ;convert back to IEEE ext format
       beq.b   add_s_sclr
       bset.b  #sign_bit,FPTEMP_EX(a6)
add_s_sclr:
       lea.l   WBTEMP(a6),a0
       move.l  FPTEMP(a6),(a0) ;write result to wbtemp
       move.l  FPTEMP_HI(a6),4(a0)
       move.l  FPTEMP_LO(a6),8(a0)
       tst.w   FPTEMP_EX(a6)
       bgt     add_ckovf
       or.l    #neg_mask,USER_FPSR(a6)
add_ckovf:
       move.w  WBTEMP_EX(a6),d0
       andi.w  #$7fff,d0
       cmpi.w  #$7fff,d0
       bne     frcfpnr
*
* The result has overflowed to $7fff exponent.  Set I, ovfl,
* and aovfl, and clr the mantissa (incorrectly set by the
* round routine.)
*
       or.l    #inf_mask+ovfl_inx_mask,USER_FPSR(a6)
       clr.l   4(a0)
       bra     frcfpnr
*
* Inst is fsub.
*
wrap_sub:
       cmp.b   #$ff,DNRM_FLG(a6) ;if both ops denorm,
       beq     fix_stk          ;restore to fpu
*
* One of the ops is denormalized.  Test for wrap condition
* and complete the instruction.
*
       cmp.b   #$0f,DNRM_FLG(a6) ;check for dest denorm
       bne.b   sub_srcd
sub_destd:
       bsr.l   ckinf_ns
       bne     fix_stk
       bfextu  ETEMP_EX(a6){1:15},d0   ;get src exp (always pos)
       bfexts  FPTEMP_EX(a6){1:15},d1  ;get dest exp (always neg)
       sub.l   d1,d0                   ;subtract src from dest
       cmp.l   #$8000,d0
       blt     fix_stk                 ;if less, not wrap case
       bra     sub_wrap
sub_srcd:
       bsr.l   ckinf_nd
       bne     fix_stk
       bfextu  FPTEMP_EX(a6){1:15},d0  ;get dest exp (always pos)
       bfexts  ETEMP_EX(a6){1:15},d1   ;get src exp (always neg)
       sub.l   d1,d0                   ;subtract dest from src
       cmp.l   #$8000,d0
       blt     fix_stk                 ;if less, not wrap case
*
* Check the signs of the operands.  If they are alike, the fpu
* can be used to subtract from the norm 1.0 with the sign of the
* denorm and it will correctly generate the result in extended
* precision.  We can then call round with no sticky and the result
* will be correct for the user's rounding mode and precision.  If
* the signs are unlike, we call round with the sticky bit set
* and the result will be correctfor the user's rounding mode and
* precision.
*
sub_wrap:
       move.w  ETEMP_EX(a6),d0
       move.w  FPTEMP_EX(a6),d1
       eor.w   d1,d0
       andi.w  #$8000,d0
       bne     sub_diff
*
* The signs are alike.
*
       cmp.b   #$0f,DNRM_FLG(a6) ;is dest the denorm?
       bne.b   sub_u_srcd
       move.w  FPTEMP_EX(a6),d0
       andi.w  #$8000,d0
       or.w    #$3fff,d0       ;force the exponent to +/- 1
       move.w  d0,FPTEMP_EX(a6) ;in the denorm
       move.l  USER_FPCR(a6),d0
       andi.l  #$30,d0
       fmove.l d0,fpcr         ;set up users rmode and X
       fmove.x FPTEMP(a6),fp0
       fsub.x  ETEMP(a6),fp0
       fmove.l fpsr,d1
       or.l    d1,USER_FPSR(a6) ;capture cc's and inex from fadd
       lea.l   WBTEMP(a6),a0   ;point a0 to wbtemp in frame
       fmove.x fp0,WBTEMP(a6)  ;write result to memory
       lsr.l   #4,d0           ;put rmode in lower 2 bits
       move.l  USER_FPCR(a6),d1
       andi.l  #$c0,d1
       lsr.l   #6,d1           ;put precision in upper word
       swap    d1
       or.l    d0,d1           ;set up for round call
       clr.l   d0              ;force sticky to zero
       bclr.b  #sign_bit,WBTEMP_EX(a6)
       sne     WBTEMP_SGN(a6)
       bsr.l   round           ;round result to users rmode & prec
       bfclr   WBTEMP_SGN(a6){0:8}     ;convert back to IEEE ext format
       beq     frcfpnr
       bset.b  #sign_bit,WBTEMP_EX(a6)
       bra     frcfpnr
sub_u_srcd:
       move.w  ETEMP_EX(a6),d0
       andi.w  #$8000,d0
       or.w    #$3fff,d0       ;force the exponent to +/- 1
       move.w  d0,ETEMP_EX(a6) ;in the denorm
       move.l  USER_FPCR(a6),d0
       andi.l  #$30,d0
       fmove.l d0,fpcr         ;set up users rmode and X
       fmove.x FPTEMP(a6),fp0
       fsub.x  ETEMP(a6),fp0
       fmove.l fpsr,d1
       or.l    d1,USER_FPSR(a6) ;capture cc's and inex from fadd
       lea.l   WBTEMP(a6),a0   ;point a0 to wbtemp in frame
       fmove.x fp0,WBTEMP(a6)  ;write result to memory
       lsr.l   #4,d0           ;put rmode in lower 2 bits
       move.l  USER_FPCR(a6),d1
       andi.l  #$c0,d1
       lsr.l   #6,d1           ;put precision in upper word
       swap    d1
       or.l    d0,d1           ;set up for round call
       clr.l   d0              ;force sticky to zero
       bclr.b  #sign_bit,WBTEMP_EX(a6)
       sne     WBTEMP_SGN(a6)
       bsr.l   round           ;round result to users rmode & prec
       bfclr   WBTEMP_SGN(a6){0:8}     ;convert back to IEEE ext format
       beq     frcfpnr
       bset.b  #sign_bit,WBTEMP_EX(a6)
       bra     frcfpnr
*
* Signs are unlike:
*
sub_diff:
       cmp.b   #$0f,DNRM_FLG(a6) ;is dest the denorm?
       bne.b   sub_s_srcd
sub_s_destd:
       lea.l   ETEMP(a6),a0
       move.l  USER_FPCR(a6),d0
       andi.l  #$30,d0
       lsr.l   #4,d0           ;put rmode in lower 2 bits
       move.l  USER_FPCR(a6),d1
       andi.l  #$c0,d1
       lsr.l   #6,d1           ;put precision in upper word
       swap    d1
       or.l    d0,d1           ;set up for round call
       move.l  #$20000000,d0   ;set sticky for round
*
* Since the dest is the denorm, the sign is the opposite of the
* norm sign.
*
       eori.w  #$8000,ETEMP_EX(a6)     ;flip sign on result
       tst.w   ETEMP_EX(a6)
       bgt.b   sub_s_dwr
       or.l    #neg_mask,USER_FPSR(a6)
sub_s_dwr:
       bclr.b  #sign_bit,ETEMP_EX(a6)
       sne     ETEMP_SGN(a6)
       bsr.l   round           ;round result to users rmode & prec
       bfclr   ETEMP_SGN(a6){0:8}      ;convert back to IEEE ext format
       beq.b   sub_s_dclr
       bset.b  #sign_bit,ETEMP_EX(a6)
sub_s_dclr:
       lea.l   WBTEMP(a6),a0
       move.l  ETEMP(a6),(a0)  ;write result to wbtemp
       move.l  ETEMP_HI(a6),4(a0)
       move.l  ETEMP_LO(a6),8(a0)
       bra     sub_ckovf
sub_s_srcd:
       lea.l   FPTEMP(a6),a0
       move.l  USER_FPCR(a6),d0
       andi.l  #$30,d0
       lsr.l   #4,d0           ;put rmode in lower 2 bits
       move.l  USER_FPCR(a6),d1
       andi.l  #$c0,d1
       lsr.l   #6,d1           ;put precision in upper word
       swap    d1
       or.l    d0,d1           ;set up for round call
       move.l  #$20000000,d0   ;set sticky for round
       bclr.b  #sign_bit,FPTEMP_EX(a6)
       sne     FPTEMP_SGN(a6)
       bsr.l   round           ;round result to users rmode & prec
       bfclr   FPTEMP_SGN(a6){0:8}     ;convert back to IEEE ext format
       beq.b   sub_s_sclr
       bset.b  #sign_bit,FPTEMP_EX(a6)
sub_s_sclr:
       lea.l   WBTEMP(a6),a0
       move.l  FPTEMP(a6),(a0) ;write result to wbtemp
       move.l  FPTEMP_HI(a6),4(a0)
       move.l  FPTEMP_LO(a6),8(a0)
       tst.w   FPTEMP_EX(a6)
       bgt     sub_ckovf
       or.l    #neg_mask,USER_FPSR(a6)
sub_ckovf:
       move.w  WBTEMP_EX(a6),d0
       andi.w  #$7fff,d0
       cmpi.w  #$7fff,d0
       bne     frcfpnr
*
* The result has overflowed to $7fff exponent.  Set I, ovfl,
* and aovfl, and clr the mantissa (incorrectly set by the
* round routine.)
*
       or.l    #inf_mask+ovfl_inx_mask,USER_FPSR(a6)
       clr.l   4(a0)
       bra     frcfpnr
*
* Inst is fcmp.
*
wrap_cmp:
       cmp.b   #$ff,DNRM_FLG(a6) ;if both ops denorm,
       beq     fix_stk          ;restore to fpu
*
* One of the ops is denormalized.  Test for wrap condition
* and complete the instruction.
*
       cmp.b   #$0f,DNRM_FLG(a6) ;check for dest denorm
       bne.b   cmp_srcd
cmp_destd:
       bsr.l   ckinf_ns
       bne     fix_stk
       bfextu  ETEMP_EX(a6){1:15},d0   ;get src exp (always pos)
       bfexts  FPTEMP_EX(a6){1:15},d1  ;get dest exp (always neg)
       sub.l   d1,d0                   ;subtract dest from src
       cmp.l   #$8000,d0
       blt     fix_stk                 ;if less, not wrap case
       tst.w   ETEMP_EX(a6)            ;set N to ~sign_of(src)
       bge     cmp_setn
       rts
cmp_srcd:
       bsr.l   ckinf_nd
       bne     fix_stk
       bfextu  FPTEMP_EX(a6){1:15},d0  ;get dest exp (always pos)
       bfexts  ETEMP_EX(a6){1:15},d1   ;get src exp (always neg)
       sub.l   d1,d0                   ;subtract src from dest
       cmp.l   #$8000,d0
       blt     fix_stk                 ;if less, not wrap case
       tst.w   FPTEMP_EX(a6)           ;set N to sign_of(dest)
       blt     cmp_setn
       rts
cmp_setn:
       or.l    #neg_mask,USER_FPSR(a6)
       rts

*
* Inst is fmul.
*
wrap_mul:
       cmp.b   #$ff,DNRM_FLG(a6) ;if both ops denorm,
       beq     force_unf       ;force an underflow (really!)
*
* One of the ops is denormalized.  Test for wrap condition
* and complete the instruction.
*
       cmp.b   #$0f,DNRM_FLG(a6) ;check for dest denorm
       bne.b   mul_srcd
mul_destd:
       bsr.l   ckinf_ns
       bne     fix_stk
       bfextu  ETEMP_EX(a6){1:15},d0   ;get src exp (always pos)
       bfexts  FPTEMP_EX(a6){1:15},d1  ;get dest exp (always neg)
       add.l   d1,d0                   ;subtract dest from src
       bgt     fix_stk
       bra     force_unf
mul_srcd:
       bsr.l   ckinf_nd
       bne     fix_stk
       bfextu  FPTEMP_EX(a6){1:15},d0  ;get dest exp (always pos)
       bfexts  ETEMP_EX(a6){1:15},d1   ;get src exp (always neg)
       add.l   d1,d0                   ;subtract src from dest
       bgt     fix_stk

*
* This code handles the case of the instruction resulting in
* an underflow condition.
*
force_unf:
       bclr.b  #E1,E_BYTE(a6)
       or.l    #unfinx_mask,USER_FPSR(a6)
       clr.w   NMNEXC(a6)
       clr.b   WBTEMP_SGN(a6)
       move.w  ETEMP_EX(a6),d0         ;find the sign of the result
       move.w  FPTEMP_EX(a6),d1
       eor.w   d1,d0
       andi.w  #$8000,d0
       beq.b   frcunfcont
       st.b    WBTEMP_SGN(a6)
frcunfcont:
       lea     WBTEMP(a6),a0           ;point a0 to memory location
       move.w  CMDREG1B(a6),d0
       btst.l  #6,d0                   ;test for forced precision
       beq.b   frcunf_fpcr
       btst.l  #2,d0                   ;check for double
       bne.b   frcunf_dbl
       move.l  #$1,d0                  ;inst is forced single
       bra.b   frcunf_rnd
frcunf_dbl:
       move.l  #$2,d0                  ;inst is forced double
       bra.b   frcunf_rnd
frcunf_fpcr:
       bfextu  FPCR_MODE(a6){0:2},d0   ;inst not forced - use fpcr prec
frcunf_rnd:
       bsr.l   unf_sub                 ;get correct result based on
*                                       ;round precision/mode.  This
*                                       ;sets FPSR_CC correctly
       bfclr   WBTEMP_SGN(a6){0:8}     ;convert back to IEEE ext format
       beq.b   frcfpn
       bset.b  #sign_bit,WBTEMP_EX(a6)
       bra     frcfpn

*
* Write the result to the user's fpn.  All results must be HUGE to be
* written; otherwise the results would have overflowed or underflowed.
* If the rounding precision is single or double, the ovf_res routine
* is needed to correctly supply the max value.
*
frcfpnr:
       move.w  CMDREG1B(a6),d0
       btst.l  #6,d0                   ;test for forced precision
       beq.b   frcfpn_fpcr
       btst.l  #2,d0                   ;check for double
       bne.b   frcfpn_dbl
       move.l  #$1,d0                  ;inst is forced single
       bra.b   frcfpn_rnd
frcfpn_dbl:
       move.l  #$2,d0                  ;inst is forced double
       bra.b   frcfpn_rnd
frcfpn_fpcr:
       bfextu  FPCR_MODE(a6){0:2},d0   ;inst not forced - use fpcr prec
       tst.b   d0
       beq.b   frcfpn                  ;if extended, write what you got
frcfpn_rnd:
       bclr.b  #sign_bit,WBTEMP_EX(a6)
       sne     WBTEMP_SGN(a6)
       bsr.l   ovf_res                 ;get correct result based on
*                                       ;round precision/mode.  This
*                                       ;sets FPSR_CC correctly
       bfclr   WBTEMP_SGN(a6){0:8}     ;convert back to IEEE ext format
       beq.b   frcfpn_clr
       bset.b  #sign_bit,WBTEMP_EX(a6)
frcfpn_clr:
       or.l    #ovfinx_mask,USER_FPSR(a6)
*
* Perform the write.
*
frcfpn:
       bfextu  CMDREG1B(a6){6:3},d0    ;extract fp destination register
       cmpi.b  #3,d0
       ble.b   frc0123                 ;check if dest is fp0-fp3
       move.l  #7,d1
       sub.l   d0,d1
       clr.l   d0
       bset.l  d1,d0
       fmovem.x WBTEMP(a6),d0
       rts
frc0123:
       tst.b   d0
       beq.b   frc0_dst
       cmpi.b  #1,d0
       beq.b   frc1_dst
       cmpi.b  #2,d0
       beq.b   frc2_dst
frc3_dst:
       move.l  WBTEMP_EX(a6),USER_FP3(a6)
       move.l  WBTEMP_HI(a6),USER_FP3+4(a6)
       move.l  WBTEMP_LO(a6),USER_FP3+8(a6)
       rts
frc2_dst:
       move.l  WBTEMP_EX(a6),USER_FP2(a6)
       move.l  WBTEMP_HI(a6),USER_FP2+4(a6)
       move.l  WBTEMP_LO(a6),USER_FP2+8(a6)
       rts
frc1_dst:
       move.l  WBTEMP_EX(a6),USER_FP1(a6)
       move.l  WBTEMP_HI(a6),USER_FP1+4(a6)
       move.l  WBTEMP_LO(a6),USER_FP1+8(a6)
       rts
frc0_dst:
       move.l  WBTEMP_EX(a6),USER_FP0(a6)
       move.l  WBTEMP_HI(a6),USER_FP0+4(a6)
       move.l  WBTEMP_LO(a6),USER_FP0+8(a6)
       rts

*
* Write etemp to fpn.
* A check is made on enabled and signalled snan exceptions,
* and the destination is not overwritten if this condition exists.
* This code is designed to make fmoveins of unsupported data types
* faster.
*
wr_etemp:
       btst.b  #snan_bit,FPSR_EXCEPT(a6)       ;if snan is set, and
       beq.b   fmoveinc                ;enabled, force restore
       btst.b  #snan_bit,FPCR_ENABLE(a6) ;and don't overwrite
       beq.b   fmoveinc                ;the dest
       move.l  ETEMP_EX(a6),FPTEMP_EX(a6)      ;set up fptemp sign for
*                                               ;snan handler
       tst.b   ETEMP(a6)               ;check for negative
       blt.b   snan_neg
       rts
snan_neg:
       or.l    #neg_bit,USER_FPSR(a6)  ;snan is negative; set N
       rts
fmoveinc:
       clr.w   NMNEXC(a6)
       bclr.b  #E1,E_BYTE(a6)
       move.b  STAG(a6),d0             ;check if stag is inf
       andi.b  #$e0,d0
       cmpi.b  #$40,d0
       bne.b   fminc_cnan
       or.l    #inf_mask,USER_FPSR(a6) ;if inf, nothing yet has set I
       tst.w   LOCAL_EX(a0)            ;check sign
       bge.b   fminc_con
       or.l    #neg_mask,USER_FPSR(a6)
       bra     fminc_con
fminc_cnan:
       cmpi.b  #$60,d0                 ;check if stag is NaN
       bne.b   fminc_czero
       or.l    #nan_mask,USER_FPSR(a6) ;if nan, nothing yet has set NaN
       move.l  ETEMP_EX(a6),FPTEMP_EX(a6)      ;set up fptemp sign for
*                                               ;snan handler
       tst.w   LOCAL_EX(a0)            ;check sign
       bge.b   fminc_con
       or.l    #neg_mask,USER_FPSR(a6)
       bra     fminc_con
fminc_czero:
       cmpi.b  #$20,d0                 ;check if zero
       bne.b   fminc_con
       or.l    #z_mask,USER_FPSR(a6)   ;if zero, set Z
       tst.w   LOCAL_EX(a0)            ;check sign
       bge.b   fminc_con
       or.l    #neg_mask,USER_FPSR(a6)
fminc_con:
       bfextu  CMDREG1B(a6){6:3},d0    ;extract fp destination register
       cmpi.b  #3,d0
       ble.b   fp0123                  ;check if dest is fp0-fp3
       move.l  #7,d1
       sub.l   d0,d1
       clr.l   d0
       bset.l  d1,d0
       fmovem.x ETEMP(a6),d0
       rts

fp0123:
       tst.b   d0
       beq.b   fp0_dst
       cmpi.b  #1,d0
       beq.b   fp1_dst
       cmpi.b  #2,d0
       beq.b   fp2_dst
fp3_dst:
       move.l  ETEMP_EX(a6),USER_FP3(a6)
       move.l  ETEMP_HI(a6),USER_FP3+4(a6)
       move.l  ETEMP_LO(a6),USER_FP3+8(a6)
       rts
fp2_dst:
       move.l  ETEMP_EX(a6),USER_FP2(a6)
       move.l  ETEMP_HI(a6),USER_FP2+4(a6)
       move.l  ETEMP_LO(a6),USER_FP2+8(a6)
       rts
fp1_dst:
       move.l  ETEMP_EX(a6),USER_FP1(a6)
       move.l  ETEMP_HI(a6),USER_FP1+4(a6)
       move.l  ETEMP_LO(a6),USER_FP1+8(a6)
       rts
fp0_dst:
       move.l  ETEMP_EX(a6),USER_FP0(a6)
       move.l  ETEMP_HI(a6),USER_FP0+4(a6)
       move.l  ETEMP_LO(a6),USER_FP0+8(a6)
       rts

opclass3:
       st.b    CU_ONLY(a6)
       move.w  CMDREG1B(a6),d0 ;check if packed moveout
       andi.w  #$0c00,d0       ;isolate last 2 bits of size field
       cmpi.w  #$0c00,d0       ;if size is 011 or 111, it is packed
       beq.w   pack_out        ;else it is norm or denorm
       bra.w   mv_out


*
*       MOVE OUT
*

mv_tbl:
       dc.l    li
       dc.l    sgp
       dc.l    xp
       dc.l    mvout_end       ;should never be taken
       dc.l    wi
       dc.l    dp
       dc.l    bi
       dc.l    mvout_end       ;should never be taken
mv_out:
       bfextu  CMDREG1B(a6){3:3},d1    ;put source specifier in d1
       lea.l   mv_tbl,a0
       move.l  (a0,d1*4),a0
       jmp     (a0)

*
* This exit is for move-out to memory.  The aunfl bit is
* set if the result is inex and unfl is signalled.
*
mvout_end:
       btst.b  #inex2_bit,FPSR_EXCEPT(a6)
       beq.b   no_aufl
       btst.b  #unfl_bit,FPSR_EXCEPT(a6)
       beq.b   no_aufl
       bset.b  #aunfl_bit,FPSR_AEXCEPT(a6)
no_aufl:
       clr.w   NMNEXC(a6)
       bclr.b  #E1,E_BYTE(a6)
       fmove.l #0,FPSR                 ;clear any cc bits from res_func
*
* Return ETEMP to extended format from internal extended format so
* that gen_except will have a correctly signed value for ovfl/unfl
* handlers.
*
       bfclr   ETEMP_SGN(a6){0:8}
       beq.b   mvout_con
       bset.b  #sign_bit,ETEMP_EX(a6)
mvout_con:
       rts
*
* This exit is for move-out to int register.  The aunfl bit is
* not set in any case for this move.
*
mvouti_end:
       clr.w   NMNEXC(a6)
       bclr.b  #E1,E_BYTE(a6)
       fmove.l #0,FPSR                 ;clear any cc bits from res_func
*
* Return ETEMP to extended format from internal extended format so
* that gen_except will have a correctly signed value for ovfl/unfl
* handlers.
*
       bfclr   ETEMP_SGN(a6){0:8}
       beq.b   mvouti_con
       bset.b  #sign_bit,ETEMP_EX(a6)
mvouti_con:
       rts
*
* li is used to handle a long integer source specifier
*

li:
       moveq.l #4,d0           ;set byte count

       btst.b  #7,STAG(a6)     ;check for extended denorm
       bne.w   int_dnrm        ;if so, branch

       fmovem.x ETEMP(a6),fp0
       fcmp.d  #:41dfffffffc00000,fp0
* 41dfffffffc00000 in dbl prec = 401d0000fffffffe00000000 in ext prec
       fbge.w  lo_plrg
       fcmp.d  #:c1e0000000000000,fp0
* c1e0000000000000 in dbl prec = c01e00008000000000000000 in ext prec
       fble.w  lo_nlrg
*
* at this point, the answer is between the largest pos and neg values
*
       move.l  USER_FPCR(a6),d1        ;use user's rounding mode
       andi.l  #$30,d1
       fmove.l d1,fpcr
       fmove.l fp0,L_SCR1(a6)  ;let the 040 perform conversion
       fmove.l fpsr,d1
       or.l    d1,USER_FPSR(a6)        ;capture inex2/ainex if set
       bra.w   int_wrt


lo_plrg:
       move.l  #$7fffffff,L_SCR1(a6)   ;answer is largest positive int
       fbeq.w  int_wrt                 ;exact answer
       fcmp.d  #:41dfffffffe00000,fp0
* 41dfffffffe00000 in dbl prec = 401d0000ffffffff00000000 in ext prec
       fbge.w  int_operr               ;set operr
       bra.w   int_inx                 ;set inexact

lo_nlrg:
       move.l  #$80000000,L_SCR1(a6)
       fbeq.w  int_wrt                 ;exact answer
       fcmp.d  #:c1e0000000100000,fp0
* c1e0000000100000 in dbl prec = c01e00008000000080000000 in ext prec
       fblt.w  int_operr               ;set operr
       bra.w   int_inx                 ;set inexact

*
* wi is used to handle a word integer source specifier
*

wi:
       moveq.l #2,d0           ;set byte count

       btst.b  #7,STAG(a6)     ;check for extended denorm
       bne.w   int_dnrm        ;branch if so

       fmovem.x ETEMP(a6),fp0
       fcmp.s  #:46fffe00,fp0
* 46fffe00 in sgl prec = 400d0000fffe000000000000 in ext prec
       fbge.w  wo_plrg
       fcmp.s  #:c7000000,fp0
* c7000000 in sgl prec = c00e00008000000000000000 in ext prec
       fble.w  wo_nlrg

*
* at this point, the answer is between the largest pos and neg values
*
       move.l  USER_FPCR(a6),d1        ;use user's rounding mode
       andi.l  #$30,d1
       fmove.l d1,fpcr
       fmove.w fp0,L_SCR1(a6)  ;let the 040 perform conversion
       fmove.l fpsr,d1
       or.l    d1,USER_FPSR(a6)        ;capture inex2/ainex if set
       bra.w   int_wrt

wo_plrg:
       move.w  #$7fff,L_SCR1(a6)       ;answer is largest positive int
       fbeq.w  int_wrt                 ;exact answer
       fcmp.s  #:46ffff00,fp0
* 46ffff00 in sgl prec = 400d0000ffff000000000000 in ext prec
       fbge.w  int_operr               ;set operr
       bra.w   int_inx                 ;set inexact

wo_nlrg:
       move.w  #$8000,L_SCR1(a6)
       fbeq.w  int_wrt                 ;exact answer
       fcmp.s  #:c7000080,fp0
* c7000080 in sgl prec = c00e00008000800000000000 in ext prec
       fblt.w  int_operr               ;set operr
       bra.w   int_inx                 ;set inexact

*
* bi is used to handle a byte integer source specifier
*

bi:
       moveq.l #1,d0           ;set byte count

       btst.b  #7,STAG(a6)     ;check for extended denorm
       bne.w   int_dnrm        ;branch if so

       fmovem.x ETEMP(a6),fp0
       fcmp.s  #:42fe0000,fp0
* 42fe0000 in sgl prec = 40050000fe00000000000000 in ext prec
       fbge.w  by_plrg
       fcmp.s  #:c3000000,fp0
* c3000000 in sgl prec = c00600008000000000000000 in ext prec
       fble.w  by_nlrg

*
* at this point, the answer is between the largest pos and neg values
*
       move.l  USER_FPCR(a6),d1        ;use user's rounding mode
       andi.l  #$30,d1
       fmove.l d1,fpcr
       fmove.b fp0,L_SCR1(a6)  ;let the 040 perform conversion
       fmove.l fpsr,d1
       or.l    d1,USER_FPSR(a6)        ;capture inex2/ainex if set
       bra.w   int_wrt

by_plrg:
       move.b  #$7f,L_SCR1(a6)         ;answer is largest positive int
       fbeq.w  int_wrt                 ;exact answer
       fcmp.s  #:42ff0000,fp0
* 42ff0000 in sgl prec = 40050000ff00000000000000 in ext prec
       fbge.w  int_operr               ;set operr
       bra.w   int_inx                 ;set inexact

by_nlrg:
       move.b  #$80,L_SCR1(a6)
       fbeq.w  int_wrt                 ;exact answer
       fcmp.s  #:c3008000,fp0
* c3008000 in sgl prec = c00600008080000000000000 in ext prec
       fblt.w  int_operr               ;set operr
       bra.w   int_inx                 ;set inexact

*
* Common integer routines
*
* int_drnrm---account for possible nonzero result for round up with positive
* operand and round down for negative answer.  In the first case (result = 1)
* byte-width (store in d0) of result must be honored.  In the second case,
* -1 in L_SCR1(a6) will cover all contingencies (FMOVE.B/W/L out).

int_dnrm:
       clr.l   L_SCR1(a6)      ; initialize result to 0
       bfextu  FPCR_MODE(a6){2:2},d1   ; d1 is the rounding mode
       cmp.b   #2,d1
       bmi.b   int_inx         ; if RN or RZ, done
       bne.b   int_rp          ; if RP, continue below
       tst.w   ETEMP(a6)       ; RM: store -1 in L_SCR1 if src is negative
       bpl.b   int_inx         ; otherwise result is 0
       move.l  #-1,L_SCR1(a6)
       bra.b   int_inx
int_rp:
       tst.w   ETEMP(a6)       ; RP: store +1 of proper width in L_SCR1 if
*                               ; source is greater than 0
       bmi.b   int_inx         ; otherwise, result is 0
       lea     L_SCR1(a6),a1   ; a1 is address of L_SCR1
       adda.l  d0,a1           ; offset by destination width -1
       suba.l  #1,a1
       bset.b  #0,(a1)         ; set low bit at a1 address
int_inx:
       ori.l   #inx2a_mask,USER_FPSR(a6)
       bra.b   int_wrt
int_operr:
       fmovem.x fp0,FPTEMP(a6) ;FPTEMP must contain the extended
*                               ;precision source that needs to be
*                               ;converted to integer this is required
*                               ;if the operr exception is enabled.
*                               ;set operr/aiop (no inex2 on int ovfl)

       ori.l   #opaop_mask,USER_FPSR(a6)
*                               ;fall through to perform int_wrt
int_wrt:
       move.l  EXC_EA(a6),a1   ;load destination address
       tst.l   a1              ;check to see if it is a dest register
       beq.b   wrt_dn          ;write data register
       lea     L_SCR1(a6),a0   ;point to supervisor source address
       bsr.l   mem_write
       bra.w   mvouti_end

wrt_dn:
       move.l  d0,-(sp)        ;d0 currently contains the size to write
       bsr.l   get_fline       ;get_fline returns Dn in d0
       andi.w  #$7,d0          ;isolate register
       move.l  (sp)+,d1        ;get size
       cmpi.l  #4,d1           ;most frequent case
       beq.b   sz_long
       cmpi.l  #2,d1
       bne.b   sz_con
       or.l    #8,d0           ;add 'word' size to register#
       bra.b   sz_con
sz_long:
       or.l    #$10,d0         ;add 'long' size to register#
sz_con:
       move.l  d0,d1           ;reg_dest expects size:reg in d1
       bsr.l   reg_dest        ;load proper data register
       bra.w   mvouti_end
xp:
       lea     ETEMP(a6),a0
       bclr.b  #sign_bit,LOCAL_EX(a0)
       sne     LOCAL_SGN(a0)
       btst.b  #7,STAG(a6)     ;check for extended denorm
       bne.w   xdnrm
       clr.l   d0
       bra.b   do_fp           ;do normal case
sgp:
       lea     ETEMP(a6),a0
       bclr.b  #sign_bit,LOCAL_EX(a0)
       sne     LOCAL_SGN(a0)
       btst.b  #7,STAG(a6)     ;check for extended denorm
       bne.w   sp_catas        ;branch if so
       move.w  LOCAL_EX(a0),d0
       lea     sp_bnds,a1
       cmp.w   (a1),d0
       blt.w   sp_under
       cmp.w   2(a1),d0
       bgt.w   sp_over
       move.l  #1,d0           ;set destination format to single
       bra.b   do_fp           ;do normal case
dp:
       lea     ETEMP(a6),a0
       bclr.b  #sign_bit,LOCAL_EX(a0)
       sne     LOCAL_SGN(a0)

       btst.b  #7,STAG(a6)     ;check for extended denorm
       bne.w   dp_catas        ;branch if so

       move.w  LOCAL_EX(a0),d0
       lea     dp_bnds,a1

       cmp.w   (a1),d0
       blt.w   dp_under
       cmp.w   2(a1),d0
       bgt.w   dp_over

       move.l  #2,d0           ;set destination format to double
*                               ;fall through to do_fp
*
do_fp:
       bfextu  FPCR_MODE(a6){2:2},d1   ;rnd mode in d1
       swap    d0                      ;rnd prec in upper word
       add.l   d0,d1                   ;d1 has PREC/MODE info

       clr.l   d0                      ;clear g,r,s

       bsr.l   round                   ;round

       move.l  a0,a1
       move.l  EXC_EA(a6),a0

       bfextu  CMDREG1B(a6){3:3},d1    ;extract destination format
*                                       ;at this point only the dest
*                                       ;formats sgl, dbl, ext are
*                                       ;possible
       cmp.b   #2,d1
       bgt.b   ddbl                    ;double=5, extended=2, single=1
       bne.b   dsgl
*                                       ;fall through to dext
dext:
       bsr.l   dest_ext
       bra.w   mvout_end
dsgl:
       bsr.l   dest_sgl
       bra.w   mvout_end
ddbl:
       bsr.l   dest_dbl
       bra.w   mvout_end

*
* Handle possible denorm or catastrophic underflow cases here
*
xdnrm:
       bsr.w   set_xop         ;initialize WBTEMP
       bset.b  #wbtemp15_bit,WB_BYTE(a6) ;set wbtemp15

       move.l  a0,a1
       move.l  EXC_EA(a6),a0   ;a0 has the destination pointer
       bsr.l   dest_ext        ;store to memory
       bset.b  #unfl_bit,FPSR_EXCEPT(a6)
       bra.w   mvout_end

sp_under:
       bset.b  #etemp15_bit,STAG(a6)

       cmp.w   4(a1),d0
       blt.b   sp_catas        ;catastrophic underflow case

       move.l  #1,d0           ;load in round precision
       move.l  #sgl_thresh,d1  ;load in single denorm threshold
       bsr.l   dpspdnrm        ;expects d1 to have the proper
*                               ;denorm threshold
       bsr.l   dest_sgl        ;stores value to destination
       bset.b  #unfl_bit,FPSR_EXCEPT(a6)
       bra.w   mvout_end       ;exit

dp_under:
       bset.b  #etemp15_bit,STAG(a6)

       cmp.w   4(a1),d0
       blt.b   dp_catas        ;catastrophic underflow case

       move.l  #dbl_thresh,d1  ;load in double precision threshold
       move.l  #2,d0
       bsr.l   dpspdnrm        ;expects d1 to have proper
*                               ;denorm threshold
*                               ;expects d0 to have round precision
       bsr.l   dest_dbl        ;store value to destination
       bset.b  #unfl_bit,FPSR_EXCEPT(a6)
       bra.w   mvout_end       ;exit

*
* Handle catastrophic underflow cases here
*
sp_catas:
* Temp fix for z bit set in unf_sub
       move.l  USER_FPSR(a6),-(a7)

       move.l  #1,d0           ;set round precision to sgl

       bsr.l   unf_sub         ;a0 points to result

       move.l  (a7)+,USER_FPSR(a6)

       move.l  #1,d0
       sub.w   d0,LOCAL_EX(a0) ;account for difference between
*                               ;denorm/norm bias

       move.l  a0,a1           ;a1 has the operand input
       move.l  EXC_EA(a6),a0   ;a0 has the destination pointer

       bsr.l   dest_sgl        ;store the result
       ori.l   #unfinx_mask,USER_FPSR(a6)
       bra.w   mvout_end

dp_catas:
* Temp fix for z bit set in unf_sub
       move.l  USER_FPSR(a6),-(a7)

       move.l  #2,d0           ;set round precision to dbl
       bsr.l   unf_sub         ;a0 points to result

       move.l  (a7)+,USER_FPSR(a6)

       move.l  #1,d0
       sub.w   d0,LOCAL_EX(a0) ;account for difference between
*                               ;denorm/norm bias

       move.l  a0,a1           ;a1 has the operand input
       move.l  EXC_EA(a6),a0   ;a0 has the destination pointer

       bsr.l   dest_dbl        ;store the result
       ori.l   #unfinx_mask,USER_FPSR(a6)
       bra.w   mvout_end

*
* Handle catastrophic overflow cases here
*
sp_over:
* Temp fix for z bit set in unf_sub
       move.l  USER_FPSR(a6),-(a7)

       move.l  #1,d0
       lea.l   FP_SCR1(a6),a0  ;use FP_SCR1 for creating result
       move.l  ETEMP_EX(a6),(a0)
       move.l  ETEMP_HI(a6),4(a0)
       move.l  ETEMP_LO(a6),8(a0)
       bsr.l   ovf_res

       move.l  (a7)+,USER_FPSR(a6)

       move.l  a0,a1
       move.l  EXC_EA(a6),a0
       bsr.l   dest_sgl
       or.l    #ovfinx_mask,USER_FPSR(a6)
       bra.w   mvout_end

dp_over:
* Temp fix for z bit set in ovf_res
       move.l  USER_FPSR(a6),-(a7)

       move.l  #2,d0
       lea.l   FP_SCR1(a6),a0  ;use FP_SCR1 for creating result
       move.l  ETEMP_EX(a6),(a0)
       move.l  ETEMP_HI(a6),4(a0)
       move.l  ETEMP_LO(a6),8(a0)
       bsr.l   ovf_res

       move.l  (a7)+,USER_FPSR(a6)

       move.l  a0,a1
       move.l  EXC_EA(a6),a0
       bsr.l   dest_dbl
       or.l    #ovfinx_mask,USER_FPSR(a6)
       bra.w   mvout_end

*
*       DPSPDNRM
*
* This subroutine takes an extended normalized number and denormalizes
* it to the given round precision. This subroutine also decrements
* the input operand's exponent by 1 to account for the fact that
* dest_sgl or dest_dbl expects a normalized number's bias.
*
* Input: a0  points to a normalized number in internal extended format
*        d0  is the round precision (=1 for sgl; =2 for dbl)
*        d1  is the single precision or double precision
*            denorm threshold
*
* Output: (In the format for dest_sgl or dest_dbl)
*        a0   points to the destination
*        a1   points to the operand
*
* Exceptions: Reports inexact 2 exception by setting USER_FPSR bits
*
dpspdnrm:
       move.l  d0,-(a7)        ;save round precision
       clr.l   d0              ;clear initial g,r,s
       bsr.l   dnrm_lp         ;careful with d0, it's needed by round

       bfextu  FPCR_MODE(a6){2:2},d1 ;get rounding mode
       swap    d1
       move.w  2(a7),d1        ;set rounding precision
       swap    d1              ;at this point d1 has PREC/MODE info
       bsr.l   round           ;round result, sets the inex bit in
*                               ;USER_FPSR if needed

       move.w  #1,d0
       sub.w   d0,LOCAL_EX(a0) ;account for difference in denorm
*                               ;vs norm bias

       move.l  a0,a1           ;a1 has the operand input
       move.l  EXC_EA(a6),a0   ;a0 has the destination pointer
       addq.l  #4,a7           ;pop stack
       rts
*
* SET_XOP initialized WBTEMP with the value pointed to by a0
* input: a0 points to input operand in the internal extended format
*
set_xop:
       move.l  LOCAL_EX(a0),WBTEMP_EX(a6)
       move.l  LOCAL_HI(a0),WBTEMP_HI(a6)
       move.l  LOCAL_LO(a0),WBTEMP_LO(a6)
       bfclr   WBTEMP_SGN(a6){0:8}
       beq.b   sxop
       bset.b  #sign_bit,WBTEMP_EX(a6)
sxop:
       bfclr   STAG(a6){5:4}   ;clear wbtm66,wbtm1,wbtm0,sbit
       rts
*
*       P_MOVE
*
p_movet:
       dc.l    p_move
       dc.l    p_movez
       dc.l    p_movei
       dc.l    p_moven
       dc.l    p_move
p_regd:
       dc.l    p_dyd0
       dc.l    p_dyd1
       dc.l    p_dyd2
       dc.l    p_dyd3
       dc.l    p_dyd4
       dc.l    p_dyd5
       dc.l    p_dyd6
       dc.l    p_dyd7

pack_out:
       lea.l   p_movet,a0      ;load jmp table address
       move.w  STAG(a6),d0     ;get source tag
       bfextu  d0{16:3},d0     ;isolate source bits
       move.l  (a0,d0.w*4),a0  ;load a0 with routine label for tag
       jmp     (a0)            ;go to the routine

p_write:
       move.l  #$0c,d0         ;get byte count
       move.l  EXC_EA(a6),a1   ;get the destination address
       bsr     mem_write       ;write the user's destination
       clr.b   CU_SAVEPC(a6) ;set the cu save pc to all 0's

*
* Also note that the dtag must be set to norm here - this is because
* the 040 uses the dtag to execute the correct microcode.
*
       bfclr    DTAG(a6){0:3}  ;set dtag to norm

       rts

* Notes on handling of special case (zero, inf, and nan) inputs:
*       1. Operr is not signalled if the k-factor is greater than 18.
*       2. Per the manual, status bits are not set.
*

p_move:
       move.w  CMDREG1B(a6),d0
       btst.l  #kfact_bit,d0   ;test for dynamic k-factor
       beq.b   statick         ;if clear, k-factor is static
dynamick:
       bfextu  d0{25:3},d0     ;isolate register for dynamic k-factor
       lea     p_regd,a0
       move.l  (a0,d0*4),a0
       jmp     (a0)
statick:
       andi.w  #$007f,d0       ;get k-factor
       bfexts  d0{25:7},d0     ;sign extend d0 for bindec
       lea.l   ETEMP(a6),a0    ;a0 will point to the packed decimal
       bsr.l   bindec          ;perform the convert; data at a6
       lea.l   FP_SCR1(a6),a0  ;load a0 with result address
       bra.l   p_write
p_movez:
       lea.l   ETEMP(a6),a0    ;a0 will point to the packed decimal
       clr.w   2(a0)           ;clear lower word of exp
       clr.l   4(a0)           ;load second lword of ZERO
       clr.l   8(a0)           ;load third lword of ZERO
       bra.w   p_write         ;go write results
p_movei:
       fmove.l #0,FPSR         ;clear aiop
       lea.l   ETEMP(a6),a0    ;a0 will point to the packed decimal
       clr.w   2(a0)           ;clear lower word of exp
       bra.w   p_write         ;go write the result
p_moven:
       lea.l   ETEMP(a6),a0    ;a0 will point to the packed decimal
       clr.w   2(a0)           ;clear lower word of exp
       bra.w   p_write         ;go write the result

*
* Routines to read the dynamic k-factor from Dn.
*
p_dyd0:
       move.l  USER_D0(a6),d0
       bra.b   statick
p_dyd1:
       move.l  USER_D1(a6),d0
       bra.b   statick
p_dyd2:
       move.l  d2,d0
       bra.b   statick
p_dyd3:
       move.l  d3,d0
       bra.b   statick
p_dyd4:
       move.l  d4,d0
       bra.b   statick
p_dyd5:
       move.l  d5,d0
       bra.b   statick
p_dyd6:
       move.l  d6,d0
       bra.w   statick
p_dyd7:
       move.l  d7,d0
       bra.w   statick

       end