summaryrefslogtreecommitdiffstats
path: root/private/ntos/dll/i386/emfadd.asm
blob: cdd0a8f581f7c34705d1530f20a21794855c8c64 (plain) (blame)
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
	subttl  emfadd.asm - Addition and Subtraction
	page
;*******************************************************************************
;	 Copyright (c) Microsoft Corporation 1991
;	 All Rights Reserved
;
;emfadd.asm - long double add and subtract
;	by Tim Paterson
;
;Purpose:
;	Long double add/subtract.
;Outputs:
;	Jumps to [RoundMode] to round and store result.
;
;Revision History:
;
; []	09/05/91  TP	Initial 32-bit version.
;
;*******************************************************************************

;*******************************************************************************
; Dispatch for Add/Sub/Subr
;
; Signs are passed in dx:
;       xor source sign with dl
;       xor dest sign with dh
;
;One operand has been loaded into ecx:ebx:esi ("source"), the other is
;pointed to by edi ("dest").  
;
;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
;Any special case routines not found in this file are in emarith.asm
tFaddDisp	label	dword		;Source (reg)	Dest (*[di])
	dd	AddDouble		;single		single
	dd	AddDouble		;single		double
	dd	AddSourceSign		;single		zero
	dd	AddSpclDest		;single		special
	dd	AddDouble		;double		single
	dd	AddDouble		;double		double
	dd	AddSourceSign		;double		zero
	dd	AddSpclDest		;double		special
	dd	AddDestSign		;zero		single
	dd	AddDestSign		;zero		double
	dd	AddZeroZero		;zero		zero
	dd	AddSpclDest		;zero		special
	dd	AddSpclSource		;special	single
	dd	AddSpclSource		;special	double
	dd	AddSpclSource		;special	zero
	dd	TwoOpBothSpcl		;special	special
	dd	AddTwoInf		;Two infinities

EM_ENTRY eFISUB16
eFISUB16:
        call    Load16Int
        mov     dx,bSign                ;Change sign of source
        jmp     AddSetResult

EM_ENTRY eFISUBR16
eFISUBR16:
        call    Load16Int
        mov     dx,bSign shl 8          ;Change sign of dest
        jmp     AddSetResult

EM_ENTRY eFIADD16
eFIADD16:
        call    Load16Int
        xor     edx,edx                 ;Both signs positive
        jmp     AddSetResult

EM_ENTRY eFISUB32
eFISUB32:
        call    Load32Int
        mov     dx,bSign                ;Change sign of source
        jmp     AddSetResult

EM_ENTRY eFISUBR32
eFISUBR32:
        call    Load32Int
        mov     dx,bSign shl 8          ;Change sign of dest
        jmp     AddSetResult

EM_ENTRY eFIADD32
eFIADD32:
        call    Load32Int
        xor     edx,edx                 ;Both signs positive
        jmp     AddSetResult

EM_ENTRY eFSUB32
eFSUB32:
        call    Load32Real
        mov     dx,bSign                ;Change sign of source
        jmp     AddSetResult

EM_ENTRY eFSUBR32
eFSUBR32:
        call    Load32Real
        mov     dx,bSign shl 8          ;Change sign of dest
        jmp     AddSetResult

EM_ENTRY eFADD32
eFADD32:
        call    Load32Real
        xor     edx,edx                 ;Both signs positive
        jmp     AddSetResult

EM_ENTRY eFSUB64
eFSUB64:
        call    Load64Real
        mov     dx,bSign                ;Change sign of source
        jmp     AddSetResult

EM_ENTRY eFSUBR64
eFSUBR64:
        call    Load64Real
        mov     dx,bSign shl 8          ;Change sign of dest
        jmp     AddSetResult

EM_ENTRY eFADD64
eFADD64:
        call    Load64Real
        xor     edx,edx                 ;Both signs positive
        jmp     AddSetResult


PolyAddDouble:
;This entry point is used by polynomial evaluator.
;It checks the operand in registers for zero, and doesn't require
;signs to be set up in dx.
;
;op1 mantissa in ebx:esi, exponent in high ecx, sign in ch bit 7, tag in cl
;edi = pointer to op2 in ds
	xor	edx,edx			;Addition
	cmp	cl,bTAG_ZERO		;Adding to zero?
        jnz     AddDouble
;Number in registers is zero, so just return value from memory.
        mov     ecx,EMSEG:[edi].ExpSgn
        mov     ebx,EMSEG:[edi].lManHi
        mov     esi,EMSEG:[edi].lManLo
        ret

EM_ENTRY eFSUBPreg
eFSUBPreg:
        push    offset PopWhenDone

