[FAST486]
[reactos.git] / reactos / lib / fast486 / common.c
index a16b591..5827339 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
@@ -32,6 +32,7 @@
 /* PUBLIC FUNCTIONS ***********************************************************/
 
 BOOLEAN
+FASTCALL
 Fast486ReadMemory(PFAST486_STATE State,
                   FAST486_SEG_REGS SegmentReg,
                   ULONG Offset,
@@ -47,15 +48,27 @@ Fast486ReadMemory(PFAST486_STATE State,
     /* Get the cached descriptor */
     CachedDescriptor = &State->SegmentRegs[SegmentReg];
 
-    if ((Offset + Size - 1) > CachedDescriptor->Limit)
+    if (InstFetch || CachedDescriptor->Executable || !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[FAST486_REG_CR0] & FAST486_CR0_PE)
+    if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) && !State->Flags.Vm)
     {
         /* Privilege checks */
 
@@ -111,7 +124,7 @@ Fast486ReadMemory(PFAST486_STATE State,
             {
                 /* We can't prefetch without possibly violating page permissions */
                 State->PrefetchValid = FALSE;
-                return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size);
+                return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size, TRUE);
             }
         }
 
@@ -119,7 +132,8 @@ Fast486ReadMemory(PFAST486_STATE State,
         if (Fast486ReadLinearMemory(State,
                                     State->PrefetchAddress,
                                     State->PrefetchCache,
-                                    FAST486_CACHE_SIZE))
+                                    FAST486_CACHE_SIZE,
+                                    TRUE))
         {
             State->PrefetchValid = TRUE;
 
@@ -138,11 +152,12 @@ Fast486ReadMemory(PFAST486_STATE State,
 #endif
     {
         /* Read from the linear address */
-        return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size);
+        return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size, TRUE);
     }
 }
 
 BOOLEAN
+FASTCALL
 Fast486WriteMemory(PFAST486_STATE State,
                    FAST486_SEG_REGS SegmentReg,
                    ULONG Offset,
@@ -157,15 +172,27 @@ Fast486WriteMemory(PFAST486_STATE State,
     /* Get the cached descriptor */
     CachedDescriptor = &State->SegmentRegs[SegmentReg];
 
-    if ((Offset + Size - 1) > CachedDescriptor->Limit)
+    if (CachedDescriptor->Executable || !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[FAST486_REG_CR0] & FAST486_CR0_PE)
+    if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) && !State->Flags.Vm)
     {
         /* Privilege checks */
 
@@ -212,7 +239,7 @@ Fast486WriteMemory(PFAST486_STATE State,
 #endif
 
     /* Write to the linear address */
-    return Fast486WriteLinearMemory(State, LinearAddress, Buffer, Size);
+    return Fast486WriteLinearMemory(State, LinearAddress, Buffer, Size, TRUE);
 }
 
 static inline BOOLEAN
@@ -229,7 +256,8 @@ Fast486GetIntVector(PFAST486_STATE State,
                                      State->Idtr.Address
                                      + Number * sizeof(*IdtEntry),
                                      IdtEntry,
-                                     sizeof(*IdtEntry)))
+                                     sizeof(*IdtEntry),
+                                     FALSE))
         {
             /* Exception occurred */
             return FALSE;
@@ -264,87 +292,100 @@ Fast486GetIntVector(PFAST486_STATE State,
 static inline BOOLEAN
 FASTCALL
 Fast486InterruptInternal(PFAST486_STATE State,
-                         PFAST486_IDT_ENTRY IdtEntry)
+                         PFAST486_IDT_ENTRY IdtEntry,
+                         BOOLEAN PushErrorCode,
+                         ULONG ErrorCode)
 {
-    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;
+    BOOLEAN GateSize = (IdtEntry->Type == FAST486_IDT_INT_GATE_32) ||
+                       (IdtEntry->Type == FAST486_IDT_TRAP_GATE_32);
+    USHORT OldCs = State->SegmentRegs[FAST486_REG_CS].Selector;
+    ULONG OldEip = State->InstPtr.Long;
+    ULONG OldFlags = State->Flags.Long;
+    UCHAR OldCpl = State->Cpl;
 
     /* Check for protected mode */
     if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
     {
-        FAST486_TSS Tss;
         USHORT OldSs = State->SegmentRegs[FAST486_REG_SS].Selector;
         ULONG OldEsp = State->GeneralRegs[FAST486_REG_ESP].Long;
+        BOOLEAN OldVm = State->Flags.Vm;
 
-        if (GateType == FAST486_TASK_GATE_SIGNATURE)
+        if (IdtEntry->Type == 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;
-        }
 
-        /* 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 ((OldCpl > GET_SEGMENT_RPL(IdtEntry->Selector)) || State->Flags.Vm)
         {
+            FAST486_TSS Tss;
+            PFAST486_LEGACY_TSS LegacyTss = (PFAST486_LEGACY_TSS)&Tss;
+            USHORT NewSs;
+            ULONG NewEsp;
+
             /* Read the TSS */
             if (!Fast486ReadLinearMemory(State,
                                          State->TaskReg.Base,
                                          &Tss,
-                                         sizeof(Tss)))
+                                         State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1)
+                                         ? sizeof(FAST486_TSS) : sizeof(FAST486_LEGACY_TSS),
+                                         FALSE))
             {
                 /* Exception occurred */
-                goto Cleanup;
+                return FALSE;
             }
 
