1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
|
subttl emfdiv.asm - Division
page
;*******************************************************************************
; Copyright (c) Microsoft Corporation 1991
; All Rights Reserved
;
;emfdiv.asm - long double divide
; by Tim Paterson
;
;Purpose:
; Long double division.
;Inputs:
; ebx:esi = op1 mantissa
; ecx = op1 sign in bit 15, exponent in high half
; edi = pointer to op2 and result location
; [Result] = edi
;
; Exponents are unbiased. Denormals have been normalized using
; this expanded exponent range. Neither operand is allowed to be zero.
;Outputs:
; Jumps to [RoundMode] to round and store result.
;
;Revision History:
;
; [] 09/05/91 TP Initial 32-bit version.
;
;*******************************************************************************
;Dispatch tables for division
;
;One operand has been loaded into ecx:ebx:esi ("source"), the other is
;pointed to by edi ("dest"). edi points to dividend for fdiv,
;to divisor for fdivr.
;
;Tag of source is shifted. Tag values are as follows:
;
.erre TAG_SNGL eq 0 ;SINGLE: low 32 bits are zero
.erre TAG_VALID eq 1
.erre TAG_ZERO eq 2
.erre TAG_SPCL eq 3 ;NAN, Infinity, Denormal, Empty
;dest = dest / source
tFdivDisp label dword ;Source (reg) Dest (*[di])
dd DivSingle ;single single
dd DivSingle ;single double
dd XorDestSign ;single zero
dd DivSpclDest ;single special
dd DivDouble ;double single
dd DivDouble ;double double
dd XorDestSign ;double zero
dd DivSpclDest ;double special
dd DivideByZero ;zero single
dd DivideByZero ;zero double
dd ReturnIndefinite ;zero zero
dd DivSpclDest ;zero special
dd DivSpclSource ;special single
dd DivSpclSource ;special double
dd DivSpclSource ;special zero
dd TwoOpBothSpcl ;special special
dd ReturnIndefinite ;Two infinities
;dest = source / dest
tFdivrDisp label dword ;Source (reg) Dest (*[di])
dd DivrSingle ;single single
dd DivrDouble ;single double
dd DivideByZero ;single zero
dd DivrSpclDest ;single special
dd DivrSingle ;double single
dd DivrDouble ;double double
dd DivideByZero ;double zero
dd DivrSpclDest ;double special
dd XorSourceSign ;zero single
dd XorSourceSign ;zero double
dd ReturnIndefinite ;zero zero
dd DivrSpclDest ;zero special
dd DivrSpclSource ;special single
dd DivrSpclSource ;special double
dd DivrSpclSource ;special zero
dd TwoOpBothSpcl ;special special
dd ReturnIndefinite ;Two infinities
EM_ENTRY eFIDIV16
eFIDIV16:
push offset DivSetResult
jmp Load16Int ;Returns to DivSetResult
EM_ENTRY eFIDIVR16
eFIDIVR16:
push offset DivrSetResult
jmp Load16Int
EM_ENTRY eFIDIV32
eFIDIV32:
push offset DivSetResult
jmp Load32Int
EM_ENTRY eFIDIVR32
eFIDIVR32:
push offset DivrSetResult
jmp Load32Int
EM_ENTRY eFDIV32
eFDIV32:
push offset DivSetResult
jmp Load32Real ;Returns to DivSetResult
EM_ENTRY eFDIVR32
eFDIVR32:
push offset DivrSetResult ;Returns to DivrSetResult
jmp Load32Real
EM_ENTRY eFDIV64
eFDIV64:
push offset DivSetResult
jmp Load64Real ;Returns to DivSetResult
EM_ENTRY eFDIVR64
eFDIVR64:
push offset DivrSetResult
jmp Load64Real ;Returns to DivrSetResult
EM_ENTRY eFDIVRPreg
eFDIVRPreg:
push offset PopWhenDone
EM_ENTRY eFDIVRreg
eFDIVRreg:
xchg esi,edi
EM_ENTRY eFDIVRtop
eFDIVRtop:
mov ecx,EMSEG:[esi].ExpSgn
mov ebx,EMSEG:[esi].lManHi
mov esi,EMSEG:[esi].lManLo
DivrSetResult:
;cl has tag of dividend
mov ebp,offset tFdivrDisp
mov EMSEG:[Result],edi ;Save result pointer
mov ah,cl
mov al,EMSEG:[edi].bTag
and ah,not 1 ;Ignore single vs. double on dividend
cmp ax,1
.erre bTAG_VALID eq 1
.erre bTAG_SNGL eq 0
jz DivrDouble ;Divisor was double
ja TwoOpResultSet
;.erre DivrSingle eq $ ;Fall into DivrSingle
;*********
DivrSingle:
;*********
;Computes op1/op2
;Op1 is double, op2 is single (low 32 bits are zero)
mov edx,ebx
mov eax,esi ;Mantissa in edx:eax
mov ebx,EMSEG:[edi].ExpSgn
mov edi,EMSEG:[edi].lManHi
jmp DivSingleReg
SDivBigUnderflow:
;Overflow flag set could only occur with denormals (true exp < -32768)
or EMSEG:[CURerr],Underflow
test EMSEG:[CWmask],Underflow ;Is exception masked?
jnz UnderflowZero ;Yes, return zero (in emfmul.asm)
add ecx,Underbias shl 16 ;Fix up exponent
jmp ContSdiv ;Continue with multiply
EM_ENTRY eFDIVPreg
eFDIVPreg:
push offset PopWhenDone
EM_ENTRY eFDIVreg
eFDIVreg:
xchg esi,edi
EM_ENTRY eFDIVtop
eFDIVtop:
mov ecx,EMSEG:[esi].ExpSgn
mov ebx,EMSEG:[esi].lManHi
mov esi,EMSEG:[esi].lManLo
DivSetResult:
;cl has tag of divisor
mov ebp,offset tFdivDisp
mov EMSEG:[Result],edi ;Save result pointer
mov al,cl
mov ah,EMSEG:[edi].bTag
and ah,not 1 ;Ignore single vs. double on dividend
cmp ax,1
.erre bTAG_VALID eq 1
.erre bTAG_SNGL eq 0
jz DivDouble ;Divisor was double
ja TwoOpResultSet
;.erre DivSingle eq $ ;Fall into DivSingle
;*********
DivSingle:
;*********
;Computes op2/op1
;Op2 is double, op1 is single (low 32 bits are zero)
xchg edi,ebx ;Mantissa in edi, op2 ptr to ebx
xchg ebx,ecx ;ExpSgn to ebx, op2 ptr to ecx
mov edx,EMSEG:[ecx].lManHi
mov eax,EMSEG:[ecx].lManLo
mov ecx,EMSEG:[ecx].ExpSgn ;Op2 loaded
DivSingleReg:
;dividend mantissa in edx:eax, exponent in high ecx, sign in ch bit 7
;divisor mantissa in edi, exponent in high ebx, sign in bh bit 7
xor ch,bh ;Compute result sign
xor bx,bx ;Clear out sign and tag
sub ecx,1 shl 16 ;Exponent adjustment needed
sub ecx,ebx ;Compute result exponent
.erre TexpBias eq 0 ;Exponents not biased
jo SDivBigUnderflow ;Dividing denormal by large number
ContSdiv:
;If dividend >= divisor, the DIV instruction will overflow. Check for
;this condition and shift the dividend right one bit if necessary.
;
;In previous versions of this algorithm for 24-bit and 53-bit mantissas,
;this shift was always performed without a test. This meant that a 1-bit
;normalization might be required at the end. This worked fine because
;32 or 64 bits were calculated, so extra precision was available for
;normalization. However, this version needs all 64 bits that are calculated,
;so we can't afford a normalization shift at the end. This test tells us
;up front how to align so we'll be normalized.
xor ebx,ebx ;Extend dividend
cmp edi,edx ;Will DIV overflow?
ja DoSdiv ;No, we're safe
shrd ebx,eax,1
shrd eax,edx,1
shr edx,1
add ecx,1 shl 16 ;Bump exponent to account for shift
DoSdiv:
div edi
xchg ebx,eax ;Save quotient in ebx, extend remainder
div edi
mov esi,eax
;We have a 64-bit quotient in ebx:esi. Now compare remainder*2 with divisor
;to compute round and sticky bits.
mov eax,-1 ;Set round and sticky bits
shl edx,1 ;Double remainder
jc RoundJmp ;If too big, round & sticky set
cmp edx,edi ;Is remainder*2 > divisor?
ja RoundJmp
;Observe, oh wondering one, how you can assume the result of this last
;compare is not equality. Use the following notation: n=numerator,
;d=denominator,q=quotient,r=remainder,b=base(2^64 here). If
;initially we had n < d then there was no shift and we will find q and r
;so that q*d+r=n*b, if initially we had n >= d then there was a shift and
;we will find q and r so that q*d+r=n*b/2. If we have equality here
;then r=d/2 ==> n={possibly 2*}(2*q+1)*d/(2*b), since this can only
;be integral if d is a multiple of b, but by definition b/2 <= d < b, we
;have a contradiction. Equality is thus impossible at this point.
cmp edx,1 ;Check for zero remainder
sbb eax,-2 ;eax==0 if CY, ==1 if NC (was -1)
RoundJmp:
jmp EMSEG:[RoundMode]
;*******************************************************************************
DDivBigUnderflow:
;Overflow flag set could only occur with denormals (true exp < -32768)
or EMSEG:[CURerr],Underflow
test EMSEG:[CWmask],Underflow ;Is exception masked?
jnz UnderflowZero ;Yes, return zero (in emfmul.asm)
add ecx,Underbias shl 16 ;Fix up exponent
jmp ContDdiv ;Continue with multiply
DivrDoubleSetFlag:
;Special entry point used by FPATAN to set bit 6 of flag dword pushed
;on stack before call.
or byte ptr [esp+4],40H
;*********
DivrDouble:
;*********
;Computes op1/op2
mov edx,ebx
mov eax,esi ;Mantissa in edx:eax
mov ebx,EMSEG:[edi].ExpSgn
mov esi,EMSEG:[edi].lManHi
mov edi,EMSEG:[edi].lManLo
jmp short DivDoubleReg
HighHalfEqual:
;edx:eax:ebp = dividend
;esi:edi = divisor
;ecx = exponent and sign of result
;
;High half of dividend is equal to high half of divisor. This will cause
;the DIV instruction to overflow. If whole dividend >= whole divisor, then
;we just shift the dividend right 1 bit.
cmp eax,edi ;Is dividend >= divisor?
jae ShiftDividend ;Yes, divide it by two
;DIV instruction would overflow, so skip it and calculate the effective
;result. Assume a quotient of 2^32-1 and calculate the remainder. See
;detailed comments under MaxQuo below--this is a copy of that code.
push ecx ;Save exp. and sign
mov ebx,-1 ;Max quotient digit
sub eax,edi ;Calculate correct remainder
;Currently edx == esi, but the next instruction ensures that is no longer
;true, since eax != 0. This will allow us to skip the MaxQuo check at
;DivFirstDigit.
add edx,eax ;Should set CY if quotient fit
mov eax,edi ;ecx:eax has new remainder
jc ComputeSecond ;Remainder was positive
;Quotient doesn't fit. Note that we can no longer ensure that edx != esi
;after making a correction.
mov ecx,edx ;Need remainder in ecx:eax
jmp DivCorrect1
;*********
DivDouble:
;*********
;Computes op2/op1
mov eax,edi ;Move op2 pointer
mov edi,esi
mov esi,ebx ;Mantissa in esi:edi
mov ebx,ecx ;ExpSgn to ebx
mov ecx,EMSEG:[eax].ExpSgn ;Op2 loaded
mov edx,EMSEG:[eax].lManHi
mov eax,EMSEG:[eax].lManLo
DivDoubleReg:
;dividend mantissa in edx:eax, exponent in high ecx, sign in ch bit 7
;divisor mantissa in esi:edi, exponent in high ebx, sign in bh bit 7
xor ch,bh ;Compute result sign
xor bx,bx ;Clear out sign and tag
sub ecx,1 shl 16 ;Exponent adjustment needed
sub ecx,ebx ;Compute result exponent
.erre TexpBias eq 0 ;Exponents not biased
jo DDivBigUnderflow ;Dividing denormal by large number
ContDdiv:
;If dividend >= divisor, we must shift the dividend right one bit.
;This will ensure the result is normalized.
;
;In previous versions of this algorithm for 24-bit and 53-bit mantissas,
;this shift was always performed without a test. This meant that a 1-bit
;normalization might be required at the end. This worked fine because
;32 or 64 bits were calculated, so extra precision was available for
;normalization. However, this version needs all 64 bits that are calculated,
;so we can't afford a normalization shift at the end. This test tells us
;up front how to align so we'll be normalized.
xor ebp,ebp ;Extend dividend
cmp esi,edx ;Dividend > divisor
ja DoDdiv
jz HighHalfEqual ;Go compare low halves
ShiftDividend:
shrd ebp,eax,1
shrd eax,edx,1
shr edx,1
add ecx,1 shl 16 ;Bump exponent to account for shift
DoDdiv:
push ecx ;Save exp. and sign
;edx:eax:ebp = dividend
;esi:edi = divisor
;
;Division algorithm from Knuth vol. 2, p. 237, using 32-bit "digits":
;Guess a quotient digit by dividing two MSDs of dividend by the MSD of
;divisor. If divisor is >= 1/2 the radix (radix = 2^32 in this case), then
;this guess will be no more than 2 larger than the correct value of that
;quotient digit (and never smaller). Divisor meets magnitude condition
;because it's normalized.
div esi ;Guess first quotient "digit"
;Check out our guess.
;Currently, remainder in edx = dividend - (quotient * high half divisor).
;The definition of remainder is dividend - (quotient * all divisor). So
;if we subtract (quotient * low half divisor) from edx, we'll get
;the true remainder. If it's negative, our guess was too big.
mov ebx,eax ;Save quotient
mov ecx,edx ;Save remainder
mul edi ;Quotient * low half divisor
sub ebp,eax ;Subtract from dividend extension
sbb ecx,edx ;Subtract from remainder
mov eax,ebp ;Low remainder to eax
jnc DivFirstDigit ;Was quotient OK?
DivCorrect1:
dec ebx ;Quotient was too big
add eax,edi ;Add divisor back into remainder
adc ecx,esi
jnc DivCorrect1 ;Repeat if quotient is still too big
DivFirstDigit:
cmp ecx,esi ;Would DIV instruction overflow?
jae short MaxQuo ;Yes, figure alternate quotient
mov edx,ecx ;Remainder back to edx:eax
;Compute 2nd quotient "digit"
ComputeSecond:
div esi ;Guess 2nd quotient "digit"
mov ebp,eax ;Save quotient
mov ecx,edx ;Save remainder
mul edi ;Quotient * low half divisor
neg eax ;Subtract from dividend extended with 0
sbb ecx,edx ;Subtract from remainder
jnc DivSecondDigit ;Was quotient OK?
DivCorrect2:
dec ebp ;Quotient was too big
add eax,edi ;Add divisor back into remainder
adc ecx,esi
jnc DivCorrect2 ;Repeat if quotient is still too big
DivSecondDigit:
;ebx:ebp = quotient
;ecx:eax = remainder
;esi:edi = divisor
;Now compare remainder*2 with divisor to compute round and sticky bits.
mov edx,-1 ;Set round and sticky bits
shld ecx,eax,1 ;Double remainder
jc DDivEnd ;If too big, round & sticky set
shl eax,1
sub edi,eax
sbb esi,ecx ;Subtract remainder*2 from divisor
jb DDivEnd ;If <0, use round & sticky bits set
;Observe, oh wondering one, how you can assume the result of this last
;compare is not equality. Use the following notation: n=numerator,
;d=denominator,q=quotient,r=remainder,b=base(2^64 here). If
;initially we had n < d then there was no shift and we will find q and r
;so that q*d+r=n*b, if initially we had n >= d then there was a shift and
;we will find q and r so that q*d+r=n*b/2. If we have equality here
;then r=d/2 ==> n={possibly 2*}(2*q+1)*d/(2*b), since this can only
;be integral if d is a multiple of b, but by definition b/2 <= d < b, we
;have a contradiction. Equality is thus impossible at this point.
;No round bit, but set sticky bit if remainder != 0.
or eax,ecx ;Is remainder zero?
add eax,-1 ;Set CY if non-zero
adc edx,1 ;edx==0 if NC, ==1 if CY (was -1)
DDivEnd:
mov esi,ebp ;Result in ebx:esi
mov eax,edx ;Round/sticky bits to eax
pop ecx ;Recover sign/exponent
jmp EMSEG:[RoundMode]
MaxQuo:
;ebx = first quotient "digit"
;ecx:eax = remainder
;esi:edi = divisor
;On exit, ebp = second quotient "digit"
;
;Come here if divide instruction would overflow. This must mean that ecx == esi,
;i.e., the high halves of the dividend and divisor are equal. Assume a result
;of 2^32-1, thus remainder = dividend - ( divisor * (2^32-1) )
; = dividend - divisor * 2^32 + divisor. Since the high halves of the dividend
;and divisor are equal, dividend - divisor * 2^32 can be computed by
;subtracting only the low halves. When adding divisor (in esi) to this, note
;that ecx == esi, and we want the result in ecx anyway.
;
;Note also that since the dividend is a previous remainder, the
;dividend - divisor * 2^32 calculation must always be negative. Thus the
;addition of divisor back to it should generate a carry if it goes positive.
mov ebp,-1 ;Max quotient digit
sub eax,edi ;Calculate correct remainder
add ecx,eax ;Should set CY if quotient fit
mov eax,edi ;ecx:eax has new remainder
jc DivSecondDigit ;Remainder was positive
jmp DivCorrect2
|