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.
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;
143 /* Return the table entry */
144 return TableEntry.Value;
150 Fast486ReadLinearMemory(PFAST486_STATE State,
155 /* Check if paging is enabled */
156 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG)
159 FAST486_PAGE_TABLE TableEntry;
160 INT Cpl = Fast486GetCurrentPrivLevel(State);
161 ULONG BufferOffset = 0;
163 for (Page = PAGE_ALIGN(LinearAddress);
164 Page <= PAGE_ALIGN(LinearAddress + Size - 1);
165 Page += FAST486_PAGE_SIZE)
167 ULONG PageOffset = 0, PageLength = FAST486_PAGE_SIZE;
169 /* Get the table entry */
170 TableEntry.Value = Fast486GetPageTableEntry(State, Page, FALSE);
172 /* Check if this is the first page */
173 if (Page == PAGE_ALIGN(LinearAddress))
175 /* Start reading from the offset from the beginning of the page */
176 PageOffset = PAGE_OFFSET(LinearAddress);
177 PageLength -= PageOffset;
180 if (!TableEntry.Present || (!TableEntry.Usermode && (Cpl > 0)))
182 State->ControlRegisters[FAST486_REG_CR2] = Page + PageOffset;
185 Fast486ExceptionWithErrorCode(State,
186 FAST486_EXCEPTION_PF,
187 TableEntry.Present | (State->Cpl ? 0x04 : 0));
191 /* Check if this is the last page */
192 if (Page == PAGE_ALIGN(LinearAddress + Size - 1))
194 /* Read only a part of the page */
195 PageLength = PAGE_OFFSET(LinearAddress + Size - 1) - PageOffset + 1;
198 /* Read the memory */
199 State->MemReadCallback(State,
200 (TableEntry.Address << 12) | PageOffset,
201 (PVOID)((ULONG_PTR)Buffer + BufferOffset),
204 BufferOffset += PageLength;
209 /* Read the memory */
210 State->MemReadCallback(State, LinearAddress, Buffer, Size);
219 Fast486WriteLinearMemory(PFAST486_STATE State,
224 /* Check if paging is enabled */
225 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG)
228 FAST486_PAGE_TABLE TableEntry;
229 INT Cpl = Fast486GetCurrentPrivLevel(State);
230 ULONG BufferOffset = 0;
232 for (Page = PAGE_ALIGN(LinearAddress);
233 Page <= PAGE_ALIGN(LinearAddress + Size - 1);
234 Page += FAST486_PAGE_SIZE)
236 ULONG PageOffset = 0, PageLength = FAST486_PAGE_SIZE;
238 /* Get the table entry */
239 TableEntry.Value = Fast486GetPageTableEntry(State, Page, TRUE);
241 /* Check if this is the first page */
242 if (Page == PAGE_ALIGN(LinearAddress))
244 /* Start writing from the offset from the beginning of the page */
245 PageOffset = PAGE_OFFSET(LinearAddress);
246 PageLength -= PageOffset;
249 if ((!TableEntry.Present || (!TableEntry.Usermode && (Cpl > 0)))
250 || ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_WP)
251 && !TableEntry.Writeable))
253 State->ControlRegisters[FAST486_REG_CR2] = Page + PageOffset;
256 Fast486ExceptionWithErrorCode(State,
257 FAST486_EXCEPTION_PF,
258 TableEntry.Present | 0x02 | (State->Cpl ? 0x04 : 0));
262 /* Check if this is the last page */
263 if (Page == PAGE_ALIGN(LinearAddress + Size - 1))
265 /* Write only a part of the page */
266 PageLength = PAGE_OFFSET(LinearAddress + Size - 1) - PageOffset + 1;
269 /* Write the memory */
270 State->MemWriteCallback(State,
271 (TableEntry.Address << 12) | PageOffset,
272 (PVOID)((ULONG_PTR)Buffer + BufferOffset),
275 BufferOffset += PageLength;
280 /* Write the memory */
281 State->MemWriteCallback(State, LinearAddress, Buffer, Size);
290 Fast486Exception(PFAST486_STATE State,
291 FAST486_EXCEPTIONS ExceptionCode)
293 /* Call the internal function */
294 Fast486ExceptionWithErrorCode(State, ExceptionCode, 0);
300 Fast486StackPush(PFAST486_STATE State,
303 BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;
304 ULONG StackPointer = State->GeneralRegs[FAST486_REG_ESP].Long;
306 /* The OPSIZE prefix toggles the size */
313 /* Check if ESP is between 1 and 3 */
314 if (State->GeneralRegs[FAST486_REG_ESP].Long >= 1
315 && State->GeneralRegs[FAST486_REG_ESP].Long <= 3)
317 Fast486Exception(State, FAST486_EXCEPTION_SS);
321 /* Store the value in SS:[ESP - 4] */
322 if (!Fast486WriteMemory(State,
324 State->SegmentRegs[FAST486_REG_SS].Size
325 ? StackPointer - sizeof(ULONG)
326 : LOWORD(StackPointer - sizeof(ULONG)),
330 /* Exception occurred */
334 if (State->SegmentRegs[FAST486_REG_SS].Size)
336 /* Subtract ESP by 4 */
337 State->GeneralRegs[FAST486_REG_ESP].Long -= sizeof(ULONG);
341 /* Subtract SP by 4 */
342 State->GeneralRegs[FAST486_REG_ESP].LowWord -= sizeof(ULONG);
348 USHORT ShortValue = LOWORD(Value);
350 /* Check if SP is 1 */
351 if (State->GeneralRegs[FAST486_REG_ESP].LowWord == 1)
353 Fast486Exception(State, FAST486_EXCEPTION_SS);
357 /* Store the value in SS:[SP - 2] */
358 if (!Fast486WriteMemory(State,
360 State->SegmentRegs[FAST486_REG_SS].Size
361 ? StackPointer - sizeof(USHORT)
362 : LOWORD(StackPointer - sizeof(USHORT)),
366 /* Exception occurred */
370 if (State->SegmentRegs[FAST486_REG_SS].Size)
372 /* Subtract ESP by 2 */
373 State->GeneralRegs[FAST486_REG_ESP].Long -= sizeof(USHORT);
377 /* Subtract SP by 2 */
378 State->GeneralRegs[FAST486_REG_ESP].LowWord -= sizeof(USHORT);
388 Fast486StackPop(PFAST486_STATE State,
391 BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size;
393 /* The OPSIZE prefix toggles the size */
401 /* Check if ESP is 0xFFFFFFFF */
402 if (State->GeneralRegs[FAST486_REG_ESP].Long == 0xFFFFFFFF)
404 Fast486Exception(State, FAST486_EXCEPTION_SS);
408 /* Read the value from SS:ESP */
409 if (!Fast486ReadMemory(State,
411 State->GeneralRegs[FAST486_REG_ESP].Long,
416 /* An exception occurred */
420 /* Increment ESP by 4 */
421 State->GeneralRegs[FAST486_REG_ESP].Long += sizeof(ULONG);
423 /* Store the value in the result */
431 /* Check if SP is 0xFFFF */
432 if (State->GeneralRegs[FAST486_REG_ESP].LowWord == 0xFFFF)
434 Fast486Exception(State, FAST486_EXCEPTION_SS);
438 /* Read the value from SS:SP */
439 if (!Fast486ReadMemory(State,
441 State->GeneralRegs[FAST486_REG_ESP].LowWord,
446 /* An exception occurred */
450 /* Increment SP by 2 */
451 State->GeneralRegs[FAST486_REG_ESP].LowWord += sizeof(USHORT);
453 /* Store the value in the result */
463 Fast486ReadDescriptorEntry(PFAST486_STATE State,
466 PFAST486_GDT_ENTRY Entry)
468 if (!(Selector & SEGMENT_TABLE_INDICATOR))
470 /* Make sure the GDT contains the entry */
471 if (GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1u))
478 if (!Fast486ReadLinearMemory(State,
480 + GET_SEGMENT_INDEX(Selector),
484 /* Exception occurred */
491 /* Make sure the LDT contains the entry */
492 if (GET_SEGMENT_INDEX(Selector) >= (State->Ldtr.Limit + 1u))
499 if (!Fast486ReadLinearMemory(State,
501 + GET_SEGMENT_INDEX(Selector),
505 /* Exception occurred */
518 Fast486LoadSegmentInternal(PFAST486_STATE State,
519 FAST486_SEG_REGS Segment,
521 FAST486_EXCEPTIONS Exception)
523 PFAST486_SEG_REG CachedDescriptor;
525 FAST486_GDT_ENTRY GdtEntry;
527 ASSERT(Segment < FAST486_NUM_SEG_REGS);
529 /* Get the cached descriptor */
530 CachedDescriptor = &State->SegmentRegs[Segment];
532 /* Check for protected mode */
533 if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) && !State->Flags.Vm)
535 if (!Fast486ReadDescriptorEntry(State, Selector, &Valid, &GdtEntry))
537 /* Exception occurred */
543 /* Invalid selector */
544 Fast486ExceptionWithErrorCode(State, Exception, Selector);
548 if (Segment == FAST486_REG_SS)
550 /* Loading the stack segment */
552 if (GET_SEGMENT_INDEX(Selector) == 0)
554 Fast486Exception(State, Exception);
558 if (!GdtEntry.SystemType)
560 /* This is a special descriptor */
561 Fast486ExceptionWithErrorCode(State, Exception, Selector);
565 if (GdtEntry.Executable || !GdtEntry.ReadWrite)
567 Fast486ExceptionWithErrorCode(State, Exception, Selector);
571 if ((GET_SEGMENT_RPL(Selector) != Fast486GetCurrentPrivLevel(State))
572 || (GET_SEGMENT_RPL(Selector) != GdtEntry.Dpl))
574 Fast486ExceptionWithErrorCode(State, Exception, Selector);
578 if (!GdtEntry.Present)
580 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_SS, Selector);
584 else if (Segment == FAST486_REG_CS)
586 /* Loading the code segment */
588 #ifndef FAST486_NO_PREFETCH
589 /* Invalidate the prefetch */
590 State->PrefetchValid = FALSE;
593 if (GET_SEGMENT_INDEX(Selector) == 0)
595 Fast486Exception(State, Exception);
599 if (!GdtEntry.SystemType)
601 /* Must be a segment descriptor */
602 Fast486ExceptionWithErrorCode(State, Exception, Selector);
606 if (!GdtEntry.Present)
608 Fast486ExceptionWithErrorCode(State, Exception, Selector);
612 if (!GdtEntry.Executable)
614 Fast486ExceptionWithErrorCode(State, Exception, Selector);
618 if (GdtEntry.DirConf)
620 /* Conforming Code Segment */
622 if (GdtEntry.Dpl > Fast486GetCurrentPrivLevel(State))
624 /* Must be accessed from lower-privileged code */
625 Fast486ExceptionWithErrorCode(State, Exception, Selector);
631 /* Regular code segment */
633 if ((GET_SEGMENT_RPL(Selector) < Fast486GetCurrentPrivLevel(State)))
635 Fast486ExceptionWithErrorCode(State, Exception, Selector);
642 /* Loading a data segment */
644 if (GET_SEGMENT_INDEX(Selector) != 0)
646 if (!GdtEntry.SystemType)
648 /* This is a special descriptor */
649 Fast486ExceptionWithErrorCode(State, Exception, Selector);
653 if ((GET_SEGMENT_RPL(Selector) > GdtEntry.Dpl)
654 || (Fast486GetCurrentPrivLevel(State) > GdtEntry.Dpl))
656 Fast486ExceptionWithErrorCode(State, Exception, Selector);
660 if (!GdtEntry.Present)
662 Fast486ExceptionWithErrorCode(State, Exception, Selector);
668 /* This is a NULL selector */
669 RtlZeroMemory(&GdtEntry, sizeof(GdtEntry));
673 /* Update the cache entry */
674 CachedDescriptor->Selector = Selector;
675 CachedDescriptor->Base = GdtEntry.Base | (GdtEntry.BaseMid << 16) | (GdtEntry.BaseHigh << 24);
676 CachedDescriptor->Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16);
677 CachedDescriptor->Accessed = GdtEntry.Accessed;
678 CachedDescriptor->ReadWrite = GdtEntry.ReadWrite;
679 CachedDescriptor->DirConf = GdtEntry.DirConf;
680 CachedDescriptor->Executable = GdtEntry.Executable;
681 CachedDescriptor->SystemType = GdtEntry.SystemType;
682 CachedDescriptor->Rpl = GET_SEGMENT_RPL(Selector);
683 CachedDescriptor->Dpl = GdtEntry.Dpl;
684 CachedDescriptor->Present = GdtEntry.Present;
685 CachedDescriptor->Size = GdtEntry.Size;
687 /* Check for page granularity */
688 if (GdtEntry.Granularity)
690 CachedDescriptor->Limit <<= 12;
691 CachedDescriptor->Limit |= 0x00000FFF;
696 /* Update the selector and base */
697 CachedDescriptor->Selector = Selector;
698 CachedDescriptor->Base = Selector << 4;
707 Fast486LoadSegment(PFAST486_STATE State,
708 FAST486_SEG_REGS Segment,
711 return Fast486LoadSegmentInternal(State,
714 FAST486_EXCEPTION_GP);
720 Fast486ProcessGate(PFAST486_STATE State, USHORT Selector, ULONG Offset, BOOLEAN Call)
723 FAST486_SYSTEM_DESCRIPTOR Descriptor;
725 if (!Fast486ReadDescriptorEntry(State,
728 (PFAST486_GDT_ENTRY)&Descriptor))
730 /* Exception occurred */
736 /* Invalid selector */
737 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
741 switch (Descriptor.Signature)
743 case FAST486_TASK_GATE_SIGNATURE:
745 Fast486TaskSwitch(State,
746 Call ? FAST486_TASK_CALL : FAST486_TASK_JUMP,
747 ((PFAST486_IDT_ENTRY)&Descriptor)->Selector);
752 case FAST486_TSS_SIGNATURE:
754 Fast486TaskSwitch(State,
755 Call ? FAST486_TASK_CALL : FAST486_TASK_JUMP,
761 case FAST486_CALL_GATE_SIGNATURE:
763 // TODO: NOT IMPLEMENTED
769 /* Security check for jumps and calls only */
770 if (State->Cpl != Descriptor.Dpl)
772 Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
784 Fast486FetchByte(PFAST486_STATE State,
787 PFAST486_SEG_REG CachedDescriptor;
789 #ifndef FAST486_NO_PREFETCH
793 /* Get the cached descriptor of CS */
794 CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS];
796 Offset = (CachedDescriptor->Size) ? State->InstPtr.Long
797 : State->InstPtr.LowWord;
798 #ifndef FAST486_NO_PREFETCH
799 LinearAddress = CachedDescriptor->Base + Offset;
801 if (State->PrefetchValid
802 && (LinearAddress >= State->PrefetchAddress)
803 && ((LinearAddress + sizeof(UCHAR)) <= (State->PrefetchAddress + FAST486_CACHE_SIZE)))
805 *Data = *(PUCHAR)&State->PrefetchCache[LinearAddress - State->PrefetchAddress];
810 /* Read from memory */
811 if (!Fast486ReadMemory(State,
818 /* Exception occurred during instruction fetch */
823 /* Advance the instruction pointer */
824 if (CachedDescriptor->Size) State->InstPtr.Long++;
825 else State->InstPtr.LowWord++;
833 Fast486FetchWord(PFAST486_STATE State,
836 PFAST486_SEG_REG CachedDescriptor;
838 #ifndef FAST486_NO_PREFETCH
842 /* Get the cached descriptor of CS */
843 CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS];
845 Offset = (CachedDescriptor->Size) ? State->InstPtr.Long
846 : State->InstPtr.LowWord;
848 #ifndef FAST486_NO_PREFETCH
849 LinearAddress = CachedDescriptor->Base + Offset;
851 if (State->PrefetchValid
852 && (LinearAddress >= State->PrefetchAddress)
853 && ((LinearAddress + sizeof(USHORT)) <= (State->PrefetchAddress + FAST486_CACHE_SIZE)))
855 *Data = *(PUSHORT)&State->PrefetchCache[LinearAddress - State->PrefetchAddress];
860 /* Read from memory */
861 // FIXME: Fix byte order on big-endian machines
862 if (!Fast486ReadMemory(State,
869 /* Exception occurred during instruction fetch */
874 /* Advance the instruction pointer */
875 if (CachedDescriptor->Size) State->InstPtr.Long += sizeof(USHORT);
876 else State->InstPtr.LowWord += sizeof(USHORT);
884 Fast486FetchDword(PFAST486_STATE State,
887 PFAST486_SEG_REG CachedDescriptor;
889 #ifndef FAST486_NO_PREFETCH
893 /* Get the cached descriptor of CS */
894 CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS];
896 Offset = (CachedDescriptor->Size) ? State->InstPtr.Long
897 : State->InstPtr.LowWord;
899 #ifndef FAST486_NO_PREFETCH
900 LinearAddress = CachedDescriptor->Base + Offset;
902 if (State->PrefetchValid
903 && (LinearAddress >= State->PrefetchAddress)
904 && ((LinearAddress + sizeof(ULONG)) <= (State->PrefetchAddress + FAST486_CACHE_SIZE)))
906 *Data = *(PULONG)&State->PrefetchCache[LinearAddress - State->PrefetchAddress];
911 /* Read from memory */
912 // FIXME: Fix byte order on big-endian machines
913 if (!Fast486ReadMemory(State,
920 /* Exception occurred during instruction fetch */
925 /* Advance the instruction pointer */
926 if (CachedDescriptor->Size) State->InstPtr.Long += sizeof(ULONG);
927 else State->InstPtr.LowWord += sizeof(ULONG);
935 Fast486CalculateParity(UCHAR Number)
937 // See http://graphics.stanford.edu/~seander/bithacks.html#ParityLookupTable too...
938 return (0x9669 >> ((Number & 0x0F) ^ (Number >> 4))) & 1;
944 Fast486ParseModRegRm(PFAST486_STATE State,
946 PFAST486_MOD_REG_RM ModRegRm)
948 UCHAR ModRmByte, Mode, RegMem;
950 /* Fetch the MOD REG R/M byte */
951 if (!Fast486FetchByte(State, &ModRmByte))
953 /* Exception occurred */
957 /* Unpack the mode and R/M */
958 Mode = ModRmByte >> 6;
959 RegMem = ModRmByte & 0x07;
961 /* Set the register operand */
962 ModRegRm->Register = (ModRmByte >> 3) & 0x07;
967 /* The second operand is also a register */
968 ModRegRm->Memory = FALSE;
969 ModRegRm->SecondRegister = RegMem;
975 /* The second operand is memory */
976 ModRegRm->Memory = TRUE;
980 if (RegMem == FAST486_REG_ESP)
983 ULONG Scale, Index, Base;
985 /* Fetch the SIB byte */
986 if (!Fast486FetchByte(State, &SibByte))
988 /* Exception occurred */
992 /* Unpack the scale, index and base */
993 Scale = 1 << (SibByte >> 6);
994 Index = (SibByte >> 3) & 0x07;
995 if (Index != FAST486_REG_ESP) Index = State->GeneralRegs[Index].Long;
998 if (((SibByte & 0x07) != FAST486_REG_EBP) || (Mode != 0))
1000 /* Use the register a base */
1001 Base = State->GeneralRegs[SibByte & 0x07].Long;
1005 /* Fetch the base */
1006 if (!Fast486FetchDword(State, &Base))
1008 /* Exception occurred */
1013 if ((SibByte & 0x07) == FAST486_REG_ESP)
1015 /* Check if there is no segment override */
1016 if (!(State->PrefixFlags & FAST486_PREFIX_SEG))
1018 /* Add a SS: prefix */
1019 State->PrefixFlags |= FAST486_PREFIX_SEG;
1020 State->SegmentOverride = FAST486_REG_SS;
1024 /* Calculate the address */
1025 ModRegRm->MemoryAddress = Base + Index * Scale;
1027 else if (RegMem == FAST486_REG_EBP)
1029 if (Mode) ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].Long;
1030 else ModRegRm->MemoryAddress = 0;
1034 /* Get the base from the register */
1035 ModRegRm->MemoryAddress = State->GeneralRegs[RegMem].Long;
1038 /* Check if there is no segment override */
1039 if (!(State->PrefixFlags & FAST486_PREFIX_SEG))
1041 /* Check if the default segment should be SS */
1042 if ((RegMem == FAST486_REG_EBP) && Mode)
1044 /* Add a SS: prefix */
1045 State->PrefixFlags |= FAST486_PREFIX_SEG;
1046 State->SegmentOverride = FAST486_REG_SS;
1054 /* Fetch the byte */
1055 if (!Fast486FetchByte(State, (PUCHAR)&Offset))
1057 /* Exception occurred */
1061 /* Add the signed offset to the address */
1062 ModRegRm->MemoryAddress += (LONG)Offset;
1064 else if ((Mode == 2) || ((Mode == 0) && (RegMem == FAST486_REG_EBP)))
1068 /* Fetch the dword */
1069 if (!Fast486FetchDword(State, (PULONG)&Offset))
1071 /* Exception occurred */
1075 /* Add the signed offset to the address */
1076 ModRegRm->MemoryAddress += Offset;
1081 /* Check the operand */
1087 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord
1088 + State->GeneralRegs[FAST486_REG_ESI].LowWord;
1095 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord
1096 + State->GeneralRegs[FAST486_REG_EDI].LowWord;
1103 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord
1104 + State->GeneralRegs[FAST486_REG_ESI].LowWord;
1111 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord
1112 + State->GeneralRegs[FAST486_REG_EDI].LowWord;
1119 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_ESI].LowWord;
1126 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EDI].LowWord;
1135 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord;
1139 /* [constant] (added later) */
1140 ModRegRm->MemoryAddress = 0;
1149 ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord;
1154 /* Check if there is no segment override */
1155 if (!(State->PrefixFlags & FAST486_PREFIX_SEG))
1157 /* Check if the default segment should be SS */
1158 if ((RegMem == 2) || (RegMem == 3) || ((RegMem == 6) && Mode))
1160 /* Add a SS: prefix */
1161 State->PrefixFlags |= FAST486_PREFIX_SEG;
1162 State->SegmentOverride = FAST486_REG_SS;
1170 /* Fetch the byte */
1171 if (!Fast486FetchByte(State, (PUCHAR)&Offset))
1173 /* Exception occurred */
1177 /* Add the signed offset to the address */
1178 ModRegRm->MemoryAddress += (LONG)Offset;
1180 else if ((Mode == 2) || ((Mode == 0) && (RegMem == 6)))
1184 /* Fetch the word */
1185 if (!Fast486FetchWord(State, (PUSHORT)&Offset))
1187 /* Exception occurred */
1191 /* Add the signed offset to the address */
1192 ModRegRm->MemoryAddress += (LONG)Offset;
1195 /* Clear the top 16 bits */
1196 ModRegRm->MemoryAddress &= 0x0000FFFF;
1205 Fast486ReadModrmByteOperands(PFAST486_STATE State,
1206 PFAST486_MOD_REG_RM ModRegRm,
1210 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1214 /* Get the register value */
1215 if (ModRegRm->Register & 0x04)
1217 /* AH, CH, DH, BH */
1218 *RegValue = State->GeneralRegs[ModRegRm->Register & 0x03].HighByte;
1222 /* AL, CL, DL, BL */
1223 *RegValue = State->GeneralRegs[ModRegRm->Register & 0x03].LowByte;
1229 if (!ModRegRm->Memory)
1231 /* Get the second register value */
1232 if (ModRegRm->SecondRegister & 0x04)
1234 /* AH, CH, DH, BH */
1235 *RmValue = State->GeneralRegs[ModRegRm->SecondRegister & 0x03].HighByte;
1239 /* AL, CL, DL, BL */
1240 *RmValue = State->GeneralRegs[ModRegRm->SecondRegister & 0x03].LowByte;
1245 /* Check for the segment override */
1246 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1248 /* Use the override segment instead */
1249 Segment = State->SegmentOverride;
1253 if (!Fast486ReadMemory(State,
1255 ModRegRm->MemoryAddress,
1260 /* Exception occurred */
1272 Fast486ReadModrmWordOperands(PFAST486_STATE State,
1273 PFAST486_MOD_REG_RM ModRegRm,
1277 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1281 /* Get the register value */
1282 *RegValue = State->GeneralRegs[ModRegRm->Register].LowWord;
1287 if (!ModRegRm->Memory)
1289 /* Get the second register value */
1290 *RmValue = State->GeneralRegs[ModRegRm->SecondRegister].LowWord;
1294 /* Check for the segment override */
1295 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1297 /* Use the override segment instead */
1298 Segment = State->SegmentOverride;
1302 if (!Fast486ReadMemory(State,
1304 ModRegRm->MemoryAddress,
1309 /* Exception occurred */
1321 Fast486ReadModrmDwordOperands(PFAST486_STATE State,
1322 PFAST486_MOD_REG_RM ModRegRm,
1326 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1330 /* Get the register value */
1331 *RegValue = State->GeneralRegs[ModRegRm->Register].Long;
1336 if (!ModRegRm->Memory)
1338 /* Get the second register value */
1339 *RmValue = State->GeneralRegs[ModRegRm->SecondRegister].Long;
1343 /* Check for the segment override */
1344 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1346 /* Use the override segment instead */
1347 Segment = State->SegmentOverride;
1351 if (!Fast486ReadMemory(State,
1353 ModRegRm->MemoryAddress,
1358 /* Exception occurred */
1370 Fast486WriteModrmByteOperands(PFAST486_STATE State,
1371 PFAST486_MOD_REG_RM ModRegRm,
1372 BOOLEAN WriteRegister,
1375 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1379 /* Store the value in the register */
1380 if (ModRegRm->Register & 0x04)
1382 /* AH, CH, DH, BH */
1383 State->GeneralRegs[ModRegRm->Register & 0x03].HighByte = Value;
1387 /* AL, CL, DL, BL */
1388 State->GeneralRegs[ModRegRm->Register & 0x03].LowByte = Value;
1393 if (!ModRegRm->Memory)
1395 /* Store the value in the second register */
1396 if (ModRegRm->SecondRegister & 0x04)
1398 /* AH, CH, DH, BH */
1399 State->GeneralRegs[ModRegRm->SecondRegister & 0x03].HighByte = Value;
1403 /* AL, CL, DL, BL */
1404 State->GeneralRegs[ModRegRm->SecondRegister & 0x03].LowByte = Value;
1409 /* Check for the segment override */
1410 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1412 /* Use the override segment instead */
1413 Segment = State->SegmentOverride;
1417 if (!Fast486WriteMemory(State,
1419 ModRegRm->MemoryAddress,
1423 /* Exception occurred */
1435 Fast486WriteModrmWordOperands(PFAST486_STATE State,
1436 PFAST486_MOD_REG_RM ModRegRm,
1437 BOOLEAN WriteRegister,
1440 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1444 /* Store the value in the register */
1445 State->GeneralRegs[ModRegRm->Register].LowWord = Value;
1449 if (!ModRegRm->Memory)
1451 /* Store the value in the second register */
1452 State->GeneralRegs[ModRegRm->SecondRegister].LowWord = Value;
1456 /* Check for the segment override */
1457 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1459 /* Use the override segment instead */
1460 Segment = State->SegmentOverride;
1464 if (!Fast486WriteMemory(State,
1466 ModRegRm->MemoryAddress,
1470 /* Exception occurred */
1482 Fast486WriteModrmDwordOperands(PFAST486_STATE State,
1483 PFAST486_MOD_REG_RM ModRegRm,
1484 BOOLEAN WriteRegister,
1487 FAST486_SEG_REGS Segment = FAST486_REG_DS;
1491 /* Store the value in the register */
1492 State->GeneralRegs[ModRegRm->Register].Long = Value;
1496 if (!ModRegRm->Memory)
1498 /* Store the value in the second register */
1499 State->GeneralRegs[ModRegRm->SecondRegister].Long = Value;
1503 /* Check for the segment override */
1504 if (State->PrefixFlags & FAST486_PREFIX_SEG)
1506 /* Use the override segment instead */
1507 Segment = State->SegmentOverride;
1511 if (!Fast486WriteMemory(State,
1513 ModRegRm->MemoryAddress,
1517 /* Exception occurred */
1526 #ifndef FAST486_NO_FPU
1531 Fast486FpuException(PFAST486_STATE State)
1533 if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_NE)
1535 /* Call the #MF handler */
1536 Fast486Exception(State, FAST486_EXCEPTION_MF);
1540 /* Use the external interrupt */
1541 State->FpuCallback(State);
1548 Fast486FpuNormalize(PFAST486_STATE State,
1549 PFAST486_FPU_DATA_REG Data)
1553 if (FPU_IS_NORMALIZED(Data)) return TRUE;
1554 if (FPU_IS_ZERO(Data))
1560 LeadingZeros = CountLeadingZeros64(Data->Mantissa);
1562 if (LeadingZeros < Data->Exponent)
1564 Data->Mantissa <<= LeadingZeros;
1565 Data->Exponent -= LeadingZeros;
1569 /* Raise the underflow exception */
1570 State->FpuStatus.Ue = TRUE;
1572 if (State->FpuControl.Um)
1574 /* Make it denormalized */
1575 Data->Mantissa <<= Data->Exponent - 1;
1580 Fast486FpuException(State);
1591 Fast486FpuGetValueTag(PFAST486_FPU_DATA_REG Data)
1593 if (FPU_IS_ZERO(Data)) return FPU_TAG_ZERO;
1594 else if (FPU_IS_NAN(Data)) return FPU_TAG_SPECIAL;
1595 else return FPU_TAG_VALID;
1601 Fast486FpuPush(PFAST486_STATE State,
1602 PCFAST486_FPU_DATA_REG Data)
1604 State->FpuStatus.Top--;
1606 if (FPU_GET_TAG(0) == FPU_TAG_EMPTY)
1615 /* Raise the stack fault and invalid operation exception */
1616 State->FpuStatus.Sf = State->FpuStatus.Ie = TRUE;
1618 /* Set the C1 condition code bit (stack overflow) */
1619 State->FpuStatus.Code1 = TRUE;
1621 if (!State->FpuControl.Im) Fast486FpuException(State);
1629 Fast486FpuPop(PFAST486_STATE State)
1631 if (FPU_GET_TAG(0) != FPU_TAG_EMPTY)
1633 FPU_SET_TAG(0, FPU_TAG_EMPTY);
1634 State->FpuStatus.Top++;
1640 /* Raise the stack fault and invalid operation exception */
1641 State->FpuStatus.Sf = State->FpuStatus.Ie = TRUE;
1643 /* Clear the C1 condition code bit (stack underflow) */
1644 State->FpuStatus.Code1 = FALSE;
1646 if (!State->FpuControl.Im) Fast486FpuException(State);