; ; File: FPHWNonArith.a ; ; Contains: Non-arithmetic FP68K functions ; ; 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: ; ; <3> 6/30/91 SAM Merged from TERROR. [<8> Fixed the PrivTrap detection method to ; account for when PrivTrap is not physically located in Pack4.] ; <2> 5/28/91 SAM Merged from TERROR. [<7> Added Stuart McDonald's latest fixes ; (see below for details)] ; <1> 5/15/91 SAM Split off from TERROR Proj. ; ; Terror Change History: ; ; <5> 1/18/91 BG Removed debugging code. ; <4> 1/17/91 BG Conditionalized the PrivTrap source and installation code so it ; will only check whether or not it is needs to be installed if it ; is assembled as a System Disk .rsrc. The 040 code is retained so ; that 020/030 machines containing an 040 accelerator will work ; correctly. ; <3> 1/14/91 BG Integrated the 040 PrivTrap (installation and run-time) code ; into this file. ; <2> 1/9/91 BG Modified the code to handle at run-time the differences between ; the 881/882 and the 040 FPUs. ; <1> 1/6/90 BG Added to TERROR/BBS for the time. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; File: FP881nonarith.a ;; Implementation of FP68K non-arithmetic functions. ;; Copyright Apple Computer, Inc. 1985,1986,1987,1989,1990 ;; All Rights Reserved ;; Confidential and Proprietary to Apple Computer,Inc. ;; ;; Written by Clayton Lewis, begun 14 Feb 85. ;; Debugged by Stuart McDonald. ;; ;; Modification history: ;; Rev2: 16 May 85 ;; Rev3: 17 May 85 ;; Rev4: 17 May 85 ;; Rev5: 27 May 85 ;; Rev6: 30 May 85 ;; Rev7: 04 Jun 85 ;; Rev8: 16 Jun 85 CRL moved to MPW ;; Rev9: 08 Sep 86 -S.McD. Minor revs. to NextAfter to handle NaNs and SNaNs ;; RevA: 19 Nov 86 -S.McD. Fixed TestExp's case selection. ;; 26 Dec 86 -S.McD. CopySign rearranged slightly. ;; 28 Dec 86 -S.McD. Fixed ProcEx's behavior. ;; 30 Dec 86 -S.McD. Zeroed FPSR for NonArith codes exiting thru halt check. ;; 03 Jan 87 -S.McD. Nextafter was signalling on old status. ;; 13 Jan 87 -S.McD. Now save/restore any of the registers FP2-FP7 used. ;; 15 Jan 87 -S.McD. Status and copyright notice changed. ;; 21 Jan 87 -S.McD. Cleaned up SetHv/GetHv. ;; 22 Jan 87 -S.McD. SetExp was setting halt bit instead of flag bit. ;; RevB: 28 Dec 89 -S.McD. Complete rewrite begins. ;; 22 May 90 -S.McD. Goes alpha for Waimea. Updated Copyright and version. ;; 7 Sep 90 -S.McD. Goes beta for Tim. Updated version number. ;; 30 Sep 90 -S.McD. Made fp exception handlers A-trap aware. ;; 30 Sep 90 -S.McD. Goes final for Terror alpha. ;; 12 Dec 90 -S.McD. Added stmt to return computed FPIAR to FPIAR. ;; 12 Dec 90 -JPO Modified SetE, trap handlers, and PrivTrap% for MC68040 ;; 17 Dec 90 -JPO Corrected sign of INF in DZHANDLER for ATANH(±1). ;; 20 Dec 90 -JPO Corrected branch dest in STDTAIL after clearing E3 exception ;; pending bit (040 mode). ;; 18 May 91 -S.McD. Fixed spurious flag errors for FMOVEs in trap enabled cases. ;; 21 May 91 -S.McD. Had test backwards for SNAN FPU dst case when traps enabled. ;; 21 Jun 91 -JPO Modified STDTAIL for 881 to check for trapping caused by ;; PrivTrap not installed by ˝SANE. ;; 30 Mar 92 -JPO Substituted "FMOVE.W #1,FP2" for "FMOVECR #$32,FP1". ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; GET ENVIRONMENT. INVALID halt bit kept as (BSUN OR SNAN). ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QGETENV: MOVEM.L D0-D2/A0,-(SP) FMOVE.L FPSR,D0 ; D0 := FPSR MOVEA.L 20(SP),A0 ; A0 := DST ADDR MOVEQ #0,D2 ; D2 := 0, IMAGE OF ENV. FMOVE.L FPCR,D1 ; D1 := FPCR BFEXTU D0{24:5},D0 ; GRAB 5 STICKIES MOVE.B STICKYMAP(D0),D0; MAP TO SANE ORDERING BFINS D0,D2{19:5} ; STUFF 5 MAPPED STICKIES INTO USER'S ENV BCLR #13,D1 ; PREPARE TO COPY INVALID BIT TO OPERR BIT IN D1 BFTST D1{16:2} ; HALT ON INVALID IFF (BSUN OR SNAN) IS SET BEQ.S @1 ; NO, BRANCH BSET #13,D1 ; YES, SET OPERR BIT @1: BFEXTU D1{18:5},D0 ; GRAB 5 HALT ENABLES (ASSUMES SET BY QSETENV) MOVE.B STICKYMAP(D0),D0; MAP TO SANE ORDERING BFINS D0,D2{27:5} ; STUFF 5 MAPPED HALT ENABLES INTO USER'S ENV BFEXTU D1{24:2},D0 ; GRAB 2 PREC BITS MOVE.B PRECMAP(D0),D0 ; MAP TO SANE ORDERING BFINS D0,D2{25:2} ; STUFF 2 MAPPED PREC BITS INTO USER'S ENV BFEXTU D1{26:2},D0 ; GRAB 2 RND BITS MOVE.B RNDMAP(D0),D0 ; MAP TO SANE ORDERING BFINS D0,D2{17:2} ; STUFF 2 MAPPED RND BITS INTO USER'S ENV MOVE D2,(A0) ; WRITE BACK UPDATED DST MOVEM.L (SP)+,D0-D2/A0 RTD #4 STICKYMAP: DC.B 0,16,8,24,2,18,10,26,4,20,12,28,6,22,14,30 DC.B 1,17,9,25,3,19,11,27,5,21,13,29,7,23,15,31 PRECMAP: DC.B 0,2,1,3 RNDMAP: DC.B 0,3,2,1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SET ENVIRONMENT. INVALID halt bit kept as (BSUN OR SNAN). ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QSETENV: MOVEM.L D0-D2/A0,-(SP) MOVEA.L 20(SP),A0 ; A0 := SRC ADDR MOVE (A0),D2 ; D2 := SRC ENV FMOVE.L FPSR,D1 ; GET FPSR INTO D1 BFEXTU D2{19:5},D0 ; GRAB 5 STICKIES MOVE.B STICKYUNMAP(D0),D0 ; MAP TO 881 ORDERING BFINS D0,D1{24:5} ; STUFF 5 UNMAPPED STICKIES INTO D1 FMOVE.L D1,FPSR ; HAMMER FPSR WITH D1 FMOVE.L FPCR,D1 ; GET FPCR INTO D1 BFEXTU D2{27:5},D0 ; GRAB 5 HALT ENABLES MOVE.B STICKYUNMAP(D0),D0 ; MAP TO 881 ORDERING BSET #4,D0 ; ALWAYS ENABLE OPERR TRAPPING BFINS D0,D1{16:7} ; STUFF 5 (SIGN EXTENDED TO 7) HALT ENABLES INTO D1 BFEXTU D2{25:2},D0 ; GRAB 2 PREC BITS MOVE.B PRECMAP(D0),D0 ; MAP TO 881 ORDERING BFINS D0,D1{24:2} ; STUFF 2 UNMAPPED PREC BITS INTO D1 BFEXTU D2{17:2},D0 ; GRAB 2 RND BITS MOVE.B RNDMAP(D0),D0 ; MAP TO 881 ORDERING BFINS D0,D1{26:2} ; STUFF 2 UNMAPPED RND BITS INTO D1 FMOVE.L D1,FPCR ; HAMMER FPCR WITH D1 MOVEM.L (SP)+,D0-D2/A0 RTD #4 STICKYUNMAP: DC.B %0000000,%1110000,%0000100,%1110100,%0001000,%1111000,%0001100,%1111100 DC.B %0000010,%1110010,%0000110,%1110110,%0001010,%1111010,%0001110,%1111110 DC.B %0000001,%1110001,%0000101,%1110101,%0001001,%1111001,%0001101,%1111101 DC.B %0000011,%1110011,%0000111,%1110111,%0001011,%1111011,%0001111,%1111111 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SET HALT VECTOR & GET HALT VECTOR. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QSETHV: MOVEM.L D0/A0-A1,-(SP) ; D0 < A0 < A1 < RTS < &HALTVECTOR MOVEA.L 16(SP),A0 ; A0 := HALTVECTOR ADDR MOVE.L (A0),FPSTATE+2 ; SAVE HALTVECTOR INTO LOW-MEMORY cmp.b #cpu68040,CPUFlag; are we on an 040? blt.s @use881Excepts ; branch if NOT on an 040 to install 881 exception handlers PEA SNANHANDLER040 ; SIGNALING NAN TRAP VECTOR PEA STDHANDLER040 ; OVERFLOW TRAP VECTOR PEA OPERRHANDLER040 ; OPERAND ERROR TRAP VECTOR PEA STDHANDLER040 ; UNDERFLOW TRAP VECTOR PEA DZHANDLER040 ; DIVIDE BY ZERO TRAP VECTOR PEA STDHANDLER040 ; INEXACT RESULT TRAP VECTOR PEA BSUNHANDLER040 ; UNORDERED CONDITION TRAP VECTOR bra.s @getOnWithIt ; proceed with the installation @use881Excepts PEA SNANHANDLER ; SIGNALING NAN TRAP VECTOR PEA STDHANDLER ; OVERFLOW TRAP VECTOR PEA OPERRHANDLER ; OPERAND ERROR TRAP VECTOR PEA STDHANDLER ; UNDERFLOW TRAP VECTOR PEA DZHANDLER ; DIVIDE BY ZERO TRAP VECTOR PEA STDHANDLER ; INEXACT RESULT TRAP VECTOR PEA BSUNHANDLER ; UNORDERED CONDITION TRAP VECTOR @getOnWithIt MOVEA.L SP,A0 ; A0 := address of seven trap vectors MOVE #2,D0 ; D0 := PrivTrap% SetTrapVec selector BSR.W PrivTrap% ; SET THE 881/882 TRAP VECTORS! ; (WHY THIS CAN'T BE DONE INLINE, I DON'T KNOW. ; PERHAPS A/UX GROUP LIKES FUNNY STUFF ISOLATED?) ADDA.W #28,SP ; POP SEVEN SCRATCH ADDRESSES MOVEM.L (SP)+,D0/A0-A1 RTD #4 QGETHV: MOVE.L FPSTATE+2,([4,SP]) RTD #4 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; NEGATE. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QNEG: BCHG.B #7,([4,SP]) RTD #4 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ABSOLUTE VALUE. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QABS: BCLR.B #7,([4,SP]) RTD #4 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; PROCENTRY. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QPROCENTRY: MOVE.L 4(SP),-(SP) ; COPY DST ADDRESS AND CALL GETENV BSR.S QGETENV FMOVEM.L ZEROENV,FPCR/FPSR ;SET DEFAULT ENVIRONMENT RTD #4 ZEROENV: DC.L $2000,0 ; OPERR TRAPPING ALWAYS ENABLED ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; TESTEXCEPTION. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QTESTXCP: MOVEM.L D0-D1/A0,-(SP) ; A0 < D1 < D0 < RTN ADDR < DST ADDR MOVEQ #0,D0 ; CLEAR FOR BIT FIELD OFFSET LATER MOVEA.L 16(SP),A0 ; A0 := DESTINATION (SOURCE) ADDRESS FMOVE.L FPSR,D1 ; D1 := ACCRUED EXCEPTIONS MOVE.W (A0),D0 ; D0 := SANE BIT INDEX MOVE.W IndTab(PC,D0*2),D0 ; D0 := ABOVE MAPPED TO 881 BIT FIELD POS. BFEXTU D1{D0:1},D1 ; GRAB THE BIT MOVE.B D1,(A0) ; STUFF IT IN HIGH BYTE OF WORD MOVEM.L (SP)+,D0-D1/A0 ; RESTORE REGISTERS AND GET OUT RTD #4 IndTab: DC.W 24,26,25,27,28 ; ALL BUT OVER/UNDERFLOW IN RIGHT ORDER! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; <5/18/91-S.McD.> ;; preFMOVE disables underflow trapping and precision control until postFMOVE <5/18/91-S.McD.> ;; is called; FMOVEs will act like FMOVEMs except SNANs WILL signal. Uses D2,D3. <5/18/91-S.McD.> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; <5/18/91-S.McD.> preFMOVE: ; DISABLE UNDERFLOW TRAPPING FOR SUBNORMALS <5/18/91-S.McD.> FMOVE.L FPCR,D2 ; D2 := FPCR <5/18/91-S.McD.> MOVE.L #%100011000000,D3 ; MASK FOR UNFL,PREC,PREC <5/18/91-S.McD.> AND.L D2,D3 ; UNFL OR PREC SET IN FPCR? <5/18/91-S.McD.> BEQ.S @1 ; BRANCH: NONE SET <5/18/91-S.McD.> EOR.L D3,D2 ; OTHERWISE, TOGGLE THOSE SET(CLEARING THEM)<5/18/91-S.McD.> FMOVE.L D2,FPCR ; DISABLE UNFL TRAPS AND PRECISION CONTROL <5/18/91-S.McD.> @1: ; <5/18/91-S.McD.> RTS ; <5/18/91-S.McD.> postFMOVE: ; RESTORES FPCR AFTER preFMOVE (USES D2,D3) <5/18/91-S.McD.> TST.L D3 ; ANY OF UNFL,PREC,PREC SET? <5/18/91-S.McD.> BEQ.S @1 ; BRANCH: NONE SET <5/18/91-S.McD.> EOR.L D3,D2 ; OTHERWISE, TOGGLE THEM (RESTORING THEM) <5/18/91-S.McD.> FMOVE.L D2,FPCR ; AND RESTORE FPCR <5/18/91-S.McD.> @1: ; <5/18/91-S.McD.> RTS ; <5/18/91-S.McD.> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; NEXTAFTER96 - Modify SRC96 by one ulp in direction of DST96 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QNEXTX96: LINK A6,#-LK2SIZE ; use link so halthandler can find args MOVE.L #LK2POP+FFEXT+FONEXT+FFEXT96,LKOP(A6); popsize & opword for halthandler MOVEM.L D0-D3/A0,-(SP) ; SAVE REGS <5/18/91-S.McD.> BSR.S preFMOVE ; DISABLE UNFL TRAPPING AND PREC CONTROL <5/18/91-S.McD.> MOVEA.L LKDST(A6),A0 FMOVE (A0),FP1 ; FP1 := DST MOVEA.L LKSRC(A6),A0 ; A0 := &SRC FMOVE (A0),FP0 ; FP0 := SRC FMOVE FP0,(A0) ; MUST WRITE BACK BECAUSE OF UNNORMALS MOVE.W (A0)+,(A0) ; REPLICATE EXPONENT & BUMP A0 FOR 80-BIT CODE BRA.S QNEXTXENTRY ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; NEXTAFTER - Modify SRC by one ulp in direction of DST ;; 1. Handle NaN and SRC equals DST cases. ;; 2. Handle SRC zero case. ;; 3. Handle SRC non-zero case (the typical case) by bumping SRC in memory. ;; 4. Handle result underflows case. ;; 5. Handle result overflows case. ;; 6. Return. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QNEXTX: LINK A6,#-LK2SIZE ; use link so halthandler can find args MOVE.L #LK2POP+FFEXT+FONEXT,LKOP(A6) ; popsize & opword for halthandler MOVEM.L D0-D3/A0,-(SP) ; SAVE REGS <5/18/91-S.McD.> BSR.S preFMOVE ; DISABLE UNFL TRAPPING AND PREC CONTROL <5/18/91-S.McD.> DST2STK FMOVE (SP)+,FP1 ; FP1 := DST ; THE FOLLOWING FMOVE'S COULD BE FMOVEM'S ; TO PREVENT PRECISION CONTROL NARROWING; ; TRUE FOR ANY OTHER FOP'S IN THIS CODE, TOO. SRC2STK FMOVE (SP)+,FP0 ; FP0 := SRC FPU2SRC ; MUST WRITE BACK BECAUSE OF UNNORMALS QNEXTXENTRY: FCMP FP0,FP1 ; EITHER INPUT A NAN? FBOGL.W @2 ; BRANCH: ORDERED, SRC != DST CASE ; NAN AND EQUALITY CASES: FBEQ.W @1 ; BRANCH: SRC == DST FCMP FP0,FP0 ; IS SRC NAN? FBUN.W @1 ; BRANCH: SRC NAN CASE FMOVE FP1,FP0 ; SRC := DST NAN @1: ; SRC NAN CASE (AND SRC == DST CASE): FPU2SRC ; WRITE NAN TO MEMORY BSR.W postFMOVE ; RESTORE FPCR. USES D2,D3. <5/18/91-S.McD.> BRA.W @6 ; BRANCH: RETURN CASE @2: ; ORDERED, SRC != DST CASE: FSOLT.B D1 ; D1.B := (SRC > DST ? -1 : 0) ... USED IN @3 FTEST FP0 ; SRC ZERO? FBNE.W @3 ; BRANCH: SRC NON-ZERO CASE ; SRC ZERO CASE: FMOVE.X #"$000000000000000000000001",FP0 ; SRC := TINIEST SUBNORMAL FTEST FP1 ; FP0 GETS FP1'S SIGN... FBOGT.W @21 ; BRANCH: DST NEG. CASE FNEG FP0 ; DST NEG. CASE: @21: ; DST POS. CASE: FPU2SRC ; WRITE RESULT TO MEM. BEFORE SIGNALING UNDERFLOW BRA.S @4 ; BRANCH: RESULT EXPONENT FIELD ZERO CASE @3: ; SRC NON-ZERO CASE (TYPICAL CASE): FSOLT.B D0 ; D0.B := SRC'S SIGN SIGN-EXTENDED TO BYTE EOR.B D1,D0 ; SRC < 0 TOGGLE'S SENSE (SIGN-MAG VS. 2'S COMP) BPL.S @31 ; BRANCH: INCREMENT CASE ; DECREMENT CASE: BFTST (A0){1:15} ; EXPONENT FIELD ZERO? BEQ.S @30 ; IF SO, SKIP NEXT INSTRUCTION BCLR #7,2(A0) ; ...CLEAR EXPLICIT BIT @30: SUBQ.L #1,6(A0) ; BUMP THE LOW 3RD OF EXTENDED BCC.S @33 ; BRANCH: CHECK FOR UNDER/OVERFLOW CASE SUBQ.L #1,2(A0) ; SUBTRACT BORROW FROM MID 3RD OF EXTENDED BCC.S @33 ; BRANCH: CHECK FOR UNDER/OVERFLOW CASE SUBQ.W #1,(A0) ; SUBTRACT BORROW FROM HI 3RD OF EXTENDED BRA.S @33 ; BRANCH: CHECK FOR UNDER/OVERFLOW CASE @31: ; INCREMENT CASE: BFTST (A0){1:15} ; EXPONENT FIELD ZERO? BEQ.S @32 ; IF SO, SKIP NEXT INSTRUCTION BSET #7,2(A0) ; ...SET EXPLICIT BIT @32: ADDQ.L #1,6(A0) ; BUMP THE LOW 3RD OF EXTENDED BCC.S @33 ; BRANCH: CHECK FOR UNDER/OVERFLOW CASE ADDQ.L #1,2(A0) ; ADD CARRY INTO MID 3RD OF EXTENDED BCC.S @33 ; BRANCH: CHECK FOR UNDER/OVERFLOW CASE ADDQ.W #1,(A0) ; ADD CARRY INTO HI 3RD OF EXTENDED @33: ; CHECK FOR UNDER/OVERFLOW CASE: MOVE.L (A0),D0 ; PICK IT UP AGAIN FOR SIGNAL CHECKS BELOW BFCHG D0{1:15} ; TOGGLE THE EXPONENT FIELD BNE.S @5 ; BRANCH: RESULT EXPONENT FIELD NON-ZERO CASE @4: ; RESULT EXPONENT FIELD ZERO CASE: BSR.W postFMOVE ; RESTORE FPCR. USES D2,D3. <5/18/91-S.McD.> BTST #7,2(A0) ; DO WE HAVE A SUBNORMAL? BNE.S @6 ; BRANCH: RETURN CASE (WAS NORMAL) ; SUBNORMAL CASE: ; FMOVECR #$32,FP1 ; FP1 := 1.0 DELETED <3/30/92, JPO> fmove.w #1,fp1 ; FP1 := 1.0 <3/30/92, JPO> FSCALE.W #$8000,FP1 ; SIGNAL UNDERFLOW!!! FNOP ; CATCH SIGNAL BEFORE RTD BRA.S @6 ; BRANCH: RETURN CASE @5: ; RESULT EXPONENT FIELD NON-ZERO CASE: BSR.W postFMOVE ; RESTORE FPCR. USES D2,D3. <5/18/91-S.McD.> BFCHG D0{1:15} ; TOGGLE THE EXPONENT FIELD BEQ.S @51 ; BRANCH: EXPONENT FIELD ALL-ONE ; ORDINARY EXPONENT FIELD CASE: BSET #7,2(A0) ; SET THE EXPLICIT BIT BRA.S @6 ; BRANCH: RETURN CASE @51: ; RESULT EXPONENT FIELD ALL-ONE CASE: ; FMOVECR #$32,FP1 ; FP1 := 1.0 DELETED <3/30/92, JPO> fmove.w #1,fp1 ; FP1 := 1.0 <3/30/92, JPO> FSCALE.W #$7FFF,FP1 ; SIGNAL OVERFLOW!!! FNOP ; CATCH SIGNAL BEFORE RTD @6: ; RETURN CASE: BTST #FPX96BIT,LKOP+3(A6); 96 BIT CLEAR? BEQ.S @61 ; IF SO, SKIP NEXT INSTRUCTION MOVE.W (A0),-(A0) ; COPY 80-BIT EXP. WORD TO 96-BIT LOCATION @61: MOVEM.L (SP)+,D0-D3/A0 ; RESTORE REGS <5/18/91-S.McD.> UNLK A6 RTD #8 QNEXTD: LINK A6,#-LK2SIZE ; use link so halthandler can find args MOVE.L #LK2POP+FFDBL+FONEXT,LKOP(A6) ; popsize & opword for halthandler MOVEM.L D0-D3/A0,-(SP) ; SAVE REGS <5/18/91-S.McD.> BSR.W preFMOVE ; DISABLE UNFL TRAPPING AND PREC CONTROL <5/18/91-S.McD.> MOVEA.L LKDST(A6),A0 FMOVE.D (A0),FP1 ; FP1 := DST ... SNAN DST SIGNALS HERE! MOVEA.L LKSRC(A6),A0 FMOVE.D (A0),FP0 ; FP0 := SRC ... SNAN SRC SIGNALS HERE! FCMP FP0,FP1 ; EITHER INPUT A NAN? FBOGL.W @2 ; BRANCH: ORDERED, SRC != DST CASE ; NAN AND EQUALITY CASES: FBEQ.W @1 ; BRANCH: SRC == DST FCMP FP0,FP0 ; IS SRC NAN? FBUN.W @1 ; BRANCH: SRC NAN CASE FMOVE FP1,FP0 ; SRC := DST NAN @1: ; SRC NAN CASE (AND SRC == DST CASE): FMOVE.D FP0,(A0) ; WRITE NAN TO MEMORY BSR.W postFMOVE ; RESTORE FPCR. USES D2,D3. <5/18/91-S.McD.> BRA.S @6 ; BRANCH: RETURN CASE @2: ; ORDERED, SRC != DST CASE: FSOLT.B D1 ; D1.B := (SRC > DST ? -1 : 0) ... SEE @3 FTEST FP0 ; SRC ZERO? FBNE.W @3 ; BRANCH: SRC NON-ZERO CASE ; SRC ZERO CASE: FMOVE.D #"$0000000000000001",FP0 ; SRC := TINIEST SUBNORMAL FTEST FP1 ; FP0 GETS FP1'S SIGN... FBOGT.W @21 ; BRANCH: DST NEG. CASE FNEG FP0 ; DST NEG. CASE: @21: ; DST POS. CASE: FMOVE.D FP0,(A0) ; WRITE RESULT TO MEM. BEFORE SIGNALING UNDERFLOW BRA.S @4 ; BRANCH: RESULT EXPONENT FIELD ZERO CASE @3: ; SRC NON-ZERO CASE (TYPICAL CASE): FSOLT.B D0 ; D0.B := SRC'S SIGN SIGN-EXTENDED TO BYTE EOR.B D1,D0 ; SRC < 0 TOGGLE'S SENSE (SIGN-MAG VS. 2'S COMP) BPL.S @31 ; BRANCH: INCREMENT CASE ; DECREMENT CASE: SUBQ.L #1,4(A0) ; BUMP THE LOW HALF OF THE DOUBLE BCC.S @32 ; BRANCH: CHECK FOR UNDER/OVERFLOW CASE SUBQ.L #1,(A0) ; SUBTRACT BORROW FROM HIGH HALF OF DOUBLE BRA.S @32 ; BRANCH: CHECK FOR UNDER/OVERFLOW CASE ; INCREMENT CASE: @31: ADDQ.L #1,4(A0) ; BUMP THE LOW HALF OF THE DOUBLE BCC.S @32 ; BRANCH: CHECK FOR UNDER/OVERFLOW CASE ADDQ.L #1,(A0) ; ADD CARRY INTO HIGH HALF OF DOUBLE @32: ; CHECK FOR UNDER/OVERFLOW CASE: MOVE.L (A0),D0 ; PICK IT UP AGAIN FOR SIGNAL CHECKS BELOW BFCHG D0{1:11} ; TOGGLE THE EXPONENT FIELD BNE.S @5 ; BRANCH: RESULT EXPONENT FIELD NON-ZERO CASE @4: ; RESULT EXPONENT FIELD ZERO CASE: BSR.W postFMOVE ; RESTORE FPCR. USES D2,D3. <5/18/91-S.McD.> ; FMOVECR #$32,FP1 ; FP1 := 1.0 DELETED <3/30/92, JPO> fmove.w #1,fp1 ; FP1 :- 1.0 <3/30/92, JPO> FSCALE.W #$8000,FP1 ; SIGNAL UNDERFLOW!!! FNOP ; CATCH SIGNAL BEFORE RTD BRA.S @6 ; BRANCH: RETURN CASE @5: ; RESULT EXPONENT FIELD NON-ZERO CASE: BSR.W postFMOVE ; RESTORE FPCR. USES D2,D3. <5/18/91-S.McD.> BFCHG D0{1:11} ; TOGGLE THE EXPONENT FIELD BNE.S @6 ; BRANCH: RETURN CASE ; RESULT EXPONENT FIELD ALL-ONE CASE: ; FMOVECR #$32,FP1 ; FP1 := 1.0 DELETED <3/30/92, JPO> fmove.w #1,fp1 ; FP1 := 1.0 <3/30/92, JPO> FSCALE.W #$7FFF,FP1 ; SIGNAL OVERFLOW!!! FNOP ; CATCH SIGNAL BEFORE RTD @6: ; RETURN CASE: MOVEM.L (SP)+,D0-D3/A0 ; RESTORE REGS <5/18/91-S.McD.> UNLK A6 RTD #8 QNEXTS: ; Modify SRC by one ulp in direction of DST LINK A6,#-LK2SIZE ; use link so halthandler can find args MOVE.L #LK2POP+FFSGL+FONEXT,LKOP(A6) ; popsize & opword for halthandler MOVEM.L D0-D3/A0,-(SP) ; SAVE REGS <5/18/91-S.McD.> MOVEA.L LKDST(A6),A0 MOVE.L (A0),D1 ; D1 := DST MOVEA.L LKSRC(A6),A0 MOVE.L (A0),D0 ; D0 := SRC BSR.W preFMOVE ; DISABLE UNFL TRAPPING AND PREC CONTROL <5/18/91-S.McD.> FMOVE.S D1,FP1 ; FP1 := DST ... SNAN DST SIGNALS HERE! FMOVE.S D0,FP0 ; FP0 := SRC ... SNAN SRC SIGNALS HERE! FCMP FP0,FP1 ; EITHER INPUT A NAN? FBOGL.W @2 ; BRANCH: ORDERED, SRC != DST CASE ; NAN AND EQUALITY CASES: FBEQ.W @1 ; BRANCH: SRC == DST FCMP FP0,FP0 ; IS SRC NAN? FBUN.W @1 ; BRANCH: SRC NAN CASE FMOVE FP1,FP0 ; SRC := DST NAN @1: ; SRC NAN CASE (AND SRC == DST CASE): FMOVE.S FP0,(A0) ; WRITE NAN TO MEMORY BSR.W postFMOVE ; RESTORE FPCR. USES D2,D3. <5/18/91-S.McD.> BRA.S @6 ; BRANCH: RETURN CASE @2: ; ORDERED, SRC != DST CASE: BSR.W postFMOVE ; RESTORE FPCR. USES D2,D3. <5/18/91-S.McD.> BFTST D0{1:31} ; IS SRC ZERO? BNE.S @3 ; BRANCH: SRC NON-ZERO CASE ; SRC ZERO CASE: MOVEQ.L #2,D0 ; CONSTRUCT SIGNED TINEST SUBNORMAL... ADD.L D1,D1 ; SHIFT DST SIGN INTO CARRY ROXR.L #1,D0 ; D0 := $80000001 (OR $00000001) MOVE.L D0,(A0) ; WRITE RESULT TO MEMORY BEFORE SIGNALING UNDERFLOW BRA.S @4 ; BRANCH: RESULT EXPONENT FIELD ZERO CASE @3: ; SRC NON-ZERO CASE (TYPICAL CASE): BTST #31,D0 ; TEST SRC'S SIGN SNE.B D0 ; D0.B := SRC'S SIGN SIGN-EXTENDED TO BYTE FSOLT.B D1 ; D1.B := (SRC > DST ? -1 : 0) EOR.B D1,D0 ; SRC < 0 TOGGLE'S SENSE (SIGN-MAG VS. 2'S COMP) EXTB.L D0 ; D0.L := -1 OR 0 ADD.L D0,D0 ; DO.L := -2 OR 0 ADDQ.L #1,D0 ; DO.L := -1 OR 1 ADD.L D0,(A0) ; BUMP THE FLOAT IN MEMORY! MOVE.L (A0),D0 ; PICK IT UP AGAIN FOR SIGNAL CHECKS BELOW ; CHECK FOR UNDER/OVERFLOW: BFCHG D0{1:8} ; TOGGLE THE EXPONENT FIELD BNE.S @5 ; BRANCH: RESULT EXPONENT FIELD NON-ZERO CASE @4: ; RESULT EXPONENT FIELD ZERO CASE: ; FMOVECR #$32,FP1 ; FP1 := 1.0 DELETED <3/30/92, JPO> fmove.w #1,fp1 ; FP1 := 1.0 <3/30/92, JPO> FSCALE.W #$8000,FP1 ; SIGNAL UNDERFLOW!!! FNOP ; CATCH SIGNAL BEFORE RTD BRA.S @6 ; BRANCH: RETURN CASE @5: ; RESULT EXPONENT FIELD NON-ZERO CASE: BFCHG D0{1:8} ; TOGGLE THE EXPONENT FIELD BNE.S @6 ; BRANCH: RETURN CASE ; RESULT EXPONENT FIELD ALL-ONE CASE: ; FMOVECR #$32,FP1 ; FP1 := 1.0 DELETED <3/30/92, JPO> fmove.w #1,fp1 ; FP1 := 1.0 <3/30/92, JPO> FSCALE.W #$7FFF,FP1 ; SIGNAL OVERFLOW!!! FNOP ; CATCH SIGNAL BEFORE RTD @6: ; RETURN CASE: MOVEM.L (SP)+,D0-D3/A0 ; RESTORE REGS <5/18/91-S.McD.> UNLK A6 RTD #8 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; PROCEXIT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QPROCEXIT: LINK A6,#-LK1SIZE ; use link so halthandler can find args MOVE.L #LK1POP+FOPROCEXIT,LKOP(A6) ; popsize & opword for halthandler MOVEM.L D0-D1/A0,-(SP) ; SAVE REGS FMOVE.L FPSR,D1 ; D1 := SAVE PENDING PENDING FLAGS BFEXTS D1{24:5},D0 ; D0.L := ACCRUED EXCEPTIONS SIGN-EXTENDED BFINS D0,D1{16:7} ; MAP ACCRUED EXCEPTIONS ONTO NON-ACCRUED MOVE.L LKDST(A6),-(SP) ; PUSH OLD ENVIRONMENT ADDRESS BSR.S QSETENV ; RESTORE OLD ENVIRONMENT...SOMEWHAT WASTEFUL BRA.S SetE ; BRANCH: SIGNAL PENDING FLAGS IN D1 AND RETURN ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SETEXCEPTION * ASSUMPTION: BSUN AND SNAN SET IFF INVALID HALT ENABLED. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QXCPMAP: DC.W $E080,$0820,$1040,$0410,$0208 ; ALL BUT OVER/UNDERFLOW IN RIGHT ORDER! QSETXCP: LINK A6,#-LK1SIZE ; use link so halthandler can find args MOVE.L #LK1POP+FOSETXCP,LKOP(A6) ; popsize & opword for halthandler MOVEM.L D0-D1/A0,-(SP) ; SAVE REGS MOVEA.L LKDST(A6),A0 ; A0 := &XCP MOVEQ #0,D1 ; CLEAR FOR USE AS A FPSR VALUE MOVE.W (A0),D1 ; D0 := XCP (0=I,1=U, 2=O,3=D,4=X) MOVE.W QXCPMAP(D1*2),D1 ; MAPS XCP TO FPSR FORMAT SetE: FMOVE.L FPCR,D0 ; DO WE REALLY HAVE TO TRAP? AND.W D1,D0 ; INTERSECT PENDINGS WITH HALT ENABLES ANDI.W #$DF00,D0 ; MASK OUT JUNK, ANY INTERSECTION? (SEE * ABOVE) BEQ @1 ; BRANCH: JUST SET FLAGS CASE cmp.b #cpu68040,CPUFlag ; are we an 040? blt @trapAs881 ; branch if NOT an 040/050 ; Handle trap case for MC68040 ; 000000000000 444444 000000000000 ; 00 00 44 44 00 00 ; 00 00 44 44 00 00 ; 00 00 44 44 00 00 ; 00 00 44 44 00 00 ; 00 00 444444444444444 00 00 ; 00 00 44 00 00 ; 00 00 44 00 00 ; 000000000000 44 000000000000 MOVEA.L D0,A0 ; save trap exceptions FMOVE.L FPSR,D0 ; D0 <- FPSR OR.W D1,D0 ; set desired exceptions in FPSR FMOVE.L D0,FPSR MOVE.L A0,D0 ; restore trap exceptions BTST #15,D0 ; BSUN trap? BEQ.S @10 ; no FMOVE.S #"$7FFFFFFF",FP1 ; yes; create BSUN condition FBGT.W @15 FNOP BRA.S @2 @10: BTST #14,D0 ; SNaN trap? BEQ.S @11 ; no FCMP.S #"$7FBFFFFF",FP1 ; yes; create SNaN condition BRA.S @15 @11: BTST #12,D0 ; OVERFLOW trap? BEQ.S @12 ; no FMOVE.S #"$7F000000",FP1 ; yes; create OVERFLOW condition FSMUL.X FP1,FP1 BRA.S @15 @12: BTST #11,D0 ; UNDERFLOW trap? BEQ.S @13 ; no FMOVE.S #"$1F000000",FP1 ; yes; create UNDERFLOW condition FSMUL.X FP1,FP1 BRA.S @15 @13: BTST #10,D0 ; DIVIDE-BY-ZERO trap? BEQ.S @14 ; no; must be INEXACT FMOVE.B #1,FP1 ; yes; create DIVIDE-BY-ZERO condition FDIV.B #0,FP1 BRA.S @15 @14: FMOVE.B #1,FP1 ; create INEXACT condition FADD.S #"$01000000",FP1 @15: FNOP BRA.S @2 ; ------- 040 ---------------------------------------------------------------- @trapAs881 ; handle trap case as 881/2 ; 888888888888 888888888888 1111 ; 88 88 88 88 11 11 ; 88 88 88 88 11 11 ; 88 88 88 88 11 ; 888888888888 888888888888 11 ; 88 88 88 88 11 ; 88 88 88 88 11 ; 88 88 88 88 11 ; 888888888888 888888888888 1111111111 MOVE.L D1,A0 ; PRIVTRAP% EXPECTS PENDING FLAGS IN A0 MOVEQ #0,D0 ; SET SELECTOR TO SETEXCEPT BSR.W PrivTrap% ; CALL THE PRIVILEGED INSTRUCTION TRAP FNOP ; FLOATING-POINT EXCEPTION HAPPENS HERE! BRA.S @2 ; ------- 881 ---------------------------------------------------------------- @1: ; JUST SET FLAGS CASE: FMOVE.L FPSR,D0 ; GET FPSR ...WASTEFUL, QSETENV ALREADY GOT IT! AND.W $00FF,D0 ; RETAIN ACCRUED EXCEPTIONS ONLY OR.W D1,D0 ; MODIFY FPSR WITH NEW EXCEPTIONS FMOVE.L D0,FPSR ; RESTORE FPSR @2: MOVEM.L (SP)+,D0-D1/A0 ; RESTORE REGS UNLK A6 RTD #4 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; COPYSIGN. ; SRC GETS DST'S SIGN: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; QCPYSGN: MOVE.L A0,-(SP) ; A0 < RTN < &DST < &SRC MOVEA.L 12(SP),A0 ; A0 := SRC ADDR BCLR #7,(A0) ; CLEAR SRC'S SIGN TST ([8,SP]) ; DST'S SIGN ZERO? BPL.S @1 ; IF SO, SKIP NEXT INSTRUCTION BSET #7,(A0) ; ...SET SRC'S SIGN @1: MOVE.L (SP)+,A0 RTD #8 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; WRITEFPU writes extended (SP)+ to FPn, where n = D0.W. TRASHES: A0. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; WRITEFPU: MOVEA.L (SP)+,A0 ; POP RETURN ADDRESS JMP CASEFP0(D0*8) ; SKIP 2 FOR JMP'S EXTENSION WORD (PC POINTS THERE) CASEFP0: FMOVEM (SP)+,FP0 ; 4 BYTE INSTRUCTION, FP0 := (SP)+ BRA.W @1 ; 4 BYTE INSTRUCTION (DON'T CHANGE TO BRA.B) FMOVEM (SP)+,FP1 ; 4 BYTE INSTRUCTION, FP1 := (SP)+ BRA.W @1 ; 4 BYTE INSTRUCTION (DON'T CHANGE TO BRA.B) FMOVEM (SP)+,FP2 ; 4 BYTE INSTRUCTION, FP2 := (SP)+ BRA.W @1 ; 4 BYTE INSTRUCTION (DON'T CHANGE TO BRA.B) FMOVEM (SP)+,FP3 ; 4 BYTE INSTRUCTION, FP3 := (SP)+ BRA.W @1 ; 4 BYTE INSTRUCTION (DON'T CHANGE TO BRA.B) FMOVEM (SP)+,FP4 ; 4 BYTE INSTRUCTION, FP4 := (SP)+ BRA.W @1 ; 4 BYTE INSTRUCTION (DON'T CHANGE TO BRA.B) FMOVEM (SP)+,FP5 ; 4 BYTE INSTRUCTION, FP5 := (SP)+ BRA.W @1 ; 4 BYTE INSTRUCTION (DON'T CHANGE TO BRA.B) FMOVEM (SP)+,FP6 ; 4 BYTE INSTRUCTION, FP6 := (SP)+ BRA.W @1 ; 4 BYTE INSTRUCTION (DON'T CHANGE TO BRA.B) FMOVEM (SP)+,FP7 ; 4 BYTE INSTRUCTION, FP7 := (SP)+ @1: JMP (A0) ; RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; NAN2FPU writes NaN(D1.B) to FPn, where n = D0.W (0 thru 7). TRASHES: A0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; NAN2FPU: CLR.L -(SP) ; CREATE NAN'S LEAST SIG. LONG MOVE.L #$40000000,-(SP) ; CREATE NAN'S MID. SIG. LONG MOVE.B D1,1(SP) ; STUFF NAN CODE BYTE XX INTO 40XX0000 MOVE.L #$7FFF0000,-(SP) ; CREATE NAN'S MOST SIG. LONG BSR.B WRITEFPU ; POPS AND WRITES IT TO FPn RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 000000000000 444444 000000000000 ; 00 00 44 44 00 00 ; 00 00 44 44 00 00 ; 00 00 44 44 00 00 ; 00 00 44 44 00 00 ; 00 00 444444444444444 00 00 ; 00 00 44 00 00 ; 00 00 44 00 00 ; 000000000000 44 000000000000 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The code that follows applies to the 881/882 exception handling model. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; DIVIDE BY ZERO FLOATING-POINT TRAP HANDLER. (See STDHANDLER's comments for more details.) ;; Does the same stuff as STDHANDLER, but also writes back an infinite result from ;; finite operands because the FPU by default does nothing in the trap enabled case; ;; See 6.1.6 Divide-by-Zero, Trap Enabled Results, p. 6-14 of the 881/2 Manual for futher ;; details. Table 6-3 (Possible Operand Errors) on p. 6-14 is also instructive, as well as ;; Table 4-13 (Extension Filed Encoding for Arithmetic Operations), p. 4-128. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DZHANDLER040: FSAVE -(SP) ; SAVE INTERNAL STATE (MUST BE FIRST FPU INSTRUCTION) TST.B (SP) ; NULL FRAME? BEQ.S @0 ; IF NOT, BRANCH: NON-NULL FRAME TST.B 1(SP) ; IDLE 040 frame? BNE.S @1 ; no @0: ; null or idle 040 frame: (NEVER HAPPENS, BUT BE SAFE) FRESTORE (SP)+ ; RESTORE FPU STATE RTE ; GET OUT @1: ; NON-NULL/IDLE FRAME CASE: (ALWAYS HAPPENS) MOVEM.L D0-D2/A0-A1,-(SP) ; SAVE REGISTERS FOR TRAPS USE FMOVEM.L FPCR/FPSR/FPIAR,-(SP) ; SAVE FPU CONTROL REGS FOR TRAPS USE MOVEQ #4,D0 ; A1 <- addr just beyond ETEMP in FSAVE frame ADD.B 1+8*4(SP),D0 LEA 8*4(SP),A1 ADDA.L D0,A1 * * Write-back answer depending on FDIV & FSGLDIV, FLOGx, FATANH. * MOVE.L 8(SP),A0 ; A0 := FPIAR (ADDRESS OF F-LINE OPERATION) MOVE.L (A0),D2 ; D2.W := 2ND WORD OF F-LINE OP, THE COMMAND WORD MOVEQ #$7F,D1 BFTST D2{7:3} ; GENERAL INSTR. TYPE? (SEE 4.7 INSTR. ENCODING DETAILS) BNE STDTAIL040 ; IF NOT, GET OUT VIA STANDARD TAIL AND D2,D1 ; D1 := EXTENSION FIELD (SEE TABLE 4-13, 882 MANUAL) BCLR #6,D1 ; make FSDIV and FDDIV look like FDIV BEQ.S @2 BCLR #2,D1 @2: BFEXTU D2{22:3},D0 ; D0 := n, DESTINATION REGISTER FIELD, 0 THRU 7 CLR.L -(SP) CLR.L -(SP) MOVE.L #$7FFF0000,-(SP) ; PUSH INFINITY ; POOR MAN'S CASE TABLE WITH MOST COMMON FIRST: CMPI.B #$20,D1 ; FDIV? BEQ.S FDIV040 ; YES, BRANCH: FDIV CMPI.B #$24,D1 ; FSGLDIV? BEQ.S FDIV040 ; YES, BRANCH: FDIV CMPI.B #$06,D1 ; FLOGNP1? BEQ.S FLOGx040 ; YES, BRANCH: FLOGx CMPI.B #$14,D1 ; FLOGN? BEQ.S FLOGx040 ; YES, BRANCH: FLOGx CMPI.B #$15,D1 ; FLOG10? BEQ.S FLOGx040 ; YES, BRANCH: FLOGx CMPI.B #$16,D1 ; FLOG2? BEQ.S FLOGx040 ; YES, BRANCH: FLOGx CMPI.B #$0D,D1 ; FATANH? BEQ.S FATANH040 ; YES, BRANCH: FATANH LEA 12(SP),SP ; POP INFINITY BRA STDTAIL040 ; OTHER? GO FINISH UP FDIV040: MOVE.L -24(A1),D1 ; D1 := first longword of extended DST MOVE.L -12(A1),D2 ; D2 := first longword of extended zero SRC EOR.L D1,D2 ; XOR SIGNS OF SRC AND DST TOGETHER BPL.S @1 ; XOR'D SIGNS POSITIVE? IF SO, SKIP NEXT INSTR. BSET #7,(SP) ; NEGATE INFINITY @1: BSR WRITEFPU ; WRITE +-INFINITY TO DST BRA STDTAIL040 ; AND GET OUT FLOGx040: BSET #7,(SP) ; NEGATE INFINITY BSR WRITEFPU ; WRITE -INFINITY TO DST BRA STDTAIL040 ; AND GET OUT FATANH040: BTST.B #7,-12(A1) ; IS SOURCE NEGATIVE? BEQ.S @1 ; IF NOT, SKIP NEXT INSTR. BSET #7,(SP) ; NEGATE INFINITY @1: BSR WRITEFPU ; WRITE +-INFINITY TO DST BRA STDTAIL040 ; AND GET OUT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SNAN FLOATING-POINT TRAP HANDLER. (See STDHANDLER's comments for more details.) ;; Does the same stuff as STDHANDLER, but also quiets SNAN when delivering to FP registers ;; (for SNAN trap enabled situations, the FPU doesn't modify the FP registers). See 6.1.2 ;; Signaling Not-a-Number, p. 6-7 of the 881/2 Manual for futher details for trap ;; enabled/disabled results; Figures 6-4 & 6-5 (881/2 State Frame Formats) are a help, too. ;; ;; Note: 4.5.4 NANS, p. 4-17 of 881/2 Manual, says in case of two NaNs, the dest NaN is picked; ;; Note: p. 6-9 of 881/2 Manual, "When an SNAN TRAP occurs, the exceptional operand is the ;; source input argument converted to extended precision." ;; Ancient History: the Mac+ and IIGS SANEs always delivered the NaN with the higher NaN code ;; when both inputs were NaNs; as of the MacII, this archaic practice was stopped. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SNANHANDLER040: FSAVE -(SP) ; SAVE INTERNAL STATE (MUST BE FIRST FPU INSTRUCTION) TST.B (SP) ; NULL FRAME? BEQ.S @0 ; If so, branch: null 040 frame TST.B 1(SP) ; IDLE 040 frame? BNE.S @1 ; no @0: ; null or idle 040 frame: (NEVER HAPPENS, BUT BE SAFE) FRESTORE (SP)+ ; RESTORE FPU STATE RTE ; GET OUT @1: ; BUSY or UNIMPLEMENTED frame case: (ALWAYS HAPPENS) MOVEM.L D0-D2/A0-A1,-(SP) ; SAVE REGISTERS FOR TRAPS USE FMOVEM.L FPCR/FPSR/FPIAR,-(SP) ; SAVE FPU CONTROL REGS FOR TRAPS USE MOVEQ #4,D0 ; A1 <- addr just beyond ETEMP in FSAVE frame ADD.B 1+8*4(SP),D0 LEA 8*4(SP),A1 ADDA.L D0,A1 * * Is the destination a FPU register? If so, we've got work to do. * MOVE.L 8(SP),A0 ; A0 := FPIAR (ADDRESS OF F-LINE OPERATION) MOVE.L (A0),D2 ; D2.W := 2ND WORD OF F-LINE OP, THE COMMAND WORD BFTST D2{7:3} ; GENERAL INSTR. TYPE? (SEE 4.7 INSTR. ENCODING DETAILS) BNE STDTAIL040 ; IF NOT, GET OUT VIA STANDARD TAIL BFEXTU D2{16:3},D0 ; D0 := OPCLASS (SEE TABLE 4-11, 882 MANUAL) BEQ.S @2 ; OPCLASS 000? BRANCH: FPn DESTINATION SUBQ #2,D0 ; D0 := OPCLASS - 2 BEQ.S @2 ; OPCLASS 010? BRANCH: FPn DESTINATION BRA STDTAIL040 ; OPCLASS XXX? BRANCH: DO NOTHING SPECIAL * * One (or more) of src or dst is SNAN. Which is it so we can quiet it and write it back. * @2: ; FPn DESTINATION CASE: BFEXTU D2{22:3},D0 ; D0 := n, DESTINATION REGISTER FIELD, 0 THRU 7 * * Is the SRC extended NAN? * LEA -12(A1),A0 ; A0 points to ETEMP (extended SRC) in FSAVE frame BFEXTU -40(A1){0:3},D1 ; D1 <- STAG CMPI.B #3,D1 ; NaN SRC? BEQ.B @3 ; yes ADDA #-12,A0 ; no, point A0 to FPTEMP (extended DST) in FSAVE frame @3: MOVE.L 8(A0),D1 ; push last longword of NaN MOVE.L D1,-(SP) MOVE.L 4(A0),D1 ; push middle longword of NaN after setting quiet bit BSET #30,D1 MOVE.L D1,-(SP) MOVE.L (A0),-(SP) ; push first longword of NaN BSR WRITEFPU ; AND WRITE IT BACK BRA STDTAIL040 ; AND GET OUT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; OPERR FLOATING-POINT TRAP HANDLER. (See STDHANDLER's comments for more details.) ;; Does the same stuff as STDHANDLER, but also writes back SANE invalid answers since ;; the FPU writes a default NaN for operand error exceptions. See 6.1.3 Operand Error, ;; Trap Enabled Results, p. 6-8 of the 881/2 Manual for futher details. Table 6-2 ;; (Possible Operand Errors) on p. 6-8 is also instructive, as well as Table 4-13 ;; (Extension Filed Encoding for Arithmetic Operations), p. 4-128. SANE NaN codes ;; can be found in Table 5-1 of the Apple Numerics Manual, 2nd Edition, p. 41. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; OPERRHANDLER040: FSAVE -(SP) ; SAVE INTERNAL STATE (MUST BE FIRST FPU INSTRUCTION) TST.B (SP) ; NULL FRAME? BEQ.S @0 ; If so, branch: null 040 frame TST.B 1(SP) ; IDLE 040 frame? BNE.S @1 ; no @0: ; null or idle 040 frame: (NEVER HAPPENS, BUT BE SAFE) FRESTORE (SP)+ ; RESTORE FPU STATE RTE ; GET OUT @1: ; BUSY or UNIMPLEMENTED frame case: (ALWAYS HAPPENS) MOVEM.L D0-D2/A0-A1,-(SP) ; SAVE REGISTERS FOR TRAPS USE FMOVEM.L FPCR/FPSR/FPIAR,-(SP) ; SAVE FPU CONTROL REGS FOR TRAPS USE MOVEQ #4,D0 ; A1 <- addr just beyond ETEMP in FSAVE frame ADD.B 1+8*4(SP),D0 LEA 8*4(SP),A1 ADDA.L D0,A1 * * Write-back answer. * MOVE.L 8(SP),A0 ; A0 := FPIAR (ADDRESS OF F-LINE OPERATION) MOVE.L (A0),D2 ; D2.W := 2ND WORD OF F-LINE OP, THE COMMAND WORD BFTST D2{7:3} ; GENERAL INSTR. TYPE? (SEE 4.7 INSTR. ENCODING DETAILS) BNE STDTAIL040 ; IF NOT, GET OUT VIA STANDARD TAIL BFEXTU D2{16:3},D0 ; D0 := OPCLASS (SEE TABLE 4-11, 882 MANUAL) BEQ.S FPUDEST040 ; OPCLASS 000? BRANCH: FPn DESTINATION SUBQ #2,D0 ; D0 := OPCLASS - 2 BEQ.S FPUDEST040 ; OPCLASS 010? BRANCH: FPn DESTINATION BRA STDTAIL040 ; OPCLASS XXX? BRANCH: DO NOTHING SPECIAL ; NOTE: QX2{W,L} WILL PEG 881/2 STYLE, NOT MAC+! FPUDEST040: ; FPn DESTINATION CASE: BFEXTU D2{25,7},D1 ; extract command extension word BPL.S @2 ; leading bit clear; not single/double precision BCLR #6,D1 ; clear leading bit (range now $00-$3f) BCLR #2,D1 ; clear bit 2 CMPI.B #1,D1 ; if result is 1, operation is FSSQRT or FDSQRT BNE.S @2 ; otherwise, treat like 881/882 operation MOVEQ #4,D1 @2: MOVE.B NANMAP(D1),D1 ; D1.B := MAP TO SANE NAN CODE (SEE TABLE 5-1,ANM2) ; Create SANE NaN on stack from code in D1.B BEQ.S FSINCOS040 ; ZERO FLAG? BRANCH: WRITE TWO NANS TO FPU ; WRITE ONE NAN TO FPU CASE: BFEXTU D2{22:3},D0 ; D0 := n, DESTINATION REGISTER FIELD, 0 THRU 7 BSR NAN2FPU ; FPn := NAN(D1.B), n = D0.W BRA.S STDTAIL040 ; GO FINISH UP FSINCOS040: ; WRITE TWO NANS TO FPU CASE: (SEE FSINCOS, 882 MAN) MOVE.B #NANTRIG,D1 ; D1 := NANTRIG NAN CODE BFEXTU D2{22:3},D0 ; D0 := s, DESTINATION REGISTER FPs, 0 THRU 7 BSR NAN2FPU ; FPs := NAN(D1.B), s = D0.W BFEXTU D2{29:3},D0 ; D0 := c, DESTINATION REGISTER FPc, 0 THRU 7 BSR NAN2FPU ; FPc := NAN(D1.B), c = D0.W BRA.S STDTAIL040 ; GO FINISH UP * * For a given F-line instruction, return the corresponding SANE NaN code if the * instruction can signal OPERR, otherwise return NaN(255), NANINIT. * * This table is the cross between * * Table 4-13 (Extension Field Encoding for Arithmetic Operations), p.4-128, 882 Manual, * * and * * Table 5-1 (SANE NaN codes), p.41, Apple Numerics Manual, Second Edition, * * with a little help thrown in from * * Table 6-2 (Possible Operand Errors), p.6-8, 882 Manual. * NANMAP: * $00 $01 $02 $03 $04 $05 $06 $07 * FMOVE, FINT, FSINH, FINTRZ, FSQRT, undef, FLOGNP1,undef dc.b NANINIT,NANINIT,NANINIT,NANINIT,NANSQRT,NANINIT,NANLOG, NANINIT * $08 $09 $0A $0B $0C $0D $0E $0F * FETOXM1,FTANH, FATAN, undef, FASIN, FATANH, FSIN, FTAN dc.b NANINIT,NANINIT,NANINIT,NANINIT,NANINVTRIG,NANLOG,NANTRIG,NANTRIG * $10 $11 $12 $13 $14 $15 $16 $17 * FETOX, FTWOTOX,FTENTOX,undef, FLOGN, FLOG10, FLOG2, undef dc.b NANINIT,NANINIT,NANINIT,NANINIT,NANLOG, NANLOG, NANLOG, NANINIT * $18 $19 $1A $1B $1C $1D $1E $1F * FABS, FCOSH, FNEG, undef, FACOS, FCOS, FGETEXP,FGETMAN dc.b NANINIT,NANINIT,NANINIT,NANINIT,NANINVTRIG,NANTRIG,NANINIT,NANINIT * $20 $21 $22 $23 $24 $25 $26 $27 * FDIV, FMOD, FADD, FMUL, FSGLDIV,FREM, FSCALE, FSGLMUL dc.b NANDIV, NANREM, NANADD, NANMUL, NANDIV, NANREM, NANINIT,NANMUL * $28 $29 $2A $2B $2C $2D $2E $2F * FSUB, undef, undef, undef, undef, undef, undef, undef dc.b NANADD, NANINIT,NANINIT,NANINIT,NANINIT,NANINIT,NANINIT,NANINIT * $30 $31 $32 $33 $34 $35 $36 $37 * FSINCOS,FSINCOS,FSINCOS,FSINCOS,FSINCOS,FSINCOS,FSINCOS,FSINCOS dc.b 0, 0, 0, 0, 0, 0, 0, 0; FSINCOS FLAGS * $38 $39 $3A $3B $3C $3D $3E $3F * FCMP, undef, FTST, undef, undef, undef, undef, undef dc.b NANINIT,NANINIT,NANINIT,NANINIT,NANINIT,NANINIT,NANINIT,NANINIT OMEGABOUNDS dc.w OMEGA881BASE-QADDX, OMEGA881END-QADDX BSUNHANDLER040: ; "The third methodÉ", p.6-6 882 Manual, disable BSUN. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; STANDARD TRAP HANDLER FOR FLOATING-POINT EXCEPTIONS BSUN, OVERFLOW, UNDERFLOW, and INEXACT ;; ;; Squirrels away information for emulating SANE's halt mechanism by catching ;; exceptional SANE operations and tail patching them to return to the HALTEMULATOR ;; before returning to the user's code. Creates an image of the SANE stack arguments ;; inside the SANE operation's own local stack frame, places other pertinent information ;; there, disables further trapping, re-directions A6 and the return address, then lets ;; the SANE operation resume executing with the image. The exceptional SANE operation ;; eventually executes a 'UNLK A6' and 'RTD' which takes it surreptitiously to the ;; HALTEMULATOR which then does the final return and stack cleanup. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; STDHANDLER040: FSAVE -(SP) ; SAVE INTERNAL STATE (MUST BE FIRST FPU INSTRUCTION) TST.B (SP) ; NULL FRAME? BEQ.S @0 ; If so, branch: null 040 frame TST.B 1(SP) ; idle 040 frame? BNE.S @1 ; no, busy frame @0: ; null or idle 040 frame: (NEVER HAPPENS, BUT BE SAFE) FRESTORE (SP)+ ; RESTORE FPU STATE RTE ; GET OUT @1: ; NON-NULL/IDLE FRAME CASE: (ALWAYS HAPPENS) MOVEM.L D0-D2/A0-A1,-(SP) ; SAVE REGISTERS FOR TRAPS USE FMOVEM.L FPCR/FPSR/FPIAR,-(SP) ; SAVE FPU CONTROL REGS FOR TRAPS USE MOVEQ #4,D0 ; A1 <- addr just beyond ETEMP in FSAVE frame ADD.B 1+8*4(SP),D0 LEA 8*4(SP),A1 ADDA.L D0,A1 STDTAIL040: MOVEM.L (SP),D1/D2 ; D1 := FPCR ; D2 := FPSR * * Make the exception no longer pending. (See Figure 9-7, 68040 Manual, p. 9-31.) * Always clear E3 bit first in FSAVE frame. If it was already clear, clear E1 bit. * BCLR.B #1,-28(A1) ; clr E3 bit ; BNE.S @2 ; if it was set, clear exception enables---WRONG dest <12/20/90, JPO> BNE.S @0 ; if it was set, check FPIAR <12/20/90, JPO> BCLR.B #2,-28(A1) ; otherwise, clr E1 bit * * Are we being called from PACK4? If not, get out; otherwise, squirrel away info * into the SANE operation's local stack frame. * @0: ; added label <12/20/90, JPO> MOVEA.L 8(SP),A0 ; A0 := FPIAR SUBA.L $0B6E,A0 ; A0 := FPIAR'S RELATIVE DISTANCE FROM &QADDX ; USING QADDX'S JMP ISLAND AT $0B6C ; TECHNOTE #213 says _StripAddress(s) NOT NEEDED??? CMP2 OMEGABOUNDS,A0 ; ARE WE INSIDE PACK4? BCS STDEXIT ; IF NOT, GET OUT; OTHERWISE PROCEED ; NOW KNOW WE GOT HERE FROM PACK4, SO A6 IS OURS! * * Reserve space for the user's scratch registers lest their halthandler (invoked at the * end of the SANE routine) trashes non-trashable registers. Note: SANE has always * preserved all the address & data registers, but is allowed to trash FP0 & FP1. * SUBA #24,A6 ; SKIP OVER POPCOUNT/OPCODE LONG & D0-D2/A0-A1 FMOVEM.L FPCR/FPSR/FPIAR,-(A6) ; SAVE FPU CONTROL REGS FOR HALTEMULATOR SUBA #12,A6 ; ROOM FOR FP3 SCRATCH REGISTER FOR HALTEMULATOR * * Disable traps lest we trap again. (We'll restore them later.) * MOVE.L D1,D0 ; D0 := FRCR BFCLR D0{16:8} ; CLEAR FPCR EXCEPTION ENABLES (FIG.1-1, 881/2 MAN.) FMOVEM D0,FPCR * * Compute the intersection of the previous operation flags and the trap enables. * All bits are kept on the FPU. The user's invalid halt enable bit is kept as * (BSUN or SNAN) so OPERR trapping can always remain on, allowing us to override * the FPU's default OPERR behavior. * MOVE.L D1,D0 ; D0 := FPCR BFCLR D0{16:3} ; PREPARE TO MAP INVALID ENABLE ONTO BSUN,SNAN,OPERR BFTST D1{16:2} ; HALT ON INVALID IFF (BSUN OR SNAN) IS SET BEQ.S @3 ; IF NOT, LEAVE BSUN,SNAN,OPERR BITS CLEARED BFSET D0{16:3} ; OTHERWISE, SET BSUN,SNAN,OPERR BITS @3: AND D2,D0 ; D0 := INTERSECTION FPSR & FPCR BFTST D0{16:2} ; BEGIN MAP'N 8 TO 5 BY OR'N BSUN,SNAN ONTO OPERR BEQ.S @31 ; IF (BSUN OR SNAN) NOT SET, LEAVE OPERR ALONE BSET #13,D0 ; OTHERWISE, SET OPERR BIT @31: BFEXTU D0{18:5},D0 ; GRAB 5 HALT ENABLES MOVE.B (STICKYMAP,PC,D0),D0 ; MAP TO SANE ORDERING; D0.W := HALT_EXCPTNS * * Begin constructing the SANE halt stack frame. * (See Figure 23-1, p.169, Apple Numerics Manual, 2nd Ed.) * LEA LKA6+48(A6),A0 ; A0 := ADDRESS OF ORIGINAL SAVED A6 VALUE SUBQ #8,A6 ; ROOM FOR MISC REC IN FRAME FOR HALTEMULATOR MOVE.W D0,(A6) ; COPY HALT_EXCPTNS INTO FRAME FOR HALTEMULATOR MOVE.L A6,-(A6) ; COPY MISC ADDRESS INTO FRAME FOR HALTEMULATOR MOVE.L LKSRC2(A0),-(A6) ; COPY SRC2 ADDRESS INTO FRAME FOR HALTEMULATOR MOVE.L LKSRC(A0),-(A6) ; COPY SRC ADDRESS INTO FRAME FOR HALTEMULATOR MOVE.L LKDST(A0),-(A6) ; COPY DST ADDRESS INTO FRAME FOR HALTEMULATOR MOVE.W LKOPW(A0),-(A6) ; COPY OPCODE WORD INTO FRAME FOR HALTEMULATOR * * Compute #args - 2 , where #args is the number of SANE addresses pushed: * #args - 2 (-1,0,or 1) = LKCNT/4 - 4 * MOVE LKCNT(A0),D0 ; D0 := LKCNT (HOW MUCH TO POP FOR MANUAL RTD) LSR #2,D0 ; D0 := D0 / 4 SUBQ #4,D0 ; D0 := LKCNT / 4 - 4 * * Construct image of SANE addresses in local frame. * BEQ.S @2 ; ZERO? BRANCH: TWO SANE ARGUMENTS BMI.S @1 ; NEG? BRANCH: ONE SANE ARGUMENT ; THREE SANE ARGUMENTS CASE: MOVE.L LKSRC2(A0),-(A6) ; SRC2 ADDRESS IMAGE FOR RESUMING SANE OP @2: MOVE.L LKSRC(A0),-(A6) ; SRC ADDRESS IMAGE FOR RESUMING SANE OP @1: MOVE.L LKDST(A0),-(A6) ; DST ADDRESS IMAGE FOR RESUMING SANE OP ; (ADDS {4,8,12} BYTES TO LOCAL FRAME SIZE) *** * * Tail patch the user's SANE routine image to return to HALTEMULATOR. * LEA HALTEMULATOR,A1 ; A1 := &HALTEMULATOR MOVE.L A1,-(A6) ; USER'S NEW RETURN ADDRESS := &HALTEMULATOR ; (ADDS 4 BYTES TO LOCAL FRAME SIZE) ** * * Adjust A6 so SANE code will UNLK and RTD the stack image just created. * MOVE.L LKA6(A0),-(A6) ; BOGUS SAVED A6 VALUE ; (ADDS 4 BYTES TO LOCAL FRAME SIZE) ** MOVE.L LKOP(A0),-(A6) ; REPLICATE POPCOUNT AND OPCODE WORDS ; (ADDS 4 BYTES TO LOCAL FRAME SIZE) ** ADDQ #4,A6 ; POINT A6 AT BOGUS SAVED A6 * * Restore FPU state and return from exception. Restore D0_D2/A0_A1, too. * BRA STDEXIT ; ;STDEXIT040: LEA 12(SP),SP ; POP FPCR/FPSR/FPIAR ; MOVEM.L (SP)+,D0-D2/A0-A1 ; RESTORE REGISTERS FOR RESUMING SANE OP ; FRESTORE (SP)+ ; RESTORE FPU STATE ; RTE ; RETURN FROM EXCEPTION resumes exceptional operation ; with traps disabled ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 888888888888 888888888888 1111 ; 88 88 88 88 11 11 ; 88 88 88 88 11 11 ; 88 88 88 88 11 ; 888888888888 888888888888 11 ; 88 88 88 88 11 ; 88 88 88 88 11 ; 88 88 88 88 11 ; 888888888888 888888888888 1111111111 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The code that follows applies to the 881/882 exception handling model. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; PUSHFPn writes extended FPn to -(SP), where n = D0.W. TRASHES: A0. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PUSHFPn: MOVEA.L (SP)+,A0 ; POP RETURN ADDRESS JMP READFP0(D0*8) ; SKIP 2 FOR JMP'S EXTENSION WORD (PC POINTS THERE) READFP0: FMOVEM FP0,-(SP) ; 4 BYTE INSTRUCTION, -(SP) := FP0 BRA.W @1 ; 4 BYTE INSTRUCTION (DON'T CHANGE TO BRA.B) FMOVEM FP1,-(SP) ; 4 BYTE INSTRUCTION, -(SP) := FP1 BRA.W @1 ; 4 BYTE INSTRUCTION (DON'T CHANGE TO BRA.B) FMOVEM FP2,-(SP) ; 4 BYTE INSTRUCTION, -(SP) := FP2 BRA.W @1 ; 4 BYTE INSTRUCTION (DON'T CHANGE TO BRA.B) FMOVEM FP3,-(SP) ; 4 BYTE INSTRUCTION, -(SP) := FP3 BRA.W @1 ; 4 BYTE INSTRUCTION (DON'T CHANGE TO BRA.B) FMOVEM FP4,-(SP) ; 4 BYTE INSTRUCTION, -(SP) := FP4 BRA.W @1 ; 4 BYTE INSTRUCTION (DON'T CHANGE TO BRA.B) FMOVEM FP5,-(SP) ; 4 BYTE INSTRUCTION, -(SP) := FP5 BRA.W @1 ; 4 BYTE INSTRUCTION (DON'T CHANGE TO BRA.B) FMOVEM FP6,-(SP) ; 4 BYTE INSTRUCTION, -(SP) := FP6 BRA.W @1 ; 4 BYTE INSTRUCTION (DON'T CHANGE TO BRA.B) FMOVEM FP7,-(SP) ; 4 BYTE INSTRUCTION, -(SP) := FP7 @1: JMP (A0) ; RTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; DIVIDE BY ZERO FLOATING-POINT TRAP HANDLER. (See STDHANDLER's comments for more details.) ;; Does the same stuff as STDHANDLER, but also writes back an infinite result from ;; finite operands because the FPU by default does nothing in the trap enabled case; ;; See 6.1.6 Divide-by-Zero, Trap Enabled Results, p. 6-14 of the 881/2 Manual for futher ;; details. Table 6-3 (Possible Operand Errors) on p. 6-14 is also instructive, as well as ;; Table 4-13 (Extension Filed Encoding for Arithmetic Operations), p. 4-128. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DZHANDLER: FSAVE -(SP) ; SAVE INTERNAL STATE (MUST BE FIRST FPU INSTRUCTION) TST.B (SP) ; NULL FRAME? BNE.S @0 ; IF NOT, BRANCH: NON-NULL FRAME ; NULL FRAME CASE: (NEVER HAPPENS, BUT BE SAFE) FRESTORE (SP)+ ; RESTORE FPU STATE RTE ; GET OUT @0: ; NON-NULL FRAME CASE: (ALWAYS HAPPENS) MOVEM.L D0-D2/A0-A1,-(SP) ; SAVE REGISTERS FOR TRAPS USE FMOVEM.L FPCR/FPSR/FPIAR,-(SP); SAVE FPU CONTROL REGS FOR TRAPS USE * * Write-back answer depending on FDIV & FSGLDIV, FLOGx, FATANH. * MOVE.L 8(SP),A0 ; A0 := FPIAR (ADDRESS OF F-LINE OPERATION) MOVE.L (A0),D2 ; D2.W := 2ND WORD OF F-LINE OP, THE COMMAND WORD MOVEQ #$3F,D1 BFTST D2{7:3} ; GENERAL INSTR. TYPE? (SEE 4.7 INSTR. ENCODING DETAILS) BNE STDTAIL ; IF NOT, GET OUT VIA STANDARD TAIL AND D2,D1 ; D1 := EXTENSION FIELD (SEE TABLE 4-13, 882 MANUAL) BFEXTU D2{22:3},D0 ; D0 := n, DESTINATION REGISTER FIELD, 0 THRU 7 CLR D2 MOVE.B 1+8*4(SP),D2 ; D2 := STATE FRAME SIZE (SEE FIG. 5-7 882 MANUAL) MOVE.L -16+8*4(SP,D2),D2 ; D2 := THE FIRST LONGWORD OF THE EXCEPTIONAL OPERAND CLR.L -(SP) CLR.L -(SP) MOVE.L #$7FFF0000,-(SP) ; PUSH INFINITY ; POOR MAN'S CASE TABLE WITH MOST COMMON FIRST: CMPI.B #$20,D1 ; FDIV? BEQ.S FDIV ; YES, BRANCH: FDIV CMPI.B #$24,D1 ; FSGLDIV? BEQ.S FDIV ; YES, BRANCH: FDIV CMPI.B #$06,D1 ; FLOGNP1? BEQ.S FLOGx ; YES, BRANCH: FLOGx CMPI.B #$14,D1 ; FLOGN? BEQ.S FLOGx ; YES, BRANCH: FLOGx CMPI.B #$15,D1 ; FLOG10? BEQ.S FLOGx ; YES, BRANCH: FLOGx CMPI.B #$16,D1 ; FLOG2? BEQ.S FLOGx ; YES, BRANCH: FLOGx CMPI.B #$0D,D1 ; FATANH? BEQ.S FATANH ; YES, BRANCH: FATANH LEA 12(SP),SP ; POP INFINITY BRA STDTAIL ; OTHER? GO FINISH UP FDIV: BSR.S PUSHFPn ; PUSH FPn, WHERE n = DO.W MOVE.L (SP)+,D1 ; D1 := FIRST LONGWORD OF DST EXTENDED ADDQ #8,SP ; POP REST OF DST EXTENDED EOR.L D1,D2 ; XOR SIGNS OF SRC AND DST TOGETHER BPL.S @1 ; XOR'D SIGNS POSITIVE? IF SO, SKIP NEXT INSTR. BSET #7,(SP) ; NEGATE INFINITY @1: BSR.S WRITEFPU ; WRITE +-INFINITY TO DST BRA STDTAIL ; AND GET OUT FLOGx: BSET #7,(SP) ; NEGATE INFINITY BSR.S WRITEFPU ; WRITE -INFINITY TO DST BRA STDTAIL ; AND GET OUT FATANH: TST.L D2 ; IS SOURCE NEGATIVE? ; BMI.S @1 ; IF SO, SKIP NEXT INSTR. - deleted <12/17/90, JPO> BPL.S @1 ; if not, skip next instruction <12/17/90, JPO> BSET #7,(SP) ; NEGATE INFINITY @1: BSR.S WRITEFPU ; WRITE +-INFINITY TO DST BRA STDTAIL ; AND GET OUT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SNAN FLOATING-POINT TRAP HANDLER. (See STDHANDLER's comments for more details.) ;; Does the same stuff as STDHANDLER, but also quiets SNAN when delivering to FP registers ;; (for SNAN trap enabled situations, the FPU doesn't modify the FP registers). See 6.1.2 ;; Signaling Not-a-Number, p. 6-7 of the 881/2 Manual for futher details for trap ;; enabled/disabled results; Figures 6-4 & 6-5 (881/2 State Frame Formats) are a help, too. ;; ;; Note: 4.5.4 NANS, p. 4-17 of 881/2 Manual, says in case of two NaNs, the dest NaN is picked; ;; Note: p. 6-9 of 881/2 Manual, "When an SNAN TRAP occurs, the exceptional operand is the ;; source input argument converted to extended precision." ;; Ancient History: the Mac+ and IIGS SANEs always delivered the NaN with the higher NaN code ;; when both inputs were NaNs; as of the MacII, this archaic practice was stopped. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SNANHANDLER: FSAVE -(SP) ; SAVE INTERNAL STATE (MUST BE FIRST FPU INSTRUCTION) TST.B (SP) ; NULL FRAME? BNE.S @0 ; IF NOT, BRANCH: NON-NULL FRAME ; NULL FRAME CASE: (NEVER HAPPENS, BUT BE SAFE) FRESTORE (SP)+ ; RESTORE FPU STATE RTE ; GET OUT @0: ; NON-NULL FRAME CASE: (ALWAYS HAPPENS) MOVEM.L D0-D2/A0-A1,-(SP) ; SAVE REGISTERS FOR TRAPS USE FMOVEM.L FPCR/FPSR/FPIAR,-(SP); SAVE FPU CONTROL REGS FOR TRAPS USE * * Is the destination a FPU register? If so, we've got work to do. * MOVE.L 8(SP),A0 ; A0 := FPIAR (ADDRESS OF F-LINE OPERATION) MOVE.L (A0),D2 ; D2.W := 2ND WORD OF F-LINE OP, THE COMMAND WORD BFTST D2{7:3} ; GENERAL INSTR. TYPE? (SEE 4.7 INSTR. ENCODING DETAILS) BNE STDTAIL ; IF NOT, GET OUT VIA STANDARD TAIL BFEXTU D2{16:3},D0 ; D0 := OPCLASS (SEE TABLE 4-11, 882 MANUAL) BEQ.S @1 ; OPCLASS 000? BRANCH: FPn DESTINATION SUBQ #2,D0 ; D0 := OPCLASS - 2 BEQ.S @1 ; OPCLASS 010? BRANCH: FPn DESTINATION BRA STDTAIL ; OPCLASS XXX? BRANCH: DO NOTHING SPECIAL * * One (or more) of src or dst is SNAN. Which is it so we can quiet it and write it back. * @1: ; FPn DESTINATION CASE: BFEXTU D2{22:3},D0 ; D0 := n, DESTINATION REGISTER FIELD, 0 THRU 7 BSR.S PUSHFPn ; -(SP) := FPn, n = D0.W LEA (SP),A1 ; A1 := © OF FPn * * Is the dst extended NAN? * MOVE.L (A1)+,D1 ; D1 := FIRST LONG OF FPn NOT.L D1 ; TOGGLE EXPONENT BITS <5/21/91-S.McD.> BFTST D1{1:15} ; ARE THEY ALL ZERO? <5/21/91-S.McD.> BNE.S @2 ; IF NOT ALL ONES, BRANCH: SRC MUST BE NAN MOVE.L (A1)+,D1 ; D1 := SECOND LONG OF FPn BCLR #31,D1 ; CLEAR DON'T CARE BIT OR.L (A1),D1 ; D1 := 2ND AND 3RD LONGS OF FPn OR'D TOGETHER BEQ.S @2 ; IF SIGNIFICAND ZERO, BRANCH: SRC MUST BE NAN ; DST IS NAN CASE: BSET #6,-(A1) ; QUIET IT BSR.S WRITEFPU ; AND WRITE IT BACK BRA STDTAIL ; AND GET OUT @2: ; SRC MUST BE NAN CASE: LEA 12(SP),SP ; POP DST PUSHED EARLIER * * Push the exceptional operand from the state frame (see Figure 5-7, 881/2 Manual, p. 5-13), * quiet it, and write it back to the destination register. * CLR D1 ; D0 := 0 MOVE.B 1+8*4(SP),D1 ; D0 := STATE FRAME SIZE LEA -8+8*4(SP,D1),A1 ; A1 := ADDR LEAST SIG. LONG OF EXCEPTIONAL OPERAND MOVE.L (A1),-(SP) ; PUSH LOW LONG OF EXCEPTIONAL OPERAND MOVE.L -(A1),-(SP) ; PUSH MID. LONG OF EXCEPTIONAL OPERAND BSET #6,(SP) ; QUIET IT MOVE.L -(A1),-(SP) ; PUSH HIGH LONG OF EXCEPTIONAL OPERAND BSR.S WRITEFPU ; WRITE IT BACK BRA STDTAIL ; AND GET OUT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; OPERR FLOATING-POINT TRAP HANDLER. (See STDHANDLER's comments for more details.) ;; Does the same stuff as STDHANDLER, but also writes back invalid answers since ;; the FPU doesn't write them for operand error exceptions. See 6.1.3 Operand Error, ;; Trap Enabled Results, p. 6-8 of the 881/2 Manual for futher details. Table 6-2 ;; (Possible Operand Errors) on p. 6-8 is also instructive, as well as Table 4-13 ;; (Extension Filed Encoding for Arithmetic Operations), p. 4-128. SANE NaN codes ;; can be found in Table 5-1 of the Apple Numerics Manual, 2nd Edition, p. 41. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; OPERRHANDLER: FSAVE -(SP) ; SAVE INTERNAL STATE (MUST BE FIRST FPU INSTRUCTION) TST.B (SP) ; NULL FRAME? BNE.S @0 ; IF NOT, BRANCH: NON-NULL FRAME ; NULL FRAME CASE: (NEVER HAPPENS, BUT BE SAFE) FRESTORE (SP)+ ; RESTORE FPU STATE RTE ; GET OUT @0: ; NON-NULL FRAME CASE: (ALWAYS HAPPENS) MOVEM.L D0-D2/A0-A1,-(SP) ; SAVE REGISTERS FOR TRAPS USE FMOVEM.L FPCR/FPSR/FPIAR,-(SP); SAVE FPU CONTROL REGS FOR TRAPS USE * * Write-back answer. * MOVE.L 8(SP),A0 ; A0 := FPIAR (ADDRESS OF F-LINE OPERATION) MOVE.L (A0),D2 ; D2.W := 2ND WORD OF F-LINE OP, THE COMMAND WORD BFTST D2{7:3} ; GENERAL INSTR. TYPE? (SEE 4.7 INSTR. ENCODING DETAILS) BNE STDTAIL ; IF NOT, GET OUT VIA STANDARD TAIL BFEXTU D2{16:3},D0 ; D0 := OPCLASS (SEE TABLE 4-11, 882 MANUAL) BEQ.S FPUDEST ; OPCLASS 000? BRANCH: FPn DESTINATION SUBQ #2,D0 ; D0 := OPCLASS - 2 BEQ.S FPUDEST ; OPCLASS 010? BRANCH: FPn DESTINATION BRA STDTAIL ; OPCLASS XXX? BRANCH: DO NOTHING SPECIAL ; NOTE: QX2{W,L} WILL PEG 881/2 STYLE, NOT MAC+! FPUDEST: ; FPn DESTINATION CASE: MOVE D2,D1 ; D1 := COMMAND WORD ANDI #$3F,D1 ; D1 := EXTENSION FIELD (SEE TABLE 4-13, 882 MAN) MOVE.B NANMAP040(D1),D1 ; D1.B := MAP TO SANE NAN CODE (SEE TABLE 5-1,ANM2) BEQ.S FSINCOS ; ZERO FLAG? BRANCH: WRITE TWO NANS TO FPU ; WRITE ONE NAN TO FPU CASE: BFEXTU D2{22:3},D0 ; D0 := n, DESTINATION REGISTER FIELD, 0 THRU 7 BSR.S NAN2FPU ; FPn := NAN(D1.B), n = D0.W BRA.S STDTAIL ; GO FINISH UP FSINCOS: ; WRITE TWO NANS TO FPU CASE: (SEE FSINCOS, 882 MAN) MOVE.B #NANTRIG,D1 ; D1 := NANTRIG NAN CODE BFEXTU D2{22:3},D0 ; D0 := s, DESTINATION REGISTER FPs, 0 THRU 7 BSR.S NAN2FPU ; FPs := NAN(D1.B), s = D0.W BFEXTU D2{29:3},D0 ; D0 := c, DESTINATION REGISTER FPc, 0 THRU 7 BSR.S NAN2FPU ; FPc := NAN(D1.B), c = D0.W BRA.S STDTAIL ; GO FINISH UP * * For a given F-line instruction, return the corresponding SANE NaN code if the * instruction can signal OPERR, otherwise return NaN(255), NANINIT. * * This table is the cross between * * Table 4-13 (Extension Field Encoding for Arithmetic Operations), p.4-128, 882 Manual, * * and * * Table 5-1 (SANE NaN codes), p.41, Apple Numerics Manual, Second Edition, * * with a little help thrown in from * * Table 6-2 (Possible Operand Errors), p.6-8, 882 Manual. * NANMAP040: * $00 $01 $02 $03 $04 $05 $06 $07 * FMOVE, FINT, FSINH, FINTRZ, FSQRT, undef, FLOGNP1,undef dc.b NANINIT,NANINIT,NANINIT,NANINIT,NANSQRT,NANINIT,NANLOG, NANINIT * $08 $09 $0A $0B $0C $0D $0E $0F * FETOXM1,FTANH, FATAN, undef, FASIN, FATANH, FSIN, FTAN dc.b NANINIT,NANINIT,NANINIT,NANINIT,NANINVTRIG,NANLOG,NANTRIG,NANTRIG * $10 $11 $12 $13 $14 $15 $16 $17 * FETOX, FTWOTOX,FTENTOX,undef, FLOGN, FLOG10, FLOG2, undef dc.b NANINIT,NANINIT,NANINIT,NANINIT,NANLOG, NANLOG, NANLOG, NANINIT * $18 $19 $1A $1B $1C $1D $1E $1F * FABS, FCOSH, FNEG, undef, FACOS, FCOS, FGETEXP,FGETMAN dc.b NANINIT,NANINIT,NANINIT,NANINIT,NANINVTRIG,NANTRIG,NANINIT,NANINIT * $20 $21 $22 $23 $24 $25 $26 $27 * FDIV, FMOD, FADD, FMUL, FSGLDIV,FREM, FSCALE, FSGLMUL dc.b NANDIV, NANREM, NANADD, NANMUL, NANDIV, NANREM, NANINIT,NANMUL * $28 $29 $2A $2B $2C $2D $2E $2F * FSUB, undef, undef, undef, undef, undef, undef, undef dc.b NANADD, NANINIT,NANINIT,NANINIT,NANINIT,NANINIT,NANINIT,NANINIT * $30 $31 $32 $33 $34 $35 $36 $37 * FSINCOS,FSINCOS,FSINCOS,FSINCOS,FSINCOS,FSINCOS,FSINCOS,FSINCOS dc.b 0, 0, 0, 0, 0, 0, 0, 0; FSINCOS FLAGS * $38 $39 $3A $3B $3C $3D $3E $3F * FCMP, undef, FTST, undef, undef, undef, undef, undef dc.b NANINIT,NANINIT,NANINIT,NANINIT,NANINIT,NANINIT,NANINIT,NANINIT ; We don't need to define this twice, right? ; OMEGABOUNDS dc.w OMEGA881BASE-QADDX, OMEGA881END-QADDX PRIVTRAPBOUNDS dc.w $0000,$0080 ; size of PrivTrap <6/21/91, JPO> PrivTrapAddr EQU $65C ; dispatch table addr for PrivTrap <6/21/91, JPO> BSUNHANDLER: ; "The third methodÉ", p.6-6 882 Manual, disable BSUN. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; STANDARD TRAP HANDLER FOR FLOATING-POINT EXCEPTIONS OVERFLOW, UNDERFLOW, INEXACT, BSUN. ;; ;; Squirrels away information for emulating SANE's halt mechanism by catching ;; exceptional SANE operations and tail patching them to return to the HALTEMULATOR ;; before returning to the user's code. Creates an image of the SANE stack arguments ;; inside the SANE operation's own local stack frame, places other pertinent information ;; there, disables further trapping, re-directions A6 and the return address, then lets ;; the SANE operation resume executing with the image. The exceptional SANE operation ;; eventually executes a 'UNLK A6' and 'RTD' which takes it surreptitiously to the ;; HALTEMULATOR which then does the final return and stack cleanup. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; STDHANDLER: FSAVE -(SP) ; SAVE INTERNAL STATE (MUST BE FIRST FPU INSTRUCTION) TST.B (SP) ; NULL FRAME? BNE.S @0 ; IF NOT, BRANCH: NON-NULL FRAME ; NULL FRAME CASE: (NEVER HAPPENS, BUT BE SAFE) FRESTORE (SP)+ ; RESTORE FPU STATE RTE ; GET OUT @0: ; NON-NULL FRAME CASE: (ALWAYS HAPPENS) MOVEM.L D0-D2/A0-A1,-(SP) ; SAVE REGISTERS FOR TRAPS USE FMOVEM.L FPCR/FPSR/FPIAR,-(SP); SAVE FPU CONTROL REGS FOR TRAPS USE STDTAIL: MOVEM.L (SP),D1/D2 ; D1 := FPCR ; D2 := FPSR * * Make the exception no longer pending. (See Figure 5-6, 881/2 Manual, p. 5-11.) * CLR D0 ; D0 := 0 MOVE.B 1+8*4(SP),D0 ; D0 := STATE FRAME SIZE BSET #3,8*4(SP,D0) ; SET BIT 27 OF BIU * * Are we being called from PACK4? If not, get out; otherwise, squirrel away info * into the SANE operation's local stack frame. * MOVEA.L 8(SP),A0 ; A0 := FPIAR SUBA.L $0B6E,A0 ; A0 := FPIAR'S RELATIVE DISTANCE FROM &QADDX ; USING QADDX'S JMP ISLAND AT $0B6C ; TECHNOTE #213 says _StripAddress(s) NOT NEEDED??? CMP2 OMEGABOUNDS,A0 ; ARE WE INSIDE PACK4? ; BCS.S STDEXIT ; IF NOT, GET OUT; OTHERWISE PROCEED - deleted <6/21/91, JPO> BCC.B @4 ; yes <6/21/91, JPO> ; Although exception did not occur within PACK4, it may have been caused by PrivTrap881 MOVEA.L 8(SP),A0 ; A0 <- FPIAR <6/21/91, JPO> SUBA.L PrivTrapAddr,A0 ; A0 <- FPIAR's relative distance from ; PrivTrap881 entry point <6/21/91, JPO> CMP2 PRIVTRAPBOUNDS,A0 ; are we inside PrivTrap881? <6/21/91, JPO> BCS.B STDEXIT ; no. get out <6/21/91, JPO> ; NOW KNOW WE GOT HERE FROM PACK4 (or PrivTrap881), SO A6 IS OURS! * * Reserve space for the user's scratch registers lest their halthandler (invoked at the * end of the SANE routine) trashes non-trashable registers. Note: SANE has always * preserved all the address & data registers, but is allowed to trash FP0 & FP1. * @4: ; label added <6/21/91, JPO> SUBA #24,A6 ; SKIP OVER POPCOUNT/OPCODE LONG & D0-D2/A0-A1 FMOVEM.L FPCR/FPSR/FPIAR,-(A6); SAVE FPU CONTROL REGS FOR HALTEMULATOR SUBA #12,A6 ; ROOM FOR FP3 SCRATCH REGISTER FOR HALTEMULATOR * * Disable traps lest we trap again. (We'll restore them later.) * MOVE.L D1,D0 ; D0 := FRCR BFCLR D0{16:8} ; CLEAR FPCR EXCEPTION ENABLES (FIG.1-1, 881/2 MAN.) FMOVEM D0,FPCR * * Compute the intersection of the previous operation flags and the trap enables. * All bits are kept on the FPU. The user's invalid halt enable bit is kept as * (BSUN or SNAN) so OPERR trapping can always remain on, allowing us to override * the FPU's default OPERR behavior. * MOVE.L D1,D0 ; D0 := FPCR BFCLR D0{16:3} ; PREPARE TO MAP INVALID ENABLE ONTO BSUN,SNAN,OPERR BFTST D1{16:2} ; HALT ON INVALID IFF (BSUN OR SNAN) IS SET BEQ.S @3 ; IF NOT, LEAVE BSUN,SNAN,OPERR BITS CLEARED BFSET D0{16:3} ; OTHERWISE, SET BSUN,SNAN,OPERR BITS @3: AND D2,D0 ; D0 := INTERSECTION FPSR & FPCR BFTST D0{16:2} ; BEGIN MAP'N 8 TO 5 BY OR'N BSUN,SNAN ONTO OPERR BEQ.S @31 ; IF (BSUN OR SNAN) NOT SET, LEAVE OPERR ALONE BSET #13,D0 ; OTHERWISE, SET OPERR BIT @31: BFEXTU D0{18:5},D0 ; GRAB 5 HALT ENABLES MOVE.B (STICKYMAP,PC,D0),D0; MAP TO SANE ORDERING; D0.W := HALT_EXCPTNS * * Begin constructing the SANE halt stack frame. * (See Figure 23-1, p.169, Apple Numerics Manual, 2nd Ed.) * LEA LKA6+48(A6),A0 ; A0 := ADDRESS OF ORIGINAL SAVED A6 VALUE SUBQ #8,A6 ; ROOM FOR MISC REC IN FRAME FOR HALTEMULATOR MOVE.W D0,(A6) ; COPY HALT_EXCPTNS INTO FRAME FOR HALTEMULATOR MOVE.L A6,-(A6) ; COPY MISC ADDRESS INTO FRAME FOR HALTEMULATOR MOVE.L LKSRC2(A0),-(A6) ; COPY SRC2 ADDRESS INTO FRAME FOR HALTEMULATOR MOVE.L LKSRC(A0),-(A6) ; COPY SRC ADDRESS INTO FRAME FOR HALTEMULATOR MOVE.L LKDST(A0),-(A6) ; COPY DST ADDRESS INTO FRAME FOR HALTEMULATOR MOVE.W LKOPW(A0),-(A6) ; COPY OPCODE WORD INTO FRAME FOR HALTEMULATOR * * Compute #args - 2 , where #args is the number of SANE addresses pushed: * #args - 2 (-1,0,or 1) = LKCNT/4 - 4 * MOVE LKCNT(A0),D0 ; D0 := LKCNT (HOW MUCH TO POP FOR MANUAL RTD) LSR #2,D0 ; D0 := D0 / 4 SUBQ #4,D0 ; D0 := LKCNT / 4 - 4 * * Construct image of SANE addresses in local frame. * BEQ.S @2 ; ZERO? BRANCH: TWO SANE ARGUMENTS BMI.S @1 ; NEG? BRANCH: ONE SANE ARGUMENT ; THREE SANE ARGUMENTS CASE: MOVE.L LKSRC2(A0),-(A6) ; SRC2 ADDRESS IMAGE FOR RESUMING SANE OP @2: MOVE.L LKSRC(A0),-(A6) ; SRC ADDRESS IMAGE FOR RESUMING SANE OP @1: MOVE.L LKDST(A0),-(A6) ; DST ADDRESS IMAGE FOR RESUMING SANE OP ; (ADDS {4,8,12} BYTES TO LOCAL FRAME SIZE) *** * * Tail patch the user's SANE routine image to return to HALTEMULATOR. * LEA HALTEMULATOR,A1 ; A1 := &HALTEMULATOR MOVE.L A1,-(A6) ; USER'S NEW RETURN ADDRESS := &HALTEMULATOR ; (ADDS 4 BYTES TO LOCAL FRAME SIZE) ** * * Adjust A6 so SANE code will UNLK and RTD the stack image just created. * MOVE.L LKA6(A0),-(A6) ; BOGUS SAVED A6 VALUE ; (ADDS 4 BYTES TO LOCAL FRAME SIZE) ** MOVE.L LKOP(A0),-(A6) ; REPLICATE POPCOUNT AND OPCODE WORDS ; (ADDS 4 BYTES TO LOCAL FRAME SIZE) ** ADDQ #4,A6 ; POINT A6 AT BOGUS SAVED A6 * * Restore FPU state and return from exception. Restore D0_D2/A0_A1, too. * ; NOTE - this is a COMMON exit point for both the 881/2 and the 040 exception ; handlers. If you change anything here, make sure it's ok with the ; STDEXIT040 code. STDEXIT: LEA 12(SP),SP ; POP FPCR/FPSR/FPIAR MOVEM.L (SP)+,D0-D2/A0-A1 ; RESTORE REGISTERS FOR RESUMING SANE OP FRESTORE (SP)+ ; RESTORE FPU STATE RTE ; RETURN FROM EXCEPTION * * HALTEMULATOR: Exceptional, trapping, SANE routines are tail patched by the above * code to RTS to this routine before returning to the user's code. The * stack looks like the following before HALTEMULATOR pops everything. * * [HALT_FRAME] (18 local frame bytes) * STACK: [Opcode.W< &DST< &SRC< &SRC2< &MISC]< * * [MISC_REC] ( 8 local frame bytes) * < [Halt_excpt.W< PendingCCR.W< PendingD0.L]< * * [REGS] (44 local frame bytes) * < [FP3.X< FPCR.L< FPSR.L< FPIAR.L< D0.L< D1.L< D2.L< A0.L< A1.L]< * * [OLD_STACK] ( 4 local frame bytes) * < [POPCOUNT.W< OPCODE.W< A6.L< &RETURN< &DST< &SRC< &SRC2] * (< &SRC< &SRC2, NOT ALWAYS) * * 74 total frame bytes + * 12 prior " " [see **] = * ------------------ * 86 = LKSIZE, local frame size * +{4,8, or 12} bytes [see ***]= * ={LK1SIZE, LK2SIZE, LK3SIZE} * * NOTE: D0 and CCR have been set by remainder and comparison, so we must be careful * not to trash D0 or affect the CCRs. See pp.168-169 of Numerics Manual, 2nd Ed. * HALTEMULATOR: FMOVEM FP3,26(SP) ; SAVE SCRATCH FP3 LEST HANDLER TRASHES IT MOVEM.L D0-D2/A0-A1,50(SP) ; SAVE SCRATCH ADDRESS & DATA REGISTERS LEA 20(SP),A0 ; A0 := &PENDING_CCR.W MOVE CCR,(A0)+ ; PendingCCR.W := CCR MOVE.L D0,(A0) ; PendingD0.L := D0 TST -4(A0) ; DO WE REALLY NEED TO CALL USER'S HANDLER? BEQ.S @0 ; IF NOT, BRANCH: NO INTERSECTIONS ; INTERSECTIONS CASE: * * Call the user's halt handler. * MOVE.L FPSTATE+2,A0 ; A0 := USER'S HALT ROUTINE ADDRESS JSR (A0) ; CALL USER'S HALT HANDLER, POPPING HALT_FRAME BRA.S @1 ; BRANCH: DONE POPPING HALT_FRAME @0: ; NO INTERSECTIONS CASE: ADDA #18,SP ; POP HALT_FRAME @1: * * Prepare for a manual RTD by copying &RETURN onto last SANE address. * ; DONE POPPING HALT_FRAME CASE: LEA 52(SP),A6 ; A6 := &OLD_STACK ( = &POPCOUNT) MOVE (A6),D1 ; D1 := POPCOUNT MOVE.L 8(A6),0(A6,D1) ; COPY &RETURN ON TOP OF LAST OLD_STACK ADDRESS ADDQ #2,SP ; POP HALT_EXCPT.W MOVE.L 2(SP),30(SP) ; SET D0.L IN REGS TO USER'S PREFERENCE MOVE (SP),CCR ; SET CCR TO USER'S PREFERENCE ADDQ #6,SP ; POP PENDING_CCR.W AND PENDING_D0.L ; DONE POPPING MISC_REC FMOVEM.X (SP)+,FP3 ; RESTORE FPU SCRATCH REGISTERS FMOVEM.L (SP)+,FPCR/FPSR/FPIAR ; RESTORE FPU CONTROL REGISERS MOVEM.L (SP)+,D0-D2/A0-A1 ; RESTORE CPU SCRATCH REGISTERS ; DONE POPPING REGS MOVEA.L 4(A6),A6 ; RESTORE FINAL A6 ADDA (SP),SP ; MANUAL UNLINK RTS ; POP OLD_STACK AND RETURN TO USER'S CODE tFPPriv EQU $A097 ; privileged instruction trap number tUnimplemented EQU $A89F ; _Unimplemented trap number _FPPriv OPWORD tFPPriv IF NOT forROM THEN ; if being built for a System Disk ****************************************************************************************** * * THE FOLLOWING CODE IS DIRECTLY LIFTED FROM MPW3.2'S CSANELIBRT881.A SOURCE CODE, * WRITTEN BY PAUL FINLAYSON, BRIAN MCGHIE, JON OKADA AND STUART MCDONALD. * * IF THIS CODE IS ROMMED, tFPPriv TRAP ($A097) SHOULD BE ROMMED, TOO! THEN WE * WON'T BE CONTINUALLY INSTALLING IT ON THE FLY. A/UX & VM PEOPLE, ARE YOU LISTENING??? * THIS ISOLATION OF PRIVILEGED INSTRUCTIONS TO A SINGLE TRAP WAS FOR YOUR BENEFIT. * WHY, AS OF THE MAC IICI, HAS NO ONE BURNT tFPPriv TRAP INTO ROM? INSTALLING IT * ON THE FLY IS SURELY A NO-NO IN THE A/UX WORLD AND I BET THE A/UX FOLKS AREN'T * OVERRIDDING IT... HOW COULD THEY IF INSTALLING IT IS A NO-NO? * ****************************************************************************************** ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; privileged instruction trap ;; This routine calls trap code containing privileged instructions. If the trap ;; has not been installed it installs it. The purpose of this routine is to provide ;; compatability with future architectures which will not allow user-mode library ;; code to execute privileged instructions. TO BE ADDED: FSAVE/FRESTORE ENTRIES? ;; ;; Trap conventions: ;; Registers D1,D2,A0,A1,A2 are restored to their pre-call values after the ;; trap call. Registers D0,A0,A1 are visible to the trap code and provide the ;; mechanism for input values to the trap code. D0 is the only register that ;; can be changed by the trap code (after return to the caller). TST.W D0 is ;; the last instruction before return to the program calling the trap. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 888888888888 888888888888 1111 ; 88 88 88 88 11 11 ; 88 88 88 88 11 11 ; 88 88 88 88 11 ; 888888888888 888888888888 11 ; 88 88 88 88 11 ; 88 88 88 88 11 ; 88 88 88 88 11 ; 888888888888 888888888888 1111111111 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 16 ; align on nearest cache line boundary PrivTrap881 CMP.W #0,D0 BEQ.S SetExcept881 ; Set the exceptions contained in A0 CMP.W #1,D0 BEQ.S GetTrapVec881 ; GetTrapVector code CMP.W #2,D0 BEQ.S SetTrapVec881 ; SetTrapVector code _SysBreak ; Error in selector code SkipMarkRel dc.w SkipMark-QADDX ; offset to SkipMark from QADDX <9/30/90-S.McD.> SetExcept881 ; FNOP ; Ensure 881 is idle MOVE.L A0,D1 ; Copy exceptions into D1 FSAVE -(SP) ; Save 881 environment FMOVE.L FPSR,D0 ; D0 <- FPSR AND.W #$00FF,D0 ; Clear previous op flags <5/12/90-S.McD.> OR.W D1,D0 ; Set proper exceptions FMOVE.L D0,FPSR ; Move results back to FPSR FMOVE.L FPCR,D0 ; D0 <- FPCR AND.W D1,D0 ; Find exception intersection ANDI.L #$FF00,D0 ; Mask off low byte and high word BEQ.S SkipMark ; If no intersection, then don't ; mark exception pending bit * * LEA SkipMark,A0 ; A0 := @FRESTORE <8/31/90-S.McD.> * FMOVEM.L A0,FPIAR ; FPIAR := A0 <8/31/90-S.McD.> * * Alas, once tFPPriv trap has installed itself, SkipMark is no longer in PACK4! * Since we want FPIAR to always point to something inside PACK4, we must be more * careful how we set it up. Here's how using QADDX's JMP island at $0B6C: * MOVEA.L $0B6E,A0 ; A0 := &QADDX <9/30/90-S.McD.> ADDA.W SkipMarkRel,A0 ; A0 := &SkipMark in PACK4 <9/30/90-S.McD.> FMOVEM.L A0,FPIAR ; FPIAR := A0 <12/12/90-S.McD.> CLR.L D0 MOVE.B 1(SP),D0 ; Load state frame size BCLR #3,(SP,D0) ; Clear bit 27 of BIU SkipMark FRESTORE (SP)+ ; Restore 881 environment RTS GetTrapVec881 ; MOVE.L #$0C0,A1 ; A1 <- &Unordered vector in table MOVE.L (A1)+,(A0)+ ; Traps.Unordered <- &Unordered vector MOVE.L (A1)+,(A0)+ ; Traps.Inexact <- &Inexact vector MOVE.L (A1)+,(A0)+ ; Traps.DivByZero <- &DivByZero vector MOVE.L (A1)+,(A0)+ ; Traps.Underflow <- &Underflow vector MOVE.L (A1)+,(A0)+ ; Traps.OpError <- &OpError vector MOVE.L (A1)+,(A0)+ ; Traps.Overflow <- &Overflow vector MOVE.L (A1)+,(A0)+ ; Traps.SigNaN <- &SigNaN vector RTS SetTrapVec881 ; MOVE.L #$0C0,A1 ; A1 <- &Unordered vector in table MOVE.L (A0)+,(A1)+ ; &Unordered vector <- Traps.Unordered MOVE.L (A0)+,(A1)+ ; &Inexact vector <- Traps.Inexact MOVE.L (A0)+,(A1)+ ; &DivByZero vector <- Traps.DivByZero MOVE.L (A0)+,(A1)+ ; &Underflow vector <- Traps.Underflow MOVE.L (A0)+,(A1)+ ; &OpError vector <- Traps.OpError MOVE.L (A0)+,(A1)+ ; &Overflow vector <- Traps.Overflow MOVE.L (A0)+,(A1)+ ; &SigNaN vector <- Traps.SigNaN RTS EndPt881 EQU * ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; thru next ; 000000000000 444444 000000000000 ; 00 00 44 44 00 00 ; 00 00 44 44 00 00 ; 00 00 44 44 00 00 ; 00 00 44 44 00 00 ; 00 00 444444444444444 00 00 ; 00 00 44 00 00 ; 00 00 44 00 00 ; 000000000000 44 000000000000 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; FPBSUN_VEC040 EQU $1FCC ; special FP exception vector addresses <12/03/90, JPO> FPUNFL_VEC040 EQU $1FD0 FPOPERR_VEC040 EQU $1FD4 FPOVFL_VEC040 EQU $1FD8 FPSNAN_VEC040 EQU $1FDC ALIGN 16 ; align on nearest cache line boundary PrivTrap040 CMP.W #0,D0 BEQ.S SetExcept040 ; Set the exceptions contained in A0 CMP.W #1,D0 BEQ.S GetTrapVec040 ; GetTrapVector code CMP.W #2,D0 BEQ.S SetTrapVec040 ; SetTrapVector code _SysBreak ; Error in selector code GetTrapVec040 ; 040-style MOVE.L #$0C4,A1 ; A1 <- &Inexact vector in table MOVE.L (FPBSUN_VEC040).W,(A0)+ ; Traps.Unordered <- &Unordered vector MOVE.L (A1)+,(A0)+ ; Traps.Inexact <- &Inexact vector MOVE.L (A1),(A0)+ ; Traps.DivByZero <- &DivByZero vector MOVE.L (FPUNFL_VEC040).W,(A0)+ ; Traps.Underflow <- &Underflow vector MOVE.L (FPOPERR_VEC040).W,(A0)+; Traps.OpError <- &OpError vector MOVE.L (FPOVFL_VEC040).W,(A0)+ ; Traps.Overflow <- &Overflow vector MOVE.L (FPSNAN_VEC040).W,(A0)+ ; Traps.SigNaN <- &SigNaN vector RTS SetTrapVec040 MOVE.L #$0C4,A1 ; A1 <- &Inexact vector in table MOVE.L (A0)+,(FPBSUN_VEC040).W ; &Unordered vector <- Traps.Unordered MOVE.L (A0)+,(A1)+ ; &Inexact vector <- Traps.Inexact MOVE.L (A0)+,(A1) ; &DivByZero vector <- Traps.DivByZero MOVE.L (A0)+,(FPUNFL_VEC040).W ; &Underflow vector <- Traps.Underflow MOVE.L (A0)+,(FPOPERR_VEC040).W; &OpError vector <- Traps.OpError MOVE.L (A0)+,(FPOVFL_VEC040).W ; &Overflow vector <- Traps.Overflow MOVE.L (A0)+,(FPSNAN_VEC040).W ; &SigNaN vector <- Traps.SigNaN RTS SetExcept040 FNOP ; Ensure the 040 FP is idle MOVE.L A0,D1 ; Copy exceptions into D1 FMOVE.L FPSR,D0 ; D0 <- FPSR AND.W #$00FF,D0 ; Clear previous op flags <5/12/90-S.McD.> OR.W D1,D0 ; Set proper exceptions FMOVE.L D0,FPSR ; Move results back to FPSR FMOVE.L FPCR,D0 ; D0 <- FPCR AND.W D0,D1 ; Find exception intersection ANDI.L #$FF00,D1 ; Mask off low byte and high word BNE.S @1 ; Force vectoring to highest priority exception handler RTS ; Return if none enabled @1: BTST #15,D1 ; BSUN handler? BEQ.S @2 ; No FMOVE.S #"$7FFFFFFF",FP1 ; Yes; set NaN condition code FBGT.W @done ; BSUN set on unordered branch condition FNOP RTS @2: BTST #14,D1 ; SNaN? BEQ.S @3 ; No FCMP.S #"$7FBFFFFF",FP1 ; Yes; compare FP1 with signaling NaN BRA.S @done @3: BTST #13,D1 ; Operror? BEQ.S @4 ; No FMOVE.S #"$7F800000",FP1 ; Yes; do INF - INF FSUB.X FP1,FP1 BRA.S @done @4: BTST #12,D1 ; Overflow? BEQ.S @5 ; No FMOVE.S #"$7F000000",FP1 ; Yes; load large single-precision value FSMUL.X FP1,FP1 ; and square it @done: FNOP ; Flush pending exceptions RTS ; Return @5: BTST #11,D1 ; Underflow? BEQ.S @6 ; No FMOVE.S #"$1F000000",FP1 ; Yes; load small single-precision value FSMUL.X FP1,FP1 ; and square it (result is subnormal/exact) BRA.S @done @6: BTST #10,D1 ; Divide-by-zero? BEQ.S @7 ; No. Inexact FMOVE.B #1,FP1 ; Yes; divide 1.0 by 0.0 FDIV.B #0,FP1 BRA.S @done @7: FMOVE.B #1,FP1 ; 040 can trap only on INEX2 condition FADD.S #"$00800000",FP1 ; add 1.0 to 2.0**-126 BRA.S @done EndPT040 ENDIF ; {NOT forROM} ; ------------------------------------------------------------------------------------ ; ; Check if the _FPPriv trap is installed and EXECUTE it, one way or another. ; ; If it is installed, execute it. ; If not, install it, THEN execute it. ; ; ------------------------------------------------------------------------------------ PrivTrap% ; Check if the privileged instruction trap is installed IF NOT forROM THEN ; the code lives in PrivTrap.a in ROM MOVEM.L D0/D1/A0/A1,-(SP) ; Save registers MOVE.W #tFPPriv,D0 ; is _FPPriv implemented? _GetTrapAddress ,NEWOS MOVE.L A0,A1 ; Save off address MOVE.W #tUnimplemented,D0 _GetTrapAddress ,NEWOS CMPA.L A0,A1 BNE.S @SkipInstall ; If A0<>A1 then trap is implemented, branch out ; Trap is not installed, install privileged instruction trap cmp.b #cpu68040,CPUFlag ; are we on an 040 or 050 blt.s @881PrivSize ; IF CPU >= 040 THEN lea PrivTrap040,a1 ; A1 <- address of trap code move.l #EndPT040-PrivTrap040,D0; D0 <- size of trap code bra.s @createOnSysHeap ; ELSE @881PrivSize lea PrivTrap881,a1 ; A1 <- address of trap code move.l #EndPT881-PrivTrap881,D0; D0 <- size of trap code @createOnSysHeap ; ENDIF move.l d0,d1 ; save "size of trap code" for later _BlockMove _NewPtr ,SYS ; Get a block from the system heap BMI.S @Error ; Check for error exg a0,a1 ; A0 <- address of trap code; A1 -< address of heap block move.l d1,d0 ; D0 <- size of trap code _BlockMove ; Move copy of code into system heap MOVE.L A1,A0 ; A0 <- address of trap code in system heap MOVE.W #tFPPriv,D0 _SetTrapAddress ,NEWOS ; Install new trap @SkipInstall MOVEM.L (SP)+,D0/D1/A0/A1 ; Restore registers ENDIF ; { NOT forROM } _FPPriv ; privileged instruction trap RTS IF NOT forROM THEN ; ... continued from above @Error _SysBreak ; Crash and burn in a Big Way RTS ; return to caller? ENDIF ; { NOT forROM }