/* SPDX-License-Identifier: GPL-2.0 */1.file "reg_u_div.S"2/*---------------------------------------------------------------------------+3| reg_u_div.S |4| |5| Divide one FPU_REG by another and put the result in a destination FPU_REG.|6| |7| Copyright (C) 1992,1993,1995,1997 |8| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |9| E-mail [email protected] |10| |11| |12+---------------------------------------------------------------------------*/1314/*---------------------------------------------------------------------------+15| Call from C as: |16| int FPU_u_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest, |17| unsigned int control_word, char *sign) |18| |19| Does not compute the destination exponent, but does adjust it. |20| |21| Return value is the tag of the answer, or-ed with FPU_Exception if |22| one was raised, or -1 on internal error. |23+---------------------------------------------------------------------------*/2425#include "exception.h"26#include "fpu_emu.h"27#include "control_w.h"282930/* #define dSIGL(x) (x) */31/* #define dSIGH(x) 4(x) */323334#ifndef NON_REENTRANT_FPU35/*36Local storage on the stack:37Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_038Overflow flag: ovfl_flag39*/40#define FPU_accum_3 -4(%ebp)41#define FPU_accum_2 -8(%ebp)42#define FPU_accum_1 -12(%ebp)43#define FPU_accum_0 -16(%ebp)44#define FPU_result_1 -20(%ebp)45#define FPU_result_2 -24(%ebp)46#define FPU_ovfl_flag -28(%ebp)4748#else49.data50/*51Local storage in a static area:52Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_053Overflow flag: ovfl_flag54*/55.align 4,056FPU_accum_3:57.long 058FPU_accum_2:59.long 060FPU_accum_1:61.long 062FPU_accum_0:63.long 064FPU_result_1:65.long 066FPU_result_2:67.long 068FPU_ovfl_flag:69.byte 070#endif /* NON_REENTRANT_FPU */7172#define REGA PARAM173#define REGB PARAM274#define DEST PARAM37576.text77SYM_FUNC_START(FPU_u_div)78pushl %ebp79movl %esp,%ebp80#ifndef NON_REENTRANT_FPU81subl $28,%esp82#endif /* NON_REENTRANT_FPU */8384pushl %esi85pushl %edi86pushl %ebx8788movl REGA,%esi89movl REGB,%ebx90movl DEST,%edi9192movswl EXP(%esi),%edx93movswl EXP(%ebx),%eax94subl %eax,%edx95addl EXP_BIAS,%edx9697/* A denormal and a large number can cause an exponent underflow */98cmpl EXP_WAY_UNDER,%edx99jg xExp_not_underflow100101/* Set to a really low value allow correct handling */102movl EXP_WAY_UNDER,%edx103104xExp_not_underflow:105106movw %dx,EXP(%edi)107108#ifdef PARANOID109/* testl $0x80000000, SIGH(%esi) // Dividend */110/* je L_bugged */111testl $0x80000000, SIGH(%ebx) /* Divisor */112je L_bugged113#endif /* PARANOID */114115/* Check if the divisor can be treated as having just 32 bits */116cmpl $0,SIGL(%ebx)117jnz L_Full_Division /* Can't do a quick divide */118119/* We should be able to zip through the division here */120movl SIGH(%ebx),%ecx /* The divisor */121movl SIGH(%esi),%edx /* Dividend */122movl SIGL(%esi),%eax /* Dividend */123124cmpl %ecx,%edx125setaeb FPU_ovfl_flag /* Keep a record */126jb L_no_adjust127128subl %ecx,%edx /* Prevent the overflow */129130L_no_adjust:131/* Divide the 64 bit number by the 32 bit denominator */132divl %ecx133movl %eax,FPU_result_2134135/* Work on the remainder of the first division */136xorl %eax,%eax137divl %ecx138movl %eax,FPU_result_1139140/* Work on the remainder of the 64 bit division */141xorl %eax,%eax142divl %ecx143144testb $255,FPU_ovfl_flag /* was the num > denom ? */145je L_no_overflow146147/* Do the shifting here */148/* increase the exponent */149incw EXP(%edi)150151/* shift the mantissa right one bit */152stc /* To set the ms bit */153rcrl FPU_result_2154rcrl FPU_result_1155rcrl %eax156157L_no_overflow:158jmp LRound_precision /* Do the rounding as required */159160161/*---------------------------------------------------------------------------+162| Divide: Return arg1/arg2 to arg3. |163| |164| This routine does not use the exponents of arg1 and arg2, but does |165| adjust the exponent of arg3. |166| |167| The maximum returned value is (ignoring exponents) |168| .ffffffff ffffffff |169| ------------------ = 1.ffffffff fffffffe |170| .80000000 00000000 |171| and the minimum is |172| .80000000 00000000 |173| ------------------ = .80000000 00000001 (rounded) |174| .ffffffff ffffffff |175| |176+---------------------------------------------------------------------------*/177178179L_Full_Division:180/* Save extended dividend in local register */181movl SIGL(%esi),%eax182movl %eax,FPU_accum_2183movl SIGH(%esi),%eax184movl %eax,FPU_accum_3185xorl %eax,%eax186movl %eax,FPU_accum_1 /* zero the extension */187movl %eax,FPU_accum_0 /* zero the extension */188189movl SIGL(%esi),%eax /* Get the current num */190movl SIGH(%esi),%edx191192/*----------------------------------------------------------------------*/193/* Initialization done.194Do the first 32 bits. */195196movb $0,FPU_ovfl_flag197cmpl SIGH(%ebx),%edx /* Test for imminent overflow */198jb LLess_than_1199ja LGreater_than_1200201cmpl SIGL(%ebx),%eax202jb LLess_than_1203204LGreater_than_1:205/* The dividend is greater or equal, would cause overflow */206setaeb FPU_ovfl_flag /* Keep a record */207208subl SIGL(%ebx),%eax209sbbl SIGH(%ebx),%edx /* Prevent the overflow */210movl %eax,FPU_accum_2211movl %edx,FPU_accum_3212213LLess_than_1:214/* At this point, we have a dividend < divisor, with a record of215adjustment in FPU_ovfl_flag */216217/* We will divide by a number which is too large */218movl SIGH(%ebx),%ecx219addl $1,%ecx220jnc LFirst_div_not_1221222/* here we need to divide by 100000000h,223i.e., no division at all.. */224mov %edx,%eax225jmp LFirst_div_done226227LFirst_div_not_1:228divl %ecx /* Divide the numerator by the augmented229denom ms dw */230231LFirst_div_done:232movl %eax,FPU_result_2 /* Put the result in the answer */233234mull SIGH(%ebx) /* mul by the ms dw of the denom */235236subl %eax,FPU_accum_2 /* Subtract from the num local reg */237sbbl %edx,FPU_accum_3238239movl FPU_result_2,%eax /* Get the result back */240mull SIGL(%ebx) /* now mul the ls dw of the denom */241242subl %eax,FPU_accum_1 /* Subtract from the num local reg */243sbbl %edx,FPU_accum_2244sbbl $0,FPU_accum_3245je LDo_2nd_32_bits /* Must check for non-zero result here */246247#ifdef PARANOID248jb L_bugged_1249#endif /* PARANOID */250251/* need to subtract another once of the denom */252incl FPU_result_2 /* Correct the answer */253254movl SIGL(%ebx),%eax255movl SIGH(%ebx),%edx256subl %eax,FPU_accum_1 /* Subtract from the num local reg */257sbbl %edx,FPU_accum_2258259#ifdef PARANOID260sbbl $0,FPU_accum_3261jne L_bugged_1 /* Must check for non-zero result here */262#endif /* PARANOID */263264/*----------------------------------------------------------------------*/265/* Half of the main problem is done, there is just a reduced numerator266to handle now.267Work with the second 32 bits, FPU_accum_0 not used from now on */268LDo_2nd_32_bits:269movl FPU_accum_2,%edx /* get the reduced num */270movl FPU_accum_1,%eax271272/* need to check for possible subsequent overflow */273cmpl SIGH(%ebx),%edx274jb LDo_2nd_div275ja LPrevent_2nd_overflow276277cmpl SIGL(%ebx),%eax278jb LDo_2nd_div279280LPrevent_2nd_overflow:281/* The numerator is greater or equal, would cause overflow */282/* prevent overflow */283subl SIGL(%ebx),%eax284sbbl SIGH(%ebx),%edx285movl %edx,FPU_accum_2286movl %eax,FPU_accum_1287288incl FPU_result_2 /* Reflect the subtraction in the answer */289290#ifdef PARANOID291je L_bugged_2 /* Can't bump the result to 1.0 */292#endif /* PARANOID */293294LDo_2nd_div:295cmpl $0,%ecx /* augmented denom msw */296jnz LSecond_div_not_1297298/* %ecx == 0, we are dividing by 1.0 */299mov %edx,%eax300jmp LSecond_div_done301302LSecond_div_not_1:303divl %ecx /* Divide the numerator by the denom ms dw */304305LSecond_div_done:306movl %eax,FPU_result_1 /* Put the result in the answer */307308mull SIGH(%ebx) /* mul by the ms dw of the denom */309310subl %eax,FPU_accum_1 /* Subtract from the num local reg */311sbbl %edx,FPU_accum_2312313#ifdef PARANOID314jc L_bugged_2315#endif /* PARANOID */316317movl FPU_result_1,%eax /* Get the result back */318mull SIGL(%ebx) /* now mul the ls dw of the denom */319320subl %eax,FPU_accum_0 /* Subtract from the num local reg */321sbbl %edx,FPU_accum_1 /* Subtract from the num local reg */322sbbl $0,FPU_accum_2323324#ifdef PARANOID325jc L_bugged_2326#endif /* PARANOID */327328jz LDo_3rd_32_bits329330#ifdef PARANOID331cmpl $1,FPU_accum_2332jne L_bugged_2333#endif /* PARANOID */334335/* need to subtract another once of the denom */336movl SIGL(%ebx),%eax337movl SIGH(%ebx),%edx338subl %eax,FPU_accum_0 /* Subtract from the num local reg */339sbbl %edx,FPU_accum_1340sbbl $0,FPU_accum_2341342#ifdef PARANOID343jc L_bugged_2344jne L_bugged_2345#endif /* PARANOID */346347addl $1,FPU_result_1 /* Correct the answer */348adcl $0,FPU_result_2349350#ifdef PARANOID351jc L_bugged_2 /* Must check for non-zero result here */352#endif /* PARANOID */353354/*----------------------------------------------------------------------*/355/* The division is essentially finished here, we just need to perform356tidying operations.357Deal with the 3rd 32 bits */358LDo_3rd_32_bits:359movl FPU_accum_1,%edx /* get the reduced num */360movl FPU_accum_0,%eax361362/* need to check for possible subsequent overflow */363cmpl SIGH(%ebx),%edx /* denom */364jb LRound_prep365ja LPrevent_3rd_overflow366367cmpl SIGL(%ebx),%eax /* denom */368jb LRound_prep369370LPrevent_3rd_overflow:371/* prevent overflow */372subl SIGL(%ebx),%eax373sbbl SIGH(%ebx),%edx374movl %edx,FPU_accum_1375movl %eax,FPU_accum_0376377addl $1,FPU_result_1 /* Reflect the subtraction in the answer */378adcl $0,FPU_result_2379jne LRound_prep380jnc LRound_prep381382/* This is a tricky spot, there is an overflow of the answer */383movb $255,FPU_ovfl_flag /* Overflow -> 1.000 */384385LRound_prep:386/*387* Prepare for rounding.388* To test for rounding, we just need to compare 2*accum with the389* denom.390*/391movl FPU_accum_0,%ecx392movl FPU_accum_1,%edx393movl %ecx,%eax394orl %edx,%eax395jz LRound_ovfl /* The accumulator contains zero. */396397/* Multiply by 2 */398clc399rcll $1,%ecx400rcll $1,%edx401jc LRound_large /* No need to compare, denom smaller */402403subl SIGL(%ebx),%ecx404sbbl SIGH(%ebx),%edx405jnc LRound_not_small406407movl $0x70000000,%eax /* Denom was larger */408jmp LRound_ovfl409410LRound_not_small:411jnz LRound_large412413movl $0x80000000,%eax /* Remainder was exactly 1/2 denom */414jmp LRound_ovfl415416LRound_large:417movl $0xff000000,%eax /* Denom was smaller */418419LRound_ovfl:420/* We are now ready to deal with rounding, but first we must get421the bits properly aligned */422testb $255,FPU_ovfl_flag /* was the num > denom ? */423je LRound_precision424425incw EXP(%edi)426427/* shift the mantissa right one bit */428stc /* Will set the ms bit */429rcrl FPU_result_2430rcrl FPU_result_1431rcrl %eax432433/* Round the result as required */434LRound_precision:435decw EXP(%edi) /* binary point between 1st & 2nd bits */436437movl %eax,%edx438movl FPU_result_1,%ebx439movl FPU_result_2,%eax440jmp fpu_reg_round441442443#ifdef PARANOID444/* The logic is wrong if we got here */445L_bugged:446pushl EX_INTERNAL|0x202447call EXCEPTION448pop %ebx449jmp L_exit450451L_bugged_1:452pushl EX_INTERNAL|0x203453call EXCEPTION454pop %ebx455jmp L_exit456457L_bugged_2:458pushl EX_INTERNAL|0x204459call EXCEPTION460pop %ebx461jmp L_exit462463L_exit:464movl $-1,%eax465popl %ebx466popl %edi467popl %esi468469leave470RET471#endif /* PARANOID */472473SYM_FUNC_END(FPU_u_div)474475476