+            /* Switch to the new privilege level */
+            State->Cpl = GET_SEGMENT_RPL(IdtEntry->Selector);
+
             /* Check the new (higher) privilege level */
-            switch (GET_SEGMENT_RPL(SegmentSelector))
+            switch (State->Cpl)
             {
                 case 0:
                 {
-                    if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss0))
+                    if (State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1))
                     {
-                        /* Exception occurred */
-                        goto Cleanup;
+                        NewSs = Tss.Ss0;
+                        NewEsp = Tss.Esp0;
+                    }
+                    else
+                    {
+                        NewSs = LegacyTss->Ss0;
+                        NewEsp = LegacyTss->Sp0;
                     }
-                    State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp0;
 
                     break;
                 }
 
                 case 1:
                 {
-                    if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss1))
+                    if (State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1))
+                    {
+                        NewSs = Tss.Ss1;
+                        NewEsp = Tss.Esp1;
+                    }
+                    else
                     {
-                        /* Exception occurred */
-                        goto Cleanup;
+                        NewSs = LegacyTss->Ss1;
+                        NewEsp = LegacyTss->Sp1;
                     }
-                    State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp1;
 
                     break;
                 }
 
                 case 2:
                 {
-                    if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss2))
+                    if (State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1))
                     {
-                        /* Exception occurred */
-                        goto Cleanup;
+                        NewSs = Tss.Ss2;
+                        NewEsp = Tss.Esp2;
+                    }
+                    else
+                    {
+                        NewSs = LegacyTss->Ss2;
+                        NewEsp = LegacyTss->Sp2;
                     }
-                    State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp2;
 
                     break;
                 }
@@ -356,62 +397,126 @@ Fast486InterruptInternal(PFAST486_STATE State,
                 }
             }
 
