[NTVDM]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / dos / dos32krnl / dos.c
index a4a455d..b4028a1 100644 (file)
@@ -11,6 +11,7 @@
 
 #define NDEBUG
 
+#include "ntvdm.h"
 #include "emulator.h"
 #include "cpu/cpu.h"
 #include "int32.h"
 #include "dos.h"
 #include "dos/dem.h"
 #include "device.h"
+#include "handle.h"
+#include "dosfiles.h"
 #include "memory.h"
+#include "himem.h"
 
 #include "bios/bios.h"
 
 #include "io.h"
 #include "hardware/ps2.h"
 
+#include "emsdrv.h"
+
 /* PRIVATE VARIABLES **********************************************************/
 
+#define INDOS_POINTER MAKELONG(0x00FE, 0x0070)
+
 CALLBACK16 DosContext;
 
 static DWORD DiskTransferArea;
 /*static*/ BYTE CurrentDrive;
 static CHAR LastDrive = 'E';
 static CHAR CurrentDirectories[NUM_DRIVES][DOS_DIR_LENGTH];
-static DOS_SFT_ENTRY DosSystemFileTable[DOS_SFT_SIZE];
 static WORD DosErrorLevel = 0x0000;
+static PBYTE InDos;
 
 /* PUBLIC VARIABLES ***********************************************************/
 
+PDOS_SYSVARS SysVars;
+
 /* Echo state for INT 21h, AH = 01h and AH = 3Fh */
 BOOLEAN DoEcho = FALSE;
 WORD CurrentPsp = SYSTEM_PSP;
@@ -113,462 +123,6 @@ static WORD DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL,
     return DestSegment;
 }
 
