/* SPDX-License-Identifier: GPL-2.0 */1.file "reg_u_sub.S"2/*---------------------------------------------------------------------------+3| reg_u_sub.S |4| |5| Core floating point subtraction routine. |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| Call from C as: |12| int FPU_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ, |13| int control_w) |14| Return value is the tag of the answer, or-ed with FPU_Exception if |15| one was raised, or -1 on internal error. |16| |17+---------------------------------------------------------------------------*/1819/*20| Kernel subtraction routine FPU_u_sub(reg *arg1, reg *arg2, reg *answ).21| Takes two valid reg f.p. numbers (TAG_Valid), which are22| treated as unsigned numbers,23| and returns their difference as a TAG_Valid or TAG_Zero f.p.24| number.25| The first number (arg1) must be the larger.26| The returned number is normalized.27| Basic checks are performed if PARANOID is defined.28*/2930#include "exception.h"31#include "fpu_emu.h"32#include "control_w.h"3334.text35SYM_FUNC_START(FPU_u_sub)36pushl %ebp37movl %esp,%ebp38pushl %esi39pushl %edi40pushl %ebx4142movl PARAM1,%esi /* source 1 */43movl PARAM2,%edi /* source 2 */4445movl PARAM6,%ecx46subl PARAM7,%ecx /* exp1 - exp2 */4748#ifdef PARANOID49/* source 2 is always smaller than source 1 */50js L_bugged_15152testl $0x80000000,SIGH(%edi) /* The args are assumed to be be normalized */53je L_bugged_25455testl $0x80000000,SIGH(%esi)56je L_bugged_257#endif /* PARANOID */5859/*--------------------------------------+60| Form a register holding the |61| smaller number |62+--------------------------------------*/63movl SIGH(%edi),%eax /* register ms word */64movl SIGL(%edi),%ebx /* register ls word */6566movl PARAM3,%edi /* destination */67movl PARAM6,%edx68movw %dx,EXP(%edi) /* Copy exponent to destination */6970xorl %edx,%edx /* register extension */7172/*--------------------------------------+73| Shift the temporary register |74| right the required number of |75| places. |76+--------------------------------------*/7778cmpw $32,%cx /* shrd only works for 0..31 bits */79jnc L_more_than_318081/* less than 32 bits */82shrd %cl,%ebx,%edx83shrd %cl,%eax,%ebx84shr %cl,%eax85jmp L_shift_done8687L_more_than_31:88cmpw $64,%cx89jnc L_more_than_639091subb $32,%cl92jz L_exactly_329394shrd %cl,%eax,%edx95shr %cl,%eax96orl %ebx,%ebx97jz L_more_31_no_low /* none of the lowest bits is set */9899orl $1,%edx /* record the fact in the extension */100101L_more_31_no_low:102movl %eax,%ebx103xorl %eax,%eax104jmp L_shift_done105106L_exactly_32:107movl %ebx,%edx108movl %eax,%ebx109xorl %eax,%eax110jmp L_shift_done111112L_more_than_63:113cmpw $65,%cx114jnc L_more_than_64115116/* Shift right by 64 bits */117movl %eax,%edx118orl %ebx,%ebx119jz L_more_63_no_low120121orl $1,%edx122jmp L_more_63_no_low123124L_more_than_64:125jne L_more_than_65126127/* Shift right by 65 bits */128/* Carry is clear if we get here */129movl %eax,%edx130rcrl %edx131jnc L_shift_65_nc132133orl $1,%edx134jmp L_more_63_no_low135136L_shift_65_nc:137orl %ebx,%ebx138jz L_more_63_no_low139140orl $1,%edx141jmp L_more_63_no_low142143L_more_than_65:144movl $1,%edx /* The shifted nr always at least one '1' */145146L_more_63_no_low:147xorl %ebx,%ebx148xorl %eax,%eax149150L_shift_done:151L_subtr:152/*------------------------------+153| Do the subtraction |154+------------------------------*/155xorl %ecx,%ecx156subl %edx,%ecx157movl %ecx,%edx158movl SIGL(%esi),%ecx159sbbl %ebx,%ecx160movl %ecx,%ebx161movl SIGH(%esi),%ecx162sbbl %eax,%ecx163movl %ecx,%eax164165#ifdef PARANOID166/* We can never get a borrow */167jc L_bugged168#endif /* PARANOID */169170/*--------------------------------------+171| Normalize the result |172+--------------------------------------*/173testl $0x80000000,%eax174jnz L_round /* no shifting needed */175176orl %eax,%eax177jnz L_shift_1 /* shift left 1 - 31 bits */178179orl %ebx,%ebx180jnz L_shift_32 /* shift left 32 - 63 bits */181182/*183* A rare case, the only one which is non-zero if we got here184* is: 1000000 .... 0000185* -0111111 .... 1111 1186* --------------------187* 0000000 .... 0000 1188*/189190cmpl $0x80000000,%edx191jnz L_must_be_zero192193/* Shift left 64 bits */194subw $64,EXP(%edi)195xchg %edx,%eax196jmp fpu_reg_round197198L_must_be_zero:199#ifdef PARANOID200orl %edx,%edx201jnz L_bugged_3202#endif /* PARANOID */203204/* The result is zero */205movw $0,EXP(%edi) /* exponent */206movl $0,SIGL(%edi)207movl $0,SIGH(%edi)208movl TAG_Zero,%eax209jmp L_exit210211L_shift_32:212movl %ebx,%eax213movl %edx,%ebx214movl $0,%edx215subw $32,EXP(%edi) /* Can get underflow here */216217/* We need to shift left by 1 - 31 bits */218L_shift_1:219bsrl %eax,%ecx /* get the required shift in %ecx */220subl $31,%ecx221negl %ecx222shld %cl,%ebx,%eax223shld %cl,%edx,%ebx224shl %cl,%edx225subw %cx,EXP(%edi) /* Can get underflow here */226227L_round:228jmp fpu_reg_round /* Round the result */229230231#ifdef PARANOID232L_bugged_1:233pushl EX_INTERNAL|0x206234call EXCEPTION235pop %ebx236jmp L_error_exit237238L_bugged_2:239pushl EX_INTERNAL|0x209240call EXCEPTION241pop %ebx242jmp L_error_exit243244L_bugged_3:245pushl EX_INTERNAL|0x210246call EXCEPTION247pop %ebx248jmp L_error_exit249250L_bugged_4:251pushl EX_INTERNAL|0x211252call EXCEPTION253pop %ebx254jmp L_error_exit255256L_bugged:257pushl EX_INTERNAL|0x212258call EXCEPTION259pop %ebx260jmp L_error_exit261262L_error_exit:263movl $-1,%eax264265#endif /* PARANOID */266267L_exit:268popl %ebx269popl %edi270popl %esi271leave272RET273SYM_FUNC_END(FPU_u_sub)274275276