supermario/base/SuperMarioProj.1994-02-09/OS/FPUEmulation/ResFunc.a

2165 lines
62 KiB
Plaintext
Raw Permalink Normal View History

2019-06-29 15:17:50 +00:00
;
; File: ResFunc.a
;
; Contains: Routines to handle denormalized numbers
;
; Originally Written by: Motorola Inc.
; Adapted to Apple/MPW: Jon Okada
;
; Copyright: © 1990, 1991 by Apple Computer, Inc., all rights reserved.
;
; This file is used in these builds: Mac32
;
; Change History (most recent first):
;
; <5> 7/8/91 BG Modified test for zero result in "cu_wrexn". Zeroed exponent
; field for zero operands in "dyadic" and "monadic" to prevent
; possible exponent-wrap errors. Removed duplicate code from
; "div_destd". After rounding in "force_unf", normalize any
; unnormalized result or force zero exponent for zero result.
; <4> 6/24/91 BG Folded in Motorola version 2.0 bug fixes in routines "force_ovf"
; and "force_unf".
; <3> 5/24/91 BG Several changes (From Jon):
; - Workaround added for FSAVE bug with static k-factor
; on FMOVE.P out to memory
; - Provide correct rounding for FMOVE.B/W/L of denormal
; out to memory (see "int_dnrm:" below)
; - Provide correct rounding for underflow at single/
; double precisions for FMOVE/ABS/NEG when src operand
; is unnormalized extended or denormal single/double
; <2> 3/30/91 BG Rolling in Jon Okada's latest changes.
; <1> 12/14/90 BG First checked into TERROR/BBS.
; resfunc.a
; Based upon Motorola file 'res_func.sa'.
; CHANGE LOG:
; 07 Jan 91 JPO Changed tables "mv_tbl", "p_movet", and "p_regd" to
; contain 16-bit addresses relative to the respective
; table tops. Streamlined routine "wrt_dn" by in-lining
; 'get_fline' functionality. Replaced 'bsr mem_write' by
; in-line code in routine "p_write". Renamed label
; "normal" to "resnormal".
; 29 Jan 91 JPO Changed wrap condition limit for SUBNORMAL/NORMAL under
; 'div_destd' from $8000 to $7fff to avoid spurious
; overflow result.
; 31 Jan 91 JPO Protected wrap checks against INF or NaN inputs for
; FADD, FSUB, FCMP, and FDIV. Protected wrap checks
; against zero input for FMUL.
; 15 Mar 91 JPO For extended denormal operands for FMOVE (to reg), FABS,
; and FNEG with single- or double-precision rounding,
; force results to be true zero (including exponents) or
; normalized extendeds.
; 26 Apr 91 JPO Worked around FSAVE bug for static k-factor(=4) on
; FMOVE.P out to memory.
; 02 May 91 JPO Provide correct rounding for FMOVE.B/W/L of denormal
; out to memory (see "int_dnrm:" below).
; 03 May 91 JPO Provide correct rounding for underflow at single/
; double precisions for FMOVE/ABS/NEG when src
; operand is unnormalized extended or denormal
; single/double.
; 07 Jun 91 JPO Folded in Motorola version 2.0 bug fixes in routines "force_ovf"
; and "force_unf".
; 05 Jul 91 JPO Modified test for zero result in "cu_wrexn". Zeroed exponent
; field for zero operands in "dyadic" and "monadic" to prevent
; possible exponent-wrap errors. Removed duplicate code from
; "div_destd". After rounding in "force_unf", normalize any
; unnormalized result or force zero exponent for zero result.
;
*
* res_func.sa 3.7 4/26/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
ALIGN 16 ; <1/7/91, JPO>
sp_bnds: dc.w $3f81,$407e
dc.w $3f6a,$0000
dp_bnds: dc.w $3c01,$43fe
dc.w $3bcd,$0000
res_func:
clr.b DNRM_FLG(a6)
clr.b RES_FLG(a6)
clr.b CU_ONLY(a6)
tst.b DY_MO_FLG(a6)
beq.b monadic
dyadic:
; btst.b #7,DTAG(a6) ;if dop = norm=000, zero=001, - DELETED <7/5/91, JPO> <T5>
* ;inf=010 or nan=011 <T5>
; beq.b monadic ;then branch - DELETED <7/5/91, JPO> <T5>
* ;else denorm <T5>
bfextu DTAG(a6){0:3},d0 ; extract DTAG <7/5/91, JPO> <T5>
bmi.b @1 ; denorm <7/5/91, JPO> <T5>
cmpi.b #1,d0 ; zero? <7/5/91, JPO> <T5>
bne.b monadic ; no <7/5/91, JPO> <T5>
bfclr FPTEMP(a6){1:15} ; yes. zero dst exponent field <7/5/91, JPO> <T5>
bra.b monadic ; <7/5/91, JPO> <T5>
* HANDLE DESTINATION DENORM HERE <T5>
* ;set dtag to norm <T5>
* ;write the tag & fpte15 to the fstack <T5>
@1: ; label ADDED <7/5/91, JPO> <T5>
lea.l FPTEMP(a6),a0
bclr.b #sign_bit,LOCAL_EX(a0)
sne LOCAL_SGN(a0)
bsr nrm_set ;normalize number (exp will go negative)
bclr.b #sign_bit,LOCAL_EX(a0) ;get rid of false sign
bfclr LOCAL_SGN(a0){0:8} ;change back to IEEE ext format
beq.b dpos
bset.b #sign_bit,LOCAL_EX(a0)
dpos:
bfclr DTAG(a6){0:4} ;set tag to normalized, FPTE15 = 0
bset.b #4,DTAG(a6) ;set FPTE15
or.b #$0f,DNRM_FLG(a6)
monadic:
lea.l ETEMP(a6),a0
btst.b #direction_bit,CMDREG1B(a6) ;check direction
bne.w opclass3 ;it is a mv out
*
* At this point, only oplcass 0 and 2 possible
*
; btst.b #7,STAG(a6) ;if sop = norm=000, zero=001, - DELETED <7/5/91, JPO> <T5>
* ;inf=010 or nan=011 <T5>
; bne.w mon_dnrm ;else denorm - DELETED <T5>
bfextu STAG(a6){0:3},d0 ; extract STAG <7/5/91, JPO> <T5>
bmi mon_dnrm ; denorm <7/5/91, JPO> <T5>
cmpi.b #1,d0 ; zero? <7/5/91, JPO> <T5>
bne.b @1 ; no <7/5/91, JPO> <T5>
bfclr ETEMP(a6){1:15} ; yes. zero src exponent field <7/5/91, JPO> <T5>
@1: ; label ADDED <7/5/91, JPO> <T5>
tst.b DY_MO_FLG(a6) ;all cases of dyadic instructions would
bne.w resnormal ;require normalization of denorm - label RENAMED <1/7/91, JPO>
* At this point:
* monadic instructions: fabs = $18 fneg = $1a ftst = $3a
* fmove = $00 fsmove = $40 fdmove = $44
* fsqrt = $05* fssqrt = $41 fdsqrt = $45
* (*fsqrt reencoded to $05)
*
move.w CMDREG1B(a6),d0 ;get command register
andi.l #$7f,d0 ;strip to only command word
*
* At this point, fabs, fneg, fsmove, fdmove, ftst, fsqrt, fssqrt, and
* fdsqrt are possible.
* For cases fabs, fneg, fsmove, and fdmove goto spos (do not normalize)
* For cases fsqrt, fssqrt, and fdsqrt goto nrm_src (do normalize)
*
btst.l #0,d0
bne.w resnormal ;weed out fsqrt instructions - label RENAMED <1/7/91, JPO>
*
* cu_norm handles fmove in instructions with normalized inputs.
* The routine round is used to correctly round the input for the
* destination precision and mode.
*
cu_norm:
st CU_ONLY(a6) ;set cu-only inst flag
move.w CMDREG1B(a6),d0
andi.b #$3b,d0 ;isolate bits to select inst
tst.b d0
beq cu_nmove ;if zero, it is an fmove
cmpi.b #$18,d0
beq.b cu_nabs ;if $18, it is fabs
cmpi.b #$1a,d0
beq.b cu_nneg ;if $1a, it is fneg
*
* Inst is ftst. Check the source operand and set the cc's accordingly.
* No write is done, so simply rts.
*
cu_ntst:
move.w LOCAL_EX(a0),d0
bclr.l #15,d0
sne LOCAL_SGN(a0)
beq.b cu_ntpo
or.l #neg_mask,USER_FPSR(a6) ;set N
cu_ntpo:
cmpi.w #$7fff,d0 ;test for inf/nan
bne.b cu_ntcz
tst.l LOCAL_HI(a0)
bne.b cu_ntn
tst.l LOCAL_LO(a0)
bne.b cu_ntn
or.l #inf_mask,USER_FPSR(a6)
rts
cu_ntn:
or.l #nan_mask,USER_FPSR(a6)
move.l ETEMP_EX(a6),FPTEMP_EX(a6) ;set up fptemp sign for
* ;snan handler
rts
cu_ntcz:
tst.l LOCAL_HI(a0)
bne.b cu_ntsx
tst.l LOCAL_LO(a0)
bne.b cu_ntsx
or.l #z_mask,USER_FPSR(a6)
cu_ntsx:
rts
*
* Inst is fabs. Execute the absolute value function on the input.
* Branch to the fmove code. If the operand is NaN, do nothing.
*
cu_nabs:
move.b STAG(a6),d0
btst.l #5,d0 ;test for NaN or zero
bne wr_etemp ;if either, simply write it
bclr.b #7,LOCAL_EX(a0) ;do abs
bra.b cu_nmove ;fmove code will finish
*
* Inst is fneg. Execute the negate value function on the input.
* Fall though to the fmove code. If the operand is NaN, do nothing.
*
cu_nneg:
move.b STAG(a6),d0
btst.l #5,d0 ;test for NaN or zero
bne wr_etemp ;if either, simply write it
bchg.b #7,LOCAL_EX(a0) ;do neg
*
* Inst is fmove. This code also handles all result writes.
* If bit 2 is set, round is forced to double. If it is clear,
* and bit 6 is set, round is forced to single. If both are clear,
* the round precision is found in the fpcr. If the rounding precision
* is double or single, round the result before the write.
*
cu_nmove:
move.b STAG(a6),d0
andi.b #$e0,d0 ;isolate stag bits
bne wr_etemp ;if not norm, simply write it
btst.b #2,CMDREG1B+1(a6) ;check for rd
bne.b cu_nmrd
btst.b #6,CMDREG1B+1(a6) ;check for rs
bne.b cu_nmrs
*
* The move or operation is not with forced precision. Test for
* nan or inf as the input; if so, simply write it to FPn. Use the
* FPCR_MODE byte to get rounding on norms and zeros.
*
cu_nmnr:
bfextu FPCR_MODE(a6){0:2},d0
; tst.b d0 ;check for extended - DELETED <5/3/91, JPO> <T3>
beq cu_wrexn ;if so, just write result
cmpi.b #1,d0 ;check for single
beq.b cu_nmrs ;fall through to double
*
* The move is fdmove or round precision is double.
*
cu_nmrd:
bfextu FPCR_MODE(a6){2:2},d1 ;get rmode
or.l #$00020000,d1 ;force double
move.l d1,-(sp) ; push rounding modes for round <5/3/91, JPO> <T3>
; clr.l d0 ;clr grs for round - DELETED <5/3/91, JPO> <T3>
bclr.b #sign_bit,LOCAL_EX(a0)
sne LOCAL_SGN(a0)
moveq.l #2,d0 ; set up size for denorm <5/3/91, JPO> <T3>
move.w LOCAL_EX(a0),d1 ; check for underflow <5/3/91, JPO> <T3>
cmp.w #$3c01,d1 ; <5/3/91, JPO> <T3>
blt.b cu_nunfl ; underflow <5/3/91, JPO> <T3>
move.l (sp)+,d1 ; pop rounding modes <5/3/91, JPO> <T3>
clr.l d0 ; zero grs for round <5/3/91, JPO> <T3>
bsr round ;perform the round
bfclr LOCAL_SGN(a0){0:8}
beq.b cu_nmrdc
bset.b #sign_bit,LOCAL_EX(a0)
cu_nmrdc:
; move.l #2,d0 ;set up size for denorm - DELETED <5/3/91, JPO> <T3>
move.w LOCAL_EX(a0),d1
and.w #$7FFF,d1
; cmp.w #$3c01,d1 ; DELETED <5/3/91, JPO> <T3>
; bls.b cu_nunfl ; DELETED <5/3/91, JPO> <T3>
cmp.w #$43ff,d1
bge.b cu_novfl
bra.w cu_wrexn
*
* The move is fsmove or round precision is single.
*
cu_nmrs:
bfextu FPCR_MODE(a6){2:2},d1 ;get rmode
or.l #$00010000,d1 ;force single
move.l d1,-(sp) ; push rounding modes for round <5/3/91, JPO> <T3>
; clr.l d0 ;clr grs for round - DELETED <5/3/91, JPO> <T3>
bclr.b #sign_bit,LOCAL_EX(a0)
sne LOCAL_SGN(a0)
moveq.l #1,d0 ; set up size for denorm <5/3/91, JPO> <T3>
move.w LOCAL_EX(a0),d1 ; check for underflow <5/3/91, JPO> <T3>
cmp.w #$3f81,d1 ; <5/3/91, JPO> <T3>
blt.b cu_nunfl ; underflow <5/3/91, JPO> <T3>
move.l (sp)+,d1 ; pop rounding modes <5/3/91, JPO> <T3>
clr.l d0 ; zero grs for round <5/3/91, JPO> <T3>
bsr round ;perform the round
bfclr LOCAL_SGN(a0){0:8}
beq.b cu_nmrsc
bset.b #sign_bit,LOCAL_EX(a0)
cu_nmrsc:
; move.l #1,d0 ;set up size for denorm - DELETED <5/3/91, JPO> <T3>
move.w LOCAL_EX(a0),d1
and.w #$7FFF,d1
; cmp.w #$3f81,d1 ; DELETED <5/3/91, JPO> <T3>
; bls.b cu_nunfl ; DELETED <5/3/91, JPO> <T3>
cmp.w #$407f,d1
blt.b cu_wrexn
*
* The operand is above precision boundaries. Use t_ovfl to
* generate the correct value.
*
cu_novfl:
bsr t_ovfl
bra.b cu_wrexn ; <T3> thru next <T3>
*
* The operand is below precision boundaries. Use denorm and round to
* generate the correct value. Rounding precision in d0 and rounding
* modes (precision/direction) at top of stack upon entry. Normalized
* extended operand is in internal format at (a0) = ETEMP. <5/3/91, JPO>
*
cu_nunfl:
; bclr.b #sign_bit,LOCAL_EX(a0) ; DELETED <5/3/91, JPO>
; sne LOCAL_SGN(a0) ; DELETED <5/3/91, JPO>
bsr denorm
move.l (sp)+,d1 ; pop rounding modes <5/3/91, JPO>
bsr round ; round <5/3/91, JPO> <T3>
bfclr LOCAL_SGN(a0){0:8} ;change back to IEEE ext format
beq.b cu_nuflp
bset.b #sign_bit,LOCAL_EX(a0)
cu_nuflp:
or.l #unfl_mask,USER_FPSR(a6) ; set UNFL <5/3/91, JPO> <T3>
btst.b #inex2_bit,FPSR_EXCEPT(a6)
beq.b cu_nuninx
or.l #aunfl_mask,USER_FPSR(a6) ;if the round was inex, set AUNFL
cu_nuninx:
tst.l LOCAL_HI(a0) ;test for zero
bne.b cu_nunzro
tst.l LOCAL_LO(a0)
bne.b cu_nunzro
; The result has underflowed to zero. Zero the exponent, set appropriate <T3> thru next <T3>
; FPCC bits, and write result. <5/3/91, JPO>
andi.w #$8000,(a0) ; zero exponent <5/3/91, JPO>
bra.b cu_wrzero ; set FPCC bits
; The underflow result is nonzero. Normalize it, set appropriate FPCC
; bits, and write result. Normalization via nrm_set is safe to use
; because result is denormal single or double value <5/3/91, JPO>.
cu_nunzro: ; label moved <5/3/91, JPO>
bsr nrm_set ; normalize result <5/3/91, JPO>
bra.b cu_wreon ; check FPCC
; The following code has been obsoleted by the new code flow for
; detecting and obtaining underflow results for FMOVE/ABS/NEG with
; narrower than extended precision. <5/3/91, JPO> <T3>
*
* The mantissa is zero from the denorm loop. Check sign and rmode
* to see if rounding should have occured which would leave the lsb.
*
; move.l USER_FPCR(a6),d0 <T3> thru next <T3>
; andi.l #$30,d0 ;isolate rmode
; cmpi.l #$20,d0
; blt.b cu_nzro
; bne.b cu_nrp
;cu_nrm:
; tst.w LOCAL_EX(a0) ;if positive, set lsb
; bge.b cu_nzro
; btst.b #7,FPCR_MODE(a6) ;check for double
; beq.b cu_nincs
; bra.b cu_nincd
;cu_nrp:
; tst.w LOCAL_EX(a0) ;if positive, set lsb
; blt.b cu_nzro
; btst.b #7,FPCR_MODE(a6) ;check for double
; beq.b cu_nincs
;cu_nincd:
; or.l #$800,LOCAL_HI(a0) ;inc for double
; bra.b cu_nunzro
;cu_nincs:
; or.l #$100,LOCAL_HI(a0) ;inc for single
; bra.b cu_nunzro
;cu_nzro:
; or.l #z_mask,USER_FPSR(a6)
; move.b STAG(a6),d0
; andi.b #$e0,d0
; cmpi.b #$40,d0 ;check if input was tagged zero
; beq.b cu_numv
;cu_nunzro:
; or.l #unfl_mask,USER_FPSR(a6) ;set unfl - DELETED <5/3/91, JPO>
;cu_numv:
; move.l (a0),ETEMP(a6)
; move.l 4(a0),ETEMP_HI(a6)
; move.l 8(a0),ETEMP_LO(a6) <T3>
*
* Write the result to memory, setting the fpsr cc bits. NaN and Inf
* bypass cu_wrexn.
*
cu_wrexn:
; tst.w LOCAL_EX(a0) ;test for zero - DELETED <7/5/91, JPO> <T5>
; beq.b cu_wrzero ; DELETED <7/5/91, JPO> <T5>
; cmp.w #$8000,LOCAL_EX(a0) ;test for zero - DELETED <7/5/91, JPO> <T5>
; bne.b cu_wreon ; DELETED <7/5/91, JPO> <T5>
tst.l LOCAL_HI(a0) ; test for zero result <7/5/91, JPO> <T5>
bne.b cu_wreon ; <7/5/91, JPO> <T5>
tst.l LOCAL_LO(a0) ; <7/5/91, JPO> <T5>
bne.b cu_wreon ; <7/5/91, JPO> <T5>
cu_wrzero:
or.l #z_mask,USER_FPSR(a6) ;set Z bit
cu_wreon:
tst.w LOCAL_EX(a0)
bpl wr_etemp
or.l #neg_mask,USER_FPSR(a6)
bra wr_etemp
*
* HANDLE SOURCE DENORM HERE
*
* ;clear denorm stag to norm
* ;write the new tag & ete15 to the fstack
mon_dnrm:
*
* At this point, check for the cases in which normalizing the
* denorm produces incorrect results.
*
tst.b DY_MO_FLG(a6) ;all cases of dyadic instructions would
bne.b nrm_src ;require normalization of denorm
* At this point:
* monadic instructions: fabs = $18 fneg = $1a ftst = $3a
* fmove = $00 fsmove = $40 fdmove = $44
* fsqrt = $05* fssqrt = $41 fdsqrt = $45
* (*fsqrt reencoded to $05)
*
move.w CMDREG1B(a6),d0 ;get command register
andi.l #$7f,d0 ;strip to only command word
*
* At this point, fabs, fneg, fsmove, fdmove, ftst, fsqrt, fssqrt, and
* fdsqrt are possible.
* For cases fabs, fneg, fsmove, and fdmove goto spos (do not normalize)
* For cases fsqrt, fssqrt, and fdsqrt goto nrm_src (do normalize)
*
btst.l #0,d0
bne.b nrm_src ;weed out fsqrt instructions
st CU_ONLY(a6) ;set cu-only inst flag
bra.b cu_dnrm ;fmove, fabs, fneg, ftst
* ;cases go to cu_dnrm
nrm_src:
bclr.b #sign_bit,LOCAL_EX(a0)
sne LOCAL_SGN(a0)
bsr nrm_set ;normalize number (exponent will go
* ; negative)
bclr.b #sign_bit,LOCAL_EX(a0) ;get rid of false sign
bfclr LOCAL_SGN(a0){0:8} ;change back to IEEE ext format
beq.b spos
bset.b #sign_bit,LOCAL_EX(a0)
spos:
bfclr STAG(a6){0:4} ;set tag to normalized, FPTE15 = 0
bset.b #4,STAG(a6) ;set ETE15
or.b #$f0,DNRM_FLG(a6)
resnormal: ; label RENAMED <1/7/91, JPO>
tst.b DNRM_FLG(a6) ;check if any of the ops were denorms
bne ck_wrap ;if so, check if it is a potential
* ;wrap-around case
fix_stk:
move.b #$fe,CU_SAVEPC(a6)
bclr.b #E1,E_BYTE(a6)
clr.w NMNEXC(a6)
st.b RES_FLG(a6) ;indicate that a restore is needed
rts
*
* cu_dnrm handles all cu-only instructions (fmove, fabs, fneg, and
* ftst) completly in software without an frestore to the 040.
*
cu_dnrm:
st.b CU_ONLY(a6)
move.w CMDREG1B(a6),d0
andi.b #$3b,d0 ;isolate bits to select inst
tst.b d0
beq.b cu_dmove ;if zero, it is an fmove
cmpi.b #$18,d0
beq.b cu_dabs ;if $18, it is fabs
cmpi.b #$1a,d0
beq.b cu_dneg ;if $1a, it is fneg
*
* Inst is ftst. Check the source operand and set the cc's accordingly.
* No write is done, so simply rts.
*
cu_dtst:
move.w LOCAL_EX(a0),d0
bclr.l #15,d0
sne LOCAL_SGN(a0)
beq.b cu_dtpo
or.l #neg_mask,USER_FPSR(a6) ;set N
cu_dtpo:
cmpi.w #$7fff,d0 ;test for inf/nan
bne.b cu_dtcz
tst.l LOCAL_HI(a0)
bne.b cu_dtn
tst.l LOCAL_LO(a0)
bne.b cu_dtn
or.l #inf_mask,USER_FPSR(a6)
rts
cu_dtn:
or.l #nan_mask,USER_FPSR(a6)
move.l ETEMP_EX(a6),FPTEMP_EX(a6) ;set up fptemp sign for
* ;snan handler
rts
cu_dtcz:
tst.l LOCAL_HI(a0)
bne.b cu_dtsx
tst.l LOCAL_LO(a0)
bne.b cu_dtsx
or.l #z_mask,USER_FPSR(a6)
cu_dtsx:
rts
*
* Inst is fabs. Execute the absolute value function on the input.
* Branch to the fmove code.
*
cu_dabs:
bclr.b #7,LOCAL_EX(a0) ;do abs
bra.b cu_dmove ;fmove code will finish
*
* Inst is fneg. Execute the negate value function on the input.
* Fall though to the fmove code.
*
cu_dneg:
bchg.b #7,LOCAL_EX(a0) ;do neg
*
* Inst is fmove. This code also handles all result writes.
* If bit 2 is set, round is forced to double. If it is clear,
* and bit 6 is set, round is forced to single. If both are clear,
* the round precision is found in the fpcr. If the rounding precision
* is double or single, the result is zero, and the mode is checked
* to determine if the lsb of the result should be set.
*
cu_dmove:
btst.b #2,CMDREG1B+1(a6) ;check for rd
bne.b cu_dmrd
btst.b #6,CMDREG1B+1(a6) ;check for rs
bne.b cu_dmrs
*
* The move or operation is not with forced precision. Use the
* FPCR_MODE byte to get rounding.
*
cu_dmnr:
bfextu FPCR_MODE(a6){0:2},d0
tst.b d0 ;check for extended
beq.b cu_wrexd ;if so, just write result
cmpi.b #1,d0 ;check for single
beq.b cu_dmrs ;fall through to double
*
* The move is fdmove or round precision is double. Result is zero.
* Check rmode for rp or rm and set lsb accordingly.
*
cu_dmrd:
bfextu FPCR_MODE(a6){2:2},d1 ;get rmode
tst.w LOCAL_EX(a0) ;check sign
blt.b cu_dmdn
cmpi.b #3,d1 ;check for rp
bne.b cu_pzero ;load double pos zero - label RENAMED <3/15/91, JPO>
bra.b cu_dpdr ;load double pos zero w/lsb
cu_dmdn:
cmpi.b #2,d1 ;check for rm
bne.b cu_nzero ;load double neg zero - label RENAMED <3/15/91, JPO>
bra cu_dndr ;load double neg zero w/lsb
*
* The move is fsmove or round precision is single. Result is zero.
* Check for rp or rm and set lsb accordingly.
*
cu_dmrs:
bfextu FPCR_MODE(a6){2:2},d1 ;get rmode
tst.w LOCAL_EX(a0) ;check sign
blt.b cu_dmsn
cmpi.b #3,d1 ;check for rp
; bne cu_spd ;load single pos zero - deleted <3/15/91, JPO>
bne.b cu_pzero ; load pos zero <3/15/91, JPO>
bra cu_spdr ;load single pos zero w/lsb
cu_dmsn:
cmpi.b #2,d1 ;check for rm
; bne cu_snd ;load single neg zero - deleted <3/15/91, JPO>
bne.b cu_nzero
bra cu_sndr ;load single neg zero w/lsb
*
* The precision is extended, so the result in etemp is correct.
* Simply set unfl (not inex2 or aunfl) and write the result to
* the correct fp register.
cu_wrexd:
or.l #unfl_mask,USER_FPSR(a6)
tst.w LOCAL_EX(a0)
beq wr_etemp
or.l #neg_mask,USER_FPSR(a6)
bra wr_etemp
*
* These routines write +/- zero in double format. The routines
* cu_dpdr and cu_dndr set the double lsb.
*
cu_pzero: ; label RENAMED <3/15/91, JPO>
; move.l #$3c010000,LOCAL_EX(a0) ;force pos zero - DELETED <3/15/91, JPO>
clr.l LOCAL_EX(a0) ; <3/15/91, JPO>
clr.l LOCAL_HI(a0)
clr.l LOCAL_LO(a0)
; or.l #z_mask,USER_FPSR(a6) ; DELETED <3/15/91, JPO>
; or.l #unfinx_mask,USER_FPSR(a6) ; DELETED <3/15/91, JPO>
or.l #z_mask+unfinx_mask,USER_FPSR(a6) ; <3/15/91, JPO>
bra wr_etemp
cu_dpdr:
; move.l #$3c010000,LOCAL_EX(a0) ;force pos double zero - DELETED <3/15/91, JPO>
; clr.l LOCAL_HI(a0) ; DELETED <3/15/91, JPO>
; move.l #$800,LOCAL_LO(a0) ;with lsb set - DELETED
move.l #$3bcd0000,LOCAL_EX(a0) ; force smallest pos double denorm <3/15/91, JPO>
cu_ptail: ; label ADDED <3/15/91, JPO>
move.l #$80000000,LOCAL_HI(a0) ; <3/15/91, JPO>
clr.l LOCAL_LO(a0) ; <3/15/91, JPO>
or.l #unfinx_mask,USER_FPSR(a6)
bra wr_etemp
cu_nzero: ; label RENAMED <3/15/91, JPO>
; move.l #$bc010000,LOCAL_EX(a0) ;force pos double zero - DELETED <3/15/91, JPO>
move.l #$80000000,LOCAL_EX(a0) ; force negative zero <3/15/91, JPO>
clr.l LOCAL_HI(a0)
clr.l LOCAL_LO(a0)
; or.l #z_mask,USER_FPSR(a6) ; DELETED <3/15/91, JPO>
; or.l #neg_mask,USER_FPSR(a6) ; DELETED <3/15/91, JPO>
; or.l #unfinx_mask,USER_FPSR(a6) ; DELETED <3/15/91, JPO>
or.l #z_mask+neg_mask+unfinx_mask,USER_FPSR(a6) ; <3/15/91, JPO>
bra wr_etemp
cu_dndr:
; move.l #$bc010000,LOCAL_EX(a0) ;force pos double zero - DELETED <3/15/91, JPO>
; clr.l LOCAL_HI(a0) ; DELETED <3/15/91, JPO>
; move.l #$800,LOCAL_LO(a0) ;with lsb set - DELETED <3/15/91, JPO>
move.l #$bbcd0000,LOCAL_EX(a0) ; force smallest neg double denorm <3/15/91, JPO>
cu_ntail: ; label ADDED <3/15/91, JPO>
move.l #$80000000,LOCAL_HI(a0) ; <3/15/91, JPO>
clr.l LOCAL_LO(a0) ; <3/15/91, JPO>
; or.l #neg_mask,USER_FPSR(a6) ; DELETED <3/15/91, JPO>
; or.l #unfinx_mask,USER_FPSR(a6) ; DELETED <3/15/91, JPO>
or.l #neg_mask+unfinx_mask,USER_FPSR(a6) ; <3/15/91, JPO>
bra wr_etemp
*
* These routines write +/- zero in single format. The routines
* cu_dpdr and cu_dndr set the single lsb.
*
;cu_spd: ; routine DELETED <3/15/91, JPO>
; move.l #$3f810000,LOCAL_EX(a0) ;force pos single zero
; clr.l LOCAL_HI(a0)
; clr.l LOCAL_LO(a0)
; or.l #z_mask,USER_FPSR(a6)
; or.l #unfinx_mask,USER_FPSR(a6)
; bra wr_etemp
cu_spdr:
; move.l #$3f810000,LOCAL_EX(a0) ;force pos single zero - deleted <3/15/91, JPO>
; move.l #$100,LOCAL_HI(a0) ;with lsb set - DELETED <3/15/91, JPO>
; clr.l LOCAL_LO(a0) ; DELETED <3/15/91, JPO>
; or.l #unfinx_mask,USER_FPSR(a6) ; DELETED <3/15/91, JPO>
; bra wr_etemp ; DELETED <3/15/91, JPO>
move.l #$3f6a0000,LOCAL_EX(a0) ; force smallest pos single denorm <3/15/91, JPO>
bra.b cu_ptail ; <3/15/91, JPO>
;cu_snd: ; routine DELETED <3/15/91, JPO>
; move.l #$bf810000,LOCAL_EX(a0) ;force pos single zero
; clr.l LOCAL_HI(a0)
; clr.l LOCAL_LO(a0)
; or.l #z_mask,USER_FPSR(a6)
; or.l #neg_mask,USER_FPSR(a6)
; or.l #unfinx_mask,USER_FPSR(a6)
; bra wr_etemp
cu_sndr:
; move.l #$bf810000,LOCAL_EX(a0) ;force pos single zero - DELETED <3/15/91, JPO>
; move.l #$100,LOCAL_HI(a0) ;with lsb set - DELETED <3/15/91, JPO>
; clr.l LOCAL_LO(a0) ; DELETED <3/15/91, JPO>
; or.l #neg_mask,USER_FPSR(a6) ; DELETED <3/15/91, JPO>
; or.l #unfinx_mask,USER_FPSR(a6) ; DELETED <3/15/91, JPO>
; bra wr_etemp ; DELETED <3/15/91, JPO>
move.l #$bf6a0000,LOCAL_EX(a0) ; force smallest neg single denorm <3/15/91, JPO>
bra.b cu_ntail ; <3/15/91, JPO>
*
* This code checks for 16-bit overflow conditions on dyadic
* operations which are not restorable into the floating-point
* unit and must be completed in software. Basically, this
* condition exists with a very large norm and a denorm. One
* of the operands must be denormalized to enter this code.
*
* Flags used:
* DY_MO_FLG contains 0 for monadic op, $ff for dyadic
* DNRM_FLG contains $00 for neither op denormalized
* $0f for the destination op denormalized
* $f0 for the source op denormalized
* $ff for both ops denormalzed
*
* The wrap-around condition occurs for add, sub, div, and cmp
* when
*
* abs(dest_exp - src_exp) >= $8000
*
* and for mul when
*
* (dest_exp + src_exp) < $0
*
* we must process the operation here if this case is true.
*
* The rts following the frcfpn routine is the exit from res_func
* for this condition. The restore flag (RES_FLG) is left clear.
* No frestore is done unless an exception is to be reported.
*
* For fadd:
* if(sign_of(dest) != sign_of(src))
* replace exponent of src with $3fff (keep sign)
* use fpu to perform dest+new_src (user's rmode and X)
* clr sticky
* else
* set sticky
* call round with user's precision and mode
* move result to fpn and wbtemp
*
* For fsub:
* if(sign_of(dest) == sign_of(src))
* replace exponent of src with $3fff (keep sign)
* use fpu to perform dest+new_src (user's rmode and X)
* clr sticky
* else
* set sticky
* call round with user's precision and mode
* move result to fpn and wbtemp
*
* For fdiv/fsgldiv:
* if(both operands are denorm)
* restore_to_fpu;
* if(dest is norm)
* force_ovf;
* else(dest is denorm)
* force_unf:
*
* For fcmp:
* if(dest is norm)
* N = sign_of(dest);
* else(dest is denorm)
* N = sign_of(src);
*
* For fmul:
* if(both operands are denorm)
* force_unf;
* if((dest_exp + src_exp) < 0)
* force_unf:
* else
* restore_to_fpu;
*
* local equates:
addcode equ $22
subcode equ $28
mulcode equ $23
divcode equ $20
cmpcode equ $38
ck_wrap:
tst.b DY_MO_FLG(a6) ;check for fsqrt
beq fix_stk ;if zero, it is fsqrt
move.w CMDREG1B(a6),d0
andi.w #$3b,d0 ;strip to command bits
cmpi.w #addcode,d0
beq wrap_add
cmpi.w #subcode,d0
beq wrap_sub
cmpi.w #mulcode,d0
beq wrap_mul
cmpi.w #cmpcode,d0
beq wrap_cmp
*
* Inst is fdiv.
*
wrap_div:
cmp.b #$ff,DNRM_FLG(a6) ;if both ops denorm,
beq fix_stk ;restore to fpu
*
* One of the ops is denormalized. Test for wrap condition
* and force the result.
*
cmp.b #$0f,DNRM_FLG(a6) ;check for dest denorm
bne.b div_srcd
div_destd:
bfextu ETEMP_EX(a6){1:15},d0 ;get src exp (always pos)
bfexts FPTEMP_EX(a6){1:15},d1 ;get dest exp (always neg)
sub.l d1,d0 ;subtract dest from src
; cmp.l #$8000,d0 ; DELETED <1/29/91, JPO>
cmp.l #$7fff,d0 ; new wrap condition <1/29/91, JPO>
blt fix_stk ;if less, not wrap case
btst.b #6,STAG(a6) ; if src is INF or NaN, not wrap case <1/31/91, JPO>
bne fix_stk ; <1/31/91, JPO>
; clr.b WBTEMP_SGN(a6) ; DELETED (duplicated in "force_unf" <7/5/91, JPO> <T5>
; move.w ETEMP_EX(a6),d0 ;find the sign of the result - DELETED <7/5/91, JPO> <T5>
; move.w FPTEMP_EX(a6),d1 ; DELETED <7/5/91, JPO> <T5>
; eor.w d1,d0 ; DELETED <7/5/91, JPO> <T5>
; andi.w #$8000,d0 ; DELETED <7/5/91, JPO> <T5>
; beq force_unf ; DELETED <7/5/91, JPO> <T5>
; st.b WBTEMP_SGN(a6) ; DELETED <7/5/91, JPO> <T5>
bra force_unf
div_srcd:
bfextu FPTEMP_EX(a6){1:15},d0 ;get dest exp (always pos)
bfexts ETEMP_EX(a6){1:15},d1 ;get src exp (always neg)
sub.l d1,d0 ;subtract src from dest
cmp.l #$8000,d0
blt fix_stk ;if less, not wrap case
btst.b #6,DTAG(a6) ; if dst is INF or NaN, not wrap case <1/31/91, JPO>
bne fix_stk
clr.b WBTEMP_SGN(a6)
move.w ETEMP_EX(a6),d0 ;find the sign of the result
move.w FPTEMP_EX(a6),d1
eor.w d1,d0
andi.w #$8000,d0
beq.b force_ovf
st.b WBTEMP_SGN(a6)
*
* This code handles the case of the instruction resulting in
* an overflow condition.
*
force_ovf:
bclr.b #E1,E_BYTE(a6)
or.l #ovfl_inx_mask,USER_FPSR(a6)
clr.w NMNEXC(a6)
lea.l WBTEMP(a6),a0 ;point a0 to memory location
move.w CMDREG1B(a6),d0
btst.l #6,d0 ;test for forced precision
beq.b frcovf_fpcr
btst.l #2,d0 ;check for double
bne.b frcovf_dbl
move.l #$1,d0 ;inst is forced single
bra.b frcovf_rnd
frcovf_dbl:
move.l #$2,d0 ;inst is forced double
bra.b frcovf_rnd
frcovf_fpcr:
bfextu FPCR_MODE(a6){0:2},d0 ;inst not forced - use fpcr prec
frcovf_rnd:
* The 881/882 does not set inex2 for the following case, so the <T4>
* line is commented out to be compatible with 881/882 <T4>
* tst.b d0 ; DELETED <6/7/91, JPO> <T4>
* beq.b frcovf_x ; DELETED <6/7/91, JPO> <T4>
* or.l #inex2_mask,USER_FPSR(a6) ;if prec is s or d, set inex2 - DELETED <6/7/91, JPO> <T4>
*frcovf_x: ; label DELETED <6/7/91, JPO> <T4>
bsr ovf_res ;get correct result based on
* ;round precision/mode. This
* ;sets FPSR_CC correctly
* ;returns in external format
bfclr WBTEMP_SGN(a6){0:8}
beq frcfpn
bset.b #sign_bit,WBTEMP_EX(a6)
bra frcfpn
*
* Inst is fadd.
*
wrap_add:
cmp.b #$ff,DNRM_FLG(a6) ;if both ops denorm,
beq fix_stk ;restore to fpu
*
* One of the ops is denormalized. Test for wrap condition
* and complete the instruction.
*
cmp.b #$0f,DNRM_FLG(a6) ;check for dest denorm
bne.b add_srcd
add_destd:
bfextu ETEMP_EX(a6){1:15},d0 ;get src exp (always pos)
bfexts FPTEMP_EX(a6){1:15},d1 ;get dest exp (always neg)
sub.l d1,d0 ;subtract dest from src
cmp.l #$8000,d0
blt fix_stk ;if less, not wrap case
btst.b #6,STAG(a6) ; if src is INF or NaN, not wrap case <1/31/91, JPO>
bne fix_stk ; <1/31/91, JPO>
bra.b add_wrap
add_srcd:
bfextu FPTEMP_EX(a6){1:15},d0 ;get dest exp (always pos)
bfexts ETEMP_EX(a6){1:15},d1 ;get src exp (always neg)
sub.l d1,d0 ;subtract src from dest
cmp.l #$8000,d0
blt fix_stk ;if less, not wrap case
btst.b #6,DTAG(a6) ; if dst is INF or NaN, not wrap case <1/31/91, JPO>
bne fix_stk
*
* Check the signs of the operands. If they are unlike, the fpu
* can be used to add the norm and 1.0 with the sign of the
* denorm and it will correctly generate the result in extended
* precision. We can then call round with no sticky and the result
* will be correct for the user's rounding mode and precision. If
* the signs are the same, we call round with the sticky bit set
* and the result will be correctfor the user's rounding mode and
* precision.
*
add_wrap:
move.w ETEMP_EX(a6),d0
move.w FPTEMP_EX(a6),d1
eor.w d1,d0
andi.w #$8000,d0
beq add_same
*
* The signs are unlike.
*
cmp.b #$0f,DNRM_FLG(a6) ;is dest the denorm?
bne.b add_u_srcd
move.w FPTEMP_EX(a6),d0
andi.w #$8000,d0
or.w #$3fff,d0 ;force the exponent to +/- 1
move.w d0,FPTEMP_EX(a6) ;in the denorm
move.l USER_FPCR(a6),d0
andi.l #$30,d0
fmove.l d0,fpcr ;set up users rmode and X
fmove.x ETEMP(a6),fp0
fadd.x FPTEMP(a6),fp0
lea.l WBTEMP(a6),a0 ;point a0 to wbtemp in frame
fmove.l fpsr,d1
or.l d1,USER_FPSR(a6) ;capture cc's and inex from fadd
fmove.x fp0,WBTEMP(a6) ;write result to memory
lsr.l #4,d0 ;put rmode in lower 2 bits
move.l USER_FPCR(a6),d1
andi.l #$c0,d1
lsr.l #6,d1 ;put precision in upper word
swap d1
or.l d0,d1 ;set up for round call
clr.l d0 ;force sticky to zero
bclr.b #sign_bit,WBTEMP_EX(a6)
sne WBTEMP_SGN(a6)
bsr round ;round result to users rmode & prec
bfclr WBTEMP_SGN(a6){0:8} ;convert back to IEEE ext format
beq frcfpnr
bset.b #sign_bit,WBTEMP_EX(a6)
bra frcfpnr
add_u_srcd:
move.w ETEMP_EX(a6),d0
andi.w #$8000,d0
or.w #$3fff,d0 ;force the exponent to +/- 1
move.w d0,ETEMP_EX(a6) ;in the denorm
move.l USER_FPCR(a6),d0
andi.l #$30,d0
fmove.l d0,fpcr ;set up users rmode and X
fmove.x ETEMP(a6),fp0
fadd.x FPTEMP(a6),fp0
fmove.l fpsr,d1
or.l d1,USER_FPSR(a6) ;capture cc's and inex from fadd
lea.l WBTEMP(a6),a0 ;point a0 to wbtemp in frame
fmove.x fp0,WBTEMP(a6) ;write result to memory
lsr.l #4,d0 ;put rmode in lower 2 bits
move.l USER_FPCR(a6),d1
andi.l #$c0,d1
lsr.l #6,d1 ;put precision in upper word
swap d1
or.l d0,d1 ;set up for round call
clr.l d0 ;force sticky to zero
bclr.b #sign_bit,WBTEMP_EX(a6)
sne WBTEMP_SGN(a6) ;use internal format for round
bsr round ;round result to users rmode & prec
bfclr WBTEMP_SGN(a6){0:8} ;convert back to IEEE ext format
beq frcfpnr
bset.b #sign_bit,WBTEMP_EX(a6)
bra frcfpnr
*
* Signs are alike:
*
add_same:
cmp.b #$0f,DNRM_FLG(a6) ;is dest the denorm?
bne.b add_s_srcd
add_s_destd:
lea.l ETEMP(a6),a0
move.l USER_FPCR(a6),d0
andi.l #$30,d0
lsr.l #4,d0 ;put rmode in lower 2 bits
move.l USER_FPCR(a6),d1
andi.l #$c0,d1
lsr.l #6,d1 ;put precision in upper word
swap d1
or.l d0,d1 ;set up for round call
move.l #$20000000,d0 ;set sticky for round
bclr.b #sign_bit,ETEMP_EX(a6)
sne ETEMP_SGN(a6)
bsr round ;round result to users rmode & prec
bfclr ETEMP_SGN(a6){0:8} ;convert back to IEEE ext format
beq.b add_s_dclr
bset.b #sign_bit,ETEMP_EX(a6)
add_s_dclr:
lea.l WBTEMP(a6),a0
move.l ETEMP(a6),(a0) ;write result to wbtemp
move.l ETEMP_HI(a6),4(a0)
move.l ETEMP_LO(a6),8(a0)
tst.w ETEMP_EX(a6)
bgt.b add_ckovf
or.l #neg_mask,USER_FPSR(a6)
bra.b add_ckovf
add_s_srcd:
lea.l FPTEMP(a6),a0
move.l USER_FPCR(a6),d0
andi.l #$30,d0
lsr.l #4,d0 ;put rmode in lower 2 bits
move.l USER_FPCR(a6),d1
andi.l #$c0,d1
lsr.l #6,d1 ;put precision in upper word
swap d1
or.l d0,d1 ;set up for round call
move.l #$20000000,d0 ;set sticky for round
bclr.b #sign_bit,FPTEMP_EX(a6)
sne FPTEMP_SGN(a6)
bsr round ;round result to users rmode & prec
bfclr FPTEMP_SGN(a6){0:8} ;convert back to IEEE ext format
beq.b add_s_sclr
bset.b #sign_bit,FPTEMP_EX(a6)
add_s_sclr:
lea.l WBTEMP(a6),a0
move.l FPTEMP(a6),(a0) ;write result to wbtemp
move.l FPTEMP_HI(a6),4(a0)
move.l FPTEMP_LO(a6),8(a0)
tst.w FPTEMP_EX(a6)
bgt.b add_ckovf
or.l #neg_mask,USER_FPSR(a6)
add_ckovf:
move.w WBTEMP_EX(a6),d0
andi.w #$7fff,d0
cmpi.w #$7fff,d0
bne frcfpnr
*
* The result has overflowed to $7fff exponent. Set I, ovfl,
* and aovfl, and clr the mantissa (incorrectly set by the
* round routine.)
*
or.l #inf_mask+ovfl_inx_mask,USER_FPSR(a6)
clr.l 4(a0)
bra frcfpnr
*
* Inst is fsub.
*
wrap_sub:
cmp.b #$ff,DNRM_FLG(a6) ;if both ops denorm,
beq fix_stk ;restore to fpu
*
* One of the ops is denormalized. Test for wrap condition
* and complete the instruction.
*
cmp.b #$0f,DNRM_FLG(a6) ;check for dest denorm
bne.b sub_srcd
sub_destd:
bfextu ETEMP_EX(a6){1:15},d0 ;get src exp (always pos)
bfexts FPTEMP_EX(a6){1:15},d1 ;get dest exp (always neg)
sub.l d1,d0 ;subtract src from dest
cmp.l #$8000,d0
blt fix_stk ;if less, not wrap case
btst.b #6,STAG(a6) ; if src is INF or NaN, not wrap case <1/31/91, JPO>
bne fix_stk ; <1/31/91, JPO>
bra.b sub_wrap
sub_srcd:
bfextu FPTEMP_EX(a6){1:15},d0 ;get dest exp (always pos)
bfexts ETEMP_EX(a6){1:15},d1 ;get src exp (always neg)
sub.l d1,d0 ;subtract dest from src
cmp.l #$8000,d0
blt fix_stk ;if less, not wrap case
btst.b #6,DTAG(a6) ; if dst is INF or NaN, not wrap case <1/31/91, JPO>
bne fix_stk ; <1/31/91, JPO>
*
* Check the signs of the operands. If they are alike, the fpu
* can be used to subtract from the norm 1.0 with the sign of the
* denorm and it will correctly generate the result in extended
* precision. We can then call round with no sticky and the result
* will be correct for the user's rounding mode and precision. If
* the signs are unlike, we call round with the sticky bit set
* and the result will be correctfor the user's rounding mode and
* precision.
*
sub_wrap:
move.w ETEMP_EX(a6),d0
move.w FPTEMP_EX(a6),d1
eor.w d1,d0
andi.w #$8000,d0
bne sub_diff
*
* The signs are alike.
*
cmp.b #$0f,DNRM_FLG(a6) ;is dest the denorm?
bne.b sub_u_srcd
move.w FPTEMP_EX(a6),d0
andi.w #$8000,d0
or.w #$3fff,d0 ;force the exponent to +/- 1
move.w d0,FPTEMP_EX(a6) ;in the denorm
move.l USER_FPCR(a6),d0
andi.l #$30,d0
fmove.l d0,fpcr ;set up users rmode and X
fmove.x FPTEMP(a6),fp0
fsub.x ETEMP(a6),fp0
fmove.l fpsr,d1
or.l d1,USER_FPSR(a6) ;capture cc's and inex from fadd
lea.l WBTEMP(a6),a0 ;point a0 to wbtemp in frame
fmove.x fp0,WBTEMP(a6) ;write result to memory
lsr.l #4,d0 ;put rmode in lower 2 bits
move.l USER_FPCR(a6),d1
andi.l #$c0,d1
lsr.l #6,d1 ;put precision in upper word
swap d1
or.l d0,d1 ;set up for round call
clr.l d0 ;force sticky to zero
bclr.b #sign_bit,WBTEMP_EX(a6)
sne WBTEMP_SGN(a6)
bsr round ;round result to users rmode & prec
bfclr WBTEMP_SGN(a6){0:8} ;convert back to IEEE ext format
beq frcfpnr
bset.b #sign_bit,WBTEMP_EX(a6)
bra frcfpnr
sub_u_srcd:
move.w ETEMP_EX(a6),d0
andi.w #$8000,d0
or.w #$3fff,d0 ;force the exponent to +/- 1
move.w d0,ETEMP_EX(a6) ;in the denorm
move.l USER_FPCR(a6),d0
andi.l #$30,d0
fmove.l d0,fpcr ;set up users rmode and X
fmove.x FPTEMP(a6),fp0
fsub.x ETEMP(a6),fp0
fmove.l fpsr,d1
or.l d1,USER_FPSR(a6) ;capture cc's and inex from fadd
lea.l WBTEMP(a6),a0 ;point a0 to wbtemp in frame
fmove.x fp0,WBTEMP(a6) ;write result to memory
lsr.l #4,d0 ;put rmode in lower 2 bits
move.l USER_FPCR(a6),d1
andi.l #$c0,d1
lsr.l #6,d1 ;put precision in upper word
swap d1
or.l d0,d1 ;set up for round call
clr.l d0 ;force sticky to zero
bclr.b #sign_bit,WBTEMP_EX(a6)
sne WBTEMP_SGN(a6)
bsr round ;round result to users rmode & prec
bfclr WBTEMP_SGN(a6){0:8} ;convert back to IEEE ext format
beq frcfpnr
bset.b #sign_bit,WBTEMP_EX(a6)
bra frcfpnr
*
* Signs are unlike:
*
sub_diff:
cmp.b #$0f,DNRM_FLG(a6) ;is dest the denorm?
bne.b sub_s_srcd
sub_s_destd:
lea.l ETEMP(a6),a0
move.l USER_FPCR(a6),d0
andi.l #$30,d0
lsr.l #4,d0 ;put rmode in lower 2 bits
move.l USER_FPCR(a6),d1
andi.l #$c0,d1
lsr.l #6,d1 ;put precision in upper word
swap d1
or.l d0,d1 ;set up for round call
move.l #$20000000,d0 ;set sticky for round
*
* Since the dest is the denorm, the sign is the opposite of the
* norm sign.
*
eori.w #$8000,ETEMP_EX(a6) ;flip sign on result
tst.w ETEMP_EX(a6)
bgt.b sub_s_dwr
or.l #neg_mask,USER_FPSR(a6)
sub_s_dwr:
bclr.b #sign_bit,ETEMP_EX(a6)
sne ETEMP_SGN(a6)
bsr round ;round result to users rmode & prec
bfclr ETEMP_SGN(a6){0:8} ;convert back to IEEE ext format
beq.b sub_s_dclr
bset.b #sign_bit,ETEMP_EX(a6)
sub_s_dclr:
lea.l WBTEMP(a6),a0
move.l ETEMP(a6),(a0) ;write result to wbtemp
move.l ETEMP_HI(a6),4(a0)
move.l ETEMP_LO(a6),8(a0)
bra.b sub_ckovf
sub_s_srcd:
lea.l FPTEMP(a6),a0
move.l USER_FPCR(a6),d0
andi.l #$30,d0
lsr.l #4,d0 ;put rmode in lower 2 bits
move.l USER_FPCR(a6),d1
andi.l #$c0,d1
lsr.l #6,d1 ;put precision in upper word
swap d1
or.l d0,d1 ;set up for round call
move.l #$20000000,d0 ;set sticky for round
bclr.b #sign_bit,FPTEMP_EX(a6)
sne FPTEMP_SGN(a6)
bsr round ;round result to users rmode & prec
bfclr FPTEMP_SGN(a6){0:8} ;convert back to IEEE ext format
beq.b sub_s_sclr
bset.b #sign_bit,FPTEMP_EX(a6)
sub_s_sclr:
lea.l WBTEMP(a6),a0
move.l FPTEMP(a6),(a0) ;write result to wbtemp
move.l FPTEMP_HI(a6),4(a0)
move.l FPTEMP_LO(a6),8(a0)
tst.w FPTEMP_EX(a6)
bgt.b sub_ckovf
or.l #neg_mask,USER_FPSR(a6)
sub_ckovf:
move.w WBTEMP_EX(a6),d0
andi.w #$7fff,d0
cmpi.w #$7fff,d0
bne frcfpnr
*
* The result has overflowed to $7fff exponent. Set I, ovfl,
* and aovfl, and clr the mantissa (incorrectly set by the
* round routine.)
*
or.l #inf_mask+ovfl_inx_mask,USER_FPSR(a6)
clr.l 4(a0)
bra frcfpnr
*
* Inst is fcmp.
*
wrap_cmp:
cmp.b #$ff,DNRM_FLG(a6) ;if both ops denorm,
beq fix_stk ;restore to fpu
*
* One of the ops is denormalized. Test for wrap condition
* and complete the instruction.
*
cmp.b #$0f,DNRM_FLG(a6) ;check for dest denorm
bne.b cmp_srcd
cmp_destd:
bfextu ETEMP_EX(a6){1:15},d0 ;get src exp (always pos)
bfexts FPTEMP_EX(a6){1:15},d1 ;get dest exp (always neg)
sub.l d1,d0 ;subtract dest from src
cmp.l #$8000,d0
blt fix_stk ;if less, not wrap case
btst.b #6,STAG(a6) ; if src is INF or NaN, not wrap case <1/31/91, JPO>
bne fix_stk ; <1/31/91, JPO>
tst.w ETEMP_EX(a6) ;set N to ~sign_of(src)
bge.b cmp_setn
rts
cmp_srcd:
bfextu FPTEMP_EX(a6){1:15},d0 ;get dest exp (always pos)
bfexts ETEMP_EX(a6){1:15},d1 ;get src exp (always neg)
sub.l d1,d0 ;subtract src from dest
cmp.l #$8000,d0
blt fix_stk ;if less, not wrap case
btst.b #6,DTAG(a6) ; if dst is INF or NaN, not wrap case <1/31/91, JPO>
bne fix_stk ; <1/31/91, JPO>
tst.w FPTEMP_EX(a6) ;set N to sign_of(dest)
blt.b cmp_setn
rts
cmp_setn:
or.l #neg_mask,USER_FPSR(a6)
rts
*
* Inst is fmul.
*
wrap_mul:
cmp.b #$ff,DNRM_FLG(a6) ;if both ops denorm,
beq.b force_unf ;force an underflow (really!)
*
* One of the ops is denormalized. Test for wrap condition
* and complete the instruction.
*
cmp.b #$0f,DNRM_FLG(a6) ;check for dest denorm
bne.b mul_srcd
mul_destd:
bfextu ETEMP_EX(a6){1:15},d0 ;get src exp (always pos)
bfexts FPTEMP_EX(a6){1:15},d1 ;get dest exp (always neg)
add.l d1,d0 ;subtract dest from src
bgt fix_stk
btst.b #5,STAG(a6) ; no wrap if src is zero <1/31/91, JPO>
bne fix_stk ; <1/31/91, JPO>
bra.b force_unf
mul_srcd:
bfextu FPTEMP_EX(a6){1:15},d0 ;get dest exp (always pos)
bfexts ETEMP_EX(a6){1:15},d1 ;get src exp (always neg)
add.l d1,d0 ;subtract src from dest
bgt fix_stk
btst.b #5,DTAG(a6) ; no wrap if dst is zero <1/31/91, JPO>
bne fix_stk ; <1/31/91, JPO>
*
* This code handles the case of the instruction resulting in
* an underflow condition.
*
force_unf:
bclr.b #E1,E_BYTE(a6)
or.l #unfinx_mask,USER_FPSR(a6)
clr.w NMNEXC(a6)
clr.b WBTEMP_SGN(a6)
move.w ETEMP_EX(a6),d0 ;find the sign of the result
move.w FPTEMP_EX(a6),d1
eor.w d1,d0
andi.w #$8000,d0
beq.b frcunfcont
st.b WBTEMP_SGN(a6)
frcunfcont:
lea WBTEMP(a6),a0 ;point a0 to memory location
move.w CMDREG1B(a6),d0 ; ADDED <6/7/91, JPO> <T4>
btst.l #6,d0 ;test for forced precision
beq.b frcunf_fpcr
btst.l #2,d0 ;check for double
bne.b frcunf_dbl
move.l #$1,d0 ;inst is forced single
bra.b frcunf_rnd
frcunf_dbl:
move.l #$2,d0 ;inst is forced double
bra.b frcunf_rnd
frcunf_fpcr:
bfextu FPCR_MODE(a6){0:2},d0 ;inst not forced - use fpcr prec
frcunf_rnd:
bsr unf_sub ;get correct result based on
* ;round precision/mode. This
* ;sets FPSR_CC correctly
; If result is unnormalized, normalize it here. If result is zero with nonzero <T5>
; exponent, zero exponent here. Only possible values at this point are zero, <T5>
; extended denorm, or extended unnormal (due to single/double denorm). <7/5/91, JPO> <T5>
tst.w (a0) ; test exponent <7/5/91, JPO> <T5>
beq.b @2 ; zero means extended rounding <7/5/91, JPO> <T5>
tst.l LOCAL_HI(a0) ; test high mantissa <7/5/91, JPO> <T5>
bne.b @1 ; unnormalized (tiniest single denorm) <7/5/91, JPO> <T5>
tst.l LOCAL_LO(a0) ; test low mantissa <7/5/91, JPO> <T5>
bne.b @1 ; unnormalized (tiniest double denorm) <7/5/91, JPO> <T5>
clr.w (a0) ; zero (single/double rounding), clr expo <7/5/91, JPO> <T5>
bra.b @2 ; <7/5/91, JPO> <T5>
@1: ; label ADDED <7/5/91, JPO> <T5>
bsr nrm_set ; normalize unnormal <7/5/91, JPO> <T5>
@2: ; label ADDED <7/5/91, JPO> <T5>
bfclr WBTEMP_SGN(a6){0:8} ;convert back to IEEE ext format
beq.b frcfpn
bset.b #sign_bit,WBTEMP_EX(a6)
bra.b frcfpn
*
* Write the result to the user's fpn. All results must be HUGE to be
* written; otherwise the results would have overflowed or underflowed.
* If the rounding precision is single or double, the ovf_res routine
* is needed to correctly supply the max value.
*
frcfpnr:
move.w CMDREG1B(a6),d0
btst.l #6,d0 ;test for forced precision
beq.b frcfpn_fpcr
btst.l #2,d0 ;check for double
bne.b frcfpn_dbl
move.l #$1,d0 ;inst is forced single
bra.b frcfpn_rnd
frcfpn_dbl:
move.l #$2,d0 ;inst is forced double
bra.b frcfpn_rnd
frcfpn_fpcr:
bfextu FPCR_MODE(a6){0:2},d0 ;inst not forced - use fpcr prec
tst.b d0
beq.b frcfpn ;if extended, write what you got
frcfpn_rnd:
bclr.b #sign_bit,WBTEMP_EX(a6)
sne WBTEMP_SGN(a6)
bsr ovf_res ;get correct result based on
* ;round precision/mode. This
* ;sets FPSR_CC correctly
bfclr WBTEMP_SGN(a6){0:8} ;convert back to IEEE ext format
beq.b frcfpn_clr
bset.b #sign_bit,WBTEMP_EX(a6)
frcfpn_clr:
or.l #ovfinx_mask,USER_FPSR(a6)
*
* Perform the write.
*
frcfpn:
bfextu CMDREG1B(a6){6:3},d0 ;extract fp destination register
cmpi.b #3,d0
ble.b frc0123 ;check if dest is fp0-fp3
move.l #7,d1
sub.l d0,d1
clr.l d0
bset.l d1,d0
fmovem.x WBTEMP(a6),d0
rts
frc0123:
cmpi.b #0,d0
beq.b frc0_dst
cmpi.b #1,d0
beq.b frc1_dst
cmpi.b #2,d0
beq.b frc2_dst
frc3_dst:
move.l WBTEMP_EX(a6),USER_FP3(a6)
move.l WBTEMP_HI(a6),USER_FP3+4(a6)
move.l WBTEMP_LO(a6),USER_FP3+8(a6)
rts
frc2_dst:
move.l WBTEMP_EX(a6),USER_FP2(a6)
move.l WBTEMP_HI(a6),USER_FP2+4(a6)
move.l WBTEMP_LO(a6),USER_FP2+8(a6)
rts
frc1_dst:
move.l WBTEMP_EX(a6),USER_FP1(a6)
move.l WBTEMP_HI(a6),USER_FP1+4(a6)
move.l WBTEMP_LO(a6),USER_FP1+8(a6)
rts
frc0_dst:
move.l WBTEMP_EX(a6),USER_FP0(a6)
move.l WBTEMP_HI(a6),USER_FP0+4(a6)
move.l WBTEMP_LO(a6),USER_FP0+8(a6)
rts
*
* Write etemp to fpn.
* A check is made on enabled and signalled snan exceptions,
* and the destination is not overwritten if this condition exists.
* This code is designed to make fmoveins of unsupported data types
* faster.
*
wr_etemp:
btst.b #snan_bit,FPSR_EXCEPT(a6) ;if snan is set, and
beq.b fmoveinc ;enabled, force restore
btst.b #snan_bit,FPCR_ENABLE(a6) ;and don't overwrite
beq.b fmoveinc ;the dest
move.l ETEMP_EX(a6),FPTEMP_EX(a6) ;set up fptemp sign for
* ;snan handler
tst.b ETEMP(a6) ;check for negative
blt.b snan_neg
rts
snan_neg:
or.l #neg_bit,USER_FPSR(a6) ;snan is negative; set N
rts
fmoveinc:
clr.w NMNEXC(a6)
bclr.b #E1,E_BYTE(a6)
move.b STAG(a6),d0 ;check if stag is inf
andi.b #$e0,d0
cmpi.b #$40,d0
bne.b fminc_cnan
or.l #inf_mask,USER_FPSR(a6) ;if inf, nothing yet has set I
tst.w LOCAL_EX(a0) ;check sign
bge.b fminc_con
or.l #neg_mask,USER_FPSR(a6)
bra.b fminc_con
fminc_cnan:
cmpi.b #$60,d0 ;check if stag is NaN
bne.b fminc_czero
or.l #nan_mask,USER_FPSR(a6) ;if nan, nothing yet has set NaN
move.l ETEMP_EX(a6),FPTEMP_EX(a6) ;set up fptemp sign for
* ;snan handler
tst.w LOCAL_EX(a0) ;check sign
bge.b fminc_con
or.l #neg_mask,USER_FPSR(a6)
bra.b fminc_con
fminc_czero:
cmpi.b #$20,d0 ;check if zero
bne.b fminc_con
or.l #z_mask,USER_FPSR(a6) ;if zero, set Z
tst.w LOCAL_EX(a0) ;check sign
bge.b fminc_con
or.l #neg_mask,USER_FPSR(a6)
fminc_con:
bfextu CMDREG1B(a6){6:3},d0 ;extract fp destination register
cmpi.b #3,d0
ble.b fp0123 ;check if dest is fp0-fp3
move.l #7,d1
sub.l d0,d1
clr.l d0
bset.l d1,d0
fmovem.x ETEMP(a6),d0
rts
fp0123:
cmpi.b #0,d0
beq.b fp0_dst
cmpi.b #1,d0
beq.b fp1_dst
cmpi.b #2,d0
beq.b fp2_dst
fp3_dst:
move.l ETEMP_EX(a6),USER_FP3(a6)
move.l ETEMP_HI(a6),USER_FP3+4(a6)
move.l ETEMP_LO(a6),USER_FP3+8(a6)
rts
fp2_dst:
move.l ETEMP_EX(a6),USER_FP2(a6)
move.l ETEMP_HI(a6),USER_FP2+4(a6)
move.l ETEMP_LO(a6),USER_FP2+8(a6)
rts
fp1_dst:
move.l ETEMP_EX(a6),USER_FP1(a6)
move.l ETEMP_HI(a6),USER_FP1+4(a6)
move.l ETEMP_LO(a6),USER_FP1+8(a6)
rts
fp0_dst:
move.l ETEMP_EX(a6),USER_FP0(a6)
move.l ETEMP_HI(a6),USER_FP0+4(a6)
move.l ETEMP_LO(a6),USER_FP0+8(a6)
rts
opclass3:
st.b CU_ONLY(a6)
move.w CMDREG1B(a6),d0 ;check if packed moveout
andi.w #$0c00,d0 ;isolate last 2 bits of size field
cmpi.w #$0c00,d0 ;if size is 011 or 111, it is packed
beq.w pack_out ;else it is norm or denorm
bra.b mv_out
*
* MOVE OUT
*
mv_tbl: ; table modified <1/7/91, JPO>
dc.w li-mv_tbl
dc.w sgp-mv_tbl
dc.w xp-mv_tbl
dc.w mvout_end-mv_tbl ;should never be taken
dc.w wi-mv_tbl
dc.w dp-mv_tbl
dc.w bi-mv_tbl
dc.w mvout_end-mv_tbl ;should never be taken
mv_out:
bfextu CMDREG1B(a6){3:3},d1 ;put source specifier in d1
lea.l mv_tbl,a0
; move.l (a0,d1*4),a0 ; deleted <1/7/91, JPO>
adda.w (a0,d1.w*2),a0 ; <1/7/91, JPO>
jmp (a0)
*
* This exit is for move-out to memory. The aunfl bit is
* set if the result is inex and unfl is signalled.
*
mvout_end:
btst.b #inex2_bit,FPSR_EXCEPT(a6)
beq.b no_aufl
btst.b #unfl_bit,FPSR_EXCEPT(a6)
beq.b no_aufl
bset.b #aunfl_bit,FPSR_AEXCEPT(a6)
no_aufl:
clr.w NMNEXC(a6)
bclr.b #E1,E_BYTE(a6)
fmove.l #0,FPSR ;clear any cc bits from res_func
*
* Return ETEMP to extended format from internal extended format so
* that gen_except will have a correctly signed value for ovfl/unfl
* handlers.
*
bfclr ETEMP_SGN(a6){0:8}
beq.b mvout_con
bset.b #sign_bit,ETEMP_EX(a6)
mvout_con:
rts
*
* This exit is for move-out to int register. The aunfl bit is
* not set in any case for this move.
*
mvouti_end:
clr.w NMNEXC(a6)
bclr.b #E1,E_BYTE(a6)
fmove.l #0,FPSR ;clear any cc bits from res_func
*
* Return ETEMP to extended format from internal extended format so
* that gen_except will have a correctly signed value for ovfl/unfl
* handlers.
*
bfclr ETEMP_SGN(a6){0:8}
beq.b mvouti_con
bset.b #sign_bit,ETEMP_EX(a6)
mvouti_con:
rts
*
* li is used to handle a long integer source specifier
*
li:
moveq.l #4,d0 ;set byte count
btst.b #7,STAG(a6) ;check for extended denorm
bne.w int_dnrm ;if so, branch
fmovem.x ETEMP(a6),fp0
fcmp.d #"$41dfffffffc00000",fp0
* 41dfffffffc00000 in dbl prec = 401d0000fffffffe00000000 in ext prec
fbge.w lo_plrg
fcmp.d #"$c1e0000000000000",fp0
* c1e0000000000000 in dbl prec = c01e00008000000000000000 in ext prec
fble.w lo_nlrg
*
* at this point, the answer is between the largest pos and neg values
*
move.l USER_FPCR(a6),d1 ;use user's rounding mode
andi.l #$30,d1
fmove.l d1,fpcr
fmove.l fp0,L_SCR1(a6) ;let the 040 perform conversion
fmove.l fpsr,d1
or.l d1,USER_FPSR(a6) ;capture inex2/ainex if set
bra.w int_wrt
lo_plrg:
move.l #$7fffffff,L_SCR1(a6) ;answer is largest positive int
fbeq.w int_wrt ;exact answer
fcmp.d #"$41dfffffffe00000",fp0
* 41dfffffffe00000 in dbl prec = 401d0000ffffffff00000000 in ext prec
fbge.w int_operr ;set operr
bra.w int_inx ;set inexact
lo_nlrg:
move.l #$80000000,L_SCR1(a6)
fbeq.w int_wrt ;exact answer
fcmp.d #"$c1e0000000100000",fp0
* c1e0000000100000 in dbl prec = c01e00008000000080000000 in ext prec
fblt.w int_operr ;set operr
bra.w int_inx ;set inexact
*
* wi is used to handle a word integer source specifier
*
wi:
moveq.l #2,d0 ;set byte count
btst.b #7,STAG(a6) ;check for extended denorm
bne.w int_dnrm ;branch if so
fmovem.x ETEMP(a6),fp0
fcmp.s #"$46fffe00",fp0
* 46fffe00 in sgl prec = 400d0000fffe000000000000 in ext prec
fbge.w wo_plrg
fcmp.s #"$c7000000",fp0
* c7000000 in sgl prec = c00e00008000000000000000 in ext prec
fble.w wo_nlrg
*
* at this point, the answer is between the largest pos and neg values
*
move.l USER_FPCR(a6),d1 ;use user's rounding mode
andi.l #$30,d1
fmove.l d1,fpcr
fmove.w fp0,L_SCR1(a6) ;let the 040 perform conversion
fmove.l fpsr,d1
or.l d1,USER_FPSR(a6) ;capture inex2/ainex if set
bra.w int_wrt
wo_plrg:
move.w #$7fff,L_SCR1(a6) ;answer is largest positive int
fbeq.w int_wrt ;exact answer
fcmp.s #"$46ffff00",fp0
* 46ffff00 in sgl prec = 400d0000ffff000000000000 in ext prec
fbge.w int_operr ;set operr
bra int_inx ;set inexact
wo_nlrg:
move.w #$8000,L_SCR1(a6)
fbeq.w int_wrt ;exact answer
fcmp.s #"$c7000080",fp0
* c7000080 in sgl prec = c00e00008000800000000000 in ext prec
fblt.w int_operr ;set operr
bra int_inx ;set inexact <T3>
*
* bi is used to handle a byte integer source specifier
*
bi:
moveq.l #1,d0 ;set byte count
btst.b #7,STAG(a6) ;check for extended denorm
bne.b int_dnrm ;branch if so
fmovem.x ETEMP(a6),fp0
fcmp.s #"$42fe0000",fp0
* 42fe0000 in sgl prec = 40050000fe00000000000000 in ext prec
fbge.w by_plrg
fcmp.s #"$c3000000",fp0
* c3000000 in sgl prec = c00600008000000000000000 in ext prec
fble.w by_nlrg
*
* at this point, the answer is between the largest pos and neg values
*
move.l USER_FPCR(a6),d1 ;use user's rounding mode
andi.l #$30,d1
fmove.l d1,fpcr
fmove.b fp0,L_SCR1(a6) ;let the 040 perform conversion
fmove.l fpsr,d1
or.l d1,USER_FPSR(a6) ;capture inex2/ainex if set
bra.b int_wrt
by_plrg:
move.b #$7f,L_SCR1(a6) ;answer is largest positive int
fbeq.w int_wrt ;exact answer
fcmp.s #"$42ff0000",fp0
* 42ff0000 in sgl prec = 40050000ff00000000000000 in ext prec
fbge.w int_operr ;set operr
bra.b int_inx ;set inexact
by_nlrg:
move.b #$80,L_SCR1(a6)
fbeq.w int_wrt ;exact answer
fcmp.s #"$c3008000",fp0
* c3008000 in sgl prec = c00600008080000000000000 in ext prec
fblt.w int_operr ;set operr
bra.b int_inx ;set inexact
*
* Common integer routines
*
;int_dnrm---account for possible nonzero result for round up with <T3> thru next <T3>
; positive operand and round down for negative answer. In the
; first case (result = 1), byte-width of result in d0 must be
; honored. In the second case, -1 in L_SCR1(a6) will cover
; all contingencies <5/2/91, JPO>.
int_dnrm:
move.l #0,L_SCR1(a6) ;if extended denorm, answer is probably zero
bfextu FPCR_MODE(a6){2:2},d1 ; d1 <- rounding direction <5/2/91, JPO>
cmp.b #2,d1 ; <5/2/91, JPO>
bmi.b int_inx ; if RN or RZ, done <5/2/91, JPO>
bne.b @1 ; if RP, continue below <5/2/91, JPO>
tst.w ETEMP(a6) ; RM: store -1 in L_SCR1 if src is negative <5/2/91, JPO>
bpl.b int_inx ; otherwise, result is zero <5/2/91, JPO>
move.l #-1,L_SCR1(a6) ; <5/2/91, JPO>
bra.b int_inx ; <5/2/91, JPO>
@1:
tst.w ETEMP(a6) ; RP: store +1 of proper width in L_SCR1 if <5/2/91, JPO>
bmi.b int_inx ; src is positive; otherwise, result is zero <5/2/91, JPO>
lea L_SCR1(a6),a1 ; a1 <- addr(L_SCR1) <5/2/91, JPO>
adda.l d0,a1 ; offset by dst width -1 <5/2/91, JPO>
suba.l #1,a1 ; <5/2/91, JPO>
bset.b #0,(a1) ; set low bit at a1 addr <5/2/91, JPO> <T3>
* ;fall through to int_inx
int_inx:
ori.l #inx2a_mask,USER_FPSR(a6)
bra.b int_wrt
int_operr:
fmovem.x fp0,FPTEMP(a6) ;FPTEMP must contain the extended
* ;precision source that needs to be
* ;converted to integer this is required
* ;if the operr exception is enabled.
* ;set operr/aiop (no inex2 on int ovfl)
ori.l #opaop_mask,USER_FPSR(a6)
* ;fall through to perform int_wrt
int_wrt:
move.l EXC_EA(a6),a1 ;load destination address
tst.l a1 ;check to see if it is a dest register
beq.b wrt_dn ;write data register
lea L_SCR1(a6),a0 ;point to supervisor source address
bsr mem_write
bra.w mvouti_end
wrt_dn:
; move.l d0,-(sp) ;d0 currently contains the size to write - deleted <1/7/91, JPO>
; bsr get_fline ;get_fline returns Dn in d0 - deleted <1/7/91, JPO>
; andi.w #$7,d0 ;isolate register - deleted <1/7/91, JPO>
; move.l (sp)+,d1 ;get size - deleted <1/7/91, JPO>
move.l d0,d1 ; byte count to d1 <1/7/91, JPO>
movea.l USER_FPIAR(a6),a0 ; read opcode into d0.w <1/7/91, JPO>
sub.l d0,d0 ; <1/7/91, JPO>
move.w (a0),d0 ; <1/7/91, JPO>
andi.w #7,d0 ;isolate register
cmpi.l #4,d1 ;most frequent case
beq.b sz_long
cmpi.l #2,d1
bne.b sz_con
or.l #8,d0 ;add 'word' size to register#
bra.b sz_con
sz_long:
or.l #$10,d0 ;add 'long' size to register#
sz_con:
move.l d0,d1 ;reg_dest expects size:reg in d1
bsr reg_dest ;load proper data register
bra.w mvouti_end
xp:
lea ETEMP(a6),a0
bclr.b #sign_bit,LOCAL_EX(a0)
sne LOCAL_SGN(a0)
btst.b #7,STAG(a6) ;check for extended denorm
bne.w xdnrm
clr.l d0
bra.b do_fp ;do normal case
sgp:
lea ETEMP(a6),a0
bclr.b #sign_bit,LOCAL_EX(a0)
sne LOCAL_SGN(a0)
btst.b #7,STAG(a6) ;check for extended denorm
bne.w sp_catas ;branch if so
move.w LOCAL_EX(a0),d0
lea sp_bnds,a1
cmp.w (a1),d0
blt.w sp_under
cmp.w 2(a1),d0
bgt.w sp_over
move.l #1,d0 ;set destination format to single
bra.b do_fp ;do normal case
dp:
lea ETEMP(a6),a0
bclr.b #sign_bit,LOCAL_EX(a0)
sne LOCAL_SGN(a0)
btst.b #7,STAG(a6) ;check for extended denorm
bne.w dp_catas ;branch if so
move.w LOCAL_EX(a0),d0
lea dp_bnds,a1
cmp.w (a1),d0
blt.w dp_under
cmp.w 2(a1),d0
bgt.w dp_over
move.l #2,d0 ;set destination format to double
* ;fall through to do_fp
*
do_fp:
bfextu FPCR_MODE(a6){2:2},d1 ;rnd mode in d1
swap d0 ;rnd prec in upper word
add.l d0,d1 ;d1 has PREC/MODE info
clr.l d0 ;clear g,r,s
bsr round ;round
move.l a0,a1
move.l EXC_EA(a6),a0
bfextu CMDREG1B(a6){3:3},d1 ;extract destination format
* ;at this point only the dest
* ;formats sgl, dbl, ext are
* ;possible
cmp.b #2,d1
bgt.b ddbl ;double=5, extended=2, single=1
bne.b dsgl
* ;fall through to dext
dext:
bsr dest_ext
bra.w mvout_end
dsgl:
bsr dest_sgl
bra.w mvout_end
ddbl:
bsr dest_dbl
bra.w mvout_end
*
* Handle possible denorm or catastrophic underflow cases here
*
xdnrm:
bsr.w set_xop ;initialize WBTEMP
bset.b #wbtemp15_bit,WB_BYTE(a6) ;set wbtemp15
move.l a0,a1
move.l EXC_EA(a6),a0 ;a0 has the destination pointer
bsr dest_ext ;store to memory
bset.b #unfl_bit,FPSR_EXCEPT(a6)
bra.w mvout_end
sp_under:
bset.b #etemp15_bit,STAG(a6)
cmp.w 4(a1),d0
ble.b sp_catas ;catastrophic underflow case
move.l #1,d0 ;load in round precision
move.l #sgl_thresh,d1 ;load in single denorm threshold
bsr dpspdnrm ;expects d1 to have the proper
* ;denorm threshold
bsr dest_sgl ;stores value to destination
bset.b #unfl_bit,FPSR_EXCEPT(a6)
bra.w mvout_end ;exit
dp_under:
bset.b #etemp15_bit,STAG(a6)
cmp.w 4(a1),d0
ble.b dp_catas ;catastrophic underflow case
move.l #dbl_thresh,d1 ;load in double precision threshold
move.l #2,d0
bsr dpspdnrm ;expects d1 to have proper
* ;denorm threshold
* ;expects d0 to have round precision
bsr dest_dbl ;store value to destination
bset.b #unfl_bit,FPSR_EXCEPT(a6)
bra.w mvout_end ;exit
*
* Handle catastrophic underflow cases here
*
sp_catas:
* Temp fix for z bit set in unf_sub
move.l USER_FPSR(a6),-(a7)
move.l #1,d0 ;set round precision to sgl
bsr unf_sub ;a0 points to result
move.l (a7)+,USER_FPSR(a6)
move.l #1,d0
sub.w d0,LOCAL_EX(a0) ;account for difference between
* ;denorm/norm bias
move.l a0,a1 ;a1 has the operand input
move.l EXC_EA(a6),a0 ;a0 has the destination pointer
bsr dest_sgl ;store the result
ori.l #unfinx_mask,USER_FPSR(a6)
bra.w mvout_end
dp_catas:
* Temp fix for z bit set in unf_sub
move.l USER_FPSR(a6),-(a7)
move.l #2,d0 ;set round precision to dbl
bsr unf_sub ;a0 points to result
move.l (a7)+,USER_FPSR(a6)
move.l #1,d0
sub.w d0,LOCAL_EX(a0) ;account for difference between
* ;denorm/norm bias
move.l a0,a1 ;a1 has the operand input
move.l EXC_EA(a6),a0 ;a0 has the destination pointer
bsr dest_dbl ;store the result
ori.l #unfinx_mask,USER_FPSR(a6)
bra.w mvout_end
*
* Handle catastrophic overflow cases here
*
sp_over:
* Temp fix for z bit set in unf_sub
move.l USER_FPSR(a6),-(a7)
move.l #1,d0
lea.l FP_SCR1(a6),a0 ;use FP_SCR1 for creating result
move.l ETEMP_EX(a6),(a0)
move.l ETEMP_HI(a6),4(a0)
move.l ETEMP_LO(a6),8(a0)
bsr ovf_res
move.l (a7)+,USER_FPSR(a6)
move.l a0,a1
move.l EXC_EA(a6),a0
bsr dest_sgl
or.l #ovfinx_mask,USER_FPSR(a6)
bra.w mvout_end
dp_over:
* Temp fix for z bit set in ovf_res
move.l USER_FPSR(a6),-(a7)
move.l #2,d0
lea.l FP_SCR1(a6),a0 ;use FP_SCR1 for creating result
move.l ETEMP_EX(a6),(a0)
move.l ETEMP_HI(a6),4(a0)
move.l ETEMP_LO(a6),8(a0)
bsr ovf_res
move.l (a7)+,USER_FPSR(a6)
move.l a0,a1
move.l EXC_EA(a6),a0
bsr dest_dbl
or.l #ovfinx_mask,USER_FPSR(a6)
bra.w mvout_end
*
* DPSPDNRM
*
* This subroutine takes an extended normalized number and denormalizes
* it to the given round precision. This subroutine also decrements
* the input operand's exponent by 1 to account for the fact that
* dest_sgl or dest_dbl expects a normalized number's bias.
*
* Input: a0 points to a normalized number in internal extended format
* d0 is the round precision (=1 for sgl; =2 for dbl)
* d1 is the the single precision or double precision
* denorm threshold
*
* Output: (In the format for dest_sgl or dest_dbl)
* a0 points to the destination
* a1 points to the operand
*
* Exceptions: Reports inexact 2 exception by setting USER_FPSR bits
*
dpspdnrm:
move.l d0,-(a7) ;save round precision
clr.l d0 ;clear initial g,r,s
bsr dnrm_lp ;careful with d0, it's needed by round
bfextu FPCR_MODE(a6){2:2},d1 ;get rounding mode
swap d1
move.w 2(a7),d1 ;set rounding precision
swap d1 ;at this point d1 has PREC/MODE info
bsr round ;round result, sets the inex bit in
* ;USER_FPSR if needed
move.w #1,d0
sub.w d0,LOCAL_EX(a0) ;account for difference in denorm
* ;vs norm bias
move.l a0,a1 ;a1 has the operand input
move.l EXC_EA(a6),a0 ;a0 has the destination pointer
add.w #4,a7 ;pop stack
rts
*
* SET_XOP initialized WBTEMP with the value pointed to by a0
* input: a0 points to input operand in the internal extended format
*
set_xop:
move.l LOCAL_EX(a0),WBTEMP_EX(a6)
move.l LOCAL_HI(a0),WBTEMP_HI(a6)
move.l LOCAL_LO(a0),WBTEMP_LO(a6)
bfclr WBTEMP_SGN(a6){0:8}
beq.b sxop
bset.b #sign_bit,WBTEMP_EX(a6)
sxop:
bfclr STAG(a6){5:4} ;clear wbtm66,wbtm1,wbtm0,sbit
rts
*
* P_MOVE
*
p_movet: ; table modified <1/7/91, JPO>
dc.w p_move-p_movet
dc.w p_movez-p_movet
dc.w p_movei-p_movet
dc.w p_moven-p_movet
dc.w p_move-p_movet
p_regd: ; table modified <1/7/91, JPO>
dc.w p_dyd0-p_regd
dc.w p_dyd1-p_regd
dc.w p_dyd2-p_regd
dc.w p_dyd3-p_regd
dc.w p_dyd4-p_regd
dc.w p_dyd5-p_regd
dc.w p_dyd6-p_regd
dc.w p_dyd7-p_regd
pack_out:
lea.l p_movet,a0 ;load jmp table address
move.w STAG(a6),d0 ;get source tag
bfextu d0{16:3},d0 ;isolate source bits
; move.l (a0,d0.w*4),a0 ;load a0 with routine label for tag - deleted <1/7/91, JPO>
adda.w (a0,d0.w*2),a0 ; <1/7/91, JPO>
jmp (a0) ;go to the routine
p_write:
; move.l #$0c,d0 ;get byte count - deleted <1/7/91, JPO>
move.l EXC_EA(a6),a1 ;get the destination address
; bsr mem_write ;write the user's destination - deleted <1/7/91, JPO>
move.l (a0)+,(a1)+ ; write 12 bytes <1/7/91, JPO>
move.l (a0)+,(a1)+ ; <1/7/91, JPO>
move.l (a0),(a1) ; <1/7/91, JPO>
move.b #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.
*
; Fixed static k-factor case to work around hardware bug for k = 4 <4/26/91, JPO> <T3>
p_move:
move.w CMDREG1B(a6),d0
btst.l #kfact_bit,d0 ;test for dynamic k-factor
; beq.b statick ;if clear, k-factor is static - DELETED <4/26/91, JPO> <T3>
bne.b dynamick ; <4/26/91, JPO> <T3>
movea.l USER_FPIAR(a6),a0 ; get k-factor from instruction <4/26/91, JPO> <T3>
move.l (a0),d0 ; <T3>
bra.b statick ; continue below <4/26/91, JPO> <T3>
dynamick:
bfextu d0{25:3},d0 ;isolate register for dynamic k-factor
lea p_regd,a0
; move.l (a0,d0*4),a0 ; deleted <1/7/91, JPO>
adda.w (a0,d0.w*2),a0 ; <1/7/91, JPO>
jmp (a0)
statick:
andi.w #$007f,d0 ;get k-factor
bfexts d0{25:7},d0 ;sign extend d0 for bindec
lea.l ETEMP(a6),a0 ;a0 will point to the packed decimal
bsr bindec ;perform the convert; data at a6
lea.l FP_SCR1(a6),a0 ;load a0 with result address
bra.b p_write
p_movez:
lea.l ETEMP(a6),a0 ;a0 will point to the packed decimal
clr.w 2(a0) ;clear lower word of exp
clr.l 4(a0) ;load second lword of ZERO
clr.l 8(a0) ;load third lword of ZERO
bra.b p_write ;go write results
p_movei:
fmove.l #0,FPSR ;clear aiop
lea.l ETEMP(a6),a0 ;a0 will point to the packed decimal
clr.w 2(a0) ;clear lower word of exp
bra.b p_write ;go write the result
p_moven:
lea.l ETEMP(a6),a0 ;a0 will point to the packed decimal
clr.w 2(a0) ;clear lower word of exp
bra.b p_write ;go write the result
*
* Routines to read the dynamic k-factor from Dn.
*
p_dyd0:
move.l USER_D0(a6),d0
bra.b statick
p_dyd1:
move.l USER_D1(a6),d0
bra.b statick
p_dyd2:
move.l d2,d0
bra.b statick
p_dyd3:
move.l d3,d0
bra.b statick
p_dyd4:
move.l d4,d0
bra.b statick
p_dyd5:
move.l d5,d0
bra.b statick
p_dyd6:
move.l d6,d0
bra.b statick
p_dyd7:
move.l d7,d0
bra.b statick