summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libcpu/m68k/m68040/fpsp/res_func.S
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/lib/libcpu/m68k/m68040/fpsp/res_func.S')
-rw-r--r--c/src/lib/libcpu/m68k/m68040/fpsp/res_func.S2042
1 files changed, 2042 insertions, 0 deletions
diff --git a/c/src/lib/libcpu/m68k/m68040/fpsp/res_func.S b/c/src/lib/libcpu/m68k/m68040/fpsp/res_func.S
new file mode 100644
index 0000000000..2f6b22870a
--- /dev/null
+++ b/c/src/lib/libcpu/m68k/m68040/fpsp/res_func.S
@@ -0,0 +1,2042 @@
+//
+// $Id$
+//
+// 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.
+//
+//
+// Copyright (C) Motorola, Inc. 1990
+// All Rights Reserved
+//
+// THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF MOTOROLA
+// The copyright notice above does not evidence any
+// actual or intended publication of such source code.
+
+RES_FUNC: //idnt 2,1 | Motorola 040 Floating Point Software Package
+
+ |section 8
+
+#include "fpsp.defs"
+
+sp_bnds: .short 0x3f81,0x407e
+ .short 0x3f6a,0x0000
+dp_bnds: .short 0x3c01,0x43fe
+ .short 0x3bcd,0x0000
+
+ |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
+
+ .global res_func
+ .global p_move
+
+res_func:
+ clrb DNRM_FLG(%a6)
+ clrb RES_FLG(%a6)
+ clrb CU_ONLY(%a6)
+ tstb DY_MO_FLG(%a6)
+ beqs monadic
+dyadic:
+ btstb #7,DTAG(%a6) //if dop = norm=000, zero=001,
+// ;inf=010 or nan=011
+ beqs monadic //then branch
+// ;else denorm
+// HANDLE DESTINATION DENORM HERE
+// ;set dtag to norm
+// ;write the tag & fpte15 to the fstack
+ leal FPTEMP(%a6),%a0
+
+ bclrb #sign_bit,LOCAL_EX(%a0)
+ sne LOCAL_SGN(%a0)
+
+ bsr nrm_set //normalize number (exp will go negative)
+ bclrb #sign_bit,LOCAL_EX(%a0) //get rid of false sign
+ bfclr LOCAL_SGN(%a0){#0:#8} //change back to IEEE ext format
+ beqs dpos
+ bsetb #sign_bit,LOCAL_EX(%a0)
+dpos:
+ bfclr DTAG(%a6){#0:#4} //set tag to normalized, FPTE15 = 0
+ bsetb #4,DTAG(%a6) //set FPTE15
+ orb #0x0f,DNRM_FLG(%a6)
+monadic:
+ leal ETEMP(%a6),%a0
+ btstb #direction_bit,CMDREG1B(%a6) //check direction
+ bne opclass3 //it is a mv out
+//
+// At this point, only opclass 0 and 2 possible
+//
+ btstb #7,STAG(%a6) //if sop = norm=000, zero=001,
+// ;inf=010 or nan=011
+ bne mon_dnrm //else denorm
+ tstb DY_MO_FLG(%a6) //all cases of dyadic instructions would
+ bne 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)
+//
+ movew CMDREG1B(%a6),%d0 //get command register
+ andil #0x7f,%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)
+//
+ btstl #0,%d0
+ bne 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
+ movew CMDREG1B(%a6),%d0
+ andib #0x3b,%d0 //isolate bits to select inst
+ tstb %d0
+ beql cu_nmove //if zero, it is an fmove
+ cmpib #0x18,%d0
+ beql cu_nabs //if $18, it is fabs
+ cmpib #0x1a,%d0
+ beql 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:
+ movew LOCAL_EX(%a0),%d0
+ bclrl #15,%d0
+ sne LOCAL_SGN(%a0)
+ beqs cu_ntpo
+ orl #neg_mask,USER_FPSR(%a6) //set N
+cu_ntpo:
+ cmpiw #0x7fff,%d0 //test for inf/nan
+ bnes cu_ntcz
+ tstl LOCAL_HI(%a0)
+ bnes cu_ntn
+ tstl LOCAL_LO(%a0)
+ bnes cu_ntn
+ orl #inf_mask,USER_FPSR(%a6)
+ rts
+cu_ntn:
+ orl #nan_mask,USER_FPSR(%a6)
+ movel ETEMP_EX(%a6),FPTEMP_EX(%a6) //set up fptemp sign for
+// ;snan handler
+
+ rts
+cu_ntcz:
+ tstl LOCAL_HI(%a0)
+ bnel cu_ntsx
+ tstl LOCAL_LO(%a0)
+ bnel cu_ntsx
+ orl #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:
+ moveb STAG(%a6),%d0
+ btstl #5,%d0 //test for NaN or zero
+ bne wr_etemp //if either, simply write it
+ bclrb #7,LOCAL_EX(%a0) //do abs
+ bras 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:
+ moveb STAG(%a6),%d0
+ btstl #5,%d0 //test for NaN or zero
+ bne wr_etemp //if either, simply write it
+ bchgb #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:
+ moveb STAG(%a6),%d0
+ andib #0xe0,%d0 //isolate stag bits
+ bne wr_etemp //if not norm, simply write it
+ btstb #2,CMDREG1B+1(%a6) //check for rd
+ bne cu_nmrd
+ btstb #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
+ tstb %d0 //check for extended
+ beq cu_wrexn //if so, just write result
+ cmpib #1,%d0 //check for single
+ beq cu_nmrs //fall through to double
+//
+// The move is fdmove or round precision is double.
+//
+cu_nmrd:
+ movel #2,%d0 //set up the size for denorm
+ movew LOCAL_EX(%a0),%d1 //compare exponent to double threshold
+ andw #0x7fff,%d1
+ cmpw #0x3c01,%d1
+ bls cu_nunfl
+ bfextu FPCR_MODE(%a6){#2:#2},%d1 //get rmode
+ orl #0x00020000,%d1 //or in rprec (double)
+ clrl %d0 //clear g,r,s for round
+ bclrb #sign_bit,LOCAL_EX(%a0) //convert to internal format
+ sne LOCAL_SGN(%a0)
+ bsrl round
+ bfclr LOCAL_SGN(%a0){#0:#8}
+ beqs cu_nmrdc
+ bsetb #sign_bit,LOCAL_EX(%a0)
+cu_nmrdc:
+ movew LOCAL_EX(%a0),%d1 //check for overflow
+ andw #0x7fff,%d1
+ cmpw #0x43ff,%d1
+ bge cu_novfl //take care of overflow case
+ bra cu_wrexn
+//
+// The move is fsmove or round precision is single.
+//
+cu_nmrs:
+ movel #1,%d0
+ movew LOCAL_EX(%a0),%d1
+ andw #0x7fff,%d1
+ cmpw #0x3f81,%d1
+ bls cu_nunfl
+ bfextu FPCR_MODE(%a6){#2:#2},%d1
+ orl #0x00010000,%d1
+ clrl %d0
+ bclrb #sign_bit,LOCAL_EX(%a0)
+ sne LOCAL_SGN(%a0)
+ bsrl round
+ bfclr LOCAL_SGN(%a0){#0:#8}
+ beqs cu_nmrsc
+ bsetb #sign_bit,LOCAL_EX(%a0)
+cu_nmrsc:
+ movew LOCAL_EX(%a0),%d1
+ andw #0x7FFF,%d1
+ cmpw #0x407f,%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:
+ bclrb #sign_bit,LOCAL_EX(%a0)
+ sne LOCAL_SGN(%a0)
+ bsr denorm
+ bfclr LOCAL_SGN(%a0){#0:#8} //change back to IEEE ext format
+ beqs cu_nucont
+ bsetb #sign_bit,LOCAL_EX(%a0)
+cu_nucont:
+ bfextu FPCR_MODE(%a6){#2:#2},%d1
+ btstb #2,CMDREG1B+1(%a6) //check for rd
+ bne inst_d
+ btstb #6,CMDREG1B+1(%a6) //check for rs
+ bne inst_s
+ swap %d1
+ moveb FPCR_MODE(%a6),%d1
+ lsrb #6,%d1
+ swap %d1
+ bra inst_sd
+inst_d:
+ orl #0x00020000,%d1
+ bra inst_sd
+inst_s:
+ orl #0x00010000,%d1
+inst_sd:
+ bclrb #sign_bit,LOCAL_EX(%a0)
+ sne LOCAL_SGN(%a0)
+ bsrl round
+ bfclr LOCAL_SGN(%a0){#0:#8}
+ beqs cu_nuflp
+ bsetb #sign_bit,LOCAL_EX(%a0)
+cu_nuflp:
+ btstb #inex2_bit,FPSR_EXCEPT(%a6)
+ beqs cu_nuninx
+ orl #aunfl_mask,USER_FPSR(%a6) //if the round was inex, set AUNFL
+cu_nuninx:
+ tstl LOCAL_HI(%a0) //test for zero
+ bnes cu_nunzro
+ tstl LOCAL_LO(%a0)
+ bnes 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.
+//
+ movel USER_FPCR(%a6),%d0
+ andil #0x30,%d0 //isolate rmode
+ cmpil #0x20,%d0
+ blts cu_nzro
+ bnes cu_nrp
+cu_nrm:
+ tstw LOCAL_EX(%a0) //if positive, set lsb
+ bges cu_nzro
+ btstb #7,FPCR_MODE(%a6) //check for double
+ beqs cu_nincs
+ bras cu_nincd
+cu_nrp:
+ tstw LOCAL_EX(%a0) //if positive, set lsb
+ blts cu_nzro
+ btstb #7,FPCR_MODE(%a6) //check for double
+ beqs cu_nincs
+cu_nincd:
+ orl #0x800,LOCAL_LO(%a0) //inc for double
+ bra cu_nunzro
+cu_nincs:
+ orl #0x100,LOCAL_HI(%a0) //inc for single
+ bra cu_nunzro
+cu_nzro:
+ orl #z_mask,USER_FPSR(%a6)
+ moveb STAG(%a6),%d0
+ andib #0xe0,%d0
+ cmpib #0x40,%d0 //check if input was tagged zero
+ beqs cu_numv
+cu_nunzro:
+ orl #unfl_mask,USER_FPSR(%a6) //set unfl
+cu_numv:
+ movel (%a0),ETEMP(%a6)
+ movel 4(%a0),ETEMP_HI(%a6)
+ movel 8(%a0),ETEMP_LO(%a6)
+//
+// Write the result to memory, setting the fpsr cc bits. NaN and Inf
+// bypass cu_wrexn.
+//
+cu_wrexn:
+ tstw LOCAL_EX(%a0) //test for zero
+ beqs cu_wrzero
+ cmpw #0x8000,LOCAL_EX(%a0) //test for zero
+ bnes cu_wreon
+cu_wrzero:
+ orl #z_mask,USER_FPSR(%a6) //set Z bit
+cu_wreon:
+ tstw LOCAL_EX(%a0)
+ bpl wr_etemp
+ orl #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.
+//
+ tstb DY_MO_FLG(%a6) //all cases of dyadic instructions would
+ bnes 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)
+//
+ movew CMDREG1B(%a6),%d0 //get command register
+ andil #0x7f,%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)
+//
+ btstl #0,%d0
+ bnes 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:
+ bclrb #sign_bit,LOCAL_EX(%a0)
+ sne LOCAL_SGN(%a0)
+ bsr nrm_set //normalize number (exponent will go
+// ; negative)
+ bclrb #sign_bit,LOCAL_EX(%a0) //get rid of false sign
+
+ bfclr LOCAL_SGN(%a0){#0:#8} //change back to IEEE ext format
+ beqs spos
+ bsetb #sign_bit,LOCAL_EX(%a0)
+spos:
+ bfclr STAG(%a6){#0:#4} //set tag to normalized, FPTE15 = 0
+ bsetb #4,STAG(%a6) //set ETE15
+ orb #0xf0,DNRM_FLG(%a6)
+normal:
+ tstb 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:
+ moveb #0xfe,CU_SAVEPC(%a6)
+ bclrb #E1,E_BYTE(%a6)
+
+ clrw NMNEXC(%a6)
+
+ st 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 CU_ONLY(%a6)
+ movew CMDREG1B(%a6),%d0
+ andib #0x3b,%d0 //isolate bits to select inst
+ tstb %d0
+ beql cu_dmove //if zero, it is an fmove
+ cmpib #0x18,%d0
+ beql cu_dabs //if $18, it is fabs
+ cmpib #0x1a,%d0
+ beql 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:
+ movew LOCAL_EX(%a0),%d0
+ bclrl #15,%d0
+ sne LOCAL_SGN(%a0)
+ beqs cu_dtpo
+ orl #neg_mask,USER_FPSR(%a6) //set N
+cu_dtpo:
+ cmpiw #0x7fff,%d0 //test for inf/nan
+ bnes cu_dtcz
+ tstl LOCAL_HI(%a0)
+ bnes cu_dtn
+ tstl LOCAL_LO(%a0)
+ bnes cu_dtn
+ orl #inf_mask,USER_FPSR(%a6)
+ rts
+cu_dtn:
+ orl #nan_mask,USER_FPSR(%a6)
+ movel ETEMP_EX(%a6),FPTEMP_EX(%a6) //set up fptemp sign for
+// ;snan handler
+ rts
+cu_dtcz:
+ tstl LOCAL_HI(%a0)
+ bnel cu_dtsx
+ tstl LOCAL_LO(%a0)
+ bnel cu_dtsx
+ orl #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:
+ bclrb #7,LOCAL_EX(%a0) //do abs
+ bras 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:
+ bchgb #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:
+ btstb #2,CMDREG1B+1(%a6) //check for rd
+ bne cu_dmrd
+ btstb #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
+ tstb %d0 //check for extended
+ beq cu_wrexd //if so, just write result
+ cmpib #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
+ tstw LOCAL_EX(%a0) //check sign
+ blts cu_dmdn
+ cmpib #3,%d1 //check for rp
+ bne cu_dpd //load double pos zero
+ bra cu_dpdr //load double pos zero w/lsb
+cu_dmdn:
+ cmpib #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
+ tstw LOCAL_EX(%a0) //check sign
+ blts cu_dmsn
+ cmpib #3,%d1 //check for rp
+ bne cu_spd //load single pos zero
+ bra cu_spdr //load single pos zero w/lsb
+cu_dmsn:
+ cmpib #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:
+ orl #unfl_mask,USER_FPSR(%a6)
+ tstw LOCAL_EX(%a0)
+ beq wr_etemp
+ orl #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:
+ movel #0x3c010000,LOCAL_EX(%a0) //force pos double zero
+ clrl LOCAL_HI(%a0)
+ clrl LOCAL_LO(%a0)
+ orl #z_mask,USER_FPSR(%a6)
+ orl #unfinx_mask,USER_FPSR(%a6)
+ bra wr_etemp
+cu_dpdr:
+ movel #0x3c010000,LOCAL_EX(%a0) //force pos double zero
+ clrl LOCAL_HI(%a0)
+ movel #0x800,LOCAL_LO(%a0) //with lsb set
+ orl #unfinx_mask,USER_FPSR(%a6)
+ bra wr_etemp
+cu_dnd:
+ movel #0xbc010000,LOCAL_EX(%a0) //force pos double zero
+ clrl LOCAL_HI(%a0)
+ clrl LOCAL_LO(%a0)
+ orl #z_mask,USER_FPSR(%a6)
+ orl #neg_mask,USER_FPSR(%a6)
+ orl #unfinx_mask,USER_FPSR(%a6)
+ bra wr_etemp
+cu_dndr:
+ movel #0xbc010000,LOCAL_EX(%a0) //force pos double zero
+ clrl LOCAL_HI(%a0)
+ movel #0x800,LOCAL_LO(%a0) //with lsb set
+ orl #neg_mask,USER_FPSR(%a6)
+ orl #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:
+ movel #0x3f810000,LOCAL_EX(%a0) //force pos single zero
+ clrl LOCAL_HI(%a0)
+ clrl LOCAL_LO(%a0)
+ orl #z_mask,USER_FPSR(%a6)
+ orl #unfinx_mask,USER_FPSR(%a6)
+ bra wr_etemp
+cu_spdr:
+ movel #0x3f810000,LOCAL_EX(%a0) //force pos single zero
+ movel #0x100,LOCAL_HI(%a0) //with lsb set
+ clrl LOCAL_LO(%a0)
+ orl #unfinx_mask,USER_FPSR(%a6)
+ bra wr_etemp
+cu_snd:
+ movel #0xbf810000,LOCAL_EX(%a0) //force pos single zero
+ clrl LOCAL_HI(%a0)
+ clrl LOCAL_LO(%a0)
+ orl #z_mask,USER_FPSR(%a6)
+ orl #neg_mask,USER_FPSR(%a6)
+ orl #unfinx_mask,USER_FPSR(%a6)
+ bra wr_etemp
+cu_sndr:
+ movel #0xbf810000,LOCAL_EX(%a0) //force pos single zero
+ movel #0x100,LOCAL_HI(%a0) //with lsb set
+ clrl LOCAL_LO(%a0)
+ orl #neg_mask,USER_FPSR(%a6)
+ orl #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 denormalized
+//
+// 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:
+ .set addcode,0x22
+ .set subcode,0x28
+ .set mulcode,0x23
+ .set divcode,0x20
+ .set cmpcode,0x38
+ck_wrap:
+ | tstb DY_MO_FLG(%a6) ;check for fsqrt
+ beq fix_stk //if zero, it is fsqrt
+ movew CMDREG1B(%a6),%d0
+ andiw #0x3b,%d0 //strip to command bits
+ cmpiw #addcode,%d0
+ beq wrap_add
+ cmpiw #subcode,%d0
+ beq wrap_sub
+ cmpiw #mulcode,%d0
+ beq wrap_mul
+ cmpiw #cmpcode,%d0
+ beq wrap_cmp
+//
+// Inst is fdiv.
+//
+wrap_div:
+ cmpb #0xff,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.
+//
+ cmpb #0x0f,DNRM_FLG(%a6) //check for dest denorm
+ bnes div_srcd
+div_destd:
+ bsrl 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)
+ subl %d1,%d0 //subtract dest from src
+ cmpl #0x7fff,%d0
+ blt fix_stk //if less, not wrap case
+ clrb WBTEMP_SGN(%a6)
+ movew ETEMP_EX(%a6),%d0 //find the sign of the result
+ movew FPTEMP_EX(%a6),%d1
+ eorw %d1,%d0
+ andiw #0x8000,%d0
+ beq force_unf
+ st WBTEMP_SGN(%a6)
+ bra force_unf
+
+ckinf_ns:
+ moveb STAG(%a6),%d0 //check source tag for inf or nan
+ bra ck_in_com
+ckinf_nd:
+ moveb DTAG(%a6),%d0 //check destination tag for inf or nan
+ck_in_com:
+ andib #0x60,%d0 //isolate tag bits
+ cmpb #0x40,%d0 //is it inf?
+ beq nan_or_inf //not wrap case
+ cmpb #0x60,%d0 //is it nan?
+ beq nan_or_inf //yes, not wrap case?
+ cmpb #0x20,%d0 //is it a zero?
+ beq nan_or_inf //yes
+ clrl %d0
+ rts //then ; it is either a zero of norm,
+// ;check wrap case
+nan_or_inf:
+ moveql #-1,%d0
+ rts
+
+
+
+div_srcd:
+ bsrl 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)
+ subl %d1,%d0 //subtract src from dest
+ cmpl #0x8000,%d0
+ blt fix_stk //if less, not wrap case
+ clrb WBTEMP_SGN(%a6)
+ movew ETEMP_EX(%a6),%d0 //find the sign of the result
+ movew FPTEMP_EX(%a6),%d1
+ eorw %d1,%d0
+ andiw #0x8000,%d0
+ beqs force_ovf
+ st WBTEMP_SGN(%a6)
+//
+// This code handles the case of the instruction resulting in
+// an overflow condition.
+//
+force_ovf:
+ bclrb #E1,E_BYTE(%a6)
+ orl #ovfl_inx_mask,USER_FPSR(%a6)
+ clrw NMNEXC(%a6)
+ leal WBTEMP(%a6),%a0 //point a0 to memory location
+ movew CMDREG1B(%a6),%d0
+ btstl #6,%d0 //test for forced precision
+ beqs frcovf_fpcr
+ btstl #2,%d0 //check for double
+ bnes frcovf_dbl
+ movel #0x1,%d0 //inst is forced single
+ bras frcovf_rnd
+frcovf_dbl:
+ movel #0x2,%d0 //inst is forced double
+ bras 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:
+ bsrl 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
+ bsetb #sign_bit,WBTEMP_EX(%a6)
+ bra frcfpn
+//
+// Inst is fadd.
+//
+wrap_add:
+ cmpb #0xff,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.
+//
+ cmpb #0x0f,DNRM_FLG(%a6) //check for dest denorm
+ bnes add_srcd
+add_destd:
+ bsrl 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)
+ subl %d1,%d0 //subtract dest from src
+ cmpl #0x8000,%d0
+ blt fix_stk //if less, not wrap case
+ bra add_wrap
+add_srcd:
+ bsrl 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)
+ subl %d1,%d0 //subtract src from dest
+ cmpl #0x8000,%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 correct for the user's rounding mode and
+// precision.
+//
+add_wrap:
+ movew ETEMP_EX(%a6),%d0
+ movew FPTEMP_EX(%a6),%d1
+ eorw %d1,%d0
+ andiw #0x8000,%d0
+ beq add_same
+//
+// The signs are unlike.
+//
+ cmpb #0x0f,DNRM_FLG(%a6) //is dest the denorm?
+ bnes add_u_srcd
+ movew FPTEMP_EX(%a6),%d0
+ andiw #0x8000,%d0
+ orw #0x3fff,%d0 //force the exponent to +/- 1
+ movew %d0,FPTEMP_EX(%a6) //in the denorm
+ movel USER_FPCR(%a6),%d0
+ andil #0x30,%d0
+ fmovel %d0,%fpcr //set up users rmode and X
+ fmovex ETEMP(%a6),%fp0
+ faddx FPTEMP(%a6),%fp0
+ leal WBTEMP(%a6),%a0 //point a0 to wbtemp in frame
+ fmovel %fpsr,%d1
+ orl %d1,USER_FPSR(%a6) //capture cc's and inex from fadd
+ fmovex %fp0,WBTEMP(%a6) //write result to memory
+ lsrl #4,%d0 //put rmode in lower 2 bits
+ movel USER_FPCR(%a6),%d1
+ andil #0xc0,%d1
+ lsrl #6,%d1 //put precision in upper word
+ swap %d1
+ orl %d0,%d1 //set up for round call
+ clrl %d0 //force sticky to zero
+ bclrb #sign_bit,WBTEMP_EX(%a6)
+ sne WBTEMP_SGN(%a6)
+ bsrl round //round result to users rmode & prec
+ bfclr WBTEMP_SGN(%a6){#0:#8} //convert back to IEEE ext format
+ beq frcfpnr
+ bsetb #sign_bit,WBTEMP_EX(%a6)
+ bra frcfpnr
+add_u_srcd:
+ movew ETEMP_EX(%a6),%d0
+ andiw #0x8000,%d0
+ orw #0x3fff,%d0 //force the exponent to +/- 1
+ movew %d0,ETEMP_EX(%a6) //in the denorm
+ movel USER_FPCR(%a6),%d0
+ andil #0x30,%d0
+ fmovel %d0,%fpcr //set up users rmode and X
+ fmovex ETEMP(%a6),%fp0
+ faddx FPTEMP(%a6),%fp0
+ fmovel %fpsr,%d1
+ orl %d1,USER_FPSR(%a6) //capture cc's and inex from fadd
+ leal WBTEMP(%a6),%a0 //point a0 to wbtemp in frame
+ fmovex %fp0,WBTEMP(%a6) //write result to memory
+ lsrl #4,%d0 //put rmode in lower 2 bits
+ movel USER_FPCR(%a6),%d1
+ andil #0xc0,%d1
+ lsrl #6,%d1 //put precision in upper word
+ swap %d1
+ orl %d0,%d1 //set up for round call
+ clrl %d0 //force sticky to zero
+ bclrb #sign_bit,WBTEMP_EX(%a6)
+ sne WBTEMP_SGN(%a6) //use internal format for round
+ bsrl round //round result to users rmode & prec
+ bfclr WBTEMP_SGN(%a6){#0:#8} //convert back to IEEE ext format
+ beq frcfpnr
+ bsetb #sign_bit,WBTEMP_EX(%a6)
+ bra frcfpnr
+//
+// Signs are alike:
+//
+add_same:
+ cmpb #0x0f,DNRM_FLG(%a6) //is dest the denorm?
+ bnes add_s_srcd
+add_s_destd:
+ leal ETEMP(%a6),%a0
+ movel USER_FPCR(%a6),%d0
+ andil #0x30,%d0
+ lsrl #4,%d0 //put rmode in lower 2 bits
+ movel USER_FPCR(%a6),%d1
+ andil #0xc0,%d1
+ lsrl #6,%d1 //put precision in upper word
+ swap %d1
+ orl %d0,%d1 //set up for round call
+ movel #0x20000000,%d0 //set sticky for round
+ bclrb #sign_bit,ETEMP_EX(%a6)
+ sne ETEMP_SGN(%a6)
+ bsrl round //round result to users rmode & prec
+ bfclr ETEMP_SGN(%a6){#0:#8} //convert back to IEEE ext format
+ beqs add_s_dclr
+ bsetb #sign_bit,ETEMP_EX(%a6)
+add_s_dclr:
+ leal WBTEMP(%a6),%a0
+ movel ETEMP(%a6),(%a0) //write result to wbtemp
+ movel ETEMP_HI(%a6),4(%a0)
+ movel ETEMP_LO(%a6),8(%a0)
+ tstw ETEMP_EX(%a6)
+ bgt add_ckovf
+ orl #neg_mask,USER_FPSR(%a6)
+ bra add_ckovf
+add_s_srcd:
+ leal FPTEMP(%a6),%a0
+ movel USER_FPCR(%a6),%d0
+ andil #0x30,%d0
+ lsrl #4,%d0 //put rmode in lower 2 bits
+ movel USER_FPCR(%a6),%d1
+ andil #0xc0,%d1
+ lsrl #6,%d1 //put precision in upper word
+ swap %d1
+ orl %d0,%d1 //set up for round call
+ movel #0x20000000,%d0 //set sticky for round
+ bclrb #sign_bit,FPTEMP_EX(%a6)
+ sne FPTEMP_SGN(%a6)
+ bsrl round //round result to users rmode & prec
+ bfclr FPTEMP_SGN(%a6){#0:#8} //convert back to IEEE ext format
+ beqs add_s_sclr
+ bsetb #sign_bit,FPTEMP_EX(%a6)
+add_s_sclr:
+ leal WBTEMP(%a6),%a0
+ movel FPTEMP(%a6),(%a0) //write result to wbtemp
+ movel FPTEMP_HI(%a6),4(%a0)
+ movel FPTEMP_LO(%a6),8(%a0)
+ tstw FPTEMP_EX(%a6)
+ bgt add_ckovf
+ orl #neg_mask,USER_FPSR(%a6)
+add_ckovf:
+ movew WBTEMP_EX(%a6),%d0
+ andiw #0x7fff,%d0
+ cmpiw #0x7fff,%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.)
+//
+ orl #inf_mask+ovfl_inx_mask,USER_FPSR(%a6)
+ clrl 4(%a0)
+ bra frcfpnr
+//
+// Inst is fsub.
+//
+wrap_sub:
+ cmpb #0xff,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.
+//
+ cmpb #0x0f,DNRM_FLG(%a6) //check for dest denorm
+ bnes sub_srcd
+sub_destd:
+ bsrl 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)
+ subl %d1,%d0 //subtract src from dest
+ cmpl #0x8000,%d0
+ blt fix_stk //if less, not wrap case
+ bra sub_wrap
+sub_srcd:
+ bsrl 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)
+ subl %d1,%d0 //subtract dest from src
+ cmpl #0x8000,%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 correct for the user's rounding mode and
+// precision.
+//
+sub_wrap:
+ movew ETEMP_EX(%a6),%d0
+ movew FPTEMP_EX(%a6),%d1
+ eorw %d1,%d0
+ andiw #0x8000,%d0
+ bne sub_diff
+//
+// The signs are alike.
+//
+ cmpb #0x0f,DNRM_FLG(%a6) //is dest the denorm?
+ bnes sub_u_srcd
+ movew FPTEMP_EX(%a6),%d0
+ andiw #0x8000,%d0
+ orw #0x3fff,%d0 //force the exponent to +/- 1
+ movew %d0,FPTEMP_EX(%a6) //in the denorm
+ movel USER_FPCR(%a6),%d0
+ andil #0x30,%d0
+ fmovel %d0,%fpcr //set up users rmode and X
+ fmovex FPTEMP(%a6),%fp0
+ fsubx ETEMP(%a6),%fp0
+ fmovel %fpsr,%d1
+ orl %d1,USER_FPSR(%a6) //capture cc's and inex from fadd
+ leal WBTEMP(%a6),%a0 //point a0 to wbtemp in frame
+ fmovex %fp0,WBTEMP(%a6) //write result to memory
+ lsrl #4,%d0 //put rmode in lower 2 bits
+ movel USER_FPCR(%a6),%d1
+ andil #0xc0,%d1
+ lsrl #6,%d1 //put precision in upper word
+ swap %d1
+ orl %d0,%d1 //set up for round call
+ clrl %d0 //force sticky to zero
+ bclrb #sign_bit,WBTEMP_EX(%a6)
+ sne WBTEMP_SGN(%a6)
+ bsrl round //round result to users rmode & prec
+ bfclr WBTEMP_SGN(%a6){#0:#8} //convert back to IEEE ext format
+ beq frcfpnr
+ bsetb #sign_bit,WBTEMP_EX(%a6)
+ bra frcfpnr
+sub_u_srcd:
+ movew ETEMP_EX(%a6),%d0
+ andiw #0x8000,%d0
+ orw #0x3fff,%d0 //force the exponent to +/- 1
+ movew %d0,ETEMP_EX(%a6) //in the denorm
+ movel USER_FPCR(%a6),%d0
+ andil #0x30,%d0
+ fmovel %d0,%fpcr //set up users rmode and X
+ fmovex FPTEMP(%a6),%fp0
+ fsubx ETEMP(%a6),%fp0
+ fmovel %fpsr,%d1
+ orl %d1,USER_FPSR(%a6) //capture cc's and inex from fadd
+ leal WBTEMP(%a6),%a0 //point a0 to wbtemp in frame
+ fmovex %fp0,WBTEMP(%a6) //write result to memory
+ lsrl #4,%d0 //put rmode in lower 2 bits
+ movel USER_FPCR(%a6),%d1
+ andil #0xc0,%d1
+ lsrl #6,%d1 //put precision in upper word
+ swap %d1
+ orl %d0,%d1 //set up for round call
+ clrl %d0 //force sticky to zero
+ bclrb #sign_bit,WBTEMP_EX(%a6)
+ sne WBTEMP_SGN(%a6)
+ bsrl round //round result to users rmode & prec
+ bfclr WBTEMP_SGN(%a6){#0:#8} //convert back to IEEE ext format
+ beq frcfpnr
+ bsetb #sign_bit,WBTEMP_EX(%a6)
+ bra frcfpnr
+//
+// Signs are unlike:
+//
+sub_diff:
+ cmpb #0x0f,DNRM_FLG(%a6) //is dest the denorm?
+ bnes sub_s_srcd
+sub_s_destd:
+ leal ETEMP(%a6),%a0
+ movel USER_FPCR(%a6),%d0
+ andil #0x30,%d0
+ lsrl #4,%d0 //put rmode in lower 2 bits
+ movel USER_FPCR(%a6),%d1
+ andil #0xc0,%d1
+ lsrl #6,%d1 //put precision in upper word
+ swap %d1
+ orl %d0,%d1 //set up for round call
+ movel #0x20000000,%d0 //set sticky for round
+//
+// Since the dest is the denorm, the sign is the opposite of the
+// norm sign.
+//
+ eoriw #0x8000,ETEMP_EX(%a6) //flip sign on result
+ tstw ETEMP_EX(%a6)
+ bgts sub_s_dwr
+ orl #neg_mask,USER_FPSR(%a6)
+sub_s_dwr:
+ bclrb #sign_bit,ETEMP_EX(%a6)
+ sne ETEMP_SGN(%a6)
+ bsrl round //round result to users rmode & prec
+ bfclr ETEMP_SGN(%a6){#0:#8} //convert back to IEEE ext format
+ beqs sub_s_dclr
+ bsetb #sign_bit,ETEMP_EX(%a6)
+sub_s_dclr:
+ leal WBTEMP(%a6),%a0
+ movel ETEMP(%a6),(%a0) //write result to wbtemp
+ movel ETEMP_HI(%a6),4(%a0)
+ movel ETEMP_LO(%a6),8(%a0)
+ bra sub_ckovf
+sub_s_srcd:
+ leal FPTEMP(%a6),%a0
+ movel USER_FPCR(%a6),%d0
+ andil #0x30,%d0
+ lsrl #4,%d0 //put rmode in lower 2 bits
+ movel USER_FPCR(%a6),%d1
+ andil #0xc0,%d1
+ lsrl #6,%d1 //put precision in upper word
+ swap %d1
+ orl %d0,%d1 //set up for round call
+ movel #0x20000000,%d0 //set sticky for round
+ bclrb #sign_bit,FPTEMP_EX(%a6)
+ sne FPTEMP_SGN(%a6)
+ bsrl round //round result to users rmode & prec
+ bfclr FPTEMP_SGN(%a6){#0:#8} //convert back to IEEE ext format
+ beqs sub_s_sclr
+ bsetb #sign_bit,FPTEMP_EX(%a6)
+sub_s_sclr:
+ leal WBTEMP(%a6),%a0
+ movel FPTEMP(%a6),(%a0) //write result to wbtemp
+ movel FPTEMP_HI(%a6),4(%a0)
+ movel FPTEMP_LO(%a6),8(%a0)
+ tstw FPTEMP_EX(%a6)
+ bgt sub_ckovf
+ orl #neg_mask,USER_FPSR(%a6)
+sub_ckovf:
+ movew WBTEMP_EX(%a6),%d0
+ andiw #0x7fff,%d0
+ cmpiw #0x7fff,%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.)
+//
+ orl #inf_mask+ovfl_inx_mask,USER_FPSR(%a6)
+ clrl 4(%a0)
+ bra frcfpnr
+//
+// Inst is fcmp.
+//
+wrap_cmp:
+ cmpb #0xff,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.
+//
+ cmpb #0x0f,DNRM_FLG(%a6) //check for dest denorm
+ bnes cmp_srcd
+cmp_destd:
+ bsrl 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)
+ subl %d1,%d0 //subtract dest from src
+ cmpl #0x8000,%d0
+ blt fix_stk //if less, not wrap case
+ tstw ETEMP_EX(%a6) //set N to ~sign_of(src)
+ bge cmp_setn
+ rts
+cmp_srcd:
+ bsrl 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)
+ subl %d1,%d0 //subtract src from dest
+ cmpl #0x8000,%d0
+ blt fix_stk //if less, not wrap case
+ tstw FPTEMP_EX(%a6) //set N to sign_of(dest)
+ blt cmp_setn
+ rts
+cmp_setn:
+ orl #neg_mask,USER_FPSR(%a6)
+ rts
+
+//
+// Inst is fmul.
+//
+wrap_mul:
+ cmpb #0xff,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.
+//
+ cmpb #0x0f,DNRM_FLG(%a6) //check for dest denorm
+ bnes mul_srcd
+mul_destd:
+ bsrl 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)
+ addl %d1,%d0 //subtract dest from src
+ bgt fix_stk
+ bra force_unf
+mul_srcd:
+ bsrl 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)
+ addl %d1,%d0 //subtract src from dest
+ bgt fix_stk
+
+//
+// This code handles the case of the instruction resulting in
+// an underflow condition.
+//
+force_unf:
+ bclrb #E1,E_BYTE(%a6)
+ orl #unfinx_mask,USER_FPSR(%a6)
+ clrw NMNEXC(%a6)
+ clrb WBTEMP_SGN(%a6)
+ movew ETEMP_EX(%a6),%d0 //find the sign of the result
+ movew FPTEMP_EX(%a6),%d1
+ eorw %d1,%d0
+ andiw #0x8000,%d0
+ beqs frcunfcont
+ st WBTEMP_SGN(%a6)
+frcunfcont:
+ lea WBTEMP(%a6),%a0 //point a0 to memory location
+ movew CMDREG1B(%a6),%d0
+ btstl #6,%d0 //test for forced precision
+ beqs frcunf_fpcr
+ btstl #2,%d0 //check for double
+ bnes frcunf_dbl
+ movel #0x1,%d0 //inst is forced single
+ bras frcunf_rnd
+frcunf_dbl:
+ movel #0x2,%d0 //inst is forced double
+ bras frcunf_rnd
+frcunf_fpcr:
+ bfextu FPCR_MODE(%a6){#0:#2},%d0 //inst not forced - use fpcr prec
+frcunf_rnd:
+ bsrl 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
+ beqs frcfpn
+ bsetb #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:
+ movew CMDREG1B(%a6),%d0
+ btstl #6,%d0 //test for forced precision
+ beqs frcfpn_fpcr
+ btstl #2,%d0 //check for double
+ bnes frcfpn_dbl
+ movel #0x1,%d0 //inst is forced single
+ bras frcfpn_rnd
+frcfpn_dbl:
+ movel #0x2,%d0 //inst is forced double
+ bras frcfpn_rnd
+frcfpn_fpcr:
+ bfextu FPCR_MODE(%a6){#0:#2},%d0 //inst not forced - use fpcr prec
+ tstb %d0
+ beqs frcfpn //if extended, write what you got
+frcfpn_rnd:
+ bclrb #sign_bit,WBTEMP_EX(%a6)
+ sne WBTEMP_SGN(%a6)
+ bsrl 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
+ beqs frcfpn_clr
+ bsetb #sign_bit,WBTEMP_EX(%a6)
+frcfpn_clr:
+ orl #ovfinx_mask,USER_FPSR(%a6)
+//
+// Perform the write.
+//
+frcfpn:
+ bfextu CMDREG1B(%a6){#6:#3},%d0 //extract fp destination register
+ cmpib #3,%d0
+ bles frc0123 //check if dest is fp0-fp3
+ movel #7,%d1
+ subl %d0,%d1
+ clrl %d0
+ bsetl %d1,%d0
+ fmovemx WBTEMP(%a6),%d0
+ rts
+frc0123:
+ cmpib #0,%d0
+ beqs frc0_dst
+ cmpib #1,%d0
+ beqs frc1_dst
+ cmpib #2,%d0
+ beqs frc2_dst
+frc3_dst:
+ movel WBTEMP_EX(%a6),USER_FP3(%a6)
+ movel WBTEMP_HI(%a6),USER_FP3+4(%a6)
+ movel WBTEMP_LO(%a6),USER_FP3+8(%a6)
+ rts
+frc2_dst:
+ movel WBTEMP_EX(%a6),USER_FP2(%a6)
+ movel WBTEMP_HI(%a6),USER_FP2+4(%a6)
+ movel WBTEMP_LO(%a6),USER_FP2+8(%a6)
+ rts
+frc1_dst:
+ movel WBTEMP_EX(%a6),USER_FP1(%a6)
+ movel WBTEMP_HI(%a6),USER_FP1+4(%a6)
+ movel WBTEMP_LO(%a6),USER_FP1+8(%a6)
+ rts
+frc0_dst:
+ movel WBTEMP_EX(%a6),USER_FP0(%a6)
+ movel WBTEMP_HI(%a6),USER_FP0+4(%a6)
+ movel 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:
+ btstb #snan_bit,FPSR_EXCEPT(%a6) //if snan is set, and
+ beqs fmoveinc //enabled, force restore
+ btstb #snan_bit,FPCR_ENABLE(%a6) //and don't overwrite
+ beqs fmoveinc //the dest
+ movel ETEMP_EX(%a6),FPTEMP_EX(%a6) //set up fptemp sign for
+// ;snan handler
+ tstb ETEMP(%a6) //check for negative
+ blts snan_neg
+ rts
+snan_neg:
+ orl #neg_bit,USER_FPSR(%a6) //snan is negative; set N
+ rts
+fmoveinc:
+ clrw NMNEXC(%a6)
+ bclrb #E1,E_BYTE(%a6)
+ moveb STAG(%a6),%d0 //check if stag is inf
+ andib #0xe0,%d0
+ cmpib #0x40,%d0
+ bnes fminc_cnan
+ orl #inf_mask,USER_FPSR(%a6) //if inf, nothing yet has set I
+ tstw LOCAL_EX(%a0) //check sign
+ bges fminc_con
+ orl #neg_mask,USER_FPSR(%a6)
+ bra fminc_con
+fminc_cnan:
+ cmpib #0x60,%d0 //check if stag is NaN
+ bnes fminc_czero
+ orl #nan_mask,USER_FPSR(%a6) //if nan, nothing yet has set NaN
+ movel ETEMP_EX(%a6),FPTEMP_EX(%a6) //set up fptemp sign for
+// ;snan handler
+ tstw LOCAL_EX(%a0) //check sign
+ bges fminc_con
+ orl #neg_mask,USER_FPSR(%a6)
+ bra fminc_con
+fminc_czero:
+ cmpib #0x20,%d0 //check if zero
+ bnes fminc_con
+ orl #z_mask,USER_FPSR(%a6) //if zero, set Z
+ tstw LOCAL_EX(%a0) //check sign
+ bges fminc_con
+ orl #neg_mask,USER_FPSR(%a6)
+fminc_con:
+ bfextu CMDREG1B(%a6){#6:#3},%d0 //extract fp destination register
+ cmpib #3,%d0
+ bles fp0123 //check if dest is fp0-fp3
+ movel #7,%d1
+ subl %d0,%d1
+ clrl %d0
+ bsetl %d1,%d0
+ fmovemx ETEMP(%a6),%d0
+ rts
+
+fp0123:
+ cmpib #0,%d0
+ beqs fp0_dst
+ cmpib #1,%d0
+ beqs fp1_dst
+ cmpib #2,%d0
+ beqs fp2_dst
+fp3_dst:
+ movel ETEMP_EX(%a6),USER_FP3(%a6)
+ movel ETEMP_HI(%a6),USER_FP3+4(%a6)
+ movel ETEMP_LO(%a6),USER_FP3+8(%a6)
+ rts
+fp2_dst:
+ movel ETEMP_EX(%a6),USER_FP2(%a6)
+ movel ETEMP_HI(%a6),USER_FP2+4(%a6)
+ movel ETEMP_LO(%a6),USER_FP2+8(%a6)
+ rts
+fp1_dst:
+ movel ETEMP_EX(%a6),USER_FP1(%a6)
+ movel ETEMP_HI(%a6),USER_FP1+4(%a6)
+ movel ETEMP_LO(%a6),USER_FP1+8(%a6)
+ rts
+fp0_dst:
+ movel ETEMP_EX(%a6),USER_FP0(%a6)
+ movel ETEMP_HI(%a6),USER_FP0+4(%a6)
+ movel ETEMP_LO(%a6),USER_FP0+8(%a6)
+ rts
+
+opclass3:
+ st CU_ONLY(%a6)
+ movew CMDREG1B(%a6),%d0 //check if packed moveout
+ andiw #0x0c00,%d0 //isolate last 2 bits of size field
+ cmpiw #0x0c00,%d0 //if size is 011 or 111, it is packed
+ beq pack_out //else it is norm or denorm
+ bra mv_out
+
+
+//
+// MOVE OUT
+//
+
+mv_tbl:
+ .long li
+ .long sgp
+ .long xp
+ .long mvout_end //should never be taken
+ .long wi
+ .long dp
+ .long bi
+ .long mvout_end //should never be taken
+mv_out:
+ bfextu CMDREG1B(%a6){#3:#3},%d1 //put source specifier in d1
+ leal mv_tbl,%a0
+ movel %a0@(%d1:l: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:
+ btstb #inex2_bit,FPSR_EXCEPT(%a6)
+ beqs no_aufl
+ btstb #unfl_bit,FPSR_EXCEPT(%a6)
+ beqs no_aufl
+ bsetb #aunfl_bit,FPSR_AEXCEPT(%a6)
+no_aufl:
+ clrw NMNEXC(%a6)
+ bclrb #E1,E_BYTE(%a6)
+ fmovel #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}
+ beqs mvout_con
+ bsetb #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:
+ clrw NMNEXC(%a6)
+ bclrb #E1,E_BYTE(%a6)
+ fmovel #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}
+ beqs mvouti_con
+ bsetb #sign_bit,ETEMP_EX(%a6)
+mvouti_con:
+ rts
+//
+// li is used to handle a long integer source specifier
+//
+
+li:
+ moveql #4,%d0 //set byte count
+
+ btstb #7,STAG(%a6) //check for extended denorm
+ bne int_dnrm //if so, branch
+
+ fmovemx ETEMP(%a6),%fp0-%fp0
+ fcmpd #0x41dfffffffc00000,%fp0
+// 41dfffffffc00000 in dbl prec = 401d0000fffffffe00000000 in ext prec
+ fbge lo_plrg
+ fcmpd #0xc1e0000000000000,%fp0
+// c1e0000000000000 in dbl prec = c01e00008000000000000000 in ext prec
+ fble lo_nlrg
+//
+// at this point, the answer is between the largest pos and neg values
+//
+ movel USER_FPCR(%a6),%d1 //use user's rounding mode
+ andil #0x30,%d1
+ fmovel %d1,%fpcr
+ fmovel %fp0,L_SCR1(%a6) //let the 040 perform conversion
+ fmovel %fpsr,%d1
+ orl %d1,USER_FPSR(%a6) //capture inex2/ainex if set
+ bra int_wrt
+
+
+lo_plrg:
+ movel #0x7fffffff,L_SCR1(%a6) //answer is largest positive int
+ fbeq int_wrt //exact answer
+ fcmpd #0x41dfffffffe00000,%fp0
+// 41dfffffffe00000 in dbl prec = 401d0000ffffffff00000000 in ext prec
+ fbge int_operr //set operr
+ bra int_inx //set inexact
+
+lo_nlrg:
+ movel #0x80000000,L_SCR1(%a6)
+ fbeq int_wrt //exact answer
+ fcmpd #0xc1e0000000100000,%fp0
+// c1e0000000100000 in dbl prec = c01e00008000000080000000 in ext prec
+ fblt int_operr //set operr
+ bra int_inx //set inexact
+
+//
+// wi is used to handle a word integer source specifier
+//
+
+wi:
+ moveql #2,%d0 //set byte count
+
+ btstb #7,STAG(%a6) //check for extended denorm
+ bne int_dnrm //branch if so
+
+ fmovemx ETEMP(%a6),%fp0-%fp0
+ fcmps #0x46fffe00,%fp0
+// 46fffe00 in sgl prec = 400d0000fffe000000000000 in ext prec
+ fbge wo_plrg
+ fcmps #0xc7000000,%fp0
+// c7000000 in sgl prec = c00e00008000000000000000 in ext prec
+ fble wo_nlrg
+
+//
+// at this point, the answer is between the largest pos and neg values
+//
+ movel USER_FPCR(%a6),%d1 //use user's rounding mode
+ andil #0x30,%d1
+ fmovel %d1,%fpcr
+ fmovew %fp0,L_SCR1(%a6) //let the 040 perform conversion
+ fmovel %fpsr,%d1
+ orl %d1,USER_FPSR(%a6) //capture inex2/ainex if set
+ bra int_wrt
+
+wo_plrg:
+ movew #0x7fff,L_SCR1(%a6) //answer is largest positive int
+ fbeq int_wrt //exact answer
+ fcmps #0x46ffff00,%fp0
+// 46ffff00 in sgl prec = 400d0000ffff000000000000 in ext prec
+ fbge int_operr //set operr
+ bra int_inx //set inexact
+
+wo_nlrg:
+ movew #0x8000,L_SCR1(%a6)
+ fbeq int_wrt //exact answer
+ fcmps #0xc7000080,%fp0
+// c7000080 in sgl prec = c00e00008000800000000000 in ext prec
+ fblt int_operr //set operr
+ bra int_inx //set inexact
+
+//
+// bi is used to handle a byte integer source specifier
+//
+
+bi:
+ moveql #1,%d0 //set byte count
+
+ btstb #7,STAG(%a6) //check for extended denorm
+ bne int_dnrm //branch if so
+
+ fmovemx ETEMP(%a6),%fp0-%fp0
+ fcmps #0x42fe0000,%fp0
+// 42fe0000 in sgl prec = 40050000fe00000000000000 in ext prec
+ fbge by_plrg
+ fcmps #0xc3000000,%fp0
+// c3000000 in sgl prec = c00600008000000000000000 in ext prec
+ fble by_nlrg
+
+//
+// at this point, the answer is between the largest pos and neg values
+//
+ movel USER_FPCR(%a6),%d1 //use user's rounding mode
+ andil #0x30,%d1
+ fmovel %d1,%fpcr
+ fmoveb %fp0,L_SCR1(%a6) //let the 040 perform conversion
+ fmovel %fpsr,%d1
+ orl %d1,USER_FPSR(%a6) //capture inex2/ainex if set
+ bra int_wrt
+
+by_plrg:
+ moveb #0x7f,L_SCR1(%a6) //answer is largest positive int
+ fbeq int_wrt //exact answer
+ fcmps #0x42ff0000,%fp0
+// 42ff0000 in sgl prec = 40050000ff00000000000000 in ext prec
+ fbge int_operr //set operr
+ bra int_inx //set inexact
+
+by_nlrg:
+ moveb #0x80,L_SCR1(%a6)
+ fbeq int_wrt //exact answer
+ fcmps #0xc3008000,%fp0
+// c3008000 in sgl prec = c00600008080000000000000 in ext prec
+ fblt int_operr //set operr
+ bra 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:
+ movel #0,L_SCR1(%a6) // initialize result to 0
+ bfextu FPCR_MODE(%a6){#2:#2},%d1 // d1 is the rounding mode
+ cmpb #2,%d1
+ bmis int_inx // if RN or RZ, done
+ bnes int_rp // if RP, continue below
+ tstw ETEMP(%a6) // RM: store -1 in L_SCR1 if src is negative
+ bpls int_inx // otherwise result is 0
+ movel #-1,L_SCR1(%a6)
+ bras int_inx
+int_rp:
+ tstw ETEMP(%a6) // RP: store +1 of proper width in L_SCR1 if
+// ; source is greater than 0
+ bmis int_inx // otherwise, result is 0
+ lea L_SCR1(%a6),%a1 // a1 is address of L_SCR1
+ addal %d0,%a1 // offset by destination width -1
+ subal #1,%a1
+ bsetb #0,(%a1) // set low bit at a1 address
+int_inx:
+ oril #inx2a_mask,USER_FPSR(%a6)
+ bras int_wrt
+int_operr:
+ fmovemx %fp0-%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)
+
+ oril #opaop_mask,USER_FPSR(%a6)
+// ;fall through to perform int_wrt
+int_wrt:
+ movel EXC_EA(%a6),%a1 //load destination address
+ tstl %a1 //check to see if it is a dest register
+ beqs wrt_dn //write data register
+ lea L_SCR1(%a6),%a0 //point to supervisor source address
+ bsrl mem_write
+ bra mvouti_end
+
+wrt_dn:
+ movel %d0,-(%sp) //d0 currently contains the size to write
+ bsrl get_fline //get_fline returns Dn in d0
+ andiw #0x7,%d0 //isolate register
+ movel (%sp)+,%d1 //get size
+ cmpil #4,%d1 //most frequent case
+ beqs sz_long
+ cmpil #2,%d1
+ bnes sz_con
+ orl #8,%d0 //add 'word' size to register#
+ bras sz_con
+sz_long:
+ orl #0x10,%d0 //add 'long' size to register#
+sz_con:
+ movel %d0,%d1 //reg_dest expects size:reg in d1
+ bsrl reg_dest //load proper data register
+ bra mvouti_end
+xp:
+ lea ETEMP(%a6),%a0
+ bclrb #sign_bit,LOCAL_EX(%a0)
+ sne LOCAL_SGN(%a0)
+ btstb #7,STAG(%a6) //check for extended denorm
+ bne xdnrm
+ clrl %d0
+ bras do_fp //do normal case
+sgp:
+ lea ETEMP(%a6),%a0
+ bclrb #sign_bit,LOCAL_EX(%a0)
+ sne LOCAL_SGN(%a0)
+ btstb #7,STAG(%a6) //check for extended denorm
+ bne sp_catas //branch if so
+ movew LOCAL_EX(%a0),%d0
+ lea sp_bnds,%a1
+ cmpw (%a1),%d0
+ blt sp_under
+ cmpw 2(%a1),%d0
+ bgt sp_over
+ movel #1,%d0 //set destination format to single
+ bras do_fp //do normal case
+dp:
+ lea ETEMP(%a6),%a0
+ bclrb #sign_bit,LOCAL_EX(%a0)
+ sne LOCAL_SGN(%a0)
+
+ btstb #7,STAG(%a6) //check for extended denorm
+ bne dp_catas //branch if so
+
+ movew LOCAL_EX(%a0),%d0
+ lea dp_bnds,%a1
+
+ cmpw (%a1),%d0
+ blt dp_under
+ cmpw 2(%a1),%d0
+ bgt dp_over
+
+ movel #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
+ addl %d0,%d1 //d1 has PREC/MODE info
+
+ clrl %d0 //clear g,r,s
+
+ bsrl round //round
+
+ movel %a0,%a1
+ movel 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
+ cmpb #2,%d1
+ bgts ddbl //double=5, extended=2, single=1
+ bnes dsgl
+// ;fall through to dext
+dext:
+ bsrl dest_ext
+ bra mvout_end
+dsgl:
+ bsrl dest_sgl
+ bra mvout_end
+ddbl:
+ bsrl dest_dbl
+ bra mvout_end
+
+//
+// Handle possible denorm or catastrophic underflow cases here
+//
+xdnrm:
+ bsr set_xop //initialize WBTEMP
+ bsetb #wbtemp15_bit,WB_BYTE(%a6) //set wbtemp15
+
+ movel %a0,%a1
+ movel EXC_EA(%a6),%a0 //a0 has the destination pointer
+ bsrl dest_ext //store to memory
+ bsetb #unfl_bit,FPSR_EXCEPT(%a6)
+ bra mvout_end
+
+sp_under:
+ bsetb #etemp15_bit,STAG(%a6)
+
+ cmpw 4(%a1),%d0
+ blts sp_catas //catastrophic underflow case
+
+ movel #1,%d0 //load in round precision
+ movel #sgl_thresh,%d1 //load in single denorm threshold
+ bsrl dpspdnrm //expects d1 to have the proper
+// ;denorm threshold
+ bsrl dest_sgl //stores value to destination
+ bsetb #unfl_bit,FPSR_EXCEPT(%a6)
+ bra mvout_end //exit
+
+dp_under:
+ bsetb #etemp15_bit,STAG(%a6)
+
+ cmpw 4(%a1),%d0
+ blts dp_catas //catastrophic underflow case
+
+ movel #dbl_thresh,%d1 //load in double precision threshold
+ movel #2,%d0
+ bsrl dpspdnrm //expects d1 to have proper
+// ;denorm threshold
+// ;expects d0 to have round precision
+ bsrl dest_dbl //store value to destination
+ bsetb #unfl_bit,FPSR_EXCEPT(%a6)
+ bra mvout_end //exit
+
+//
+// Handle catastrophic underflow cases here
+//
+sp_catas:
+// Temp fix for z bit set in unf_sub
+ movel USER_FPSR(%a6),-(%a7)
+
+ movel #1,%d0 //set round precision to sgl
+
+ bsrl unf_sub //a0 points to result
+
+ movel (%a7)+,USER_FPSR(%a6)
+
+ movel #1,%d0
+ subw %d0,LOCAL_EX(%a0) //account for difference between
+// ;denorm/norm bias
+
+ movel %a0,%a1 //a1 has the operand input
+ movel EXC_EA(%a6),%a0 //a0 has the destination pointer
+
+ bsrl dest_sgl //store the result
+ oril #unfinx_mask,USER_FPSR(%a6)
+ bra mvout_end
+
+dp_catas:
+// Temp fix for z bit set in unf_sub
+ movel USER_FPSR(%a6),-(%a7)
+
+ movel #2,%d0 //set round precision to dbl
+ bsrl unf_sub //a0 points to result
+
+ movel (%a7)+,USER_FPSR(%a6)
+
+ movel #1,%d0
+ subw %d0,LOCAL_EX(%a0) //account for difference between
+// ;denorm/norm bias
+
+ movel %a0,%a1 //a1 has the operand input
+ movel EXC_EA(%a6),%a0 //a0 has the destination pointer
+
+ bsrl dest_dbl //store the result
+ oril #unfinx_mask,USER_FPSR(%a6)
+ bra mvout_end
+
+//
+// Handle catastrophic overflow cases here
+//
+sp_over:
+// Temp fix for z bit set in unf_sub
+ movel USER_FPSR(%a6),-(%a7)
+
+ movel #1,%d0
+ leal FP_SCR1(%a6),%a0 //use FP_SCR1 for creating result
+ movel ETEMP_EX(%a6),(%a0)
+ movel ETEMP_HI(%a6),4(%a0)
+ movel ETEMP_LO(%a6),8(%a0)
+ bsrl ovf_res
+
+ movel (%a7)+,USER_FPSR(%a6)
+
+ movel %a0,%a1
+ movel EXC_EA(%a6),%a0
+ bsrl dest_sgl
+ orl #ovfinx_mask,USER_FPSR(%a6)
+ bra mvout_end
+
+dp_over:
+// Temp fix for z bit set in ovf_res
+ movel USER_FPSR(%a6),-(%a7)
+
+ movel #2,%d0
+ leal FP_SCR1(%a6),%a0 //use FP_SCR1 for creating result
+ movel ETEMP_EX(%a6),(%a0)
+ movel ETEMP_HI(%a6),4(%a0)
+ movel ETEMP_LO(%a6),8(%a0)
+ bsrl ovf_res
+
+ movel (%a7)+,USER_FPSR(%a6)
+
+ movel %a0,%a1
+ movel EXC_EA(%a6),%a0
+ bsrl dest_dbl
+ orl #ovfinx_mask,USER_FPSR(%a6)
+ bra 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:
+ movel %d0,-(%a7) //save round precision
+ clrl %d0 //clear initial g,r,s
+ bsrl dnrm_lp //careful with d0, it's needed by round
+
+ bfextu FPCR_MODE(%a6){#2:#2},%d1 //get rounding mode
+ swap %d1
+ movew 2(%a7),%d1 //set rounding precision
+ swap %d1 //at this point d1 has PREC/MODE info
+ bsrl round //round result, sets the inex bit in
+// ;USER_FPSR if needed
+
+ movew #1,%d0
+ subw %d0,LOCAL_EX(%a0) //account for difference in denorm
+// ;vs norm bias
+
+ movel %a0,%a1 //a1 has the operand input
+ movel EXC_EA(%a6),%a0 //a0 has the destination pointer
+ addw #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:
+ movel LOCAL_EX(%a0),WBTEMP_EX(%a6)
+ movel LOCAL_HI(%a0),WBTEMP_HI(%a6)
+ movel LOCAL_LO(%a0),WBTEMP_LO(%a6)
+ bfclr WBTEMP_SGN(%a6){#0:#8}
+ beqs sxop
+ bsetb #sign_bit,WBTEMP_EX(%a6)
+sxop:
+ bfclr STAG(%a6){#5:#4} //clear wbtm66,wbtm1,wbtm0,sbit
+ rts
+//
+// P_MOVE
+//
+p_movet:
+ .long p_move
+ .long p_movez
+ .long p_movei
+ .long p_moven
+ .long p_move
+p_regd:
+ .long p_dyd0
+ .long p_dyd1
+ .long p_dyd2
+ .long p_dyd3
+ .long p_dyd4
+ .long p_dyd5
+ .long p_dyd6
+ .long p_dyd7
+
+pack_out:
+ leal p_movet,%a0 //load jmp table address
+ movew STAG(%a6),%d0 //get source tag
+ bfextu %d0{#16:#3},%d0 //isolate source bits
+ movel (%a0,%d0.w*4),%a0 //load a0 with routine label for tag
+ jmp (%a0) //go to the routine
+
+p_write:
+ movel #0x0c,%d0 //get byte count
+ movel EXC_EA(%a6),%a1 //get the destination address
+ bsr mem_write //write the user's destination
+ moveb #0,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:
+ movew CMDREG1B(%a6),%d0
+ btstl #kfact_bit,%d0 //test for dynamic k-factor
+ beqs statick //if clear, k-factor is static
+dynamick:
+ bfextu %d0{#25:#3},%d0 //isolate register for dynamic k-factor
+ lea p_regd,%a0
+ movel %a0@(%d0:l:4),%a0
+ jmp (%a0)
+statick:
+ andiw #0x007f,%d0 //get k-factor
+ bfexts %d0{#25:#7},%d0 //sign extend d0 for bindec
+ leal ETEMP(%a6),%a0 //a0 will point to the packed decimal
+ bsrl bindec //perform the convert; data at a6
+ leal FP_SCR1(%a6),%a0 //load a0 with result address
+ bral p_write
+p_movez:
+ leal ETEMP(%a6),%a0 //a0 will point to the packed decimal
+ clrw 2(%a0) //clear lower word of exp
+ clrl 4(%a0) //load second lword of ZERO
+ clrl 8(%a0) //load third lword of ZERO
+ bra p_write //go write results
+p_movei:
+ fmovel #0,%FPSR //clear aiop
+ leal ETEMP(%a6),%a0 //a0 will point to the packed decimal
+ clrw 2(%a0) //clear lower word of exp
+ bra p_write //go write the result
+p_moven:
+ leal ETEMP(%a6),%a0 //a0 will point to the packed decimal
+ clrw 2(%a0) //clear lower word of exp
+ bra p_write //go write the result
+
+//
+// Routines to read the dynamic k-factor from Dn.
+//
+p_dyd0:
+ movel USER_D0(%a6),%d0
+ bras statick
+p_dyd1:
+ movel USER_D1(%a6),%d0
+ bras statick
+p_dyd2:
+ movel %d2,%d0
+ bras statick
+p_dyd3:
+ movel %d3,%d0
+ bras statick
+p_dyd4:
+ movel %d4,%d0
+ bras statick
+p_dyd5:
+ movel %d5,%d0
+ bras statick
+p_dyd6:
+ movel %d6,%d0
+ bra statick
+p_dyd7:
+ movel %d7,%d0
+ bra statick
+
+ |end