-/* Taken from base/shell/cmd/console.c */
-static BOOL IsConsoleHandle(HANDLE hHandle)
-{
-    DWORD dwMode;
-
-    /* Check whether the handle may be that of a console... */
-    if ((GetFileType(hHandle) & FILE_TYPE_CHAR) == 0) return FALSE;
-
-    /*
-     * It may be. Perform another test... The idea comes from the
-     * MSDN description of the WriteConsole API:
-     *
-     * "WriteConsole fails if it is used with a standard handle
-     *  that is redirected to a file. If an application processes
-     *  multilingual output that can be redirected, determine whether
-     *  the output handle is a console handle (one method is to call
-     *  the GetConsoleMode function and check whether it succeeds).
-     *  If the handle is a console handle, call WriteConsole. If the
-     *  handle is not a console handle, the output is redirected and
-     *  you should call WriteFile to perform the I/O."
-     */
-    return GetConsoleMode(hHandle, &dwMode);
-}
-
-static inline PDOS_SFT_ENTRY DosFindFreeSftEntry(VOID)
-{
-    INT i;
-
-    for (i = 0; i < DOS_SFT_SIZE; i++)
-    {
-        if (DosSystemFileTable[i].Type == DOS_SFT_ENTRY_NONE)
-        {
-            return &DosSystemFileTable[i];
-        }
-    }
-
-    return NULL;
-}
-
-static inline PDOS_SFT_ENTRY DosFindWin32SftEntry(HANDLE Handle)
-{
-    INT i;
-
-    for (i = 0; i < DOS_SFT_SIZE; i++)
-    {
-        if (DosSystemFileTable[i].Type == DOS_SFT_ENTRY_WIN32
-            && DosSystemFileTable[i].Handle == Handle)
-        {
-            return &DosSystemFileTable[i];
-        }
-    }
-
-    return NULL;
-}
-
-static inline PDOS_SFT_ENTRY DosFindDeviceSftEntry(PDOS_DEVICE_NODE Device)
-{
-    INT i;
-
-    for (i = 0; i < DOS_SFT_SIZE; i++)
-    {
-        if (DosSystemFileTable[i].Type == DOS_SFT_ENTRY_DEVICE
-            && DosSystemFileTable[i].DeviceNode == Device)
-        {
-            return &DosSystemFileTable[i];
-        }
-    }
-
-    return NULL;
-}
-
-WORD DosOpenHandle(HANDLE Handle)
-{
-    WORD DosHandle;
-    PDOS_PSP PspBlock;
-    LPBYTE HandleTable;
-    PDOS_SFT_ENTRY SftEntry;
-
-    /* The system PSP has no handle table */
-    if (CurrentPsp == SYSTEM_PSP) return INVALID_DOS_HANDLE;
-
-    /* Get a pointer to the handle table */
-    PspBlock = SEGMENT_TO_PSP(CurrentPsp);
-    HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
-
-    /* Find a free entry in the JFT */
-    for (DosHandle = 0; DosHandle < PspBlock->HandleTableSize; DosHandle++)
-    {
-        if (HandleTable[DosHandle] == 0xFF) break;
-    }
-
-    /* If there are no free entries, fail */
-    if (DosHandle == PspBlock->HandleTableSize) return INVALID_DOS_HANDLE;
-
-    /* Check if the handle is already in the SFT */
-    SftEntry = DosFindWin32SftEntry(Handle);
-
-    if (SftEntry != NULL)
-    {
-        /* Already in the table, reference it */
-        SftEntry->RefCount++;
-        goto Finish;
-    }
-
-    /* Find a free SFT entry to use */
-    SftEntry = DosFindFreeSftEntry();
-    if (SftEntry == NULL)
-    {
-        /* The SFT is full */
-        return INVALID_DOS_HANDLE;
-    }
-
-    /* Initialize the empty table entry */
-    SftEntry->Type       = DOS_SFT_ENTRY_WIN32;
-    SftEntry->Handle     = Handle;
-    SftEntry->RefCount   = 1;
-
-Finish:
-
-    /* Set the JFT entry to that SFT index */
-    HandleTable[DosHandle] = ARRAY_INDEX(SftEntry, DosSystemFileTable);
-
-    /* Return the new handle */
-    return DosHandle;
-}
-
-WORD DosOpenDevice(PDOS_DEVICE_NODE Device)
-{
-    WORD DosHandle;
-    PDOS_PSP PspBlock;
-    LPBYTE HandleTable;
-    PDOS_SFT_ENTRY SftEntry;
-
-    DPRINT("DosOpenDevice(\"%Z\")\n", &Device->Name);
-
-    /* The system PSP has no handle table */
-    if (CurrentPsp == SYSTEM_PSP) return INVALID_DOS_HANDLE;
-
-    /* Get a pointer to the handle table */
-    PspBlock = SEGMENT_TO_PSP(CurrentPsp);
-    HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
-
-    /* Find a free entry in the JFT */
-    for (DosHandle = 0; DosHandle < PspBlock->HandleTableSize; DosHandle++)
-    {
-        if (HandleTable[DosHandle] == 0xFF) break;
-    }
-
-    /* If there are no free entries, fail */
-    if (DosHandle == PspBlock->HandleTableSize) return INVALID_DOS_HANDLE;
-
-    /* Check if the device is already in the SFT */
-    SftEntry = DosFindDeviceSftEntry(Device);
-
-    if (SftEntry != NULL)
-    {
-        /* Already in the table, reference it */
-        SftEntry->RefCount++;
-        goto Finish;
-    }
-
-    /* Find a free SFT entry to use */
-    SftEntry = DosFindFreeSftEntry();
-    if (SftEntry == NULL)
-    {
-        /* The SFT is full */
-        return INVALID_DOS_HANDLE;
-    }
-
-    /* Initialize the empty table entry */
-    SftEntry->Type       = DOS_SFT_ENTRY_DEVICE;
-    SftEntry->DeviceNode = Device;
-    SftEntry->RefCount   = 1;
-
-Finish:
-
-    /* Call the open routine, if it exists */
-    if (Device->OpenRoutine) Device->OpenRoutine(Device);
-
-    /* Set the JFT entry to that SFT index */
-    HandleTable[DosHandle] = ARRAY_INDEX(SftEntry, DosSystemFileTable);
-
-    /* Return the new handle */
-    return DosHandle;
-}
-
-static VOID DosCopyHandleTable(LPBYTE DestinationTable)
-{
-    INT i;
-    PDOS_PSP PspBlock;
-    LPBYTE SourceTable;
-
-    /* Clear the table first */
-    for (i = 0; i < 20; i++) DestinationTable[i] = 0xFF;
-
-    /* Check if this is the initial process */
-    if (CurrentPsp == SYSTEM_PSP)
-    {
-        PDOS_SFT_ENTRY SftEntry;
-        HANDLE StandardHandles[3];
-        PDOS_DEVICE_NODE ConIn = DosGetDevice("CONIN$");
-        PDOS_DEVICE_NODE ConOut = DosGetDevice("CONOUT$");
-        ASSERT(ConIn != NULL && ConOut != NULL);
-
-        /* Get the native standard handles */
-        StandardHandles[0] = GetStdHandle(STD_INPUT_HANDLE);
-        StandardHandles[1] = GetStdHandle(STD_OUTPUT_HANDLE);
-        StandardHandles[2] = GetStdHandle(STD_ERROR_HANDLE);
-
-        for (i = 0; i < 3; i++)
-        {
-            PDOS_DEVICE_NODE Device = (i == 0) ? ConIn : ConOut;
-
-            /* Find the corresponding SFT entry */
-            if (IsConsoleHandle(StandardHandles[i]))
-            {
-                SftEntry = DosFindDeviceSftEntry(Device);
-            }
-            else
-            {
-                SftEntry = DosFindWin32SftEntry(StandardHandles[i]);
-            }
-
-            if (SftEntry == NULL)
-            {
-                /* Create a new SFT entry for it */
-                SftEntry = DosFindFreeSftEntry();
-
-                if (SftEntry == NULL)
-                {
-                    DPRINT1("Cannot create standard handle %d, the SFT is full!\n", i);
-                    continue;
-                }
-
-                SftEntry->RefCount = 0;
-
-                if (IsConsoleHandle(StandardHandles[i]))
-                {
-                    SftEntry->Type = DOS_SFT_ENTRY_DEVICE;
-                    SftEntry->DeviceNode = Device;
-
-                    /* Call the open routine */
-                    if (Device->OpenRoutine) Device->OpenRoutine(Device);
-                }
-                else
-                {
-                    SftEntry->Type = DOS_SFT_ENTRY_WIN32;
-                    SftEntry->Handle = StandardHandles[i];
-                }
-            }
-
-            SftEntry->RefCount++;
-            DestinationTable[i] = ARRAY_INDEX(SftEntry, DosSystemFileTable);
-        }
-    }
-    else
-    {
-        /* Get the parent PSP block and handle table */
-        PspBlock = SEGMENT_TO_PSP(CurrentPsp);
-        SourceTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
-
-        /* Copy the first 20 handles into the new table */
-        for (i = 0; i < DEFAULT_JFT_SIZE; i++)
-        {
-            DestinationTable[i] = SourceTable[i];
-
-            /* Increase the reference count */
-            DosSystemFileTable[SourceTable[i]].RefCount++;
-        }
-    }
-}
-
-static BOOLEAN DosResizeHandleTable(WORD NewSize)
-{
-    PDOS_PSP PspBlock;
-    LPBYTE HandleTable;
-    WORD Segment;
-
-    /* Get the PSP block */
-    PspBlock = SEGMENT_TO_PSP(CurrentPsp);
-
-    if (NewSize == PspBlock->HandleTableSize)
-    {
-        /* No change */
-        return TRUE;
-    }
-
-    if (PspBlock->HandleTableSize > DEFAULT_JFT_SIZE)
-    {
-        /* Get the segment of the current table */
-        Segment = (LOWORD(PspBlock->HandleTablePtr) >> 4) + HIWORD(PspBlock->HandleTablePtr);
-
-        if (NewSize <= DEFAULT_JFT_SIZE)
-        {
-            /* Get the current handle table */
-            HandleTable = FAR_POINTER(PspBlock->HandleTablePtr);
-
-            /* Copy it to the PSP */
-            RtlCopyMemory(PspBlock->HandleTable, HandleTable, NewSize);
-
-            /* Free the memory */
-            DosFreeMemory(Segment);
-
-            /* Update the handle table pointer and size */
-            PspBlock->HandleTableSize = NewSize;
-            PspBlock->HandleTablePtr = MAKELONG(0x18, CurrentPsp);
-        }
-        else
-        {
-            /* Resize the memory */
-            if (!DosResizeMemory(Segment, NewSize, NULL))
-            {
-                /* Unable to resize, try allocating it somewhere else */
-                Segment = DosAllocateMemory(NewSize, NULL);
-                if (Segment == 0) return FALSE;
-
-                /* Get the new handle table */
-                HandleTable = SEG_OFF_TO_PTR(Segment, 0);
-
-                /* Copy the handles to the new table */
-                RtlCopyMemory(HandleTable,
-                              FAR_POINTER(PspBlock->HandleTablePtr),
-                              PspBlock->HandleTableSize);
-
-                /* Update the handle table pointer */
-                PspBlock->HandleTablePtr = MAKELONG(0, Segment);
-            }
-
-            /* Update the handle table size */
-            PspBlock->HandleTableSize = NewSize;
-        }
-    }
-    else if (NewSize > DEFAULT_JFT_SIZE)
-    {
-        Segment = DosAllocateMemory(NewSize, NULL);
-        if (Segment == 0) return FALSE;
-
-        /* Get the new handle table */
-        HandleTable = SEG_OFF_TO_PTR(Segment, 0);
-
-        /* Copy the handles from the PSP to the new table */
-        RtlCopyMemory(HandleTable,
-                      FAR_POINTER(PspBlock->HandleTablePtr),
-                      PspBlock->HandleTableSize);
-
-        /* Update the handle table pointer and size */
-        PspBlock->HandleTableSize = NewSize;
-        PspBlock->HandleTablePtr = MAKELONG(0, Segment);
-    }
-
-    return TRUE;
-}
-
-static BOOLEAN DosCloseHandle(WORD DosHandle)
-{
-    PDOS_PSP PspBlock;
-    LPBYTE HandleTable;
-    PDOS_SFT_ENTRY SftEntry;
-
-    DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle);
-
-    /* The system PSP has no handle table */
-    if (CurrentPsp == SYSTEM_PSP) return FALSE;
-
-    /* Get a pointer to the handle table */
-    PspBlock = SEGMENT_TO_PSP(CurrentPsp);
-    HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
-
-    /* Make sure the handle is open */
-    if (HandleTable[DosHandle] == 0xFF) return FALSE;
-
-    /* Make sure the SFT entry is valid */
-    SftEntry = &DosSystemFileTable[HandleTable[DosHandle]];
-    if (SftEntry->Type == DOS_SFT_ENTRY_NONE) return FALSE;
-
-    /* Decrement the reference count of the SFT entry */
-    SftEntry->RefCount--;
-
-    /* Check if the reference count fell to zero */
-    if (!SftEntry->RefCount)
-    {
-        switch (SftEntry->Type)
-        {
-            case DOS_SFT_ENTRY_WIN32:
-            {
-                /* Close the win32 handle and clear it */
-                CloseHandle(SftEntry->Handle);
-
-                break;
-            }
-
-            case DOS_SFT_ENTRY_DEVICE:
-            {
-                PDOS_DEVICE_NODE Node = SftEntry->DeviceNode;
-
-                /* Call the close routine, if it exists */
-                if (Node->CloseRoutine) SftEntry->DeviceNode->CloseRoutine(SftEntry->DeviceNode);
-
-                break;
-            }
-
-            default:
-            {
-                /* Shouldn't happen */
-                ASSERT(FALSE);
-            }
-        }
-
-        /* Invalidate the SFT entry */
-        SftEntry->Type = DOS_SFT_ENTRY_NONE;
-    }
-
-    /* Clear the entry in the JFT */
-    HandleTable[DosHandle] = 0xFF;
-
-    return TRUE;
-}
-
-static BOOLEAN DosDuplicateHandle(WORD OldHandle, WORD NewHandle)
-{
-    BYTE SftIndex;
-    PDOS_PSP PspBlock;
-    LPBYTE HandleTable;
-
-    DPRINT("DosDuplicateHandle: OldHandle 0x%04X, NewHandle 0x%04X\n",
-           OldHandle,
-           NewHandle);
-
-    /* The system PSP has no handle table */
-    if (CurrentPsp == SYSTEM_PSP) return FALSE;
-
-    /* Get a pointer to the handle table */
-    PspBlock = SEGMENT_TO_PSP(CurrentPsp);
-    HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
-
-    /* Make sure the old handle is open */
-    if (HandleTable[OldHandle] == 0xFF) return FALSE;
-
-    /* Check if the new handle is open */
-    if (HandleTable[NewHandle] != 0xFF)
-    {
-        /* Close it */
-        DosCloseHandle(NewHandle);
-    }
-
-    /* Increment the reference count of the SFT entry */
-    SftIndex = HandleTable[OldHandle];
-    DosSystemFileTable[SftIndex].RefCount++;
-
-    /* Make the new handle point to that SFT entry */
-    HandleTable[NewHandle] = SftIndex;
-
-    /* Return success */
-    return TRUE;
-}
-
 static BOOLEAN DosChangeDrive(BYTE Drive)
 {
     WCHAR DirectoryPath[DOS_CMDLINE_LENGTH];
@@ -656,28 +210,29 @@ static BOOLEAN DosChangeDirectory(LPSTR Directory)
     return TRUE;
 }
 
