#include "fpsp-namespace.h" // // // 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. // // // Copyright (C) Motorola, Inc. 1990 // All Rights Reserved // // THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF MOTOROLA // The copyright notice above does not evidence any // actual or intended publication of such source code. RES_FUNC: //idnt 2,1 | Motorola 040 Floating Point Software Package |section 8 #include "fpsp.defs" sp_bnds: .short 0x3f81,0x407e .short 0x3f6a,0x0000 dp_bnds: .short 0x3c01,0x43fe .short 0x3bcd,0x0000 |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 .global res_func .global p_move res_func: clrb DNRM_FLG(%a6) clrb RES_FLG(%a6) clrb CU_ONLY(%a6) tstb DY_MO_FLG(%a6) beqs monadic dyadic: btstb #7,DTAG(%a6) //if dop = norm=000, zero=001, // ;inf=010 or nan=011 beqs monadic //then branch // ;else denorm // HANDLE DESTINATION DENORM HERE // ;set dtag to norm // ;write the tag & fpte15 to the fstack leal FPTEMP(%a6),%a0 bclrb #sign_bit,LOCAL_EX(%a0) sne LOCAL_SGN(%a0) bsr nrm_set //normalize number (exp will go negative) bclrb #sign_bit,LOCAL_EX(%a0) //get rid of false sign bfclr LOCAL_SGN(%a0){#0:#8} //change back to IEEE ext format beqs dpos bsetb #sign_bit,LOCAL_EX(%a0) dpos: bfclr DTAG(%a6){#0:#4} //set tag to normalized, FPTE15 = 0 bsetb #4,DTAG(%a6) //set FPTE15 orb #0x0f,DNRM_FLG(%a6) monadic: leal ETEMP(%a6),%a0 btstb #direction_bit,CMDREG1B(%a6) //check direction bne opclass3 //it is a mv out // // At this point, only opclass 0 and 2 possible // btstb #7,STAG(%a6) //if sop = norm=000, zero=001, // ;inf=010 or nan=011 bne mon_dnrm //else denorm tstb DY_MO_FLG(%a6) //all cases of dyadic instructions would bne 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) // movew CMDREG1B(%a6),%d0 //get command register andil #0x7f,%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) // btstl #0,%d0 bne 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 movew CMDREG1B(%a6),%d0 andib #0x3b,%d0 //isolate bits to select inst tstb %d0 beql cu_nmove //if zero, it is an fmove cmpib #0x18,%d0 beql cu_nabs //if $18, it is fabs cmpib #0x1a,%d0 beql 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: movew LOCAL_EX(%a0),%d0 bclrl #15,%d0 sne LOCAL_SGN(%a0) beqs cu_ntpo orl #neg_mask,USER_FPSR(%a6) //set N cu_ntpo: cmpiw #0x7fff,%d0 //test for inf/nan bnes cu_ntcz tstl LOCAL_HI(%a0) bnes cu_ntn tstl LOCAL_LO(%a0) bnes cu_ntn orl #inf_mask,USER_FPSR(%a6) rts cu_ntn: orl #nan_mask,USER_FPSR(%a6) movel ETEMP_EX(%a6),FPTEMP_EX(%a6) //set up fptemp sign for // ;snan handler rts cu_ntcz: tstl LOCAL_HI(%a0) bnel cu_ntsx tstl LOCAL_LO(%a0) bnel cu_ntsx orl #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: moveb STAG(%a6),%d0 btstl #5,%d0 //test for NaN or zero bne wr_etemp //if either, simply write it bclrb #7,LOCAL_EX(%a0) //do abs bras 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: moveb STAG(%a6),%d0 btstl #5,%d0 //test for NaN or zero bne wr_etemp //if either, simply write it bchgb #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: moveb STAG(%a6),%d0 andib #0xe0,%d0 //isolate stag bits bne wr_etemp //if not norm, simply write it btstb #2,CMDREG1B+1(%a6) //check for rd bne cu_nmrd btstb #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 tstb %d0 //check for extended beq cu_wrexn //if so, just write result cmpib #1,%d0 //check for single beq cu_nmrs //fall through to double // // The move is fdmove or round precision is double. // cu_nmrd: movel #2,%d0 //set up the size for denorm movew LOCAL_EX(%a0),%d1 //compare exponent to double threshold andw #0x7fff,%d1 cmpw #0x3c01,%d1 bls cu_nunfl bfextu FPCR_MODE(%a6){#2:#2},%d1 //get rmode orl #0x00020000,%d1 //or in rprec (double) clrl %d0 //clear g,r,s for round bclrb #sign_bit,LOCAL_EX(%a0) //convert to internal format sne LOCAL_SGN(%a0) bsrl round bfclr LOCAL_SGN(%a0){#0:#8} beqs cu_nmrdc bsetb #sign_bit,LOCAL_EX(%a0) cu_nmrdc: movew LOCAL_EX(%a0),%d1 //check for overflow andw #0x7fff,%d1 cmpw #0x43ff,%d1 bge cu_novfl //take care of overflow case bra cu_wrexn // // The move is fsmove or round precision is single. // cu_nmrs: movel #1,%d0 movew LOCAL_EX(%a0),%d1 andw #0x7fff,%d1 cmpw #0x3f81,%d1 bls cu_nunfl bfextu FPCR_MODE(%a6){#2:#2},%d1 orl #0x00010000,%d1 clrl %d0 bclrb #sign_bit,LOCAL_EX(%a0) sne LOCAL_SGN(%a0) bsrl round bfclr LOCAL_SGN(%a0){#0:#8} beqs cu_nmrsc bsetb #sign_bit,LOCAL_EX(%a0) cu_nmrsc: movew LOCAL_EX(%a0),%d1 andw #0x7FFF,%d1 cmpw #0x407f,%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: bclrb #sign_bit,LOCAL_EX(%a0) sne LOCAL_SGN(%a0) bsr denorm bfclr LOCAL_SGN(%a0){#0:#8} //change back to IEEE ext format beqs cu_nucont bsetb #sign_bit,LOCAL_EX(%a0) cu_nucont: bfextu FPCR_MODE(%a6){#2:#2},%d1 btstb #2,CMDREG1B+1(%a6) //check for rd bne inst_d btstb #6,CMDREG1B+1(%a6) //check for rs bne inst_s swap %d1 moveb FPCR_MODE(%a6),%d1 lsrb #6,%d1 swap %d1 bra inst_sd inst_d: orl #0x00020000,%d1 bra inst_sd inst_s: orl #0x00010000,%d1 inst_sd: bclrb #sign_bit,LOCAL_EX(%a0) sne LOCAL_SGN(%a0) bsrl round bfclr LOCAL_SGN(%a0){#0:#8} beqs cu_nuflp bsetb #sign_bit,LOCAL_EX(%a0) cu_nuflp: btstb #inex2_bit,FPSR_EXCEPT(%a6) beqs cu_nuninx orl #aunfl_mask,USER_FPSR(%a6) //if the round was inex, set AUNFL cu_nuninx: tstl LOCAL_HI(%a0) //test for zero bnes cu_nunzro tstl LOCAL_LO(%a0) bnes cu_nunzro // // The mantissa is zero from the denorm loop. Check sign and rmode // to see if rounding should have occurred which would leave the lsb. // movel USER_FPCR(%a6),%d0 andil #0x30,%d0 //isolate rmode cmpil #0x20,%d0 blts cu_nzro bnes cu_nrp cu_nrm: tstw LOCAL_EX(%a0) //if positive, set lsb bges cu_nzro btstb #7,FPCR_MODE(%a6) //check for double beqs cu_nincs bras cu_nincd cu_nrp: tstw LOCAL_EX(%a0) //if positive, set lsb blts cu_nzro btstb #7,FPCR_MODE(%a6) //check for double beqs cu_nincs cu_nincd: orl #0x800,LOCAL_LO(%a0) //inc for double bra cu_nunzro cu_nincs: orl #0x100,LOCAL_HI(%a0) //inc for single bra cu_nunzro cu_nzro: orl #z_mask,USER_FPSR(%a6) moveb STAG(%a6),%d0 andib #0xe0,%d0 cmpib #0x40,%d0 //check if input was tagged zero beqs cu_numv cu_nunzro: orl #unfl_mask,USER_FPSR(%a6) //set unfl cu_numv: movel (%a0),ETEMP(%a6) movel 4(%a0),ETEMP_HI(%a6) movel 8(%a0),ETEMP_LO(%a6) // // Write the result to memory, setting the fpsr cc bits. NaN and Inf // bypass cu_wrexn. // cu_wrexn: tstw LOCAL_EX(%a0) //test for zero beqs cu_wrzero cmpw #0x8000,LOCAL_EX(%a0) //test for zero bnes cu_wreon cu_wrzero: orl #z_mask,USER_FPSR(%a6) //set Z bit cu_wreon: tstw LOCAL_EX(%a0) bpl wr_etemp orl #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. // tstb DY_MO_FLG(%a6) //all cases of dyadic instructions would bnes 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) // movew CMDREG1B(%a6),%d0 //get command register andil #0x7f,%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) // btstl #0,%d0 bnes 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: bclrb #sign_bit,LOCAL_EX(%a0) sne LOCAL_SGN(%a0) bsr nrm_set //normalize number (exponent will go // ; negative) bclrb #sign_bit,LOCAL_EX(%a0) //get rid of false sign bfclr LOCAL_SGN(%a0){#0:#8} //change back to IEEE ext format beqs spos bsetb #sign_bit,LOCAL_EX(%a0) spos: bfclr STAG(%a6){#0:#4} //set tag to normalized, FPTE15 = 0 bsetb #4,STAG(%a6) //set ETE15 orb #0xf0,DNRM_FLG(%a6) normal: tstb 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: moveb #0xfe,CU_SAVEPC(%a6) bclrb #E1,E_BYTE(%a6) clrw NMNEXC(%a6) st RES_FLG(%a6) //indicate that a restore is needed rts // // cu_dnrm handles all cu-only instructions (fmove, fabs, fneg, and // ftst) completely in software without an frestore to the 040. // cu_dnrm: st CU_ONLY(%a6) movew CMDREG1B(%a6),%d0 andib #0x3b,%d0 //isolate bits to select inst tstb %d0 beql cu_dmove //if zero, it is an fmove cmpib #0x18,%d0 beql cu_dabs //if $18, it is fabs cmpib #0x1a,%d0 beql 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: movew LOCAL_EX(%a0),%d0 bclrl #15,%d0 sne LOCAL_SGN(%a0) beqs cu_dtpo orl #neg_mask,USER_FPSR(%a6) //set N cu_dtpo: cmpiw #0x7fff,%d0 //test for inf/nan bnes cu_dtcz tstl LOCAL_HI(%a0) bnes cu_dtn tstl LOCAL_LO(%a0) bnes cu_dtn orl #inf_mask,USER_FPSR(%a6) rts cu_dtn: orl #nan_mask,USER_FPSR(%a6) movel ETEMP_EX(%a6),FPTEMP_EX(%a6) //set up fptemp sign for // ;snan handler rts cu_dtcz: tstl LOCAL_HI(%a0) bnel cu_dtsx tstl LOCAL_LO(%a0) bnel cu_dtsx orl #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: bclrb #7,LOCAL_EX(%a0) //do abs bras 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: bchgb #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: btstb #2,CMDREG1B+1(%a6) //check for rd bne cu_dmrd btstb #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 tstb %d0 //check for extended beq cu_wrexd //if so, just write result cmpib #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 tstw LOCAL_EX(%a0) //check sign blts cu_dmdn cmpib #3,%d1 //check for rp bne cu_dpd //load double pos zero bra cu_dpdr //load double pos zero w/lsb cu_dmdn: cmpib #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 tstw LOCAL_EX(%a0) //check sign blts cu_dmsn cmpib #3,%d1 //check for rp bne cu_spd //load single pos zero bra cu_spdr //load single pos zero w/lsb cu_dmsn: cmpib #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: orl #unfl_mask,USER_FPSR(%a6) tstw LOCAL_EX(%a0) beq wr_etemp orl #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: movel #0x3c010000,LOCAL_EX(%a0) //force pos double zero clrl LOCAL_HI(%a0) clrl LOCAL_LO(%a0) orl #z_mask,USER_FPSR(%a6) orl #unfinx_mask,USER_FPSR(%a6) bra wr_etemp cu_dpdr: movel #0x3c010000,LOCAL_EX(%a0) //force pos double zero clrl LOCAL_HI(%a0) movel #0x800,LOCAL_LO(%a0) //with lsb set orl #unfinx_mask,USER_FPSR(%a6) bra wr_etemp cu_dnd: movel #0xbc010000,LOCAL_EX(%a0) //force pos double zero clrl LOCAL_HI(%a0) clrl LOCAL_LO(%a0) orl #z_mask,USER_FPSR(%a6) orl #neg_mask,USER_FPSR(%a6) orl #unfinx_mask,USER_FPSR(%a6) bra wr_etemp cu_dndr: movel #0xbc010000,LOCAL_EX(%a0) //force pos double zero clrl LOCAL_HI(%a0) movel #0x800,LOCAL_LO(%a0) //with lsb set orl #neg_mask,USER_FPSR(%a6) orl #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: movel #0x3f810000,LOCAL_EX(%a0) //force pos single zero clrl LOCAL_HI(%a0) clrl LOCAL_LO(%a0) orl #z_mask,USER_FPSR(%a6) orl #unfinx_mask,USER_FPSR(%a6) bra wr_etemp cu_spdr: movel #0x3f810000,LOCAL_EX(%a0) //force pos single zero movel #0x100,LOCAL_HI(%a0) //with lsb set clrl LOCAL_LO(%a0) orl #unfinx_mask,USER_FPSR(%a6) bra wr_etemp cu_snd: movel #0xbf810000,LOCAL_EX(%a0) //force pos single zero clrl LOCAL_HI(%a0) clrl LOCAL_LO(%a0) orl #z_mask,USER_FPSR(%a6) orl #neg_mask,USER_FPSR(%a6) orl #unfinx_mask,USER_FPSR(%a6) bra wr_etemp cu_sndr: movel #0xbf810000,LOCAL_EX(%a0) //force pos single zero movel #0x100,LOCAL_HI(%a0) //with lsb set clrl LOCAL_LO(%a0) orl #neg_mask,USER_FPSR(%a6) orl #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 denormalized // // 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: .set addcode,0x22 .set subcode,0x28 .set mulcode,0x23 .set divcode,0x20 .set cmpcode,0x38 ck_wrap: | tstb DY_MO_FLG(%a6) ;check for fsqrt beq fix_stk //if zero, it is fsqrt movew CMDREG1B(%a6),%d0 andiw #0x3b,%d0 //strip to command bits cmpiw #addcode,%d0 beq wrap_add cmpiw #subcode,%d0 beq wrap_sub cmpiw #mulcode,%d0 beq wrap_mul cmpiw #cmpcode,%d0 beq wrap_cmp // // Inst is fdiv. // wrap_div: cmpb #0xff,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. // cmpb #0x0f,DNRM_FLG(%a6) //check for dest denorm bnes div_srcd div_destd: bsrl 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) subl %d1,%d0 //subtract dest from src cmpl #0x7fff,%d0 blt fix_stk //if less, not wrap case clrb WBTEMP_SGN(%a6) movew ETEMP_EX(%a6),%d0 //find the sign of the result movew FPTEMP_EX(%a6),%d1 eorw %d1,%d0 andiw #0x8000,%d0 beq force_unf st WBTEMP_SGN(%a6) bra force_unf ckinf_ns: moveb STAG(%a6),%d0 //check source tag for inf or nan bra ck_in_com ckinf_nd: moveb DTAG(%a6),%d0 //check destination tag for inf or nan ck_in_com: andib #0x60,%d0 //isolate tag bits cmpb #0x40,%d0 //is it inf? beq nan_or_inf //not wrap case cmpb #0x60,%d0 //is it nan? beq nan_or_inf //yes, not wrap case? cmpb #0x20,%d0 //is it a zero? beq nan_or_inf //yes clrl %d0 rts //then ; it is either a zero of norm, // ;check wrap case nan_or_inf: moveql #-1,%d0 rts div_srcd: bsrl 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) subl %d1,%d0 //subtract src from dest cmpl #0x8000,%d0 blt fix_stk //if less, not wrap case clrb WBTEMP_SGN(%a6) movew ETEMP_EX(%a6),%d0 //find the sign of the result movew FPTEMP_EX(%a6),%d1 eorw %d1,%d0 andiw #0x8000,%d0 beqs force_ovf st WBTEMP_SGN(%a6) // // This code handles the case of the instruction resulting in // an overflow condition. // force_ovf: bclrb #E1,E_BYTE(%a6) orl #ovfl_inx_mask,USER_FPSR(%a6) clrw NMNEXC(%a6) leal WBTEMP(%a6),%a0 //point a0 to memory location movew CMDREG1B(%a6),%d0 btstl #6,%d0 //test for forced precision beqs frcovf_fpcr btstl #2,%d0 //check for double bnes frcovf_dbl movel #0x1,%d0 //inst is forced single bras frcovf_rnd frcovf_dbl: movel #0x2,%d0 //inst is forced double bras 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: bsrl 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 bsetb #sign_bit,WBTEMP_EX(%a6) bra frcfpn // // Inst is fadd. // wrap_add: cmpb #0xff,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. // cmpb #0x0f,DNRM_FLG(%a6) //check for dest denorm bnes add_srcd add_destd: bsrl 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) subl %d1,%d0 //subtract dest from src cmpl #0x8000,%d0 blt fix_stk //if less, not wrap case bra add_wrap add_srcd: bsrl 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) subl %d1,%d0 //subtract src from dest cmpl #0x8000,%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 correct for the user's rounding mode and // precision. // add_wrap: movew ETEMP_EX(%a6),%d0 movew FPTEMP_EX(%a6),%d1 eorw %d1,%d0 andiw #0x8000,%d0 beq add_same // // The signs are unlike. // cmpb #0x0f,DNRM_FLG(%a6) //is dest the denorm? bnes add_u_srcd movew FPTEMP_EX(%a6),%d0 andiw #0x8000,%d0 orw #0x3fff,%d0 //force the exponent to +/- 1 movew %d0,FPTEMP_EX(%a6) //in the denorm movel USER_FPCR(%a6),%d0 andil #0x30,%d0 fmovel %d0,%fpcr //set up users rmode and X fmovex ETEMP(%a6),%fp0 faddx FPTEMP(%a6),%fp0 leal WBTEMP(%a6),%a0 //point a0 to wbtemp in frame fmovel %fpsr,%d1 orl %d1,USER_FPSR(%a6) //capture cc's and inex from fadd fmovex %fp0,WBTEMP(%a6) //write result to memory lsrl #4,%d0 //put rmode in lower 2 bits movel USER_FPCR(%a6),%d1 andil #0xc0,%d1 lsrl #6,%d1 //put precision in upper word swap %d1 orl %d0,%d1 //set up for round call clrl %d0 //force sticky to zero bclrb #sign_bit,WBTEMP_EX(%a6) sne WBTEMP_SGN(%a6) bsrl round //round result to users rmode & prec bfclr WBTEMP_SGN(%a6){#0:#8} //convert back to IEEE ext format beq frcfpnr bsetb #sign_bit,WBTEMP_EX(%a6) bra frcfpnr add_u_srcd: movew ETEMP_EX(%a6),%d0 andiw #0x8000,%d0 orw #0x3fff,%d0 //force the exponent to +/- 1 movew %d0,ETEMP_EX(%a6) //in the denorm movel USER_FPCR(%a6),%d0 andil #0x30,%d0 fmovel %d0,%fpcr //set up users rmode and X fmovex ETEMP(%a6),%fp0 faddx FPTEMP(%a6),%fp0 fmovel %fpsr,%d1 orl %d1,USER_FPSR(%a6) //capture cc's and inex from fadd leal WBTEMP(%a6),%a0 //point a0 to wbtemp in frame fmovex %fp0,WBTEMP(%a6) //write result to memory lsrl #4,%d0 //put rmode in lower 2 bits movel USER_FPCR(%a6),%d1 andil #0xc0,%d1 lsrl #6,%d1 //put precision in upper word swap %d1 orl %d0,%d1 //set up for round call clrl %d0 //force sticky to zero bclrb #sign_bit,WBTEMP_EX(%a6) sne WBTEMP_SGN(%a6) //use internal format for round bsrl round //round result to users rmode & prec bfclr WBTEMP_SGN(%a6){#0:#8} //convert back to IEEE ext format beq frcfpnr bsetb #sign_bit,WBTEMP_EX(%a6) bra frcfpnr // // Signs are alike: // add_same: cmpb #0x0f,DNRM_FLG(%a6) //is dest the denorm? bnes add_s_srcd add_s_destd: leal ETEMP(%a6),%a0 movel USER_FPCR(%a6),%d0 andil #0x30,%d0 lsrl #4,%d0 //put rmode in lower 2 bits movel USER_FPCR(%a6),%d1 andil #0xc0,%d1 lsrl #6,%d1 //put precision in upper word swap %d1 orl %d0,%d1 //set up for round call movel #0x20000000,%d0 //set sticky for round bclrb #sign_bit,ETEMP_EX(%a6) sne ETEMP_SGN(%a6) bsrl round //round result to users rmode & prec bfclr ETEMP_SGN(%a6){#0:#8} //convert back to IEEE ext format beqs add_s_dclr bsetb #sign_bit,ETEMP_EX(%a6) add_s_dclr: leal WBTEMP(%a6),%a0 movel ETEMP(%a6),(%a0) //write result to wbtemp movel ETEMP_HI(%a6),4(%a0) movel ETEMP_LO(%a6),8(%a0) tstw ETEMP_EX(%a6) bgt add_ckovf orl #neg_mask,USER_FPSR(%a6) bra add_ckovf add_s_srcd: leal FPTEMP(%a6),%a0 movel USER_FPCR(%a6),%d0 andil #0x30,%d0 lsrl #4,%d0 //put rmode in lower 2 bits movel USER_FPCR(%a6),%d1 andil #0xc0,%d1 lsrl #6,%d1 //put precision in upper word swap %d1 orl %d0,%d1 //set up for round call movel #0x20000000,%d0 //set sticky for round bclrb #sign_bit,FPTEMP_EX(%a6) sne FPTEMP_SGN(%a6) bsrl round //round result to users rmode & prec bfclr FPTEMP_SGN(%a6){#0:#8} //convert back to IEEE ext format beqs add_s_sclr bsetb #sign_bit,FPTEMP_EX(%a6) add_s_sclr: leal WBTEMP(%a6),%a0 movel FPTEMP(%a6),(%a0) //write result to wbtemp movel FPTEMP_HI(%a6),4(%a0) movel FPTEMP_LO(%a6),8(%a0) tstw FPTEMP_EX(%a6) bgt add_ckovf orl #neg_mask,USER_FPSR(%a6) add_ckovf: movew WBTEMP_EX(%a6),%d0 andiw #0x7fff,%d0 cmpiw #0x7fff,%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.) // orl #inf_mask+ovfl_inx_mask,USER_FPSR(%a6) clrl 4(%a0) bra frcfpnr // // Inst is fsub. // wrap_sub: cmpb #0xff,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. // cmpb #0x0f,DNRM_FLG(%a6) //check for dest denorm bnes sub_srcd sub_destd: bsrl 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) subl %d1,%d0 //subtract src from dest cmpl #0x8000,%d0 blt fix_stk //if less, not wrap case bra sub_wrap sub_srcd: bsrl 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) subl %d1,%d0 //subtract dest from src cmpl #0x8000,%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 correct for the user's rounding mode and // precision. // sub_wrap: movew ETEMP_EX(%a6),%d0 movew FPTEMP_EX(%a6),%d1 eorw %d1,%d0 andiw #0x8000,%d0 bne sub_diff // // The signs are alike. // cmpb #0x0f,DNRM_FLG(%a6) //is dest the denorm? bnes sub_u_srcd movew FPTEMP_EX(%a6),%d0 andiw #0x8000,%d0 orw #0x3fff,%d0 //force the exponent to +/- 1 movew %d0,FPTEMP_EX(%a6) //in the denorm movel USER_FPCR(%a6),%d0 andil #0x30,%d0 fmovel %d0,%fpcr //set up users rmode and X fmovex FPTEMP(%a6),%fp0 fsubx ETEMP(%a6),%fp0 fmovel %fpsr,%d1 orl %d1,USER_FPSR(%a6) //capture cc's and inex from fadd leal WBTEMP(%a6),%a0 //point a0 to wbtemp in frame fmovex %fp0,WBTEMP(%a6) //write result to memory lsrl #4,%d0 //put rmode in lower 2 bits movel USER_FPCR(%a6),%d1 andil #0xc0,%d1 lsrl #6,%d1 //put precision in upper word swap %d1 orl %d0,%d1 //set up for round call clrl %d0 //force sticky to zero bclrb #sign_bit,WBTEMP_EX(%a6) sne WBTEMP_SGN(%a6) bsrl round //round result to users rmode & prec bfclr WBTEMP_SGN(%a6){#0:#8} //convert back to IEEE ext format beq frcfpnr bsetb #sign_bit,WBTEMP_EX(%a6) bra frcfpnr sub_u_srcd: movew ETEMP_EX(%a6),%d0 andiw #0x8000,%d0 orw #0x3fff,%d0 //force the exponent to +/- 1 movew %d0,ETEMP_EX(%a6) //in the denorm movel USER_FPCR(%a6),%d0 andil #0x30,%d0 fmovel %d0,%fpcr //set up users rmode and X fmovex FPTEMP(%a6),%fp0 fsubx ETEMP(%a6),%fp0 fmovel %fpsr,%d1 orl %d1,USER_FPSR(%a6) //capture cc's and inex from fadd leal WBTEMP(%a6),%a0 //point a0 to wbtemp in frame fmovex %fp0,WBTEMP(%a6) //write result to memory lsrl #4,%d0 //put rmode in lower 2 bits movel USER_FPCR(%a6),%d1 andil #0xc0,%d1 lsrl #6,%d1 //put precision in upper word swap %d1 orl %d0,%d1 //set up for round call clrl %d0 //force sticky to zero bclrb #sign_bit,WBTEMP_EX(%a6) sne WBTEMP_SGN(%a6) bsrl round //round result to users rmode & prec bfclr WBTEMP_SGN(%a6){#0:#8} //convert back to IEEE ext format beq frcfpnr bsetb #sign_bit,WBTEMP_EX(%a6) bra frcfpnr // // Signs are unlike: // sub_diff: cmpb #0x0f,DNRM_FLG(%a6) //is dest the denorm? bnes sub_s_srcd sub_s_destd: leal ETEMP(%a6),%a0 movel USER_FPCR(%a6),%d0 andil #0x30,%d0 lsrl #4,%d0 //put rmode in lower 2 bits movel USER_FPCR(%a6),%d1 andil #0xc0,%d1 lsrl #6,%d1 //put precision in upper word swap %d1 orl %d0,%d1 //set up for round call movel #0x20000000,%d0 //set sticky for round // // Since the dest is the denorm, the sign is the opposite of the // norm sign. // eoriw #0x8000,ETEMP_EX(%a6) //flip sign on result tstw ETEMP_EX(%a6) bgts sub_s_dwr orl #neg_mask,USER_FPSR(%a6) sub_s_dwr: bclrb #sign_bit,ETEMP_EX(%a6) sne ETEMP_SGN(%a6) bsrl round //round result to users rmode & prec bfclr ETEMP_SGN(%a6){#0:#8} //convert back to IEEE ext format beqs sub_s_dclr bsetb #sign_bit,ETEMP_EX(%a6) sub_s_dclr: leal WBTEMP(%a6),%a0 movel ETEMP(%a6),(%a0) //write result to wbtemp movel ETEMP_HI(%a6),4(%a0) movel ETEMP_LO(%a6),8(%a0) bra sub_ckovf sub_s_srcd: leal FPTEMP(%a6),%a0 movel USER_FPCR(%a6),%d0 andil #0x30,%d0 lsrl #4,%d0 //put rmode in lower 2 bits movel USER_FPCR(%a6),%d1 andil #0xc0,%d1 lsrl #6,%d1 //put precision in upper word swap %d1 orl %d0,%d1 //set up for round call movel #0x20000000,%d0 //set sticky for round bclrb #sign_bit,FPTEMP_EX(%a6) sne FPTEMP_SGN(%a6) bsrl round //round result to users rmode & prec bfclr FPTEMP_SGN(%a6){#0:#8} //convert back to IEEE ext format beqs sub_s_sclr bsetb #sign_bit,FPTEMP_EX(%a6) sub_s_sclr: leal WBTEMP(%a6),%a0 movel FPTEMP(%a6),(%a0) //write result to wbtemp movel FPTEMP_HI(%a6),4(%a0) movel FPTEMP_LO(%a6),8(%a0) tstw FPTEMP_EX(%a6) bgt sub_ckovf orl #neg_mask,USER_FPSR(%a6) sub_ckovf: movew WBTEMP_EX(%a6),%d0 andiw #0x7fff,%d0 cmpiw #0x7fff,%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.) // orl #inf_mask+ovfl_inx_mask,USER_FPSR(%a6) clrl 4(%a0) bra frcfpnr // // Inst is fcmp. // wrap_cmp: cmpb #0xff,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. // cmpb #0x0f,DNRM_FLG(%a6) //check for dest denorm bnes cmp_srcd cmp_destd: bsrl 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) subl %d1,%d0 //subtract dest from src cmpl #0x8000,%d0 blt fix_stk //if less, not wrap case tstw ETEMP_EX(%a6) //set N to ~sign_of(src) bge cmp_setn rts cmp_srcd: bsrl 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) subl %d1,%d0 //subtract src from dest cmpl #0x8000,%d0 blt fix_stk //if less, not wrap case tstw FPTEMP_EX(%a6) //set N to sign_of(dest) blt cmp_setn rts cmp_setn: orl #neg_mask,USER_FPSR(%a6) rts // // Inst is fmul. // wrap_mul: cmpb #0xff,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. // cmpb #0x0f,DNRM_FLG(%a6) //check for dest denorm bnes mul_srcd mul_destd: bsrl 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) addl %d1,%d0 //subtract dest from src bgt fix_stk bra force_unf mul_srcd: bsrl 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) addl %d1,%d0 //subtract src from dest bgt fix_stk // // This code handles the case of the instruction resulting in // an underflow condition. // force_unf: bclrb #E1,E_BYTE(%a6) orl #unfinx_mask,USER_FPSR(%a6) clrw NMNEXC(%a6) clrb WBTEMP_SGN(%a6) movew ETEMP_EX(%a6),%d0 //find the sign of the result movew FPTEMP_EX(%a6),%d1 eorw %d1,%d0 andiw #0x8000,%d0 beqs frcunfcont st WBTEMP_SGN(%a6) frcunfcont: lea WBTEMP(%a6),%a0 //point a0 to memory location movew CMDREG1B(%a6),%d0 btstl #6,%d0 //test for forced precision beqs frcunf_fpcr btstl #2,%d0 //check for double bnes frcunf_dbl movel #0x1,%d0 //inst is forced single bras frcunf_rnd frcunf_dbl: movel #0x2,%d0 //inst is forced double bras frcunf_rnd frcunf_fpcr: bfextu FPCR_MODE(%a6){#0:#2},%d0 //inst not forced - use fpcr prec frcunf_rnd: bsrl 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 beqs frcfpn bsetb #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: movew CMDREG1B(%a6),%d0 btstl #6,%d0 //test for forced precision beqs frcfpn_fpcr btstl #2,%d0 //check for double bnes frcfpn_dbl movel #0x1,%d0 //inst is forced single bras frcfpn_rnd frcfpn_dbl: movel #0x2,%d0 //inst is forced double bras frcfpn_rnd frcfpn_fpcr: bfextu FPCR_MODE(%a6){#0:#2},%d0 //inst not forced - use fpcr prec tstb %d0 beqs frcfpn //if extended, write what you got frcfpn_rnd: bclrb #sign_bit,WBTEMP_EX(%a6) sne WBTEMP_SGN(%a6) bsrl 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 beqs frcfpn_clr bsetb #sign_bit,WBTEMP_EX(%a6) frcfpn_clr: orl #ovfinx_mask,USER_FPSR(%a6) // // Perform the write. // frcfpn: bfextu CMDREG1B(%a6){#6:#3},%d0 //extract fp destination register cmpib #3,%d0 bles frc0123 //check if dest is fp0-fp3 movel #7,%d1 subl %d0,%d1 clrl %d0 bsetl %d1,%d0 fmovemx WBTEMP(%a6),%d0 rts frc0123: cmpib #0,%d0 beqs frc0_dst cmpib #1,%d0 beqs frc1_dst cmpib #2,%d0 beqs frc2_dst frc3_dst: movel WBTEMP_EX(%a6),USER_FP3(%a6) movel WBTEMP_HI(%a6),USER_FP3+4(%a6) movel WBTEMP_LO(%a6),USER_FP3+8(%a6) rts frc2_dst: movel WBTEMP_EX(%a6),USER_FP2(%a6) movel WBTEMP_HI(%a6),USER_FP2+4(%a6) movel WBTEMP_LO(%a6),USER_FP2+8(%a6) rts frc1_dst: movel WBTEMP_EX(%a6),USER_FP1(%a6) movel WBTEMP_HI(%a6),USER_FP1+4(%a6) movel WBTEMP_LO(%a6),USER_FP1+8(%a6) rts frc0_dst: movel WBTEMP_EX(%a6),USER_FP0(%a6) movel WBTEMP_HI(%a6),USER_FP0+4(%a6) movel 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: btstb #snan_bit,FPSR_EXCEPT(%a6) //if snan is set, and beqs fmoveinc //enabled, force restore btstb #snan_bit,FPCR_ENABLE(%a6) //and don't overwrite beqs fmoveinc //the dest movel ETEMP_EX(%a6),FPTEMP_EX(%a6) //set up fptemp sign for // ;snan handler tstb ETEMP(%a6) //check for negative blts snan_neg rts snan_neg: orl #neg_bit,USER_FPSR(%a6) //snan is negative; set N rts fmoveinc: clrw NMNEXC(%a6) bclrb #E1,E_BYTE(%a6) moveb STAG(%a6),%d0 //check if stag is inf andib #0xe0,%d0 cmpib #0x40,%d0 bnes fminc_cnan orl #inf_mask,USER_FPSR(%a6) //if inf, nothing yet has set I tstw LOCAL_EX(%a0) //check sign bges fminc_con orl #neg_mask,USER_FPSR(%a6) bra fminc_con fminc_cnan: cmpib #0x60,%d0 //check if stag is NaN bnes fminc_czero orl #nan_mask,USER_FPSR(%a6) //if nan, nothing yet has set NaN movel ETEMP_EX(%a6),FPTEMP_EX(%a6) //set up fptemp sign for // ;snan handler tstw LOCAL_EX(%a0) //check sign bges fminc_con orl #neg_mask,USER_FPSR(%a6) bra fminc_con fminc_czero: cmpib #0x20,%d0 //check if zero bnes fminc_con orl #z_mask,USER_FPSR(%a6) //if zero, set Z tstw LOCAL_EX(%a0) //check sign bges fminc_con orl #neg_mask,USER_FPSR(%a6) fminc_con: bfextu CMDREG1B(%a6){#6:#3},%d0 //extract fp destination register cmpib #3,%d0 bles fp0123 //check if dest is fp0-fp3 movel #7,%d1 subl %d0,%d1 clrl %d0 bsetl %d1,%d0 fmovemx ETEMP(%a6),%d0 rts fp0123: cmpib #0,%d0 beqs fp0_dst cmpib #1,%d0 beqs fp1_dst cmpib #2,%d0 beqs fp2_dst fp3_dst: movel ETEMP_EX(%a6),USER_FP3(%a6) movel ETEMP_HI(%a6),USER_FP3+4(%a6) movel ETEMP_LO(%a6),USER_FP3+8(%a6) rts fp2_dst: movel ETEMP_EX(%a6),USER_FP2(%a6) movel ETEMP_HI(%a6),USER_FP2+4(%a6) movel ETEMP_LO(%a6),USER_FP2+8(%a6) rts fp1_dst: movel ETEMP_EX(%a6),USER_FP1(%a6) movel ETEMP_HI(%a6),USER_FP1+4(%a6) movel ETEMP_LO(%a6),USER_FP1+8(%a6) rts fp0_dst: movel ETEMP_EX(%a6),USER_FP0(%a6) movel ETEMP_HI(%a6),USER_FP0+4(%a6) movel ETEMP_LO(%a6),USER_FP0+8(%a6) rts opclass3: st CU_ONLY(%a6) movew CMDREG1B(%a6),%d0 //check if packed moveout andiw #0x0c00,%d0 //isolate last 2 bits of size field cmpiw #0x0c00,%d0 //if size is 011 or 111, it is packed beq pack_out //else it is norm or denorm bra mv_out // // MOVE OUT // mv_tbl: .long li .long sgp .long xp .long mvout_end //should never be taken .long wi .long dp .long bi .long mvout_end //should never be taken mv_out: bfextu CMDREG1B(%a6){#3:#3},%d1 //put source specifier in d1 leal mv_tbl,%a0 movel %a0@(%d1:l: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: btstb #inex2_bit,FPSR_EXCEPT(%a6) beqs no_aufl btstb #unfl_bit,FPSR_EXCEPT(%a6) beqs no_aufl bsetb #aunfl_bit,FPSR_AEXCEPT(%a6) no_aufl: clrw NMNEXC(%a6) bclrb #E1,E_BYTE(%a6) fmovel #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} beqs mvout_con bsetb #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: clrw NMNEXC(%a6) bclrb #E1,E_BYTE(%a6) fmovel #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} beqs mvouti_con bsetb #sign_bit,ETEMP_EX(%a6) mvouti_con: rts // // li is used to handle a long integer source specifier // li: moveql #4,%d0 //set byte count btstb #7,STAG(%a6) //check for extended denorm bne int_dnrm //if so, branch fmovemx ETEMP(%a6),%fp0-%fp0 fcmpd #0x41dfffffffc00000,%fp0 // 41dfffffffc00000 in dbl prec = 401d0000fffffffe00000000 in ext prec fbge lo_plrg fcmpd #0xc1e0000000000000,%fp0 // c1e0000000000000 in dbl prec = c01e00008000000000000000 in ext prec fble lo_nlrg // // at this point, the answer is between the largest pos and neg values // movel USER_FPCR(%a6),%d1 //use user's rounding mode andil #0x30,%d1 fmovel %d1,%fpcr fmovel %fp0,L_SCR1(%a6) //let the 040 perform conversion fmovel %fpsr,%d1 orl %d1,USER_FPSR(%a6) //capture inex2/ainex if set bra int_wrt lo_plrg: movel #0x7fffffff,L_SCR1(%a6) //answer is largest positive int fbeq int_wrt //exact answer fcmpd #0x41dfffffffe00000,%fp0 // 41dfffffffe00000 in dbl prec = 401d0000ffffffff00000000 in ext prec fbge int_operr //set operr bra int_inx //set inexact lo_nlrg: movel #0x80000000,L_SCR1(%a6) fbeq int_wrt //exact answer fcmpd #0xc1e0000000100000,%fp0 // c1e0000000100000 in dbl prec = c01e00008000000080000000 in ext prec fblt int_operr //set operr bra int_inx //set inexact // // wi is used to handle a word integer source specifier // wi: moveql #2,%d0 //set byte count btstb #7,STAG(%a6) //check for extended denorm bne int_dnrm //branch if so fmovemx ETEMP(%a6),%fp0-%fp0 fcmps #0x46fffe00,%fp0 // 46fffe00 in sgl prec = 400d0000fffe000000000000 in ext prec fbge wo_plrg fcmps #0xc7000000,%fp0 // c7000000 in sgl prec = c00e00008000000000000000 in ext prec fble wo_nlrg // // at this point, the answer is between the largest pos and neg values // movel USER_FPCR(%a6),%d1 //use user's rounding mode andil #0x30,%d1 fmovel %d1,%fpcr fmovew %fp0,L_SCR1(%a6) //let the 040 perform conversion fmovel %fpsr,%d1 orl %d1,USER_FPSR(%a6) //capture inex2/ainex if set bra int_wrt wo_plrg: movew #0x7fff,L_SCR1(%a6) //answer is largest positive int fbeq int_wrt //exact answer fcmps #0x46ffff00,%fp0 // 46ffff00 in sgl prec = 400d0000ffff000000000000 in ext prec fbge int_operr //set operr bra int_inx //set inexact wo_nlrg: movew #0x8000,L_SCR1(%a6) fbeq int_wrt //exact answer fcmps #0xc7000080,%fp0 // c7000080 in sgl prec = c00e00008000800000000000 in ext prec fblt int_operr //set operr bra int_inx //set inexact // // bi is used to handle a byte integer source specifier // bi: moveql #1,%d0 //set byte count btstb #7,STAG(%a6) //check for extended denorm bne int_dnrm //branch if so fmovemx ETEMP(%a6),%fp0-%fp0 fcmps #0x42fe0000,%fp0 // 42fe0000 in sgl prec = 40050000fe00000000000000 in ext prec fbge by_plrg fcmps #0xc3000000,%fp0 // c3000000 in sgl prec = c00600008000000000000000 in ext prec fble by_nlrg // // at this point, the answer is between the largest pos and neg values // movel USER_FPCR(%a6),%d1 //use user's rounding mode andil #0x30,%d1 fmovel %d1,%fpcr fmoveb %fp0,L_SCR1(%a6) //let the 040 perform conversion fmovel %fpsr,%d1 orl %d1,USER_FPSR(%a6) //capture inex2/ainex if set bra int_wrt by_plrg: moveb #0x7f,L_SCR1(%a6) //answer is largest positive int fbeq int_wrt //exact answer fcmps #0x42ff0000,%fp0 // 42ff0000 in sgl prec = 40050000ff00000000000000 in ext prec fbge int_operr //set operr bra int_inx //set inexact by_nlrg: moveb #0x80,L_SCR1(%a6) fbeq int_wrt //exact answer fcmps #0xc3008000,%fp0 // c3008000 in sgl prec = c00600008080000000000000 in ext prec fblt int_operr //set operr bra 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: movel #0,L_SCR1(%a6) // initialize result to 0 bfextu FPCR_MODE(%a6){#2:#2},%d1 // d1 is the rounding mode cmpb #2,%d1 bmis int_inx // if RN or RZ, done bnes int_rp // if RP, continue below tstw ETEMP(%a6) // RM: store -1 in L_SCR1 if src is negative bpls int_inx // otherwise result is 0 movel #-1,L_SCR1(%a6) bras int_inx int_rp: tstw ETEMP(%a6) // RP: store +1 of proper width in L_SCR1 if // ; source is greater than 0 bmis int_inx // otherwise, result is 0 lea L_SCR1(%a6),%a1 // a1 is address of L_SCR1 addal %d0,%a1 // offset by destination width -1 subal #1,%a1 bsetb #0,(%a1) // set low bit at a1 address int_inx: oril #inx2a_mask,USER_FPSR(%a6) bras int_wrt int_operr: fmovemx %fp0-%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) oril #opaop_mask,USER_FPSR(%a6) // ;fall through to perform int_wrt int_wrt: movel EXC_EA(%a6),%a1 //load destination address tstl %a1 //check to see if it is a dest register beqs wrt_dn //write data register lea L_SCR1(%a6),%a0 //point to supervisor source address bsrl mem_write bra mvouti_end wrt_dn: movel %d0,-(%sp) //d0 currently contains the size to write bsrl get_fline //get_fline returns Dn in d0 andiw #0x7,%d0 //isolate register movel (%sp)+,%d1 //get size cmpil #4,%d1 //most frequent case beqs sz_long cmpil #2,%d1 bnes sz_con orl #8,%d0 //add 'word' size to register# bras sz_con sz_long: orl #0x10,%d0 //add 'long' size to register# sz_con: movel %d0,%d1 //reg_dest expects size:reg in d1 bsrl reg_dest //load proper data register bra mvouti_end xp: lea ETEMP(%a6),%a0 bclrb #sign_bit,LOCAL_EX(%a0) sne LOCAL_SGN(%a0) btstb #7,STAG(%a6) //check for extended denorm bne xdnrm clrl %d0 bras do_fp //do normal case sgp: lea ETEMP(%a6),%a0 bclrb #sign_bit,LOCAL_EX(%a0) sne LOCAL_SGN(%a0) btstb #7,STAG(%a6) //check for extended denorm bne sp_catas //branch if so movew LOCAL_EX(%a0),%d0 lea sp_bnds,%a1 cmpw (%a1),%d0 blt sp_under cmpw 2(%a1),%d0 bgt sp_over movel #1,%d0 //set destination format to single bras do_fp //do normal case dp: lea ETEMP(%a6),%a0 bclrb #sign_bit,LOCAL_EX(%a0) sne LOCAL_SGN(%a0) btstb #7,STAG(%a6) //check for extended denorm bne dp_catas //branch if so movew LOCAL_EX(%a0),%d0 lea dp_bnds,%a1 cmpw (%a1),%d0 blt dp_under cmpw 2(%a1),%d0 bgt dp_over movel #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 addl %d0,%d1 //d1 has PREC/MODE info clrl %d0 //clear g,r,s bsrl round //round movel %a0,%a1 movel 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 cmpb #2,%d1 bgts ddbl //double=5, extended=2, single=1 bnes dsgl // ;fall through to dext dext: bsrl dest_ext bra mvout_end dsgl: bsrl dest_sgl bra mvout_end ddbl: bsrl dest_dbl bra mvout_end // // Handle possible denorm or catastrophic underflow cases here // xdnrm: bsr set_xop //initialize WBTEMP bsetb #wbtemp15_bit,WB_BYTE(%a6) //set wbtemp15 movel %a0,%a1 movel EXC_EA(%a6),%a0 //a0 has the destination pointer bsrl dest_ext //store to memory bsetb #unfl_bit,FPSR_EXCEPT(%a6) bra mvout_end sp_under: bsetb #etemp15_bit,STAG(%a6) cmpw 4(%a1),%d0 blts sp_catas //catastrophic underflow case movel #1,%d0 //load in round precision movel #sgl_thresh,%d1 //load in single denorm threshold bsrl dpspdnrm //expects d1 to have the proper // ;denorm threshold bsrl dest_sgl //stores value to destination bsetb #unfl_bit,FPSR_EXCEPT(%a6) bra mvout_end //exit dp_under: bsetb #etemp15_bit,STAG(%a6) cmpw 4(%a1),%d0 blts dp_catas //catastrophic underflow case movel #dbl_thresh,%d1 //load in double precision threshold movel #2,%d0 bsrl dpspdnrm //expects d1 to have proper // ;denorm threshold // ;expects d0 to have round precision bsrl dest_dbl //store value to destination bsetb #unfl_bit,FPSR_EXCEPT(%a6) bra mvout_end //exit // // Handle catastrophic underflow cases here // sp_catas: // Temp fix for z bit set in unf_sub movel USER_FPSR(%a6),-(%a7) movel #1,%d0 //set round precision to sgl bsrl unf_sub //a0 points to result movel (%a7)+,USER_FPSR(%a6) movel #1,%d0 subw %d0,LOCAL_EX(%a0) //account for difference between // ;denorm/norm bias movel %a0,%a1 //a1 has the operand input movel EXC_EA(%a6),%a0 //a0 has the destination pointer bsrl dest_sgl //store the result oril #unfinx_mask,USER_FPSR(%a6) bra mvout_end dp_catas: // Temp fix for z bit set in unf_sub movel USER_FPSR(%a6),-(%a7) movel #2,%d0 //set round precision to dbl bsrl unf_sub //a0 points to result movel (%a7)+,USER_FPSR(%a6) movel #1,%d0 subw %d0,LOCAL_EX(%a0) //account for difference between // ;denorm/norm bias movel %a0,%a1 //a1 has the operand input movel EXC_EA(%a6),%a0 //a0 has the destination pointer bsrl dest_dbl //store the result oril #unfinx_mask,USER_FPSR(%a6) bra mvout_end // // Handle catastrophic overflow cases here // sp_over: // Temp fix for z bit set in unf_sub movel USER_FPSR(%a6),-(%a7) movel #1,%d0 leal FP_SCR1(%a6),%a0 //use FP_SCR1 for creating result movel ETEMP_EX(%a6),(%a0) movel ETEMP_HI(%a6),4(%a0) movel ETEMP_LO(%a6),8(%a0) bsrl ovf_res movel (%a7)+,USER_FPSR(%a6) movel %a0,%a1 movel EXC_EA(%a6),%a0 bsrl dest_sgl orl #ovfinx_mask,USER_FPSR(%a6) bra mvout_end dp_over: // Temp fix for z bit set in ovf_res movel USER_FPSR(%a6),-(%a7) movel #2,%d0 leal FP_SCR1(%a6),%a0 //use FP_SCR1 for creating result movel ETEMP_EX(%a6),(%a0) movel ETEMP_HI(%a6),4(%a0) movel ETEMP_LO(%a6),8(%a0) bsrl ovf_res movel (%a7)+,USER_FPSR(%a6) movel %a0,%a1 movel EXC_EA(%a6),%a0 bsrl dest_dbl orl #ovfinx_mask,USER_FPSR(%a6) bra 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: movel %d0,-(%a7) //save round precision clrl %d0 //clear initial g,r,s bsrl dnrm_lp //careful with d0, it's needed by round bfextu FPCR_MODE(%a6){#2:#2},%d1 //get rounding mode swap %d1 movew 2(%a7),%d1 //set rounding precision swap %d1 //at this point d1 has PREC/MODE info bsrl round //round result, sets the inex bit in // ;USER_FPSR if needed movew #1,%d0 subw %d0,LOCAL_EX(%a0) //account for difference in denorm // ;vs norm bias movel %a0,%a1 //a1 has the operand input movel EXC_EA(%a6),%a0 //a0 has the destination pointer addw #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: movel LOCAL_EX(%a0),WBTEMP_EX(%a6) movel LOCAL_HI(%a0),WBTEMP_HI(%a6) movel LOCAL_LO(%a0),WBTEMP_LO(%a6) bfclr WBTEMP_SGN(%a6){#0:#8} beqs sxop bsetb #sign_bit,WBTEMP_EX(%a6) sxop: bfclr STAG(%a6){#5:#4} //clear wbtm66,wbtm1,wbtm0,sbit rts // // P_MOVE // p_movet: .long p_move .long p_movez .long p_movei .long p_moven .long p_move p_regd: .long p_dyd0 .long p_dyd1 .long p_dyd2 .long p_dyd3 .long p_dyd4 .long p_dyd5 .long p_dyd6 .long p_dyd7 pack_out: leal p_movet,%a0 //load jmp table address movew STAG(%a6),%d0 //get source tag bfextu %d0{#16:#3},%d0 //isolate source bits movel (%a0,%d0.w*4),%a0 //load a0 with routine label for tag jmp (%a0) //go to the routine p_write: movel #0x0c,%d0 //get byte count movel EXC_EA(%a6),%a1 //get the destination address bsr mem_write //write the user's destination moveb #0,CU_SAVEPC(%a6) //set the cu save pc to all 0's // // Also note that the dtag must be set to norm here - this is because // the 040 uses the dtag to execute the correct microcode. // bfclr DTAG(%a6){#0:#3} //set dtag to norm rts // Notes on handling of special case (zero, inf, and nan) inputs: // 1. Operr is not signalled if the k-factor is greater than 18. // 2. Per the manual, status bits are not set. // p_move: movew CMDREG1B(%a6),%d0 btstl #kfact_bit,%d0 //test for dynamic k-factor beqs statick //if clear, k-factor is static dynamick: bfextu %d0{#25:#3},%d0 //isolate register for dynamic k-factor lea p_regd,%a0 movel %a0@(%d0:l:4),%a0 jmp (%a0) statick: andiw #0x007f,%d0 //get k-factor bfexts %d0{#25:#7},%d0 //sign extend d0 for bindec leal ETEMP(%a6),%a0 //a0 will point to the packed decimal bsrl bindec //perform the convert; data at a6 leal FP_SCR1(%a6),%a0 //load a0 with result address bral p_write p_movez: leal ETEMP(%a6),%a0 //a0 will point to the packed decimal clrw 2(%a0) //clear lower word of exp clrl 4(%a0) //load second lword of ZERO clrl 8(%a0) //load third lword of ZERO bra p_write //go write results p_movei: fmovel #0,%FPSR //clear aiop leal ETEMP(%a6),%a0 //a0 will point to the packed decimal clrw 2(%a0) //clear lower word of exp bra p_write //go write the result p_moven: leal ETEMP(%a6),%a0 //a0 will point to the packed decimal clrw 2(%a0) //clear lower word of exp bra p_write //go write the result // // Routines to read the dynamic k-factor from Dn. // p_dyd0: movel USER_D0(%a6),%d0 bras statick p_dyd1: movel USER_D1(%a6),%d0 bras statick p_dyd2: movel %d2,%d0 bras statick p_dyd3: movel %d3,%d0 bras statick p_dyd4: movel %d4,%d0 bras statick p_dyd5: movel %d5,%d0 bras statick p_dyd6: movel %d6,%d0 bra statick p_dyd7: movel %d7,%d0 bra statick |end