* $OpenBSD: res_func.sa,v 1.2 1996/05/29 21:05:32 niklas Exp $ * $NetBSD: res_func.sa,v 1.3 1994/10/26 07:49:22 cgd Exp $ * MOTOROLA MICROPROCESSOR & MEMORY TECHNOLOGY GROUP * M68000 Hi-Performance Microprocessor Division * M68040 Software Package * * M68040 Software Package Copyright (c) 1993, 1994 Motorola Inc. * All rights reserved. * * THE SOFTWARE is provided on an "AS IS" basis and without warranty. * To the maximum extent permitted by applicable law, * MOTOROLA DISCLAIMS ALL WARRANTIES WHETHER EXPRESS OR IMPLIED, * INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A * PARTICULAR PURPOSE and any warranty against infringement with * regard to the SOFTWARE (INCLUDING ANY MODIFIED VERSIONS THEREOF) * and any accompanying written materials. * * To the maximum extent permitted by applicable law, * IN NO EVENT SHALL MOTOROLA BE LIABLE FOR ANY DAMAGES WHATSOEVER * (INCLUDING WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS * PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR * OTHER PECUNIARY LOSS) ARISING OF THE USE OR INABILITY TO USE THE * SOFTWARE. Motorola assumes no responsibility for the maintenance * and support of the SOFTWARE. * * You are hereby granted a copyright license to use, modify, and * distribute the SOFTWARE so long as this entire notice is retained * without alteration in any modified and/or redistributed versions, * and that such modified versions are clearly identified as such. * No licenses are granted by implication, estoppel or otherwise * under any patents or trademarks of Motorola, Inc. * * res_func.sa 3.9 7/29/91 * * Normalizes denormalized numbers if necessary and updates the * stack frame. The function is then restored back into the * machine and the 040 completes the operation. This routine * is only used by the unsupported data type/format handler. * (Exception vector 55). * * For packed move out (fmove.p fpm,) the operation is * completed here; data is packed and moved to user memory. * The stack is restored to the 040 only in the case of a * reportable exception in the conversion. * RES_FUNC IDNT 2,1 Motorola 040 Floating Point Software Package section 8 include fpsp.h sp_bnds: dc.w $3f81,$407e dc.w $3f6a,$0000 dp_bnds: dc.w $3c01,$43fe dc.w $3bcd,$0000 xref mem_write xref bindec xref get_fline xref round xref denorm xref dest_ext xref dest_dbl xref dest_sgl xref unf_sub xref nrm_set xref dnrm_lp xref ovf_res xref reg_dest xref t_ovfl xref t_unfl xdef res_func xdef p_move res_func: clr.b DNRM_FLG(a6) clr.b RES_FLG(a6) clr.b CU_ONLY(a6) tst.b DY_MO_FLG(a6) beq.b monadic dyadic: btst.b #7,DTAG(a6) ;if dop = norm=000, zero=001, * ;inf=010 or nan=011 beq.b monadic ;then branch * ;else denorm * HANDLE DESTINATION DENORM HERE * ;set dtag to norm * ;write the tag & fpte15 to the fstack lea.l FPTEMP(a6),a0 bclr.b #sign_bit,LOCAL_EX(a0) sne LOCAL_SGN(a0) bsr nrm_set ;normalize number (exp will go negative) bclr.b #sign_bit,LOCAL_EX(a0) ;get rid of false sign bfclr LOCAL_SGN(a0){0:8} ;change back to IEEE ext format beq.b dpos bset.b #sign_bit,LOCAL_EX(a0) dpos: bfclr DTAG(a6){0:4} ;set tag to normalized, FPTE15 = 0 bset.b #4,DTAG(a6) ;set FPTE15 or.b #$0f,DNRM_FLG(a6) monadic: lea.l ETEMP(a6),a0 btst.b #direction_bit,CMDREG1B(a6) ;check direction bne.w opclass3 ;it is a mv out * * At this point, only oplcass 0 and 2 possible * btst.b #7,STAG(a6) ;if sop = norm=000, zero=001, * ;inf=010 or nan=011 bne.w mon_dnrm ;else denorm tst.b DY_MO_FLG(a6) ;all cases of dyadic instructions would bne.w normal ;require normalization of denorm * At this point: * monadic instructions: fabs = $18 fneg = $1a ftst = $3a * fmove = $00 fsmove = $40 fdmove = $44 * fsqrt = $05* fssqrt = $41 fdsqrt = $45 * (*fsqrt reencoded to $05) * move.w CMDREG1B(a6),d0 ;get command register andi.l #$7f,d0 ;strip to only command word * * At this point, fabs, fneg, fsmove, fdmove, ftst, fsqrt, fssqrt, and * fdsqrt are possible. * For cases fabs, fneg, fsmove, and fdmove goto spos (do not normalize) * For cases fsqrt, fssqrt, and fdsqrt goto nrm_src (do normalize) * btst.l #0,d0 bne.w normal ;weed out fsqrt instructions * * cu_norm handles fmove in instructions with normalized inputs. * The routine round is used to correctly round the input for the * destination precision and mode. * cu_norm: st CU_ONLY(a6) ;set cu-only inst flag move.w CMDREG1B(a6),d0 andi.b #$3b,d0 ;isolate bits to select inst tst.b d0 beq.l cu_nmove ;if zero, it is an fmove cmpi.b #$18,d0 beq.l cu_nabs ;if $18, it is fabs cmpi.b #$1a,d0 beq.l cu_nneg ;if $1a, it is fneg * * Inst is ftst. Check the source operand and set the cc's accordingly. * No write is done, so simply rts. * cu_ntst: move.w LOCAL_EX(a0),d0 bclr.l #15,d0 sne LOCAL_SGN(a0) beq.b cu_ntpo or.l #neg_mask,USER_FPSR(a6) ;set N cu_ntpo: cmpi.w #$7fff,d0 ;test for inf/nan bne.b cu_ntcz tst.l LOCAL_HI(a0) bne.b cu_ntn tst.l LOCAL_LO(a0) bne.b cu_ntn or.l #inf_mask,USER_FPSR(a6) rts cu_ntn: or.l #nan_mask,USER_FPSR(a6) move.l ETEMP_EX(a6),FPTEMP_EX(a6) ;set up fptemp sign for * ;snan handler rts cu_ntcz: tst.l LOCAL_HI(a0) bne.l cu_ntsx tst.l LOCAL_LO(a0) bne.l cu_ntsx or.l #z_mask,USER_FPSR(a6) cu_ntsx: rts * * Inst is fabs. Execute the absolute value function on the input. * Branch to the fmove code. If the operand is NaN, do nothing. * cu_nabs: move.b STAG(a6),d0 btst.l #5,d0 ;test for NaN or zero bne wr_etemp ;if either, simply write it bclr.b #7,LOCAL_EX(a0) ;do abs bra.b cu_nmove ;fmove code will finish * * Inst is fneg. Execute the negate value function on the input. * Fall though to the fmove code. If the operand is NaN, do nothing. * cu_nneg: move.b STAG(a6),d0 btst.l #5,d0 ;test for NaN or zero bne wr_etemp ;if either, simply write it bchg.b #7,LOCAL_EX(a0) ;do neg * * Inst is fmove. This code also handles all result writes. * If bit 2 is set, round is forced to double. If it is clear, * and bit 6 is set, round is forced to single. If both are clear, * the round precision is found in the fpcr. If the rounding precision * is double or single, round the result before the write. * cu_nmove: move.b STAG(a6),d0 andi.b #$e0,d0 ;isolate stag bits bne wr_etemp ;if not norm, simply write it btst.b #2,CMDREG1B+1(a6) ;check for rd bne cu_nmrd btst.b #6,CMDREG1B+1(a6) ;check for rs bne cu_nmrs * * The move or operation is not with forced precision. Test for * nan or inf as the input; if so, simply write it to FPn. Use the * FPCR_MODE byte to get rounding on norms and zeros. * cu_nmnr: bfextu FPCR_MODE(a6){0:2},d0 tst.b d0 ;check for extended beq cu_wrexn ;if so, just write result cmpi.b #1,d0 ;check for single beq cu_nmrs ;fall through to double * * The move is fdmove or round precision is double. * cu_nmrd: move.l #2,d0 ;set up the size for denorm move.w LOCAL_EX(a0),d1 ;compare exponent to double threshold and.w #$7fff,d1 cmp.w #$3c01,d1 bls cu_nunfl bfextu FPCR_MODE(a6){2:2},d1 ;get rmode or.l #$00020000,d1 ;or in rprec (double) clr.l d0 ;clear g,r,s for round bclr.b #sign_bit,LOCAL_EX(a0) ;convert to internal format sne LOCAL_SGN(a0) bsr.l round bfclr LOCAL_SGN(a0){0:8} beq.b cu_nmrdc bset.b #sign_bit,LOCAL_EX(a0) cu_nmrdc: move.w LOCAL_EX(a0),d1 ;check for overflow and.w #$7fff,d1 cmp.w #$43ff,d1 bge cu_novfl ;take care of overflow case bra.w cu_wrexn * * The move is fsmove or round precision is single. * cu_nmrs: move.l #1,d0 move.w LOCAL_EX(a0),d1 and.w #$7fff,d1 cmp.w #$3f81,d1 bls cu_nunfl bfextu FPCR_MODE(a6){2:2},d1 or.l #$00010000,d1 clr.l d0 bclr.b #sign_bit,LOCAL_EX(a0) sne LOCAL_SGN(a0) bsr.l round bfclr LOCAL_SGN(a0){0:8} beq.b cu_nmrsc bset.b #sign_bit,LOCAL_EX(a0) cu_nmrsc: move.w LOCAL_EX(a0),d1 and.w #$7FFF,d1 cmp.w #$407f,d1 blt cu_wrexn * * The operand is above precision boundaries. Use t_ovfl to * generate the correct value. * cu_novfl: bsr t_ovfl bra cu_wrexn * * The operand is below precision boundaries. Use denorm to * generate the correct value. * cu_nunfl: bclr.b #sign_bit,LOCAL_EX(a0) sne LOCAL_SGN(a0) bsr denorm bfclr LOCAL_SGN(a0){0:8} ;change back to IEEE ext format beq.b cu_nucont bset.b #sign_bit,LOCAL_EX(a0) cu_nucont: bfextu FPCR_MODE(a6){2:2},d1 btst.b #2,CMDREG1B+1(a6) ;check for rd bne inst_d btst.b #6,CMDREG1B+1(a6) ;check for rs bne inst_s swap d1 move.b FPCR_MODE(a6),d1 lsr.b #6,d1 swap d1 bra inst_sd inst_d: or.l #$00020000,d1 bra inst_sd inst_s: or.l #$00010000,d1 inst_sd: bclr.b #sign_bit,LOCAL_EX(a0) sne LOCAL_SGN(a0) bsr.l round bfclr LOCAL_SGN(a0){0:8} beq.b cu_nuflp bset.b #sign_bit,LOCAL_EX(a0) cu_nuflp: btst.b #inex2_bit,FPSR_EXCEPT(a6) beq.b cu_nuninx or.l #aunfl_mask,USER_FPSR(a6) ;if the round was inex, set AUNFL cu_nuninx: tst.l LOCAL_HI(a0) ;test for zero bne.b cu_nunzro tst.l LOCAL_LO(a0) bne.b cu_nunzro * * The mantissa is zero from the denorm loop. Check sign and rmode * to see if rounding should have occured which would leave the lsb. * move.l USER_FPCR(a6),d0 andi.l #$30,d0 ;isolate rmode cmpi.l #$20,d0 blt.b cu_nzro bne.b cu_nrp cu_nrm: tst.w LOCAL_EX(a0) ;if positive, set lsb bge.b cu_nzro btst.b #7,FPCR_MODE(a6) ;check for double beq.b cu_nincs bra.b cu_nincd cu_nrp: tst.w LOCAL_EX(a0) ;if positive, set lsb blt.b cu_nzro btst.b #7,FPCR_MODE(a6) ;check for double beq.b cu_nincs cu_nincd: or.l #$800,LOCAL_LO(a0) ;inc for double bra cu_nunzro cu_nincs: or.l #$100,LOCAL_HI(a0) ;inc for single bra cu_nunzro cu_nzro: or.l #z_mask,USER_FPSR(a6) move.b STAG(a6),d0 andi.b #$e0,d0 cmpi.b #$40,d0 ;check if input was tagged zero beq.b cu_numv cu_nunzro: or.l #unfl_mask,USER_FPSR(a6) ;set unfl cu_numv: move.l (a0),ETEMP(a6) move.l 4(a0),ETEMP_HI(a6) move.l 8(a0),ETEMP_LO(a6) * * Write the result to memory, setting the fpsr cc bits. NaN and Inf * bypass cu_wrexn. * cu_wrexn: tst.w LOCAL_EX(a0) ;test for zero beq.b cu_wrzero cmp.w #$8000,LOCAL_EX(a0) ;test for zero bne.b cu_wreon cu_wrzero: or.l #z_mask,USER_FPSR(a6) ;set Z bit cu_wreon: tst.w LOCAL_EX(a0) bpl wr_etemp or.l #neg_mask,USER_FPSR(a6) bra wr_etemp * * HANDLE SOURCE DENORM HERE * * ;clear denorm stag to norm * ;write the new tag & ete15 to the fstack mon_dnrm: * * At this point, check for the cases in which normalizing the * denorm produces incorrect results. * tst.b DY_MO_FLG(a6) ;all cases of dyadic instructions would bne.b nrm_src ;require normalization of denorm * At this point: * monadic instructions: fabs = $18 fneg = $1a ftst = $3a * fmove = $00 fsmove = $40 fdmove = $44 * fsqrt = $05* fssqrt = $41 fdsqrt = $45 * (*fsqrt reencoded to $05) * move.w CMDREG1B(a6),d0 ;get command register andi.l #$7f,d0 ;strip to only command word * * At this point, fabs, fneg, fsmove, fdmove, ftst, fsqrt, fssqrt, and * fdsqrt are possible. * For cases fabs, fneg, fsmove, and fdmove goto spos (do not normalize) * For cases fsqrt, fssqrt, and fdsqrt goto nrm_src (do normalize) * btst.l #0,d0 bne.b nrm_src ;weed out fsqrt instructions st CU_ONLY(a6) ;set cu-only inst flag bra cu_dnrm ;fmove, fabs, fneg, ftst * ;cases go to cu_dnrm nrm_src: bclr.b #sign_bit,LOCAL_EX(a0) sne LOCAL_SGN(a0) bsr nrm_set ;normalize number (exponent will go * ; negative) bclr.b #sign_bit,LOCAL_EX(a0) ;get rid of false sign bfclr LOCAL_SGN(a0){0:8} ;change back to IEEE ext format beq.b spos bset.b #sign_bit,LOCAL_EX(a0) spos: bfclr STAG(a6){0:4} ;set tag to normalized, FPTE15 = 0 bset.b #4,STAG(a6) ;set ETE15 or.b #$f0,DNRM_FLG(a6) normal: tst.b DNRM_FLG(a6) ;check if any of the ops were denorms bne ck_wrap ;if so, check if it is a potential * ;wrap-around case fix_stk: move.b #$fe,CU_SAVEPC(a6) bclr.b #E1,E_BYTE(a6) clr.w NMNEXC(a6) st.b RES_FLG(a6) ;indicate that a restore is needed rts * * cu_dnrm handles all cu-only instructions (fmove, fabs, fneg, and * ftst) completly in software without an frestore to the 040. * cu_dnrm: st.b CU_ONLY(a6) move.w CMDREG1B(a6),d0 andi.b #$3b,d0 ;isolate bits to select inst tst.b d0 beq.l cu_dmove ;if zero, it is an fmove cmpi.b #$18,d0 beq.l cu_dabs ;if $18, it is fabs cmpi.b #$1a,d0 beq.l cu_dneg ;if $1a, it is fneg * * Inst is ftst. Check the source operand and set the cc's accordingly. * No write is done, so simply rts. * cu_dtst: move.w LOCAL_EX(a0),d0 bclr.l #15,d0 sne LOCAL_SGN(a0) beq.b cu_dtpo or.l #neg_mask,USER_FPSR(a6) ;set N cu_dtpo: cmpi.w #$7fff,d0 ;test for inf/nan bne.b cu_dtcz tst.l LOCAL_HI(a0) bne.b cu_dtn tst.l LOCAL_LO(a0) bne.b cu_dtn or.l #inf_mask,USER_FPSR(a6) rts cu_dtn: or.l #nan_mask,USER_FPSR(a6) move.l ETEMP_EX(a6),FPTEMP_EX(a6) ;set up fptemp sign for * ;snan handler rts cu_dtcz: tst.l LOCAL_HI(a0) bne.l cu_dtsx tst.l LOCAL_LO(a0) bne.l cu_dtsx or.l #z_mask,USER_FPSR(a6) cu_dtsx: rts * * Inst is fabs. Execute the absolute value function on the input. * Branch to the fmove code. * cu_dabs: bclr.b #7,LOCAL_EX(a0) ;do abs bra.b cu_dmove ;fmove code will finish * * Inst is fneg. Execute the negate value function on the input. * Fall though to the fmove code. * cu_dneg: bchg.b #7,LOCAL_EX(a0) ;do neg * * Inst is fmove. This code also handles all result writes. * If bit 2 is set, round is forced to double. If it is clear, * and bit 6 is set, round is forced to single. If both are clear, * the round precision is found in the fpcr. If the rounding precision * is double or single, the result is zero, and the mode is checked * to determine if the lsb of the result should be set. * cu_dmove: btst.b #2,CMDREG1B+1(a6) ;check for rd bne cu_dmrd btst.b #6,CMDREG1B+1(a6) ;check for rs bne cu_dmrs * * The move or operation is not with forced precision. Use the * FPCR_MODE byte to get rounding. * cu_dmnr: bfextu FPCR_MODE(a6){0:2},d0 tst.b d0 ;check for extended beq cu_wrexd ;if so, just write result cmpi.b #1,d0 ;check for single beq cu_dmrs ;fall through to double * * The move is fdmove or round precision is double. Result is zero. * Check rmode for rp or rm and set lsb accordingly. * cu_dmrd: bfextu FPCR_MODE(a6){2:2},d1 ;get rmode tst.w LOCAL_EX(a0) ;check sign blt.b cu_dmdn cmpi.b #3,d1 ;check for rp bne cu_dpd ;load double pos zero bra cu_dpdr ;load double pos zero w/lsb cu_dmdn: cmpi.b #2,d1 ;check for rm bne cu_dnd ;load double neg zero bra cu_dndr ;load double neg zero w/lsb * * The move is fsmove or round precision is single. Result is zero. * Check for rp or rm and set lsb accordingly. * cu_dmrs: bfextu FPCR_MODE(a6){2:2},d1 ;get rmode tst.w LOCAL_EX(a0) ;check sign blt.b cu_dmsn cmpi.b #3,d1 ;check for rp bne cu_spd ;load single pos zero bra cu_spdr ;load single pos zero w/lsb cu_dmsn: cmpi.b #2,d1 ;check for rm bne cu_snd ;load single neg zero bra cu_sndr ;load single neg zero w/lsb * * The precision is extended, so the result in etemp is correct. * Simply set unfl (not inex2 or aunfl) and write the result to * the correct fp register. cu_wrexd: or.l #unfl_mask,USER_FPSR(a6) tst.w LOCAL_EX(a0) beq wr_etemp or.l #neg_mask,USER_FPSR(a6) bra wr_etemp * * These routines write +/- zero in double format. The routines * cu_dpdr and cu_dndr set the double lsb. * cu_dpd: move.l #$3c010000,LOCAL_EX(a0) ;force pos double zero clr.l LOCAL_HI(a0) clr.l LOCAL_LO(a0) or.l #z_mask,USER_FPSR(a6) or.l #unfinx_mask,USER_FPSR(a6) bra wr_etemp cu_dpdr: move.l #$3c010000,LOCAL_EX(a0) ;force pos double zero clr.l LOCAL_HI(a0) move.l #$800,LOCAL_LO(a0) ;with lsb set or.l #unfinx_mask,USER_FPSR(a6) bra wr_etemp cu_dnd: move.l #$bc010000,LOCAL_EX(a0) ;force pos double zero clr.l LOCAL_HI(a0) clr.l LOCAL_LO(a0) or.l #z_mask,USER_FPSR(a6) or.l #neg_mask,USER_FPSR(a6) or.l #unfinx_mask,USER_FPSR(a6) bra wr_etemp cu_dndr: move.l #$bc010000,LOCAL_EX(a0) ;force pos double zero clr.l LOCAL_HI(a0) move.l #$800,LOCAL_LO(a0) ;with lsb set or.l #neg_mask,USER_FPSR(a6) or.l #unfinx_mask,USER_FPSR(a6) bra wr_etemp * * These routines write +/- zero in single format. The routines * cu_dpdr and cu_dndr set the single lsb. * cu_spd: move.l #$3f810000,LOCAL_EX(a0) ;force pos single zero clr.l LOCAL_HI(a0) clr.l LOCAL_LO(a0) or.l #z_mask,USER_FPSR(a6) or.l #unfinx_mask,USER_FPSR(a6) bra wr_etemp cu_spdr: move.l #$3f810000,LOCAL_EX(a0) ;force pos single zero move.l #$100,LOCAL_HI(a0) ;with lsb set clr.l LOCAL_LO(a0) or.l #unfinx_mask,USER_FPSR(a6) bra wr_etemp cu_snd: move.l #$bf810000,LOCAL_EX(a0) ;force pos single zero clr.l LOCAL_HI(a0) clr.l LOCAL_LO(a0) or.l #z_mask,USER_FPSR(a6) or.l #neg_mask,USER_FPSR(a6) or.l #unfinx_mask,USER_FPSR(a6) bra wr_etemp cu_sndr: move.l #$bf810000,LOCAL_EX(a0) ;force pos single zero move.l #$100,LOCAL_HI(a0) ;with lsb set clr.l LOCAL_LO(a0) or.l #neg_mask,USER_FPSR(a6) or.l #unfinx_mask,USER_FPSR(a6) bra wr_etemp * * This code checks for 16-bit overflow conditions on dyadic * operations which are not restorable into the floating-point * unit and must be completed in software. Basically, this * condition exists with a very large norm and a denorm. One * of the operands must be denormalized to enter this code. * * Flags used: * DY_MO_FLG contains 0 for monadic op, $ff for dyadic * DNRM_FLG contains $00 for neither op denormalized * $0f for the destination op denormalized * $f0 for the source op denormalized * $ff for both ops denormalzed * * The wrap-around condition occurs for add, sub, div, and cmp * when * * abs(dest_exp - src_exp) >= $8000 * * and for mul when * * (dest_exp + src_exp) < $0 * * we must process the operation here if this case is true. * * The rts following the frcfpn routine is the exit from res_func * for this condition. The restore flag (RES_FLG) is left clear. * No frestore is done unless an exception is to be reported. * * For fadd: * if(sign_of(dest) != sign_of(src)) * replace exponent of src with $3fff (keep sign) * use fpu to perform dest+new_src (user's rmode and X) * clr sticky * else * set sticky * call round with user's precision and mode * move result to fpn and wbtemp * * For fsub: * if(sign_of(dest) == sign_of(src)) * replace exponent of src with $3fff (keep sign) * use fpu to perform dest+new_src (user's rmode and X) * clr sticky * else * set sticky * call round with user's precision and mode * move result to fpn and wbtemp * * For fdiv/fsgldiv: * if(both operands are denorm) * restore_to_fpu; * if(dest is norm) * force_ovf; * else(dest is denorm) * force_unf: * * For fcmp: * if(dest is norm) * N = sign_of(dest); * else(dest is denorm) * N = sign_of(src); * * For fmul: * if(both operands are denorm) * force_unf; * if((dest_exp + src_exp) < 0) * force_unf: * else * restore_to_fpu; * * local equates: addcode equ $22 subcode equ $28 mulcode equ $23 divcode equ $20 cmpcode equ $38 ck_wrap: tst.b DY_MO_FLG(a6) ;check for fsqrt beq fix_stk ;if zero, it is fsqrt move.w CMDREG1B(a6),d0 andi.w #$3b,d0 ;strip to command bits cmpi.w #addcode,d0 beq wrap_add cmpi.w #subcode,d0 beq wrap_sub cmpi.w #mulcode,d0 beq wrap_mul cmpi.w #cmpcode,d0 beq wrap_cmp * * Inst is fdiv. * wrap_div: cmp.b #$ff,DNRM_FLG(a6) ;if both ops denorm, beq fix_stk ;restore to fpu * * One of the ops is denormalized. Test for wrap condition * and force the result. * cmp.b #$0f,DNRM_FLG(a6) ;check for dest denorm bne.b div_srcd div_destd: bsr.l ckinf_ns bne fix_stk bfextu ETEMP_EX(a6){1:15},d0 ;get src exp (always pos) bfexts FPTEMP_EX(a6){1:15},d1 ;get dest exp (always neg) sub.l d1,d0 ;subtract dest from src cmp.l #$7fff,d0 blt fix_stk ;if less, not wrap case clr.b WBTEMP_SGN(a6) move.w ETEMP_EX(a6),d0 ;find the sign of the result move.w FPTEMP_EX(a6),d1 eor.w d1,d0 andi.w #$8000,d0 beq force_unf st.b WBTEMP_SGN(a6) bra force_unf ckinf_ns: move.b STAG(a6),d0 ;check source tag for inf or nan bra ck_in_com ckinf_nd: move.b DTAG(a6),d0 ;check destination tag for inf or nan ck_in_com: andi.b #$60,d0 ;isolate tag bits cmp.b #$40,d0 ;is it inf? beq nan_or_inf ;not wrap case cmp.b #$60,d0 ;is it nan? beq nan_or_inf ;yes, not wrap case? cmp.b #$20,d0 ;is it a zero? beq nan_or_inf ;yes clr.l d0 rts ;then it is either a zero of norm, * ;check wrap case nan_or_inf: moveq.l #-1,d0 rts div_srcd: bsr.l ckinf_nd bne fix_stk bfextu FPTEMP_EX(a6){1:15},d0 ;get dest exp (always pos) bfexts ETEMP_EX(a6){1:15},d1 ;get src exp (always neg) sub.l d1,d0 ;subtract src from dest cmp.l #$8000,d0 blt fix_stk ;if less, not wrap case clr.b WBTEMP_SGN(a6) move.w ETEMP_EX(a6),d0 ;find the sign of the result move.w FPTEMP_EX(a6),d1 eor.w d1,d0 andi.w #$8000,d0 beq.b force_ovf st.b WBTEMP_SGN(a6) * * This code handles the case of the instruction resulting in * an overflow condition. * force_ovf: bclr.b #E1,E_BYTE(a6) or.l #ovfl_inx_mask,USER_FPSR(a6) clr.w NMNEXC(a6) lea.l WBTEMP(a6),a0 ;point a0 to memory location move.w CMDREG1B(a6),d0 btst.l #6,d0 ;test for forced precision beq.b frcovf_fpcr btst.l #2,d0 ;check for double bne.b frcovf_dbl move.l #$1,d0 ;inst is forced single bra.b frcovf_rnd frcovf_dbl: move.l #$2,d0 ;inst is forced double bra.b frcovf_rnd frcovf_fpcr: bfextu FPCR_MODE(a6){0:2},d0 ;inst not forced - use fpcr prec frcovf_rnd: * The 881/882 does not set inex2 for the following case, so the * line is commented out to be compatible with 881/882 * tst.b d0 * beq.b frcovf_x * or.l #inex2_mask,USER_FPSR(a6) ;if prec is s or d, set inex2 *frcovf_x: bsr.l ovf_res ;get correct result based on * ;round precision/mode. This * ;sets FPSR_CC correctly * ;returns in external format bfclr WBTEMP_SGN(a6){0:8} beq frcfpn bset.b #sign_bit,WBTEMP_EX(a6) bra frcfpn * * Inst is fadd. * wrap_add: cmp.b #$ff,DNRM_FLG(a6) ;if both ops denorm, beq fix_stk ;restore to fpu * * One of the ops is denormalized. Test for wrap condition * and complete the instruction. * cmp.b #$0f,DNRM_FLG(a6) ;check for dest denorm bne.b add_srcd add_destd: bsr.l ckinf_ns bne fix_stk bfextu ETEMP_EX(a6){1:15},d0 ;get src exp (always pos) bfexts FPTEMP_EX(a6){1:15},d1 ;get dest exp (always neg) sub.l d1,d0 ;subtract dest from src cmp.l #$8000,d0 blt fix_stk ;if less, not wrap case bra add_wrap add_srcd: bsr.l ckinf_nd bne fix_stk bfextu FPTEMP_EX(a6){1:15},d0 ;get dest exp (always pos) bfexts ETEMP_EX(a6){1:15},d1 ;get src exp (always neg) sub.l d1,d0 ;subtract src from dest cmp.l #$8000,d0 blt fix_stk ;if less, not wrap case * * Check the signs of the operands. If they are unlike, the fpu * can be used to add the norm and 1.0 with the sign of the * denorm and it will correctly generate the result in extended * precision. We can then call round with no sticky and the result * will be correct for the user's rounding mode and precision. If * the signs are the same, we call round with the sticky bit set * and the result will be correctfor the user's rounding mode and * precision. * add_wrap: move.w ETEMP_EX(a6),d0 move.w FPTEMP_EX(a6),d1 eor.w d1,d0 andi.w #$8000,d0 beq add_same * * The signs are unlike. * cmp.b #$0f,DNRM_FLG(a6) ;is dest the denorm? bne.b add_u_srcd move.w FPTEMP_EX(a6),d0 andi.w #$8000,d0 or.w #$3fff,d0 ;force the exponent to +/- 1 move.w d0,FPTEMP_EX(a6) ;in the denorm move.l USER_FPCR(a6),d0 andi.l #$30,d0 fmove.l d0,fpcr ;set up users rmode and X fmove.x ETEMP(a6),fp0 fadd.x FPTEMP(a6),fp0 lea.l WBTEMP(a6),a0 ;point a0 to wbtemp in frame fmove.l fpsr,d1 or.l d1,USER_FPSR(a6) ;capture cc's and inex from fadd fmove.x fp0,WBTEMP(a6) ;write result to memory lsr.l #4,d0 ;put rmode in lower 2 bits move.l USER_FPCR(a6),d1 andi.l #$c0,d1 lsr.l #6,d1 ;put precision in upper word swap d1 or.l d0,d1 ;set up for round call clr.l d0 ;force sticky to zero bclr.b #sign_bit,WBTEMP_EX(a6) sne WBTEMP_SGN(a6) bsr.l round ;round result to users rmode & prec bfclr WBTEMP_SGN(a6){0:8} ;convert back to IEEE ext format beq frcfpnr bset.b #sign_bit,WBTEMP_EX(a6) bra frcfpnr add_u_srcd: move.w ETEMP_EX(a6),d0 andi.w #$8000,d0 or.w #$3fff,d0 ;force the exponent to +/- 1 move.w d0,ETEMP_EX(a6) ;in the denorm move.l USER_FPCR(a6),d0 andi.l #$30,d0 fmove.l d0,fpcr ;set up users rmode and X fmove.x ETEMP(a6),fp0 fadd.x FPTEMP(a6),fp0 fmove.l fpsr,d1 or.l d1,USER_FPSR(a6) ;capture cc's and inex from fadd lea.l WBTEMP(a6),a0 ;point a0 to wbtemp in frame fmove.x fp0,WBTEMP(a6) ;write result to memory lsr.l #4,d0 ;put rmode in lower 2 bits move.l USER_FPCR(a6),d1 andi.l #$c0,d1 lsr.l #6,d1 ;put precision in upper word swap d1 or.l d0,d1 ;set up for round call clr.l d0 ;force sticky to zero bclr.b #sign_bit,WBTEMP_EX(a6) sne WBTEMP_SGN(a6) ;use internal format for round bsr.l round ;round result to users rmode & prec bfclr WBTEMP_SGN(a6){0:8} ;convert back to IEEE ext format beq frcfpnr bset.b #sign_bit,WBTEMP_EX(a6) bra frcfpnr * * Signs are alike: * add_same: cmp.b #$0f,DNRM_FLG(a6) ;is dest the denorm? bne.b add_s_srcd add_s_destd: lea.l ETEMP(a6),a0 move.l USER_FPCR(a6),d0 andi.l #$30,d0 lsr.l #4,d0 ;put rmode in lower 2 bits move.l USER_FPCR(a6),d1 andi.l #$c0,d1 lsr.l #6,d1 ;put precision in upper word swap d1 or.l d0,d1 ;set up for round call move.l #$20000000,d0 ;set sticky for round bclr.b #sign_bit,ETEMP_EX(a6) sne ETEMP_SGN(a6) bsr.l round ;round result to users rmode & prec bfclr ETEMP_SGN(a6){0:8} ;convert back to IEEE ext format beq.b add_s_dclr bset.b #sign_bit,ETEMP_EX(a6) add_s_dclr: lea.l WBTEMP(a6),a0 move.l ETEMP(a6),(a0) ;write result to wbtemp move.l ETEMP_HI(a6),4(a0) move.l ETEMP_LO(a6),8(a0) tst.w ETEMP_EX(a6) bgt add_ckovf or.l #neg_mask,USER_FPSR(a6) bra add_ckovf add_s_srcd: lea.l FPTEMP(a6),a0 move.l USER_FPCR(a6),d0 andi.l #$30,d0 lsr.l #4,d0 ;put rmode in lower 2 bits move.l USER_FPCR(a6),d1 andi.l #$c0,d1 lsr.l #6,d1 ;put precision in upper word swap d1 or.l d0,d1 ;set up for round call move.l #$20000000,d0 ;set sticky for round bclr.b #sign_bit,FPTEMP_EX(a6) sne FPTEMP_SGN(a6) bsr.l round ;round result to users rmode & prec bfclr FPTEMP_SGN(a6){0:8} ;convert back to IEEE ext format beq.b add_s_sclr bset.b #sign_bit,FPTEMP_EX(a6) add_s_sclr: lea.l WBTEMP(a6),a0 move.l FPTEMP(a6),(a0) ;write result to wbtemp move.l FPTEMP_HI(a6),4(a0) move.l FPTEMP_LO(a6),8(a0) tst.w FPTEMP_EX(a6) bgt add_ckovf or.l #neg_mask,USER_FPSR(a6) add_ckovf: move.w WBTEMP_EX(a6),d0 andi.w #$7fff,d0 cmpi.w #$7fff,d0 bne frcfpnr * * The result has overflowed to $7fff exponent. Set I, ovfl, * and aovfl, and clr the mantissa (incorrectly set by the * round routine.) * or.l #inf_mask+ovfl_inx_mask,USER_FPSR(a6) clr.l 4(a0) bra frcfpnr * * Inst is fsub. * wrap_sub: cmp.b #$ff,DNRM_FLG(a6) ;if both ops denorm, beq fix_stk ;restore to fpu * * One of the ops is denormalized. Test for wrap condition * and complete the instruction. * cmp.b #$0f,DNRM_FLG(a6) ;check for dest denorm bne.b sub_srcd sub_destd: bsr.l ckinf_ns bne fix_stk bfextu ETEMP_EX(a6){1:15},d0 ;get src exp (always pos) bfexts FPTEMP_EX(a6){1:15},d1 ;get dest exp (always neg) sub.l d1,d0 ;subtract src from dest cmp.l #$8000,d0 blt fix_stk ;if less, not wrap case bra sub_wrap sub_srcd: bsr.l ckinf_nd bne fix_stk bfextu FPTEMP_EX(a6){1:15},d0 ;get dest exp (always pos) bfexts ETEMP_EX(a6){1:15},d1 ;get src exp (always neg) sub.l d1,d0 ;subtract dest from src cmp.l #$8000,d0 blt fix_stk ;if less, not wrap case * * Check the signs of the operands. If they are alike, the fpu * can be used to subtract from the norm 1.0 with the sign of the * denorm and it will correctly generate the result in extended * precision. We can then call round with no sticky and the result * will be correct for the user's rounding mode and precision. If * the signs are unlike, we call round with the sticky bit set * and the result will be correctfor the user's rounding mode and * precision. * sub_wrap: move.w ETEMP_EX(a6),d0 move.w FPTEMP_EX(a6),d1 eor.w d1,d0 andi.w #$8000,d0 bne sub_diff * * The signs are alike. * cmp.b #$0f,DNRM_FLG(a6) ;is dest the denorm? bne.b sub_u_srcd move.w FPTEMP_EX(a6),d0 andi.w #$8000,d0 or.w #$3fff,d0 ;force the exponent to +/- 1 move.w d0,FPTEMP_EX(a6) ;in the denorm move.l USER_FPCR(a6),d0 andi.l #$30,d0 fmove.l d0,fpcr ;set up users rmode and X fmove.x FPTEMP(a6),fp0 fsub.x ETEMP(a6),fp0 fmove.l fpsr,d1 or.l d1,USER_FPSR(a6) ;capture cc's and inex from fadd lea.l WBTEMP(a6),a0 ;point a0 to wbtemp in frame fmove.x fp0,WBTEMP(a6) ;write result to memory lsr.l #4,d0 ;put rmode in lower 2 bits move.l USER_FPCR(a6),d1 andi.l #$c0,d1 lsr.l #6,d1 ;put precision in upper word swap d1 or.l d0,d1 ;set up for round call clr.l d0 ;force sticky to zero bclr.b #sign_bit,WBTEMP_EX(a6) sne WBTEMP_SGN(a6) bsr.l round ;round result to users rmode & prec bfclr WBTEMP_SGN(a6){0:8} ;convert back to IEEE ext format beq frcfpnr bset.b #sign_bit,WBTEMP_EX(a6) bra frcfpnr sub_u_srcd: move.w ETEMP_EX(a6),d0 andi.w #$8000,d0 or.w #$3fff,d0 ;force the exponent to +/- 1 move.w d0,ETEMP_EX(a6) ;in the denorm move.l USER_FPCR(a6),d0 andi.l #$30,d0 fmove.l d0,fpcr ;set up users rmode and X fmove.x FPTEMP(a6),fp0 fsub.x ETEMP(a6),fp0 fmove.l fpsr,d1 or.l d1,USER_FPSR(a6) ;capture cc's and inex from fadd lea.l WBTEMP(a6),a0 ;point a0 to wbtemp in frame fmove.x fp0,WBTEMP(a6) ;write result to memory lsr.l #4,d0 ;put rmode in lower 2 bits move.l USER_FPCR(a6),d1 andi.l #$c0,d1 lsr.l #6,d1 ;put precision in upper word swap d1 or.l d0,d1 ;set up for round call clr.l d0 ;force sticky to zero bclr.b #sign_bit,WBTEMP_EX(a6) sne WBTEMP_SGN(a6) bsr.l round ;round result to users rmode & prec bfclr WBTEMP_SGN(a6){0:8} ;convert back to IEEE ext format beq frcfpnr bset.b #sign_bit,WBTEMP_EX(a6) bra frcfpnr * * Signs are unlike: * sub_diff: cmp.b #$0f,DNRM_FLG(a6) ;is dest the denorm? bne.b sub_s_srcd sub_s_destd: lea.l ETEMP(a6),a0 move.l USER_FPCR(a6),d0 andi.l #$30,d0 lsr.l #4,d0 ;put rmode in lower 2 bits move.l USER_FPCR(a6),d1 andi.l #$c0,d1 lsr.l #6,d1 ;put precision in upper word swap d1 or.l d0,d1 ;set up for round call move.l #$20000000,d0 ;set sticky for round * * Since the dest is the denorm, the sign is the opposite of the * norm sign. * eori.w #$8000,ETEMP_EX(a6) ;flip sign on result tst.w ETEMP_EX(a6) bgt.b sub_s_dwr or.l #neg_mask,USER_FPSR(a6) sub_s_dwr: bclr.b #sign_bit,ETEMP_EX(a6) sne ETEMP_SGN(a6) bsr.l round ;round result to users rmode & prec bfclr ETEMP_SGN(a6){0:8} ;convert back to IEEE ext format beq.b sub_s_dclr bset.b #sign_bit,ETEMP_EX(a6) sub_s_dclr: lea.l WBTEMP(a6),a0 move.l ETEMP(a6),(a0) ;write result to wbtemp move.l ETEMP_HI(a6),4(a0) move.l ETEMP_LO(a6),8(a0) bra sub_ckovf sub_s_srcd: lea.l FPTEMP(a6),a0 move.l USER_FPCR(a6),d0 andi.l #$30,d0 lsr.l #4,d0 ;put rmode in lower 2 bits move.l USER_FPCR(a6),d1 andi.l #$c0,d1 lsr.l #6,d1 ;put precision in upper word swap d1 or.l d0,d1 ;set up for round call move.l #$20000000,d0 ;set sticky for round bclr.b #sign_bit,FPTEMP_EX(a6) sne FPTEMP_SGN(a6) bsr.l round ;round result to users rmode & prec bfclr FPTEMP_SGN(a6){0:8} ;convert back to IEEE ext format beq.b sub_s_sclr bset.b #sign_bit,FPTEMP_EX(a6) sub_s_sclr: lea.l WBTEMP(a6),a0 move.l FPTEMP(a6),(a0) ;write result to wbtemp move.l FPTEMP_HI(a6),4(a0) move.l FPTEMP_LO(a6),8(a0) tst.w FPTEMP_EX(a6) bgt sub_ckovf or.l #neg_mask,USER_FPSR(a6) sub_ckovf: move.w WBTEMP_EX(a6),d0 andi.w #$7fff,d0 cmpi.w #$7fff,d0 bne frcfpnr * * The result has overflowed to $7fff exponent. Set I, ovfl, * and aovfl, and clr the mantissa (incorrectly set by the * round routine.) * or.l #inf_mask+ovfl_inx_mask,USER_FPSR(a6) clr.l 4(a0) bra frcfpnr * * Inst is fcmp. * wrap_cmp: cmp.b #$ff,DNRM_FLG(a6) ;if both ops denorm, beq fix_stk ;restore to fpu * * One of the ops is denormalized. Test for wrap condition * and complete the instruction. * cmp.b #$0f,DNRM_FLG(a6) ;check for dest denorm bne.b cmp_srcd cmp_destd: bsr.l ckinf_ns bne fix_stk bfextu ETEMP_EX(a6){1:15},d0 ;get src exp (always pos) bfexts FPTEMP_EX(a6){1:15},d1 ;get dest exp (always neg) sub.l d1,d0 ;subtract dest from src cmp.l #$8000,d0 blt fix_stk ;if less, not wrap case tst.w ETEMP_EX(a6) ;set N to ~sign_of(src) bge cmp_setn rts cmp_srcd: bsr.l ckinf_nd bne fix_stk bfextu FPTEMP_EX(a6){1:15},d0 ;get dest exp (always pos) bfexts ETEMP_EX(a6){1:15},d1 ;get src exp (always neg) sub.l d1,d0 ;subtract src from dest cmp.l #$8000,d0 blt fix_stk ;if less, not wrap case tst.w FPTEMP_EX(a6) ;set N to sign_of(dest) blt cmp_setn rts cmp_setn: or.l #neg_mask,USER_FPSR(a6) rts * * Inst is fmul. * wrap_mul: cmp.b #$ff,DNRM_FLG(a6) ;if both ops denorm, beq force_unf ;force an underflow (really!) * * One of the ops is denormalized. Test for wrap condition * and complete the instruction. * cmp.b #$0f,DNRM_FLG(a6) ;check for dest denorm bne.b mul_srcd mul_destd: bsr.l ckinf_ns bne fix_stk bfextu ETEMP_EX(a6){1:15},d0 ;get src exp (always pos) bfexts FPTEMP_EX(a6){1:15},d1 ;get dest exp (always neg) add.l d1,d0 ;subtract dest from src bgt fix_stk bra force_unf mul_srcd: bsr.l ckinf_nd bne fix_stk bfextu FPTEMP_EX(a6){1:15},d0 ;get dest exp (always pos) bfexts ETEMP_EX(a6){1:15},d1 ;get src exp (always neg) add.l d1,d0 ;subtract src from dest bgt fix_stk * * This code handles the case of the instruction resulting in * an underflow condition. * force_unf: bclr.b #E1,E_BYTE(a6) or.l #unfinx_mask,USER_FPSR(a6) clr.w NMNEXC(a6) clr.b WBTEMP_SGN(a6) move.w ETEMP_EX(a6),d0 ;find the sign of the result move.w FPTEMP_EX(a6),d1 eor.w d1,d0 andi.w #$8000,d0 beq.b frcunfcont st.b WBTEMP_SGN(a6) frcunfcont: lea WBTEMP(a6),a0 ;point a0 to memory location move.w CMDREG1B(a6),d0 btst.l #6,d0 ;test for forced precision beq.b frcunf_fpcr btst.l #2,d0 ;check for double bne.b frcunf_dbl move.l #$1,d0 ;inst is forced single bra.b frcunf_rnd frcunf_dbl: move.l #$2,d0 ;inst is forced double bra.b frcunf_rnd frcunf_fpcr: bfextu FPCR_MODE(a6){0:2},d0 ;inst not forced - use fpcr prec frcunf_rnd: bsr.l unf_sub ;get correct result based on * ;round precision/mode. This * ;sets FPSR_CC correctly bfclr WBTEMP_SGN(a6){0:8} ;convert back to IEEE ext format beq.b frcfpn bset.b #sign_bit,WBTEMP_EX(a6) bra frcfpn * * Write the result to the user's fpn. All results must be HUGE to be * written; otherwise the results would have overflowed or underflowed. * If the rounding precision is single or double, the ovf_res routine * is needed to correctly supply the max value. * frcfpnr: move.w CMDREG1B(a6),d0 btst.l #6,d0 ;test for forced precision beq.b frcfpn_fpcr btst.l #2,d0 ;check for double bne.b frcfpn_dbl move.l #$1,d0 ;inst is forced single bra.b frcfpn_rnd frcfpn_dbl: move.l #$2,d0 ;inst is forced double bra.b frcfpn_rnd frcfpn_fpcr: bfextu FPCR_MODE(a6){0:2},d0 ;inst not forced - use fpcr prec tst.b d0 beq.b frcfpn ;if extended, write what you got frcfpn_rnd: bclr.b #sign_bit,WBTEMP_EX(a6) sne WBTEMP_SGN(a6) bsr.l ovf_res ;get correct result based on * ;round precision/mode. This * ;sets FPSR_CC correctly bfclr WBTEMP_SGN(a6){0:8} ;convert back to IEEE ext format beq.b frcfpn_clr bset.b #sign_bit,WBTEMP_EX(a6) frcfpn_clr: or.l #ovfinx_mask,USER_FPSR(a6) * * Perform the write. * frcfpn: bfextu CMDREG1B(a6){6:3},d0 ;extract fp destination register cmpi.b #3,d0 ble.b frc0123 ;check if dest is fp0-fp3 move.l #7,d1 sub.l d0,d1 clr.l d0 bset.l d1,d0 fmovem.x WBTEMP(a6),d0 rts frc0123: tst.b d0 beq.b frc0_dst cmpi.b #1,d0 beq.b frc1_dst cmpi.b #2,d0 beq.b frc2_dst frc3_dst: move.l WBTEMP_EX(a6),USER_FP3(a6) move.l WBTEMP_HI(a6),USER_FP3+4(a6) move.l WBTEMP_LO(a6),USER_FP3+8(a6) rts frc2_dst: move.l WBTEMP_EX(a6),USER_FP2(a6) move.l WBTEMP_HI(a6),USER_FP2+4(a6) move.l WBTEMP_LO(a6),USER_FP2+8(a6) rts frc1_dst: move.l WBTEMP_EX(a6),USER_FP1(a6) move.l WBTEMP_HI(a6),USER_FP1+4(a6) move.l WBTEMP_LO(a6),USER_FP1+8(a6) rts frc0_dst: move.l WBTEMP_EX(a6),USER_FP0(a6) move.l WBTEMP_HI(a6),USER_FP0+4(a6) move.l WBTEMP_LO(a6),USER_FP0+8(a6) rts * * Write etemp to fpn. * A check is made on enabled and signalled snan exceptions, * and the destination is not overwritten if this condition exists. * This code is designed to make fmoveins of unsupported data types * faster. * wr_etemp: btst.b #snan_bit,FPSR_EXCEPT(a6) ;if snan is set, and beq.b fmoveinc ;enabled, force restore btst.b #snan_bit,FPCR_ENABLE(a6) ;and don't overwrite beq.b fmoveinc ;the dest move.l ETEMP_EX(a6),FPTEMP_EX(a6) ;set up fptemp sign for * ;snan handler tst.b ETEMP(a6) ;check for negative blt.b snan_neg rts snan_neg: or.l #neg_bit,USER_FPSR(a6) ;snan is negative; set N rts fmoveinc: clr.w NMNEXC(a6) bclr.b #E1,E_BYTE(a6) move.b STAG(a6),d0 ;check if stag is inf andi.b #$e0,d0 cmpi.b #$40,d0 bne.b fminc_cnan or.l #inf_mask,USER_FPSR(a6) ;if inf, nothing yet has set I tst.w LOCAL_EX(a0) ;check sign bge.b fminc_con or.l #neg_mask,USER_FPSR(a6) bra fminc_con fminc_cnan: cmpi.b #$60,d0 ;check if stag is NaN bne.b fminc_czero or.l #nan_mask,USER_FPSR(a6) ;if nan, nothing yet has set NaN move.l ETEMP_EX(a6),FPTEMP_EX(a6) ;set up fptemp sign for * ;snan handler tst.w LOCAL_EX(a0) ;check sign bge.b fminc_con or.l #neg_mask,USER_FPSR(a6) bra fminc_con fminc_czero: cmpi.b #$20,d0 ;check if zero bne.b fminc_con or.l #z_mask,USER_FPSR(a6) ;if zero, set Z tst.w LOCAL_EX(a0) ;check sign bge.b fminc_con or.l #neg_mask,USER_FPSR(a6) fminc_con: bfextu CMDREG1B(a6){6:3},d0 ;extract fp destination register cmpi.b #3,d0 ble.b fp0123 ;check if dest is fp0-fp3 move.l #7,d1 sub.l d0,d1 clr.l d0 bset.l d1,d0 fmovem.x ETEMP(a6),d0 rts fp0123: tst.b d0 beq.b fp0_dst cmpi.b #1,d0 beq.b fp1_dst cmpi.b #2,d0 beq.b fp2_dst fp3_dst: move.l ETEMP_EX(a6),USER_FP3(a6) move.l ETEMP_HI(a6),USER_FP3+4(a6) move.l ETEMP_LO(a6),USER_FP3+8(a6) rts fp2_dst: move.l ETEMP_EX(a6),USER_FP2(a6) move.l ETEMP_HI(a6),USER_FP2+4(a6) move.l ETEMP_LO(a6),USER_FP2+8(a6) rts fp1_dst: move.l ETEMP_EX(a6),USER_FP1(a6) move.l ETEMP_HI(a6),USER_FP1+4(a6) move.l ETEMP_LO(a6),USER_FP1+8(a6) rts fp0_dst: move.l ETEMP_EX(a6),USER_FP0(a6) move.l ETEMP_HI(a6),USER_FP0+4(a6) move.l ETEMP_LO(a6),USER_FP0+8(a6) rts opclass3: st.b CU_ONLY(a6) move.w CMDREG1B(a6),d0 ;check if packed moveout andi.w #$0c00,d0 ;isolate last 2 bits of size field cmpi.w #$0c00,d0 ;if size is 011 or 111, it is packed beq.w pack_out ;else it is norm or denorm bra.w mv_out * * MOVE OUT * mv_tbl: dc.l li dc.l sgp dc.l xp dc.l mvout_end ;should never be taken dc.l wi dc.l dp dc.l bi dc.l mvout_end ;should never be taken mv_out: bfextu CMDREG1B(a6){3:3},d1 ;put source specifier in d1 lea.l mv_tbl,a0 move.l (a0,d1*4),a0 jmp (a0) * * This exit is for move-out to memory. The aunfl bit is * set if the result is inex and unfl is signalled. * mvout_end: btst.b #inex2_bit,FPSR_EXCEPT(a6) beq.b no_aufl btst.b #unfl_bit,FPSR_EXCEPT(a6) beq.b no_aufl bset.b #aunfl_bit,FPSR_AEXCEPT(a6) no_aufl: clr.w NMNEXC(a6) bclr.b #E1,E_BYTE(a6) fmove.l #0,FPSR ;clear any cc bits from res_func * * Return ETEMP to extended format from internal extended format so * that gen_except will have a correctly signed value for ovfl/unfl * handlers. * bfclr ETEMP_SGN(a6){0:8} beq.b mvout_con bset.b #sign_bit,ETEMP_EX(a6) mvout_con: rts * * This exit is for move-out to int register. The aunfl bit is * not set in any case for this move. * mvouti_end: clr.w NMNEXC(a6) bclr.b #E1,E_BYTE(a6) fmove.l #0,FPSR ;clear any cc bits from res_func * * Return ETEMP to extended format from internal extended format so * that gen_except will have a correctly signed value for ovfl/unfl * handlers. * bfclr ETEMP_SGN(a6){0:8} beq.b mvouti_con bset.b #sign_bit,ETEMP_EX(a6) mvouti_con: rts * * li is used to handle a long integer source specifier * li: moveq.l #4,d0 ;set byte count btst.b #7,STAG(a6) ;check for extended denorm bne.w int_dnrm ;if so, branch fmovem.x ETEMP(a6),fp0 fcmp.d #:41dfffffffc00000,fp0 * 41dfffffffc00000 in dbl prec = 401d0000fffffffe00000000 in ext prec fbge.w lo_plrg fcmp.d #:c1e0000000000000,fp0 * c1e0000000000000 in dbl prec = c01e00008000000000000000 in ext prec fble.w lo_nlrg * * at this point, the answer is between the largest pos and neg values * move.l USER_FPCR(a6),d1 ;use user's rounding mode andi.l #$30,d1 fmove.l d1,fpcr fmove.l fp0,L_SCR1(a6) ;let the 040 perform conversion fmove.l fpsr,d1 or.l d1,USER_FPSR(a6) ;capture inex2/ainex if set bra.w int_wrt lo_plrg: move.l #$7fffffff,L_SCR1(a6) ;answer is largest positive int fbeq.w int_wrt ;exact answer fcmp.d #:41dfffffffe00000,fp0 * 41dfffffffe00000 in dbl prec = 401d0000ffffffff00000000 in ext prec fbge.w int_operr ;set operr bra.w int_inx ;set inexact lo_nlrg: move.l #$80000000,L_SCR1(a6) fbeq.w int_wrt ;exact answer fcmp.d #:c1e0000000100000,fp0 * c1e0000000100000 in dbl prec = c01e00008000000080000000 in ext prec fblt.w int_operr ;set operr bra.w int_inx ;set inexact * * wi is used to handle a word integer source specifier * wi: moveq.l #2,d0 ;set byte count btst.b #7,STAG(a6) ;check for extended denorm bne.w int_dnrm ;branch if so fmovem.x ETEMP(a6),fp0 fcmp.s #:46fffe00,fp0 * 46fffe00 in sgl prec = 400d0000fffe000000000000 in ext prec fbge.w wo_plrg fcmp.s #:c7000000,fp0 * c7000000 in sgl prec = c00e00008000000000000000 in ext prec fble.w wo_nlrg * * at this point, the answer is between the largest pos and neg values * move.l USER_FPCR(a6),d1 ;use user's rounding mode andi.l #$30,d1 fmove.l d1,fpcr fmove.w fp0,L_SCR1(a6) ;let the 040 perform conversion fmove.l fpsr,d1 or.l d1,USER_FPSR(a6) ;capture inex2/ainex if set bra.w int_wrt wo_plrg: move.w #$7fff,L_SCR1(a6) ;answer is largest positive int fbeq.w int_wrt ;exact answer fcmp.s #:46ffff00,fp0 * 46ffff00 in sgl prec = 400d0000ffff000000000000 in ext prec fbge.w int_operr ;set operr bra.w int_inx ;set inexact wo_nlrg: move.w #$8000,L_SCR1(a6) fbeq.w int_wrt ;exact answer fcmp.s #:c7000080,fp0 * c7000080 in sgl prec = c00e00008000800000000000 in ext prec fblt.w int_operr ;set operr bra.w int_inx ;set inexact * * bi is used to handle a byte integer source specifier * bi: moveq.l #1,d0 ;set byte count btst.b #7,STAG(a6) ;check for extended denorm bne.w int_dnrm ;branch if so fmovem.x ETEMP(a6),fp0 fcmp.s #:42fe0000,fp0 * 42fe0000 in sgl prec = 40050000fe00000000000000 in ext prec fbge.w by_plrg fcmp.s #:c3000000,fp0 * c3000000 in sgl prec = c00600008000000000000000 in ext prec fble.w by_nlrg * * at this point, the answer is between the largest pos and neg values * move.l USER_FPCR(a6),d1 ;use user's rounding mode andi.l #$30,d1 fmove.l d1,fpcr fmove.b fp0,L_SCR1(a6) ;let the 040 perform conversion fmove.l fpsr,d1 or.l d1,USER_FPSR(a6) ;capture inex2/ainex if set bra.w int_wrt by_plrg: move.b #$7f,L_SCR1(a6) ;answer is largest positive int fbeq.w int_wrt ;exact answer fcmp.s #:42ff0000,fp0 * 42ff0000 in sgl prec = 40050000ff00000000000000 in ext prec fbge.w int_operr ;set operr bra.w int_inx ;set inexact by_nlrg: move.b #$80,L_SCR1(a6) fbeq.w int_wrt ;exact answer fcmp.s #:c3008000,fp0 * c3008000 in sgl prec = c00600008080000000000000 in ext prec fblt.w int_operr ;set operr bra.w int_inx ;set inexact * * Common integer routines * * int_drnrm---account for possible nonzero result for round up with positive * operand and round down for negative answer. In the first case (result = 1) * byte-width (store in d0) of result must be honored. In the second case, * -1 in L_SCR1(a6) will cover all contingencies (FMOVE.B/W/L out). int_dnrm: clr.l L_SCR1(a6) ; initialize result to 0 bfextu FPCR_MODE(a6){2:2},d1 ; d1 is the rounding mode cmp.b #2,d1 bmi.b int_inx ; if RN or RZ, done bne.b int_rp ; if RP, continue below tst.w ETEMP(a6) ; RM: store -1 in L_SCR1 if src is negative bpl.b int_inx ; otherwise result is 0 move.l #-1,L_SCR1(a6) bra.b int_inx int_rp: tst.w ETEMP(a6) ; RP: store +1 of proper width in L_SCR1 if * ; source is greater than 0 bmi.b int_inx ; otherwise, result is 0 lea L_SCR1(a6),a1 ; a1 is address of L_SCR1 adda.l d0,a1 ; offset by destination width -1 suba.l #1,a1 bset.b #0,(a1) ; set low bit at a1 address int_inx: ori.l #inx2a_mask,USER_FPSR(a6) bra.b int_wrt int_operr: fmovem.x fp0,FPTEMP(a6) ;FPTEMP must contain the extended * ;precision source that needs to be * ;converted to integer this is required * ;if the operr exception is enabled. * ;set operr/aiop (no inex2 on int ovfl) ori.l #opaop_mask,USER_FPSR(a6) * ;fall through to perform int_wrt int_wrt: move.l EXC_EA(a6),a1 ;load destination address tst.l a1 ;check to see if it is a dest register beq.b wrt_dn ;write data register lea L_SCR1(a6),a0 ;point to supervisor source address bsr.l mem_write bra.w mvouti_end wrt_dn: move.l d0,-(sp) ;d0 currently contains the size to write bsr.l get_fline ;get_fline returns Dn in d0 andi.w #$7,d0 ;isolate register move.l (sp)+,d1 ;get size cmpi.l #4,d1 ;most frequent case beq.b sz_long cmpi.l #2,d1 bne.b sz_con or.l #8,d0 ;add 'word' size to register# bra.b sz_con sz_long: or.l #$10,d0 ;add 'long' size to register# sz_con: move.l d0,d1 ;reg_dest expects size:reg in d1 bsr.l reg_dest ;load proper data register bra.w mvouti_end xp: lea ETEMP(a6),a0 bclr.b #sign_bit,LOCAL_EX(a0) sne LOCAL_SGN(a0) btst.b #7,STAG(a6) ;check for extended denorm bne.w xdnrm clr.l d0 bra.b do_fp ;do normal case sgp: lea ETEMP(a6),a0 bclr.b #sign_bit,LOCAL_EX(a0) sne LOCAL_SGN(a0) btst.b #7,STAG(a6) ;check for extended denorm bne.w sp_catas ;branch if so move.w LOCAL_EX(a0),d0 lea sp_bnds,a1 cmp.w (a1),d0 blt.w sp_under cmp.w 2(a1),d0 bgt.w sp_over move.l #1,d0 ;set destination format to single bra.b do_fp ;do normal case dp: lea ETEMP(a6),a0 bclr.b #sign_bit,LOCAL_EX(a0) sne LOCAL_SGN(a0) btst.b #7,STAG(a6) ;check for extended denorm bne.w dp_catas ;branch if so move.w LOCAL_EX(a0),d0 lea dp_bnds,a1 cmp.w (a1),d0 blt.w dp_under cmp.w 2(a1),d0 bgt.w dp_over move.l #2,d0 ;set destination format to double * ;fall through to do_fp * do_fp: bfextu FPCR_MODE(a6){2:2},d1 ;rnd mode in d1 swap d0 ;rnd prec in upper word add.l d0,d1 ;d1 has PREC/MODE info clr.l d0 ;clear g,r,s bsr.l round ;round move.l a0,a1 move.l EXC_EA(a6),a0 bfextu CMDREG1B(a6){3:3},d1 ;extract destination format * ;at this point only the dest * ;formats sgl, dbl, ext are * ;possible cmp.b #2,d1 bgt.b ddbl ;double=5, extended=2, single=1 bne.b dsgl * ;fall through to dext dext: bsr.l dest_ext bra.w mvout_end dsgl: bsr.l dest_sgl bra.w mvout_end ddbl: bsr.l dest_dbl bra.w mvout_end * * Handle possible denorm or catastrophic underflow cases here * xdnrm: bsr.w set_xop ;initialize WBTEMP bset.b #wbtemp15_bit,WB_BYTE(a6) ;set wbtemp15 move.l a0,a1 move.l EXC_EA(a6),a0 ;a0 has the destination pointer bsr.l dest_ext ;store to memory bset.b #unfl_bit,FPSR_EXCEPT(a6) bra.w mvout_end sp_under: bset.b #etemp15_bit,STAG(a6) cmp.w 4(a1),d0 blt.b sp_catas ;catastrophic underflow case move.l #1,d0 ;load in round precision move.l #sgl_thresh,d1 ;load in single denorm threshold bsr.l dpspdnrm ;expects d1 to have the proper * ;denorm threshold bsr.l dest_sgl ;stores value to destination bset.b #unfl_bit,FPSR_EXCEPT(a6) bra.w mvout_end ;exit dp_under: bset.b #etemp15_bit,STAG(a6) cmp.w 4(a1),d0 blt.b dp_catas ;catastrophic underflow case move.l #dbl_thresh,d1 ;load in double precision threshold move.l #2,d0 bsr.l dpspdnrm ;expects d1 to have proper * ;denorm threshold * ;expects d0 to have round precision bsr.l dest_dbl ;store value to destination bset.b #unfl_bit,FPSR_EXCEPT(a6) bra.w mvout_end ;exit * * Handle catastrophic underflow cases here * sp_catas: * Temp fix for z bit set in unf_sub move.l USER_FPSR(a6),-(a7) move.l #1,d0 ;set round precision to sgl bsr.l unf_sub ;a0 points to result move.l (a7)+,USER_FPSR(a6) move.l #1,d0 sub.w d0,LOCAL_EX(a0) ;account for difference between * ;denorm/norm bias move.l a0,a1 ;a1 has the operand input move.l EXC_EA(a6),a0 ;a0 has the destination pointer bsr.l dest_sgl ;store the result ori.l #unfinx_mask,USER_FPSR(a6) bra.w mvout_end dp_catas: * Temp fix for z bit set in unf_sub move.l USER_FPSR(a6),-(a7) move.l #2,d0 ;set round precision to dbl bsr.l unf_sub ;a0 points to result move.l (a7)+,USER_FPSR(a6) move.l #1,d0 sub.w d0,LOCAL_EX(a0) ;account for difference between * ;denorm/norm bias move.l a0,a1 ;a1 has the operand input move.l EXC_EA(a6),a0 ;a0 has the destination pointer bsr.l dest_dbl ;store the result ori.l #unfinx_mask,USER_FPSR(a6) bra.w mvout_end * * Handle catastrophic overflow cases here * sp_over: * Temp fix for z bit set in unf_sub move.l USER_FPSR(a6),-(a7) move.l #1,d0 lea.l FP_SCR1(a6),a0 ;use FP_SCR1 for creating result move.l ETEMP_EX(a6),(a0) move.l ETEMP_HI(a6),4(a0) move.l ETEMP_LO(a6),8(a0) bsr.l ovf_res move.l (a7)+,USER_FPSR(a6) move.l a0,a1 move.l EXC_EA(a6),a0 bsr.l dest_sgl or.l #ovfinx_mask,USER_FPSR(a6) bra.w mvout_end dp_over: * Temp fix for z bit set in ovf_res move.l USER_FPSR(a6),-(a7) move.l #2,d0 lea.l FP_SCR1(a6),a0 ;use FP_SCR1 for creating result move.l ETEMP_EX(a6),(a0) move.l ETEMP_HI(a6),4(a0) move.l ETEMP_LO(a6),8(a0) bsr.l ovf_res move.l (a7)+,USER_FPSR(a6) move.l a0,a1 move.l EXC_EA(a6),a0 bsr.l dest_dbl or.l #ovfinx_mask,USER_FPSR(a6) bra.w mvout_end * * DPSPDNRM * * This subroutine takes an extended normalized number and denormalizes * it to the given round precision. This subroutine also decrements * the input operand's exponent by 1 to account for the fact that * dest_sgl or dest_dbl expects a normalized number's bias. * * Input: a0 points to a normalized number in internal extended format * d0 is the round precision (=1 for sgl; =2 for dbl) * d1 is the the single precision or double precision * denorm threshold * * Output: (In the format for dest_sgl or dest_dbl) * a0 points to the destination * a1 points to the operand * * Exceptions: Reports inexact 2 exception by setting USER_FPSR bits * dpspdnrm: move.l d0,-(a7) ;save round precision clr.l d0 ;clear initial g,r,s bsr.l dnrm_lp ;careful with d0, it's needed by round bfextu FPCR_MODE(a6){2:2},d1 ;get rounding mode swap d1 move.w 2(a7),d1 ;set rounding precision swap d1 ;at this point d1 has PREC/MODE info bsr.l round ;round result, sets the inex bit in * ;USER_FPSR if needed move.w #1,d0 sub.w d0,LOCAL_EX(a0) ;account for difference in denorm * ;vs norm bias move.l a0,a1 ;a1 has the operand input move.l EXC_EA(a6),a0 ;a0 has the destination pointer addq.l #4,a7 ;pop stack rts * * SET_XOP initialized WBTEMP with the value pointed to by a0 * input: a0 points to input operand in the internal extended format * set_xop: move.l LOCAL_EX(a0),WBTEMP_EX(a6) move.l LOCAL_HI(a0),WBTEMP_HI(a6) move.l LOCAL_LO(a0),WBTEMP_LO(a6) bfclr WBTEMP_SGN(a6){0:8} beq.b sxop bset.b #sign_bit,WBTEMP_EX(a6) sxop: bfclr STAG(a6){5:4} ;clear wbtm66,wbtm1,wbtm0,sbit rts * * P_MOVE * p_movet: dc.l p_move dc.l p_movez dc.l p_movei dc.l p_moven dc.l p_move p_regd: dc.l p_dyd0 dc.l p_dyd1 dc.l p_dyd2 dc.l p_dyd3 dc.l p_dyd4 dc.l p_dyd5 dc.l p_dyd6 dc.l p_dyd7 pack_out: lea.l p_movet,a0 ;load jmp table address move.w STAG(a6),d0 ;get source tag bfextu d0{16:3},d0 ;isolate source bits move.l (a0,d0.w*4),a0 ;load a0 with routine label for tag jmp (a0) ;go to the routine p_write: move.l #$0c,d0 ;get byte count move.l EXC_EA(a6),a1 ;get the destination address bsr mem_write ;write the user's destination clr.b CU_SAVEPC(a6) ;set the cu save pc to all 0's * * Also note that the dtag must be set to norm here - this is because * the 040 uses the dtag to execute the correct microcode. * bfclr DTAG(a6){0:3} ;set dtag to norm rts * Notes on handling of special case (zero, inf, and nan) inputs: * 1. Operr is not signalled if the k-factor is greater than 18. * 2. Per the manual, status bits are not set. * p_move: move.w CMDREG1B(a6),d0 btst.l #kfact_bit,d0 ;test for dynamic k-factor beq.b statick ;if clear, k-factor is static dynamick: bfextu d0{25:3},d0 ;isolate register for dynamic k-factor lea p_regd,a0 move.l (a0,d0*4),a0 jmp (a0) statick: andi.w #$007f,d0 ;get k-factor bfexts d0{25:7},d0 ;sign extend d0 for bindec lea.l ETEMP(a6),a0 ;a0 will point to the packed decimal bsr.l bindec ;perform the convert; data at a6 lea.l FP_SCR1(a6),a0 ;load a0 with result address bra.l p_write p_movez: lea.l ETEMP(a6),a0 ;a0 will point to the packed decimal clr.w 2(a0) ;clear lower word of exp clr.l 4(a0) ;load second lword of ZERO clr.l 8(a0) ;load third lword of ZERO bra.w p_write ;go write results p_movei: fmove.l #0,FPSR ;clear aiop lea.l ETEMP(a6),a0 ;a0 will point to the packed decimal clr.w 2(a0) ;clear lower word of exp bra.w p_write ;go write the result p_moven: lea.l ETEMP(a6),a0 ;a0 will point to the packed decimal clr.w 2(a0) ;clear lower word of exp bra.w p_write ;go write the result * * Routines to read the dynamic k-factor from Dn. * p_dyd0: move.l USER_D0(a6),d0 bra.b statick p_dyd1: move.l USER_D1(a6),d0 bra.b statick p_dyd2: move.l d2,d0 bra.b statick p_dyd3: move.l d3,d0 bra.b statick p_dyd4: move.l d4,d0 bra.b statick p_dyd5: move.l d5,d0 bra.b statick p_dyd6: move.l d6,d0 bra.w statick p_dyd7: move.l d7,d0 bra.w statick end