-/* PUBLIC FUNCTIONS ***********************************************************/
-
-PDOS_SFT_ENTRY DosGetSftEntry(WORD DosHandle)
+static BOOLEAN DosControlBreak(VOID)
 {
-    PDOS_PSP PspBlock;
-    LPBYTE HandleTable;
+    setCF(0);
 
-    /* The system PSP has no handle table */
-    if (CurrentPsp == SYSTEM_PSP) return NULL;
+    /* Call interrupt 0x23 */
+    Int32Call(&DosContext, 0x23);
 
-    /* Get a pointer to the handle table */
-    PspBlock = SEGMENT_TO_PSP(CurrentPsp);
-    HandleTable = (LPBYTE)FAR_POINTER(PspBlock->HandleTablePtr);
-
-    /* Make sure the handle is open */
-    if (HandleTable[DosHandle] == 0xFF) return NULL;
+    if (getCF())
+    {
+        DosTerminateProcess(CurrentPsp, 0, 0);
+        return TRUE;
+    }
 
-    /* Return a pointer to the SFT entry */
-    return &DosSystemFileTable[HandleTable[DosHandle]];
+    return FALSE;
 }
 
-VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WORD Environment)
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+VOID DosInitializePsp(WORD PspSegment,
+                      LPCSTR CommandLine,
+                      WORD ProgramSize,
+                      WORD Environment,
+                      DWORD ReturnAddress)
 {
     PDOS_PSP PspBlock = SEGMENT_TO_PSP(PspSegment);
     LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
@@ -692,7 +247,7 @@ VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WOR
     PspBlock->LastParagraph = PspSegment + ProgramSize - 1;
 
     /* Save the interrupt vectors */
-    PspBlock->TerminateAddress = IntVecTable[0x22];
+    PspBlock->TerminateAddress = ReturnAddress;
     PspBlock->BreakAddress     = IntVecTable[0x23];
     PspBlock->CriticalAddress  = IntVecTable[0x24];
 
@@ -727,6 +282,7 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
                         IN LPCSTR ExecutablePath,
                         IN LPCSTR CommandLine,
                         IN LPCSTR Environment OPTIONAL,
+                        IN DWORD ReturnAddress OPTIONAL,
                         OUT PDWORD StackLocation OPTIONAL,
                         OUT PDWORD EntryPoint OPTIONAL)
 {
@@ -848,7 +404,8 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
         DosInitializePsp(Segment,
                          CommandLine,
                          (WORD)ExeSize,
-                         EnvBlock);
+                         EnvBlock,
+                         ReturnAddress);
 
         /* The process owns its own memory */
         DosChangeMemoryOwner(Segment, Segment);
