summaryrefslogtreecommitdiff
path: root/sys/arch/m68k/fpsp/res_func.sa
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch/m68k/fpsp/res_func.sa')
-rw-r--r--sys/arch/m68k/fpsp/res_func.sa2065
1 files changed, 2065 insertions, 0 deletions
diff --git a/sys/arch/m68k/fpsp/res_func.sa b/sys/arch/m68k/fpsp/res_func.sa
new file mode 100644
index 00000000000..5c036b742fc
--- /dev/null
+++ b/sys/arch/m68k/fpsp/res_func.sa
@@ -0,0 +1,2065 @@
+* $NetBSD: res_func.sa,v 1.3 1994/10/26 07:49:22 cgd 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 occured 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) completly 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 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