/* * Fast486 386/486 CPU Emulation Library * common.inl * * Copyright (C) 2015 Aleksandar Andrejevic * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "common.h" #include "fpu.h" /* PUBLIC FUNCTIONS ***********************************************************/ #if defined (__GNUC__) #define CountLeadingZeros64(x) __builtin_clzll(x) /* #elif (_MSC_VER >= 1500) && defined(_WIN64) #define CountLeadingZeros64(x) __lzcnt64(x) #elif (_MSC_VER >= 1500) #define CountLeadingZeros64(x) ((x) > 0xFFFFFFFFULL) ? __lzcnt((x) >> 32) \ : (__lzcnt(x) + 32) */ #else FORCEINLINE ULONG CountLeadingZeros64(ULONGLONG Value) { ULONG Count = 0; Value = ~Value; while ((LONGLONG)Value < 0) { Count++; Value <<= 1; } return Count; } #endif FORCEINLINE UINT FASTCALL Fast486GetCurrentPrivLevel(PFAST486_STATE State) { /* Return the CPL, or 3 if we're in virtual 8086 mode */ return (!State->Flags.Vm) ? State->Cpl : 3; } FORCEINLINE ULONG FASTCALL Fast486GetPageTableEntry(PFAST486_STATE State, ULONG VirtualAddress, BOOLEAN MarkAsDirty) { ULONG PdeIndex = GET_ADDR_PDE(VirtualAddress); ULONG PteIndex = GET_ADDR_PTE(VirtualAddress); FAST486_PAGE_DIR DirectoryEntry; FAST486_PAGE_TABLE TableEntry; ULONG PageDirectory = State->ControlRegisters[FAST486_REG_CR3]; if ((State->Tlb != NULL) && (State->Tlb[VirtualAddress >> 12] != INVALID_TLB_FIELD)) { /* Return the cached entry */ return State->Tlb[VirtualAddress >> 12]; } /* Read the directory entry */ State->MemReadCallback(State, PageDirectory + PdeIndex * sizeof(ULONG), &DirectoryEntry.Value, sizeof(DirectoryEntry)); /* Make sure it is present */ if (!DirectoryEntry.Present) return 0; /* Was the directory entry accessed before? */ if (!DirectoryEntry.Accessed) { /* Well, it is now */ DirectoryEntry.Accessed = TRUE; /* Write back the directory entry */ State->MemWriteCallback(State, PageDirectory + PdeIndex * sizeof(ULONG), &DirectoryEntry.Value, sizeof(DirectoryEntry)); } /* Read the table entry */ State->MemReadCallback(State, (DirectoryEntry.TableAddress << 12) + PteIndex * sizeof(ULONG), &TableEntry.Value, sizeof(TableEntry)); /* Make sure it is present */ if (!TableEntry.Present) return 0; /* Do we need to change any flags? */ if (!TableEntry.Accessed || (MarkAsDirty && !TableEntry.Dirty)) { /* Mark it as accessed and optionally dirty too */ TableEntry.Accessed = TRUE; if (MarkAsDirty) TableEntry.Dirty = TRUE; /* Write back the table entry */ State->MemWriteCallback(State, (DirectoryEntry.TableAddress << 12) + PteIndex * sizeof(ULONG), &TableEntry.Value, sizeof(TableEntry)); } /* * The resulting permissions depend on the permissions * in the page directory table too */ TableEntry.Writeable &= DirectoryEntry.Writeable; TableEntry.Usermode &= DirectoryEntry.Usermode; if (State->Tlb != NULL) { /* Set the TLB entry */ State->Tlb[VirtualAddress >> 12] = TableEntry.Value; State->TlbEmpty = FALSE; } /* Return the table entry */ return TableEntry.Value; } FORCEINLINE VOID FASTCALL Fast486FlushTlb(PFAST486_STATE State) { if (!State->Tlb || State->TlbEmpty) return; RtlFillMemory(State->Tlb, NUM_TLB_ENTRIES * sizeof(ULONG), 0xFF); State->TlbEmpty = TRUE; } FORCEINLINE BOOLEAN FASTCALL Fast486ReadLinearMemory(PFAST486_STATE State, ULONG LinearAddress, PVOID Buffer, ULONG Size, BOOLEAN CheckPrivilege) { /* Check if paging is enabled */ if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG) { ULONG Page; FAST486_PAGE_TABLE TableEntry; INT Cpl = Fast486GetCurrentPrivLevel(State); ULONG BufferOffset = 0; for (Page = PAGE_ALIGN(LinearAddress); Page <= PAGE_ALIGN(LinearAddress + Size - 1); Page += FAST486_PAGE_SIZE) { ULONG PageOffset = 0, PageLength = FAST486_PAGE_SIZE; /* Get the table entry */ TableEntry.Value = Fast486GetPageTableEntry(State, Page, FALSE); /* Check if this is the first page */ if (Page == PAGE_ALIGN(LinearAddress)) { /* Start reading from the offset from the beginning of the page */ PageOffset = PAGE_OFFSET(LinearAddress); PageLength -= PageOffset; } if (CheckPrivilege && (!TableEntry.Present || (!TableEntry.Usermode && (Cpl > 0)))) { State->ControlRegisters[FAST486_REG_CR2] = Page + PageOffset; /* Exception */ Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_PF, TableEntry.Present | (State->Cpl ? 0x04 : 0)); return FALSE; } /* Check if this is the last page */ if (Page == PAGE_ALIGN(LinearAddress + Size - 1)) { /* Read only a part of the page */ PageLength = PAGE_OFFSET(LinearAddress + Size - 1) - PageOffset + 1; } /* Read the memory */ State->MemReadCallback(State, (TableEntry.Address << 12) | PageOffset, (PVOID)((ULONG_PTR)Buffer + BufferOffset), PageLength); BufferOffset += PageLength; } } else { /* Read the memory */ State->MemReadCallback(State, LinearAddress, Buffer, Size); } return TRUE; } FORCEINLINE BOOLEAN FASTCALL Fast486WriteLinearMemory(PFAST486_STATE State, ULONG LinearAddress, PVOID Buffer, ULONG Size, BOOLEAN CheckPrivilege) { /* Check if paging is enabled */ if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG) { ULONG Page; FAST486_PAGE_TABLE TableEntry; INT Cpl = Fast486GetCurrentPrivLevel(State); ULONG BufferOffset = 0; for (Page = PAGE_ALIGN(LinearAddress); Page <= PAGE_ALIGN(LinearAddress + Size - 1); Page += FAST486_PAGE_SIZE) { ULONG PageOffset = 0, PageLength = FAST486_PAGE_SIZE; /* Get the table entry */ TableEntry.Value = Fast486GetPageTableEntry(State, Page, TRUE); /* Check if this is the first page */ if (Page == PAGE_ALIGN(LinearAddress)) { /* Start writing from the offset from the beginning of the page */ PageOffset = PAGE_OFFSET(LinearAddress); PageLength -= PageOffset; } if (CheckPrivilege && ((!TableEntry.Present || (!TableEntry.Usermode && (Cpl > 0))) || ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_WP) && !TableEntry.Writeable))) { State->ControlRegisters[FAST486_REG_CR2] = Page + PageOffset; /* Exception */ Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_PF, TableEntry.Present | 0x02 | (State->Cpl ? 0x04 : 0)); return FALSE; } /* Check if this is the last page */ if (Page == PAGE_ALIGN(LinearAddress + Size - 1)) { /* Write only a part of the page */ PageLength = PAGE_OFFSET(LinearAddress + Size - 1) - PageOffset + 1; } /* Write the memory */ State->MemWriteCallback(State, (TableEntry.Address << 12) | PageOffset, (PVOID)((ULONG_PTR)Buffer + BufferOffset), PageLength); BufferOffset += PageLength; } } else { /* Write the memory */ State->MemWriteCallback(State, LinearAddress, Buffer, Size); } return TRUE; } FORCEINLINE VOID FASTCALL Fast486Exception(PFAST486_STATE State, FAST486_EXCEPTIONS ExceptionCode) { /* Call the internal function */ Fast486ExceptionWithErrorCode(State, ExceptionCode, 0); } FORCEINLINE BOOLEAN FASTCALL Fast486StackPushInternal(PFAST486_STATE State, BOOLEAN Size, ULONG Value) { ULONG StackPointer = State->GeneralRegs[FAST486_REG_ESP].Long; if (Size) { /* Check if ESP is between 1 and 3 */ if (State->GeneralRegs[FAST486_REG_ESP].Long >= 1 && State->GeneralRegs[FAST486_REG_ESP].Long <= 3) { Fast486Exception(State, FAST486_EXCEPTION_SS); return FALSE; } /* Store the value in SS:[ESP - 4] */ if (!Fast486WriteMemory(State, FAST486_REG_SS, State->SegmentRegs[FAST486_REG_SS].Size ? StackPointer - sizeof(ULONG) : LOWORD(StackPointer - sizeof(ULONG)), &Value, sizeof(ULONG))) { /* Exception occurred */ return FALSE; } if (State->SegmentRegs[FAST486_REG_SS].Size) { /* Subtract ESP by 4 */ State->GeneralRegs[FAST486_REG_ESP].Long -= sizeof(ULONG); } else { /* Subtract SP by 4 */ State->GeneralRegs[FAST486_REG_ESP].LowWord -= sizeof(ULONG); } } else { /* Check if SP is 1 */ if (State->GeneralRegs[FAST486_REG_ESP].LowWord == 1) { Fast486Exception(State, FAST486_EXCEPTION_SS); return FALSE; } /* Store the value in SS:[SP - 2] */ if (!Fast486WriteMemory(State, FAST486_REG_SS, State->SegmentRegs[FAST486_REG_SS].Size ? StackPointer - sizeof(USHORT) : LOWORD(StackPointer - sizeof(USHORT)), &Value, sizeof(USHORT))) { /* Exception occurred */ return FALSE; } if (State->SegmentRegs[FAST486_REG_SS].Size) { /* Subtract ESP by 2 */ State->GeneralRegs[FAST486_REG_ESP].Long -= sizeof(USHORT); } else { /* Subtract SP by 2 */ State->GeneralRegs[FAST486_REG_ESP].LowWord -= sizeof(USHORT); } } return TRUE; } FORCEINLINE BOOLEAN FASTCALL Fast486StackPush(PFAST486_STATE State, ULONG Value) { BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; /* The OPSIZE prefix toggles the size */ TOGGLE_OPSIZE(Size); /* Call the internal function */ return Fast486StackPushInternal(State, Size, Value); } FORCEINLINE BOOLEAN FASTCALL Fast486StackPop(PFAST486_STATE State, PULONG Value) { BOOLEAN Size = State->SegmentRegs[FAST486_REG_CS].Size; /* The OPSIZE prefix toggles the size */ TOGGLE_OPSIZE(Size); if (Size) { /* 32-bit size */ ULONG LongValue; /* Check if ESP is 0xFFFFFFFF */ if (State->GeneralRegs[FAST486_REG_ESP].Long == 0xFFFFFFFF) { Fast486Exception(State, FAST486_EXCEPTION_SS); return FALSE; } /* Read the value from SS:ESP */ if (!Fast486ReadMemory(State, FAST486_REG_SS, State->SegmentRegs[FAST486_REG_SS].Size ? State->GeneralRegs[FAST486_REG_ESP].Long : State->GeneralRegs[FAST486_REG_ESP].LowWord, FALSE, &LongValue, sizeof(LongValue))) { /* An exception occurred */ return FALSE; } if (State->SegmentRegs[FAST486_REG_SS].Size) { /* Increment ESP by 4 */ State->GeneralRegs[FAST486_REG_ESP].Long += sizeof(ULONG); } else { /* Increment SP by 4 */ State->GeneralRegs[FAST486_REG_ESP].LowWord += sizeof(ULONG); } /* Store the value in the result */ *Value = LongValue; } else { /* 16-bit size */ USHORT ShortValue; /* Check if SP is 0xFFFF */ if (State->GeneralRegs[FAST486_REG_ESP].LowWord == 0xFFFF) { Fast486Exception(State, FAST486_EXCEPTION_SS); return FALSE; } /* Read the value from SS:SP */ if (!Fast486ReadMemory(State, FAST486_REG_SS, State->SegmentRegs[FAST486_REG_SS].Size ? State->GeneralRegs[FAST486_REG_ESP].Long : State->GeneralRegs[FAST486_REG_ESP].LowWord, FALSE, &ShortValue, sizeof(ShortValue))) { /* An exception occurred */ return FALSE; } if (State->SegmentRegs[FAST486_REG_SS].Size) { /* Increment ESP by 2 */ State->GeneralRegs[FAST486_REG_ESP].Long += sizeof(USHORT); } else { /* Increment SP by 2 */ State->GeneralRegs[FAST486_REG_ESP].LowWord += sizeof(USHORT); } /* Store the value in the result */ *Value = ShortValue; } return TRUE; } FORCEINLINE BOOLEAN FASTCALL Fast486ReadDescriptorEntry(PFAST486_STATE State, USHORT Selector, PBOOLEAN EntryValid, PFAST486_GDT_ENTRY Entry) { if (!(Selector & SEGMENT_TABLE_INDICATOR)) { /* Make sure the GDT contains the entry */ if (GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1u)) { *EntryValid = FALSE; return TRUE; } /* Read the GDT */ if (!Fast486ReadLinearMemory(State, State->Gdtr.Address + GET_SEGMENT_INDEX(Selector), Entry, sizeof(*Entry), FALSE)) { /* Exception occurred */ *EntryValid = FALSE; return FALSE; } } else { /* Make sure the LDT contains the entry */ if (GET_SEGMENT_INDEX(Selector) >= (State->Ldtr.Limit + 1u)) { *EntryValid = FALSE; return TRUE; } /* Read the LDT */ if (!Fast486ReadLinearMemory(State, State->Ldtr.Base + GET_SEGMENT_INDEX(Selector), Entry, sizeof(*Entry), FALSE)) { /* Exception occurred */ *EntryValid = FALSE; return FALSE; } } *EntryValid = TRUE; return TRUE; } FORCEINLINE BOOLEAN FASTCALL Fast486LoadSegmentInternal(PFAST486_STATE State, FAST486_SEG_REGS Segment, USHORT Selector, FAST486_EXCEPTIONS Exception) { PFAST486_SEG_REG CachedDescriptor; BOOLEAN Valid; FAST486_GDT_ENTRY GdtEntry; ASSERT(Segment < FAST486_NUM_SEG_REGS); /* Get the cached descriptor */ CachedDescriptor = &State->SegmentRegs[Segment]; /* Check for protected mode */ if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) { /* Check for VM86 mode */ if (State->Flags.Vm) { /* Update the cached descriptor with VM86 values */ CachedDescriptor->Selector = Selector; CachedDescriptor->Base = Selector << 4; CachedDescriptor->Limit = 0xFFFF; CachedDescriptor->ReadWrite = TRUE; CachedDescriptor->DirConf = FALSE; CachedDescriptor->SystemType = TRUE; CachedDescriptor->Dpl = CachedDescriptor->Rpl = 3; CachedDescriptor->Present = TRUE; CachedDescriptor->Size = FALSE; return TRUE; } if (!Fast486ReadDescriptorEntry(State, Selector, &Valid, &GdtEntry)) { /* Exception occurred */ return FALSE; } if (!Valid) { /* Invalid selector */ Fast486ExceptionWithErrorCode(State, Exception, Selector); return FALSE; } if (Segment == FAST486_REG_SS) { /* Loading the stack segment */ if (!(Selector & SEGMENT_TABLE_INDICATOR) && GET_SEGMENT_INDEX(Selector) == 0) { Fast486Exception(State, Exception); return FALSE; } if (!GdtEntry.SystemType) { /* This is a special descriptor */ Fast486ExceptionWithErrorCode(State, Exception, Selector); return FALSE; } if (GdtEntry.Executable || !GdtEntry.ReadWrite) { Fast486ExceptionWithErrorCode(State, Exception, Selector); return FALSE; } if ((GET_SEGMENT_RPL(Selector) != Fast486GetCurrentPrivLevel(State)) || (GET_SEGMENT_RPL(Selector) != GdtEntry.Dpl)) { Fast486ExceptionWithErrorCode(State, Exception, Selector); return FALSE; } if (!GdtEntry.Present) { Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_SS, Selector); return FALSE; } } else if (Segment == FAST486_REG_CS) { /* Loading the code segment */ #ifndef FAST486_NO_PREFETCH /* Invalidate the prefetch */ State->PrefetchValid = FALSE; #endif if (!(Selector & SEGMENT_TABLE_INDICATOR) && GET_SEGMENT_INDEX(Selector) == 0) { Fast486Exception(State, Exception); return FALSE; } if (!GdtEntry.SystemType) { /* Must be a segment descriptor */ Fast486ExceptionWithErrorCode(State, Exception, Selector); return FALSE; } if (!GdtEntry.Present) { Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector); return FALSE; } if (!GdtEntry.Executable) { Fast486ExceptionWithErrorCode(State, Exception, Selector); return FALSE; } if (GdtEntry.DirConf) { /* Conforming Code Segment */ if (GdtEntry.Dpl > Fast486GetCurrentPrivLevel(State)) { /* Must be accessed from lower-privileged code */ Fast486ExceptionWithErrorCode(State, Exception, Selector); return FALSE; } } else { /* Regular code segment */ if ((GET_SEGMENT_RPL(Selector) < Fast486GetCurrentPrivLevel(State))) { Fast486ExceptionWithErrorCode(State, Exception, Selector); return FALSE; } } } else { /* Loading a data segment */ if (GET_SEGMENT_INDEX(Selector) != 0 || (Selector & SEGMENT_TABLE_INDICATOR)) { if (!GdtEntry.SystemType) { /* This is a special descriptor */ Fast486ExceptionWithErrorCode(State, Exception, Selector); return FALSE; } if ((GET_SEGMENT_RPL(Selector) > GdtEntry.Dpl) || (Fast486GetCurrentPrivLevel(State) > GdtEntry.Dpl)) { Fast486ExceptionWithErrorCode(State, Exception, Selector); return FALSE; } if (!GdtEntry.Present) { Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector); return FALSE; } } else { /* This is a NULL selector */ RtlZeroMemory(&GdtEntry, sizeof(GdtEntry)); } } /* Update the cache entry */ CachedDescriptor->Selector = Selector; CachedDescriptor->Base = GdtEntry.Base | (GdtEntry.BaseMid << 16) | (GdtEntry.BaseHigh << 24); CachedDescriptor->Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16); CachedDescriptor->Accessed = GdtEntry.Accessed; CachedDescriptor->ReadWrite = GdtEntry.ReadWrite; CachedDescriptor->DirConf = GdtEntry.DirConf; CachedDescriptor->Executable = GdtEntry.Executable; CachedDescriptor->SystemType = GdtEntry.SystemType; CachedDescriptor->Rpl = GET_SEGMENT_RPL(Selector); CachedDescriptor->Dpl = GdtEntry.Dpl; CachedDescriptor->Present = GdtEntry.Present; CachedDescriptor->Size = GdtEntry.Size; /* Check for page granularity */ if (GdtEntry.Granularity) { CachedDescriptor->Limit <<= 12; CachedDescriptor->Limit |= 0x00000FFF; } } else { /* Update the selector and base */ CachedDescriptor->Selector = Selector; CachedDescriptor->Base = Selector << 4; } return TRUE; } FORCEINLINE BOOLEAN FASTCALL Fast486LoadSegment(PFAST486_STATE State, FAST486_SEG_REGS Segment, USHORT Selector) { return Fast486LoadSegmentInternal(State, Segment, Selector, FAST486_EXCEPTION_GP); } FORCEINLINE BOOLEAN FASTCALL Fast486ProcessGate(PFAST486_STATE State, USHORT Selector, ULONG Offset, BOOLEAN Call) { BOOLEAN Valid; FAST486_SYSTEM_DESCRIPTOR Descriptor; if (!Fast486ReadDescriptorEntry(State, Selector, &Valid, (PFAST486_GDT_ENTRY)&Descriptor)) { /* Exception occurred */ return FALSE; } if (!Valid) { /* Invalid selector */ Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); return FALSE; } switch (Descriptor.Signature) { case FAST486_TASK_GATE_SIGNATURE: { Fast486TaskSwitch(State, Call ? FAST486_TASK_CALL : FAST486_TASK_JUMP, ((PFAST486_IDT_ENTRY)&Descriptor)->Selector); return FALSE; } case FAST486_TSS_16_SIGNATURE: case FAST486_BUSY_TSS_16_SIGNATURE: case FAST486_TSS_SIGNATURE: case FAST486_BUSY_TSS_SIGNATURE: { Fast486TaskSwitch(State, Call ? FAST486_TASK_CALL : FAST486_TASK_JUMP, Selector); return FALSE; } case FAST486_CALL_GATE_16_SIGNATURE: case FAST486_CALL_GATE_SIGNATURE: { if ((Descriptor.Dpl < Fast486GetCurrentPrivLevel(State)) && (Descriptor.Dpl < GET_SEGMENT_RPL(Selector))) { Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); return FALSE; } if (!Descriptor.Present) { Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector); return FALSE; } Fast486CallGate(State, (PFAST486_CALL_GATE)&Descriptor, Call); /* The gate has been processed here, so return FALSE */ return FALSE; } default: { /* Security check for jumps and calls only */ if (State->Cpl != Descriptor.Dpl) { Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); return FALSE; } return TRUE; } } } FORCEINLINE BOOLEAN FASTCALL Fast486FetchByte(PFAST486_STATE State, PUCHAR Data) { PFAST486_SEG_REG CachedDescriptor; ULONG Offset; #ifndef FAST486_NO_PREFETCH ULONG LinearAddress; #endif /* Get the cached descriptor of CS */ CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS]; Offset = (CachedDescriptor->Size) ? State->InstPtr.Long : State->InstPtr.LowWord; #ifndef FAST486_NO_PREFETCH LinearAddress = CachedDescriptor->Base + Offset; if (State->PrefetchValid && (LinearAddress >= State->PrefetchAddress) && ((LinearAddress + sizeof(UCHAR)) <= (State->PrefetchAddress + FAST486_CACHE_SIZE))) { *Data = *(PUCHAR)&State->PrefetchCache[LinearAddress - State->PrefetchAddress]; } else #endif { /* Read from memory */ if (!Fast486ReadMemory(State, FAST486_REG_CS, Offset, TRUE, Data, sizeof(UCHAR))) { /* Exception occurred during instruction fetch */ return FALSE; } } /* Advance the instruction pointer */ if (CachedDescriptor->Size) State->InstPtr.Long++; else State->InstPtr.LowWord++; return TRUE; } FORCEINLINE BOOLEAN FASTCALL Fast486FetchWord(PFAST486_STATE State, PUSHORT Data) { PFAST486_SEG_REG CachedDescriptor; ULONG Offset; #ifndef FAST486_NO_PREFETCH ULONG LinearAddress; #endif /* Get the cached descriptor of CS */ CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS]; Offset = (CachedDescriptor->Size) ? State->InstPtr.Long : State->InstPtr.LowWord; #ifndef FAST486_NO_PREFETCH LinearAddress = CachedDescriptor->Base + Offset; if (State->PrefetchValid && (LinearAddress >= State->PrefetchAddress) && ((LinearAddress + sizeof(USHORT)) <= (State->PrefetchAddress + FAST486_CACHE_SIZE))) { *Data = *(PUSHORT)&State->PrefetchCache[LinearAddress - State->PrefetchAddress]; } else #endif { /* Read from memory */ // FIXME: Fix byte order on big-endian machines if (!Fast486ReadMemory(State, FAST486_REG_CS, Offset, TRUE, Data, sizeof(USHORT))) { /* Exception occurred during instruction fetch */ return FALSE; } } /* Advance the instruction pointer */ if (CachedDescriptor->Size) State->InstPtr.Long += sizeof(USHORT); else State->InstPtr.LowWord += sizeof(USHORT); return TRUE; } FORCEINLINE BOOLEAN FASTCALL Fast486FetchDword(PFAST486_STATE State, PULONG Data) { PFAST486_SEG_REG CachedDescriptor; ULONG Offset; #ifndef FAST486_NO_PREFETCH ULONG LinearAddress; #endif /* Get the cached descriptor of CS */ CachedDescriptor = &State->SegmentRegs[FAST486_REG_CS]; Offset = (CachedDescriptor->Size) ? State->InstPtr.Long : State->InstPtr.LowWord; #ifndef FAST486_NO_PREFETCH LinearAddress = CachedDescriptor->Base + Offset; if (State->PrefetchValid && (LinearAddress >= State->PrefetchAddress) && ((LinearAddress + sizeof(ULONG)) <= (State->PrefetchAddress + FAST486_CACHE_SIZE))) { *Data = *(PULONG)&State->PrefetchCache[LinearAddress - State->PrefetchAddress]; } else #endif { /* Read from memory */ // FIXME: Fix byte order on big-endian machines if (!Fast486ReadMemory(State, FAST486_REG_CS, Offset, TRUE, Data, sizeof(ULONG))) { /* Exception occurred during instruction fetch */ return FALSE; } } /* Advance the instruction pointer */ if (CachedDescriptor->Size) State->InstPtr.Long += sizeof(ULONG); else State->InstPtr.LowWord += sizeof(ULONG); return TRUE; } FORCEINLINE BOOLEAN FASTCALL Fast486CalculateParity(UCHAR Number) { // See http://graphics.stanford.edu/~seander/bithacks.html#ParityLookupTable too... return (0x9669 >> ((Number & 0x0F) ^ (Number >> 4))) & 1; } FORCEINLINE BOOLEAN FASTCALL Fast486ParseModRegRm(PFAST486_STATE State, BOOLEAN AddressSize, PFAST486_MOD_REG_RM ModRegRm) { UCHAR ModRmByte, Mode, RegMem; /* Fetch the MOD REG R/M byte */ if (!Fast486FetchByte(State, &ModRmByte)) { /* Exception occurred */ return FALSE; } /* Unpack the mode and R/M */ Mode = ModRmByte >> 6; RegMem = ModRmByte & 0x07; /* Set the register operand */ ModRegRm->Register = (ModRmByte >> 3) & 0x07; /* Check the mode */ if (Mode == 3) { /* The second operand is also a register */ ModRegRm->Memory = FALSE; ModRegRm->SecondRegister = RegMem; /* Done parsing */ return TRUE; } /* The second operand is memory */ ModRegRm->Memory = TRUE; if (AddressSize) { if (RegMem == FAST486_REG_ESP) { UCHAR SibByte; ULONG Scale, Index, Base; /* Fetch the SIB byte */ if (!Fast486FetchByte(State, &SibByte)) { /* Exception occurred */ return FALSE; } /* Unpack the scale, index and base */ Scale = 1 << (SibByte >> 6); Index = (SibByte >> 3) & 0x07; if (Index != FAST486_REG_ESP) Index = State->GeneralRegs[Index].Long; else Index = 0; if (((SibByte & 0x07) != FAST486_REG_EBP) || (Mode != 0)) { /* Use the register a base */ Base = State->GeneralRegs[SibByte & 0x07].Long; } else { /* Fetch the base */ if (!Fast486FetchDword(State, &Base)) { /* Exception occurred */ return FALSE; } } if (((SibByte & 0x07) == FAST486_REG_ESP) || ((SibByte & 0x07) == FAST486_REG_EBP && Mode != 0)) { /* Check if there is no segment override */ if (!(State->PrefixFlags & FAST486_PREFIX_SEG)) { /* Add a SS: prefix */ State->PrefixFlags |= FAST486_PREFIX_SEG; State->SegmentOverride = FAST486_REG_SS; } } /* Calculate the address */ ModRegRm->MemoryAddress = Base + Index * Scale; } else if (RegMem == FAST486_REG_EBP) { if (Mode) ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].Long; else ModRegRm->MemoryAddress = 0; } else { /* Get the base from the register */ ModRegRm->MemoryAddress = State->GeneralRegs[RegMem].Long; } /* Check if there is no segment override */ if (!(State->PrefixFlags & FAST486_PREFIX_SEG)) { /* Check if the default segment should be SS */ if ((RegMem == FAST486_REG_EBP) && Mode) { /* Add a SS: prefix */ State->PrefixFlags |= FAST486_PREFIX_SEG; State->SegmentOverride = FAST486_REG_SS; } } if (Mode == 1) { CHAR Offset; /* Fetch the byte */ if (!Fast486FetchByte(State, (PUCHAR)&Offset)) { /* Exception occurred */ return FALSE; } /* Add the signed offset to the address */ ModRegRm->MemoryAddress += (LONG)Offset; } else if ((Mode == 2) || ((Mode == 0) && (RegMem == FAST486_REG_EBP))) { LONG Offset; /* Fetch the dword */ if (!Fast486FetchDword(State, (PULONG)&Offset)) { /* Exception occurred */ return FALSE; } /* Add the signed offset to the address */ ModRegRm->MemoryAddress += Offset; } } else { /* Check the operand */ switch (RegMem) { case 0: { /* [BX + SI] */ ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord + State->GeneralRegs[FAST486_REG_ESI].LowWord; break; } case 1: { /* [BX + DI] */ ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord + State->GeneralRegs[FAST486_REG_EDI].LowWord; break; } case 2: { /* SS:[BP + SI] */ ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord + State->GeneralRegs[FAST486_REG_ESI].LowWord; break; } case 3: { /* SS:[BP + DI] */ ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord + State->GeneralRegs[FAST486_REG_EDI].LowWord; break; } case 4: { /* [SI] */ ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_ESI].LowWord; break; } case 5: { /* [DI] */ ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EDI].LowWord; break; } case 6: { if (Mode) { /* [BP] */ ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBP].LowWord; } else { /* [constant] (added later) */ ModRegRm->MemoryAddress = 0; } break; } case 7: { /* [BX] */ ModRegRm->MemoryAddress = State->GeneralRegs[FAST486_REG_EBX].LowWord; break; } } /* Check if there is no segment override */ if (!(State->PrefixFlags & FAST486_PREFIX_SEG)) { /* Check if the default segment should be SS */ if ((RegMem == 2) || (RegMem == 3) || ((RegMem == 6) && Mode)) { /* Add a SS: prefix */ State->PrefixFlags |= FAST486_PREFIX_SEG; State->SegmentOverride = FAST486_REG_SS; } } if (Mode == 1) { CHAR Offset; /* Fetch the byte */ if (!Fast486FetchByte(State, (PUCHAR)&Offset)) { /* Exception occurred */ return FALSE; } /* Add the signed offset to the address */ ModRegRm->MemoryAddress += (LONG)Offset; } else if ((Mode == 2) || ((Mode == 0) && (RegMem == 6))) { SHORT Offset; /* Fetch the word */ if (!Fast486FetchWord(State, (PUSHORT)&Offset)) { /* Exception occurred */ return FALSE; } /* Add the signed offset to the address */ ModRegRm->MemoryAddress += (LONG)Offset; } /* Clear the top 16 bits */ ModRegRm->MemoryAddress &= 0x0000FFFF; } return TRUE; } FORCEINLINE BOOLEAN FASTCALL Fast486ReadModrmByteOperands(PFAST486_STATE State, PFAST486_MOD_REG_RM ModRegRm, PUCHAR RegValue, PUCHAR RmValue) { FAST486_SEG_REGS Segment = FAST486_REG_DS; if (RegValue) { /* Get the register value */ if (ModRegRm->Register & 0x04) { /* AH, CH, DH, BH */ *RegValue = State->GeneralRegs[ModRegRm->Register & 0x03].HighByte; } else { /* AL, CL, DL, BL */ *RegValue = State->GeneralRegs[ModRegRm->Register & 0x03].LowByte; } } if (RmValue) { if (!ModRegRm->Memory) { /* Get the second register value */ if (ModRegRm->SecondRegister & 0x04) { /* AH, CH, DH, BH */ *RmValue = State->GeneralRegs[ModRegRm->SecondRegister & 0x03].HighByte; } else { /* AL, CL, DL, BL */ *RmValue = State->GeneralRegs[ModRegRm->SecondRegister & 0x03].LowByte; } } else { /* Check for the segment override */ if (State->PrefixFlags & FAST486_PREFIX_SEG) { /* Use the override segment instead */ Segment = State->SegmentOverride; } /* Read memory */ if (!Fast486ReadMemory(State, Segment, ModRegRm->MemoryAddress, FALSE, RmValue, sizeof(UCHAR))) { /* Exception occurred */ return FALSE; } } } return TRUE; } FORCEINLINE BOOLEAN FASTCALL Fast486ReadModrmWordOperands(PFAST486_STATE State, PFAST486_MOD_REG_RM ModRegRm, PUSHORT RegValue, PUSHORT RmValue) { FAST486_SEG_REGS Segment = FAST486_REG_DS; if (RegValue) { /* Get the register value */ *RegValue = State->GeneralRegs[ModRegRm->Register].LowWord; } if (RmValue) { if (!ModRegRm->Memory) { /* Get the second register value */ *RmValue = State->GeneralRegs[ModRegRm->SecondRegister].LowWord; } else { /* Check for the segment override */ if (State->PrefixFlags & FAST486_PREFIX_SEG) { /* Use the override segment instead */ Segment = State->SegmentOverride; } /* Read memory */ if (!Fast486ReadMemory(State, Segment, ModRegRm->MemoryAddress, FALSE, RmValue, sizeof(USHORT))) { /* Exception occurred */ return FALSE; } } } return TRUE; } FORCEINLINE BOOLEAN FASTCALL Fast486ReadModrmDwordOperands(PFAST486_STATE State, PFAST486_MOD_REG_RM ModRegRm, PULONG RegValue, PULONG RmValue) { FAST486_SEG_REGS Segment = FAST486_REG_DS; if (RegValue) { /* Get the register value */ *RegValue = State->GeneralRegs[ModRegRm->Register].Long; } if (RmValue) { if (!ModRegRm->Memory) { /* Get the second register value */ *RmValue = State->GeneralRegs[ModRegRm->SecondRegister].Long; } else { /* Check for the segment override */ if (State->PrefixFlags & FAST486_PREFIX_SEG) { /* Use the override segment instead */ Segment = State->SegmentOverride; } /* Read memory */ if (!Fast486ReadMemory(State, Segment, ModRegRm->MemoryAddress, FALSE, RmValue, sizeof(ULONG))) { /* Exception occurred */ return FALSE; } } } return TRUE; } FORCEINLINE BOOLEAN FASTCALL Fast486WriteModrmByteOperands(PFAST486_STATE State, PFAST486_MOD_REG_RM ModRegRm, BOOLEAN WriteRegister, UCHAR Value) { FAST486_SEG_REGS Segment = FAST486_REG_DS; if (WriteRegister) { /* Store the value in the register */ if (ModRegRm->Register & 0x04) { /* AH, CH, DH, BH */ State->GeneralRegs[ModRegRm->Register & 0x03].HighByte = Value; } else { /* AL, CL, DL, BL */ State->GeneralRegs[ModRegRm->Register & 0x03].LowByte = Value; } } else { if (!ModRegRm->Memory) { /* Store the value in the second register */ if (ModRegRm->SecondRegister & 0x04) { /* AH, CH, DH, BH */ State->GeneralRegs[ModRegRm->SecondRegister & 0x03].HighByte = Value; } else { /* AL, CL, DL, BL */ State->GeneralRegs[ModRegRm->SecondRegister & 0x03].LowByte = Value; } } else { /* Check for the segment override */ if (State->PrefixFlags & FAST486_PREFIX_SEG) { /* Use the override segment instead */ Segment = State->SegmentOverride; } /* Write memory */ if (!Fast486WriteMemory(State, Segment, ModRegRm->MemoryAddress, &Value, sizeof(UCHAR))) { /* Exception occurred */ return FALSE; } } } return TRUE; } FORCEINLINE BOOLEAN FASTCALL Fast486WriteModrmWordOperands(PFAST486_STATE State, PFAST486_MOD_REG_RM ModRegRm, BOOLEAN WriteRegister, USHORT Value) { FAST486_SEG_REGS Segment = FAST486_REG_DS; if (WriteRegister) { /* Store the value in the register */ State->GeneralRegs[ModRegRm->Register].LowWord = Value; } else { if (!ModRegRm->Memory) { /* Store the value in the second register */ State->GeneralRegs[ModRegRm->SecondRegister].LowWord = Value; } else { /* Check for the segment override */ if (State->PrefixFlags & FAST486_PREFIX_SEG) { /* Use the override segment instead */ Segment = State->SegmentOverride; } /* Write memory */ if (!Fast486WriteMemory(State, Segment, ModRegRm->MemoryAddress, &Value, sizeof(USHORT))) { /* Exception occurred */ return FALSE; } } } return TRUE; } FORCEINLINE BOOLEAN FASTCALL Fast486WriteModrmDwordOperands(PFAST486_STATE State, PFAST486_MOD_REG_RM ModRegRm, BOOLEAN WriteRegister, ULONG Value) { FAST486_SEG_REGS Segment = FAST486_REG_DS; if (WriteRegister) { /* Store the value in the register */ State->GeneralRegs[ModRegRm->Register].Long = Value; } else { if (!ModRegRm->Memory) { /* Store the value in the second register */ State->GeneralRegs[ModRegRm->SecondRegister].Long = Value; } else { /* Check for the segment override */ if (State->PrefixFlags & FAST486_PREFIX_SEG) { /* Use the override segment instead */ Segment = State->SegmentOverride; } /* Write memory */ if (!Fast486WriteMemory(State, Segment, ModRegRm->MemoryAddress, &Value, sizeof(ULONG))) { /* Exception occurred */ return FALSE; } } } return TRUE; } FORCEINLINE BOOLEAN FASTCALL Fast486IoPrivilegeCheck(PFAST486_STATE State, USHORT Port) { UCHAR Bits; ULONG Location; FAST486_TSS Tss; /* Access is always allowed if the CPL is less than or equal to the IOPL */ if (State->Cpl <= State->Flags.Iopl) return TRUE; /* Legacy Task State Segments have no IOPB */ if (!State->TaskReg.Modern) return FALSE; /* Read the TSS */ if (!Fast486ReadLinearMemory(State, State->TaskReg.Base, &Tss, sizeof(FAST486_TSS), FALSE)) { /* Exception occurred */ return FALSE; } Location = State->TaskReg.Base + HIWORD(Tss.IopbOffset) + (Port >> 3); if (Location > State->TaskReg.Limit) { /* Access denied */ Fast486Exception(State, FAST486_EXCEPTION_GP); return FALSE; } /* Read the appropriate bit from the TSS IOPB */ if (!Fast486ReadLinearMemory(State, Location, &Bits, sizeof(UCHAR), FALSE)) { /* Exception occurred */ return FALSE; } if (Bits & (1 << (Port & 0x07))) { /* Access denied */ Fast486Exception(State, FAST486_EXCEPTION_GP); return FALSE; } return TRUE; } #ifndef FAST486_NO_FPU FORCEINLINE VOID FASTCALL Fast486FpuExceptionCheck(PFAST486_STATE State) { /* Check if an unmasked exception occurred */ if ((State->FpuStatus.Ie && !State->FpuControl.Im) || (State->FpuStatus.De && !State->FpuControl.Dm) || (State->FpuStatus.Ze && !State->FpuControl.Zm) || (State->FpuStatus.Oe && !State->FpuControl.Om) || (State->FpuStatus.Ue && !State->FpuControl.Um) || (State->FpuStatus.Pe && !State->FpuControl.Pm)) { if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_NE) { /* Call the #MF handler */ Fast486Exception(State, FAST486_EXCEPTION_MF); } else { /* Use the external interrupt */ State->FpuCallback(State); } } } FORCEINLINE BOOLEAN FASTCALL Fast486FpuNormalize(PFAST486_STATE State, PFAST486_FPU_DATA_REG Data) { UINT LeadingZeros; if (FPU_IS_ZERO(Data)) { Data->Exponent = 0; return TRUE; } if (FPU_IS_NORMALIZED(Data)) return TRUE; LeadingZeros = CountLeadingZeros64(Data->Mantissa); if (LeadingZeros < Data->Exponent) { Data->Mantissa <<= LeadingZeros; Data->Exponent -= LeadingZeros; } else { /* Raise the underflow exception */ State->FpuStatus.Ue = TRUE; if (State->FpuControl.Um) { /* Make it denormalized */ Data->Mantissa <<= Data->Exponent - 1; Data->Exponent = 1; } else { return FALSE; } } return TRUE; } FORCEINLINE USHORT FASTCALL Fast486FpuGetValueTag(PFAST486_FPU_DATA_REG Data) { if (FPU_IS_ZERO(Data)) return FPU_TAG_ZERO; else if (FPU_IS_NAN(Data)) return FPU_TAG_SPECIAL; else return FPU_TAG_VALID; } FORCEINLINE BOOLEAN FASTCALL Fast486FpuPush(PFAST486_STATE State, PCFAST486_FPU_DATA_REG Data) { State->FpuStatus.Top--; if (FPU_GET_TAG(0) == FPU_TAG_EMPTY) { FPU_ST(0) = *Data; FPU_UPDATE_TAG(0); return TRUE; } else { /* Raise the stack fault and invalid operation exception */ State->FpuStatus.Sf = State->FpuStatus.Ie = TRUE; /* Set the C1 condition code bit (stack overflow) */ State->FpuStatus.Code1 = TRUE; return FALSE; } } FORCEINLINE BOOLEAN FASTCALL Fast486FpuPop(PFAST486_STATE State) { if (FPU_GET_TAG(0) != FPU_TAG_EMPTY) { FPU_SET_TAG(0, FPU_TAG_EMPTY); State->FpuStatus.Top++; return TRUE; } else { /* Raise the stack fault and invalid operation exception */ State->FpuStatus.Sf = State->FpuStatus.Ie = TRUE; /* Clear the C1 condition code bit (stack underflow) */ State->FpuStatus.Code1 = FALSE; return FALSE; } } #endif /* EOF */