GNU Linux-libre 4.14.266-gnu1
[releases.git] / arch / x86 / math-emu / reg_u_sub.S
1 /* SPDX-License-Identifier: GPL-2.0 */
2         .file   "reg_u_sub.S"
3 /*---------------------------------------------------------------------------+
4  |  reg_u_sub.S                                                              |
5  |                                                                           |
6  | Core floating point subtraction routine.                                  |
7  |                                                                           |
8  | Copyright (C) 1992,1993,1995,1997                                         |
9  |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
10  |                  E-mail   billm@suburbia.net                              |
11  |                                                                           |
12  | Call from C as:                                                           |
13  |    int FPU_u_sub(FPU_REG *arg1, FPU_REG *arg2, FPU_REG *answ,             |
14  |                                                int control_w)             |
15  |    Return value is the tag of the answer, or-ed with FPU_Exception if     |
16  |    one was raised, or -1 on internal error.                               |
17  |                                                                           |
18  +---------------------------------------------------------------------------*/
19
20 /*
21  |    Kernel subtraction routine FPU_u_sub(reg *arg1, reg *arg2, reg *answ).
22  |    Takes two valid reg f.p. numbers (TAG_Valid), which are
23  |    treated as unsigned numbers,
24  |    and returns their difference as a TAG_Valid or TAG_Zero f.p.
25  |    number.
26  |    The first number (arg1) must be the larger.
27  |    The returned number is normalized.
28  |    Basic checks are performed if PARANOID is defined.
29  */
30
31 #include "exception.h"
32 #include "fpu_emu.h"
33 #include "control_w.h"
34
35 .text
36 ENTRY(FPU_u_sub)
37         pushl   %ebp
38         movl    %esp,%ebp
39         pushl   %esi
40         pushl   %edi
41         pushl   %ebx
42
43         movl    PARAM1,%esi     /* source 1 */
44         movl    PARAM2,%edi     /* source 2 */
45         
46         movl    PARAM6,%ecx
47         subl    PARAM7,%ecx     /* exp1 - exp2 */
48
49 #ifdef PARANOID
50         /* source 2 is always smaller than source 1 */
51         js      L_bugged_1
52
53         testl   $0x80000000,SIGH(%edi)  /* The args are assumed to be be normalized */
54         je      L_bugged_2
55
56         testl   $0x80000000,SIGH(%esi)
57         je      L_bugged_2
58 #endif /* PARANOID */
59
60 /*--------------------------------------+
61  |      Form a register holding the     |
62  |      smaller number                  |
63  +--------------------------------------*/
64         movl    SIGH(%edi),%eax /* register ms word */
65         movl    SIGL(%edi),%ebx /* register ls word */
66
67         movl    PARAM3,%edi     /* destination */
68         movl    PARAM6,%edx
69         movw    %dx,EXP(%edi)   /* Copy exponent to destination */
70
71         xorl    %edx,%edx       /* register extension */
72
73 /*--------------------------------------+
74  |      Shift the temporary register    |
75  |      right the required number of    |
76  |      places.                         |
77  +--------------------------------------*/
78
79         cmpw    $32,%cx         /* shrd only works for 0..31 bits */
80         jnc     L_more_than_31
81
82 /* less than 32 bits */
83         shrd    %cl,%ebx,%edx
84         shrd    %cl,%eax,%ebx
85         shr     %cl,%eax
86         jmp     L_shift_done
87
88 L_more_than_31:
89         cmpw    $64,%cx
90         jnc     L_more_than_63
91
92         subb    $32,%cl
93         jz      L_exactly_32
94
95         shrd    %cl,%eax,%edx
96         shr     %cl,%eax
97         orl     %ebx,%ebx
98         jz      L_more_31_no_low        /* none of the lowest bits is set */
99
100         orl     $1,%edx                 /* record the fact in the extension */
101
102 L_more_31_no_low:
103         movl    %eax,%ebx
104         xorl    %eax,%eax
105         jmp     L_shift_done
106
107 L_exactly_32:
108         movl    %ebx,%edx
109         movl    %eax,%ebx
110         xorl    %eax,%eax
111         jmp     L_shift_done
112
113 L_more_than_63:
114         cmpw    $65,%cx
115         jnc     L_more_than_64
116
117         /* Shift right by 64 bits */
118         movl    %eax,%edx
119         orl     %ebx,%ebx
120         jz      L_more_63_no_low
121
122         orl     $1,%edx
123         jmp     L_more_63_no_low
124
125 L_more_than_64:
126         jne     L_more_than_65
127
128         /* Shift right by 65 bits */
129         /* Carry is clear if we get here */
130         movl    %eax,%edx
131         rcrl    %edx
132         jnc     L_shift_65_nc
133
134         orl     $1,%edx
135         jmp     L_more_63_no_low
136
137 L_shift_65_nc:
138         orl     %ebx,%ebx
139         jz      L_more_63_no_low
140
141         orl     $1,%edx
142         jmp     L_more_63_no_low
143
144 L_more_than_65:
145         movl    $1,%edx         /* The shifted nr always at least one '1' */
146
147 L_more_63_no_low:
148         xorl    %ebx,%ebx
149         xorl    %eax,%eax
150
151 L_shift_done:
152 L_subtr:
153 /*------------------------------+
154  |      Do the subtraction      |
155  +------------------------------*/
156         xorl    %ecx,%ecx
157         subl    %edx,%ecx
158         movl    %ecx,%edx
159         movl    SIGL(%esi),%ecx
160         sbbl    %ebx,%ecx
161         movl    %ecx,%ebx
162         movl    SIGH(%esi),%ecx
163         sbbl    %eax,%ecx
164         movl    %ecx,%eax
165
166 #ifdef PARANOID
167         /* We can never get a borrow */
168         jc      L_bugged
169 #endif /* PARANOID */
170
171 /*--------------------------------------+
172  |      Normalize the result            |
173  +--------------------------------------*/
174         testl   $0x80000000,%eax
175         jnz     L_round         /* no shifting needed */
176
177         orl     %eax,%eax
178         jnz     L_shift_1       /* shift left 1 - 31 bits */
179
180         orl     %ebx,%ebx
181         jnz     L_shift_32      /* shift left 32 - 63 bits */
182
183 /*
184  *       A rare case, the only one which is non-zero if we got here
185  *         is:           1000000 .... 0000
186  *                      -0111111 .... 1111 1
187  *                       -------------------- 
188  *                       0000000 .... 0000 1 
189  */
190
191         cmpl    $0x80000000,%edx
192         jnz     L_must_be_zero
193
194         /* Shift left 64 bits */
195         subw    $64,EXP(%edi)
196         xchg    %edx,%eax
197         jmp     fpu_reg_round
198
199 L_must_be_zero:
200 #ifdef PARANOID
201         orl     %edx,%edx
202         jnz     L_bugged_3
203 #endif /* PARANOID */ 
204
205         /* The result is zero */
206         movw    $0,EXP(%edi)            /* exponent */
207         movl    $0,SIGL(%edi)
208         movl    $0,SIGH(%edi)
209         movl    TAG_Zero,%eax
210         jmp     L_exit
211
212 L_shift_32:
213         movl    %ebx,%eax
214         movl    %edx,%ebx
215         movl    $0,%edx
216         subw    $32,EXP(%edi)   /* Can get underflow here */
217
218 /* We need to shift left by 1 - 31 bits */
219 L_shift_1:
220         bsrl    %eax,%ecx       /* get the required shift in %ecx */
221         subl    $31,%ecx
222         negl    %ecx
223         shld    %cl,%ebx,%eax
224         shld    %cl,%edx,%ebx
225         shl     %cl,%edx
226         subw    %cx,EXP(%edi)   /* Can get underflow here */
227
228 L_round:
229         jmp     fpu_reg_round   /* Round the result */
230
231
232 #ifdef PARANOID
233 L_bugged_1:
234         pushl   EX_INTERNAL|0x206
235         call    EXCEPTION
236         pop     %ebx
237         jmp     L_error_exit
238
239 L_bugged_2:
240         pushl   EX_INTERNAL|0x209
241         call    EXCEPTION
242         pop     %ebx
243         jmp     L_error_exit
244
245 L_bugged_3:
246         pushl   EX_INTERNAL|0x210
247         call    EXCEPTION
248         pop     %ebx
249         jmp     L_error_exit
250
251 L_bugged_4:
252         pushl   EX_INTERNAL|0x211
253         call    EXCEPTION
254         pop     %ebx
255         jmp     L_error_exit
256
257 L_bugged:
258         pushl   EX_INTERNAL|0x212
259         call    EXCEPTION
260         pop     %ebx
261         jmp     L_error_exit
262
263 L_error_exit:
264         movl    $-1,%eax
265
266 #endif /* PARANOID */
267
268 L_exit:
269         popl    %ebx
270         popl    %edi
271         popl    %esi
272         leave
273         ret
274 ENDPROC(FPU_u_sub)