mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-11-25 09:30:50 +00:00
2165 lines
62 KiB
Plaintext
2165 lines
62 KiB
Plaintext
;
|
|
; 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
|
|
|
|
|