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 static ULONGLONG
UnsignedMult128(ULONGLONG Multiplicand
,
37 ULONGLONG
*HighProduct
)
39 ULONG MultiplicandLow
, MultiplicandHigh
, MultiplierLow
, MultiplierHigh
;
40 ULONG IntermediateLow
, IntermediateHigh
;
41 ULONGLONG LowProduct
, Intermediate
, Intermediate1
, Intermediate2
;
43 MultiplicandLow
= (ULONG
)(Multiplicand
& 0xFFFFFFFFULL
);
44 MultiplicandHigh
= (ULONG
)(Multiplicand
>> 32);
45 MultiplierLow
= (ULONG
)(Multiplier
& 0xFFFFFFFFULL
);
46 MultiplierHigh
= (ULONG
)(Multiplier
>> 32);
48 LowProduct
= (ULONGLONG
)MultiplicandLow
* (ULONGLONG
)MultiplierLow
;
49 Intermediate1
= (ULONGLONG
)MultiplicandLow
* (ULONGLONG
)MultiplierHigh
;
50 Intermediate2
= (ULONGLONG
)MultiplicandHigh
* (ULONGLONG
)MultiplierLow
;
51 *HighProduct
= (ULONGLONG
)MultiplicandHigh
* (ULONGLONG
)MultiplierHigh
;
53 Intermediate
= Intermediate1
+ Intermediate2
;
54 if (Intermediate
< Intermediate1
) *HighProduct
+= 1ULL << 32;
56 IntermediateLow
= (ULONG
)(Intermediate
& 0xFFFFFFFFULL
);
57 IntermediateHigh
= (ULONG
)(Intermediate
>> 32);
59 LowProduct
+= (ULONGLONG
)IntermediateLow
<< 32;
60 if ((ULONG
)(LowProduct
>> 32) < IntermediateLow
) (*HighProduct
)++;
62 *HighProduct
+= IntermediateHigh
;
66 static VOID
Fast486FpuGetSingleReal(PFAST486_STATE State
,
68 PFAST486_FPU_DATA_REG Result
)
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;
75 /* If this is a zero, we're done */
76 if (Value
== 0) return;
78 if (Result
->Exponent
== 0xFF) Result
->Exponent
= FPU_MAX_EXPONENT
+ 1;
81 /* Adjust the exponent bias */
82 Result
->Exponent
+= (FPU_REAL10_BIAS
- FPU_REAL4_BIAS
);
86 static VOID
Fast486FpuGetDoubleReal(PFAST486_STATE State
,
88 PFAST486_FPU_DATA_REG Result
)
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;
95 /* If this is a zero, we're done */
96 if (Value
== 0) return;
98 if (Result
->Exponent
== 0x3FF) Result
->Exponent
= FPU_MAX_EXPONENT
+ 1;
101 /* Adjust the exponent bias */
102 Result
->Exponent
+= (FPU_REAL10_BIAS
- FPU_REAL8_BIAS
);
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
)
111 FAST486_FPU_DATA_REG FirstAdjusted
= *FirstOperand
;
112 FAST486_FPU_DATA_REG SecondAdjusted
= *SecondOperand
;
113 FAST486_FPU_DATA_REG TempResult
;
115 if (!FPU_IS_NORMALIZED(FirstOperand
) || !FPU_IS_NORMALIZED(SecondOperand
))
118 State
->FpuStatus
.De
= TRUE
;
121 /* Find the largest exponent */
122 TempResult
.Exponent
= max(FirstOperand
->Exponent
, SecondOperand
->Exponent
);
124 /* Adjust the first operand to it */
125 if (FirstAdjusted
.Exponent
< TempResult
.Exponent
)
127 FirstAdjusted
.Mantissa
>>= (TempResult
.Exponent
- FirstAdjusted
.Exponent
);
128 FirstAdjusted
.Exponent
= TempResult
.Exponent
;
131 /* ... and the second one too */
132 if (SecondAdjusted
.Exponent
< TempResult
.Exponent
)
134 SecondAdjusted
.Mantissa
>>= (TempResult
.Exponent
- SecondAdjusted
.Exponent
);
135 SecondAdjusted
.Exponent
= TempResult
.Exponent
;
138 if (FirstAdjusted
.Sign
== SecondAdjusted
.Sign
)
140 /* Calculate the mantissa and sign of the result */
141 TempResult
.Mantissa
= FirstAdjusted
.Mantissa
+ SecondAdjusted
.Mantissa
;
142 TempResult
.Sign
= FirstAdjusted
.Sign
;
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
;
151 /* Invert the negative mantissa */
152 if (FirstAdjusted
.Sign
) FirstAdjusted
.Mantissa
= -FirstAdjusted
.Mantissa
;
153 if (SecondAdjusted
.Sign
) SecondAdjusted
.Mantissa
= -SecondAdjusted
.Mantissa
;
155 /* Calculate the mantissa of the result */
156 TempResult
.Mantissa
= FirstAdjusted
.Mantissa
+ SecondAdjusted
.Mantissa
;
159 /* Did it overflow? */
160 if (FPU_IS_NORMALIZED(&FirstAdjusted
) && FPU_IS_NORMALIZED(&SecondAdjusted
))
162 if (TempResult
.Exponent
== FPU_MAX_EXPONENT
)
164 /* Total overflow, return infinity */
165 TempResult
.Mantissa
= FPU_MANTISSA_HIGH_BIT
;
166 TempResult
.Exponent
= FPU_MAX_EXPONENT
+ 1;
169 State
->FpuStatus
.Oe
= TRUE
;
173 /* Lose the LSB in favor of the carry */
174 TempResult
.Mantissa
>>= 1;
175 TempResult
.Mantissa
|= FPU_MANTISSA_HIGH_BIT
;
176 TempResult
.Exponent
++;
180 /* Normalize the result and return it */
181 Fast486FpuNormalize(State
, &TempResult
);
182 *Result
= TempResult
;
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
)
190 FAST486_FPU_DATA_REG NegativeSecondOperand
= *SecondOperand
;
192 /* Invert the sign */
193 NegativeSecondOperand
.Sign
= !NegativeSecondOperand
.Sign
;
195 /* And perform an addition instead */
196 Fast486FpuAdd(State
, Result
, FirstOperand
, &NegativeSecondOperand
);
199 static VOID
Fast486FpuCompare(PFAST486_STATE State
,
200 PFAST486_FPU_DATA_REG FirstOperand
,
201 PFAST486_FPU_DATA_REG SecondOperand
)
203 if (FPU_IS_NAN(FirstOperand
) || FPU_IS_NAN(SecondOperand
))
205 if (FPU_IS_POS_INF(FirstOperand
) && FPU_IS_NEG_INF(SecondOperand
))
207 State
->FpuStatus
.Code0
= FALSE
;
208 State
->FpuStatus
.Code2
= FALSE
;
209 State
->FpuStatus
.Code3
= FALSE
;
211 else if (FPU_IS_NEG_INF(FirstOperand
) && FPU_IS_POS_INF(SecondOperand
))
213 State
->FpuStatus
.Code0
= TRUE
;
214 State
->FpuStatus
.Code2
= FALSE
;
215 State
->FpuStatus
.Code3
= FALSE
;
219 State
->FpuStatus
.Code0
= TRUE
;
220 State
->FpuStatus
.Code2
= TRUE
;
221 State
->FpuStatus
.Code3
= TRUE
;
226 FAST486_FPU_DATA_REG TempResult
;
228 Fast486FpuSubtract(State
, FirstOperand
, SecondOperand
, &TempResult
);
230 if (FPU_IS_ZERO(&TempResult
))
232 State
->FpuStatus
.Code0
= FALSE
;
233 State
->FpuStatus
.Code2
= FALSE
;
234 State
->FpuStatus
.Code3
= TRUE
;
236 else if (TempResult
.Sign
)
238 State
->FpuStatus
.Code0
= TRUE
;
239 State
->FpuStatus
.Code2
= FALSE
;
240 State
->FpuStatus
.Code3
= FALSE
;
244 State
->FpuStatus
.Code0
= FALSE
;
245 State
->FpuStatus
.Code2
= FALSE
;
246 State
->FpuStatus
.Code3
= FALSE
;
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
)
256 FAST486_FPU_DATA_REG TempResult
;
258 if (!FPU_IS_NORMALIZED(FirstOperand
) || !FPU_IS_NORMALIZED(SecondOperand
))
261 State
->FpuStatus
.De
= TRUE
;
264 UnsignedMult128(FirstOperand
->Mantissa
,
265 SecondOperand
->Mantissa
,
266 &TempResult
.Mantissa
);
268 TempResult
.Exponent
= FirstOperand
->Exponent
+ SecondOperand
->Exponent
;
269 TempResult
.Sign
= FirstOperand
->Sign
^ SecondOperand
->Sign
;
271 /* Normalize the result */
272 Fast486FpuNormalize(State
, &TempResult
);
273 *Result
= TempResult
;
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
)
281 FAST486_FPU_DATA_REG TempResult
;
283 if (FPU_IS_ZERO(SecondOperand
))
285 /* Division by zero */
286 State
->FpuStatus
.Ze
= TRUE
;
290 TempResult
.Exponent
= FirstOperand
->Exponent
- SecondOperand
->Exponent
;
291 TempResult
.Sign
= FirstOperand
->Sign
^ SecondOperand
->Sign
;
293 // TODO: NOT IMPLEMENTED
294 UNREFERENCED_PARAMETER(TempResult
);
298 /* PUBLIC FUNCTIONS ***********************************************************/
300 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8DC
)
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
;
307 /* Get the operands */
308 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
310 /* Exception occurred */
316 #ifndef FAST486_NO_FPU
320 /* Load the source operand from memory */
326 if (!Fast486ReadMemory(State
,
327 (State
->PrefixFlags
& FAST486_PREFIX_SEG
)
328 ? State
->SegmentOverride
: FAST486_REG_DS
,
329 ModRegRm
.MemoryAddress
,
334 /* Exception occurred */
338 Fast486FpuGetDoubleReal(State
, Value
, &MemoryData
);
344 if (!Fast486ReadModrmDwordOperands(State
, &ModRegRm
, NULL
, &Value
))
346 /* Exception occurred */
350 Fast486FpuGetSingleReal(State
, Value
, &MemoryData
);
353 SourceOperand
= &MemoryData
;
357 /* Load the source operand from an FPU register */
358 SourceOperand
= &FPU_ST(ModRegRm
.SecondRegister
);
360 if (FPU_GET_TAG(ModRegRm
.SecondRegister
) == FPU_TAG_EMPTY
)
362 /* Invalid operation */
363 State
->FpuStatus
.Ie
= TRUE
;
368 /* The destination operand is always ST0 */
369 DestOperand
= &FPU_ST(0);
371 if (FPU_GET_TAG(0) == FPU_TAG_EMPTY
)
373 /* Invalid operation */
374 State
->FpuStatus
.Ie
= TRUE
;
378 /* Check the operation */
379 switch (ModRegRm
.Register
)
384 Fast486FpuAdd(State
, DestOperand
, SourceOperand
, DestOperand
);
391 Fast486FpuMultiply(State
, DestOperand
, SourceOperand
, DestOperand
);
400 Fast486FpuCompare(State
, DestOperand
, SourceOperand
);
401 if (ModRegRm
.Register
== 3) Fast486FpuPop(State
);
409 Fast486FpuSubtract(State
, DestOperand
, SourceOperand
, DestOperand
);
416 Fast486FpuSubtract(State
, SourceOperand
, DestOperand
, DestOperand
);
423 Fast486FpuDivide(State
, DestOperand
, SourceOperand
, DestOperand
);
430 Fast486FpuDivide(State
, SourceOperand
, DestOperand
, DestOperand
);
440 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD9
)
442 FAST486_MOD_REG_RM ModRegRm
;
443 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
445 /* Get the operands */
446 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
448 /* Exception occurred */
454 #ifndef FAST486_NO_FPU
455 // TODO: NOT IMPLEMENTED
465 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA
)
467 FAST486_MOD_REG_RM ModRegRm
;
468 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
470 /* Get the operands */
471 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
473 /* Exception occurred */
479 #ifndef FAST486_NO_FPU
480 // TODO: NOT IMPLEMENTED
490 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB
)
492 FAST486_MOD_REG_RM ModRegRm
;
493 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
495 /* Get the operands */
496 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
498 /* Exception occurred */
504 #ifndef FAST486_NO_FPU
508 // TODO: NOT IMPLEMENTED
513 /* Only a few of these instructions have any meaning on a 487 */
514 switch ((ModRegRm
.SecondRegister
<< 3) | ModRegRm
.Register
)
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
;
536 /* Restore the state */
537 State
->FpuControl
.Value
= FAST486_FPU_DEFAULT_CONTROL
;
538 State
->FpuStatus
.Value
= 0;
539 State
->FpuTag
= 0xFFFF;
549 /* These do nothing */
556 Fast486Exception(State
, FAST486_EXCEPTION_UD
);
567 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD
)
569 FAST486_MOD_REG_RM ModRegRm
;
570 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
572 /* Get the operands */
573 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
575 /* Exception occurred */
581 #ifndef FAST486_NO_FPU
582 // TODO: NOT IMPLEMENTED
592 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDE
)
594 FAST486_MOD_REG_RM ModRegRm
;
595 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
597 /* Get the operands */
598 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
600 /* Exception occurred */
606 #ifndef FAST486_NO_FPU
607 // TODO: NOT IMPLEMENTED
617 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDF
)
619 FAST486_MOD_REG_RM ModRegRm
;
620 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
622 /* Get the operands */
623 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
625 /* Exception occurred */
631 #ifndef FAST486_NO_FPU
632 // TODO: NOT IMPLEMENTED