[FAST486]
[reactos.git] / reactos / lib / fast486 / common.c
index 83c11e2..4f78e01 100644 (file)
@@ -2,7 +2,7 @@
  * Fast486 386/486 CPU Emulation Library
  * common.c
  *
- * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * Copyright (C) 2015 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -47,15 +47,27 @@ Fast486ReadMemory(PFAST486_STATE State,
     /* Get the cached descriptor */
     CachedDescriptor = &State->SegmentRegs[SegmentReg];
 
-    if ((Offset + Size - 1) > CachedDescriptor->Limit)
+    if (InstFetch || !CachedDescriptor->DirConf)
     {
-        /* Read beyond limit */
-        Fast486Exception(State, FAST486_EXCEPTION_GP);
-        return FALSE;
+        if ((Offset + Size - 1) > CachedDescriptor->Limit)
+        {
+            /* Read beyond limit */
+            Fast486Exception(State, FAST486_EXCEPTION_GP);
+            return FALSE;
+        }
+    }
+    else
+    {
+        if (Offset < CachedDescriptor->Limit)
+        {
+            /* Read beyond limit */
+            Fast486Exception(State, FAST486_EXCEPTION_GP);
+            return FALSE;
+        }
     }
 
     /* Check for protected mode */
-    if (State->ControlRegisters[0] & FAST486_CR0_PE)
+    if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
     {
         /* Privilege checks */
 
@@ -95,8 +107,51 @@ Fast486ReadMemory(PFAST486_STATE State,
     /* Find the linear address */
     LinearAddress = CachedDescriptor->Base + Offset;
 
-    /* Read from the linear address */
-    return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size);
+#ifndef FAST486_NO_PREFETCH
+    if (InstFetch && ((Offset + FAST486_CACHE_SIZE - 1) <= CachedDescriptor->Limit))
+    {
+        State->PrefetchAddress = LinearAddress;
+
+        if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG)
+            && (PAGE_OFFSET(State->PrefetchAddress) > (FAST486_PAGE_SIZE - FAST486_CACHE_SIZE)))
+        {
+            /* We mustn't prefetch across a page boundary */
+            State->PrefetchAddress = PAGE_ALIGN(State->PrefetchAddress)
+                                     | (FAST486_PAGE_SIZE - FAST486_CACHE_SIZE);
+
+            if ((LinearAddress - State->PrefetchAddress + Size) >= FAST486_CACHE_SIZE)
+            {
+                /* We can't prefetch without possibly violating page permissions */
+                State->PrefetchValid = FALSE;
+                return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size);
+            }
+        }
+
+        /* Prefetch */
+        if (Fast486ReadLinearMemory(State,
+                                    State->PrefetchAddress,
+                                    State->PrefetchCache,
+                                    FAST486_CACHE_SIZE))
+        {
+            State->PrefetchValid = TRUE;
+
+            RtlMoveMemory(Buffer,
+                          &State->PrefetchCache[LinearAddress - State->PrefetchAddress],
+                          Size);
+            return TRUE;
+        }
+        else
+        {
+            State->PrefetchValid = FALSE;
+            return FALSE;
+        }
+    }
+    else
+#endif
+    {
+        /* Read from the linear address */
+        return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size);
+    }
 }
 
 BOOLEAN