+            if (!Fast486LoadSegment(State, FAST486_REG_SS, NewSs))
+            {
+                /* Exception occurred */
+                return FALSE;
+            }
+
+            State->GeneralRegs[FAST486_REG_ESP].Long = NewEsp;
+
+            if (State->Flags.Vm)
+            {
+                /* Clear the VM flag */
+                State->Flags.Vm = FALSE;
+            }
+        }
+
+        /* Load new CS */
+        if (!Fast486LoadSegment(State, FAST486_REG_CS, IdtEntry->Selector))
+        {
+            /* An exception occurred during the jump */
+            return FALSE;
+        }
+
+        if (GateSize)
+        {
+            /* 32-bit code segment, use EIP */
+            State->InstPtr.Long = MAKELONG(IdtEntry->Offset, IdtEntry->OffsetHigh);
+        }
+        else
+        {
+            /* 16-bit code segment, use IP */
+            State->InstPtr.LowWord = IdtEntry->Offset;
+        }
+
+        /* Clear NT */
+       State->Flags.Nt = FALSE;
+
+        if (OldVm)
+        {
+            /* Push GS, FS, DS and ES */
+            if (!Fast486StackPushInternal(State,
+                                          GateSize,
+                                          State->SegmentRegs[FAST486_REG_GS].Selector))
+            {
+                return FALSE;
+            }
+            if (!Fast486StackPushInternal(State,
+                                          GateSize,
+                                          State->SegmentRegs[FAST486_REG_FS].Selector))
+            {
+                return FALSE;
+            }
+            if (!Fast486StackPushInternal(State,
+                                          GateSize,
+                                          State->SegmentRegs[FAST486_REG_DS].Selector))
+            {
+                return FALSE;
+            }
+            if (!Fast486StackPushInternal(State,
+                                          GateSize,
+                                          State->SegmentRegs[FAST486_REG_ES].Selector))
+            {
+                return FALSE;
+            }
+
+            /* Now load them with NULL selectors, since they are useless in protected mode */
+            if (!Fast486LoadSegment(State, FAST486_REG_GS, 0)) return FALSE;
+            if (!Fast486LoadSegment(State, FAST486_REG_FS, 0)) return FALSE;
+            if (!Fast486LoadSegment(State, FAST486_REG_DS, 0)) return FALSE;
+            if (!Fast486LoadSegment(State, FAST486_REG_ES, 0)) return FALSE;
+        }
+
+        /* Check if the interrupt handler is more privileged or we're in VM86 mode (again) */
+        if ((OldCpl > GET_SEGMENT_RPL(IdtEntry->Selector)) || OldVm)
+        {
             /* Push SS selector */
-            if (!Fast486StackPush(State, OldSs)) goto Cleanup;
+            if (!Fast486StackPushInternal(State, GateSize, OldSs)) return FALSE;
 
-            /* Push stack pointer */
-            if (!Fast486StackPush(State, OldEsp)) goto Cleanup;
+            /* Push the stack pointer */
+            if (!Fast486StackPushInternal(State, GateSize, OldEsp)) return FALSE;
         }
     }
     else
     {
-        if (State->SegmentRegs[FAST486_REG_CS].Size)
+        /* Load new CS */
+        if (!Fast486LoadSegment(State, FAST486_REG_CS, IdtEntry->Selector))
         {
-            /* Set OPSIZE, because INT always pushes 16-bit values in real mode */
-            State->PrefixFlags |= FAST486_PREFIX_OPSIZE;
+            /* An exception occurred during the jump */
+            return FALSE;
         }
+
+        /* Set the new IP */
+        State->InstPtr.LowWord = IdtEntry->Offset;
     }
 
     /* Push EFLAGS */
-    if (!Fast486StackPush(State, State->Flags.Long)) goto Cleanup;
+    if (!Fast486StackPushInternal(State, GateSize, OldFlags)) return FALSE;
 
     /* Push CS selector */
-    if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_CS].Selector)) goto Cleanup;
+    if (!Fast486StackPushInternal(State, GateSize, OldCs)) return FALSE;
 
     /* Push the instruction pointer */
-    if (!Fast486StackPush(State, State->InstPtr.Long)) goto Cleanup;
-
-    if ((GateType == FAST486_IDT_INT_GATE) || (GateType == FAST486_IDT_INT_GATE_32))
-    {
-        /* Disable interrupts after a jump to an interrupt gate handler */
-        State->Flags.If = FALSE;
-    }
+    if (!Fast486StackPushInternal(State, GateSize, OldEip)) return FALSE;
 
-    /* Load new CS */
-    if (!Fast486LoadSegment(State, FAST486_REG_CS, SegmentSelector))
+    if (PushErrorCode)
     {
-        /* An exception occurred during the jump */
-        goto Cleanup;
+        /* Push the error code */
+        if (!Fast486StackPushInternal(State, GateSize, ErrorCode)) return FALSE;
     }
 
-    if (GateSize)
-    {
-        /* 32-bit code segment, use EIP */
-        State->InstPtr.Long = Offset;
-    }
-    else
+    if ((IdtEntry->Type == FAST486_IDT_INT_GATE)
+        || (IdtEntry->Type == FAST486_IDT_INT_GATE_32))
     {
-        /* 16-bit code segment, use IP */
-        State->InstPtr.LowWord = LOWORD(Offset);
+        /* Disable interrupts after a jump to an interrupt gate handler */
+        State->Flags.If = FALSE;
     }
 
-    Success = TRUE;
+    /* Clear TF */
+    State->Flags.Tf = FALSE;
 
-Cleanup:
-    /* Restore the prefix flags */
-    State->PrefixFlags = OldPrefixFlags;
-
-    return Success;
+    return TRUE;
 }
 
 BOOLEAN
@@ -429,7 +534,7 @@ Fast486PerformInterrupt(PFAST486_STATE State,
     }
 
     /* Perform the interrupt */
