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