@@ -114,15 +169,27 @@ Fast486WriteMemory(PFAST486_STATE State,
     /* Get the cached descriptor */
     CachedDescriptor = &State->SegmentRegs[SegmentReg];
 
-    if ((Offset + Size - 1) > CachedDescriptor->Limit)
+    if (!CachedDescriptor->DirConf)
     {
-        /* Write beyond limit */
-        Fast486Exception(State, FAST486_EXCEPTION_GP);
-        return FALSE;
+        if ((Offset + Size - 1) > CachedDescriptor->Limit)
+        {
+            /* Write beyond limit */
+            Fast486Exception(State, FAST486_EXCEPTION_GP);
+            return FALSE;
+        }
+    }
+    else
+    {
+        if (Offset < CachedDescriptor->Limit)
+        {
+            /* Read beyond limit */
+            Fast486Exception(State, FAST486_EXCEPTION_GP);
+            return FALSE;
+        }
     }
 
     /* Check for protected mode */
-    if (State->ControlRegisters[0] & FAST486_CR0_PE)
+    if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
     {
         /* Privilege checks */
 
@@ -156,18 +223,81 @@ Fast486WriteMemory(PFAST486_STATE State,
     /* Find the linear address */
     LinearAddress = CachedDescriptor->Base + Offset;
 
+#ifndef FAST486_NO_PREFETCH
+    if (State->PrefetchValid
+        && (LinearAddress >= State->PrefetchAddress)
+        && ((LinearAddress + Size) <= (State->PrefetchAddress + FAST486_CACHE_SIZE)))
+    {
+        /* Update the prefetch */
+        RtlMoveMemory(&State->PrefetchCache[LinearAddress - State->PrefetchAddress],
+                      Buffer,
+                      min(Size, FAST486_CACHE_SIZE + State->PrefetchAddress - LinearAddress));
+    }
+#endif
+
     /* Write to the linear address */
     return Fast486WriteLinearMemory(State, LinearAddress, Buffer, Size);
 }
 
-BOOLEAN
+static inline BOOLEAN
+FASTCALL
+Fast486GetIntVector(PFAST486_STATE State,
+                    UCHAR Number,
+                    PFAST486_IDT_ENTRY IdtEntry)
+{
+    /* Check for protected mode */
+    if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
+    {
+        /* Read from the IDT */
+        if (!Fast486ReadLinearMemory(State,
+                                     State->Idtr.Address
+                                     + Number * sizeof(*IdtEntry),
+                                     IdtEntry,
+                                     sizeof(*IdtEntry)))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+    }
+    else
+    {
+        /* Read from the real-mode IVT */
+        ULONG FarPointer;
+
+        /* Paging is always disabled in real mode */
+        State->MemReadCallback(State,
+                               State->Idtr.Address
+                               + Number * sizeof(FarPointer),
+                               &FarPointer,
+                               sizeof(FarPointer));
+
+        /* Fill a fake IDT entry */
+        IdtEntry->Offset = LOWORD(FarPointer);
+        IdtEntry->Selector = HIWORD(FarPointer);
+        IdtEntry->Zero = 0;
+        IdtEntry->Type = FAST486_IDT_INT_GATE;
+        IdtEntry->Storage = FALSE;
+        IdtEntry->Dpl = 0;
+        IdtEntry->Present = TRUE;
+        IdtEntry->OffsetHigh = 0;
+    }
+
+    return TRUE;
+}
+
+static inline BOOLEAN
+FASTCALL
 Fast486InterruptInternal(PFAST486_STATE State,
-                         USHORT SegmentSelector,
-                         ULONG Offset,
-                         ULONG GateType)
+                         PFAST486_IDT_ENTRY IdtEntry,
+                         BOOLEAN PushErrorCode,
+                         ULONG ErrorCode)
 {
-    BOOLEAN GateSize = (GateType == FAST486_IDT_INT_GATE_32)
-                       || (GateType == FAST486_IDT_TRAP_GATE_32);
+    USHORT SegmentSelector = IdtEntry->Selector;
+    ULONG  Offset          = MAKELONG(IdtEntry->Offset, IdtEntry->OffsetHigh);
+    ULONG  GateType        = IdtEntry->Type;
+    BOOLEAN GateSize = (GateType == FAST486_IDT_INT_GATE_32) ||
+                       (GateType == FAST486_IDT_TRAP_GATE_32);
+
     BOOLEAN Success = FALSE;
     ULONG OldPrefixFlags = State->PrefixFlags;
 
@@ -177,18 +307,22 @@ Fast486InterruptInternal(PFAST486_STATE State,
         FAST486_TSS Tss;
         USHORT OldSs = State->SegmentRegs[FAST486_REG_SS].Selector;
         ULONG OldEsp = State->GeneralRegs[FAST486_REG_ESP].Long;
-        
+
+        if (GateType == FAST486_TASK_GATE_SIGNATURE)
+        {
+            /* Task call */
+            return Fast486TaskSwitch(State, FAST486_TASK_CALL, IdtEntry->Selector);
+        }
+
         if (GateSize != (State->SegmentRegs[FAST486_REG_CS].Size))
         {
-            /*
-             * The gate size doesn't match the current operand size, so toggle
-             * the OPSIZE flag.
-             */
-            State->PrefixFlags ^= FAST486_PREFIX_OPSIZE;
+            /* The gate size doesn't match the current operand size, so set the OPSIZE flag. */
+            State->PrefixFlags |= FAST486_PREFIX_OPSIZE;
         }
 
-        /* Check if the interrupt handler is more privileged */
-        if (Fast486GetCurrentPrivLevel(State) > GET_SEGMENT_RPL(SegmentSelector))
+        /* Check if the interrupt handler is more privileged or if we're in V86 mode */
+        if ((Fast486GetCurrentPrivLevel(State) > GET_SEGMENT_RPL(SegmentSelector))
+            || State->Flags.Vm)
         {
             /* Read the TSS */
             if (!Fast486ReadLinearMemory(State,
@@ -200,8 +334,29 @@ Fast486InterruptInternal(PFAST486_STATE State,
                 goto Cleanup;
             }
 
+            /* Switch to the new privilege level */
+            State->Cpl = GET_SEGMENT_RPL(SegmentSelector);
+
+            if (State->Flags.Vm)
+            {
+                /* Clear the VM flag */
+                State->Flags.Vm = FALSE;
+
+                /* Push GS, FS, DS and ES */
+                if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_GS].Selector)) goto Cleanup;
+                if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_FS].Selector)) goto Cleanup;
+                if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_DS].Selector)) goto Cleanup;
+                if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_ES].Selector)) goto Cleanup;
+
+                /* Now load them with NULL selectors, since they are useless in protected mode */
+                if (!Fast486LoadSegment(State, FAST486_REG_GS, 0)) goto Cleanup;
+                if (!Fast486LoadSegment(State, FAST486_REG_FS, 0)) goto Cleanup;
+                if (!Fast486LoadSegment(State, FAST486_REG_DS, 0)) goto Cleanup;
+                if (!Fast486LoadSegment(State, FAST486_REG_ES, 0)) goto Cleanup;
+            }
+
             /* Check the new (higher) privilege level */
