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