; ; File: FPHWArith.a ; ; Contains: HW Floating Point routines for arithmetic functions of FP68K ; ; Written by: Apple Numerics Group, DSG ; ; Copyright: © 1985-1993 by Apple Computer, Inc., all rights reserved. ; ; Change History (most recent first): ; ; 2/3/93 CSS Update from Horror: ;

9/29/92 BG Rolling in Jon Okada's latest fixes. ; <1> 10/24/91 SAM/KSM Rolled in Regatta file. ; ; Regatta Change History: ; ; <2> 5/28/91 SAM Merged from TERROR [<3> Added Stuart McDonald's latest fixes ; (see below for details)] ; <1> 5/15/91 SAM Split off from TERROR Proj. ; ; Terror Change History: ; ; <2> 1/9/91 BG Corrected an INCLUDE which did not have the filename changed. ; <1> 01/06/90 BG Added to TERROR/BBS for the time. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; File: FP881arith.a ;; Implementation of FP68K arithmetic functions calling MC68881. ;; Copyright Apple Computer, Inc. 1985,1986,1987,1989,1990 ;; All Rights Reserved ;; Confidential and Proprietary to Apple Computer,Inc. ;; ;; Written by Clayton Lewis, begun 8 Feb 85. ;; Debugged by Stuart McDonald. ;; ;; Modification history: ;; Rev2: 16 May 85 ;; Rev3: 17 May 85 ;; Rev5: 27 May 85 ;; Rev9: 17 Jun 85 ;; Rev10:19 Dec 85 streamline convert and scalb in concert with FPCtrl ;; Rev11:16 Jun 86 CRL moved to MPW ;; Rev12:29 Sep 86 Made E2{int,long} handle {NaN,Inf,Out-of-range} ala SANE. -S.McD. ;; 29 Sep 86 Fixed spurious inexact in X2C's NanCreated. -S.McD. ;; 26 Dec 86 Comp2X no longer stomps on its input. -S.McD. ;; 27 Dec 86 Changes to ExitArgs routines: -S.McD. ;; 1) Added entry ExitProcExit for ProcExit finale; ;; 2) Now restore FPSR/FPCR before halt check. ;; 3) CompareCtnd now returns CCR in A1 instead of D1. ;; 05 Jan 87 Fixed Classify; now handles DENORMALNUMs properly. -S.McD. ;; 05 Jan 87 Scalb doesn't truncate subnormals to zero anymore. -S.McD. ;; 05 Jan 87 Logb now handles zero and INF properly. -S.McD. ;; 07 Jan 87 Scalb two days ago was buggy. Fixed now. -S.McD. ;; 11 Jan 87 Scalb doesn't truncate huge numbers to INF anymore. -S.McD. ;; 15 Jan 87 Changed status and copyright information. -S.McD. ;; 21 Jan 87 Cleaned up CompareCtnd code. -S.McD. ;; 21 Jan 87 Cleaned up CPXxxx entries. -S.McD. ;; 21 Jan 87 Cleaned up MoveQuot. -S.McD. ;; 21 Jan 87 Cleaned up NaNInput. -S.McD. ;; 23 Jan 87 Sqrt,Add,Div,Mul,Rem now return Apple's NaN codes. -S.McD. ;; 29 Jan 87 Apple NaN logic speeded up. -S.McD. ;; 29 Jan 87 Tossed FSCALE. It prematurely UNFLs/OVFLs on A93N mask. -S.McD. ;; Rev13:18 Dec 89 Complete rewrite begins. -S.McD. ;; 22 May 90 Goes alpha for waimea. Copyrights updated. -S.McD. ;; 7 Sep 90 Goes beta for Tim. Updated version number. -S.McD. ;; 30 Sep 90 Goes final for Terror alpha. -S.McD. ;; 19 May 91 Fixed spurious flag errors for FMOVEs in trap enabled cases.-S.McD. ;; 21 May 91 After first trap, futher traps MUST be disabled fix. -S.McD. ;; 30 Mar 92 Fixed Scalb bug for SRC = $8000 and replaced FSCALEs with FMULs. - JPO ;; Fixed X2C conversion bug for single/double rounding precision. - JPO ;; Modified Logb to not trap on denormal input. - JPO ;; 6 Apr 92 Modified subroutine COMP2FP1r to correctly handle negative comp ;; input when rounding precision is less than extended. - JPO ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Convert (on stack) source comp to FP1. - DELETED <4/6/92, JPO> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;COMP2FP1r: ; MOVEA.L LKSRC(A6),A0 ; ADDQ #4,a0 ; MOVE.L (A0),-(SP) ; MOVE.L -(A0),-(SP) ; MOVE.L #$403E403E,-(SP) ; FMOVE.X (SP)+,FP1 ; TST.B (A0) ; BPL.S @1 ; FSUB.S #"$5f800000",FP1 ; FCMP.S #"$df000000",FP1 ; FBNE.W @1 ; FMOVE.S #"$7fc01400",FP1 ;@1: RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Convert (on stack) source comp to FP1. - New routine avoids ;; loading unnormalized extended values <4/6/92, JPO> ;; ;; Stack: &ret ;; Uses: A0, FP1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; COMP2FP1r: movem.l d0-d3,-(sp) ; save four D registers, STACK: D0-D3 save < &ret movea.l LKSRC(a6),a0 ; move comp to D1-D2 move.w #$403e,d0 ; D0.W <- sign/exponent of +2^63 move.l 4(a0),d2 ; D2 <- comp.LO move.l (a0),d1 ; D1 <- comp.HI bpl.b @1 ; nonnegative input neg.l d2 ; comp is negative or NaN: negate comp or.w #$8000,d0 ; set sign bit in D0.W (doesn't affect X bit) negx.l d1 bpl.b @1 ; valid negative comp ; comp NaN result is NaN(20) fmove.s #"$7fc01400",fp1 ; get result into FP1 bra.b @restore ; restore D registers ; unnormalized extended value (may be zero) in D0.W, D1, D2. @1: bne.b @2 ; D1 (sig.HI) > 0 exg d1,d2 ; D1 is zero, exchange D1/D2 sub.w #32,d0 ; adjust exponent bfffo d1{0:32},d3 ; find leading one in D1 (new sig.HI) beq.b @zero ; zero result sub.w d3,d0 ; nonzero D1, adjust exponent suba.w #12,sp ; reserve stack space for result lsl.l d3,d1 ; normalize significand (D2 <- 0) move.w d0,(sp) ; write sign/exponent to stack bra.b @wrsig ; write significand to stack ; zero result (D1, D2 <- 0) @zero: fmove.s d1,fp1 ; load result into FP1 bra.b @restore ; restore D registers ; sig.HI > 0 @2: bfffo d1{0:32},d3 ; find leading one in sig.HI suba.w #12,sp ; reserve stack space for result sub.w d3,d0 ; adjust exponent lsl.l d3,d1 ; shift sig.HI to normalize move.w d0,(sp) ; write sign/exponent to stack bfextu d2{0:d3},d0 ; extract high bits to be shifted out of sig.LO lsl.l d3,d2 ; shift sig.LO or.l d0,d1 ; insert new low bits into sig.HI @wrsig: move.l d2,8(sp) ; write significand to stack move.l d1,4(sp) fmove.x (sp)+,fp1 ; pop result into FP1, STACK: D0-D3 save < &ret @restore: movem.l (sp)+,d0-d3 ; restore D registers, STACK: &ret rts ; return MACRO COMP2FP1 .* Convert (on stack) source comp to FP1. BSR.W COMP2FP1r ENDM MACRO SRC2STK .* Convert 80bit source to 96bits on stack. MOVEA.L LKSRC(A6),A0 ADDQ #6,A0 MOVE.L (A0),-(SP) MOVE.L -(A0),-(SP) SUBQ #2,A0 MOVE.L (A0),-(SP) ENDM MACRO DST2STK .* Convert 80bit destination to 96bits on stack. MOVEA.L LKDST(A6),A0 ADDQ #6,A0 MOVE.L (A0),-(SP) MOVE.L -(A0),-(SP) SUBQ #2,A0 MOVE.L (A0),-(SP) ENDM MACRO FOPSRC.&SZ &FOP IF &SZ = 'X' THEN &FOP..X (SP),FP0 .* ELSEIF &SZ = 'C' THEN &FOP..X FP1,FP0 .* ELSE .* All others (.W, .L, .S, .D): operate directly into FP0. &FOP..&SZ ([LKSRC,A6]),FP0 ENDIF ENDM MACRO POPFOPSRC.&SZ &FOP IF &SZ = 'X' THEN &FOP..X (SP)+,FP0 .* ELSEIF &SZ = 'C' THEN &FOP..X FP1,FP0 .* ELSE .* All others (.W, .L, .S, .D): operate directly into FP0. &FOP..&SZ ([LKSRC,A6]),FP0 ENDIF ENDM MACRO FPU2DST .* Convert FP0 to 80bits destination pointed to by A0. FMOVE.X FP0,(SP) MOVE.W (SP)+,(A0)+ ADDQ.L #2,SP MOVE.L (SP)+,(A0)+ MOVE.L (SP)+,(A0) ENDM MACRO FPU2DST8096 BTST #FPX96BIT,LKOP+3(A6); 96 BIT SET? BEQ.S @1 ; IF NOT, BRANCH: WRITE 80 BIT ANSWER ; WRITE 96 BIT ANSWER CASE: FMOVE.X FP0,(A0) ; (NOTE: A0 SET IN CORRESPONDING 96 BIT ROUTINE) BRA.S @2 ; BRANCH: EXIT @1: ; WRITE 80 BIT ANSWER CASE: FPU2DST @2: ENDM MACRO PUSHFPU2DST .* Convert FP0 to 80bits destination pointed to by A0. FMOVE.X FP0,-(SP) MOVE.W (SP)+,(A0)+ ADDQ.L #2,SP MOVE.L (SP)+,(A0)+ MOVE.L (SP)+,(A0) ENDM MACRO FPU2SRC .* Convert FP0 to 80bits source pointed to by A0; A0 unchanged. .* This macro is used by QNEXTX in file "FP881nonarith.a". FMOVE.X FP0,-(SP) MOVE.W (SP)+,(A0)+ ADDQ #2,SP MOVE.L (SP)+,(A0)+ MOVE.L (SP)+,(A0) SUBQ #6,A0 ENDM MACRO QOP.&SZ &FOP,&OPCODE .* Link required by trap handler to determine arguments. LINK A6,#-LK2SIZE .* .* Popsize and opcode required by trap handler. MOVE.L #LK2POP+&OPCODE,LKOP(A6) .* MOVE.L A0,-(SP) IF &SZ = 'X' THEN .* Convert extended source onto stack. SRC2STK .* ELSEIF &SZ = 'C' THEN .* Convert comp sources into FP1. COMP2FP1 ENDIF IF &FOP <> 'FMOVE' THEN .* Skip picking up DST if doing a conversion (FMOVE). DST2STK .* IF &SZ = 'X' THEN FMOVEM.X (SP)+,FP0 ;<5/19/91-S.McD.> ELSE FMOVEM.X (SP),FP0 ;<5/19/91-S.McD.> ENDIF .* ENDIF FOPSRC.&SZ &FOP IF &FOP <> 'FMOVE' THEN FPU2DST ELSE MOVEA.L LKDST(A6),A0 IF &SZ = 'X' THEN FPU2DST ELSE PUSHFPU2DST ENDIF ENDIF MOVE.L (SP)+,A0 UNLK A6 RTD #8 ENDM MACRO FPSR2CC .* Map FPU's CC's to CPU's CC's. FMOVE.L FPSR,D0 BFEXTU D0{4:4},D0 MOVE (CCMAP,PC,D0*2),CCR ENDM MACRO FPSR2CCX .* Map FPU's CC's to CPU's CC's AND SIGNAL! FPSR2CC FBSF.W CCMAP ; NEVER BRANCHES. SIDE-EFFECT: SIGNALS FNOP ; FNOP CATCHES SIGNAL BEFORE FINAL RTD ENDM MACRO QCPX.&SZ &FOP,&OPCODE .* .* Link required by trap handler to determine arguments. LINK A6,#-LK2SIZE .* .* Popsize and opcode required by trap handler. MOVE.L #LK2POP+&OPCODE,LKOP(A6) .* MOVEM.L D0/A0,-(SP) ; MOVEM USED BECAUSE CCR IS UNAFFECTED IF &SZ = 'X' THEN .* Convert extended source onto stack. SRC2STK .* ELSEIF &SZ = 'C' THEN .* Convert comp sources into FP1. COMP2FP1 ENDIF DST2STK FMOVEM.X (SP)+,FP0 ;<5/19/91-S.McD.> POPFOPSRC.&SZ &FOP FPSR2CCX MOVEM.L (SP)+,D0/A0 ; MOVEM USED BECAUSE CCR IS UNAFFECTED UNLK A6 RTD #8 ENDM MACRO QCMP.&SZ &FOP,&OPCODE .* .* Link required by trap handler to determine arguments. LINK A6,#-LK2SIZE .* .* Popsize and opcode required by trap handler. MOVE.L #LK2POP+&OPCODE,LKOP(A6) .* MOVEM.L D0/A0,-(SP) ; MOVEM USED BECAUSE CCR IS UNAFFECTED IF &SZ = 'X' THEN .* Convert extended source onto stack. SRC2STK .* ELSEIF &SZ = 'C' THEN .* Convert comp sources into FP1. COMP2FP1 ENDIF DST2STK FMOVEM.X (SP)+,FP0 ;<5/19/91-S.McD.> POPFOPSRC.&SZ &FOP FPSR2CC MOVEM.L (SP)+,D0/A0 ; MOVEM USED BECAUSE CCR IS UNAFFECTED UNLK A6 RTD #8 ENDM MACRO QREM.&SZ &FOP,&OPCODE .* .* Link required by trap handler to determine arguments. LINK A6,#-LK2SIZE .* .* Popsize and opcode by trap handler. MOVE.L #LK2POP+&OPCODE,LKOP(A6) .* MOVE.L A0,-(SP) IF &SZ = 'X' THEN .* Convert extended source onto stack. SRC2STK .* ELSEIF &SZ = 'C' THEN .* Convert comp sources into FP1. COMP2FP1 ENDIF DST2STK .* IF &SZ = 'X' THEN FMOVEM.X (SP)+,FP0 ;<5/19/91-S.McD.> ELSE FMOVEM.X (SP),FP0 ;<5/19/91-S.McD.> ENDIF FOPSRC.&SZ &FOP FPU2DST BSR MOVEQUOT MOVE.L (SP)+,A0 UNLK A6 RTD #8 ENDM MACRO QX2Z.&SZ &FOP,&OPCODE .* .* Link required by trap handler to determine arguments. LINK A6,#-LK2SIZE .* .* Popsize and opcode required by trap handler. MOVE.L #LK2POP+&OPCODE,LKOP(A6) .* MOVE.L A0,-(SP) SRC2STK IF &SZ = 'C' THEN ; CONVERT EXTENDED TO COMP CODE: FMOVEM.X (SP),FP0 ; FETCH SRC, AVOIDS SPURIOUS UNFL <5/19/91-S.McD.> ; Old X2C conversion routines DELETED <3/30/92, JPO> ; ;QX2CENTRY: FTEST.X FP0 ; SNAN SIGNALS, SUBNORMS DON'T <5/19/91-S.McD.> ; FBOR.W @0 ; NAN? IF NOT, BRANCH: ORDERED ; ; ; NAN CASE: ; FMOVECR #$32,FP0 ; FP0'S SIGNIFICAND: 8000000000000000 ; BRA.S @3 ; BRANCH: WRITE SIGNIFICAND ; ;@0: ; ORDERED CASE: ; FMOVEM.L FPCR/FPSR,-(SP) ; SAVE ENV. TO AVOID SPURIOUS SIGS. ; MOVE.L (SP),-(SP) ; REPLICATE FPCR ; CLR.B 2(SP) ; CLEAR FPCR EXCEPTION ENABLES (FIG.1-1, 881/2 MAN.) ; FMOVE.L (SP)+,FPCR ; DISABLE TRAPPING FOR NEXT OPS ; FINT.X FP0,FP0 ; SPURIOUS INEXACT DISABLED! ; ; FGETEXP FP0,FP1 ; FP1 := BINADE OF FINT(SRC) ; ; FSUB.W #63,FP1 ; |FINT(SRC)| < 2^63? ; FBOLT.W @1 ; IF SO, BRANCH: TYPICAL CASE ; ; ; OUT-OF-RANGE CASE: (RTN COMP NAN) ; FMOVEM.L (SP)+,FPCR/FPSR ; RESTORE ORIGINAL ENVIRONMENT ; FMOVECR #$32,FP0 ; FP0'S SIGNIFICAND: 8000000000000000 ; FMOVE.S #"$7FBFFFFF",FP1 ; SIGNAL INVALID USING FLOAT SNAN ; FNOP ; (FNOP CATCHES SIG. BEFORE RTS) ; BRA.S @3 ; BRANCH: WRITE SIGNIFICAND ; ;@1: ; TYPICAL CASE: ; FMOVEM.L (SP)+,FPCR/FPSR ; RESTORE ENVIRONMENT ; FINT.X (SP),FP0 ; RE-FETCH INPUT FOR INEXACT SIG. ; FBUGE.W @2 ; FINT(SRC) >= 0? BRANCH: POS. CASE ; ; ; NEGATIVE CASE: ; FADD.S #"$5f800000",FP0 ; CONVERT SIGN-MAG TO 2'S COMPLEMENT ; BRA.S @3 ; BRANCH: WRITE SIGNIFICAND ; ;@2: ; POSITIVE CASE: ; FMOVE.X FP0,(SP) ; TEMPORARILY WRITE EXTENDED TO MEM ; CLR.W (SP) ; CLEAR ITS EXPONENT FIELD ; FMOVE.X (SP),FP0 ; RESTORE AS SUBNORMAL ; ; FMOVE.L FPCR,-(SP) ; SAVE FPCR (ÉONLY IF UNFL ENABLED) ; FMOVE.L #0,FPCR ; DISABLE UNFL (ÉONLY IF UNFL ENABLED) ; FSCALE FP1,FP0 ; AND RIGHT JUSTIFY SIGNIFICAND EXACTLY! ; FMOVE.L (SP)+,FPCR ; RESTORE FPCR (ÉONLY IF UNFL ENABLED) ; ;@3: ; WRITE SIGNIFICAND CASE: ; FMOVEM.X FP0,(SP) ; GRAB EXPONENT AND SIGNIFICAND ; ADDQ #4,SP ; POP EXPONENT, LEAVING 64BIT COMP ; MOVEA.L LKDST(A6),A0 ; A0 := ADDRESS OF DST ; MOVE.L (SP)+,(A0)+ ; POP 1ST HALF OF COMP TO DST ; MOVE.L (SP)+,(A0) ; POP 2ND HALF OF COMP TO DST ; New X2C conversion routine avoids flushing to zero via FSCALE when rounding precision is ; single or double. This routine avoids FSCALE, FINT, and FGETEXP instructions. <3/30/92, JPO> QX2CENTRY: move.l d0,-(sp) ; save D0 fmove.l fpcr,d0 ; D0 <- user's FPCR move.l d0,-(sp) ; push user's FPCR on stack, STACK: FPCR < D0 < xsrc < A0 < . . . andi.w #$ff3f,d0 ; set rounding precision to extended fmove.l d0,fpcr ftest.x fp0 ; SNaN will signal fboge @xpos ; xsrc >= 0.0 fcmp.x @LIMNEG,fp0 ; negative xsrc definitely in comp range? fbogt @xneg ; yes fbun @xnan ; xsrc is a NaN fblt @xinval ; xsrc is out of range of comp ; Negative xsrc is borderline (out of range if rounding is down or to nearest) btst.l #4,d0 ; round down or to nearest? bne.b @xneg ; no ; Out-of-range conversion signals invalid and returns comp NaN @xinval: fmove.s #"$7fbfffff",fp1 ; signal invalid ; NaN input returns comp NaN (invalid signaled by signaling NaN input) @xnan: fmove.w #1,fp0 ; put comp NaN pattern in significand fmove.l (sp)+,FPCR ; restore user's FPCR, stack: D0 save < x < A0 < ... fmove.x fp0,4(sp) ; put extended result on stack bra.b @deliver ; deliver comp result @LIMNEG: ; nextextended(-2.0^63,0.0) dc.l $c03d0000, $ffffffff, $ffffffff @LIMPOS: ; nextextended(2.0^63,0.0) dc.l $403d0000, $ffffffff, $ffffffff ; Negative xsrc will be in range of comp after rounding to integral value. Subtract ; 2^63 from xsrc, thus rounding to integral value and placing (comp) abs(xsrc) in lowest ; 63 bits of significand while setting explicit bit. @xneg: fsub.s #"$5f000000",fp0 ; subtract 2^63, forcing rounding (sets deserved INEX) bra.b @inrange ; deliver in-range result ; Source argument is nonnegative @xpos: fcmp.x @LIMPOS,fp0 ; in comp range? fbolt @xpos1 ; definitely fbogt @xinval ; definitely not ; positive xsrc is borderline (out of range if rounding is up or to nearest addi.b #$10,d0 ; round up or to nearest? lsl.b #2,d0 bpl.b @xinval ; yes. out-of-range case ; Nonnegative xsrc will be in range of comp after rounding to integral value. Add ; 2^63 to xsrc, thus rounding to integral value and placing (comp) xsrc in lowest ; 63 bits of significand while setting explicit bit. @xpos1: fadd.s #"$5f000000",fp0 ; add 2^63, forcing rounding (sets deserved INEX) @inrange: fmove.l (sp)+,FPCR ; restore caller's FPCR, stack: D0 save < x < A0 < ... fmovem.x fp0,4(sp) ; write rounded intermediate to stack bclr.b #7,8(sp) ; clear explicit bit tst.b 4(sp) ; negative xsrc? bpl.b @deliver ; no, deliver result neg.l 12(sp) ; yes, negate significand on stack negx.l 8(sp) @deliver: move.l (sp)+,d0 ; restore D0, stack: x < A0 < ... movea.l LKDST(a6),a0 ; A0 <- addr of DST addq #4,sp ; pop significand move.l (sp)+,(a0)+ ; write comp result (significand) move.l (sp)+,(a0) .* ELSEIF (&SZ = 'W') OR (&SZ = 'L') THEN .* ; X-TO-{W OR L}; AVOIDS (INV,INX) RTN FMOVEM (SP)+,FP0 ; FP0 := SRC <5/19/91-S.McD.> FMOVEM.L FPCR/FPSR,-(SP) ; SAVE ENV. TO AVOID SPURIOUS INX MOVE.L (SP),-(SP) ; REPLICATE FPCR CLR.B 2(SP) ; CLEAR ALL TRAP ENABLES <5/21/91-S.McD.> FMOVE.L (SP)+,FPCR ; DISABLE TRAPPING FOR NEXT FOP MOVEA.L LKDST(A6),A0 ; A0 := ADDRESS OF DST FMOVE.&SZ FP0,(A0) ; SPURIOUS INEXACT DISABLED! FMOVE.L FPSR,-(SP) ; PICK UP PREVIOUS OP FLAGS BTST.B #5,2(SP) ; DID IT SIGNAL INVALID? ADDQ #4,SP ; POP IT (CC NOT EFFECTED) FMOVEM.L (SP)+,FPCR/FPSR ; RESTORE OLD ENV (CC NOT EFFECTED) BEQ.S @0 ; IF NOT, BRANCH: TYPICAL CASE ; INVALID CASE: FMOVE.S #"$7FBFFFFF",FP1 ; SIGNAL INVALID USING FLOAT SNAN BRA.S @1 @0: ; TYPICAL CASE: FMOVE.&SZ FP0,(A0) ; RE-EXECUTE TO PICK UP INX SIG. @1: FNOP ; (FNOP CATCHES SIG. BEFORE RTS) ELSE .* .* All others {.S OR .D}: move directly into memory. FMOVEM (SP)+,FP0 ;<5/19/91-S.McD.> MOVEA.L LKDST(A6),A0 &FOP..&SZ FP0,(A0) FNOP ; (FNOP CATCHES SIG. BEFORE RTS) ENDIF MOVE.L (SP)+,A0 UNLK A6 RTD #8 ENDM MACRO QOP1.&SZ &FOP,&OPCODE .* This macro is used by the one argument functions: FSQRT, FINT, FINTRZ. .* .* Link required by trap handler to determine arguments. LINK A6,#-LK1SIZE .* .* Popsize and opcode required by trap handler. MOVE.L #LK1POP+&OPCODE,LKOP(A6) .* MOVE.L A0,-(SP) DST2STK &FOP..X (SP),FP0 FPU2DST MOVE.L (SP)+,A0 UNLK A6 RTD #4 ENDM INCLUDE 'FPHWArith96.A' ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ADDITION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QADDX: QOP.X FADD, FFEXT+FOADD QADDS: QOP.S FADD, FFSGL+FOADD QADDD: QOP.D FADD, FFDBL+FOADD QADDI: QOP.W FADD, FFINT+FOADD QADDL: QOP.L FADD, FFLNG+FOADD QADDC: QOP.C FADD, FFCOMP+FOADD ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SUBTRACTION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QSUBX: QOP.X FSUB, FFEXT+FOSUB QSUBS: QOP.S FSUB, FFSGL+FOSUB QSUBD: QOP.D FSUB, FFDBL+FOSUB QSUBI: QOP.W FSUB, FFINT+FOSUB QSUBL: QOP.L FSUB, FFLNG+FOSUB QSUBC: QOP.C FSUB, FFCOMP+FOSUB ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; MULTIPLICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QMULX: QOP.X FMUL, FFEXT+FOMUL QMULS: QOP.S FMUL, FFSGL+FOMUL QMULD: QOP.D FMUL, FFDBL+FOMUL QMULI: QOP.W FMUL, FFINT+FOMUL QMULL: QOP.L FMUL, FFLNG+FOMUL QMULC: QOP.C FMUL, FFCOMP+FOMUL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; DIVISION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QDIVX: QOP.X FDIV, FFEXT+FODIV QDIVS: QOP.S FDIV, FFSGL+FODIV QDIVD: QOP.D FDIV, FFDBL+FODIV QDIVI: QOP.W FDIV, FFINT+FODIV QDIVL: QOP.L FDIV, FFLNG+FODIV QDIVC: QOP.C FDIV, FFCOMP+FODIV ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; COMPARISON ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QCPXX: QCPX.X FCMP, FFEXT+FOCPX QCPXS: QCPX.S FCMP, FFSGL+FOCPX QCPXD: QCPX.D FCMP, FFDBL+FOCPX QCPXI: QCPX.W FCMP, FFINT+FOCPX QCPXL: QCPX.L FCMP, FFLNG+FOCPX QCPXC: QCPX.C FCMP, FFCOMP+FOCPX CCMAP: DC.W $0000,$0002,$0000,$0002,$0004,$0002,$0002,$0002 DC.W $0019,$0002,$0019,$0002,$0004,$0002,$0002,$0002 QCMPX: QCMP.X FCMP, FFEXT+FOCMP QCMPS: QCMP.S FCMP, FFSGL+FOCMP QCMPD: QCMP.D FCMP, FFDBL+FOCMP QCMPI: QCMP.W FCMP, FFINT+FOCMP QCMPL: QCMP.L FCMP, FFLNG+FOCMP QCMPC: QCMP.C FCMP, FFCOMP+FOCMP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; REMAINDER ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QREMX: QREM.X FREM, FFEXT+FOREM QREMS: QREM.S FREM, FFSGL+FOREM QREMD: QREM.D FREM, FFDBL+FOREM QREMI: QREM.W FREM, FFINT+FOREM QREML: QREM.L FREM, FFLNG+FOREM QREMC: QREM.C FREM, FFCOMP+FOREM ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Collect the quotient from the 68881 status register, ;; convert from signed-magnitude to two's-complement, ;; and stuff quotient bits into user's D0. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MOVEQUOT: FMOVE FPSR,D0 ; gather QUOT SWAP D0 ; move to low order byte BCLR #7,D0 ; is QUOT negative? BEQ.S @1 ; if not, skip negate code NEG.B D0 @1 EXT.W D0 ; word's worth RTS ; PROBLEM: USER'S FREM TRAP HANDLER CAN'T GET AT D0! ; SOLUTION: ...emulator will handle ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; CONVERSIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QX2X: QOP.X FMOVE, FFEXT+FOZ2X QS2X: QOP.S FMOVE, FFSGL+FOZ2X QD2X: QOP.D FMOVE, FFDBL+FOZ2X QI2X: QOP.W FMOVE, FFINT+FOZ2X QL2X: QOP.L FMOVE, FFLNG+FOZ2X QC2X: QOP.C FMOVE, FFCOMP+FOZ2X QX2S: QX2Z.S FMOVE, FFSGL+FOX2Z QX2D: QX2Z.D FMOVE, FFDBL+FOX2Z QX2I: QX2Z.W FMOVE, FFINT+FOX2Z QX2L: QX2Z.L FMOVE, FFLNG+FOX2Z QX2C: QX2Z.C FMOVE, FFCOMP+FOX2Z ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SQUARE ROOT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QSQRTX: QOP1.X FSQRT, FOSQRT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ROUND TO INTEGRAL VALUE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QRINTX: QOP1.X FINT, FORTI ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; TRUNCATE TO INTEGRAL VALUE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QTINTX: QOP1.X FINTRZ, FOTTI ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SCALB ...THIS CODE WILL FLUSH SUBNORMS TO ZERO ON OLD MASK A93N 881s! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QSCALBX: ; NOTE: FSCALE.W REQUIRES |D0| < 2^14. LINK A6,#-LK2SIZE MOVE.L #LK2POP+FFINT+FOSCALB,LKOP(A6) MOVEM.L D0/A0,-(SP) DST2STK FMOVEM.X (SP),FP0 ;<5/19/91-S.McD.> QSCALBENTRY: SUBA.W #12,SP ; reserve 12 bytes for 2^n factor <3/30/92, JPO> MOVE.L #$80000000,4(SP) ; initialize significand of factor <3/30/92, JPO> CLR.L 8(SP) ; <3/30/92, JPO> MOVE.W ([LKSRC,A6]),D0 ; DO := SCALE FACTOR BPL.S @1 ; BRANCH: D0 >= 0 ; D0 < 0 CASE: CMP #$C000,D0 ; D0 > -2^14? BGT.S @2 ; IF SO, ONE FSCALE WILL DO ; IF NOT, D0 =< -2^14 CASE: ; ADD #$4000,D0 ; D0 := D0 + 2^14 - DELETED <3/30/92, JPO> ; FSCALE.W #$E000,FP0 ; FP0 := - DELETED <3/30/92, JPO> ; FSCALE.W #$E000,FP0 ; FP0 * 2^(-2^14) - DELETED <3/30/92, JPO> ; BRA.S @2 ; BRANCH: MULTIPLY BY 2^D0 - DELETED <3/30/92, JPO> CLR.W (SP) ; zero sign/exponent of factor <3/30/92, JPO> ADD.W #$3FFF,D0 ; adjust scale factor up by 2^14 - 1 <3/30/92, JPO> FMUL.X (SP),FP0 ; scale by -(2^14 - 1) <3/30/92, JPO> CMP.W #$C000,D0 ; final scaling still out of range? <3/30/92, JPO> BGT.S @2 ; no. do final scaling below <3/30/92, JPO> FMUL.X (SP),FP0 ; yes. scale down by 2^14 - 1 again <3/30/92, JPO> ADD.W #$3FFF,D0 ; adjust scale factor again <3/30/92, JPO> BRA.S @2 ; finish up below <3/30/92, JPO> @1: ; D0 >= 0 CASE: CMP #$4000,D0 ; D0 < 2^14? BLT.S @2 ; IF SO, ONE FSCALE WILL DO ; IF NOT, D0 >= 2^14 MOVE.W #$7FFE,(SP) ; create factor of 2^(2^14 - 1) <3/30/92, JPO> SUB #$4000,D0 ; D0 := D0 - 2^14 ; FSCALE.W #$2000,FP0 ; FP0 := - DELETED <3/30/92, JPO> ; FSCALE.W #$2000,FP0 ; FP0 * 2^( 2^14) - DELETED <3/30/92, JPO> FMUL.X (SP),FP0 ; scale by 2^14 <3/30/92, JPO> FADD.X FP0,FP0 ; <3/30/92, JPO> @2: ; MULTIPLY BY 2^D0 CASE: ; FSCALE.W D0,FP0 ; FP0 := FP0 * 2^D0 - DELETED <3/30/92, JPO> ADD.W #$3FFF,D0 ; bias scale factor to create exponent of factor <3/30/92, JPO> MOVE.W D0,(SP) ; <3/30/92, JPO> FMUL.X (SP)+,FP0 ; scale via multiplication, popping factor <3/30/92, JPO> FPU2DST8096 MOVEM.L (SP)+,D0/A0 UNLK A6 RTD #8 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; LOGB - Modified to prevent spurious underflow and to avoid using ;; the FGETEXP instruction - <3/30/92, JPO> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QLOGBX: LINK A6,#-LK1SIZE MOVE.L #LK1POP+FOLOGB,LKOP(A6) ; MOVE.L A0,-(SP) ; DELETED <3/30/92, JPO> movem.l a0/d0,-(sp) ; save A0/D0 <3/30/92, JPO> DST2STK movea.l sp,a0 ; A0 <- SP <3/30/92, JPO> ; FABS.X (SP),FP0 ; DELETED <3/30/92, JPO> QLOGBXENTRY: ; FBEQ.W @1 ; ZERO? RETURN LOG2'S EXCEPTIONS. DELETED <3/30/92, JPO> ; FCMP.S #"INF",FP0 ; DELETED <3/30/92, JPO> ; FBNE.W @2 ; NOT ZERO OR INF? RETURN FGETEXP. DELETED <3/30/92, JPO> ;@1 ; DELETED <3/30/92, JPO> ; FLOG2 FP0 ; OTHERWISE, RETURN LOG2'S EXCEPTIONS. DELETED <3/30/92, JPO> ; BRA.S @3 ; DELETED <3/30/92, JPO> ;@2 ; DELETED <3/30/92, JPO> ; FGETEXP FP0 ; DELETED <3/30/92, JPO> ;@3 DELETED <3/30/92, JPO> move.w (a0),d0 ; D0 <- exponent <3/30/92, JPO> andi.w #$7fff,d0 ; <3/30/92, JPO> cmpi.w #$7fff,d0 ; NaN or INF? <3/30/92, JPO> beq.b @naninf ; yes <3/30/92, JPO> subi.w #$3fff,d0 ; no, unbias exponent <3/30/92, JPO> tst.l 4(a0) ; normalized? <3/30/92, JPO> bmi.b @norm ; yes <3/30/92, JPO> beq.b @1 ; no, SIG.HI is zero <3/30/92, JPO> move.l d1,-(sp) ; unnormalized. save D1 <3/30/92, JPO> bfffo 4(a0){0:32},d1 ; find first nonzero significand bit <3/30/92, JPO> bra.b @unnorm ; <3/30/92, JPO> @naninf: ; NaN/INF returns NaN/+INF <3/30/92, JPO> fabs.x (a0),fp0 ; SNaN signals here <3/30/92, JPO> bra.b @deliver ; <3/30/92, JPO> @zero: ; zero returns -INF with divide-by-zero exception fmove.w #-1,fp0 ; <3/30/92, JPO> fdiv.w #0,fp0 ; <3/30/92, JPO> bra.b @deliver ; <3/30/92, JPO> @1: ; SIG.HI is zero sub.w #32,d0 ; high significand zero, adjust exponent <3/30/92, JPO> tst.l 8(a0) ; zero? <3/30/92, JPO> beq.b @zero ; yes <3/30/92, JPO> move.l d1,-(sp) ; unnormalized. save D1 <3/30/92, JPO> bfffo 8(a0){0:32},d1 ; find first nonzero signficand bit @unnorm: ; unnormalized/denormalized case <3/30/92, JPO> sub.w d1,d0 ; adjust exponent for leading significand zeros <3/30/92, JPO> move.l (sp)+,d1 ; restore D1 <3/30/92, JPO> @norm: ; unbiased exponent in D0.W <3/30/92, JPO> fmove.w d0,fp0 ; FP0 <- binary exponent of input <3/30/92, JPO> @deliver: ; deliver results <3/30/92, JPO> movea.l LKDST(a6),a0 ; A0 <- &DST once more <3/30/92, JPO> FPU2DST8096 ; MOVE.L (SP)+,A0 ; DELETED <3/30/92, JPO> movem.l (sp)+,a0/d0 ; restore registers <3/30/92, JPO> UNLK A6 RTD #4 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; CLASSIFY ;; CLASS PLACES INTEGER CODE AT DST ADDRESS. THE CODE TIES ;; IN USEFULLY WITH THE PASCAL ENUMERATED TYPES IN SANE. ;; IT IS THE SANE VALUE PLUS ONE, WITH THE SIGN OF THE INPUT ;; OPERAND. IN SANE, THE SIGN IS PLACED IN A SEPARATE INT. ;; THE VALUES ARE THUS: ;; SNAN 1 ...GADZOOKS! WHY ALL THIS CODE FOR CLASSIFY?!!? ;; QNAN 2 ...WELL, WE NOW NORMALIZE UNNORMAL NUMS BEFORE ;; INF 3 ...CLASSIFICATION. ALSO, THIS CODE AVOIDS USING ;; ZERO 4 ...THE FPU AND IS OPTIMIZED FOR THE TYPICAL CASE. ;; NORMAL 5 ...IT'S STILL GROSS, IN RETROSPECT. ;; DENORM 6 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QCLASSX: MOVEM.L D0/A0,-(SP); D0 < A0 < RTS < &DST < &SRC MOVEA.L 16(SP),A0 ; A0 := EXTENDED SRC ADDR MOVE.L (A0)+,D0 ; D0 := HIGH LONG OF EXTENDED SWAP D0 BPL.S @3 ; BRANCH: LEAD BIT ZERO LSL #1,D0 ROR #1,D0 ; SAVE SIGN BIT IN X BEQ.S @0 ; BRANCH: NORMAL CMPI.W #$7FFF,D0 BEQ.S @4 ; BRANCH: MAX EXP CASE @0 MOVEQ #5,D0 ; NORMAL CASE: @1 ; EXIT. MOVEA.L 12(SP),A0 ; A0 := DST ADDR MOVE.W D0,(A0) ; STUFF ANSWER, AFFIX SIGN AND EXIT ROXR #1,D0 ; BRING BACK SIGN FROM X BPL.S @2 NEG.W (A0) ; NEGATE (IN MEMORY) ONLY IF X BIT SET @2 MOVEM.L (SP)+,D0/A0 RTD #8 ; EXIT ; LEAD BIT ZERO CASE: @3 ; EITHER 0, UN- OR SUBNORMAL, INF, NAN LSL #1,D0 ROR #1,D0 ; SAVE SIGN BIT IN X BEQ.S @31 ; BRANCH: MIN EXP CASE CMPI.W #$7FFF,D0 BEQ.S @4 ; BRANCH: MAX EXP CASE BRA.S @34 ; BRANCH: UNNORMAL @31 ; MIN EXP CASE: MOVE.W (A0)+,D0 ; SMASH REST OF SIGNIFICAND OR.L (A0), D0 ; BITS INTO DO.L . BNE.S @33 ; BRANCH: SUBNORMAL @32 MOVEQ #4,D0 ; ZERO: BRA.S @1 ; EXIT. @33 MOVEQ #6,D0 ; SUBNORMAL: BRA.S @1 ; EXIT. @34 ; UNNORMAL CASE: (EXP IN D0.W, SGN IN XBIT!!!) ; EITHER ZERO, NORMAL, OR SUBNORMAL MOVE.L A1,-(SP) ; SAVE A1 MOVE D0,A1 ; EXP.W IN A1 ; NORMALIZATION CODE (WOW SAYS JON): SUBQ #2,A0 ; RESET A0 TO POINT TO 63-0 OF 79-0 BFFFO (A0){0:0},D0 BNE.S @35 ; BRANCH: (NON-ZERO .L), .L SUBA D0,A1 ; CC NOT AFFECTED BFFFO 4(A0){0:0},D0 BEQ.S @36 ; BRANCH: UNNORMALIZED ZER0 @35 ; (NON-ZERO .L), .L CASE. SUBA D0,A1 ; CC NOT AFFECTED TST A1 ; SHIFT EXCEEDS EXP? MOVEA.L (SP)+,A1 ; RESTORE A1 (CC NOT AFFECTED) BPL.S @0 ; BRANCH: NORMAL, SHIFT <= EXP BMI.S @33 ; BRANCH: SUBNORMAL, SHIFT > EXP @36 ; UNNORMALIZED ZERO CASE: MOVEA.L (SP)+,A1 ; RESTORE A1 BRA.S @32 ; BRANCH: ZERO @4 ; MAX EXP CASE. BTST.L #30,D0 ; TEST QUIET/SIGNAL BIT BEQ.S @41 ; BRANCH: SNAN OR INF CASE MOVEQ #2,D0 ; QNAN: BRA.S @1 ; EXIT. @41 ; SNAN OR INF CASE: BCLR.L #31,D0 ; BITS 63-62 OF 79-0 NOW CLEAR MOVE.W (A0)+,D0 ; SMASH REST OF SIGNIFICAND OR.L (A0), D0 ; BITS INTO DO.L . BNE.S @42 ; BRANCH: SNAN MOVEQ #3,D0 ; INF: BRA.S @1 ; EXIT. @42 MOVEQ #1,D0 ; SNAN: BRA.S @1 ; EXIT. QCLASSS: MOVEM.L D0/A0,-(SP); D0 < A0 < RTS < &DST < &SRC MOVEA.L 16(SP),A0 ; A0 := SRC ADDR MOVE.L (A0),D0 ; D0 := SRC FLOAT ADD.L D0,D0 ; SAVE SIGN BIT IN X BIT ; NOTE: FLOAT LOGICALLY SHIFTED LEFT ONE BIT, ; MAKING BIT OFFSETS SEEM OFF BY ONE. BEQ.S @2 ; BRANCH: ZERO BFCHG D0{0:8} BEQ.S @3 ; BRANCH: DENORM BFTST D0{0:8} BEQ.S @4 ; BRANCH: INF OR NAN MOVEQ #5,D0 ; NORMAL CASE: ; EXIT. @0 MOVEA.L 12(SP),A0 ; A0 := DST ADDR MOVE.W D0,(A0) ; WRITE ANSWER, AFFIX SIGN AND EXIT ROXR #1,D0 ; BRING BACK SIGN FROM X BIT BPL.S @1 NEG.W (A0) ; NEGATE ONLY IF X BIT WAS SET @1 MOVEM.L (SP)+,D0/A0 RTD #8 @2 MOVEQ #4,D0 ; ZERO CASE: BRA.S @0 ; EXIT. @3 MOVEQ #6,D0 ; DENORM CASE: BRA.S @0 ; EXIT. @4 ; INF OR NAN CASE: BFTST D0{8:23} ; F != 0? BNE.S @5 ; BRANCH: NAN MOVEQ #3,D0 ; INF CASE: BRA.S @0 ; EXIT. @5 ; NAN CASE: BTST #23,D0 ; QUIET BIT OFF? BEQ.S @51 ; BRANCH: SNAN MOVEQ #2,D0 ; QNAN CASE: BRA.S @0 ; EXIT. @51 MOVEQ #1,D0 ; SNAN CASE: BRA.S @0 ; EXIT. QCLASSD: ; SAME AS FLOAT CODE ABOVE (BUT OFFSETS DIFFERENT) MOVEM.L D0/A0,-(SP); D0 < A0 < RTS < &DST < &SRC MOVEA.L 16(SP),A0 ; A0 := SRC ADDR MOVE.L (A0)+,D0 ; D0 := HIGH HALF OF DOUBLE OR.W (A0)+,D0 ; 'OR' IN LOW HALF OF DOUBLE INTO OR.W (A0),D0 ; BOTTOM HALF OF HIGH HALF. ADD.L D0,D0 ; SAVE SIGN BIT IN X BIT ; NOTE: DOUBLE LOGICALLY SHIFTED LEFT ONE BIT, ; MAKING BIT OFFSETS SEEM OFF BY ONE. BEQ.S @2 ; BRANCH: ZERO BFCHG D0{0:11} BEQ.S @3 ; BRANCH: DENORM BFTST D0{0:11} BEQ.S @4 ; BRANCH: INF OR NAN MOVEQ #5,D0 ; NORMAL CASE: ; EXIT. @0 MOVEA.L 12(SP),A0 ; A0 := DST ADDR MOVE.W D0,(A0) ; WRITE ANSWER, AFFIX SIGN AND EXIT ROXR #1,D0 ; BRING BACK SIGN FROM X BIT BPL.S @1 NEG.W (A0) ; NEGATE ONLY IF X BIT WAS SET @1 MOVEM.L (SP)+,D0/A0 RTD #8 @2 MOVEQ #4,D0 ; ZERO CASE: BRA.S @0 ; EXIT. @3 MOVEQ #6,D0 ; DENORM CASE: BRA.S @0 ; EXIT. @4 ; INF OR NAN CASE: BFTST D0{11:20} ; F != 0? BNE.S @5 ; BRANCH: NAN MOVEQ #3,D0 ; INF CASE: BRA.S @0 ; EXIT. @5 ; NAN CASE: BTST #20,D0 ; QUIET BIT OFF? BEQ.S @51 ; BRANCH: SNAN MOVEQ #2,D0 ; QNAN CASE: BRA.S @0 ; EXIT. @51 MOVEQ #1,D0 ; SNAN CASE: BRA.S @0 ; EXIT. *QCLASSD: ; FROM THE "STUART HATES BRANCHES" SCHOOL OF PROGRAMMING. * ; IF YOU LIKE BRANCHES, COMPARE WITH ABOVE CODE AND VOTE. * MOVEM.L D0/A0,-(SP) ; D0 < A0 < RTS < &DST < &SRC * MOVEA.L 16(SP),A0 ; A0 := SRC ADDR * MOVEM.L (A0)+,D0 ; D0 := HIGH HALF OF DOUBLE * BFCHG D0{1:11} ; TOGGLE EXPONENT FIELD * SEQ D1 ; D1.B := -1 IF EXPONENT IS ALL ONES, ELSE 0 * NEG.B D1 ; "PUSH" 'EXP ALL ONES' BIT ONTO END OF D1.B * ADD.B D1,D1 ; MAKE ROOM FOR NEXT BIT * BFCHG D0{1:11} ; TOGGLE EXPONENT FIELD * SNE -(SP) ; (SP).B := 0 IF EXPONENT WAS ALL ZEROS, ELSE -1 * SUB.B (SP)+,D1 ; "PUSH" 'EXP ALL ZEROS' BIT ONTO END OF D1.B * ADD.L D0,D0 ; X BIT := DOUBLE'S SIGN (REMOVED) * ROXL.B D1 ; "PUSH" SIGN BIT ONTO END OF D1.B * ASL.L #12,D0 ; X BIT := QNAN BIT (ONLY 19 F BITS REMAIN) * ROXL.B D1 ; "PUSH" 'QNAN BIT' ONTO END OF D1.B * ADD.B D1,D1 ; MAKE ROOM FOR NEXT BIT * OR.L (A0),D0 ; D0 := F BITS OR'D TOGETHER (WITHOUT QNAN BIT) * SNE -(SP) ; (SP).B := 0 IF LOWER 51 BITS ALL ZERO, ELSE -1 * SUB.B (SP)+,D1 ; "PUSH" '51 BITS OF ZERO' BIT ONTO END OF D1.B * MOVEA.L 12(SP),A0 ; A0 := DST ADDR * EXT.W D1 ; D0.W := 0 THRU 31 ("PUSHED" FIVE BITS SELECTOR) * MOVE.W CLASSDTBL(D1.W*2),(A0) ; PICK UP AND STUFF ANSWER * MOVEM.L (SP)+,D0/A0 * RTD #8 * *CLASSDTBL: DC.W 0, 1, 2, 3, 4, 5, 6, 7 ; TABLE NOT FILLED IN YET!!! * DC.W 8, 9,10,11,12,13,14,15 * DC.W 16,17,18,19,20,21,22,23 * DC.W 24,25,26,27,28,29,30,31 QCLASSC: MOVEM.L D0/A0,-(SP) ; D0 < A0 < RTS < &DST < &SRC MOVEA.L 16(SP),A0 ; A0 := SRC ADDR MOVE.L (A0)+,D0 ; D0 := HIGH HALF OF COMP ADD.L D0,D0 ; X BIT := SIGN BIT (REMOVED) OR.L (A0),D0 ; D0 := TWO HALVES OR'D TOGETHER SNE D0 ; D0.B := -1 IF NON-ZERO 63 BITS, ELSE 0 ROXL.B #1,D0 ; D0.B := $FE, $FF, $00, OR $01 MOVEA.L 12(SP),A0 ; A0 := DST ADDR EXT.W D0 ; D0.W := $FFFE, $FFFF, $0000, $0001 MOVE.W CLASSCTBL(D0.W*2),(A0) ; PICK UP AND STUFF ANSWER MOVEM.L (SP)+,D0/A0 RTD #8 DC.W 5 ; CLASS OF SRC COMP: NORMALIZED DC.W -5 ; CLASS OF SRC COMP:-NORMALIZED CLASSCTBL: DC.W 4 ; CLASS OF SRC COMP: ZERO DC.W 2 ; CLASS OF SRC COMP: QUIET NAN