.file "reg_u_div.S"1/*---------------------------------------------------------------------------+2| reg_u_div.S |3| |4| Divide one FPU_REG by another and put the result in a destination FPU_REG.|5| |6| Copyright (C) 1992,1993,1995,1997 |7| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |8| E-mail [email protected] |9| |10| |11+---------------------------------------------------------------------------*/1213/*---------------------------------------------------------------------------+14| Call from C as: |15| int FPU_u_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest, |16| unsigned int control_word, char *sign) |17| |18| Does not compute the destination exponent, but does adjust it. |19| |20| Return value is the tag of the answer, or-ed with FPU_Exception if |21| one was raised, or -1 on internal error. |22+---------------------------------------------------------------------------*/2324#include "exception.h"25#include "fpu_emu.h"26#include "control_w.h"272829/* #define dSIGL(x) (x) */30/* #define dSIGH(x) 4(x) */313233#ifndef NON_REENTRANT_FPU34/*35Local storage on the stack:36Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_037Overflow flag: ovfl_flag38*/39#define FPU_accum_3 -4(%ebp)40#define FPU_accum_2 -8(%ebp)41#define FPU_accum_1 -12(%ebp)42#define FPU_accum_0 -16(%ebp)43#define FPU_result_1 -20(%ebp)44#define FPU_result_2 -24(%ebp)45#define FPU_ovfl_flag -28(%ebp)4647#else48.data49/*50Local storage in a static area:51Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_052Overflow flag: ovfl_flag53*/54.align 4,055FPU_accum_3:56.long 057FPU_accum_2:58.long 059FPU_accum_1:60.long 061FPU_accum_0:62.long 063FPU_result_1:64.long 065FPU_result_2:66.long 067FPU_ovfl_flag:68.byte 069#endif /* NON_REENTRANT_FPU */7071#define REGA PARAM172#define REGB PARAM273#define DEST PARAM37475.text76ENTRY(FPU_u_div)77pushl %ebp78movl %esp,%ebp79#ifndef NON_REENTRANT_FPU80subl $28,%esp81#endif /* NON_REENTRANT_FPU */8283pushl %esi84pushl %edi85pushl %ebx8687movl REGA,%esi88movl REGB,%ebx89movl DEST,%edi9091movswl EXP(%esi),%edx92movswl EXP(%ebx),%eax93subl %eax,%edx94addl EXP_BIAS,%edx9596/* A denormal and a large number can cause an exponent underflow */97cmpl EXP_WAY_UNDER,%edx98jg xExp_not_underflow99100/* Set to a really low value allow correct handling */101movl EXP_WAY_UNDER,%edx102103xExp_not_underflow:104105movw %dx,EXP(%edi)106107#ifdef PARANOID108/* testl $0x80000000, SIGH(%esi) // Dividend */109/* je L_bugged */110testl $0x80000000, SIGH(%ebx) /* Divisor */111je L_bugged112#endif /* PARANOID */113114/* Check if the divisor can be treated as having just 32 bits */115cmpl $0,SIGL(%ebx)116jnz L_Full_Division /* Can't do a quick divide */117118/* We should be able to zip through the division here */119movl SIGH(%ebx),%ecx /* The divisor */120movl SIGH(%esi),%edx /* Dividend */121movl SIGL(%esi),%eax /* Dividend */122123cmpl %ecx,%edx124setaeb FPU_ovfl_flag /* Keep a record */125jb L_no_adjust126127subl %ecx,%edx /* Prevent the overflow */128129L_no_adjust:130/* Divide the 64 bit number by the 32 bit denominator */131divl %ecx132movl %eax,FPU_result_2133134/* Work on the remainder of the first division */135xorl %eax,%eax136divl %ecx137movl %eax,FPU_result_1138139/* Work on the remainder of the 64 bit division */140xorl %eax,%eax141divl %ecx142143testb $255,FPU_ovfl_flag /* was the num > denom ? */144je L_no_overflow145146/* Do the shifting here */147/* increase the exponent */148incw EXP(%edi)149150/* shift the mantissa right one bit */151stc /* To set the ms bit */152rcrl FPU_result_2153rcrl FPU_result_1154rcrl %eax155156L_no_overflow:157jmp LRound_precision /* Do the rounding as required */158159160/*---------------------------------------------------------------------------+161| Divide: Return arg1/arg2 to arg3. |162| |163| This routine does not use the exponents of arg1 and arg2, but does |164| adjust the exponent of arg3. |165| |166| The maximum returned value is (ignoring exponents) |167| .ffffffff ffffffff |168| ------------------ = 1.ffffffff fffffffe |169| .80000000 00000000 |170| and the minimum is |171| .80000000 00000000 |172| ------------------ = .80000000 00000001 (rounded) |173| .ffffffff ffffffff |174| |175+---------------------------------------------------------------------------*/176177178L_Full_Division:179/* Save extended dividend in local register */180movl SIGL(%esi),%eax181movl %eax,FPU_accum_2182movl SIGH(%esi),%eax183movl %eax,FPU_accum_3184xorl %eax,%eax185movl %eax,FPU_accum_1 /* zero the extension */186movl %eax,FPU_accum_0 /* zero the extension */187188movl SIGL(%esi),%eax /* Get the current num */189movl SIGH(%esi),%edx190191/*----------------------------------------------------------------------*/192/* Initialization done.193Do the first 32 bits. */194195movb $0,FPU_ovfl_flag196cmpl SIGH(%ebx),%edx /* Test for imminent overflow */197jb LLess_than_1198ja LGreater_than_1199200cmpl SIGL(%ebx),%eax201jb LLess_than_1202203LGreater_than_1:204/* The dividend is greater or equal, would cause overflow */205setaeb FPU_ovfl_flag /* Keep a record */206207subl SIGL(%ebx),%eax208sbbl SIGH(%ebx),%edx /* Prevent the overflow */209movl %eax,FPU_accum_2210movl %edx,FPU_accum_3211212LLess_than_1:213/* At this point, we have a dividend < divisor, with a record of214adjustment in FPU_ovfl_flag */215216/* We will divide by a number which is too large */217movl SIGH(%ebx),%ecx218addl $1,%ecx219jnc LFirst_div_not_1220221/* here we need to divide by 100000000h,222i.e., no division at all.. */223mov %edx,%eax224jmp LFirst_div_done225226LFirst_div_not_1:227divl %ecx /* Divide the numerator by the augmented228denom ms dw */229230LFirst_div_done:231movl %eax,FPU_result_2 /* Put the result in the answer */232233mull SIGH(%ebx) /* mul by the ms dw of the denom */234235subl %eax,FPU_accum_2 /* Subtract from the num local reg */236sbbl %edx,FPU_accum_3237238movl FPU_result_2,%eax /* Get the result back */239mull SIGL(%ebx) /* now mul the ls dw of the denom */240241subl %eax,FPU_accum_1 /* Subtract from the num local reg */242sbbl %edx,FPU_accum_2243sbbl $0,FPU_accum_3244je LDo_2nd_32_bits /* Must check for non-zero result here */245246#ifdef PARANOID247jb L_bugged_1248#endif /* PARANOID */249250/* need to subtract another once of the denom */251incl FPU_result_2 /* Correct the answer */252253movl SIGL(%ebx),%eax254movl SIGH(%ebx),%edx255subl %eax,FPU_accum_1 /* Subtract from the num local reg */256sbbl %edx,FPU_accum_2257258#ifdef PARANOID259sbbl $0,FPU_accum_3260jne L_bugged_1 /* Must check for non-zero result here */261#endif /* PARANOID */262263/*----------------------------------------------------------------------*/264/* Half of the main problem is done, there is just a reduced numerator265to handle now.266Work with the second 32 bits, FPU_accum_0 not used from now on */267LDo_2nd_32_bits:268movl FPU_accum_2,%edx /* get the reduced num */269movl FPU_accum_1,%eax270271/* need to check for possible subsequent overflow */272cmpl SIGH(%ebx),%edx273jb LDo_2nd_div274ja LPrevent_2nd_overflow275276cmpl SIGL(%ebx),%eax277jb LDo_2nd_div278279LPrevent_2nd_overflow:280/* The numerator is greater or equal, would cause overflow */281/* prevent overflow */282subl SIGL(%ebx),%eax283sbbl SIGH(%ebx),%edx284movl %edx,FPU_accum_2285movl %eax,FPU_accum_1286287incl FPU_result_2 /* Reflect the subtraction in the answer */288289#ifdef PARANOID290je L_bugged_2 /* Can't bump the result to 1.0 */291#endif /* PARANOID */292293LDo_2nd_div:294cmpl $0,%ecx /* augmented denom msw */295jnz LSecond_div_not_1296297/* %ecx == 0, we are dividing by 1.0 */298mov %edx,%eax299jmp LSecond_div_done300301LSecond_div_not_1:302divl %ecx /* Divide the numerator by the denom ms dw */303304LSecond_div_done:305movl %eax,FPU_result_1 /* Put the result in the answer */306307mull SIGH(%ebx) /* mul by the ms dw of the denom */308309subl %eax,FPU_accum_1 /* Subtract from the num local reg */310sbbl %edx,FPU_accum_2311312#ifdef PARANOID313jc L_bugged_2314#endif /* PARANOID */315316movl FPU_result_1,%eax /* Get the result back */317mull SIGL(%ebx) /* now mul the ls dw of the denom */318319subl %eax,FPU_accum_0 /* Subtract from the num local reg */320sbbl %edx,FPU_accum_1 /* Subtract from the num local reg */321sbbl $0,FPU_accum_2322323#ifdef PARANOID324jc L_bugged_2325#endif /* PARANOID */326327jz LDo_3rd_32_bits328329#ifdef PARANOID330cmpl $1,FPU_accum_2331jne L_bugged_2332#endif /* PARANOID */333334/* need to subtract another once of the denom */335movl SIGL(%ebx),%eax336movl SIGH(%ebx),%edx337subl %eax,FPU_accum_0 /* Subtract from the num local reg */338sbbl %edx,FPU_accum_1339sbbl $0,FPU_accum_2340341#ifdef PARANOID342jc L_bugged_2343jne L_bugged_2344#endif /* PARANOID */345346addl $1,FPU_result_1 /* Correct the answer */347adcl $0,FPU_result_2348349#ifdef PARANOID350jc L_bugged_2 /* Must check for non-zero result here */351#endif /* PARANOID */352353/*----------------------------------------------------------------------*/354/* The division is essentially finished here, we just need to perform355tidying operations.356Deal with the 3rd 32 bits */357LDo_3rd_32_bits:358movl FPU_accum_1,%edx /* get the reduced num */359movl FPU_accum_0,%eax360361/* need to check for possible subsequent overflow */362cmpl SIGH(%ebx),%edx /* denom */363jb LRound_prep364ja LPrevent_3rd_overflow365366cmpl SIGL(%ebx),%eax /* denom */367jb LRound_prep368369LPrevent_3rd_overflow:370/* prevent overflow */371subl SIGL(%ebx),%eax372sbbl SIGH(%ebx),%edx373movl %edx,FPU_accum_1374movl %eax,FPU_accum_0375376addl $1,FPU_result_1 /* Reflect the subtraction in the answer */377adcl $0,FPU_result_2378jne LRound_prep379jnc LRound_prep380381/* This is a tricky spot, there is an overflow of the answer */382movb $255,FPU_ovfl_flag /* Overflow -> 1.000 */383384LRound_prep:385/*386* Prepare for rounding.387* To test for rounding, we just need to compare 2*accum with the388* denom.389*/390movl FPU_accum_0,%ecx391movl FPU_accum_1,%edx392movl %ecx,%eax393orl %edx,%eax394jz LRound_ovfl /* The accumulator contains zero. */395396/* Multiply by 2 */397clc398rcll $1,%ecx399rcll $1,%edx400jc LRound_large /* No need to compare, denom smaller */401402subl SIGL(%ebx),%ecx403sbbl SIGH(%ebx),%edx404jnc LRound_not_small405406movl $0x70000000,%eax /* Denom was larger */407jmp LRound_ovfl408409LRound_not_small:410jnz LRound_large411412movl $0x80000000,%eax /* Remainder was exactly 1/2 denom */413jmp LRound_ovfl414415LRound_large:416movl $0xff000000,%eax /* Denom was smaller */417418LRound_ovfl:419/* We are now ready to deal with rounding, but first we must get420the bits properly aligned */421testb $255,FPU_ovfl_flag /* was the num > denom ? */422je LRound_precision423424incw EXP(%edi)425426/* shift the mantissa right one bit */427stc /* Will set the ms bit */428rcrl FPU_result_2429rcrl FPU_result_1430rcrl %eax431432/* Round the result as required */433LRound_precision:434decw EXP(%edi) /* binary point between 1st & 2nd bits */435436movl %eax,%edx437movl FPU_result_1,%ebx438movl FPU_result_2,%eax439jmp fpu_reg_round440441442#ifdef PARANOID443/* The logic is wrong if we got here */444L_bugged:445pushl EX_INTERNAL|0x202446call EXCEPTION447pop %ebx448jmp L_exit449450L_bugged_1:451pushl EX_INTERNAL|0x203452call EXCEPTION453pop %ebx454jmp L_exit455456L_bugged_2:457pushl EX_INTERNAL|0x204458call EXCEPTION459pop %ebx460jmp L_exit461462L_exit:463movl $-1,%eax464popl %ebx465popl %edi466popl %esi467468leave469ret470#endif /* PARANOID */471472473