EM_ENTRY eFSUBreg
eFSUBreg:
        xchg    esi,edi

EM_ENTRY eFSUBtop
eFSUBtop:
        mov     dx,bSign                ;Change sign of source
        jmp     AddHaveSgn

EM_ENTRY eFSUBRPreg
eFSUBRPreg:
        push    offset PopWhenDone

EM_ENTRY eFSUBRreg
eFSUBRreg:
        xchg    esi,edi

EM_ENTRY eFSUBRtop
eFSUBRtop:
        mov     dx,bSign shl 8          ;Change sign of dest
        jmp     AddHaveSgn


InsignifAdd:
	mov	eax,1			;Set sticky bit
	shl	ch,1			;Get sign, CY set IFF subtracting mant.
	jnc	ReturnOp1
	sub	esi,eax			;Subtract 1 from mantissa
	sbb	ebx,0
	neg	eax
ReturnOp1:
;ebx:esi:eax = normalized unrounded mantissa
;high half of ecx = exponent
;high bit of ch = sign
	jmp	EMSEG:[RoundMode]

EM_ENTRY eFADDPreg
eFADDPreg:
        push    offset PopWhenDone

EM_ENTRY eFADDreg
eFADDreg:
        xchg    esi,edi

EM_ENTRY eFADDtop
eFADDtop:
        xor     edx,edx                 ;Both signs positive
AddHaveSgn:
        mov     ecx,EMSEG:[esi].ExpSgn
        mov     ebx,EMSEG:[esi].lManHi
        mov     esi,EMSEG:[esi].lManLo
AddSetResult:
        mov     ebp,offset tFaddDisp
        mov     EMSEG:[Result],edi            ;Save result pointer
        mov     al,cl
        mov     ah,EMSEG:[edi].bTag
        test    ax,ZEROorSPCL * 100H + ZEROorSPCL
        jnz     TwoOpDispatch

;.erre   AddDouble eq $                  ;Fall into AddDouble

;*********
AddDouble:
;*********
;
;op1 mantissa in ebx:esi, exponent in high ecx, sign in ch bit 7
;dl = sign change for op1
;dh = sign change for op2
;edi = pointer to op2

	xor	ch,dl			;Flip sign if subtracting
	mov	eax,EMSEG:[edi].ExpSgn
	xor	ah,dh			;Flip sign if subtracting
	mov	edx,EMSEG:[edi].lManHi
	mov	edi,EMSEG:[edi].lManLo

AddDoubleReg:
;op1 mantissa in ebx:esi, exponent in high ecx, sign in ch bit 7
;op2 mantissa in edx:edi, exponent in high eax, sign in ah bit 7

	cmp	eax,ecx			;Compare exponents
.erre	TexpBias eq 0			;Not biased, use signed jump
	jle	short HavLg		;op1 is larger, we have the right order
	xchg	esi,edi
	xchg	ebx,edx
	xchg	eax,ecx
HavLg:
;Larger in ebx:esi.  Note that if the exponents were equal, things like
;the sign bit or tag may have determined which is "larger".  It doesn't
;matter which is which if the exponents are equal, however.
	and	ah,80H			;Keep sign bit
	sar	ch,1			;Extend sign into bit 6 of byte
	xor	ch,ah			;See if signs are the same
	xor	ax,ax			;Clear out sign and tag
	neg	eax			;ax still 0
	add	eax,ecx			;Get exponent difference
	shr	eax,16			;Bring exp. difference down to low end
	jz	short Aligned
	cmp	eax,64+1		;Is difference in range?
;CONSIDER: tell me again why 1/4 LSB could have effect.  It seems like
;CONSIDER: 1/2 LSB is the limit.
	ja	short InsignifAdd	;  (Even 1/4 LSB could have effect)
	mov	cl,al			;Shift count to cl
;High half ecx = exponent
;ch bit 7 = sign difference
;ch bit 6 = sign
;cl = shift count
	xor	eax,eax			;Prepare to take bits shifted out
	cmp	cl,32			;More than a whole word?
	jb	short ShortShift
	xchg	eax,edx			;Save bits shifted out in eax
	xchg	edi,eax
	sub	cl,32
	cmp	cl,8			;Safe to shift this much
	jb	short ShortSticky
;Collapse all (sticky) bits of eax into LSB of edi
	neg	eax			;Sets CY if eax was not zero
	sbb	eax,eax			;-1 if CY was set, zero otherwise
	neg	eax			;Sticky bit in LSB only
	or	di,ax			;Move sticky bit up
	cmp	cl,32			;Less than another Dword?
	jb	short ShortShift
	mov	eax,edi
	xor	edi,edi			;edx = edi = 0