-            switch (GET_SEGMENT_RPL(SegmentSelector))
+            switch (State->Cpl)
             {
                 case 0:
                 {
@@ -271,6 +426,16 @@ Fast486InterruptInternal(PFAST486_STATE State,
     /* Push the instruction pointer */
     if (!Fast486StackPush(State, State->InstPtr.Long)) goto Cleanup;
 
+    if (PushErrorCode)
+    {
+        /* Push the error code */
+        if (!Fast486StackPush(State, ErrorCode))
+        {
+            /* An exception occurred */
+            goto Cleanup;
+        }
+    }
+
     if ((GateType == FAST486_IDT_INT_GATE) || (GateType == FAST486_IDT_INT_GATE_32))
     {
         /* Disable interrupts after a jump to an interrupt gate handler */
@@ -304,6 +469,30 @@ Cleanup:
     return Success;
 }
 
+BOOLEAN
+FASTCALL
+Fast486PerformInterrupt(PFAST486_STATE State,
+                        UCHAR Number)
+{
+    FAST486_IDT_ENTRY IdtEntry;
+
+    /* Get the interrupt vector */
+    if (!Fast486GetIntVector(State, Number, &IdtEntry))
+    {
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    /* Perform the interrupt */
+    if (!Fast486InterruptInternal(State, &IdtEntry, FALSE, 0))
+    {
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
 VOID
 FASTCALL
 Fast486ExceptionWithErrorCode(PFAST486_STATE State,
@@ -334,9 +523,13 @@ Fast486ExceptionWithErrorCode(PFAST486_STATE State,
         return;
     }
 
+    /* Clear the prefix flags */
+    State->PrefixFlags = 0;
+
     /* Restore the IP to the saved IP */
     State->InstPtr = State->SavedInstPtr;
 
+    /* Get the interrupt vector */
     if (!Fast486GetIntVector(State, ExceptionCode, &IdtEntry))
     {
         /*
@@ -348,9 +541,10 @@ Fast486ExceptionWithErrorCode(PFAST486_STATE State,
 
     /* Perform the interrupt */
     if (!Fast486InterruptInternal(State,
-                                  IdtEntry.Selector,
-                                  MAKELONG(IdtEntry.Offset, IdtEntry.OffsetHigh),
-                                  IdtEntry.Type))
+                                  &IdtEntry,
+                                  EXCEPTION_HAS_ERROR_CODE(ExceptionCode)
+                                  && (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE),
+                                  ErrorCode))
     {
         /*
          * If this function failed, that means Fast486Exception
@@ -359,22 +553,327 @@ Fast486ExceptionWithErrorCode(PFAST486_STATE State,
         return;
     }
 
-    if (EXCEPTION_HAS_ERROR_CODE(ExceptionCode)
-        && (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE))
+    /* Reset the exception count */
+    State->ExceptionCount = 0;
+}
+
+BOOLEAN
+FASTCALL
+Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Selector)
+{
+    ULONG NewTssAddress;
+    ULONG NewTssLimit;
+    FAST486_TSS OldTss;
+    FAST486_TSS NewTss;
+    FAST486_SYSTEM_DESCRIPTOR NewTssDescriptor;
+
+    /* Read the old TSS */
+    if (!Fast486ReadLinearMemory(State,
+                                 State->TaskReg.Base,
+                                 &OldTss,
+                                 sizeof(OldTss)))
     {
-        /* Push the error code */
-        if (!Fast486StackPush(State, ErrorCode))
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    /* If this is a task return, use the linked previous selector */
+    if (Type == FAST486_TASK_RETURN) Selector = LOWORD(OldTss.Link);
+
+    /* Make sure the entry exists in the GDT (not LDT!) */
+    if ((GET_SEGMENT_INDEX(Selector) == 0)
+        || (Selector & SEGMENT_TABLE_INDICATOR)
+        || GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1u))
+    {
+        Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector);
+        return FALSE;
+    }
+
+    /* Get the TSS descriptor from the GDT */
+    if (!Fast486ReadLinearMemory(State,
+                                 State->Gdtr.Address + GET_SEGMENT_INDEX(Selector),
+                                 &NewTssDescriptor,
+                                 sizeof(NewTssDescriptor)))
+    {
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    if (!NewTssDescriptor.Present)
+    {
+        /* Incoming task TSS not present */
+        Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector);
+        return FALSE;
+    }
+
+    /* Calculate the linear address of the new TSS */
+    NewTssAddress = NewTssDescriptor.Base;
+    NewTssAddress |= NewTssDescriptor.BaseMid << 16;
+    NewTssAddress |= NewTssDescriptor.BaseHigh << 24;
+
+    /* Calculate the limit of the new TSS */
+    NewTssLimit = NewTssDescriptor.Limit | (NewTssDescriptor.LimitHigh << 16);
+
+    if (NewTssDescriptor.Granularity)
+    {
+        NewTssLimit <<= 12;
+        NewTssLimit |= 0x00000FFF;
+    }
+
+    if (NewTssLimit < sizeof(FAST486_TSS))
+    {
+        /* TSS limit too small */
+        Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector);
+        return FALSE;
+    }
+
+    /*
+     * The incoming task shouldn't be busy if we're executing it as a
+     * new task, and it should be busy if we're returning to it.
+     */
+    if (((NewTssDescriptor.Signature != FAST486_TSS_SIGNATURE)
+        || (Type == FAST486_TASK_RETURN))
+        && ((NewTssDescriptor.Signature != FAST486_BUSY_TSS_SIGNATURE)
+        || (Type != FAST486_TASK_RETURN)))
+    {
+        Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
+        return FALSE;
+    }
+
+    /* Read the new TSS */
+    if (!Fast486ReadLinearMemory(State,
+                                 NewTssAddress,
+                                 &NewTss,
+                                 sizeof(NewTss)))
+    {
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    if (Type != FAST486_TASK_CALL)
+    {
+        /* Clear the busy bit of the outgoing task */
+        FAST486_SYSTEM_DESCRIPTOR OldTssDescriptor;
+
+        if (!Fast486ReadLinearMemory(State,
+                                     State->Gdtr.Address
+                                     + GET_SEGMENT_INDEX(State->TaskReg.Selector),
+                                     &OldTssDescriptor,
+                                     sizeof(OldTssDescriptor)))
         {
-            /*
-             * If this function failed, that means Fast486Exception
-             * was called again, so just return in this case.
-             */
-            return;
+            /* Exception occurred */
+            return FALSE;
+        }
+
+        OldTssDescriptor.Signature = FAST486_TSS_SIGNATURE;
+
+        if (!Fast486WriteLinearMemory(State,
+                                      State->Gdtr.Address
+                                      + GET_SEGMENT_INDEX(State->TaskReg.Selector),
+                                      &OldTssDescriptor,
+                                      sizeof(OldTssDescriptor)))
+        {
+            /* Exception occurred */
+            return FALSE;
         }
     }
+    else
+    {
+        /* Store the link */
+        NewTss.Link = State->TaskReg.Selector;
+    }
 
-    /* Reset the exception count */
-    State->ExceptionCount = 0;
+    /* Save the current task into the TSS */
+    OldTss.Cr3 = State->ControlRegisters[FAST486_REG_CR3];
+    OldTss.Eip = State->InstPtr.Long;
+    OldTss.Eflags = State->Flags.Long;
+    OldTss.Eax = State->GeneralRegs[FAST486_REG_EAX].Long;
+    OldTss.Ecx = State->GeneralRegs[FAST486_REG_ECX].Long;
+    OldTss.Edx = State->GeneralRegs[FAST486_REG_EDX].Long;
+    OldTss.Ebx = State->GeneralRegs[FAST486_REG_EBX].Long;
+    OldTss.Esp = State->GeneralRegs[FAST486_REG_ESP].Long;
+    OldTss.Ebp = State->GeneralRegs[FAST486_REG_EBP].Long;
+    OldTss.Esi = State->GeneralRegs[FAST486_REG_ESI].Long;
+    OldTss.Edi = State->GeneralRegs[FAST486_REG_EDI].Long;
+    OldTss.Es = State->SegmentRegs[FAST486_REG_ES].Selector;
+    OldTss.Cs = State->SegmentRegs[FAST486_REG_CS].Selector;
+    OldTss.Ss = State->SegmentRegs[FAST486_REG_SS].Selector;
+    OldTss.Ds = State->SegmentRegs[FAST486_REG_DS].Selector;
+    OldTss.Fs = State->SegmentRegs[FAST486_REG_FS].Selector;
+    OldTss.Gs = State->SegmentRegs[FAST486_REG_GS].Selector;
+    OldTss.Ldtr = State->Ldtr.Selector;
+
+    /* Write back the old TSS */
+    if (!Fast486WriteLinearMemory(State,
+                                  State->TaskReg.Base,
+                                  &OldTss,
+                                  sizeof(OldTss)))
+    {
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    /* Mark the new task as busy */
+    NewTssDescriptor.Signature = FAST486_BUSY_TSS_SIGNATURE;
+
+    /* Write back the new TSS descriptor */
+    if (!Fast486WriteLinearMemory(State,
+                                  State->Gdtr.Address + GET_SEGMENT_INDEX(Selector),
+                                  &NewTssDescriptor,
+                                  sizeof(NewTssDescriptor)))
+    {
+        /* Exception occurred */
+        return FALSE;
+    }
+
+    /* Set the task switch bit */
+    State->ControlRegisters[FAST486_REG_CR0] |= FAST486_CR0_TS;
+
+    /* Load the task register with the new values */
+    State->TaskReg.Selector = Selector;
+    State->TaskReg.Base = NewTssAddress;
+    State->TaskReg.Limit = NewTssLimit;
+
+    /* Change the page directory */
+    State->ControlRegisters[FAST486_REG_CR3] = NewTss.Cr3;
+
+    /* Flush the TLB */
+    if (State->Tlb) RtlZeroMemory(State->Tlb, NUM_TLB_ENTRIES * sizeof(ULONG));
+
+    /* Update the CPL */
+    State->Cpl = GET_SEGMENT_RPL(NewTss.Cs);
+
+#ifndef FAST486_NO_PREFETCH
+    /* Context switching invalidates the prefetch */
+    State->PrefetchValid = FALSE;
+#endif
+
+    /* Update the CPL */
+    State->Cpl = GET_SEGMENT_RPL(NewTss.Cs);
+
+    /* Load the registers */
+    State->InstPtr.Long = State->SavedInstPtr.Long = NewTss.Eip;
+    State->Flags.Long = NewTss.Eflags;
+    State->GeneralRegs[FAST486_REG_EAX].Long = NewTss.Eax;
+    State->GeneralRegs[FAST486_REG_ECX].Long = NewTss.Ecx;
+    State->GeneralRegs[FAST486_REG_EDX].Long = NewTss.Edx;
+    State->GeneralRegs[FAST486_REG_EBX].Long = NewTss.Ebx;
+    State->GeneralRegs[FAST486_REG_ESP].Long = NewTss.Esp;
+    State->GeneralRegs[FAST486_REG_EBP].Long = NewTss.Ebp;
+    State->GeneralRegs[FAST486_REG_ESI].Long = NewTss.Esi;
+    State->GeneralRegs[FAST486_REG_EDI].Long = NewTss.Edi;
+
+    /* Set the NT flag if nesting */
+    if (Type == FAST486_TASK_CALL) State->Flags.Nt = TRUE;
+
+    if (GET_SEGMENT_INDEX(NewTss.Ldtr) != 0)
+    {
+        BOOLEAN Valid;
+        FAST486_SYSTEM_DESCRIPTOR GdtEntry;
+
+        if (NewTss.Ldtr & SEGMENT_TABLE_INDICATOR)
+        {
+            /* This selector doesn't point to the GDT */
+            Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
+            return FALSE;
+        }
+
+        if (!Fast486ReadDescriptorEntry(State,
+                                        NewTss.Ldtr,
+                                        &Valid,
+                                        (PFAST486_GDT_ENTRY)&GdtEntry))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+
+        if (!Valid)
+        {
+            /* Invalid selector */
+            Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
+            return FALSE;
+        }
+
+        if (GdtEntry.Signature != FAST486_LDT_SIGNATURE)
+        {
+            /* This is not an LDT descriptor */
+            Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
+            return FALSE;
+        }
+
+        if (!GdtEntry.Present)
+        {
+            Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
+            return FALSE;
+        }
+
+        /* Update the LDTR */
+        State->Ldtr.Selector = NewTss.Ldtr;
+        State->Ldtr.Base = GdtEntry.Base | (GdtEntry.BaseMid << 16) | (GdtEntry.BaseHigh << 24);
+        State->Ldtr.Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16);
+
+        if (GdtEntry.Granularity)
+        {
+            State->Ldtr.Limit <<= 12;
+            State->Ldtr.Limit |= 0x00000FFF;
+        }
+    }
+    else
+    {
+        /* The LDT of this task is empty */
+        RtlZeroMemory(&State->Ldtr, sizeof(State->Ldtr));
+    }
+
+    /* Load the new segments */
+    if (!Fast486LoadSegmentInternal(State,
+                                    FAST486_REG_CS,
+                                    NewTss.Cs,
+                                    FAST486_EXCEPTION_TS))
+    {
+        return FALSE;
+    }
+
+    if (!Fast486LoadSegmentInternal(State,
+                                    FAST486_REG_SS,
+                                    NewTss.Ss,
+                                    FAST486_EXCEPTION_TS))
+    {
+        return FALSE;
+    }
+
+    if (!Fast486LoadSegmentInternal(State,
+                                    FAST486_REG_ES,
+                                    NewTss.Es,
+                                    FAST486_EXCEPTION_TS))
+    {
+        return FALSE;
+    }
+
+    if (!Fast486LoadSegmentInternal(State,
+                                    FAST486_REG_DS,
+                                    NewTss.Ds,
+                                    FAST486_EXCEPTION_TS))
+    {
+        return FALSE;
+    }
+
+    if (!Fast486LoadSegmentInternal(State,
+                                    FAST486_REG_FS,
+                                    NewTss.Fs,
+                                    FAST486_EXCEPTION_TS))
+    {
+        return FALSE;
+    }
+
+    if (!Fast486LoadSegmentInternal(State,
+                                    FAST486_REG_GS,
+                                    NewTss.Gs,
+                                    FAST486_EXCEPTION_TS))
+    {
+        return FALSE;
+    }
+
+    return TRUE;
 }
 
 /* EOF */