a0399e57ebf297182737d70f4dde20f41c4371df
[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
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;
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;
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;
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;
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;
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
446 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD9)
447 {
448 FAST486_MOD_REG_RM ModRegRm;
449 BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
450
451 /* Get the operands */
452 if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
453 {
454 /* Exception occurred */
455 return;
456 }
457
458 FPU_CHECK();
459
460 #ifndef FAST486_NO_FPU
461 // TODO: NOT IMPLEMENTED
462 UNIMPLEMENTED;
463 #else
464 /* Do nothing */
465 #endif
466 }
467
468 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA)
469 {
470 FAST486_MOD_REG_RM ModRegRm;
471 BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
472
473 /* Get the operands */
474 if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
475 {
476 /* Exception occurred */
477 return;
478 }
479
480 FPU_CHECK();
481
482 #ifndef FAST486_NO_FPU
483 // TODO: NOT IMPLEMENTED
484 UNIMPLEMENTED;
485 #else
486 /* Do nothing */
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;
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.Register << 3) | ModRegRm.SecondRegister)
515 {
516 /* FCLEX */
517 case 0x22:
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 0x23:
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 0x20:
546 /* FDISI */
547 case 0x21:
548 {
549 /* These do nothing */
550 break;
551 }
552
553 /* Invalid */
554 default:
555 {
556 Fast486Exception(State, FAST486_EXCEPTION_UD);
557 }
558 }
559 }
560
561 #endif
562 }
563
564 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD)
565 {
566 FAST486_MOD_REG_RM ModRegRm;
567 BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
568
569 /* Get the operands */
570 if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
571 {
572 /* Exception occurred */
573 return;
574 }
575
576 FPU_CHECK();
577
578 #ifndef FAST486_NO_FPU
579 // TODO: NOT IMPLEMENTED
580 UNIMPLEMENTED;
581 #else
582 /* Do nothing */
583 #endif
584 }
585
586 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDE)
587 {
588 FAST486_MOD_REG_RM ModRegRm;
589 BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
590
591 /* Get the operands */
592 if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
593 {
594 /* Exception occurred */
595 return;
596 }
597
598 FPU_CHECK();
599
600 #ifndef FAST486_NO_FPU
601 // TODO: NOT IMPLEMENTED
602 UNIMPLEMENTED;
603 #else
604 /* Do nothing */
605 #endif
606 }
607
608 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDF)
609 {
610 FAST486_MOD_REG_RM ModRegRm;
611 BOOLEAN AddressSize = State->SegmentRegs[FAST486_REG_CS].Size;
612
613 /* Get the operands */
614 if (!Fast486ParseModRegRm(State, AddressSize, &ModRegRm))
615 {
616 /* Exception occurred */
617 return;
618 }
619
620 FPU_CHECK();
621
622 #ifndef FAST486_NO_FPU
623 // TODO: NOT IMPLEMENTED
624 UNIMPLEMENTED;
625 #else
626 /* Do nothing */
627 #endif
628 }
629
630 /* EOF */