-    if (!Fast486InterruptInternal(State, &IdtEntry))
+    if (!Fast486InterruptInternal(State, &IdtEntry, FALSE, 0))
     {
         /* Exception occurred */
         return FALSE;
@@ -444,6 +549,8 @@ Fast486ExceptionWithErrorCode(PFAST486_STATE State,
                               FAST486_EXCEPTIONS ExceptionCode,
                               ULONG ErrorCode)
 {
+    FAST486_IDT_ENTRY IdtEntry;
+
     /* Increment the exception count */
     State->ExceptionCount++;
 
@@ -466,11 +573,17 @@ Fast486ExceptionWithErrorCode(PFAST486_STATE State,
         return;
     }
 
+    /* Clear the prefix flags */
+    State->PrefixFlags = 0;
+
     /* Restore the IP to the saved IP */
     State->InstPtr = State->SavedInstPtr;
 
-    /* Perform the interrupt */
-    if (!Fast486PerformInterrupt(State, ExceptionCode))
+    /* Restore the SP to the saved SP */
+    State->GeneralRegs[FAST486_REG_ESP] = State->SavedStackPtr;
+
+    /* Get the interrupt vector */
+    if (!Fast486GetIntVector(State, ExceptionCode, &IdtEntry))
     {
         /*
          * If this function failed, that means Fast486Exception
@@ -479,18 +592,18 @@ Fast486ExceptionWithErrorCode(PFAST486_STATE State,
         return;
     }
 
-    if (EXCEPTION_HAS_ERROR_CODE(ExceptionCode)
-        && (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE))
+    /* Perform the interrupt */
+    if (!Fast486InterruptInternal(State,
+                                  &IdtEntry,
+                                  EXCEPTION_HAS_ERROR_CODE(ExceptionCode)
+                                  && (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE),
+                                  ErrorCode))
     {
-        /* Push the error code */
-        if (!Fast486StackPush(State, ErrorCode))
-        {
-            /*
-             * If this function failed, that means Fast486Exception
-             * was called again, so just return in this case.
-             */
-            return;
-        }
+        /*
+         * If this function failed, that means Fast486Exception
+         * was called again, so just return in this case.
+         */
+        return;
     }
 
     /* Reset the exception count */
@@ -503,27 +616,45 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
 {
     ULONG NewTssAddress;
     ULONG NewTssLimit;
+    FAST486_SYSTEM_DESCRIPTOR NewTssDescriptor;
     FAST486_TSS OldTss;
+    PFAST486_LEGACY_TSS OldLegacyTss = (PFAST486_LEGACY_TSS)&OldTss;
     FAST486_TSS NewTss;
-    FAST486_SYSTEM_DESCRIPTOR NewTssDescriptor;
+    PFAST486_LEGACY_TSS NewLegacyTss = (PFAST486_LEGACY_TSS)&NewTss;
+    USHORT NewLdtr, NewEs, NewCs, NewSs, NewDs;
+
+    if (State->TaskReg.Limit < (sizeof(FAST486_TSS) - 1)
+        && State->TaskReg.Limit != (sizeof(FAST486_LEGACY_TSS) - 1))
+    {
+        /* Invalid task register limit */
+        Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, State->TaskReg.Selector);
+        return FALSE;
+    }
 
     /* Read the old TSS */
     if (!Fast486ReadLinearMemory(State,
                                  State->TaskReg.Base,
                                  &OldTss,
-                                 sizeof(OldTss)))
+                                 State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1)
+                                 ? sizeof(FAST486_TSS) : sizeof(FAST486_LEGACY_TSS),
+                                 FALSE))
     {
         /* Exception occurred */
         return FALSE;
     }
 
+
     /* If this is a task return, use the linked previous selector */
-    if (Type == FAST486_TASK_RETURN) Selector = LOWORD(OldTss.Link);
+    if (Type == FAST486_TASK_RETURN)
+    {
+        if (State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1)) Selector = LOWORD(OldTss.Link);
+        else Selector = OldLegacyTss->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 + 1))
+        || GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1u))
     {
         Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector);
         return FALSE;
@@ -533,7 +664,8 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
     if (!Fast486ReadLinearMemory(State,
                                  State->Gdtr.Address + GET_SEGMENT_INDEX(Selector),
                                  &NewTssDescriptor,
-                                 sizeof(NewTssDescriptor)))
+                                 sizeof(NewTssDescriptor),
+                                 FALSE))
     {
         /* Exception occurred */
         return FALSE;
@@ -560,22 +692,26 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
         NewTssLimit |= 0x00000FFF;
     }
 