@@ -926,7 +483,8 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
         DosInitializePsp(Segment,
                          CommandLine,
                          MaxAllocSize,
-                         EnvBlock);
+                         EnvBlock,
+                         ReturnAddress);
 
         if (LoadType == DOS_LOAD_AND_EXECUTE)
         {
@@ -976,18 +534,21 @@ DWORD DosStartProcess(IN LPCSTR ExecutablePath,
                       IN LPCSTR Environment OPTIONAL)
 {
     DWORD Result;
+    LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
 
     Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
                                ExecutablePath,
                                CommandLine,
                                Environment,
+                               IntVecTable[0x20], // Use INT 20h
                                NULL,
                                NULL);
 
     if (Result != ERROR_SUCCESS) goto Quit;
 
     /* Attach to the console */
-    VidBiosAttachToConsole(); // FIXME: And in fact, attach the full NTVDM UI to the console
+    ConsoleAttach();
+    VidBiosAttachToConsole();
 
     // HACK: Simulate a ENTER key release scancode on the PS/2 port because
     // some apps expect to read a key release scancode (> 0x80) when they
@@ -1000,7 +561,8 @@ DWORD DosStartProcess(IN LPCSTR ExecutablePath,
     CpuSimulate();
 
     /* Detach from the console */
-    VidBiosDetachFromConsole(); // FIXME: And in fact, detach the full NTVDM UI from the console
+    VidBiosDetachFromConsole();
+    ConsoleDetach();
 
 Quit:
     return Result;
@@ -1009,7 +571,8 @@ Quit:
 #ifndef STANDALONE
 WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
                       LPCSTR ProgramName,
-                      PDOS_EXEC_PARAM_BLOCK Parameters)
+                      PDOS_EXEC_PARAM_BLOCK Parameters,
+                      DWORD ReturnAddress)
 {
     DWORD Result;
     DWORD BinaryType;
@@ -1020,7 +583,8 @@ WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
     CHAR PifFile[MAX_PATH];
     CHAR Desktop[MAX_PATH];
     CHAR Title[MAX_PATH];
-    CHAR Env[MAX_PATH];
+    ULONG EnvSize = 256;
+    PVOID Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
     STARTUPINFOA StartupInfo;
     PROCESS_INFORMATION ProcessInfo;
 
@@ -1077,31 +641,44 @@ WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
             CommandInfo.Title = Title;
             CommandInfo.TitleLen = sizeof(Title);
             CommandInfo.Env = Env;
-            CommandInfo.EnvLen = sizeof(Env);
+            CommandInfo.EnvLen = EnvSize;
 
+Command:
             /* Get the VDM command information */
             if (!GetNextVDMCommand(&CommandInfo))
             {
+                if (CommandInfo.EnvLen > EnvSize)
+                {
+                    /* Expand the environment size */
+                    EnvSize = CommandInfo.EnvLen;
+                    CommandInfo.Env = Env = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Env, EnvSize);
+
+                    /* Repeat the request */
+                    CommandInfo.VDMState |= VDM_FLAG_RETRY;
+                    goto Command;
+                }
+
                 /* Shouldn't happen */
                 ASSERT(FALSE);
             }
 
-            /* Increment the re-entry count */
-            CommandInfo.VDMState = VDM_INC_REENTER_COUNT;
-            GetNextVDMCommand(&CommandInfo);
-
             /* Load the executable */
             Result = DosLoadExecutable(LoadType,
                                        AppName,
                                        CmdLine,
                                        Env,
+                                       ReturnAddress,
                                        &Parameters->StackLocation,
                                        &Parameters->EntryPoint);
-            if (Result != ERROR_SUCCESS)
+            if (Result == ERROR_SUCCESS)
+            {
+                /* Increment the re-entry count */
+                CommandInfo.VDMState = VDM_INC_REENTER_COUNT;
+                GetNextVDMCommand(&CommandInfo);
+            }
+            else
             {
                 DisplayMessage(L"Could not load '%S'. Error: %u", AppName, Result);
-                // FIXME: Decrement the reenter count. Or, instead, just increment
-                // the VDM reenter count *only* if this call succeeds...
             }
 
             break;
