* Sync up to trunk head (r65298).
[reactos.git] / lib / fast486 / common.inl
1 /*
2 * Fast486 386/486 CPU Emulation Library
3 * common.inl
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 #include "common.h"
23 #include "fpu.h"
24
25 /* PUBLIC FUNCTIONS ***********************************************************/
26
27 #if defined (__GNUC__)
28 #define CountLeadingZeros64(x) __builtin_clzll(x)
29
30 /*
31 #elif (_MSC_VER >= 1500) && defined(_WIN64)
32 #define CountLeadingZeros64(x) __lzcnt64(x)
33 #elif (_MSC_VER >= 1500)
34 #define CountLeadingZeros64(x) ((x) > 0xFFFFFFFFULL) ? __lzcnt((x) >> 32) \
35 : (__lzcnt(x) + 32)
36 */
37
38 #else
39 FORCEINLINE
40 ULONG
41 CountLeadingZeros64(ULONGLONG Value)
42 {
43 ULONG Count = 0;
44 Value = ~Value;
45 while ((LONGLONG)Value < 0)
46 {
47 Count++;
48 Value <<= 1;
49 }
50 return Count;
51 }
52 #endif
53
54 FORCEINLINE
55 INT
56 FASTCALL
57 Fast486GetCurrentPrivLevel(PFAST486_STATE State)
58 {
59 /* Return the CPL, or 3 if we're in virtual 8086 mode */
60 return (!State->Flags.Vm) ? State->Cpl : 3;
61 }
62
63 FORCEINLINE
64 ULONG
65 FASTCALL
66 Fast486GetPageTableEntry(PFAST486_STATE State,
67 ULONG VirtualAddress,
68 BOOLEAN MarkAsDirty)
69 {
70 ULONG PdeIndex = GET_ADDR_PDE(VirtualAddress);
71 ULONG PteIndex = GET_ADDR_PTE(VirtualAddress);
72 FAST486_PAGE_DIR DirectoryEntry;
73 FAST486_PAGE_TABLE TableEntry;
74 ULONG PageDirectory = State->ControlRegisters[FAST486_REG_CR3];
75
76 if ((State->Tlb != NULL)
77 && (State->Tlb[VirtualAddress >> 12] != INVALID_TLB_FIELD))
78 {
79 /* Return the cached entry */
80 return State->Tlb[VirtualAddress >> 12];
81 }
82
83 /* Read the directory entry */
84 State->MemReadCallback(State,
85 PageDirectory + PdeIndex * sizeof(ULONG),
86 &DirectoryEntry.Value,
87 sizeof(DirectoryEntry));
88
89 /* Make sure it is present */
90 if (!DirectoryEntry.Present) return 0;
91
92 /* Was the directory entry accessed before? */
93 if (!DirectoryEntry.Accessed)
94 {
95 /* Well, it is now */
96 DirectoryEntry.Accessed = TRUE;
97
98 /* Write back the directory entry */
99 State->MemWriteCallback(State,
100 PageDirectory + PdeIndex * sizeof(ULONG),
101 &DirectoryEntry.Value,
102 sizeof(DirectoryEntry));
103 }
104
105 /* Read the table entry */
106 State->MemReadCallback(State,
107 (DirectoryEntry.TableAddress << 12)
108 + PteIndex * sizeof(ULONG),
109 &TableEntry.Value,
110 sizeof(TableEntry));
111
112 /* Make sure it is present */
113 if (!TableEntry.Present) return 0;
114
115 if (MarkAsDirty) TableEntry.Dirty = TRUE;
116
117 /* Was the table entry accessed before? */
118 if (!TableEntry.Accessed)
119 {
120 /* Well, it is now */
121 TableEntry.Accessed = TRUE;
122
123 /* Write back the table entry */
124 State->MemWriteCallback(State,
125 (DirectoryEntry.TableAddress << 12)
126 + PteIndex * sizeof(ULONG),
127 &TableEntry.Value,
128 sizeof(TableEntry));
129 }
130
131 /*
132 * The resulting permissions depend on the permissions
133 * in the page directory table too
134 */
135 TableEntry.Writeable &= DirectoryEntry.Writeable;
136 TableEntry.Usermode &= DirectoryEntry.Usermode;
137
138 if (State->Tlb != NULL)
139 {
140 /* Set the TLB entry */
141 State->Tlb[VirtualAddress >> 12] = TableEntry.Value;
142 }
143
144 /* Return the table entry */
145 return TableEntry.Value;
146 }
147
148 FORCEINLINE
149 BOOLEAN
150 FASTCALL
151 Fast486ReadLinearMemory(PFAST486_STATE State,
152 ULONG LinearAddress,
153 PVOID Buffer,
154 ULONG Size)
155 {
156 /* Check if paging is enabled */
157 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG)
158 {
159 ULONG Page;
160 FAST486_PAGE_TABLE TableEntry;
161 INT Cpl = Fast486GetCurrentPrivLevel(State);
162 ULONG BufferOffset = 0;
163
164 for (Page = PAGE_ALIGN(LinearAddress);
165 Page <= PAGE_ALIGN(LinearAddress + Size - 1);
166 Page += FAST486_PAGE_SIZE)
167 {
168 ULONG PageOffset = 0, PageLength = FAST486_PAGE_SIZE;
169
170 /* Get the table entry */
171 TableEntry.Value = Fast486GetPageTableEntry(State, Page, FALSE);
172
173 if (!TableEntry.Present || (!TableEntry.Usermode && (Cpl > 0)))
174 {
175 /* Exception */
176 Fast486ExceptionWithErrorCode(State,
177 FAST486_EXCEPTION_PF,
178 TableEntry.Present | (State->Cpl ? 0x04 : 0));
179 return FALSE;
180 }
181
182 /* Check if this is the first page */
183 if (Page == PAGE_ALIGN(LinearAddress))
184 {
185 /* Start reading from the offset from the beginning of the page */
186 PageOffset = PAGE_OFFSET(LinearAddress);
187 PageLength -= PageOffset;
188 }
189
190 /* Check if this is the last page */
191 if (Page == PAGE_ALIGN(LinearAddress + Size - 1))
192 {
193 /* Read only a part of the page */
194 PageLength = PAGE_OFFSET(LinearAddress + Size - 1) - PageOffset + 1;
195 }
196
197 /* Read the memory */
198 State->MemReadCallback(State,
199 (TableEntry.Address << 12) | PageOffset,
200 (PVOID)((ULONG_PTR)Buffer + BufferOffset),
201 PageLength);
202
203 BufferOffset += PageLength;
204 }
205 }
206 else
207 {
208 /* Read the memory */
209 State->MemReadCallback(State, LinearAddress, Buffer, Size);
210 }
211
212 return TRUE;
213 }
214
215 FORCEINLINE
216 BOOLEAN
217 FASTCALL
218 Fast486WriteLinearMemory(PFAST486_STATE State,
219 ULONG LinearAddress,
220 PVOID Buffer,
221 ULONG Size)
222 {
223 /* Check if paging is enabled */
224 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG)
225 {
226 ULONG Page;
227 FAST486_PAGE_TABLE TableEntry;
228 INT Cpl = Fast486GetCurrentPrivLevel(State);
229 ULONG BufferOffset = 0;
230
231 for (Page = PAGE_ALIGN(LinearAddress);
232 Page <= PAGE_ALIGN(LinearAddress + Size - 1);
233 Page += FAST486_PAGE_SIZE)
234 {
235 ULONG PageOffset = 0, PageLength = FAST486_PAGE_SIZE;
236
237 /* Get the table entry */
238 TableEntry.Value = Fast486GetPageTableEntry(State, Page, TRUE);
239
240 if ((!TableEntry.Present || (!TableEntry.Usermode && (Cpl > 0)))
241 || ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_WP)
242 && !TableEntry.Writeable))
243 {
244 /* Exception */
245 Fast486ExceptionWithErrorCode(State,
246 FAST486_EXCEPTION_PF,
247 TableEntry.Present | 0x02 | (State->Cpl ? 0x04 : 0));
248 return FALSE;
249 }
250
251 /* Check if this is the first page */
252 if (Page == PAGE_ALIGN(LinearAddress))
253 {
254 /* Start writing from the offset from the beginning of the page */
255 PageOffset = PAGE_OFFSET(LinearAddress);
256 PageLength -= PageOffset;
257 }
258
259 /* Check if this is the last page */
260 if (Page == PAGE_ALIGN(LinearAddress + Size - 1))
261 {
262 /* Write only a part of the page */
263 PageLength = PAGE_OFFSET(LinearAddress + Size - 1) - PageOffset + 1;
264 }
265
266 /* Write the memory */
267 State->MemWriteCallback(State,
268 (TableEntry.Address << 12) | PageOffset,
269 (PVOID)((ULONG_PTR)Buffer + BufferOffset),
270 PageLength);
271
272 BufferOffset += PageLength;
273 }
274 }
275 else
276 {
277 /* Write the memory */
278 State->MemWriteCallback(State, LinearAddress, Buffer, Size);
279 }
280
281 return TRUE;
282 }
283
284 FORCEINLINE
285 VOID
286 FASTCALL
287 Fast486Exception(PFAST486_STATE State,
288 FAST486_EXCEPTIONS ExceptionCode)
289 {
290 /* Call the internal function */
291 Fast486ExceptionWithErrorCode(State, ExceptionCode, 0);
292 }
293
294 FORCEINLINE
295 BOOLEAN
296 FASTCALL
297 Fast486StackPush(PFAST486_STATE State,
298 ULONG Value)
299 {
300 BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;
301
302 /* The OPSIZE prefix toggles the size */
303 if (State->PrefixFlags & FAST486_PREFIX_OPSIZE) Size = !Size;
304
305 if (Size)
306 {
307 /* 32-bit size */
308
309 /* Check if ESP is between 1 and 3 */
310 if (State->GeneralRegs[FAST486_REG_ESP].Long >= 1
311 && State->GeneralRegs[FAST486_REG_ESP].Long <= 3)
312 {
313 Fast486Exception(State, FAST486_EXCEPTION_SS);
314 return FALSE;
315 }
316
317 /* Subtract ESP by 4 */
318 State->GeneralRegs[FAST486_REG_ESP].Long -= sizeof(ULONG);
319
320 /* Store the value in SS:ESP */
321 return Fast486WriteMemory(State,
322 FAST486_REG_SS,
323 State->GeneralRegs[FAST486_REG_ESP].Long,
324 &Value,
325 sizeof(ULONG));
326 }
327 else
328 {
329 /* 16-bit size */
330 USHORT ShortValue = LOWORD(Value);
331
332 /* Check if SP is 1 */
333 if (State->GeneralRegs[FAST486_REG_ESP].LowWord == 1)
334 {
335 Fast486Exception(State, FAST486_EXCEPTION_SS);
336 return FALSE;
337 }
338
339 /* Subtract SP by 2 */
340 State->GeneralRegs[FAST486_REG_ESP].LowWord -= sizeof(USHORT);
341
342 /* Store the value in SS:SP */
343 return Fast486WriteMemory(State,
344 FAST486_REG_SS,
345 State->GeneralRegs[FAST486_REG_ESP].LowWord,
346 &ShortValue,
347 sizeof(USHORT));
348 }
349 }
350
351 FORCEINLINE
352 BOOLEAN
353 FASTCALL
354 Fast486StackPop(PFAST486_STATE State,
355 PULONG Value)
356 {
357 BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;
358
359 /* The OPSIZE prefix toggles the size */
360 TOGGLE_OPSIZE(Size);
361
362 if (Size)
363 {
364 /* 32-bit size */
365 ULONG LongValue;
366
367 /* Check if ESP is 0xFFFFFFFF */
368 if (State->GeneralRegs[FAST486_REG_ESP].Long == 0xFFFFFFFF)
369 {
370 Fast486Exception(State, FAST486_EXCEPTION_SS);
371 return FALSE;
372 }
373
374 /* Read the value from SS:ESP */
375 if (!Fast486ReadMemory(State,
376 FAST486_REG_SS,
377 State->GeneralRegs[FAST486_REG_ESP].Long,
378 FALSE,
379 &LongValue,
380 sizeof(LongValue)))
381 {
382 /* An exception occurred */
383 return FALSE;
384 }
385
386 /* Increment ESP by 4 */
387 State->GeneralRegs[FAST486_REG_ESP].Long += sizeof(ULONG);
388
389 /* Store the value in the result */
390 *Value = LongValue;
391 }
392 else
393 {
394 /* 16-bit size */
395 USHORT ShortValue;
396
397 /* Check if SP is 0xFFFF */
398 if (State->GeneralRegs[FAST486_REG_ESP].LowWord == 0xFFFF)
399 {
400 Fast486Exception(State, FAST486_EXCEPTION_SS);
401 return FALSE;
402 }
403
404 /* Read the value from SS:SP */
405 if (!Fast486ReadMemory(State,
406 FAST486_REG_SS,
407 State->GeneralRegs[FAST486_REG_ESP].LowWord,
408 FALSE,
409 &ShortValue,
410 sizeof(ShortValue)))
411 {
412 /* An exception occurred */
413 return FALSE;
414 }
415
416 /* Increment SP by 2 */
417 State->GeneralRegs[FAST486_REG_ESP].LowWord += sizeof(USHORT);
418
419 /* Store the value in the result */
420 *Value = ShortValue;
421 }
422
423 return TRUE;
424 }
425
426 FORCEINLINE
427 BOOLEAN
428 FASTCALL
429 Fast486ReadDescriptorEntry(PFAST486_STATE State,
430 USHORT Selector,
431 PBOOLEAN EntryValid,
432 PFAST486_GDT_ENTRY Entry)
433 {
434 if (!(Selector & SEGMENT_TABLE_INDICATOR))
435 {
436 /* Make sure the GDT contains the entry */
437 if (GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1))
438 {
439 *EntryValid = FALSE;
440 return TRUE;
441 }
442
443 /* Read the GDT */
444 if (!Fast486ReadLinearMemory(State,
445 State->Gdtr.Address
446 + GET_SEGMENT_INDEX(Selector),
447 Entry,
448 sizeof(*Entry)))
449 {
450 /* Exception occurred */
451 *EntryValid = FALSE;
452 return FALSE;
453 }
454 }
455 else
456 {
457 /* Make sure the LDT contains the entry */
458 if (GET_SEGMENT_INDEX(Selector) >= (State->Ldtr.Limit + 1))
459 {
460 *EntryValid = FALSE;
461 return TRUE;
462 }
463
464 /* Read the LDT */
465 if (!Fast486ReadLinearMemory(State,
466 State->Ldtr.Base
467 + GET_SEGMENT_INDEX(Selector),
468 Entry,
469 sizeof(*Entry)))
470 {
471 /* Exception occurred */
472 *EntryValid = FALSE;
473 return FALSE;
474 }
475 }
476
477 *EntryValid = TRUE;
478 return TRUE;
479 }
480
481 FORCEINLINE
482 BOOLEAN
483 FASTCALL
484 Fast486LoadSegmentInternal(PFAST486_STATE State,
485 FAST486_SEG_REGS Segment,
486 USHORT Selector,
487 FAST486_EXCEPTIONS Exception)
488 {
489 PFAST486_SEG_REG CachedDescriptor;
490 BOOLEAN Valid;
491 FAST486_GDT_ENTRY GdtEntry;
492
493 ASSERT(Segment < FAST486_NUM_SEG_REGS);
494
495 /* Get the cached descriptor */
496 CachedDescriptor = &State->SegmentRegs[Segment];
497
498 /* Check for protected mode */
499 if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) && !State->Flags.Vm)
500 {
501 if (!Fast486ReadDescriptorEntry(State, Selector, &Valid, &GdtEntry))
502 {
503 /* Exception occurred */
504 return FALSE;
505 }
506
507 if (!Valid)
508 {
509 /* Invalid selector */
510 Fast486ExceptionWithErrorCode(State, Exception, Selector);
511 }
512
513 if (Segment == FAST486_REG_SS)
514 {
515 /* Loading the stack segment */
516
517 if (GET_SEGMENT_INDEX(Selector) == 0)
518 {
519 Fast486Exception(State, Exception);
520 return FALSE;
521 }
522
523 if (!GdtEntry.SystemType)
524 {
525 /* This is a special descriptor */
526 Fast486ExceptionWithErrorCode(State, Exception, Selector);
527 return FALSE;
528 }
529
530 if (GdtEntry.Executable || !GdtEntry.ReadWrite)
531 {
532 Fast486ExceptionWithErrorCode(State, Exception, Selector);
533 return FALSE;
534 }
535
536 if ((GET_SEGMENT_RPL(Selector) != Fast486GetCurrentPrivLevel(State))
537 || (GET_SEGMENT_RPL(Selector) != GdtEntry.Dpl))
538 {
539 Fast486ExceptionWithErrorCode(State, Exception, Selector);
540 return FALSE;
541 }
542
543 if (!GdtEntry.Present)
544 {
545 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_SS, Selector);
546 return FALSE;
547 }
548 }
549 else if (Segment == FAST486_REG_CS)
550 {
551 /* Loading the code segment */
552
553 #ifndef FAST486_NO_PREFETCH
554 /* Invalidate the prefetch */
555 State->PrefetchValid = FALSE;
556 #endif
557
558 if (GET_SEGMENT_INDEX(Selector) == 0)
559 {
560 Fast486Exception(State, Exception);
561 return FALSE;
562 }
563
564 if (!GdtEntry.SystemType)
565 {
566 /* Must be a segment descriptor */
567 Fast486ExceptionWithErrorCode(State, Exception, Selector);
568 }
569
570 if (!GdtEntry.Present)
571 {
572 Fast486ExceptionWithErrorCode(State, Exception, Selector);
573 return FALSE;
574 }
575
576 if (!GdtEntry.Executable)
577 {
578 Fast486ExceptionWithErrorCode(State, Exception, Selector);
579 return FALSE;
580 }
581
582 if (GdtEntry.DirConf)
583 {
584 /* Conforming Code Segment */
585
586 if (GdtEntry.Dpl > Fast486GetCurrentPrivLevel(State))
587 {
588 /* Must be accessed from lower-privileged code */
589 Fast486ExceptionWithErrorCode(State, Exception, Selector);
590 return FALSE;
591 }
592 }
593 else
594 {
595 /* Regular code segment */
596
597 if ((GET_SEGMENT_RPL(Selector) < Fast486GetCurrentPrivLevel(State)))
598 {
599 Fast486ExceptionWithErrorCode(State, Exception, Selector);
600 return FALSE;
601 }
602 }
603 }
604 else
605 {
606 /* Loading a data segment */
607
608 if (GET_SEGMENT_INDEX(Selector) != 0)
609 {
610 if (!GdtEntry.SystemType)
611 {
612 /* This is a special descriptor */
613 Fast486ExceptionWithErrorCode(State, Exception, Selector);
614 return FALSE;
615 }
616
617 if ((GET_SEGMENT_RPL(Selector) > GdtEntry.Dpl)
618 || (Fast486GetCurrentPrivLevel(State) > GdtEntry.Dpl))
619 {
620 Fast486ExceptionWithErrorCode(State, Exception, Selector);
621 return FALSE;
622 }
623
624 if (!GdtEntry.Present)
625 {
626 Fast486ExceptionWithErrorCode(State, Exception, Selector);
627 return FALSE;
628 }
629 }
630 else
631 {
632 /* This is a NULL selector */
633 RtlZeroMemory(&GdtEntry, sizeof(GdtEntry));
634 }
635 }
636
637 /* Update the cache entry */
638 CachedDescriptor->Selector = Selector;
639 CachedDescriptor->Base = GdtEntry.Base | (GdtEntry.BaseMid << 16) | (GdtEntry.BaseHigh << 24);
640 CachedDescriptor->Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16);
641 CachedDescriptor->Accessed = GdtEntry.Accessed;
642 CachedDescriptor->ReadWrite = GdtEntry.ReadWrite;
643 CachedDescriptor->DirConf = GdtEntry.DirConf;
644 CachedDescriptor->Executable = GdtEntry.Executable;
645 CachedDescriptor->SystemType = GdtEntry.SystemType;
646 CachedDescriptor->Rpl = GET_SEGMENT_RPL(Selector);
647 CachedDescriptor->Dpl = GdtEntry.Dpl;
648 CachedDescriptor->Present = GdtEntry.Present;
649 CachedDescriptor->Size = GdtEntry.Size;
650
651 /* Check for page granularity */
652 if (GdtEntry.Granularity)
653 {
654 CachedDescriptor->Limit <<= 12;
655 CachedDescriptor->Limit |= 0x00000FFF;
656 }
657 }
658 else
659 {
660 /* Update the selector and base */
661 CachedDescriptor->Selector = Selector;
662 CachedDescriptor->Base = Selector << 4;
663 }
664
665 return TRUE;
666 }
667
668 FORCEINLINE
669 BOOLEAN
670 FASTCALL
671 Fast486LoadSegment(PFAST486_STATE State,
672 FAST486_SEG_REGS Segment,
673 USHORT Selector)
674 {
675 return Fast486LoadSegmentInternal(State,
676 Segment,
677 Selector,
678 FAST486_EXCEPTION_GP);
679 }
680
681 FORCEINLINE
682 BOOLEAN
683 FASTCALL
684 Fast486ProcessGate(PFAST486_STATE State, USHORT Selector, ULONG Offset, BOOLEAN Call)
685 {
686 BOOLEAN Valid;
687 FAST486_SYSTEM_DESCRIPTOR Descriptor;
688
689 if (!Fast486ReadDescriptorEntry(State,
690 Selector,
691 &Valid,
692 (PFAST486_GDT_ENTRY)&Descriptor))
693 {
694 /* Exception occurred */
695 return FALSE;
696 }
697
698 if (!Valid)
699 {
700 /* Invalid selector */
701 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
702 return FALSE;
703 }
704
705 switch (Descriptor.Signature)
706 {
707 case FAST486_TASK_GATE_SIGNATURE:
708 {
709 Fast486TaskSwitch(State,
710 Call ? FAST486_TASK_CALL : FAST486_TASK_JUMP,
711 ((PFAST486_IDT_ENTRY)&Descriptor)->Selector);
712
713 return FALSE;
714 }
715
716 case FAST486_TSS_SIGNATURE:
717 {
718 Fast486TaskSwitch(State,
719 Call ? FAST486_TASK_CALL : FAST486_TASK_JUMP,
720 Selector);
721
722 return FALSE;
723 }
724
725 case FAST486_CALL_GATE_SIGNATURE:
726 {
727 // TODO: NOT IMPLEMENTED
728 UNIMPLEMENTED;
729 }
730
731 default:
732 {
733 /* Security check for jumps and calls only */
734 if (State->Cpl != Descriptor.Dpl)
735 {
736 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
737 return FALSE;
738 }
739
740 return TRUE;
741 }
742 }
743 }
744
745 FORCEINLINE
746 BOOLEAN
747 FASTCALL
748 Fast486FetchByte(PFAST486_STATE State,
749 PUCHAR Data)
750 {
751 PFAST486_SEG_REG CachedDescriptor;
752 ULONG Offset;
753 #ifndef FAST486_NO_PREFETCH
754 ULONG LinearAddress;
755 #endif
756
757 /* Get the cached descriptor of CS */
758 CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS];
759
760 Offset = (CachedDescriptor->Size) ? State->InstPtr.Long
761 : State->InstPtr.LowWord;
762 #ifndef FAST486_NO_PREFETCH
763 LinearAddress = CachedDescriptor->Base + Offset;
764
765 if (State->PrefetchValid
766 && (LinearAddress >= State->PrefetchAddress)
767 && ((LinearAddress + sizeof(UCHAR)) <= (State->PrefetchAddress + FAST486_CACHE_SIZE)))
768 {
769 *Data = *(PUCHAR)&State->PrefetchCache[LinearAddress - State->PrefetchAddress];
770 }
771 else
772 #endif
773 {
774 /* Read from memory */
775 if (!Fast486ReadMemory(State,
776 FAST486_REG_CS,
777 Offset,
778 TRUE,
779 Data,
780 sizeof(UCHAR)))
781 {
782 /* Exception occurred during instruction fetch */
783 return FALSE;
784 }
785 }
786
787 /* Advance the instruction pointer */
788 if (CachedDescriptor->Size) State->InstPtr.Long++;
789 else State->InstPtr.LowWord++;
790
791 return TRUE;
792 }
793
794 FORCEINLINE
795 BOOLEAN
796 FASTCALL
797 Fast486FetchWord(PFAST486_STATE State,
798 PUSHORT Data)
799 {
800 PFAST486_SEG_REG CachedDescriptor;
801 ULONG Offset;
802 #ifndef FAST486_NO_PREFETCH
803 ULONG LinearAddress;
804 #endif
805
806 /* Get the cached descriptor of CS */
807 CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS];
808
809 Offset = (CachedDescriptor->Size) ? State->InstPtr.Long
810 : State->InstPtr.LowWord;
811
812 #ifndef FAST486_NO_PREFETCH
813 LinearAddress = CachedDescriptor->Base + Offset;
814
815 if (State->PrefetchValid
816 && (LinearAddress >= State->PrefetchAddress)
817 && ((LinearAddress + sizeof(USHORT)) <= (State->PrefetchAddress + FAST486_CACHE_SIZE)))
818 {
819 *Data = *(PUSHORT)&State->PrefetchCache[LinearAddress - State->PrefetchAddress];
820 }
821 else
822 #endif
823 {
824 /* Read from memory */
825 // FIXME: Fix byte order on big-endian machines
826 if (!Fast486ReadMemory(State,
827 FAST486_REG_CS,
828 Offset,
829 TRUE,
830 Data,
831 sizeof(USHORT)))
832 {
833 /* Exception occurred during instruction fetch */
834 return FALSE;
835 }
836 }
837
838 /* Advance the instruction pointer */
839 if (CachedDescriptor->Size) State->InstPtr.Long += sizeof(USHORT);
840 else State->InstPtr.LowWord += sizeof(USHORT);
841
842 return TRUE;
843 }
844
845 FORCEINLINE
846 BOOLEAN
847 FASTCALL
848 Fast486FetchDword(PFAST486_STATE State,
849 PULONG Data)
850 {
851 PFAST486_SEG_REG CachedDescriptor;
852 ULONG Offset;
853 #ifndef FAST486_NO_PREFETCH
854 ULONG LinearAddress;
855 #endif
856
857 /* Get the cached descriptor of CS */
858 CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS];
859
860 Offset = (CachedDescriptor->Size) ? State->InstPtr.Long
861 : State->InstPtr.LowWord;
862
863 #ifndef FAST486_NO_PREFETCH
864 LinearAddress = CachedDescriptor->Base + Offset;
865
866 if (State->PrefetchValid
867 && (LinearAddress >= State->PrefetchAddress)
868 && ((LinearAddress + sizeof(ULONG)) <= (State->PrefetchAddress + FAST486_CACHE_SIZE)))
869 {
870 *Data = *(PULONG)&State->PrefetchCache[LinearAddress - State->PrefetchAddress];
871 }
872 else
873 #endif
874 {
875 /* Read from memory */
876 // FIXME: Fix byte order on big-endian machines
877 if (!Fast486ReadMemory(State,
878 FAST486_REG_CS,
879 Offset,
880 TRUE,
881 Data,
882 sizeof(ULONG)))
883 {
884 /* Exception occurred during instruction fetch */
885 return FALSE;
886 }
887 }
888
889 /* Advance the instruction pointer */
890 if (CachedDescriptor->Size) State->InstPtr.Long += sizeof(ULONG);
891 else State->InstPtr.LowWord += sizeof(ULONG);
892
893 return TRUE;
894 }
895
896 FORCEINLINE
897 BOOLEAN
898 FASTCALL
899 Fast486CalculateParity(UCHAR Number)
900 {
901 // See http://graphics.stanford.edu/~seander/bithacks.html#ParityLookupTable too...
902 return (0x9669 >> ((Number & 0x0F) ^ (Number >> 4))) & 1;
903 }
904
905 FORCEINLINE
906 BOOLEAN
907 FASTCALL
908 Fast486ParseModRegRm(PFAST486_STATE State,
909 BOOLEAN AddressSize,
910 PFAST486_MOD_REG_RM ModRegRm)
911 {
912 UCHAR ModRmByte, Mode, RegMem;
913
914 /* Fetch the MOD REG R/M byte */
915 if (!Fast486FetchByte(State, &ModRmByte))
916 {
917 /* Exception occurred */
918 return FALSE;
919 }
920
921 /* Unpack the mode and R/M */
922 Mode = ModRmByte >> 6;
923 RegMem = ModRmByte & 0x07;
924
925 /* Set the register operand */
926 ModRegRm->Register = (ModRmByte >> 3) & 0x07;
927
928 /* Check the mode */
929 if (Mode == 3)
930 {
931 /* The second operand is also a register */
932 ModRegRm->Memory = FALSE;
933 ModRegRm->SecondRegister = RegMem;
934
935 /* Done parsing */
936 return TRUE;
937 }
938
939 /* The second operand is memory */
940 ModRegRm->Memory = TRUE;
941
942 if (AddressSize)
943 {
944 if (RegMem == FAST486_REG_ESP)
945 {
946 UCHAR SibByte;
947 ULONG Scale, Index, Base;
948
949 /* Fetch the SIB byte */
950 if (!Fast486FetchByte(State, &SibByte))
951 {
952 /* Exception occurred */
953 return FALSE;
954 }
955
956 /* Unpack the scale, index and base */
957 Scale = 1 << (SibByte >> 6);
958 Index = (SibByte >> 3) & 0x07;
959 if (Index != FAST486_REG_ESP) Index = State->GeneralRegs[Index].Long;
960 else Index = 0;
961
962 if (((SibByte & 0x07) != FAST486_REG_EBP) || (Mode != 0))
963 {
964 /* Use the register a base */
965 Base = State->GeneralRegs[SibByte & 0x07].Long;
966 }
967 else
968 {
969 /* Fetch the base */
970 if (!Fast486FetchDword(State, &Base))
971 {
972 /* Exception occurred */
973 return FALSE;
974 }
975 }
976
977 if ((SibByte & 0x07) == FAST486_REG_ESP)
978 {
979 /* Check if there is no segment override */
980 if (!(State->PrefixFlags & FAST486_PREFIX_SEG))
981 {
982 /* Add a SS: prefix */
983 State->PrefixFlags |= FAST486_PREFIX_SEG;
984 State->SegmentOverride = FAST486_REG_SS;
985 }
986 }
987
988 /* Calculate the address */
989 ModRegRm->MemoryAddress = Base + Index * Scale;
990 }
991 else if (RegMem == FAST486_REG_EBP)
992 {
993 if (Mode) ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].Long;
994 else ModRegRm->MemoryAddress = 0;
995 }
996 else
997 {
998 /* Get the base from the register */
999 ModRegRm->MemoryAddress = State->GeneralRegs[RegMem].Long;
1000 }
1001
1002 /* Check if there is no segment override */
1003 if (!(State->PrefixFlags & FAST486_PREFIX_SEG))
1004 {
1005 /* Check if the default segment should be SS */
1006 if ((RegMem == FAST486_REG_EBP) && Mode)
1007 {
1008 /* Add a SS: prefix */
1009 State->PrefixFlags |= FAST486_PREFIX_SEG;
1010 State->SegmentOverride = FAST486_REG_SS;
1011 }
1012 }
1013
1014 if (Mode == 1)
1015 {
1016 CHAR Offset;
1017
1018 /* Fetch the byte */
1019 if (!Fast486FetchByte(State, (PUCHAR)&Offset))
1020 {
1021 /* Exception occurred */
1022 return FALSE;
1023 }
1024
1025 /* Add the signed offset to the address */
1026 ModRegRm->MemoryAddress += (LONG)Offset;
1027 }
1028 else if ((Mode == 2) || ((Mode == 0) && (RegMem == FAST486_REG_EBP)))
1029 {
1030 LONG Offset;
1031
1032 /* Fetch the dword */
1033 if (!Fast486FetchDword(State, (PULONG)&Offset))
1034 {
1035 /* Exception occurred */
1036 return FALSE;
1037 }
1038
1039 /* Add the signed offset to the address */
1040 ModRegRm->MemoryAddress += Offset;
1041 }
1042 }
1043 else
1044 {
1045 /* Check the operand */
1046 switch (RegMem)
1047 {
1048 case 0:
1049 {
1050 /* [BX + SI] */
1051 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord
1052 + State->GeneralRegs[FAST486_REG_ESI].LowWord;
1053 break;
1054 }
1055
1056 case 1:
1057 {
1058 /* [BX + DI] */
1059 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord
1060 + State->GeneralRegs[FAST486_REG_EDI].LowWord;
1061 break;
1062 }
1063
1064 case 2:
1065 {
1066 /* SS:[BP + SI] */
1067 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord
1068 + State->GeneralRegs[FAST486_REG_ESI].LowWord;
1069 break;
1070 }
1071
1072 case 3:
1073 {
1074 /* SS:[BP + DI] */
1075 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord
1076 + State->GeneralRegs[FAST486_REG_EDI].LowWord;
1077 break;
1078 }
1079
1080 case 4:
1081 {
1082 /* [SI] */
1083 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_ESI].LowWord;
1084 break;
1085 }
1086
1087 case 5:
1088 {
1089 /* [DI] */
1090 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EDI].LowWord;
1091 break;
1092 }
1093
1094 case 6:
1095 {
1096 if (Mode)
1097 {
1098 /* [BP] */
1099 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord;
1100 }
1101 else
1102 {
1103 /* [constant] (added later) */
1104 ModRegRm->MemoryAddress = 0;
1105 }
1106
1107 break;
1108 }
1109
1110 case 7:
1111 {
1112 /* [BX] */
1113 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord;
1114 break;
1115 }
1116 }
1117
1118 /* Check if there is no segment override */
1119 if (!(State->PrefixFlags & FAST486_PREFIX_SEG))
1120 {
1121 /* Check if the default segment should be SS */
1122 if ((RegMem == 2) || (RegMem == 3) || ((RegMem == 6) && Mode))
1123 {
1124 /* Add a SS: prefix */
1125 State->PrefixFlags |= FAST486_PREFIX_SEG;
1126 State->SegmentOverride = FAST486_REG_SS;
1127 }
1128 }
1129
1130 if (Mode == 1)
1131 {
1132 CHAR Offset;
1133
1134 /* Fetch the byte */
1135 if (!Fast486FetchByte(State, (PUCHAR)&Offset))
1136 {
1137 /* Exception occurred */
1138 return FALSE;
1139 }
1140
1141 /* Add the signed offset to the address */
1142 ModRegRm->MemoryAddress += (LONG)Offset;
1143 }
1144 else if ((Mode == 2) || ((Mode == 0) && (RegMem == 6)))
1145 {
1146 SHORT Offset;
1147
1148 /* Fetch the word */
1149 if (!Fast486FetchWord(State, (PUSHORT)&Offset))
1150 {
1151 /* Exception occurred */
1152 return FALSE;
1153 }
1154
1155 /* Add the signed offset to the address */
1156 ModRegRm->MemoryAddress += (LONG)Offset;
1157 }
1158
1159 /* Clear the top 16 bits */
1160 ModRegRm->MemoryAddress &= 0x0000FFFF;
1161 }
1162
1163 return TRUE;
1164 }
1165
1166 FORCEINLINE
1167 BOOLEAN
1168 FASTCALL
1169 Fast486ReadModrmByteOperands(PFAST486_STATE State,
1170 PFAST486_MOD_REG_RM ModRegRm,
1171 PUCHAR RegValue,
1172 PUCHAR RmValue)
1173 {
1174 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1175
1176 if (RegValue)
1177 {
1178 /* Get the register value */
1179 if (ModRegRm->Register & 0x04)
1180 {
1181 /* AH, CH, DH, BH */
1182 *RegValue = State->GeneralRegs[ModRegRm->Register & 0x03].HighByte;
1183 }
1184 else
1185 {
1186 /* AL, CL, DL, BL */
1187 *RegValue = State->GeneralRegs[ModRegRm->Register & 0x03].LowByte;
1188 }
1189 }
1190
1191 if (RmValue)
1192 {
1193 if (!ModRegRm->Memory)
1194 {
1195 /* Get the second register value */
1196 if (ModRegRm->SecondRegister & 0x04)
1197 {
1198 /* AH, CH, DH, BH */
1199 *RmValue = State->GeneralRegs[ModRegRm->SecondRegister & 0x03].HighByte;
1200 }
1201 else
1202 {
1203 /* AL, CL, DL, BL */
1204 *RmValue = State->GeneralRegs[ModRegRm->SecondRegister & 0x03].LowByte;
1205 }
1206 }
1207 else
1208 {
1209 /* Check for the segment override */
1210 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1211 {
1212 /* Use the override segment instead */
1213 Segment = State->SegmentOverride;
1214 }
1215
1216 /* Read memory */
1217 if (!Fast486ReadMemory(State,
1218 Segment,
1219 ModRegRm->MemoryAddress,
1220 FALSE,
1221 RmValue,
1222 sizeof(UCHAR)))
1223 {
1224 /* Exception occurred */
1225 return FALSE;
1226 }
1227 }
1228 }
1229
1230 return TRUE;
1231 }
1232
1233 FORCEINLINE
1234 BOOLEAN
1235 FASTCALL
1236 Fast486ReadModrmWordOperands(PFAST486_STATE State,
1237 PFAST486_MOD_REG_RM ModRegRm,
1238 PUSHORT RegValue,
1239 PUSHORT RmValue)
1240 {
1241 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1242
1243 if (RegValue)
1244 {
1245 /* Get the register value */
1246 *RegValue = State->GeneralRegs[ModRegRm->Register].LowWord;
1247 }
1248
1249 if (RmValue)
1250 {
1251 if (!ModRegRm->Memory)
1252 {
1253 /* Get the second register value */
1254 *RmValue = State->GeneralRegs[ModRegRm->SecondRegister].LowWord;
1255 }
1256 else
1257 {
1258 /* Check for the segment override */
1259 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1260 {
1261 /* Use the override segment instead */
1262 Segment = State->SegmentOverride;
1263 }
1264
1265 /* Read memory */
1266 if (!Fast486ReadMemory(State,
1267 Segment,
1268 ModRegRm->MemoryAddress,
1269 FALSE,
1270 RmValue,
1271 sizeof(USHORT)))
1272 {
1273 /* Exception occurred */
1274 return FALSE;
1275 }
1276 }
1277 }
1278
1279 return TRUE;
1280 }
1281
1282 FORCEINLINE
1283 BOOLEAN
1284 FASTCALL
1285 Fast486ReadModrmDwordOperands(PFAST486_STATE State,
1286 PFAST486_MOD_REG_RM ModRegRm,
1287 PULONG RegValue,
1288 PULONG RmValue)
1289 {
1290 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1291
1292 if (RegValue)
1293 {
1294 /* Get the register value */
1295 *RegValue = State->GeneralRegs[ModRegRm->Register].Long;
1296 }
1297
1298 if (RmValue)
1299 {
1300 if (!ModRegRm->Memory)
1301 {
1302 /* Get the second register value */
1303 *RmValue = State->GeneralRegs[ModRegRm->SecondRegister].Long;
1304 }
1305 else
1306 {
1307 /* Check for the segment override */
1308 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1309 {
1310 /* Use the override segment instead */
1311 Segment = State->SegmentOverride;
1312 }
1313
1314 /* Read memory */
1315 if (!Fast486ReadMemory(State,
1316 Segment,
1317 ModRegRm->MemoryAddress,
1318 FALSE,
1319 RmValue,
1320 sizeof(ULONG)))
1321 {
1322 /* Exception occurred */
1323 return FALSE;
1324 }
1325 }
1326 }
1327
1328 return TRUE;
1329 }
1330
1331 FORCEINLINE
1332 BOOLEAN
1333 FASTCALL
1334 Fast486WriteModrmByteOperands(PFAST486_STATE State,
1335 PFAST486_MOD_REG_RM ModRegRm,
1336 BOOLEAN WriteRegister,
1337 UCHAR Value)
1338 {
1339 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1340
1341 if (WriteRegister)
1342 {
1343 /* Store the value in the register */
1344 if (ModRegRm->Register & 0x04)
1345 {
1346 /* AH, CH, DH, BH */
1347 State->GeneralRegs[ModRegRm->Register & 0x03].HighByte = Value;
1348 }
1349 else
1350 {
1351 /* AL, CL, DL, BL */
1352 State->GeneralRegs[ModRegRm->Register & 0x03].LowByte = Value;
1353 }
1354 }
1355 else
1356 {
1357 if (!ModRegRm->Memory)
1358 {
1359 /* Store the value in the second register */
1360 if (ModRegRm->SecondRegister & 0x04)
1361 {
1362 /* AH, CH, DH, BH */
1363 State->GeneralRegs[ModRegRm->SecondRegister & 0x03].HighByte = Value;
1364 }
1365 else
1366 {
1367 /* AL, CL, DL, BL */
1368 State->GeneralRegs[ModRegRm->SecondRegister & 0x03].LowByte = Value;
1369 }
1370 }
1371 else
1372 {
1373 /* Check for the segment override */
1374 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1375 {
1376 /* Use the override segment instead */
1377 Segment = State->SegmentOverride;
1378 }
1379
1380 /* Write memory */
1381 if (!Fast486WriteMemory(State,
1382 Segment,
1383 ModRegRm->MemoryAddress,
1384 &Value,
1385 sizeof(UCHAR)))
1386 {
1387 /* Exception occurred */
1388 return FALSE;
1389 }
1390 }
1391 }
1392
1393 return TRUE;
1394 }
1395
1396 FORCEINLINE
1397 BOOLEAN
1398 FASTCALL
1399 Fast486WriteModrmWordOperands(PFAST486_STATE State,
1400 PFAST486_MOD_REG_RM ModRegRm,
1401 BOOLEAN WriteRegister,
1402 USHORT Value)
1403 {
1404 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1405
1406 if (WriteRegister)
1407 {
1408 /* Store the value in the register */
1409 State->GeneralRegs[ModRegRm->Register].LowWord = Value;
1410 }
1411 else
1412 {
1413 if (!ModRegRm->Memory)
1414 {
1415 /* Store the value in the second register */
1416 State->GeneralRegs[ModRegRm->SecondRegister].LowWord = Value;
1417 }
1418 else
1419 {
1420 /* Check for the segment override */
1421 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1422 {
1423 /* Use the override segment instead */
1424 Segment = State->SegmentOverride;
1425 }
1426
1427 /* Write memory */
1428 if (!Fast486WriteMemory(State,
1429 Segment,
1430 ModRegRm->MemoryAddress,
1431 &Value,
1432 sizeof(USHORT)))
1433 {
1434 /* Exception occurred */
1435 return FALSE;
1436 }
1437 }
1438 }
1439
1440 return TRUE;
1441 }
1442
1443 FORCEINLINE
1444 BOOLEAN
1445 FASTCALL
1446 Fast486WriteModrmDwordOperands(PFAST486_STATE State,
1447 PFAST486_MOD_REG_RM ModRegRm,
1448 BOOLEAN WriteRegister,
1449 ULONG Value)
1450 {
1451 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1452
1453 if (WriteRegister)
1454 {
1455 /* Store the value in the register */
1456 State->GeneralRegs[ModRegRm->Register].Long = Value;
1457 }
1458 else
1459 {
1460 if (!ModRegRm->Memory)
1461 {
1462 /* Store the value in the second register */
1463 State->GeneralRegs[ModRegRm->SecondRegister].Long = Value;
1464 }
1465 else
1466 {
1467 /* Check for the segment override */
1468 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1469 {
1470 /* Use the override segment instead */
1471 Segment = State->SegmentOverride;
1472 }
1473
1474 /* Write memory */
1475 if (!Fast486WriteMemory(State,
1476 Segment,
1477 ModRegRm->MemoryAddress,
1478 &Value,
1479 sizeof(ULONG)))
1480 {
1481 /* Exception occurred */
1482 return FALSE;
1483 }
1484 }
1485 }
1486
1487 return TRUE;
1488 }
1489
1490 #ifndef FAST486_NO_FPU
1491
1492 FORCEINLINE
1493 VOID
1494 FASTCALL
1495 Fast486FpuNormalize(PFAST486_STATE State,
1496 PFAST486_FPU_DATA_REG Data)
1497 {
1498 UINT LeadingZeros;
1499
1500 if (FPU_IS_NORMALIZED(Data)) return;
1501 if (FPU_IS_ZERO(Data))
1502 {
1503 Data->Exponent = 0;
1504 return;
1505 }
1506
1507 LeadingZeros = CountLeadingZeros64(Data->Mantissa);
1508
1509 if (LeadingZeros < Data->Exponent)
1510 {
1511 Data->Mantissa <<= LeadingZeros;
1512 Data->Exponent -= LeadingZeros;
1513 }
1514 else
1515 {
1516 /* Make it denormalized */
1517 Data->Mantissa <<= Data->Exponent - 1;
1518 Data->Exponent = 1;
1519
1520 /* Underflow */
1521 State->FpuStatus.Ue = TRUE;
1522 }
1523 }
1524
1525 FORCEINLINE
1526 USHORT
1527 FASTCALL
1528 Fast486GetValueTag(PFAST486_FPU_DATA_REG Data)
1529 {
1530 if (FPU_IS_ZERO(Data)) return FPU_TAG_ZERO;
1531 else if (FPU_IS_NAN(Data)) return FPU_TAG_SPECIAL;
1532 else return FPU_TAG_VALID;
1533 }
1534
1535 FORCEINLINE
1536 VOID
1537 FASTCALL
1538 Fast486FpuPush(PFAST486_STATE State,
1539 PFAST486_FPU_DATA_REG Data)
1540 {
1541 State->FpuStatus.Top--;
1542
1543 if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
1544 {
1545 FPU_ST(0) = *Data;
1546 FPU_SET_TAG(0, Fast486GetValueTag(Data));
1547 }
1548 else State->FpuStatus.Ie = TRUE;
1549 }
1550
1551 FORCEINLINE
1552 VOID
1553 FASTCALL
1554 Fast486FpuPop(PFAST486_STATE State)
1555 {
1556 if (FPU_GET_TAG(0) != FPU_TAG_EMPTY)
1557 {
1558 FPU_SET_TAG(0, FPU_TAG_EMPTY);
1559 State->FpuStatus.Top++;
1560 }
1561 else State->FpuStatus.Ie = TRUE;
1562 }
1563
1564 #endif
1565
1566 /* EOF */