-    if (NewTssLimit < sizeof(FAST486_TSS))
+    if (NewTssLimit < (sizeof(FAST486_TSS) - 1)
+        && NewTssLimit != (sizeof(FAST486_LEGACY_TSS) - 1))
     {
-        /* TSS limit too small */
-        Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector); 
+        /* TSS limit invalid */
+        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)
+    if ((((NewTssDescriptor.Signature != FAST486_TSS_SIGNATURE)
+        && (NewTssDescriptor.Signature != FAST486_TSS_16_SIGNATURE))
         || (Type == FAST486_TASK_RETURN))
-        && ((NewTssDescriptor.Signature != FAST486_BUSY_TSS_SIGNATURE)
+        && (((NewTssDescriptor.Signature != FAST486_BUSY_TSS_SIGNATURE)
+        && (NewTssDescriptor.Signature != FAST486_BUSY_TSS_16_SIGNATURE))
         || (Type != FAST486_TASK_RETURN)))
     {
-        Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); 
+        Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
         return FALSE;
     }
 
@@ -583,7 +719,9 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
     if (!Fast486ReadLinearMemory(State,
                                  NewTssAddress,
                                  &NewTss,
-                                 sizeof(NewTss)))
+                                 NewTssLimit >= (sizeof(FAST486_TSS) - 1)
+                                 ? sizeof(FAST486_TSS) : sizeof(FAST486_LEGACY_TSS),
+                                 FALSE))
     {
         /* Exception occurred */
         return FALSE;
@@ -598,7 +736,8 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
                                      State->Gdtr.Address
                                      + GET_SEGMENT_INDEX(State->TaskReg.Selector),
                                      &OldTssDescriptor,
-                                     sizeof(OldTssDescriptor)))
+                                     sizeof(OldTssDescriptor),
+                                     FALSE))
         {
             /* Exception occurred */
             return FALSE;
@@ -610,7 +749,8 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
                                       State->Gdtr.Address
                                       + GET_SEGMENT_INDEX(State->TaskReg.Selector),
                                       &OldTssDescriptor,
-                                      sizeof(OldTssDescriptor)))
+                                      sizeof(OldTssDescriptor),
+                                      FALSE))
         {
             /* Exception occurred */
             return FALSE;
@@ -619,34 +759,58 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
     else
     {
         /* Store the link */
-        NewTss.Link = State->TaskReg.Selector;
+        if (NewTssLimit >= (sizeof(FAST486_TSS) - 1)) NewTss.Link = State->TaskReg.Selector;
+        else NewLegacyTss->Link = State->TaskReg.Selector;
     }
 
     /* 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;
+    if (State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1))
+    {
+        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;
+    }
+    else
+    {
+        OldLegacyTss->Ip = State->InstPtr.LowWord;
+        OldLegacyTss->Flags = State->Flags.LowWord;
+        OldLegacyTss->Ax = State->GeneralRegs[FAST486_REG_EAX].LowWord;
+        OldLegacyTss->Cx = State->GeneralRegs[FAST486_REG_ECX].LowWord;
+        OldLegacyTss->Dx = State->GeneralRegs[FAST486_REG_EDX].LowWord;
+        OldLegacyTss->Bx = State->GeneralRegs[FAST486_REG_EBX].LowWord;
+        OldLegacyTss->Sp = State->GeneralRegs[FAST486_REG_ESP].LowWord;
+        OldLegacyTss->Bp = State->GeneralRegs[FAST486_REG_EBP].LowWord;
+        OldLegacyTss->Si = State->GeneralRegs[FAST486_REG_ESI].LowWord;
+        OldLegacyTss->Di = State->GeneralRegs[FAST486_REG_EDI].LowWord;
+        OldLegacyTss->Es = State->SegmentRegs[FAST486_REG_ES].Selector;
+        OldLegacyTss->Cs = State->SegmentRegs[FAST486_REG_CS].Selector;
+        OldLegacyTss->Ss = State->SegmentRegs[FAST486_REG_SS].Selector;
+        OldLegacyTss->Ds = State->SegmentRegs[FAST486_REG_DS].Selector;
+        OldLegacyTss->Ldtr = State->Ldtr.Selector;
+    }
 
     /* Write back the old TSS */
     if (!Fast486WriteLinearMemory(State,
                                   State->TaskReg.Base,
                                   &OldTss,
-                                  sizeof(OldTss)))
+                                  State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1)
+                                  ? sizeof(FAST486_TSS) : sizeof(FAST486_LEGACY_TSS),
+                                  FALSE))
     {
         /* Exception occurred */
         return FALSE;
@@ -659,7 +823,8 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
     if (!Fast486WriteLinearMemory(State,
                                   State->Gdtr.Address + GET_SEGMENT_INDEX(Selector),
                                   &NewTssDescriptor,
-                                  sizeof(NewTssDescriptor)))
+                                  sizeof(NewTssDescriptor),
+                                  FALSE))
     {
         /* Exception occurred */
         return FALSE;
@@ -673,11 +838,18 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
     State->TaskReg.Base = NewTssAddress;
     State->TaskReg.Limit = NewTssLimit;
 
-    /* Change the page directory */
-    State->ControlRegisters[FAST486_REG_CR3] = NewTss.Cr3;
+    if (NewTssLimit >= (sizeof(FAST486_TSS) - 1))
+    {
+        /* 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));
+    Fast486FlushTlb(State);
+
+    /* Update the CPL */
+    if (NewTssLimit >= (sizeof(FAST486_TSS) - 1)) State->Cpl = GET_SEGMENT_RPL(NewTss.Cs);
+    else State->Cpl = GET_SEGMENT_RPL(NewLegacyTss->Cs);
 
 #ifndef FAST486_NO_PREFETCH
     /* Context switching invalidates the prefetch */
@@ -685,36 +857,59 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
 #endif
 
     /* 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;
+    if (NewTssLimit >= (sizeof(FAST486_TSS) - 1))
+    {
+        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;
+        NewEs = NewTss.Es;
+        NewCs = NewTss.Cs;
+        NewSs = NewTss.Ss;
+        NewDs = NewTss.Ds;
+        NewLdtr = NewTss.Ldtr;
+    }
+    else
+    {
+        State->InstPtr.LowWord = State->SavedInstPtr.LowWord = NewLegacyTss->Ip;
+        State->Flags.LowWord = NewLegacyTss->Flags;
+        State->GeneralRegs[FAST486_REG_EAX].LowWord = NewLegacyTss->Ax;
+        State->GeneralRegs[FAST486_REG_ECX].LowWord = NewLegacyTss->Cx;
+        State->GeneralRegs[FAST486_REG_EDX].LowWord = NewLegacyTss->Dx;
+        State->GeneralRegs[FAST486_REG_EBX].LowWord = NewLegacyTss->Bx;
+        State->GeneralRegs[FAST486_REG_ESP].LowWord = NewLegacyTss->Sp;
+        State->GeneralRegs[FAST486_REG_EBP].LowWord = NewLegacyTss->Bp;
+        State->GeneralRegs[FAST486_REG_ESI].LowWord = NewLegacyTss->Si;
+        State->GeneralRegs[FAST486_REG_EDI].LowWord = NewLegacyTss->Di;
+        NewEs = NewLegacyTss->Es;
+        NewCs = NewLegacyTss->Cs;
+        NewSs = NewLegacyTss->Ss;
+        NewDs = NewLegacyTss->Ds;
+        NewLdtr = NewLegacyTss->Ldtr;
+    }
 
     /* Set the NT flag if nesting */
     if (Type == FAST486_TASK_CALL) State->Flags.Nt = TRUE;
 
-    if (GET_SEGMENT_INDEX(NewTss.Ldtr) != 0)
+    if (GET_SEGMENT_INDEX(NewLdtr) != 0)
     {
         BOOLEAN Valid;
         FAST486_SYSTEM_DESCRIPTOR GdtEntry;
 
-        if (NewTss.Ldtr & SEGMENT_TABLE_INDICATOR)
+        if (NewLdtr & SEGMENT_TABLE_INDICATOR)
         {
             /* This selector doesn't point to the GDT */
-            Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
+            Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewLdtr);
             return FALSE;
         }
 
-        if (!Fast486ReadDescriptorEntry(State,
-                                        NewTss.Ldtr,
-                                        &Valid,
-                                        (PFAST486_GDT_ENTRY)&GdtEntry))
+        if (!Fast486ReadDescriptorEntry(State, NewLdtr, &Valid, (PFAST486_GDT_ENTRY)&GdtEntry))
         {
             /* Exception occurred */
             return FALSE;
@@ -723,25 +918,25 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
         if (!Valid)
         {
             /* Invalid selector */
-            Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
+            Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewLdtr);
             return FALSE;
         }
 
         if (GdtEntry.Signature != FAST486_LDT_SIGNATURE)
         {
             /* This is not an LDT descriptor */
-            Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
+            Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewLdtr);
             return FALSE;
         }
 
         if (!GdtEntry.Present)
         {
-            Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
+            Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewLdtr);
             return FALSE;
         }
 
         /* Update the LDTR */
