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 **********************************************************/
36 UnsignedMult128(ULONGLONG Multiplicand
,
38 ULONGLONG
*HighProduct
)
40 ULONG MultiplicandLow
, MultiplicandHigh
, MultiplierLow
, MultiplierHigh
;
41 ULONG IntermediateLow
, IntermediateHigh
;
42 ULONGLONG LowProduct
, Intermediate
, Intermediate1
, Intermediate2
;
44 MultiplicandLow
= (ULONG
)(Multiplicand
& 0xFFFFFFFFULL
);
45 MultiplicandHigh
= (ULONG
)(Multiplicand
>> 32);
46 MultiplierLow
= (ULONG
)(Multiplier
& 0xFFFFFFFFULL
);
47 MultiplierHigh
= (ULONG
)(Multiplier
>> 32);
49 LowProduct
= (ULONGLONG
)MultiplicandLow
* (ULONGLONG
)MultiplierLow
;
50 Intermediate1
= (ULONGLONG
)MultiplicandLow
* (ULONGLONG
)MultiplierHigh
;
51 Intermediate2
= (ULONGLONG
)MultiplicandHigh
* (ULONGLONG
)MultiplierLow
;
52 *HighProduct
= (ULONGLONG
)MultiplicandHigh
* (ULONGLONG
)MultiplierHigh
;
54 Intermediate
= Intermediate1
+ Intermediate2
;
55 if (Intermediate
< Intermediate1
) *HighProduct
+= 1ULL << 32;
57 IntermediateLow
= (ULONG
)(Intermediate
& 0xFFFFFFFFULL
);
58 IntermediateHigh
= (ULONG
)(Intermediate
>> 32);
60 LowProduct
+= (ULONGLONG
)IntermediateLow
<< 32;
61 if ((ULONG
)(LowProduct
>> 32) < IntermediateLow
) (*HighProduct
)++;
63 *HighProduct
+= IntermediateHigh
;
68 Fast486FpuGetSingleReal(PFAST486_STATE State
,
70 PFAST486_FPU_DATA_REG Result
)
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;
77 /* If this is a zero, we're done */
78 if (Value
== 0) return;
80 if (Result
->Exponent
== 0xFF) Result
->Exponent
= FPU_MAX_EXPONENT
+ 1;
83 /* Adjust the exponent bias */
84 Result
->Exponent
+= (FPU_REAL10_BIAS
- FPU_REAL4_BIAS
);
89 Fast486FpuGetDoubleReal(PFAST486_STATE State
,
91 PFAST486_FPU_DATA_REG Result
)
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;
98 /* If this is a zero, we're done */
99 if (Value
== 0) return;
101 if (Result
->Exponent
== 0x3FF) Result
->Exponent
= FPU_MAX_EXPONENT
+ 1;
104 /* Adjust the exponent bias */
105 Result
->Exponent
+= (FPU_REAL10_BIAS
- FPU_REAL8_BIAS
);
110 Fast486FpuAdd(PFAST486_STATE State
,
111 PFAST486_FPU_DATA_REG FirstOperand
,
112 PFAST486_FPU_DATA_REG SecondOperand
,
113 PFAST486_FPU_DATA_REG Result
)
115 FAST486_FPU_DATA_REG FirstAdjusted
= *FirstOperand
;
116 FAST486_FPU_DATA_REG SecondAdjusted
= *SecondOperand
;
117 FAST486_FPU_DATA_REG TempResult
;
119 if (!FPU_IS_NORMALIZED(FirstOperand
) || !FPU_IS_NORMALIZED(SecondOperand
))
122 State
->FpuStatus
.De
= TRUE
;
125 /* Find the largest exponent */
126 TempResult
.Exponent
= max(FirstOperand
->Exponent
, SecondOperand
->Exponent
);
128 /* Adjust the first operand to it... */
129 if (FirstAdjusted
.Exponent
< TempResult
.Exponent
)
131 FirstAdjusted
.Mantissa
>>= (TempResult
.Exponent
- FirstAdjusted
.Exponent
);
132 FirstAdjusted
.Exponent
= TempResult
.Exponent
;
135 /* ... and the second one too */
136 if (SecondAdjusted
.Exponent
< TempResult
.Exponent
)
138 SecondAdjusted
.Mantissa
>>= (TempResult
.Exponent
- SecondAdjusted
.Exponent
);
139 SecondAdjusted
.Exponent
= TempResult
.Exponent
;
142 if (FirstAdjusted
.Sign
== SecondAdjusted
.Sign
)
144 /* Calculate the mantissa and sign of the result */
145 TempResult
.Mantissa
= FirstAdjusted
.Mantissa
+ SecondAdjusted
.Mantissa
;
146 TempResult
.Sign
= FirstAdjusted
.Sign
;
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
;
155 /* Invert the negative mantissa */
156 if (FirstAdjusted
.Sign
) FirstAdjusted
.Mantissa
= -FirstAdjusted
.Mantissa
;
157 if (SecondAdjusted
.Sign
) SecondAdjusted
.Mantissa
= -SecondAdjusted
.Mantissa
;
159 /* Calculate the mantissa of the result */
160 TempResult
.Mantissa
= FirstAdjusted
.Mantissa
+ SecondAdjusted
.Mantissa
;
163 /* Did it overflow? */
164 if (FPU_IS_NORMALIZED(&FirstAdjusted
) && FPU_IS_NORMALIZED(&SecondAdjusted
))
166 if (TempResult
.Exponent
== FPU_MAX_EXPONENT
)
168 /* Total overflow, return infinity */
169 TempResult
.Mantissa
= FPU_MANTISSA_HIGH_BIT
;
170 TempResult
.Exponent
= FPU_MAX_EXPONENT
+ 1;
173 State
->FpuStatus
.Oe
= TRUE
;
177 /* Lose the LSB in favor of the carry */
178 TempResult
.Mantissa
>>= 1;
179 TempResult
.Mantissa
|= FPU_MANTISSA_HIGH_BIT
;
180 TempResult
.Exponent
++;
184 /* Normalize the result and return it */
185 Fast486FpuNormalize(State
, &TempResult
);
186 *Result
= TempResult
;
190 Fast486FpuSubtract(PFAST486_STATE State
,
191 PFAST486_FPU_DATA_REG FirstOperand
,
192 PFAST486_FPU_DATA_REG SecondOperand
,
193 PFAST486_FPU_DATA_REG Result
)
195 FAST486_FPU_DATA_REG NegativeSecondOperand
= *SecondOperand
;
197 /* Invert the sign */
198 NegativeSecondOperand
.Sign
= !NegativeSecondOperand
.Sign
;
200 /* And perform an addition instead */
201 Fast486FpuAdd(State
, Result
, FirstOperand
, &NegativeSecondOperand
);
205 Fast486FpuCompare(PFAST486_STATE State
,
206 PFAST486_FPU_DATA_REG FirstOperand
,
207 PFAST486_FPU_DATA_REG SecondOperand
)
209 if (FPU_IS_NAN(FirstOperand
) || FPU_IS_NAN(SecondOperand
))
211 if (FPU_IS_POS_INF(FirstOperand
) && FPU_IS_NEG_INF(SecondOperand
))
213 State
->FpuStatus
.Code0
= FALSE
;
214 State
->FpuStatus
.Code2
= FALSE
;
215 State
->FpuStatus
.Code3
= FALSE
;
217 else if (FPU_IS_NEG_INF(FirstOperand
) && FPU_IS_POS_INF(SecondOperand
))
219 State
->FpuStatus
.Code0
= TRUE
;
220 State
->FpuStatus
.Code2
= FALSE
;
221 State
->FpuStatus
.Code3
= FALSE
;
225 State
->FpuStatus
.Code0
= TRUE
;
226 State
->FpuStatus
.Code2
= TRUE
;
227 State
->FpuStatus
.Code3
= TRUE
;
232 FAST486_FPU_DATA_REG TempResult
;
234 Fast486FpuSubtract(State
, FirstOperand
, SecondOperand
, &TempResult
);
236 if (FPU_IS_ZERO(&TempResult
))
238 State
->FpuStatus
.Code0
= FALSE
;
239 State
->FpuStatus
.Code2
= FALSE
;
240 State
->FpuStatus
.Code3
= TRUE
;
242 else if (TempResult
.Sign
)
244 State
->FpuStatus
.Code0
= TRUE
;
245 State
->FpuStatus
.Code2
= FALSE
;
246 State
->FpuStatus
.Code3
= FALSE
;
250 State
->FpuStatus
.Code0
= FALSE
;
251 State
->FpuStatus
.Code2
= FALSE
;
252 State
->FpuStatus
.Code3
= FALSE
;
258 Fast486FpuMultiply(PFAST486_STATE State
,
259 PFAST486_FPU_DATA_REG FirstOperand
,
260 PFAST486_FPU_DATA_REG SecondOperand
,
261 PFAST486_FPU_DATA_REG Result
)
263 FAST486_FPU_DATA_REG TempResult
;
265 if (!FPU_IS_NORMALIZED(FirstOperand
) || !FPU_IS_NORMALIZED(SecondOperand
))
268 State
->FpuStatus
.De
= TRUE
;
271 UnsignedMult128(FirstOperand
->Mantissa
,
272 SecondOperand
->Mantissa
,
273 &TempResult
.Mantissa
);
275 TempResult
.Exponent
= FirstOperand
->Exponent
+ SecondOperand
->Exponent
;
276 TempResult
.Sign
= FirstOperand
->Sign
^ SecondOperand
->Sign
;
278 /* Normalize the result */
279 Fast486FpuNormalize(State
, &TempResult
);
280 *Result
= TempResult
;
284 Fast486FpuDivide(PFAST486_STATE State
,
285 PFAST486_FPU_DATA_REG FirstOperand
,
286 PFAST486_FPU_DATA_REG SecondOperand
,
287 PFAST486_FPU_DATA_REG Result
)
289 FAST486_FPU_DATA_REG TempResult
;
291 if (FPU_IS_ZERO(SecondOperand
))
293 /* Division by zero */
294 State
->FpuStatus
.Ze
= TRUE
;
298 TempResult
.Exponent
= FirstOperand
->Exponent
- SecondOperand
->Exponent
;
299 TempResult
.Sign
= FirstOperand
->Sign
^ SecondOperand
->Sign
;
301 // TODO: NOT IMPLEMENTED
302 UNREFERENCED_PARAMETER(TempResult
);
306 /* PUBLIC FUNCTIONS ***********************************************************/
308 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD8DC
)
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
;
315 /* Get the operands */
316 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
318 /* Exception occurred */
324 #ifndef FAST486_NO_FPU
328 /* Load the source operand from memory */
334 if (!Fast486ReadMemory(State
,
335 (State
->PrefixFlags
& FAST486_PREFIX_SEG
)
336 ? State
->SegmentOverride
: FAST486_REG_DS
,
337 ModRegRm
.MemoryAddress
,
342 /* Exception occurred */
346 Fast486FpuGetDoubleReal(State
, Value
, &MemoryData
);
352 if (!Fast486ReadModrmDwordOperands(State
, &ModRegRm
, NULL
, &Value
))
354 /* Exception occurred */
358 Fast486FpuGetSingleReal(State
, Value
, &MemoryData
);
361 SourceOperand
= &MemoryData
;
365 /* Load the source operand from an FPU register */
366 SourceOperand
= &FPU_ST(ModRegRm
.SecondRegister
);
368 if (FPU_GET_TAG(ModRegRm
.SecondRegister
) == FPU_TAG_EMPTY
)
370 /* Invalid operation */
371 State
->FpuStatus
.Ie
= TRUE
;
376 /* The destination operand is always ST0 */
377 DestOperand
= &FPU_ST(0);
379 if (FPU_GET_TAG(0) == FPU_TAG_EMPTY
)
381 /* Invalid operation */
382 State
->FpuStatus
.Ie
= TRUE
;
386 /* Check the operation */
387 switch (ModRegRm
.Register
)
392 Fast486FpuAdd(State
, DestOperand
, SourceOperand
, DestOperand
);
399 Fast486FpuMultiply(State
, DestOperand
, SourceOperand
, DestOperand
);
408 Fast486FpuCompare(State
, DestOperand
, SourceOperand
);
409 if (ModRegRm
.Register
== 3) Fast486FpuPop(State
);
417 Fast486FpuSubtract(State
, DestOperand
, SourceOperand
, DestOperand
);
424 Fast486FpuSubtract(State
, SourceOperand
, DestOperand
, DestOperand
);
431 Fast486FpuDivide(State
, DestOperand
, SourceOperand
, DestOperand
);
438 Fast486FpuDivide(State
, SourceOperand
, DestOperand
, DestOperand
);
448 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeD9
)
450 FAST486_MOD_REG_RM ModRegRm
;
451 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
453 /* Get the operands */
454 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
456 /* Exception occurred */
462 #ifndef FAST486_NO_FPU
463 // TODO: NOT IMPLEMENTED
473 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDA
)
475 FAST486_MOD_REG_RM ModRegRm
;
476 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
478 /* Get the operands */
479 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
481 /* Exception occurred */
487 #ifndef FAST486_NO_FPU
488 // TODO: NOT IMPLEMENTED
498 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDB
)
500 FAST486_MOD_REG_RM ModRegRm
;
501 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
503 /* Get the operands */
504 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
506 /* Exception occurred */
512 #ifndef FAST486_NO_FPU
516 // TODO: NOT IMPLEMENTED
521 /* Only a few of these instructions have any meaning on a 487 */
522 switch ((ModRegRm
.Register
<< 3) | ModRegRm
.SecondRegister
)
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
;
544 /* Restore the state */
545 State
->FpuControl
.Value
= FAST486_FPU_DEFAULT_CONTROL
;
546 State
->FpuStatus
.Value
= 0;
547 State
->FpuTag
= 0xFFFF;
557 /* These do nothing */
564 Fast486Exception(State
, FAST486_EXCEPTION_UD
);
575 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDD
)
577 FAST486_MOD_REG_RM ModRegRm
;
578 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
580 /* Get the operands */
581 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
583 /* Exception occurred */
589 #ifndef FAST486_NO_FPU
590 // TODO: NOT IMPLEMENTED
600 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDE
)
602 FAST486_MOD_REG_RM ModRegRm
;
603 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
605 /* Get the operands */
606 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
608 /* Exception occurred */
614 #ifndef FAST486_NO_FPU
615 // TODO: NOT IMPLEMENTED
625 FAST486_OPCODE_HANDLER(Fast486FpuOpcodeDF
)
627 FAST486_MOD_REG_RM ModRegRm
;
628 BOOLEAN AddressSize
= State
->SegmentRegs
[FAST486_REG_CS
].Size
;
630 /* Get the operands */
631 if (!Fast486ParseModRegRm(State
, AddressSize
, &ModRegRm
))
633 /* Exception occurred */
639 #ifndef FAST486_NO_FPU
640 // TODO: NOT IMPLEMENTED