@@ -1115,6 +692,8 @@ WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
         }
     }
 
+    RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
+
     /* Close the handles */
     CloseHandle(ProcessInfo.hProcess);
     CloseHandle(ProcessInfo.hThread);
@@ -1123,7 +702,7 @@ WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
 }
 #endif
 
-VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
+VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
 {
     WORD i;
     WORD McbSegment = FIRST_MCB_SEGMENT;
@@ -1131,17 +710,21 @@ VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
     LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
     PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
 
-    DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
+    DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
            Psp,
-           ReturnCode);
+           ReturnCode,
+           KeepResident);
 
     /* Check if this PSP is it's own parent */
     if (PspBlock->ParentPsp == Psp) goto Done;
 
-    for (i = 0; i < PspBlock->HandleTableSize; i++)
+    if (KeepResident == 0)
     {
-        /* Close the handle */
-        DosCloseHandle(i);
+        for (i = 0; i < PspBlock->HandleTableSize; i++)
+        {
+            /* Close the handle */
+            DosCloseHandle(i);
+        }
     }
 
     /* Free the memory used by the process */
@@ -1151,10 +734,30 @@ VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
         CurrentMcb = SEGMENT_TO_MCB(McbSegment);
 
         /* Make sure the MCB is valid */
-        if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType !='Z') break;
+        if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') break;
 
-        /* If this block was allocated by the process, free it */
-        if (CurrentMcb->OwnerPsp == Psp) DosFreeMemory(McbSegment + 1);
+        /* Check if this block was allocated by the process */
+        if (CurrentMcb->OwnerPsp == Psp)
+        {
+            if (KeepResident == 0)
+            {
+                /* Free this entire block */
+                DosFreeMemory(McbSegment + 1);
+            }
+            else if (KeepResident < CurrentMcb->Size)
+            {
+                /* Reduce the size of the block */
+                DosResizeMemory(McbSegment + 1, KeepResident, NULL);
+
+                /* No further paragraphs need to stay resident */
+                KeepResident = 0;
+            }
+            else
+            {
+                /* Just reduce the amount of paragraphs we need to keep resident */
+                KeepResident -= CurrentMcb->Size;
+            }
+        }
 
         /* If this was the last block, quit */
         if (CurrentMcb->BlockType == 'Z') break;
@@ -1210,103 +813,10 @@ Done:
                LOWORD(PspBlock->TerminateAddress));
 }
 
-BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
-{
-    PDOS_SFT_ENTRY Entry = DosGetSftEntry(FileHandle);
-    PDOS_DEVICE_NODE Node = Entry->DeviceNode;
-
-    /* Make sure it exists and is a device */
-    if (!Entry || Entry->Type != DOS_SFT_ENTRY_DEVICE)
-    {
-        DosLastError = ERROR_FILE_NOT_FOUND;
-        return FALSE;
-    }
-
-    switch (ControlCode)
-    {
-        /* Get Device Information */
-        case 0x00:
-        {
-            /*
-             * See Ralf Brown: http://www.ctyme.com/intr/rb-2820.htm
-             * for a list of possible flags.
-             */
-
-            /* Return the device information word */
-            setDX(Node->DeviceAttributes);
-            return TRUE;
-        }
-
-        /* Set Device Information */
-        case 0x01:
-        {
-            Node->DeviceAttributes = getDX();
-            return TRUE;
-        }
-
-        /* Read From Device I/O Control Channel */
-        case 0x02:
-        {
-            WORD Length = getCX();
-
-            if (!(Node->DeviceAttributes & DOS_DEVATTR_IOCTL))
-            {
-                DosLastError = ERROR_INVALID_FUNCTION;
-                return FALSE;
-            }
-
-            /* Do nothing if there is no IOCTL routine */
-            if (!Node->IoctlReadRoutine)
-            {
-                setAX(0);
-                return TRUE;
-            }
-
-            Node->IoctlReadRoutine(Node, MAKELONG(getDX(), getDS()), &Length);
-
-            setAX(Length);
-            return TRUE;
-        }
-
-        /* Write To Device I/O Control Channel */
-        case 0x03:
-        {
-            WORD Length = getCX();
-
-            if (!(Node->DeviceAttributes & DOS_DEVATTR_IOCTL))
-            {
-                DosLastError = ERROR_INVALID_FUNCTION;
-                return FALSE;
-            }
-
-            /* Do nothing if there is no IOCTL routine */
-            if (!Node->IoctlWriteRoutine)
-            {
-                setAX(0);
-                return TRUE;
-            }
-
-            Node->IoctlWriteRoutine(Node, MAKELONG(getDX(), getDS()), &Length);
-
-            setAX(Length);
-            return TRUE;
-        }
-
-        /* Unsupported control code */
-        default:
-        {
-            DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode);
-
-            DosLastError = ERROR_INVALID_PARAMETER;
-            return FALSE;
-        }
-    }
-}
-
 VOID WINAPI DosInt20h(LPWORD Stack)
 {
     /* This is the exit interrupt */
-    DosTerminateProcess(Stack[STACK_CS], 0);
+    DosTerminateProcess(Stack[STACK_CS], 0, 0);
 }
 
 VOID WINAPI DosInt21h(LPWORD Stack)
@@ -1318,13 +828,15 @@ VOID WINAPI DosInt21h(LPWORD Stack)
     PDOS_COUNTRY_CODE_BUFFER CountryCodeBuffer;
     INT Return;
 
