swh:1:snp:32555a3fd8878f019c2ebd6c964bc1edcaeff337
Tip revision: 42226c989789d8da4af1de0c31070c96726d990c authored by Linus Torvalds on 16 May 2022, 01:08:58 UTC
Linux 5.18-rc7
Linux 5.18-rc7
Tip revision: 42226c9
res_func.S
|
| 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
|
| For details on the license for this file, please see the
| file, README, in this same directory.
RES_FUNC: |idnt 2,1 | Motorola 040 Floating Point Software Package
|section 8
#include "fpsp.h"
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 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