; ; File: XUnfl.a ; ; Contains: Routines to handle the FP UnderFlow exception ; ; Originally Written by: Motorola Inc. ; Adapted to Apple/MPW: Jon Okada ; ; Copyright: © 1990, 1991 by Apple Computer, Inc., all rights reserved. ; ; This file is used in these builds: Mac32 ; ; Change History (most recent first): ; ; <2> 3/30/91 BG Rolling in Jon Okada's latest changes. ; <1> 12/14/90 BG First checked into TERROR/BBS. ; xunfl.a ; Based upon Motorola file 'x_unfl.sa'. ; CHANGE LOG: ; 09 Jan 91 JPO Inserted label "unfl" at start of code. Deleted ; unreferenced labels "take_inex" and "opc011". ; Modified branching to user handlers for UNFL ; and INEX. Changed "bra fpsp_done" to "rte". ; 20 Mar 91 JPO Fixed bug for underflow FMOVE out to double or single ; where rounding produces a normal result. ; * * x_unfl.sa 3.1 12/10/90 * * fpsp_unfl --- FPSP handler for underflow exception * * Trap disabled results * For 881/2 compatibility, sw must denormalize the intermediate * result, then store the result. Denormalization is accomplished * by taking the intermediate result (which is always normalized) and * shifting the mantissa right while incrementing the exponent until * it is equal to the denormalized exponent for the destination * format. After denormalizatoin, the result is rounded to the * destination format. * * Trap enabled results * All trap disabled code applies. In addition the exceptional * operand needs to made available to the user with a bias of $6000 * added to the exponent. * * Copyright (C) Motorola, Inc. 1990 * All Rights Reserved * * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF MOTOROLA * The copyright notice above does not evidence any * actual or intended publication of such source code. unfl: ; label inserted <1/9/91, JPO> fpsp_unfl: link a6,#-LOCAL_SIZE fsave -(a7) movem.l d0-d1/a0-a1,USER_DA(a6) fmovem.x fp0-fp3,USER_FP0(a6) fmovem.l fpcr/fpsr/fpiar,USER_FPCR(a6) * bsr unf_res ;denormalize, round & store interm op * * If underflow exceptions are not enabled, check for inexact * exception * btst.b #unfl_bit,FPCR_ENABLE(a6) beq.b ck_inex btst.b #E3,E_BYTE(a6) beq.b no_e3_1 * * Clear dirty bit on dest register in the frame before branching * to b1238_fix. * bfextu CMDREG3B(a6){6:3},d0 ;get dest reg no bclr.b d0,FPR_DIRTY_BITS(a6) ;clr dest dirty bit bsr b1238_fix ;test for bug1238 case move.l USER_FPSR(a6),FPSR_SHADOW(a6) or.l #sx_mask,E_BYTE(a6) no_e3_1: movem.l USER_DA(a6),d0-d1/a0-a1 fmovem.x USER_FP0(a6),fp0-fp3 fmovem.l USER_FPCR(a6),fpcr/fpsr/fpiar frestore (a7)+ unlk a6 ; bra.l real_unfl ; DELETED <1/9/91, JPO> move.l (FPUNFL_VEC040).W,-(sp) ; push vector to user UNFL handler <1/9/91, JPO> rts ; execute user handler <1/9/91, JPO> * * It is possible to have either inex2 or inex1 exceptions with the * unfl. If the inex enable bit is set in the FPCR, and either * inex2 or inex1 occured, we must clean up and branch to the * real inex handler. * ck_inex: move.b FPCR_ENABLE(a6),d0 and.b FPSR_EXCEPT(a6),d0 andi.b #$3,d0 beq.b unfl_done * * Inexact enabled and reported, and we must take an inexact exception * ;take_inex: ; label DELETED <1/9/91, JPO> btst.b #E3,E_BYTE(a6) beq.b no_e3_2 * * Clear dirty bit on dest register in the frame before branching * to b1238_fix. * bfextu CMDREG3B(a6){6:3},d0 ;get dest reg no bclr.b d0,FPR_DIRTY_BITS(a6) ;clr dest dirty bit bsr b1238_fix ;test for bug1238 case move.l USER_FPSR(a6),FPSR_SHADOW(a6) or.l #sx_mask,E_BYTE(a6) no_e3_2: move.b #INEX_VEC,EXC_VEC+1(a6) movem.l USER_DA(a6),d0-d1/a0-a1 fmovem.x USER_FP0(a6),fp0-fp3 fmovem.l USER_FPCR(a6),fpcr/fpsr/fpiar frestore (a7)+ unlk a6 ; bra.l real_inex ; DELETED <1/9/91, JPO> move.l ($00C4).W,-(sp) ; push vector to user INEX handler <1/9/91, JPO> rts ; execute user handler <1/9/91, JPO> unfl_done: bclr.b #E3,E_BYTE(a6) beq.b e1_set ;if set then branch * * Clear dirty bit on dest resister in the frame before branching * to b1238_fix. * bfextu CMDREG3B(a6){6:3},d0 ;get dest reg no bclr.b d0,FPR_DIRTY_BITS(a6) ;clr dest dirty bit bsr b1238_fix ;test for bug1238 case move.l USER_FPSR(a6),FPSR_SHADOW(a6) or.l #sx_mask,E_BYTE(a6) movem.l USER_DA(a6),d0-d1/a0-a1 fmovem.x USER_FP0(a6),fp0-fp3 fmovem.l USER_FPCR(a6),fpcr/fpsr/fpiar frestore (a7)+ unlk a6 ; bra.l fpsp_done ; DELETED <1/9/91, JPO> rte ; <1/9/91, JPO> e1_set: movem.l USER_DA(a6),d0-d1/a0-a1 fmovem.x USER_FP0(a6),fp0-fp3 fmovem.l USER_FPCR(a6),fpcr/fpsr/fpiar unlk a6 ; bra.l fpsp_done ; DELETED <1/9/91, JPO> rte ; <1/9/91, JPO> * * unf_res --- underflow result calculation * unf_res: bsr g_rndpr ;returns RND_PREC in d0 0=ext, * ;1=sgl, 2=dbl * ;we need the RND_PREC in the * ;upper word for round move.w #0,-(a7) move.w d0,-(a7) ;copy RND_PREC to stack * * * If the exception bit set is E3, the exceptional operand from the * fpu is in WBTEMP; else it is in FPTEMP. * btst.b #E3,E_BYTE(a6) beq.b unf_E1 unf_E3: lea WBTEMP(a6),a0 ;a0 now points to operand * * Test for fsgldiv and fsglmul. If the inst was one of these, then * force the precision to extended for the denorm routine. Use * the user's precision for the round routine. * move.w CMDREG3B(a6),d1 ;check for fsgldiv or fsglmul andi.w #$7f,d1 cmpi.w #$30,d1 ;check for sgldiv beq.b unf_sgl cmpi.w #$33,d1 ;check for sglmul bne.b unf_cont ;if not, use fpcr prec in round unf_sgl: clr.l d0 move.w #$1,(a7) ;override g_rndpr precision * ;force single bra.b unf_cont unf_E1: lea FPTEMP(a6),a0 ;a0 now points to operand unf_cont: bclr.b #sign_bit,LOCAL_EX(a0) ;clear sign bit sne LOCAL_SGN(a0) ;store sign bsr denorm ;returns denorm, a0 points to it * * WARNING: * ;d0 has guard,round sticky bit * ;make sure that it is not corrupted * ;before it reaches the round subroutine * ;also ensure that a0 isn't corrupted * * Set up d1 for round subroutine d1 contains the PREC/MODE * information respectively on upper/lower register halves. * bfextu FPCR_MODE(a6){2:2},d1 ;get mode from FPCR * ;mode in lower d1 add.l (a7)+,d1 ;merge PREC/MODE * * WARNING: a0 and d0 are assumed to be intact between the denorm and * round subroutines. All code between these two subroutines * must not corrupt a0 and d0. * * * Perform Round * Input: a0 points to input operand * d0{31:29} has guard, round, sticky * d1{01:00} has rounding mode * d1{17:16} has rounding precision * Output: a0 points to rounded operand * bsr round ;returns rounded denorm at (a0) * * Differentiate between store to memory vs. store to register * unf_store: bsr g_opcls ;returns opclass in d0{2:0} cmpi.b #$3,d0 bne.b not_opc011 * * At this point, a store to memory is pending * ;opc011: ; label DELETED <1/9/91, JPO> bsr g_dfmtou tst.b d0 beq.b ext_opc011 ;If extended, do not subtract * ;If destination format is sgl/dbl, tst.b LOCAL_HI(a0) ; If round yielded normal, don't subtract <3/20/91, JPO> bmi.b ext_opc011 ; <3/20/91, JPO> subq.w #1,LOCAL_EX(a0) ;account for denorm bias vs. * ;normalized bias * ; normalized denormalized * ;single $7f $7e * ;double $3ff $3fe * ext_opc011: bsr store ;stores to memory bra.b unf_done ;finish up * * At this point, a store to a float register is pending * not_opc011: bsr store ;stores to float register * ;a0 is not corrupted on a store to a * ;float register. * * Set the condition codes according to result * tst.l LOCAL_HI(a0) ;check upper mantissa bne.b ck_sgn tst.l LOCAL_LO(a0) ;check lower mantissa bne.b ck_sgn bset.b #z_bit,FPSR_CC(a6) ;set condition codes if zero ck_sgn: btst.b #sign_bit,LOCAL_EX(a0) ;check the sign bit beq.b unf_done bset.b #neg_bit,FPSR_CC(a6) * * Finish. * unf_done: btst.b #inex2_bit,FPSR_EXCEPT(a6) beq.b no_aunfl bset.b #aunfl_bit,FPSR_AEXCEPT(a6) no_aunfl: rts