+    (*InDos)++;
+
     /* Check the value in the AH register */
     switch (getAH())
     {
         /* Terminate Program */
         case 0x00:
         {
-            DosTerminateProcess(Stack[STACK_CS], 0);
+            DosTerminateProcess(Stack[STACK_CS], 0, 0);
             break;
         }
 
@@ -1436,16 +948,12 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         {
             DPRINT("Char input without echo\n");
 
-            // FIXME: Under DOS 2+, input handle may be redirected!!!!
             Character = DosReadCharacter(DOS_INPUT_HANDLE);
 
             // FIXME: For 0x07, do not check Ctrl-C/Break.
             //        For 0x08, do check those control sequences and if needed,
             //        call INT 0x23.
 
-            // /* Let the BOP repeat if needed */
-            // if (getCF()) break;
-
             setAL(Character);
             break;
         }
@@ -1480,20 +988,69 @@ VOID WINAPI DosInt21h(LPWORD Stack)
 
             while (Count < InputBuffer->MaxLength)
             {
-                // FIXME!! This function should interpret backspaces etc...
-
                 /* Try to read a character (wait) */
                 Character = DosReadCharacter(DOS_INPUT_HANDLE);
 
-                // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
-
-                /* Echo the character and append it to the buffer */
-                DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
-                InputBuffer->Buffer[Count] = Character;
-
-                Count++; /* Carriage returns are also counted */
+                switch (Character)
+                {
+                    /* Extended character */
+                    case '\0':
+                    {
+                        /* Read the scancode */
+                        DosReadCharacter(DOS_INPUT_HANDLE);
+                        break;
+                    }
+
+                    /* Ctrl-C */
+                    case 0x03:
+                    {
+                        DosPrintCharacter(DOS_OUTPUT_HANDLE, '^');
+                        DosPrintCharacter(DOS_OUTPUT_HANDLE, 'C');
+
+                        if (DosControlBreak())
+                        {
+                            /* Set the character to a newline to exit the loop */
+                            Character = '\r';
+                        }
+
+                        break;
+                    }
+
+                    /* Backspace */
+                    case '\b':
+                    {
+                        if (Count > 0)
+                        {
+                            Count--;
+
+                            /* Erase the character */
+                            DosPrintCharacter(DOS_OUTPUT_HANDLE, '\b');
+                            DosPrintCharacter(DOS_OUTPUT_HANDLE, ' ');
+                            DosPrintCharacter(DOS_OUTPUT_HANDLE, '\b');
+                        }
+
+                        break;
+                    }
+
+                    default:
+                    {
+                        /* Append it to the buffer */
+                        InputBuffer->Buffer[Count] = Character;
+
+                        /* Check if this is a special character */
+                        if (Character < 0x20 && Character != 0x0A && Character != 0x0D)
+                        {
+                            DosPrintCharacter(DOS_OUTPUT_HANDLE, '^');
+                            Character += 'A' - 1;
+                        }
+
+                        /* Echo the character */
+                        DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
+                    }
+                }
 
                 if (Character == '\r') break;
+                Count++; /* Carriage returns are NOT counted */
             }
 
             /* Update the length */
@@ -1641,6 +1198,78 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             break;
         }
 
+        /* Parse Filename into FCB */
+        case 0x29:
+        {
+            PCHAR FileName = (PCHAR)SEG_OFF_TO_PTR(getDS(), getSI());
+            PDOS_FCB Fcb = (PDOS_FCB)SEG_OFF_TO_PTR(getES(), getDI());
+            BYTE Options = getAL();
+            INT i;
+            CHAR FillChar = ' ';
+
+            if (FileName[1] == ':')
+            {
+                /* Set the drive number */
+                Fcb->DriveNumber = RtlUpperChar(FileName[0]) - 'A' + 1;
+
+                /* Skip to the file name part */
+                FileName += 2;
+            }
+            else
+            {
+                /* No drive number specified */
+                if (Options & (1 << 1)) Fcb->DriveNumber = CurrentDrive + 1;
+                else Fcb->DriveNumber = 0;
+            }
+
+            /* Parse the file name */
+            i = 0;
+            while ((*FileName > 0x20) && (i < 8))
+            {
+                if (*FileName == '.') break;
+                else if (*FileName == '*')
+                {
+                    FillChar = '?';
+                    break;
+                }
+
+                Fcb->FileName[i++] = RtlUpperChar(*FileName++);
+            }
+
+            /* Fill the whole field with blanks only if bit 2 is not set */
+            if ((FillChar != ' ') || (i != 0) || !(Options & (1 << 2)))
+            {
+                for (; i < 8; i++) Fcb->FileName[i] = FillChar;
+            }
+
+            /* Skip to the extension part */
+            while (*FileName > 0x20 && *FileName != '.') FileName++;
+            if (*FileName == '.') FileName++;
+
+            /* Now parse the extension */
+            i = 0;
+            FillChar = ' ';
+
+            while ((*FileName > 0x20) && (i < 3))
+            {
+                if (*FileName == '*')
+                {
+                    FillChar = '?';
+                    break;
+                }
+
+                Fcb->FileExt[i++] = RtlUpperChar(*FileName++);
+            }
+
+            /* Fill the whole field with blanks only if bit 3 is not set */
+            if ((FillChar != ' ') || (i != 0) || !(Options & (1 << 3)))
+            {
+                for (; i < 3; i++) Fcb->FileExt[i] = FillChar;
+            }
+
+            break;
+        }
+
         /* Get System Date */
         case 0x2A:
         {
@@ -1740,6 +1369,14 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             break;
         }
 
+        /* Terminate and Stay Resident */
+        case 0x31:
+        {
+            DPRINT1("Process going resident: %u paragraphs kept\n", getDX());
+            DosTerminateProcess(CurrentPsp, getAL(), getDX());
+            break;
+        }
+
         /* Extended functionalities */
         case 0x33:
         {
@@ -1774,6 +1411,15 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             break;
         }
 
