2 * Fast486 386/486 CPU Emulation Library
5 * Copyright (C) 2015 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.
25 /* PUBLIC FUNCTIONS ***********************************************************/
27 #if defined (__GNUC__)
28 #define CountLeadingZeros64(x) __builtin_clzll(x)
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) \
41 CountLeadingZeros64(ULONGLONG Value)
45 while ((LONGLONG)Value < 0)
57 Fast486GetCurrentPrivLevel(PFAST486_STATE State)
59 /* Return the CPL, or 3 if we're in virtual 8086 mode */
60 return (!State->Flags.Vm) ? State->Cpl : 3;
66 Fast486GetPageTableEntry(PFAST486_STATE State,
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];
76 if ((State->Tlb != NULL)
77 && (State->Tlb[VirtualAddress >> 12] != INVALID_TLB_FIELD))
79 /* Return the cached entry */
80 return State->Tlb[VirtualAddress >> 12];
83 /* Read the directory entry */
84 State->MemReadCallback(State,
85 PageDirectory + PdeIndex * sizeof(ULONG),
86 &DirectoryEntry.Value,
87 sizeof(DirectoryEntry));
89 /* Make sure it is present */
90 if (!DirectoryEntry.Present) return 0;
92 /* Was the directory entry accessed before? */
93 if (!DirectoryEntry.Accessed)
96 DirectoryEntry.Accessed = TRUE;
98 /* Write back the directory entry */
99 State->MemWriteCallback(State,
100 PageDirectory + PdeIndex * sizeof(ULONG),
101 &DirectoryEntry.Value,
102 sizeof(DirectoryEntry));
105 /* Read the table entry */
106 State->MemReadCallback(State,
107 (DirectoryEntry.TableAddress << 12)
108 + PteIndex * sizeof(ULONG),
112 /* Make sure it is present */
113 if (!TableEntry.Present) return 0;
115 /* Do we need to change any flags? */
116 if (!TableEntry.Accessed || (MarkAsDirty && !TableEntry.Dirty))
118 /* Mark it as accessed and optionally dirty too */
119 TableEntry.Accessed = TRUE;
120 if (MarkAsDirty) TableEntry.Dirty = TRUE;
122 /* Write back the table entry */
123 State->MemWriteCallback(State,
124 (DirectoryEntry.TableAddress << 12)
125 + PteIndex * sizeof(ULONG),
131 * The resulting permissions depend on the permissions
132 * in the page directory table too
134 TableEntry.Writeable &= DirectoryEntry.Writeable;
135 TableEntry.Usermode &= DirectoryEntry.Usermode;
137 if (State->Tlb != NULL)
139 /* Set the TLB entry */
140 State->Tlb[VirtualAddress >> 12] = TableEntry.Value;
141 State->TlbEmpty = FALSE;
144 /* Return the table entry */
145 return TableEntry.Value;
151 Fast486FlushTlb(PFAST486_STATE State)
153 if (!State->Tlb || State->TlbEmpty) return;
154 RtlFillMemory(State->Tlb, NUM_TLB_ENTRIES * sizeof(ULONG), 0xFF);
155 State->TlbEmpty = TRUE;
161 Fast486ReadLinearMemory(PFAST486_STATE State,
165 BOOLEAN CheckPrivilege)
167 /* Check if paging is enabled */
168 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG)
171 FAST486_PAGE_TABLE TableEntry;
172 INT Cpl = Fast486GetCurrentPrivLevel(State);
173 ULONG BufferOffset = 0;
175 for (Page = PAGE_ALIGN(LinearAddress);
176 Page <= PAGE_ALIGN(LinearAddress + Size - 1);
177 Page += FAST486_PAGE_SIZE)
179 ULONG PageOffset = 0, PageLength = FAST486_PAGE_SIZE;
181 /* Get the table entry */
182 TableEntry.Value = Fast486GetPageTableEntry(State, Page, FALSE);
184 /* Check if this is the first page */
185 if (Page == PAGE_ALIGN(LinearAddress))
187 /* Start reading from the offset from the beginning of the page */
188 PageOffset = PAGE_OFFSET(LinearAddress);
189 PageLength -= PageOffset;
192 if (CheckPrivilege && (!TableEntry.Present || (!TableEntry.Usermode && (Cpl > 0))))
194 State->ControlRegisters[FAST486_REG_CR2] = Page + PageOffset;
197 Fast486ExceptionWithErrorCode(State,
198 FAST486_EXCEPTION_PF,
199 TableEntry.Present | (State->Cpl ? 0x04 : 0));
203 /* Check if this is the last page */
204 if (Page == PAGE_ALIGN(LinearAddress + Size - 1))
206 /* Read only a part of the page */
207 PageLength = PAGE_OFFSET(LinearAddress + Size - 1) - PageOffset + 1;
210 /* Read the memory */
211 State->MemReadCallback(State,
212 (TableEntry.Address << 12) | PageOffset,
213 (PVOID)((ULONG_PTR)Buffer + BufferOffset),
216 BufferOffset += PageLength;
221 /* Read the memory */
222 State->MemReadCallback(State, LinearAddress, Buffer, Size);
231 Fast486WriteLinearMemory(PFAST486_STATE State,
235 BOOLEAN CheckPrivilege)
237 /* Check if paging is enabled */
238 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG)
241 FAST486_PAGE_TABLE TableEntry;
242 INT Cpl = Fast486GetCurrentPrivLevel(State);
243 ULONG BufferOffset = 0;
245 for (Page = PAGE_ALIGN(LinearAddress);
246 Page <= PAGE_ALIGN(LinearAddress + Size - 1);
247 Page += FAST486_PAGE_SIZE)
249 ULONG PageOffset = 0, PageLength = FAST486_PAGE_SIZE;
251 /* Get the table entry */
252 TableEntry.Value = Fast486GetPageTableEntry(State, Page, TRUE);
254 /* Check if this is the first page */
255 if (Page == PAGE_ALIGN(LinearAddress))
257 /* Start writing from the offset from the beginning of the page */
258 PageOffset = PAGE_OFFSET(LinearAddress);
259 PageLength -= PageOffset;
263 && ((!TableEntry.Present || (!TableEntry.Usermode && (Cpl > 0)))
264 || ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_WP)
265 && !TableEntry.Writeable)))
267 State->ControlRegisters[FAST486_REG_CR2] = Page + PageOffset;
270 Fast486ExceptionWithErrorCode(State,
271 FAST486_EXCEPTION_PF,
272 TableEntry.Present | 0x02 | (State->Cpl ? 0x04 : 0));
276 /* Check if this is the last page */
277 if (Page == PAGE_ALIGN(LinearAddress + Size - 1))
279 /* Write only a part of the page */
280 PageLength = PAGE_OFFSET(LinearAddress + Size - 1) - PageOffset + 1;
283 /* Write the memory */
284 State->MemWriteCallback(State,
285 (TableEntry.Address << 12) | PageOffset,
286 (PVOID)((ULONG_PTR)Buffer + BufferOffset),
289 BufferOffset += PageLength;
294 /* Write the memory */
295 State->MemWriteCallback(State, LinearAddress, Buffer, Size);
304 Fast486Exception(PFAST486_STATE State,
305 FAST486_EXCEPTIONS ExceptionCode)
307 /* Call the internal function */
308 Fast486ExceptionWithErrorCode(State, ExceptionCode, 0);
314 Fast486StackPushInternal(PFAST486_STATE State, BOOLEAN Size, ULONG Value)
316 ULONG StackPointer = State->GeneralRegs[FAST486_REG_ESP].Long;
320 /* Check if ESP is between 1 and 3 */
321 if (State->GeneralRegs[FAST486_REG_ESP].Long >= 1
322 && State->GeneralRegs[FAST486_REG_ESP].Long <= 3)
324 Fast486Exception(State, FAST486_EXCEPTION_SS);
328 /* Store the value in SS:[ESP - 4] */
329 if (!Fast486WriteMemory(State,
331 State->SegmentRegs[FAST486_REG_SS].Size
332 ? StackPointer - sizeof(ULONG)
333 : LOWORD(StackPointer - sizeof(ULONG)),
337 /* Exception occurred */
341 if (State->SegmentRegs[FAST486_REG_SS].Size)
343 /* Subtract ESP by 4 */
344 State->GeneralRegs[FAST486_REG_ESP].Long -= sizeof(ULONG);
348 /* Subtract SP by 4 */
349 State->GeneralRegs[FAST486_REG_ESP].LowWord -= sizeof(ULONG);
354 /* Check if SP is 1 */
355 if (State->GeneralRegs[FAST486_REG_ESP].LowWord == 1)
357 Fast486Exception(State, FAST486_EXCEPTION_SS);
361 /* Store the value in SS:[SP - 2] */
362 if (!Fast486WriteMemory(State,
364 State->SegmentRegs[FAST486_REG_SS].Size
365 ? StackPointer - sizeof(USHORT)
366 : LOWORD(StackPointer - sizeof(USHORT)),
370 /* Exception occurred */
374 if (State->SegmentRegs[FAST486_REG_SS].Size)
376 /* Subtract ESP by 2 */
377 State->GeneralRegs[FAST486_REG_ESP].Long -= sizeof(USHORT);
381 /* Subtract SP by 2 */
382 State->GeneralRegs[FAST486_REG_ESP].LowWord -= sizeof(USHORT);
392 Fast486StackPush(PFAST486_STATE State, ULONG Value)
394 BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;
396 /* The OPSIZE prefix toggles the size */
399 /* Call the internal function */
400 return Fast486StackPushInternal(State, Size, Value);
406 Fast486StackPop(PFAST486_STATE State,
409 BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;
411 /* The OPSIZE prefix toggles the size */
419 /* Check if ESP is 0xFFFFFFFF */
420 if (State->GeneralRegs[FAST486_REG_ESP].Long == 0xFFFFFFFF)
422 Fast486Exception(State, FAST486_EXCEPTION_SS);
426 /* Read the value from SS:ESP */
427 if (!Fast486ReadMemory(State,
429 State->SegmentRegs[FAST486_REG_SS].Size
430 ? State->GeneralRegs[FAST486_REG_ESP].Long
431 : State->GeneralRegs[FAST486_REG_ESP].LowWord,
436 /* An exception occurred */
440 if (State->SegmentRegs[FAST486_REG_SS].Size)
442 /* Increment ESP by 4 */
443 State->GeneralRegs[FAST486_REG_ESP].Long += sizeof(ULONG);
447 /* Increment SP by 4 */
448 State->GeneralRegs[FAST486_REG_ESP].LowWord += sizeof(ULONG);
451 /* Store the value in the result */
459 /* Check if SP is 0xFFFF */
460 if (State->GeneralRegs[FAST486_REG_ESP].LowWord == 0xFFFF)
462 Fast486Exception(State, FAST486_EXCEPTION_SS);
466 /* Read the value from SS:SP */
467 if (!Fast486ReadMemory(State,
469 State->SegmentRegs[FAST486_REG_SS].Size
470 ? State->GeneralRegs[FAST486_REG_ESP].Long
471 : State->GeneralRegs[FAST486_REG_ESP].LowWord,
476 /* An exception occurred */
480 if (State->SegmentRegs[FAST486_REG_SS].Size)
482 /* Increment ESP by 2 */
483 State->GeneralRegs[FAST486_REG_ESP].Long += sizeof(USHORT);
487 /* Increment SP by 2 */
488 State->GeneralRegs[FAST486_REG_ESP].LowWord += sizeof(USHORT);
491 /* Store the value in the result */
501 Fast486ReadDescriptorEntry(PFAST486_STATE State,
504 PFAST486_GDT_ENTRY Entry)
506 if (!(Selector & SEGMENT_TABLE_INDICATOR))
508 /* Make sure the GDT contains the entry */
509 if (GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1u))
516 if (!Fast486ReadLinearMemory(State,
518 + GET_SEGMENT_INDEX(Selector),
523 /* Exception occurred */
530 /* Make sure the LDT contains the entry */
531 if (GET_SEGMENT_INDEX(Selector) >= (State->Ldtr.Limit + 1u))
538 if (!Fast486ReadLinearMemory(State,
540 + GET_SEGMENT_INDEX(Selector),
545 /* Exception occurred */
558 Fast486LoadSegmentInternal(PFAST486_STATE State,
559 FAST486_SEG_REGS Segment,
561 FAST486_EXCEPTIONS Exception)
563 PFAST486_SEG_REG CachedDescriptor;
565 FAST486_GDT_ENTRY GdtEntry;
567 ASSERT(Segment < FAST486_NUM_SEG_REGS);
569 /* Get the cached descriptor */
570 CachedDescriptor = &State->SegmentRegs[Segment];
572 /* Check for protected mode */
573 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
575 /* Check for VM86 mode */
578 /* Update the cached descriptor with VM86 values */
579 CachedDescriptor->Selector = Selector;
580 CachedDescriptor->Base = Selector << 4;
581 CachedDescriptor->Limit = 0xFFFF;
582 CachedDescriptor->ReadWrite = TRUE;
583 CachedDescriptor->DirConf = FALSE;
584 CachedDescriptor->SystemType = TRUE;
585 CachedDescriptor->Dpl = CachedDescriptor->Rpl = 3;
586 CachedDescriptor->Present = TRUE;
587 CachedDescriptor->Size = FALSE;
591 if (!Fast486ReadDescriptorEntry(State, Selector, &Valid, &GdtEntry))
593 /* Exception occurred */
599 /* Invalid selector */
600 Fast486ExceptionWithErrorCode(State, Exception, Selector);
604 if (Segment == FAST486_REG_SS)
606 /* Loading the stack segment */
608 if (!(Selector & SEGMENT_TABLE_INDICATOR) && GET_SEGMENT_INDEX(Selector) == 0)
610 Fast486Exception(State, Exception);
614 if (!GdtEntry.SystemType)
616 /* This is a special descriptor */
617 Fast486ExceptionWithErrorCode(State, Exception, Selector);
621 if (GdtEntry.Executable || !GdtEntry.ReadWrite)
623 Fast486ExceptionWithErrorCode(State, Exception, Selector);
627 if ((GET_SEGMENT_RPL(Selector) != Fast486GetCurrentPrivLevel(State))
628 || (GET_SEGMENT_RPL(Selector) != GdtEntry.Dpl))
630 Fast486ExceptionWithErrorCode(State, Exception, Selector);
634 if (!GdtEntry.Present)
636 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_SS, Selector);
640 else if (Segment == FAST486_REG_CS)
642 /* Loading the code segment */
644 #ifndef FAST486_NO_PREFETCH
645 /* Invalidate the prefetch */
646 State->PrefetchValid = FALSE;
649 if (!(Selector & SEGMENT_TABLE_INDICATOR) && GET_SEGMENT_INDEX(Selector) == 0)
651 Fast486Exception(State, Exception);
655 if (!GdtEntry.SystemType)
657 /* Must be a segment descriptor */
658 Fast486ExceptionWithErrorCode(State, Exception, Selector);
662 if (!GdtEntry.Present)
664 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector);
668 if (!GdtEntry.Executable)
670 Fast486ExceptionWithErrorCode(State, Exception, Selector);
674 if (GdtEntry.DirConf)
676 /* Conforming Code Segment */
678 if (GdtEntry.Dpl > Fast486GetCurrentPrivLevel(State))
680 /* Must be accessed from lower-privileged code */
681 Fast486ExceptionWithErrorCode(State, Exception, Selector);
687 /* Regular code segment */
689 if ((GET_SEGMENT_RPL(Selector) < Fast486GetCurrentPrivLevel(State)))
691 Fast486ExceptionWithErrorCode(State, Exception, Selector);
698 /* Loading a data segment */
700 if (GET_SEGMENT_INDEX(Selector) != 0 || (Selector & SEGMENT_TABLE_INDICATOR))
702 if (!GdtEntry.SystemType)
704 /* This is a special descriptor */
705 Fast486ExceptionWithErrorCode(State, Exception, Selector);
709 if ((GET_SEGMENT_RPL(Selector) > GdtEntry.Dpl)
710 || (Fast486GetCurrentPrivLevel(State) > GdtEntry.Dpl))
712 Fast486ExceptionWithErrorCode(State, Exception, Selector);
716 if (!GdtEntry.Present)
718 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector);
724 /* This is a NULL selector */
725 RtlZeroMemory(&GdtEntry, sizeof(GdtEntry));
729 /* Update the cache entry */
730 CachedDescriptor->Selector = Selector;
731 CachedDescriptor->Base = GdtEntry.Base | (GdtEntry.BaseMid << 16) | (GdtEntry.BaseHigh << 24);
732 CachedDescriptor->Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16);
733 CachedDescriptor->Accessed = GdtEntry.Accessed;
734 CachedDescriptor->ReadWrite = GdtEntry.ReadWrite;
735 CachedDescriptor->DirConf = GdtEntry.DirConf;
736 CachedDescriptor->Executable = GdtEntry.Executable;
737 CachedDescriptor->SystemType = GdtEntry.SystemType;
738 CachedDescriptor->Rpl = GET_SEGMENT_RPL(Selector);
739 CachedDescriptor->Dpl = GdtEntry.Dpl;
740 CachedDescriptor->Present = GdtEntry.Present;
741 CachedDescriptor->Size = GdtEntry.Size;
743 /* Check for page granularity */
744 if (GdtEntry.Granularity)
746 CachedDescriptor->Limit <<= 12;
747 CachedDescriptor->Limit |= 0x00000FFF;
752 /* Update the selector and base */
753 CachedDescriptor->Selector = Selector;
754 CachedDescriptor->Base = Selector << 4;
763 Fast486LoadSegment(PFAST486_STATE State,
764 FAST486_SEG_REGS Segment,
767 return Fast486LoadSegmentInternal(State,
770 FAST486_EXCEPTION_GP);
776 Fast486ProcessGate(PFAST486_STATE State, USHORT Selector, ULONG Offset, BOOLEAN Call)
779 FAST486_SYSTEM_DESCRIPTOR Descriptor;
781 if (!Fast486ReadDescriptorEntry(State,
784 (PFAST486_GDT_ENTRY)&Descriptor))
786 /* Exception occurred */
792 /* Invalid selector */
793 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
797 switch (Descriptor.Signature)
799 case FAST486_TASK_GATE_SIGNATURE:
801 Fast486TaskSwitch(State,
802 Call ? FAST486_TASK_CALL : FAST486_TASK_JUMP,
803 ((PFAST486_IDT_ENTRY)&Descriptor)->Selector);
808 case FAST486_TSS_16_SIGNATURE:
809 case FAST486_BUSY_TSS_16_SIGNATURE:
810 case FAST486_TSS_SIGNATURE:
811 case FAST486_BUSY_TSS_SIGNATURE:
813 Fast486TaskSwitch(State,
814 Call ? FAST486_TASK_CALL : FAST486_TASK_JUMP,
820 case FAST486_CALL_GATE_16_SIGNATURE:
821 case FAST486_CALL_GATE_SIGNATURE:
823 if ((Descriptor.Dpl < Fast486GetCurrentPrivLevel(State))
824 && (Descriptor.Dpl < GET_SEGMENT_RPL(Selector)))
826 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
830 if (!Descriptor.Present)
832 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector);
836 Fast486CallGate(State, (PFAST486_CALL_GATE)&Descriptor, Call);
838 /* The gate has been processed here, so return FALSE */
844 /* Security check for jumps and calls only */
845 if (State->Cpl != Descriptor.Dpl)
847 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
859 Fast486FetchByte(PFAST486_STATE State,
862 PFAST486_SEG_REG CachedDescriptor;
864 #ifndef FAST486_NO_PREFETCH
868 /* Get the cached descriptor of CS */
869 CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS];
871 Offset = (CachedDescriptor->Size) ? State->InstPtr.Long
872 : State->InstPtr.LowWord;
873 #ifndef FAST486_NO_PREFETCH
874 LinearAddress = CachedDescriptor->Base + Offset;
876 if (State->PrefetchValid
877 && (LinearAddress >= State->PrefetchAddress)
878 && ((LinearAddress + sizeof(UCHAR)) <= (State->PrefetchAddress + FAST486_CACHE_SIZE)))
880 *Data = *(PUCHAR)&State->PrefetchCache[LinearAddress - State->PrefetchAddress];
885 /* Read from memory */
886 if (!Fast486ReadMemory(State,
893 /* Exception occurred during instruction fetch */
898 /* Advance the instruction pointer */
899 if (CachedDescriptor->Size) State->InstPtr.Long++;
900 else State->InstPtr.LowWord++;
908 Fast486FetchWord(PFAST486_STATE State,
911 PFAST486_SEG_REG CachedDescriptor;
913 #ifndef FAST486_NO_PREFETCH
917 /* Get the cached descriptor of CS */
918 CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS];
920 Offset = (CachedDescriptor->Size) ? State->InstPtr.Long
921 : State->InstPtr.LowWord;
923 #ifndef FAST486_NO_PREFETCH
924 LinearAddress = CachedDescriptor->Base + Offset;
926 if (State->PrefetchValid
927 && (LinearAddress >= State->PrefetchAddress)
928 && ((LinearAddress + sizeof(USHORT)) <= (State->PrefetchAddress + FAST486_CACHE_SIZE)))
930 *Data = *(PUSHORT)&State->PrefetchCache[LinearAddress - State->PrefetchAddress];
935 /* Read from memory */
936 // FIXME: Fix byte order on big-endian machines
937 if (!Fast486ReadMemory(State,
944 /* Exception occurred during instruction fetch */
949 /* Advance the instruction pointer */
950 if (CachedDescriptor->Size) State->InstPtr.Long += sizeof(USHORT);
951 else State->InstPtr.LowWord += sizeof(USHORT);
959 Fast486FetchDword(PFAST486_STATE State,
962 PFAST486_SEG_REG CachedDescriptor;
964 #ifndef FAST486_NO_PREFETCH
968 /* Get the cached descriptor of CS */
969 CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS];
971 Offset = (CachedDescriptor->Size) ? State->InstPtr.Long
972 : State->InstPtr.LowWord;
974 #ifndef FAST486_NO_PREFETCH
975 LinearAddress = CachedDescriptor->Base + Offset;
977 if (State->PrefetchValid
978 && (LinearAddress >= State->PrefetchAddress)
979 && ((LinearAddress + sizeof(ULONG)) <= (State->PrefetchAddress + FAST486_CACHE_SIZE)))
981 *Data = *(PULONG)&State->PrefetchCache[LinearAddress - State->PrefetchAddress];
986 /* Read from memory */
987 // FIXME: Fix byte order on big-endian machines
988 if (!Fast486ReadMemory(State,
995 /* Exception occurred during instruction fetch */
1000 /* Advance the instruction pointer */
1001 if (CachedDescriptor->Size) State->InstPtr.Long += sizeof(ULONG);
1002 else State->InstPtr.LowWord += sizeof(ULONG);
1010 Fast486CalculateParity(UCHAR Number)
1012 // See http://graphics.stanford.edu/~seander/bithacks.html#ParityLookupTable too...
1013 return (0x9669 >> ((Number & 0x0F) ^ (Number >> 4))) & 1;
1019 Fast486ParseModRegRm(PFAST486_STATE State,
1020 BOOLEAN AddressSize,
1021 PFAST486_MOD_REG_RM ModRegRm)
1023 UCHAR ModRmByte, Mode, RegMem;
1025 /* Fetch the MOD REG R/M byte */
1026 if (!Fast486FetchByte(State, &ModRmByte))
1028 /* Exception occurred */
1032 /* Unpack the mode and R/M */
1033 Mode = ModRmByte >> 6;
1034 RegMem = ModRmByte & 0x07;
1036 /* Set the register operand */
1037 ModRegRm->Register = (ModRmByte >> 3) & 0x07;
1039 /* Check the mode */
1042 /* The second operand is also a register */
1043 ModRegRm->Memory = FALSE;
1044 ModRegRm->SecondRegister = RegMem;
1050 /* The second operand is memory */
1051 ModRegRm->Memory = TRUE;
1055 if (RegMem == FAST486_REG_ESP)
1058 ULONG Scale, Index, Base;
1060 /* Fetch the SIB byte */
1061 if (!Fast486FetchByte(State, &SibByte))
1063 /* Exception occurred */
1067 /* Unpack the scale, index and base */
1068 Scale = 1 << (SibByte >> 6);
1069 Index = (SibByte >> 3) & 0x07;
1070 if (Index != FAST486_REG_ESP) Index = State->GeneralRegs[Index].Long;
1073 if (((SibByte & 0x07) != FAST486_REG_EBP) || (Mode != 0))
1075 /* Use the register a base */
1076 Base = State->GeneralRegs[SibByte & 0x07].Long;
1080 /* Fetch the base */
1081 if (!Fast486FetchDword(State, &Base))
1083 /* Exception occurred */
1088 if (((SibByte & 0x07) == FAST486_REG_ESP)
1089 || ((SibByte & 0x07) == FAST486_REG_EBP && Mode != 0))
1091 /* Check if there is no segment override */
1092 if (!(State->PrefixFlags & FAST486_PREFIX_SEG))
1094 /* Add a SS: prefix */
1095 State->PrefixFlags |= FAST486_PREFIX_SEG;
1096 State->SegmentOverride = FAST486_REG_SS;
1100 /* Calculate the address */
1101 ModRegRm->MemoryAddress = Base + Index * Scale;
1103 else if (RegMem == FAST486_REG_EBP)
1105 if (Mode) ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].Long;
1106 else ModRegRm->MemoryAddress = 0;
1110 /* Get the base from the register */
1111 ModRegRm->MemoryAddress = State->GeneralRegs[RegMem].Long;
1114 /* Check if there is no segment override */
1115 if (!(State->PrefixFlags & FAST486_PREFIX_SEG))
1117 /* Check if the default segment should be SS */
1118 if ((RegMem == FAST486_REG_EBP) && Mode)
1120 /* Add a SS: prefix */
1121 State->PrefixFlags |= FAST486_PREFIX_SEG;
1122 State->SegmentOverride = FAST486_REG_SS;
1130 /* Fetch the byte */
1131 if (!Fast486FetchByte(State, (PUCHAR)&Offset))
1133 /* Exception occurred */
1137 /* Add the signed offset to the address */
1138 ModRegRm->MemoryAddress += (LONG)Offset;
1140 else if ((Mode == 2) || ((Mode == 0) && (RegMem == FAST486_REG_EBP)))
1144 /* Fetch the dword */
1145 if (!Fast486FetchDword(State, (PULONG)&Offset))
1147 /* Exception occurred */
1151 /* Add the signed offset to the address */
1152 ModRegRm->MemoryAddress += Offset;
1157 /* Check the operand */
1163 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord
1164 + State->GeneralRegs[FAST486_REG_ESI].LowWord;
1171 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord
1172 + State->GeneralRegs[FAST486_REG_EDI].LowWord;
1179 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord
1180 + State->GeneralRegs[FAST486_REG_ESI].LowWord;
1187 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord
1188 + State->GeneralRegs[FAST486_REG_EDI].LowWord;
1195 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_ESI].LowWord;
1202 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EDI].LowWord;
1211 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord;
1215 /* [constant] (added later) */
1216 ModRegRm->MemoryAddress = 0;
1225 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord;
1230 /* Check if there is no segment override */
1231 if (!(State->PrefixFlags & FAST486_PREFIX_SEG))
1233 /* Check if the default segment should be SS */
1234 if ((RegMem == 2) || (RegMem == 3) || ((RegMem == 6) && Mode))
1236 /* Add a SS: prefix */
1237 State->PrefixFlags |= FAST486_PREFIX_SEG;
1238 State->SegmentOverride = FAST486_REG_SS;
1246 /* Fetch the byte */
1247 if (!Fast486FetchByte(State, (PUCHAR)&Offset))
1249 /* Exception occurred */
1253 /* Add the signed offset to the address */
1254 ModRegRm->MemoryAddress += (LONG)Offset;
1256 else if ((Mode == 2) || ((Mode == 0) && (RegMem == 6)))
1260 /* Fetch the word */
1261 if (!Fast486FetchWord(State, (PUSHORT)&Offset))
1263 /* Exception occurred */
1267 /* Add the signed offset to the address */
1268 ModRegRm->MemoryAddress += (LONG)Offset;
1271 /* Clear the top 16 bits */
1272 ModRegRm->MemoryAddress &= 0x0000FFFF;
1281 Fast486ReadModrmByteOperands(PFAST486_STATE State,
1282 PFAST486_MOD_REG_RM ModRegRm,
1286 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1290 /* Get the register value */
1291 if (ModRegRm->Register & 0x04)
1293 /* AH, CH, DH, BH */
1294 *RegValue = State->GeneralRegs[ModRegRm->Register & 0x03].HighByte;
1298 /* AL, CL, DL, BL */
1299 *RegValue = State->GeneralRegs[ModRegRm->Register & 0x03].LowByte;
1305 if (!ModRegRm->Memory)
1307 /* Get the second register value */
1308 if (ModRegRm->SecondRegister & 0x04)
1310 /* AH, CH, DH, BH */
1311 *RmValue = State->GeneralRegs[ModRegRm->SecondRegister & 0x03].HighByte;
1315 /* AL, CL, DL, BL */
1316 *RmValue = State->GeneralRegs[ModRegRm->SecondRegister & 0x03].LowByte;
1321 /* Check for the segment override */
1322 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1324 /* Use the override segment instead */
1325 Segment = State->SegmentOverride;
1329 if (!Fast486ReadMemory(State,
1331 ModRegRm->MemoryAddress,
1336 /* Exception occurred */
1348 Fast486ReadModrmWordOperands(PFAST486_STATE State,
1349 PFAST486_MOD_REG_RM ModRegRm,
1353 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1357 /* Get the register value */
1358 *RegValue = State->GeneralRegs[ModRegRm->Register].LowWord;
1363 if (!ModRegRm->Memory)
1365 /* Get the second register value */
1366 *RmValue = State->GeneralRegs[ModRegRm->SecondRegister].LowWord;
1370 /* Check for the segment override */
1371 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1373 /* Use the override segment instead */
1374 Segment = State->SegmentOverride;
1378 if (!Fast486ReadMemory(State,
1380 ModRegRm->MemoryAddress,
1385 /* Exception occurred */
1397 Fast486ReadModrmDwordOperands(PFAST486_STATE State,
1398 PFAST486_MOD_REG_RM ModRegRm,
1402 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1406 /* Get the register value */
1407 *RegValue = State->GeneralRegs[ModRegRm->Register].Long;
1412 if (!ModRegRm->Memory)
1414 /* Get the second register value */
1415 *RmValue = State->GeneralRegs[ModRegRm->SecondRegister].Long;
1419 /* Check for the segment override */
1420 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1422 /* Use the override segment instead */
1423 Segment = State->SegmentOverride;
1427 if (!Fast486ReadMemory(State,
1429 ModRegRm->MemoryAddress,
1434 /* Exception occurred */
1446 Fast486WriteModrmByteOperands(PFAST486_STATE State,
1447 PFAST486_MOD_REG_RM ModRegRm,
1448 BOOLEAN WriteRegister,
1451 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1455 /* Store the value in the register */
1456 if (ModRegRm->Register & 0x04)
1458 /* AH, CH, DH, BH */
1459 State->GeneralRegs[ModRegRm->Register & 0x03].HighByte = Value;
1463 /* AL, CL, DL, BL */
1464 State->GeneralRegs[ModRegRm->Register & 0x03].LowByte = Value;
1469 if (!ModRegRm->Memory)
1471 /* Store the value in the second register */
1472 if (ModRegRm->SecondRegister & 0x04)
1474 /* AH, CH, DH, BH */
1475 State->GeneralRegs[ModRegRm->SecondRegister & 0x03].HighByte = Value;
1479 /* AL, CL, DL, BL */
1480 State->GeneralRegs[ModRegRm->SecondRegister & 0x03].LowByte = Value;
1485 /* Check for the segment override */
1486 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1488 /* Use the override segment instead */
1489 Segment = State->SegmentOverride;
1493 if (!Fast486WriteMemory(State,
1495 ModRegRm->MemoryAddress,
1499 /* Exception occurred */
1511 Fast486WriteModrmWordOperands(PFAST486_STATE State,
1512 PFAST486_MOD_REG_RM ModRegRm,
1513 BOOLEAN WriteRegister,
1516 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1520 /* Store the value in the register */
1521 State->GeneralRegs[ModRegRm->Register].LowWord = Value;
1525 if (!ModRegRm->Memory)
1527 /* Store the value in the second register */
1528 State->GeneralRegs[ModRegRm->SecondRegister].LowWord = Value;
1532 /* Check for the segment override */
1533 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1535 /* Use the override segment instead */
1536 Segment = State->SegmentOverride;
1540 if (!Fast486WriteMemory(State,
1542 ModRegRm->MemoryAddress,
1546 /* Exception occurred */
1558 Fast486WriteModrmDwordOperands(PFAST486_STATE State,
1559 PFAST486_MOD_REG_RM ModRegRm,
1560 BOOLEAN WriteRegister,
1563 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1567 /* Store the value in the register */
1568 State->GeneralRegs[ModRegRm->Register].Long = Value;
1572 if (!ModRegRm->Memory)
1574 /* Store the value in the second register */
1575 State->GeneralRegs[ModRegRm->SecondRegister].Long = Value;
1579 /* Check for the segment override */
1580 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1582 /* Use the override segment instead */
1583 Segment = State->SegmentOverride;
1587 if (!Fast486WriteMemory(State,
1589 ModRegRm->MemoryAddress,
1593 /* Exception occurred */
1605 Fast486IoPrivilegeCheck(PFAST486_STATE State, USHORT Port)
1611 /* Access is always allowed if the CPL is less than or equal to the IOPL */
1612 if (State->Cpl <= State->Flags.Iopl) return TRUE;
1614 /* Legacy Task State Segments have no IOPB */
1615 if (!State->TaskReg.Modern) return FALSE;
1618 if (!Fast486ReadLinearMemory(State, State->TaskReg.Base, &Tss, sizeof(FAST486_TSS), FALSE))
1620 /* Exception occurred */
1624 Location = State->TaskReg.Base + HIWORD(Tss.IopbOffset) + (Port >> 3);
1626 if (Location > State->TaskReg.Limit)
1629 Fast486Exception(State, FAST486_EXCEPTION_GP);
1633 /* Read the appropriate bit from the TSS IOPB */
1634 if (!Fast486ReadLinearMemory(State, Location, &Bits, sizeof(UCHAR), FALSE))
1636 /* Exception occurred */
1640 if (Bits & (1 << (Port & 0x07)))
1643 Fast486Exception(State, FAST486_EXCEPTION_GP);
1650 #ifndef FAST486_NO_FPU
1655 Fast486FpuExceptionCheck(PFAST486_STATE State)
1657 /* Check if an unmasked exception occurred */
1658 if ((State->FpuStatus.Ie && !State->FpuControl.Im)
1659 || (State->FpuStatus.De && !State->FpuControl.Dm)
1660 || (State->FpuStatus.Ze && !State->FpuControl.Zm)
1661 || (State->FpuStatus.Oe && !State->FpuControl.Om)
1662 || (State->FpuStatus.Ue && !State->FpuControl.Um)
1663 || (State->FpuStatus.Pe && !State->FpuControl.Pm))
1665 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_NE)
1667 /* Call the #MF handler */
1668 Fast486Exception(State, FAST486_EXCEPTION_MF);
1672 /* Use the external interrupt */
1673 State->FpuCallback(State);
1681 Fast486FpuNormalize(PFAST486_STATE State,
1682 PFAST486_FPU_DATA_REG Data)
1686 if (FPU_IS_ZERO(Data))
1692 if (FPU_IS_NORMALIZED(Data)) return TRUE;
1694 LeadingZeros = CountLeadingZeros64(Data->Mantissa);
1696 if (LeadingZeros < Data->Exponent)
1698 Data->Mantissa <<= LeadingZeros;
1699 Data->Exponent -= LeadingZeros;
1703 /* Raise the underflow exception */
1704 State->FpuStatus.Ue = TRUE;
1706 if (State->FpuControl.Um)
1708 /* Make it denormalized */
1709 Data->Mantissa <<= Data->Exponent - 1;
1724 Fast486FpuGetValueTag(PFAST486_FPU_DATA_REG Data)
1726 if (FPU_IS_ZERO(Data)) return FPU_TAG_ZERO;
1727 else if (FPU_IS_NAN(Data)) return FPU_TAG_SPECIAL;
1728 else return FPU_TAG_VALID;
1734 Fast486FpuPush(PFAST486_STATE State,
1735 PCFAST486_FPU_DATA_REG Data)
1737 State->FpuStatus.Top--;
1739 if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
1748 /* Raise the stack fault and invalid operation exception */
1749 State->FpuStatus.Sf = State->FpuStatus.Ie = TRUE;
1751 /* Set the C1 condition code bit (stack overflow) */
1752 State->FpuStatus.Code1 = TRUE;
1761 Fast486FpuPop(PFAST486_STATE State)
1763 if (FPU_GET_TAG(0) != FPU_TAG_EMPTY)
1765 FPU_SET_TAG(0, FPU_TAG_EMPTY);
1766 State->FpuStatus.Top++;
1772 /* Raise the stack fault and invalid operation exception */
1773 State->FpuStatus.Sf = State->FpuStatus.Ie = TRUE;
1775 /* Clear the C1 condition code bit (stack underflow) */
1776 State->FpuStatus.Code1 = FALSE;