Partial merge of condrv_restructure branch r65657.
[reactos.git] / reactos / lib / fast486 / fpu.c
1 /*
2 * Fast486 386/486 CPU Emulation Library
3 * fpu.c
4 *
5 * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22 /* INCLUDES *******************************************************************/
23
24 #include <windef.h>
25
26 // #define NDEBUG
27 #include <debug.h>
28
29 #include <fast486.h>
30 #include "common.h"
31 #include "fpu.h"
32
33 /* PRIVATE FUNCTIONS **********************************************************/
34
35 #ifndef FAST486_NO_FPU
36
37 static ULONGLONG
38 UnsignedMult128(ULONGLONG Multiplicand,
39 ULONGLONG Multiplier,
40 ULONGLONG *HighProduct)
41 {
42 ULONG MultiplicandLow, MultiplicandHigh, MultiplierLow, MultiplierHigh;
43 ULONG IntermediateLow, IntermediateHigh;
44 ULONGLONG LowProduct, Intermediate, Intermediate1, Intermediate2;
45
46 MultiplicandLow = (ULONG)(Multiplicand & 0xFFFFFFFFULL);
47 MultiplicandHigh = (ULONG)(Multiplicand >> 32);
48 MultiplierLow = (ULONG)(Multiplier & 0xFFFFFFFFULL);
49 MultiplierHigh = (ULONG)(Multiplier >> 32);
50
51 LowProduct = (ULONGLONG)MultiplicandLow * (ULONGLONG)MultiplierLow;
52 Intermediate1 = (ULONGLONG)MultiplicandLow * (ULONGLONG)MultiplierHigh;
53 Intermediate2 = (ULONGLONG)MultiplicandHigh * (ULONGLONG)MultiplierLow;
54 *HighProduct = (ULONGLONG)MultiplicandHigh * (ULONGLONG)MultiplierHigh;
55
56 Intermediate = Intermediate1 + Intermediate2;
57 if (Intermediate < Intermediate1) *HighProduct += 1ULL << 32;
58
59 IntermediateLow = (ULONG)(Intermediate & 0xFFFFFFFFULL);
60 IntermediateHigh = (ULONG)(Intermediate >> 32);
61
62 LowProduct += (ULONGLONG)IntermediateLow << 32;
63 if ((ULONG)(LowProduct >> 32) < IntermediateLow) (*HighProduct)++;
64
65 *HighProduct += IntermediateHigh;
66 return LowProduct;
67 }
68
69 static VOID
70 Fast486FpuGetSingleReal(PFAST486_STATE State,
71 ULONG Value,
72 PFAST486_FPU_DATA_REG Result)
73 {
74 /* Extract the sign, exponent and mantissa */
75 Result->Sign = (UCHAR)(Value >> 31);
76 Result->Exponent = (USHORT)((Value >> 23) & 0xFF);
77 Result->Mantissa = (((ULONGLONG)Value & 0x7FFFFFULL) | 0x800000ULL) << 40;
78
79 /* If this is a zero, we're done */
80 if (Value == 0) return;
81
82 if (Result->Exponent == 0xFF) Result->Exponent = FPU_MAX_EXPONENT + 1;
83 else
84 {
85 /* Adjust the exponent bias */
86 Result->Exponent += (FPU_REAL10_BIAS - FPU_REAL4_BIAS);
87 }
88 }
89
90 static VOID
91 Fast486FpuGetDoubleReal(PFAST486_STATE State,
92 ULONGLONG Value,
93 PFAST486_FPU_DATA_REG Result)
94 {
95 /* Extract the sign, exponent and mantissa */
96 Result->Sign = (UCHAR)(Value >> 63);
97 Result->Exponent = (USHORT)((Value >> 52) & 0x7FF);
98 Result->Mantissa = (((ULONGLONG)Value & 0xFFFFFFFFFFFFFULL) | 0x10000000000000ULL) << 11;
99
100 /* If this is a zero, we're done */
101 if (Value == 0) return;
102
103 if (Result->Exponent == 0x3FF) Result->Exponent = FPU_MAX_EXPONENT + 1;
104 else
105 {
106 /* Adjust the exponent bias */
107 Result->Exponent += (FPU_REAL10_BIAS - FPU_REAL8_BIAS);
108 }
109 }
110
111 static VOID
112 Fast486FpuAdd(PFAST486_STATE State,
113 PFAST486_FPU_DATA_REG FirstOperand,
114 PFAST486_FPU_DATA_REG SecondOperand,
115 PFAST486_FPU_DATA_REG Result)
116 {
117 FAST486_FPU_DATA_REG FirstAdjusted = *FirstOperand;
118 FAST486_FPU_DATA_REG SecondAdjusted = *SecondOperand;
119 FAST486_FPU_DATA_REG TempResult;
120
121 if (!FPU_IS_NORMALIZED(FirstOperand) || !FPU_IS_NORMALIZED(SecondOperand))
122 {
123 /* Denormalized */
124 State->FpuStatus.De = TRUE;
125 }
126
127 /* Find the largest exponent */
128 TempResult.Exponent = max(FirstOperand->Exponent, SecondOperand->Exponent);
129
130 /* Adjust the first operand to it... */
131 if (FirstAdjusted.Exponent < TempResult.Exponent)
132 {
133 FirstAdjusted.Mantissa >>= (TempResult.Exponent - FirstAdjusted.Exponent);
134 FirstAdjusted.Exponent = TempResult.Exponent;
135 }
136
137 /* ... and the second one too */
138 if (SecondAdjusted.Exponent < TempResult.Exponent)
139 {
140 SecondAdjusted.Mantissa >>= (TempResult.Exponent - SecondAdjusted.Exponent);
141 SecondAdjusted.Exponent = TempResult.Exponent;
142 }
143
144 if (FirstAdjusted.Sign == SecondAdjusted.Sign)
145 {
146 /* Calculate the mantissa and sign of the result */
147 TempResult.Mantissa = FirstAdjusted.Mantissa + SecondAdjusted.Mantissa;
148 TempResult.Sign = FirstAdjusted.Sign;
149 }
150 else
151 {
152 /* Calculate the sign of the result */
153 if (FirstAdjusted.Mantissa > SecondAdjusted.Mantissa) TempResult.Sign = FirstAdjusted.Sign;
154 else if (FirstAdjusted.Mantissa < SecondAdjusted.Mantissa) TempResult.Sign = SecondAdjusted.Sign;
155 else TempResult.Sign = FALSE;
156
157 /* Invert the negative mantissa */
158 if (FirstAdjusted.Sign) FirstAdjusted.Mantissa = -FirstAdjusted.Mantissa;
159 if (SecondAdjusted.Sign) SecondAdjusted.Mantissa = -SecondAdjusted.Mantissa;
160
161 /* Calculate the mantissa of the result */
162 TempResult.Mantissa = FirstAdjusted.Mantissa + SecondAdjusted.Mantissa;
163 }
164
165 /* Did it overflow? */
166 if (FPU_IS_NORMALIZED(&FirstAdjusted) && FPU_IS_NORMALIZED(&SecondAdjusted))
167 {
168 if (TempResult.Exponent == FPU_MAX_EXPONENT)
169 {
170 /* Total overflow, return infinity */
171 TempResult.Mantissa = FPU_MANTISSA_HIGH_BIT;
172 TempResult.Exponent = FPU_MAX_EXPONENT + 1;
173
174 /* Update flags */
175 State->FpuStatus.Oe = TRUE;
176 }
177 else
178 {
179 /* Lose the LSB in favor of the carry */
180 TempResult.Mantissa >>= 1;
181 TempResult.Mantissa |= FPU_MANTISSA_HIGH_BIT;
182 TempResult.Exponent++;
183 }
184 }
185
186 /* Normalize the result and return it */
187 Fast486FpuNormalize(State, &TempResult);
188 *Result = TempResult;
189 }
190
191 static VOID
192 Fast486FpuSubtract(PFAST486_STATE State,
193 PFAST486_FPU_DATA_REG FirstOperand,
194 PFAST486_FPU_DATA_REG SecondOperand,
195 PFAST486_FPU_DATA_REG Result)
196 {
197 FAST486_FPU_DATA_REG NegativeSecondOperand = *SecondOperand;
198
199 /* Invert the sign */
200 NegativeSecondOperand.Sign = !NegativeSecondOperand.Sign;
201
202 /* And perform an addition instead */
203 Fast486FpuAdd(State, Result, FirstOperand, &NegativeSecondOperand);
204 }
205
206 static VOID
207 Fast486FpuCompare(PFAST486_STATE State,
208 PFAST486_FPU_DATA_REG FirstOperand,
209 PFAST486_FPU_DATA_REG SecondOperand)
210 {
211 if (FPU_IS_NAN(FirstOperand) || FPU_IS_NAN(SecondOperand))
212 {
213 if (FPU_IS_POS_INF(FirstOperand) && FPU_IS_NEG_INF(SecondOperand))
214 {
215 State->FpuStatus.Code0 = FALSE;
216 State->FpuStatus.Code2 = FALSE;
217 State->FpuStatus.Code3 = FALSE;
218 }
219 else if (FPU_IS_NEG_INF(FirstOperand) && FPU_IS_POS_INF(SecondOperand))
220 {
221 State->FpuStatus.Code0 = TRUE;
222 State->FpuStatus.Code2 = FALSE;
223 State->FpuStatus.Code3 = FALSE;
224 }
225 else
226 {
227 State->FpuStatus.Code0 = TRUE;
228 State->FpuStatus.Code2 = TRUE;
229 State->FpuStatus.Code3 = TRUE;
230 }
231 }
232 else
233 {
234 FAST486_FPU_DATA_REG TempResult;
235
236 Fast486FpuSubtract(State, FirstOperand, SecondOperand, &TempResult);
237
238 if (FPU_IS_ZERO(&TempResult))
239 {
240 State->FpuStatus.Code0 = FALSE;
241 State->FpuStatus.Code2 = FALSE;
242 State->FpuStatus.Code3 = TRUE;
243 }
244 else if (TempResult.Sign)
245 {
246 State->FpuStatus.Code0 = TRUE;
247 State->FpuStatus.Code2 = FALSE;
248 State->FpuStatus.Code3 = FALSE;
249 }
250 else
251 {
252 State->FpuStatus.Code0 = FALSE;
253 State->FpuStatus.Code2 = FALSE;
254 State->FpuStatus.Code3 = FALSE;
255 }
256 }
257 }
258
259 static VOID
260 Fast486FpuMultiply(PFAST486_STATE State,
261 PFAST486_FPU_DATA_REG FirstOperand,
262 PFAST486_FPU_DATA_REG SecondOperand,
263 PFAST486_FPU_DATA_REG Result)
264 {
265 FAST486_FPU_DATA_REG TempResult;
266
267 if (!FPU_IS_NORMALIZED(FirstOperand) || !FPU_IS_NORMALIZED(SecondOperand))
268 {
269 /* Denormalized */
270 State->FpuStatus.De = TRUE;
271 }
272
273 UnsignedMult128(FirstOperand->Mantissa,
274 SecondOperand->Mantissa,
275 &TempResult.Mantissa);
276
277 TempResult.Exponent = FirstOperand->Exponent + SecondOperand->Exponent;
278 TempResult.Sign = FirstOperand->Sign ^ SecondOperand->Sign;
279
280 /* Normalize the result */
281 Fast486FpuNormalize(State, &TempResult);
282 *Result = TempResult;
283 }
284
285 static VOID
286 Fast486FpuDivide(PFAST486_STATE State,
287 PFAST486_FPU_DATA_REG FirstOperand,
288 PFAST486_FPU_DATA_REG SecondOperand,
289 PFAST486_FPU_DATA_REG Result)
290 {
291 FAST486_FPU_DATA_REG TempResult;
292
293 if (FPU_IS_ZERO(SecondOperand))
294 {
295 /* Division by zero */
296 State->FpuStatus.Ze = TRUE;
297 return;
298 }
299
300 TempResult.Exponent = FirstOperand->Exponent - SecondOperand->Exponent;
301 TempResult.Sign = FirstOperand->Sign ^ SecondOperand->Sign;
302
303 // TODO: NOT IMPLEMENTED
304 UNREFERENCED_PARAMETER(TempResult);
305 UNIMPLEMENTED;
306 }
307
308 #endif
309
310 /* PUBLIC FUNCTIONS ***********************************************************/
311
312 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8DC)
313 {
314 FAST486_MOD_REG_RM ModRegRm;
315 BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
316 PFAST486_FPU_DATA_REG SourceOperand, DestOperand;
317 FAST486_FPU_DATA_REG MemoryData;
318
319 /* Get the operands */
320 if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
321 {
322 /* Exception occurred */
323 return;
324 }
325
326 FPU_CHECK();
327
328 #ifndef FAST486_NO_FPU
329
330 if (ModRegRm.Memory)
331 {
332 /* Load the source operand from memory */
333
334 if (Opcode == 0xDC)
335 {
336 ULONGLONG Value;
337
338 if (!Fast486ReadMemory(State,
339 (State->PrefixFlags & FAST486_PREFIX_SEG)
340 ? State->SegmentOverride : FAST486_REG_DS,
341 ModRegRm.MemoryAddress,
342 FALSE,
343 &Value,
344 sizeof(ULONGLONG)))
345 {
346 /* Exception occurred */
347 return;
348 }
349
350 Fast486FpuGetDoubleReal(State, Value, &MemoryData);
351 }
352 else
353 {
354 ULONG Value;
355
356 if (!Fast486ReadModrmDwordOperands(State, &ModRegRm, NULL, &Value))
357 {
358 /* Exception occurred */
359 return;
360 }
361
362 Fast486FpuGetSingleReal(State, Value, &MemoryData);
363 }
364
365 SourceOperand = &MemoryData;
366 }
367 else
368 {
369 /* Load the source operand from an FPU register */
370 SourceOperand = &FPU_ST(ModRegRm.SecondRegister);
371
372 if (FPU_GET_TAG(ModRegRm.SecondRegister) == FPU_TAG_EMPTY)
373 {
374 /* Invalid operation */
375 State->FpuStatus.Ie = TRUE;
376 return;
377 }
378 }
379
380 /* The destination operand is always ST0 */
381 DestOperand = &FPU_ST(0);
382
383 if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
384 {
385 /* Invalid operation */
386 State->FpuStatus.Ie = TRUE;
387 return;
388 }
389
390 /* Check the operation */
391 switch (ModRegRm.Register)
392 {
393 /* FADD */
394 case 0:
395 {
396 Fast486FpuAdd(State, DestOperand, SourceOperand, DestOperand);
397 break;
398 }
399
400 /* FMUL */
401 case 1:
402 {
403 Fast486FpuMultiply(State, DestOperand, SourceOperand, DestOperand);
404 break;
405 }
406
407 /* FCOM */
408 case 2:
409 /* FCOMP */
410 case 3:
411 {
412 Fast486FpuCompare(State, DestOperand, SourceOperand);
413 if (ModRegRm.Register == 3) Fast486FpuPop(State);
414
415 break;
416 }
417
418 /* FSUB */
419 case 4:
420 {
421 Fast486FpuSubtract(State, DestOperand, SourceOperand, DestOperand);
422 break;
423 }
424
425 /* FSUBR */
426 case 5:
427 {
428 Fast486FpuSubtract(State, SourceOperand, DestOperand, DestOperand);
429 break;
430 }
431
432 /* FDIV */
433 case 6:
434 {
435 Fast486FpuDivide(State, DestOperand, SourceOperand, DestOperand);
436 break;
437 }
438
439 /* FDIVR */
440 case 7:
441 {
442 Fast486FpuDivide(State, SourceOperand, DestOperand, DestOperand);
443 break;
444 }
445 }
446
447 #endif
448 }
449
450 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD9)
451 {
452 FAST486_MOD_REG_RM ModRegRm;
453 BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
454
455 /* Get the operands */
456 if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
457 {
458 /* Exception occurred */
459 return;
460 }
461
462 FPU_CHECK();
463
464 #ifndef FAST486_NO_FPU
465 // TODO: NOT IMPLEMENTED
466 UNIMPLEMENTED;
467 #else
468 /* Do nothing */
469 #endif
470 }
471
472 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA)
473 {
474 FAST486_MOD_REG_RM ModRegRm;
475 BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
476
477 /* Get the operands */
478 if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
479 {
480 /* Exception occurred */
481 return;
482 }
483
484 FPU_CHECK();
485
486 #ifndef FAST486_NO_FPU
487 // TODO: NOT IMPLEMENTED
488 UNIMPLEMENTED;
489 #else
490 /* Do nothing */
491 #endif
492 }
493
494 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB)
495 {
496 FAST486_MOD_REG_RM ModRegRm;
497 BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
498
499 /* Get the operands */
500 if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
501 {
502 /* Exception occurred */
503 return;
504 }
505
506 FPU_CHECK();
507
508 #ifndef FAST486_NO_FPU
509
510 if (ModRegRm.Memory)
511 {
512 // TODO: NOT IMPLEMENTED
513 UNIMPLEMENTED;
514 }
515 else
516 {
517 /* Only a few of these instructions have any meaning on a 487 */
518 switch ((ModRegRm.Register << 3) | ModRegRm.SecondRegister)
519 {
520 /* FCLEX */
521 case 0x22:
522 {
523 /* Clear exception data */
524 State->FpuStatus.Ie =
525 State->FpuStatus.De =
526 State->FpuStatus.Ze =
527 State->FpuStatus.Oe =
528 State->FpuStatus.Ue =
529 State->FpuStatus.Pe =
530 State->FpuStatus.Sf =
531 State->FpuStatus.Es =
532 State->FpuStatus.Busy = FALSE;
533
534 break;
535 }
536
537 /* FINIT */
538 case 0x23:
539 {
540 /* Restore the state */
541 State->FpuControl.Value = FAST486_FPU_DEFAULT_CONTROL;
542 State->FpuStatus.Value = 0;
543 State->FpuTag = 0xFFFF;
544
545 break;
546 }
547
548 /* FENI */
549 case 0x20:
550 /* FDISI */
551 case 0x21:
552 {
553 /* These do nothing */
554 break;
555 }
556
557 /* Invalid */
558 default:
559 {
560 Fast486Exception(State, FAST486_EXCEPTION_UD);
561 }
562 }
563 }
564
565 #endif
566 }
567
568 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD)
569 {
570 FAST486_MOD_REG_RM ModRegRm;
571 BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
572
573 /* Get the operands */
574 if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
575 {
576 /* Exception occurred */
577 return;
578 }
579
580 FPU_CHECK();
581
582 #ifndef FAST486_NO_FPU
583 // TODO: NOT IMPLEMENTED
584 UNIMPLEMENTED;
585 #else
586 /* Do nothing */
587 #endif
588 }
589
590 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDE)
591 {
592 FAST486_MOD_REG_RM ModRegRm;
593 BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
594
595 /* Get the operands */
596 if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
597 {
598 /* Exception occurred */
599 return;
600 }
601
602 FPU_CHECK();
603
604 #ifndef FAST486_NO_FPU
605 // TODO: NOT IMPLEMENTED
606 UNIMPLEMENTED;
607 #else
608 /* Do nothing */
609 #endif
610 }
611
612 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDF)
613 {
614 FAST486_MOD_REG_RM ModRegRm;
615 BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
616
617 /* Get the operands */
618 if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
619 {
620 /* Exception occurred */
621 return;
622 }
623
624 FPU_CHECK();
625
626 #ifndef FAST486_NO_FPU
627 // TODO: NOT IMPLEMENTED
628 UNIMPLEMENTED;
629 #else
630 /* Do nothing */
631 #endif
632 }
633
634 /* EOF */