-        State->Ldtr.Selector = NewTss.Ldtr;
+        State->Ldtr.Selector = NewLdtr;
         State->Ldtr.Base = GdtEntry.Base | (GdtEntry.BaseMid << 16) | (GdtEntry.BaseHigh << 24);
         State->Ldtr.Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16);
 
@@ -758,54 +953,244 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
     }
 
     /* Load the new segments */
-    if (!Fast486LoadSegmentInternal(State,
-                                    FAST486_REG_CS,
-                                    NewTss.Cs,
-                                    FAST486_EXCEPTION_TS))
+    if (!Fast486LoadSegmentInternal(State, FAST486_REG_CS, NewCs, FAST486_EXCEPTION_TS))
+    {
+        return FALSE;
+    }
+
+    if (!Fast486LoadSegmentInternal(State, FAST486_REG_SS, NewSs, FAST486_EXCEPTION_TS))
     {
         return FALSE;
     }
 
-    if (!Fast486LoadSegmentInternal(State,
-                                    FAST486_REG_SS,
-                                    NewTss.Ss,
-                                    FAST486_EXCEPTION_TS))
+    if (!Fast486LoadSegmentInternal(State, FAST486_REG_ES, NewEs, FAST486_EXCEPTION_TS))
     {
         return FALSE;
     }
 
