xref: /openbmc/linux/arch/x86/math-emu/reg_u_div.S (revision 384740dc)
1	.file	"reg_u_div.S"
2/*---------------------------------------------------------------------------+
3 |  reg_u_div.S                                                              |
4 |                                                                           |
5 | Divide one FPU_REG by another and put the result in a destination FPU_REG.|
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 |                                                                           |
12 +---------------------------------------------------------------------------*/
13
14/*---------------------------------------------------------------------------+
15 | Call from C as:                                                           |
16 |    int FPU_u_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest,                   |
17 |                unsigned int control_word, char *sign)                     |
18 |                                                                           |
19 |  Does not compute the destination exponent, but does adjust it.           |
20 |                                                                           |
21 |    Return value is the tag of the answer, or-ed with FPU_Exception if     |
22 |    one was raised, or -1 on internal error.                               |
23 +---------------------------------------------------------------------------*/
24
25#include "exception.h"
26#include "fpu_emu.h"
27#include "control_w.h"
28
29
30/* #define	dSIGL(x)	(x) */
31/* #define	dSIGH(x)	4(x) */
32
33
34#ifndef NON_REENTRANT_FPU
35/*
36	Local storage on the stack:
37	Result:		FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
38	Overflow flag:	ovfl_flag
39 */
40#define FPU_accum_3	-4(%ebp)
41#define FPU_accum_2	-8(%ebp)
42#define FPU_accum_1	-12(%ebp)
43#define FPU_accum_0	-16(%ebp)
44#define FPU_result_1	-20(%ebp)
45#define FPU_result_2	-24(%ebp)
46#define FPU_ovfl_flag	-28(%ebp)
47
48#else
49.data
50/*
51	Local storage in a static area:
52	Result:		FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
53	Overflow flag:	ovfl_flag
54 */
55	.align 4,0
56FPU_accum_3:
57	.long	0
58FPU_accum_2:
59	.long	0
60FPU_accum_1:
61	.long	0
62FPU_accum_0:
63	.long	0
64FPU_result_1:
65	.long	0
66FPU_result_2:
67	.long	0
68FPU_ovfl_flag:
69	.byte	0
70#endif /* NON_REENTRANT_FPU */
71
72#define REGA	PARAM1
73#define REGB	PARAM2
74#define DEST	PARAM3
75
76.text
77ENTRY(FPU_u_div)
78	pushl	%ebp
79	movl	%esp,%ebp
80#ifndef NON_REENTRANT_FPU
81	subl	$28,%esp
82#endif /* NON_REENTRANT_FPU */
83
84	pushl	%esi
85	pushl	%edi
86	pushl	%ebx
87
88	movl	REGA,%esi
89	movl	REGB,%ebx
90	movl	DEST,%edi
91
92	movswl	EXP(%esi),%edx
93	movswl	EXP(%ebx),%eax
94	subl	%eax,%edx
95	addl	EXP_BIAS,%edx
96
97	/* A denormal and a large number can cause an exponent underflow */
98	cmpl	EXP_WAY_UNDER,%edx
99	jg	xExp_not_underflow
100
101	/* Set to a really low value allow correct handling */
102	movl	EXP_WAY_UNDER,%edx
103
104xExp_not_underflow:
105
106	movw    %dx,EXP(%edi)
107
108#ifdef PARANOID
109/*	testl	$0x80000000, SIGH(%esi)	// Dividend */
110/*	je	L_bugged */
111	testl	$0x80000000, SIGH(%ebx)	/* Divisor */
112	je	L_bugged
113#endif /* PARANOID */
114
115/* Check if the divisor can be treated as having just 32 bits */
116	cmpl	$0,SIGL(%ebx)
117	jnz	L_Full_Division	/* Can't do a quick divide */
118
119/* We should be able to zip through the division here */
120	movl	SIGH(%ebx),%ecx	/* The divisor */
121	movl	SIGH(%esi),%edx	/* Dividend */
122	movl	SIGL(%esi),%eax	/* Dividend */
123
124	cmpl	%ecx,%edx
125	setaeb	FPU_ovfl_flag	/* Keep a record */
126	jb	L_no_adjust
127
128	subl	%ecx,%edx	/* Prevent the overflow */
129
130L_no_adjust:
131	/* Divide the 64 bit number by the 32 bit denominator */
132	divl	%ecx
133	movl	%eax,FPU_result_2
134
135	/* Work on the remainder of the first division */
136	xorl	%eax,%eax
137	divl	%ecx
138	movl	%eax,FPU_result_1
139
140	/* Work on the remainder of the 64 bit division */
141	xorl	%eax,%eax
142	divl	%ecx
143
144	testb	$255,FPU_ovfl_flag	/* was the num > denom ? */
145	je	L_no_overflow
146
147	/* Do the shifting here */
148	/* increase the exponent */
149	incw	EXP(%edi)
150
151	/* shift the mantissa right one bit */
152	stc			/* To set the ms bit */
153	rcrl	FPU_result_2
154	rcrl	FPU_result_1
155	rcrl	%eax
156
157L_no_overflow:
158	jmp	LRound_precision	/* Do the rounding as required */
159
160
161/*---------------------------------------------------------------------------+
162 |  Divide:   Return  arg1/arg2 to arg3.                                     |
163 |                                                                           |
164 |  This routine does not use the exponents of arg1 and arg2, but does       |
165 |  adjust the exponent of arg3.                                             |
166 |                                                                           |
167 |  The maximum returned value is (ignoring exponents)                       |
168 |               .ffffffff ffffffff                                          |
169 |               ------------------  =  1.ffffffff fffffffe                  |
170 |               .80000000 00000000                                          |
171 | and the minimum is                                                        |
172 |               .80000000 00000000                                          |
173 |               ------------------  =  .80000000 00000001   (rounded)       |
174 |               .ffffffff ffffffff                                          |
175 |                                                                           |
176 +---------------------------------------------------------------------------*/
177
178
179L_Full_Division:
180	/* Save extended dividend in local register */
181	movl	SIGL(%esi),%eax
182	movl	%eax,FPU_accum_2
183	movl	SIGH(%esi),%eax
184	movl	%eax,FPU_accum_3
185	xorl	%eax,%eax
186	movl	%eax,FPU_accum_1	/* zero the extension */
187	movl	%eax,FPU_accum_0	/* zero the extension */
188
189	movl	SIGL(%esi),%eax	/* Get the current num */
190	movl	SIGH(%esi),%edx
191
192/*----------------------------------------------------------------------*/
193/* Initialization done.
194   Do the first 32 bits. */
195
196	movb	$0,FPU_ovfl_flag
197	cmpl	SIGH(%ebx),%edx	/* Test for imminent overflow */
198	jb	LLess_than_1
199	ja	LGreater_than_1
200
201	cmpl	SIGL(%ebx),%eax
202	jb	LLess_than_1
203
204LGreater_than_1:
205/* The dividend is greater or equal, would cause overflow */
206	setaeb	FPU_ovfl_flag		/* Keep a record */
207
208	subl	SIGL(%ebx),%eax
209	sbbl	SIGH(%ebx),%edx	/* Prevent the overflow */
210	movl	%eax,FPU_accum_2
211	movl	%edx,FPU_accum_3
212
213LLess_than_1:
214/* At this point, we have a dividend < divisor, with a record of
215   adjustment in FPU_ovfl_flag */
216
217	/* We will divide by a number which is too large */
218	movl	SIGH(%ebx),%ecx
219	addl	$1,%ecx
220	jnc	LFirst_div_not_1
221
222	/* here we need to divide by 100000000h,
223	   i.e., no division at all.. */
224	mov	%edx,%eax
225	jmp	LFirst_div_done
226
227LFirst_div_not_1:
228	divl	%ecx		/* Divide the numerator by the augmented
229				   denom ms dw */
230
231LFirst_div_done:
232	movl	%eax,FPU_result_2	/* Put the result in the answer */
233
234	mull	SIGH(%ebx)	/* mul by the ms dw of the denom */
235
236	subl	%eax,FPU_accum_2	/* Subtract from the num local reg */
237	sbbl	%edx,FPU_accum_3
238
239	movl	FPU_result_2,%eax	/* Get the result back */
240	mull	SIGL(%ebx)	/* now mul the ls dw of the denom */
241
242	subl	%eax,FPU_accum_1	/* Subtract from the num local reg */
243	sbbl	%edx,FPU_accum_2
244	sbbl	$0,FPU_accum_3
245	je	LDo_2nd_32_bits		/* Must check for non-zero result here */
246
247#ifdef PARANOID
248	jb	L_bugged_1
249#endif /* PARANOID */
250
251	/* need to subtract another once of the denom */
252	incl	FPU_result_2	/* Correct the answer */
253
254	movl	SIGL(%ebx),%eax
255	movl	SIGH(%ebx),%edx
256	subl	%eax,FPU_accum_1	/* Subtract from the num local reg */
257	sbbl	%edx,FPU_accum_2
258
259#ifdef PARANOID
260	sbbl	$0,FPU_accum_3
261	jne	L_bugged_1	/* Must check for non-zero result here */
262#endif /* PARANOID */
263
264/*----------------------------------------------------------------------*/
265/* Half of the main problem is done, there is just a reduced numerator
266   to handle now.
267   Work with the second 32 bits, FPU_accum_0 not used from now on */
268LDo_2nd_32_bits:
269	movl	FPU_accum_2,%edx	/* get the reduced num */
270	movl	FPU_accum_1,%eax
271
272	/* need to check for possible subsequent overflow */
273	cmpl	SIGH(%ebx),%edx
274	jb	LDo_2nd_div
275	ja	LPrevent_2nd_overflow
276
277	cmpl	SIGL(%ebx),%eax
278	jb	LDo_2nd_div
279
280LPrevent_2nd_overflow:
281/* The numerator is greater or equal, would cause overflow */
282	/* prevent overflow */
283	subl	SIGL(%ebx),%eax
284	sbbl	SIGH(%ebx),%edx
285	movl	%edx,FPU_accum_2
286	movl	%eax,FPU_accum_1
287
288	incl	FPU_result_2	/* Reflect the subtraction in the answer */
289
290#ifdef PARANOID
291	je	L_bugged_2	/* Can't bump the result to 1.0 */
292#endif /* PARANOID */
293
294LDo_2nd_div:
295	cmpl	$0,%ecx		/* augmented denom msw */
296	jnz	LSecond_div_not_1
297
298	/* %ecx == 0, we are dividing by 1.0 */
299	mov	%edx,%eax
300	jmp	LSecond_div_done
301
302LSecond_div_not_1:
303	divl	%ecx		/* Divide the numerator by the denom ms dw */
304
305LSecond_div_done:
306	movl	%eax,FPU_result_1	/* Put the result in the answer */
307
308	mull	SIGH(%ebx)	/* mul by the ms dw of the denom */
309
310	subl	%eax,FPU_accum_1	/* Subtract from the num local reg */
311	sbbl	%edx,FPU_accum_2
312
313#ifdef PARANOID
314	jc	L_bugged_2
315#endif /* PARANOID */
316
317	movl	FPU_result_1,%eax	/* Get the result back */
318	mull	SIGL(%ebx)	/* now mul the ls dw of the denom */
319
320	subl	%eax,FPU_accum_0	/* Subtract from the num local reg */
321	sbbl	%edx,FPU_accum_1	/* Subtract from the num local reg */
322	sbbl	$0,FPU_accum_2
323
324#ifdef PARANOID
325	jc	L_bugged_2
326#endif /* PARANOID */
327
328	jz	LDo_3rd_32_bits
329
330#ifdef PARANOID
331	cmpl	$1,FPU_accum_2
332	jne	L_bugged_2
333#endif /* PARANOID */
334
335	/* need to subtract another once of the denom */
336	movl	SIGL(%ebx),%eax
337	movl	SIGH(%ebx),%edx
338	subl	%eax,FPU_accum_0	/* Subtract from the num local reg */
339	sbbl	%edx,FPU_accum_1
340	sbbl	$0,FPU_accum_2
341
342#ifdef PARANOID
343	jc	L_bugged_2
344	jne	L_bugged_2
345#endif /* PARANOID */
346
347	addl	$1,FPU_result_1	/* Correct the answer */
348	adcl	$0,FPU_result_2
349
350#ifdef PARANOID
351	jc	L_bugged_2	/* Must check for non-zero result here */
352#endif /* PARANOID */
353
354/*----------------------------------------------------------------------*/
355/* The division is essentially finished here, we just need to perform
356   tidying operations.
357   Deal with the 3rd 32 bits */
358LDo_3rd_32_bits:
359	movl	FPU_accum_1,%edx		/* get the reduced num */
360	movl	FPU_accum_0,%eax
361
362	/* need to check for possible subsequent overflow */
363	cmpl	SIGH(%ebx),%edx	/* denom */
364	jb	LRound_prep
365	ja	LPrevent_3rd_overflow
366
367	cmpl	SIGL(%ebx),%eax	/* denom */
368	jb	LRound_prep
369
370LPrevent_3rd_overflow:
371	/* prevent overflow */
372	subl	SIGL(%ebx),%eax
373	sbbl	SIGH(%ebx),%edx
374	movl	%edx,FPU_accum_1
375	movl	%eax,FPU_accum_0
376
377	addl	$1,FPU_result_1	/* Reflect the subtraction in the answer */
378	adcl	$0,FPU_result_2
379	jne	LRound_prep
380	jnc	LRound_prep
381
382	/* This is a tricky spot, there is an overflow of the answer */
383	movb	$255,FPU_ovfl_flag		/* Overflow -> 1.000 */
384
385LRound_prep:
386/*
387 * Prepare for rounding.
388 * To test for rounding, we just need to compare 2*accum with the
389 * denom.
390 */
391	movl	FPU_accum_0,%ecx
392	movl	FPU_accum_1,%edx
393	movl	%ecx,%eax
394	orl	%edx,%eax
395	jz	LRound_ovfl		/* The accumulator contains zero. */
396
397	/* Multiply by 2 */
398	clc
399	rcll	$1,%ecx
400	rcll	$1,%edx
401	jc	LRound_large		/* No need to compare, denom smaller */
402
403	subl	SIGL(%ebx),%ecx
404	sbbl	SIGH(%ebx),%edx
405	jnc	LRound_not_small
406
407	movl	$0x70000000,%eax	/* Denom was larger */
408	jmp	LRound_ovfl
409
410LRound_not_small:
411	jnz	LRound_large
412
413	movl	$0x80000000,%eax	/* Remainder was exactly 1/2 denom */
414	jmp	LRound_ovfl
415
416LRound_large:
417	movl	$0xff000000,%eax	/* Denom was smaller */
418
419LRound_ovfl:
420/* We are now ready to deal with rounding, but first we must get
421   the bits properly aligned */
422	testb	$255,FPU_ovfl_flag	/* was the num > denom ? */
423	je	LRound_precision
424
425	incw	EXP(%edi)
426
427	/* shift the mantissa right one bit */
428	stc			/* Will set the ms bit */
429	rcrl	FPU_result_2
430	rcrl	FPU_result_1
431	rcrl	%eax
432
433/* Round the result as required */
434LRound_precision:
435	decw	EXP(%edi)	/* binary point between 1st & 2nd bits */
436
437	movl	%eax,%edx
438	movl	FPU_result_1,%ebx
439	movl	FPU_result_2,%eax
440	jmp	fpu_reg_round
441
442
443#ifdef PARANOID
444/* The logic is wrong if we got here */
445L_bugged:
446	pushl	EX_INTERNAL|0x202
447	call	EXCEPTION
448	pop	%ebx
449	jmp	L_exit
450
451L_bugged_1:
452	pushl	EX_INTERNAL|0x203
453	call	EXCEPTION
454	pop	%ebx
455	jmp	L_exit
456
457L_bugged_2:
458	pushl	EX_INTERNAL|0x204
459	call	EXCEPTION
460	pop	%ebx
461	jmp	L_exit
462
463L_exit:
464	movl	$-1,%eax
465	popl	%ebx
466	popl	%edi
467	popl	%esi
468
469	leave
470	ret
471#endif /* PARANOID */
472