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