-    if (!Fast486LoadSegmentInternal(State,
-                                    FAST486_REG_ES,
-                                    NewTss.Es,
-                                    FAST486_EXCEPTION_TS))
+    if (!Fast486LoadSegmentInternal(State, FAST486_REG_DS, NewDs, FAST486_EXCEPTION_TS))
     {
         return FALSE;
     }
 
-    if (!Fast486LoadSegmentInternal(State,
-                                    FAST486_REG_DS,
-                                    NewTss.Ds,
-                                    FAST486_EXCEPTION_TS))
+    if (NewTssLimit >= (sizeof(FAST486_TSS) - 1))
+    {
+        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;
+}
+
+BOOLEAN
+FASTCALL
+Fast486CallGate(PFAST486_STATE State,
+                PFAST486_CALL_GATE Gate,
+                BOOLEAN Call)
+{
+    BOOLEAN Valid;
+    FAST486_GDT_ENTRY NewCodeSegment;
+    BOOLEAN GateSize = (Gate->Type == FAST486_CALL_GATE_SIGNATURE);
+    FAST486_TSS Tss;
+    PFAST486_LEGACY_TSS LegacyTss = (PFAST486_LEGACY_TSS)&Tss;
+    USHORT OldCs = State->SegmentRegs[FAST486_REG_CS].Selector;
+    ULONG OldEip = State->InstPtr.Long;
+    USHORT OldCpl = State->Cpl;
+    USHORT OldSs = State->SegmentRegs[FAST486_REG_SS].Selector;
+    ULONG OldEsp = State->GeneralRegs[FAST486_REG_ESP].Long;
+    ULONG ParamBuffer[32]; /* Maximum possible size - 32 DWORDs */
+    PULONG LongParams = (PULONG)ParamBuffer;
+    PUSHORT ShortParams = (PUSHORT)ParamBuffer;
+
+    if (!Gate->Selector)
     {
+        /* The code segment is NULL */
+        Fast486Exception(State, FAST486_EXCEPTION_GP);
         return FALSE;
     }
 
-    if (!Fast486LoadSegmentInternal(State,
-                                    FAST486_REG_FS,
-                                    NewTss.Fs,
-                                    FAST486_EXCEPTION_TS))
+    if (!Fast486ReadDescriptorEntry(State, Gate->Selector, &Valid, &NewCodeSegment))
     {
+        /* Exception occurred */
         return FALSE;
     }
 
-    if (!Fast486LoadSegmentInternal(State,
-                                    FAST486_REG_GS,
-                                    NewTss.Gs,
-                                    FAST486_EXCEPTION_TS))
+    if (!Valid || (NewCodeSegment.Dpl > Fast486GetCurrentPrivLevel(State)))
     {
+        /* Code segment invalid */
+        Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Gate->Selector);
         return FALSE;
     }
 