+        /* Get Address of InDOS flag */
+        case 0x34:
+        {
+            setES(HIWORD(INDOS_POINTER));
+            setBX(LOWORD(INDOS_POINTER));
+
+            break;
+        }
+
         /* Get Interrupt Vector */
         case 0x35:
         {
@@ -1980,20 +1626,8 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         case 0x3D:
         {
             WORD FileHandle;
-            WORD ErrorCode;
             LPCSTR FileName = (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX());
-            PDOS_DEVICE_NODE Device = DosGetDevice(FileName);
-
-            if (Device)
-            {
-                FileHandle = DosOpenDevice(Device);
-                ErrorCode =  (FileHandle != INVALID_DOS_HANDLE)
-                             ? ERROR_SUCCESS : ERROR_TOO_MANY_OPEN_FILES;
-            }
-            else
-            {
-                ErrorCode = DosOpenFile(&FileHandle, FileName, getAL());
-            }
+            WORD ErrorCode = DosOpenFile(&FileHandle, FileName, getAL());
 
             if (ErrorCode == ERROR_SUCCESS)
             {
@@ -2031,7 +1665,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             WORD BytesRead = 0;
             WORD ErrorCode;
 
-            DPRINT1("INT 21h, AH = 3Fh\n");
+            DPRINT("DosReadFile(0x%04X)\n", getBX());
 
             DoEcho = TRUE;
             ErrorCode = DosReadFile(getBX(),
@@ -2175,9 +1809,12 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* IOCTL */
         case 0x44:
         {
-            if (DosHandleIoctl(getAL(), getBX()))
+            WORD Length = getCX();
+
+            if (DosDeviceIoControl(getBX(), getAL(), MAKELONG(getDX(), getDS()), &Length))
             {
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                setAX(Length);
             }
             else
             {
@@ -2191,57 +1828,26 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Duplicate Handle */
         case 0x45:
         {
-            WORD NewHandle;
-            PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(getBX());
+            WORD NewHandle = DosDuplicateHandle(getBX());
 
-            if (SftEntry == NULL || SftEntry->Type == DOS_SFT_ENTRY_NONE) 
+            if (NewHandle != INVALID_DOS_HANDLE)
             {
-                /* The handle is invalid */
-                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                setAX(ERROR_INVALID_HANDLE);
-                break;
-            }
-
-            /* Open a new handle to the same entry */
-            switch (SftEntry->Type)
-            {
-                case DOS_SFT_ENTRY_WIN32:
-                {
-                    NewHandle = DosOpenHandle(SftEntry->Handle);
-                    break;
-                }
-
-                case DOS_SFT_ENTRY_DEVICE:
-                {
-                    NewHandle = DosOpenDevice(SftEntry->DeviceNode);
-                    break;
-                }
-
-                default:
-                {
-                    /* Shouldn't happen */
-                    ASSERT(FALSE);
-                }
+                setAX(NewHandle);
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
-
-            if (NewHandle == INVALID_DOS_HANDLE)
+            else
             {
-                /* Too many files open */
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                setAX(ERROR_TOO_MANY_OPEN_FILES);
-                break;
+                setAX(DosLastError);
             }
 
-            /* Return the result */
-            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
-            setAX(NewHandle);
             break;
         }
 
         /* Force Duplicate Handle */
         case 0x46:
         {
-            if (DosDuplicateHandle(getBX(), getCX()))
+            if (DosForceDuplicateHandle(getBX(), getCX()))
             {
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
@@ -2353,7 +1959,23 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             DOS_EXEC_TYPE LoadType = (DOS_EXEC_TYPE)getAL();
             LPSTR ProgramName = SEG_OFF_TO_PTR(getDS(), getDX());
             PDOS_EXEC_PARAM_BLOCK ParamBlock = SEG_OFF_TO_PTR(getES(), getBX());
-            WORD ErrorCode = DosCreateProcess(LoadType, ProgramName, ParamBlock);
+            DWORD ReturnAddress = MAKELONG(Stack[STACK_IP], Stack[STACK_CS]);
+            WORD ErrorCode;
+            
+            if (LoadType != DOS_LOAD_OVERLAY)
+            {
+                ErrorCode = DosCreateProcess(LoadType, ProgramName, ParamBlock, ReturnAddress);
+            }
+            else
+            {
+                ErrorCode = DosLoadExecutable(DOS_LOAD_OVERLAY,
+                                              ProgramName,
+                                              FAR_POINTER(ParamBlock->CommandLine),
+                                              SEG_OFF_TO_PTR(ParamBlock->Environment, 0),
+                                              ReturnAddress,
+                                              NULL,
+                                              NULL);
+            }
 
             if (ErrorCode == ERROR_SUCCESS)
             {
@@ -2372,7 +1994,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Terminate With Return Code */
         case 0x4C:
         {
-            DosTerminateProcess(CurrentPsp, getAL());
+            DosTerminateProcess(CurrentPsp, getAL(), 0);
             break;
         }
 
@@ -2454,10 +2076,9 @@ VOID WINAPI DosInt21h(LPWORD Stack)
              */
 
             /* Return the DOS "list of lists" in ES:BX */
-            setES(0x0000);
-            setBX(0x0000);
+            setES(DOS_DATA_SEGMENT);
+            setBX(FIELD_OFFSET(DOS_SYSVARS, FirstDpb));
 
-            DisplayMessage(L"Required for AARD code, do you remember? :P");
             break;
         }
 
@@ -2618,44 +2239,30 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Lock/Unlock Region of File */
         case 0x5C:
         {
-            PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(getBX());
-
-            if (SftEntry->Type != DOS_SFT_ENTRY_WIN32)
-            {
-                /* The handle is invalid */
-                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                setAX(ERROR_INVALID_HANDLE);
-                break;
-            }
-
             if (getAL() == 0x00)
             {
                 /* Lock region of file */
-                if (LockFile(SftEntry->Handle,
-                             MAKELONG(getCX(), getDX()), 0,
-                             MAKELONG(getSI(), getDI()), 0))
+                if (DosLockFile(getBX(), MAKELONG(getCX(), getDX()), MAKELONG(getSI(), getDI())))
                 {
                     Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
                 }
                 else
                 {
                     Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                    setAX(GetLastError());
+                    setAX(DosLastError);
                 }
             }
             else if (getAL() == 0x01)
             {
                 /* Unlock region of file */
-                if (UnlockFile(SftEntry->Handle,
-                               MAKELONG(getCX(), getDX()), 0,
-                               MAKELONG(getSI(), getDI()), 0))
+                if (DosUnlockFile(getBX(), MAKELONG(getCX(), getDX()), MAKELONG(getSI(), getDI())))
                 {
                     Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
                 }
                 else
                 {
                     Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                    setAX(GetLastError());
+                    setAX(DosLastError);
                 }
             }
             else
@@ -2796,15 +2403,14 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
         }
     }
+
+    (*InDos)--;
 }
 
 VOID WINAPI DosBreakInterrupt(LPWORD Stack)
 {
-    UNREFERENCED_PARAMETER(Stack);
-
-    /* Stop the VDM task */
-    ResetEvent(VdmTaskEvent);
-    CpuUnsimulate();
+    /* Set CF to terminate the running process */
+    Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
 }
 
 VOID WINAPI DosFastConOut(LPWORD Stack)
@@ -2841,25 +2447,71 @@ VOID WINAPI DosFastConOut(LPWORD Stack)
 
 VOID WINAPI DosInt2Fh(LPWORD Stack)
 {
-    DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
-            getAH(), getAL());
-    Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+    switch (getAH())
+    {
+        /* Extended Memory Specification */
+        case 0x43:
+        {
+            DWORD DriverEntry;
+            if (!XmsGetDriverEntry(&DriverEntry)) break;
+
+            if (getAL() == 0x00)
+            {
+                /* The driver is loaded */
+                setAL(0x80);
+            }
+            else if (getAL() == 0x10)
+            {
+                setES(HIWORD(DriverEntry));
+                setBX(LOWORD(DriverEntry));
+            }
+            else
+            {
+                DPRINT1("Unknown DOS XMS Function: INT 0x2F, AH = 43h, AL = %xh\n", getAL());
+            }
+
+            break;
+        }
+        
+        default:
+        {
+            DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
+                    getAH(), getAL());
+            Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+        }
+    }
 }
 
 BOOLEAN DosKRNLInitialize(VOID)
 {
-
 #if 1
 
     UCHAR i;
     CHAR CurrentDirectory[MAX_PATH];
     CHAR DosDirectory[DOS_DIR_LENGTH];
     LPSTR Path;
-    PDOS_DEVICE_NODE ConInDevice, ConOutDevice;
+    PDOS_SFT Sft;
+
+    const BYTE NullDriverRoutine[] = {
+        /* Strategy routine entry */
+        0x26, // mov [Request.Status], DOS_DEVSTAT_DONE
+        0xC7,
+        0x47,
+        FIELD_OFFSET(DOS_REQUEST_HEADER, Status),
+        LOBYTE(DOS_DEVSTAT_DONE),
+        HIBYTE(DOS_DEVSTAT_DONE),
+
+        /* Interrupt routine entry */
+        0xCB, // retf
+    };
 
     FILE *Stream;
     WCHAR Buffer[256];
 
+    /* Setup the InDOS flag */
+    InDos = (PBYTE)FAR_POINTER(INDOS_POINTER);
+    *InDos = 0;
+
     /* Clear the current directory buffer */
     RtlZeroMemory(CurrentDirectories, sizeof(CurrentDirectories));
 
@@ -2905,19 +2557,36 @@ BOOLEAN DosKRNLInitialize(VOID)
         fclose(Stream);
     }
 
+    /* Initialize the list of lists */
+    SysVars = (PDOS_SYSVARS)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT, 0);
+    RtlZeroMemory(SysVars, sizeof(DOS_SYSVARS));
+    SysVars->FirstMcb = FIRST_MCB_SEGMENT;
+    SysVars->FirstSft = MAKELONG(MASTER_SFT_OFFSET, DOS_DATA_SEGMENT);
+
+    /* Initialize the NUL device driver */
+    SysVars->NullDevice.Link = 0xFFFFFFFF;
+    SysVars->NullDevice.DeviceAttributes = DOS_DEVATTR_NUL | DOS_DEVATTR_CHARACTER;
+    SysVars->NullDevice.StrategyRoutine = FIELD_OFFSET(DOS_SYSVARS, NullDriverRoutine);
+    SysVars->NullDevice.InterruptRoutine = SysVars->NullDevice.StrategyRoutine + 6;
+    RtlFillMemory(SysVars->NullDevice.DeviceName,
+                  sizeof(SysVars->NullDevice.DeviceName),
+                  ' ');
+    RtlCopyMemory(SysVars->NullDevice.DeviceName, "NUL", strlen("NUL"));
+    RtlCopyMemory(SysVars->NullDriverRoutine,
+                  NullDriverRoutine,
+                  sizeof(NullDriverRoutine));
+
     /* Initialize the SFT */
-    for (i = 0; i < DOS_SFT_SIZE; i++)
+    Sft = (PDOS_SFT)FAR_POINTER(SysVars->FirstSft);
+    Sft->Link = 0xFFFFFFFF;
+    Sft->NumDescriptors = DOS_SFT_SIZE;
+
+    for (i = 0; i < Sft->NumDescriptors; i++)
     {
-        DosSystemFileTable[i].Type     = DOS_SFT_ENTRY_NONE;
-        DosSystemFileTable[i].RefCount = 0;
+        /* Clear the file descriptor entry */
+        RtlZeroMemory(&Sft->FileDescriptors[i], sizeof(DOS_FILE_DESCRIPTOR));
     }
 
-    /* Load the EMS driver */
-    EmsDrvInitialize();
-
-    /* Load the CON driver */
-    ConDrvInitialize(&ConInDevice, &ConOutDevice);
-
 #endif
 
     /* Initialize the callback context */
@@ -2932,6 +2601,19 @@ BOOLEAN DosKRNLInitialize(VOID)
     RegisterDosInt32(0x29, DosFastConOut    ); // DOS 2+ Fast Console Output
     RegisterDosInt32(0x2F, DosInt2Fh        );
 
+    /* Load the EMS driver */
+    if (!EmsDrvInitialize(EMS_TOTAL_PAGES))
+    {
+        DPRINT1("Could not initialize EMS. EMS will not be available.\n"
+                "Try reducing the number of EMS pages.\n");
+    }
+
+    /* Load the XMS driver (HIMEM) */
+    XmsInitialize();
+
+    /* Load the CON driver */
+    ConDrvInitialize();
+
     return TRUE;
 }