2 * Fast486 386/486 CPU Emulation Library
5 * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
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.
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.
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.
22 /* INCLUDES *******************************************************************/
33 /* PRIVATE FUNCTIONS **********************************************************/
35 #ifndef FAST486_NO_FPU
38 UnsignedMult128(ULONGLONG Multiplicand
,
40 ULONGLONG
*HighProduct
)
42 ULONG MultiplicandLow
, MultiplicandHigh
, MultiplierLow
, MultiplierHigh
;
43 ULONG IntermediateLow
, IntermediateHigh
;
44 ULONGLONG LowProduct
, Intermediate
, Intermediate1
, Intermediate2
;
46 MultiplicandLow
= (ULONG
)(Multiplicand
& 0xFFFFFFFFULL
);
47 MultiplicandHigh
= (ULONG
)(Multiplicand
>> 32);
48 MultiplierLow
= (ULONG
)(Multiplier
& 0xFFFFFFFFULL
);
49 MultiplierHigh
= (ULONG
)(Multiplier
>> 32);
51 LowProduct
= (ULONGLONG
)MultiplicandLow
* (ULONGLONG
)MultiplierLow
;
52 Intermediate1
= (ULONGLONG
)MultiplicandLow
* (ULONGLONG
)MultiplierHigh
;
53 Intermediate2
= (ULONGLONG
)MultiplicandHigh
* (ULONGLONG
)MultiplierLow
;
54 *HighProduct
= (ULONGLONG
)MultiplicandHigh
* (ULONGLONG
)MultiplierHigh
;
56 Intermediate
= Intermediate1
+ Intermediate2
;
57 if (Intermediate
< Intermediate1
) *HighProduct
+= 1ULL << 32;
59 IntermediateLow
= (ULONG
)(Intermediate
& 0xFFFFFFFFULL
);
60 IntermediateHigh
= (ULONG
)(Intermediate
>> 32);
62 LowProduct
+= (ULONGLONG
)IntermediateLow
<< 32;
63 if ((ULONG
)(LowProduct
>> 32) < IntermediateLow
) (*HighProduct
)++;
65 *HighProduct
+= IntermediateHigh
;
70 Fast486FpuGetSingleReal(PFAST486_STATE State
,
72 PFAST486_FPU_DATA_REG Result
)
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;
79 /* If this is a zero, we're done */
80 if (Value
== 0) return;
82 if (Result
->Exponent
== 0xFF) Result
->Exponent
= FPU_MAX_EXPONENT
+ 1;
85 /* Adjust the exponent bias */
86 Result
->Exponent
+= (FPU_REAL10_BIAS
- FPU_REAL4_BIAS
);
91 Fast486FpuGetDoubleReal(PFAST486_STATE State
,
93 PFAST486_FPU_DATA_REG Result
)
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;
100 /* If this is a zero, we're done */
101 if (Value
== 0) return;
103 if (Result
->Exponent
== 0x3FF) Result
->Exponent
= FPU_MAX_EXPONENT
+ 1;
106 /* Adjust the exponent bias */
107 Result
->Exponent
+= (FPU_REAL10_BIAS
- FPU_REAL8_BIAS
);
112 Fast486FpuAdd(PFAST486_STATE State
,
113 PFAST486_FPU_DATA_REG FirstOperand
,
114 PFAST486_FPU_DATA_REG SecondOperand
,
115 PFAST486_FPU_DATA_REG Result
)
117 FAST486_FPU_DATA_REG FirstAdjusted
= *FirstOperand
;
118 FAST486_FPU_DATA_REG SecondAdjusted
= *SecondOperand
;
119 FAST486_FPU_DATA_REG TempResult
;
121 if (!FPU_IS_NORMALIZED(FirstOperand
) || !FPU_IS_NORMALIZED(SecondOperand
))
124 State
->FpuStatus
.De
= TRUE
;
127 /* Find the largest exponent */
128 TempResult
.Exponent
= max(FirstOperand
->Exponent
, SecondOperand
->Exponent
);
130 /* Adjust the first operand to it... */
131 if (FirstAdjusted
.Exponent
< TempResult
.Exponent
)
133 FirstAdjusted
.Mantissa
>>= (TempResult
.Exponent
- FirstAdjusted
.Exponent
);
134 FirstAdjusted
.Exponent
= TempResult
.Exponent
;
137 /* ... and the second one too */
138 if (SecondAdjusted
.Exponent
< TempResult
.Exponent
)
140 SecondAdjusted
.Mantissa
>>= (TempResult
.Exponent
- SecondAdjusted
.Exponent
);
141 SecondAdjusted
.Exponent
= TempResult
.Exponent
;
144 if (FirstAdjusted
.Sign
== SecondAdjusted
.Sign
)
146 /* Calculate the mantissa and sign of the result */
147 TempResult
.Mantissa
= FirstAdjusted
.Mantissa
+ SecondAdjusted
.Mantissa
;
148 TempResult
.Sign
= FirstAdjusted
.Sign
;
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
;
157 /* Invert the negative mantissa */
158 if (FirstAdjusted
.Sign
) FirstAdjusted
.Mantissa
= -FirstAdjusted
.Mantissa
;
159 if (SecondAdjusted
.Sign
) SecondAdjusted
.Mantissa
= -SecondAdjusted
.Mantissa
;
161 /* Calculate the mantissa of the result */
162 TempResult
.Mantissa
= FirstAdjusted
.Mantissa
+ SecondAdjusted
.Mantissa
;
165 /* Did it overflow? */
166 if (FPU_IS_NORMALIZED(&FirstAdjusted
) && FPU_IS_NORMALIZED(&SecondAdjusted
))
168 if (TempResult
.Exponent
== FPU_MAX_EXPONENT
)
170 /* Total overflow, return infinity */
171 TempResult
.Mantissa
= FPU_MANTISSA_HIGH_BIT
;
172 TempResult
.Exponent
= FPU_MAX_EXPONENT
+ 1;
175 State
->FpuStatus
.Oe
= TRUE
;
179 /* Lose the LSB in favor of the carry */
180 TempResult
.Mantissa
>>= 1;
181 TempResult
.Mantissa
|= FPU_MANTISSA_HIGH_BIT
;
182 TempResult
.Exponent
++;
186 /* Normalize the result and return it */
187 Fast486FpuNormalize(State
, &TempResult
);
188 *Result
= TempResult
;
192 Fast486FpuSubtract(PFAST486_STATE State
,
193 PFAST486_FPU_DATA_REG FirstOperand
,
194 PFAST486_FPU_DATA_REG SecondOperand
,
195 PFAST486_FPU_DATA_REG Result
)
197 FAST486_FPU_DATA_REG NegativeSecondOperand
= *SecondOperand
;
199 /* Invert the sign */
200 NegativeSecondOperand
.Sign
= !NegativeSecondOperand
.Sign
;
202 /* And perform an addition instead */
203 Fast486FpuAdd(State
, Result
, FirstOperand
, &NegativeSecondOperand
);
207 Fast486FpuCompare(PFAST486_STATE State
,
208 PFAST486_FPU_DATA_REG FirstOperand
,
209 PFAST486_FPU_DATA_REG SecondOperand
)
211 if (FPU_IS_NAN(FirstOperand
) || FPU_IS_NAN(SecondOperand
))
213 if (FPU_IS_POS_INF(FirstOperand
) && FPU_IS_NEG_INF(SecondOperand
))
215 State
->FpuStatus
.Code0
= FALSE
;
216 State
->FpuStatus
.Code2
= FALSE
;
217 State
->FpuStatus
.Code3
= FALSE
;
219 else if (FPU_IS_NEG_INF(FirstOperand
) && FPU_IS_POS_INF(SecondOperand
))
221 State
->FpuStatus
.Code0
= TRUE
;
222 State
->FpuStatus
.Code2
= FALSE
;
223 State
->FpuStatus
.Code3
= FALSE
;
227 State
->FpuStatus
.Code0
= TRUE
;
228 State
->FpuStatus
.Code2
= TRUE
;
229 State
->FpuStatus
.Code3
= TRUE
;
234 FAST486_FPU_DATA_REG TempResult
;
236 Fast486FpuSubtract(State
, FirstOperand
, SecondOperand
, &TempResult
);
238 if (FPU_IS_ZERO(&TempResult
))
240 State
->FpuStatus
.Code0
= FALSE
;
241 State
->FpuStatus
.Code2
= FALSE
;
242 State
->FpuStatus
.Code3
= TRUE
;
244 else if (TempResult
.Sign
)
246 State
->FpuStatus
.Code0
= TRUE
;
247 State
->FpuStatus
.Code2
= FALSE
;
248 State
->FpuStatus
.Code3
= FALSE
;
252 State
->FpuStatus
.Code0
= FALSE
;
253 State
->FpuStatus
.Code2
= FALSE
;
254 State
->FpuStatus
.Code3
= FALSE
;
260 Fast486FpuMultiply(PFAST486_STATE State
,
261 PFAST486_FPU_DATA_REG FirstOperand
,
262 PFAST486_FPU_DATA_REG SecondOperand
,
263 PFAST486_FPU_DATA_REG Result
)
265 FAST486_FPU_DATA_REG TempResult
;
267 if (!FPU_IS_NORMALIZED(FirstOperand
) || !FPU_IS_NORMALIZED(SecondOperand
))
270 State
->FpuStatus
.De
= TRUE
;
273 UnsignedMult128(FirstOperand
->Mantissa
,
274 SecondOperand
->Mantissa
,
275 &TempResult
.Mantissa
);
277 TempResult
.Exponent
= FirstOperand
->Exponent
+ SecondOperand
->Exponent
;
278 TempResult
.Sign
= FirstOperand
->Sign
^ SecondOperand
->Sign
;
280 /* Normalize the result */
281 Fast486FpuNormalize(State
, &TempResult
);
282 *Result
= TempResult
;
286 Fast486FpuDivide(PFAST486_STATE State
,
287 PFAST486_FPU_DATA_REG FirstOperand
,
288 PFAST486_FPU_DATA_REG SecondOperand
,
289 PFAST486_FPU_DATA_REG Result
)
291 FAST486_FPU_DATA_REG TempResult
;
293 if (FPU_IS_ZERO(SecondOperand
))
295 /* Division by zero */
296 State
->FpuStatus
.Ze
= TRUE
;
300 TempResult
.Exponent
= FirstOperand
->Exponent
- SecondOperand
->Exponent
;
301 TempResult
.Sign
= FirstOperand
->Sign
^ SecondOperand
->Sign
;
303 // TODO: NOT IMPLEMENTED
304 UNREFERENCED_PARAMETER(TempResult
);
310 /* PUBLIC FUNCTIONS ***********************************************************/
312 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8DC
)
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
;
319 /* Get the operands */
320 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
322 /* Exception occurred */
328 #ifndef FAST486_NO_FPU
332 /* Load the source operand from memory */
338 if (!Fast486ReadMemory(State
,
339 (State
->PrefixFlags
& FAST486_PREFIX_SEG
)
340 ? State
->SegmentOverride
: FAST486_REG_DS
,
341 ModRegRm
.MemoryAddress
,
346 /* Exception occurred */
350 Fast486FpuGetDoubleReal(State
, Value
, &MemoryData
);
356 if (!Fast486ReadModrmDwordOperands(State
, &ModRegRm
, NULL
, &Value
))
358 /* Exception occurred */
362 Fast486FpuGetSingleReal(State
, Value
, &MemoryData
);
365 SourceOperand
= &MemoryData
;
369 /* Load the source operand from an FPU register */
370 SourceOperand
= &FPU_ST(ModRegRm
.SecondRegister
);
372 if (FPU_GET_TAG(ModRegRm
.SecondRegister
) == FPU_TAG_EMPTY
)
374 /* Invalid operation */
375 State
->FpuStatus
.Ie
= TRUE
;
380 /* The destination operand is always ST0 */
381 DestOperand
= &FPU_ST(0);
383 if (FPU_GET_TAG(0) == FPU_TAG_EMPTY
)
385 /* Invalid operation */
386 State
->FpuStatus
.Ie
= TRUE
;
390 /* Check the operation */
391 switch (ModRegRm
.Register
)
396 Fast486FpuAdd(State
, DestOperand
, SourceOperand
, DestOperand
);
403 Fast486FpuMultiply(State
, DestOperand
, SourceOperand
, DestOperand
);
412 Fast486FpuCompare(State
, DestOperand
, SourceOperand
);
413 if (ModRegRm
.Register
== 3) Fast486FpuPop(State
);
421 Fast486FpuSubtract(State
, DestOperand
, SourceOperand
, DestOperand
);
428 Fast486FpuSubtract(State
, SourceOperand
, DestOperand
, DestOperand
);
435 Fast486FpuDivide(State
, DestOperand
, SourceOperand
, DestOperand
);
442 Fast486FpuDivide(State
, SourceOperand
, DestOperand
, DestOperand
);
450 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD9
)
452 FAST486_MOD_REG_RM ModRegRm
;
453 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
455 /* Get the operands */
456 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
458 /* Exception occurred */
464 #ifndef FAST486_NO_FPU
465 // TODO: NOT IMPLEMENTED
472 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA
)
474 FAST486_MOD_REG_RM ModRegRm
;
475 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
477 /* Get the operands */
478 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
480 /* Exception occurred */
486 #ifndef FAST486_NO_FPU
487 // TODO: NOT IMPLEMENTED
494 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB
)
496 FAST486_MOD_REG_RM ModRegRm
;
497 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
499 /* Get the operands */
500 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
502 /* Exception occurred */
508 #ifndef FAST486_NO_FPU
512 // TODO: NOT IMPLEMENTED
517 /* Only a few of these instructions have any meaning on a 487 */
518 switch ((ModRegRm
.Register
<< 3) | ModRegRm
.SecondRegister
)
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
;
540 /* Restore the state */
541 State
->FpuControl
.Value
= FAST486_FPU_DEFAULT_CONTROL
;
542 State
->FpuStatus
.Value
= 0;
543 State
->FpuTag
= 0xFFFF;
553 /* These do nothing */
560 Fast486Exception(State
, FAST486_EXCEPTION_UD
);
568 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD
)
570 FAST486_MOD_REG_RM ModRegRm
;
571 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
573 /* Get the operands */
574 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
576 /* Exception occurred */
582 #ifndef FAST486_NO_FPU
583 // TODO: NOT IMPLEMENTED
590 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDE
)
592 FAST486_MOD_REG_RM ModRegRm
;
593 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
595 /* Get the operands */
596 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
598 /* Exception occurred */
604 #ifndef FAST486_NO_FPU
605 // TODO: NOT IMPLEMENTED
612 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDF
)
614 FAST486_MOD_REG_RM ModRegRm
;
615 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
617 /* Get the operands */
618 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
620 /* Exception occurred */
626 #ifndef FAST486_NO_FPU
627 // TODO: NOT IMPLEMENTED