forked from Imagelibrary/rtems
2043 lines
53 KiB
ArmAsm
2043 lines
53 KiB
ArmAsm
#include "fpsp-namespace.h"
|
|
//
|
|
//
|
|
// 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
|