+    if (Call && Gate->ParamCount)
+    {
+        /* Read the parameters */
+        if (!Fast486ReadMemory(State,
+                               FAST486_REG_SS,
+                               OldEsp,
+                               FALSE,
+                               ParamBuffer,
+                               Gate->ParamCount * (GateSize ? sizeof(ULONG) : sizeof(USHORT))))
+        {
+            /* Exception occurred */
+            return FALSE;
+        }
+    }
+
+    /* Check if the new code segment is more privileged */
+    if (NewCodeSegment.Dpl < OldCpl)
+    {
+        if (Call)
+        {
+            USHORT NewSs;
+            ULONG NewEsp;
+
+            /* Read the TSS */
+            if (!Fast486ReadLinearMemory(State,
+                                         State->TaskReg.Base,
+                                         &Tss,
+                                         State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1)
+                                         ? sizeof(FAST486_TSS) : sizeof(FAST486_LEGACY_TSS),
+                                         FALSE))
+            {
+                /* Exception occurred */
+                return FALSE;
+            }
+
+            /* Switch to the new privilege level */
+            State->Cpl = NewCodeSegment.Dpl;
+
+            /* Check the new (higher) privilege level */
+            switch (State->Cpl)
+            {
+                case 0:
+                {
+                    if (State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1))
+                    {
+                        NewSs = Tss.Ss0;
+                        NewEsp = Tss.Esp0;
+                    }
+                    else
+                    {
+                        NewSs = LegacyTss->Ss0;
+                        NewEsp = LegacyTss->Sp0;
+                    }
+
+                    break;
+                }
+
+                case 1:
+                {
+                    if (State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1))
+                    {
+                        NewSs = Tss.Ss1;
+                        NewEsp = Tss.Esp1;
+                    }
+                    else
+                    {
+                        NewSs = LegacyTss->Ss1;
+                        NewEsp = LegacyTss->Sp1;
+                    }
+
+                    break;
+                }
+
+                case 2:
+                {
+                    if (State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1))
+                    {
+                        NewSs = Tss.Ss2;
+                        NewEsp = Tss.Esp2;
+                    }
+                    else
+                    {
+                        NewSs = LegacyTss->Ss2;
+                        NewEsp = LegacyTss->Sp2;
+                    }
+
+                    break;
+                }
+
+                default:
+                {
+                    /* Should never reach here! */
+                    ASSERT(FALSE);
+                }
+            }
+
+            if (!Fast486LoadSegment(State, FAST486_REG_SS, NewSs))
+            {
+                /* Exception occurred */
+                return FALSE;
+            }
+
+            State->GeneralRegs[FAST486_REG_ESP].Long = NewEsp;
+        }
+        else if (!NewCodeSegment.DirConf)
+        {
+            /* This is not allowed for jumps */
+            Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Gate->Selector);
+            return FALSE;
+        }
+    }
+
+    /* Load new CS */
+    if (!Fast486LoadSegment(State, FAST486_REG_CS, Gate->Selector))
+    {
+        /* An exception occurred during the jump */
+        return FALSE;
+    }
+
+    /* Set the instruction pointer */
+    if (GateSize) State->InstPtr.Long = MAKELONG(Gate->Offset, Gate->OffsetHigh);
+    else State->InstPtr.Long = Gate->Offset;
+
+    if (Call)
+    {
+        INT i;
+
+        /* Check if the new code segment is more privileged (again) */
+        if (NewCodeSegment.Dpl < OldCpl)
+        {
+            /* Push SS selector */
+            if (!Fast486StackPushInternal(State, GateSize, OldSs)) return FALSE;
+
+            /* Push stack pointer */
+            if (!Fast486StackPushInternal(State, GateSize, OldEsp)) return FALSE;
+        }
+
+        /* Push the parameters in reverse order */
+        for (i = Gate->ParamCount - 1; i >= 0; i--)
+        {
+            if (!Fast486StackPushInternal(State,
+                                          GateSize,
+                                          GateSize ? LongParams[i] : ShortParams[i]))
+            {
+                /* Exception occurred */
+                return FALSE;
+            }
+        }
+
+        /* Push CS selector */
+        if (!Fast486StackPushInternal(State, GateSize, OldCs)) return FALSE;
+
+        /* Push the instruction pointer */
+        if (!Fast486StackPushInternal(State, GateSize, OldEip)) return FALSE;
+    }
+
     return TRUE;
 }