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