ShortSticky:
;Shift will not be more than 8 bits
	or	ah,al			;Move up sticky bits
ShortShift:
	shrd	eax,edi,cl		;Save bits shifted out in eax
	shrd	edi,edx,cl
	shr	edx,cl
Aligned:
	shl	ch,1			;Were signs the same?
	jc	short SubMant		;No--go subtract mantissas
;Add mantissas
	add	esi,edi
	adc	ebx,edx
	jnc	short AddExit
;Addition of mantissas overflowed. Bump exponent and shift right
	shrd	eax,esi,1
	shrd	esi,ebx,1		;Faster than RCR
	sar	ebx,1
	or	ebx,1 shl 31		;Set MSB
	add	ecx,1 shl 16
AddExit:
;ebx:esi:eax = normalized unrounded mantissa
;high half of ecx = exponent
;high bit of ch = sign
	jmp	EMSEG:[RoundMode]

NegMant:
;To get here, exponents must have been equal and op2 was bigger than op1.
;Note that this means nothing ever got shifted into eax.
	not	ch			;Change sign of result
	not	ebx
	neg	esi
	sbb	ebx,-1
	js	short AddExit		;Already normalized?
	test	ebx,40000000H		;Only one bit out of normal?
	jz	short NormalizeAdd
	jmp	short NormOneBit

SubMant:
;Subtract mantissas
	neg	eax			;Pretend minuend is zero extended
	sbb	esi,edi
	sbb	ebx,edx
	jc	short NegMant
	js	short AddExit		;Already normalized?
NormChk:
	test	ebx,40000000H		;Only one bit out of normal?
	jz	short NormalizeAdd
;One bit normalization
NormOneBit:
	sub	ecx,1 shl 16		;Adjust exponent
ShiftOneBit:				;Entry point from emfmul.asm
	shld	ebx,esi,1
	shld	esi,eax,1
	shl	eax,1
	jmp	EMSEG:[RoundMode]

;***********
AddZeroZero:				;Entry point for adding two zeros
;***********
	mov	ah,EMSEG:[edi].bSgn	;Get sign of op
	xor	ch,dl			;Possibly subtracting source
	xor	ah,dh			;Possibly subtracting dest
	xor	ch,ah			;Do signs match?
	js	FindZeroSign		;No - use rounding mode to set sign
	mov	EMSEG:[edi].bSgn,ah	;Correct the sign if subtracting
	ret				;Result at [edi] is now correct

ZeroChk:
;Upper 64 bits were all zero, but there could be 1 bit in the MSB
;of eax.
	or	eax,eax
	jnz	short OneBitLeft
	mov	ebx,eax
	mov	esi,eax			;Zero mantissa
FindZeroSign:
;Round to -0 if "round down" mode, round to +0 otherwise
	xor	ecx,ecx			;Zero exponent, positive sign
	mov	dl,EMSEG:[CWcntl]	;Get control word
	and	dl,RoundControl
        cmp	dl,RCdown		;Rounding down?
	jnz	ZeroJmp
	mov	ch,80H			;Set sign bit
ZeroJmp:
	mov	cl,bTAG_ZERO
	jmp	EMSEG:[ZeroVector]

OneBitLeft:
	xchg	ebx,eax			;Bit now normalized
	sub	ecx,64 shl 16		;Adjust exponent
	jmp	EMSEG:[RoundMode]

NormalizeAdd:
;Inputs:
;	ebx:esi:eax = 65-bit number
;	ecx high half = exponent
;
;Since we are more than 1 bit out of normalization, exponents must have
;differed by 0 or 1.  Thus rounding will not be necessary for 64 bits.
	bsr	edx,ebx			;Scan for MSB
	jnz	short ShortNorm
	bsr	edx,esi
	jz	short ZeroChk
	sub	ecx,32 shl 16		;Adjust exponent
	mov	ebx,esi			;Push it up 32 bits
	mov	esi,eax
ShortNorm:
;Bit number in edx ranges from 0 to 31
	mov	cl,dl
	not	cl			;Convert bit number to shift count
	shld	ebx,esi,cl
	shld	esi,eax,cl
	shl	edx,16			;Move exp. adjustment to high end
	lea	ecx,[ecx+edx-(31 shl 16)] ;Adjust exponent
	xor	eax,eax			;No extra bits
	jmp	EMSEG:[RoundMode]

AddDestSign:
	xor	EMSEG:[edi].bSgn,dh
	ret

AddSourceSign:
	xor	ch,dl
	jmp	SaveResult