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
36SYM_FUNC_START(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
88L_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
102L_more_31_no_low:
103 movl %eax,%ebx
104 xorl %eax,%eax
105 jmp L_shift_done
106
107L_exactly_32:
108 movl %ebx,%edx
109 movl %eax,%ebx
110 xorl %eax,%eax
111 jmp L_shift_done
112
113L_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
125L_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
137L_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
144L_more_than_65:
145 movl $1,%edx /* The shifted nr always at least one '1' */
146
147L_more_63_no_low:
148 xorl %ebx,%ebx
149 xorl %eax,%eax
150
151L_shift_done:
152L_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
199L_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
212L_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 */
219L_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
228L_round:
229 jmp fpu_reg_round /* Round the result */
230
231
232#ifdef PARANOID
233L_bugged_1:
234 pushl EX_INTERNAL|0x206
235 call EXCEPTION
236 pop %ebx
237 jmp L_error_exit
238
239L_bugged_2:
240 pushl EX_INTERNAL|0x209
241 call EXCEPTION
242 pop %ebx
243 jmp L_error_exit
244
245L_bugged_3:
246 pushl EX_INTERNAL|0x210
247 call EXCEPTION
248 pop %ebx
249 jmp L_error_exit
250
251L_bugged_4:
252 pushl EX_INTERNAL|0x211
253 call EXCEPTION
254 pop %ebx
255 jmp L_error_exit
256
257L_bugged:
258 pushl EX_INTERNAL|0x212
259 call EXCEPTION
260 pop %ebx
261 jmp L_error_exit
262
263L_error_exit:
264 movl $-1,%eax
265
266#endif /* PARANOID */
267
268L_exit:
269 popl %ebx
270 popl %edi
271 popl %esi
272 leave
273 RET
274SYM_FUNC_END(FPU_u_sub)
275

source code of linux/arch/x86/math-emu/reg_u_sub.S