[NTVDM]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / dos / dos32krnl / dos.c
index b8cc55b..d4e1c76 100644 (file)
 #include "dos.h"
 #include "dos/dem.h"
 #include "device.h"
+#include "handle.h"
+#include "dosfiles.h"
 #include "memory.h"
+#include "process.h"
 #include "himem.h"
 
 #include "bios/bios.h"
 
 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 ***********************************************************/
 
-/* Echo state for INT 21h, AH = 01h and AH = 3Fh */
-BOOLEAN DoEcho = FALSE;
-WORD CurrentPsp = SYSTEM_PSP;
-WORD DosLastError = 0;
-
-/* PRIVATE FUNCTIONS **********************************************************/
-
-static WORD DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL,
-                                    LPCSTR ProgramName)
-{
-    PCHAR Ptr, DestBuffer = NULL;
-    ULONG TotalSize = 0;
-    WORD DestSegment;
-
-    /* If we have an environment strings list, compute its size */
-    if (Environment)
-    {
-        /* Calculate the size of the environment block */
-        Ptr = (PCHAR)Environment;
-        while (*Ptr) Ptr += strlen(Ptr) + 1;
-        TotalSize = (ULONG_PTR)Ptr - (ULONG_PTR)Environment;
-    }
-    else
-    {
-        /* Empty environment string */
-        TotalSize = 1;
-    }
-    /* Add the final environment block NULL-terminator */
-    TotalSize++;
-
-    /* Add the two bytes for the program name tag */
-    TotalSize += 2;
-
-    /* Add the string buffer size */
-    TotalSize += strlen(ProgramName) + 1;
-
-    /* Allocate the memory for the environment block */
-    DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
-    if (!DestSegment) return 0;
-
-    DestBuffer = (PCHAR)SEG_OFF_TO_PTR(DestSegment, 0);
-
-    /* If we have an environment strings list, copy it */
-    if (Environment)
-    {
-        Ptr = (PCHAR)Environment;
-        while (*Ptr)
-        {
-            /* Copy the string and NULL-terminate it */
-            strcpy(DestBuffer, Ptr);
-            DestBuffer += strlen(Ptr);
-            *(DestBuffer++) = '\0';
-
-            /* Move to the next string */
-            Ptr += strlen(Ptr) + 1;
-        }
-    }
-    else
-    {
-        /* Empty environment string */
-        *(DestBuffer++) = '\0';
-    }
-    /* NULL-terminate the environment block */
-    *(DestBuffer++) = '\0';
-
-    /* Store the special program name tag */
-    *(DestBuffer++) = LOBYTE(DOS_PROGRAM_NAME_TAG);
-    *(DestBuffer++) = HIBYTE(DOS_PROGRAM_NAME_TAG);
-
-    /* Copy the program name after the environment block */
-    strcpy(DestBuffer, ProgramName);
-
-    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)
-{
-    UINT 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)
-{
-    UINT 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)
-{
-    UINT 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)
-{
-    UINT 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 Con = DosGetDevice("CON");
-        ASSERT(Con != 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++)
-        {
-            /* Find the corresponding SFT entry */
-            if (IsConsoleHandle(StandardHandles[i]))
-            {
-                SftEntry = DosFindDeviceSftEntry(Con);
-            }
-            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 = Con;
-
-                    /* Call the open routine */
-                    if (Con->OpenRoutine) Con->OpenRoutine(Con);
-                }
-                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];
-
-    /* Make sure the drive exists */
-    if (Drive > (LastDrive - 'A')) return FALSE;
-
-    /* Find the path to the new current directory */
-    swprintf(DirectoryPath, L"%c\\%S", Drive + 'A', CurrentDirectories[Drive]);
-
-    /* Change the current directory of the process */
-    if (!SetCurrentDirectory(DirectoryPath)) return FALSE;
-
-    /* Set the current drive */
-    CurrentDrive = Drive;
-
-    /* Return success */
-    return TRUE;
-}
-
-static BOOLEAN DosChangeDirectory(LPSTR Directory)
-{
-    BYTE DriveNumber;
-    DWORD Attributes;
-    LPSTR Path;
-
-    /* Make sure the directory path is not too long */
-    if (strlen(Directory) >= DOS_DIR_LENGTH)
-    {
-        DosLastError = ERROR_PATH_NOT_FOUND;
-        return FALSE;
-    }
-
-    /* Get the drive number */
-    DriveNumber = Directory[0] - 'A';
-
-    /* Make sure the drive exists */
-    if (DriveNumber > (LastDrive - 'A'))
-    {
-        DosLastError = ERROR_PATH_NOT_FOUND;
-        return FALSE;
-    }
-
-    /* Get the file attributes */
-    Attributes = GetFileAttributesA(Directory);
-
-    /* Make sure the path exists and is a directory */
-    if ((Attributes == INVALID_FILE_ATTRIBUTES)
-        || !(Attributes & FILE_ATTRIBUTE_DIRECTORY))
-    {
-        DosLastError = ERROR_PATH_NOT_FOUND;
-        return FALSE;
-    }
-
-    /* Check if this is the current drive */
-    if (DriveNumber == CurrentDrive)
-    {
-        /* Change the directory */
-        if (!SetCurrentDirectoryA(Directory))
-        {
-            DosLastError = LOWORD(GetLastError());
-            return FALSE;
-        }
-    }
-
-    /* Get the directory part of the path */
-    Path = strchr(Directory, '\\');
-    if (Path != NULL)
-    {
-        /* Skip the backslash */
-        Path++;
-    }
-
-    /* Set the directory for the drive */
-    if (Path != NULL)
-    {
-        strncpy(CurrentDirectories[DriveNumber], Path, DOS_DIR_LENGTH);
-    }
-    else
-    {
-        CurrentDirectories[DriveNumber][0] = '\0';
-    }
-
-    /* Return success */
-    return TRUE;
-}
-
-static BOOLEAN DosControlBreak(VOID)
-{
-    setCF(0);
-
-    /* Call interrupt 0x23 */
-    Int32Call(&DosContext, 0x23);
-
-    if (getCF())
-    {
-        DosTerminateProcess(CurrentPsp, 0, 0);
-        return TRUE;
-    }
-
-    return FALSE;
-}
-
-/* PUBLIC FUNCTIONS ***********************************************************/
-
-PDOS_SFT_ENTRY DosGetSftEntry(WORD DosHandle)
-{
-    PDOS_PSP PspBlock;
-    LPBYTE HandleTable;
-
-    /* The system PSP has no handle table */
-    if (CurrentPsp == SYSTEM_PSP) return NULL;
-
-    /* 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;
-
-    /* Return a pointer to the SFT entry */
-    return &DosSystemFileTable[HandleTable[DosHandle]];
-}
-
-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);
-
-    RtlZeroMemory(PspBlock, sizeof(*PspBlock));
-
-    /* Set the exit interrupt */
-    PspBlock->Exit[0] = 0xCD; // int 0x20
-    PspBlock->Exit[1] = 0x20;
-
-    /* Set the number of the last paragraph */
-    PspBlock->LastParagraph = PspSegment + ProgramSize - 1;
-
-    /* Save the interrupt vectors */
-    PspBlock->TerminateAddress = ReturnAddress;
-    PspBlock->BreakAddress     = IntVecTable[0x23];
-    PspBlock->CriticalAddress  = IntVecTable[0x24];
-
-    /* Set the parent PSP */
-    PspBlock->ParentPsp = CurrentPsp;
-
-    /* Copy the parent handle table */
-    DosCopyHandleTable(PspBlock->HandleTable);
-
-    /* Set the environment block */
-    PspBlock->EnvBlock = Environment;
-
-    /* Set the handle table pointers to the internal handle table */
-    PspBlock->HandleTableSize = 20;
-    PspBlock->HandleTablePtr = MAKELONG(0x18, PspSegment);
-
-    /* Set the DOS version */
-    PspBlock->DosVersion = DOS_VERSION;
-
-    /* Set the far call opcodes */
-    PspBlock->FarCall[0] = 0xCD; // int 0x21
-    PspBlock->FarCall[1] = 0x21;
-    PspBlock->FarCall[2] = 0xCB; // retf
-
-    /* Set the command line */
-    PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1);
-    RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
-    PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
-}
-
-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)
-{
-    DWORD Result = ERROR_SUCCESS;
-    HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
-    LPBYTE Address = NULL;
-    WORD Segment = 0;
-    WORD EnvBlock = 0;
-    WORD MaxAllocSize;
-    DWORD i, FileSize, ExeSize;
-    PIMAGE_DOS_HEADER Header;
-    PDWORD RelocationTable;
-    PWORD RelocWord;
-    LPSTR CmdLinePtr = (LPSTR)CommandLine;
-
-    DPRINT1("DosLoadExecutable(%d, %s, %s, %s, 0x%08X, 0x%08X)\n",
-            LoadType,
-            ExecutablePath,
-            CommandLine,
-            Environment ? Environment : "n/a",
-            StackLocation,
-            EntryPoint);
-
-    if (LoadType == DOS_LOAD_OVERLAY)
-    {
-        DPRINT1("Overlay loading is not supported yet.\n");
-        return ERROR_NOT_SUPPORTED;
-    }
-
-    /* NULL-terminate the command line by removing the return carriage character */
-    while (*CmdLinePtr && *CmdLinePtr != '\r') CmdLinePtr++;
-    *CmdLinePtr = '\0';
-
-    /* Open a handle to the executable */
-    FileHandle = CreateFileA(ExecutablePath,
-                             GENERIC_READ,
-                             FILE_SHARE_READ,
-                             NULL,
-                             OPEN_EXISTING,
-                             FILE_ATTRIBUTE_NORMAL,
-                             NULL);
-    if (FileHandle == INVALID_HANDLE_VALUE)
-    {
-        Result = GetLastError();
-        goto Cleanup;
-    }
-
-    /* Get the file size */
-    FileSize = GetFileSize(FileHandle, NULL);
-
-    /* Create a mapping object for the file */
-    FileMapping = CreateFileMapping(FileHandle,
-                                    NULL,
-                                    PAGE_READONLY,
-                                    0,
-                                    0,
-                                    NULL);
-    if (FileMapping == NULL)
-    {
-        Result = GetLastError();
-        goto Cleanup;
-    }
-
-    /* Map the file into memory */
-    Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
-    if (Address == NULL)
-    {
-        Result = GetLastError();
-        goto Cleanup;
-    }
-
-    /* Copy the environment block to DOS memory */
-    EnvBlock = DosCopyEnvironmentBlock(Environment, ExecutablePath);
-    if (EnvBlock == 0)
-    {
-        Result = ERROR_NOT_ENOUGH_MEMORY;
-        goto Cleanup;
-    }
-
-    /* Check if this is an EXE file or a COM file */
-    if (Address[0] == 'M' && Address[1] == 'Z')
-    {
-        /* EXE file */
-
-        /* Get the MZ header */
-        Header = (PIMAGE_DOS_HEADER)Address;
-
-        /* Get the base size of the file, in paragraphs (rounded up) */
-        ExeSize = (((Header->e_cp - 1) * 512) + Header->e_cblp + 0x0F) >> 4;
-
-        /* Add the PSP size, in paragraphs */
-        ExeSize += sizeof(DOS_PSP) >> 4;
-
-        /* Add the maximum size that should be allocated */
-        ExeSize += Header->e_maxalloc;
-
-        /* Make sure it does not pass 0xFFFF */
-        if (ExeSize > 0xFFFF) ExeSize = 0xFFFF;
-
-        /* Try to allocate that much memory */
-        Segment = DosAllocateMemory((WORD)ExeSize, &MaxAllocSize);
-
-        if (Segment == 0)
-        {
-            /* Check if there's at least enough memory for the minimum size */
-            if (MaxAllocSize < (ExeSize - Header->e_maxalloc + Header->e_minalloc))
-            {
-                Result = DosLastError;
-                goto Cleanup;
-            }
-
-            /* Allocate that minimum amount */
-            ExeSize = MaxAllocSize;
-            Segment = DosAllocateMemory((WORD)ExeSize, NULL);
-            ASSERT(Segment != 0);
-        }
-
-        /* Initialize the PSP */
-        DosInitializePsp(Segment,
-                         CommandLine,
-                         (WORD)ExeSize,
-                         EnvBlock,
-                         ReturnAddress);
-
-        /* The process owns its own memory */
-        DosChangeMemoryOwner(Segment, Segment);
-        DosChangeMemoryOwner(EnvBlock, Segment);
-
-        /* Copy the program to Segment:0100 */
-        RtlCopyMemory(SEG_OFF_TO_PTR(Segment, 0x100),
-                      Address + (Header->e_cparhdr << 4),
-                      min(FileSize - (Header->e_cparhdr << 4),
-                          (ExeSize << 4) - sizeof(DOS_PSP)));
-
-        /* Get the relocation table */
-        RelocationTable = (PDWORD)(Address + Header->e_lfarlc);
-
-        /* Perform relocations */
-        for (i = 0; i < Header->e_crlc; i++)
-        {
-            /* Get a pointer to the word that needs to be patched */
-            RelocWord = (PWORD)SEG_OFF_TO_PTR(Segment + HIWORD(RelocationTable[i]),
-                                                0x100 + LOWORD(RelocationTable[i]));
-
-            /* Add the number of the EXE segment to it */
-            *RelocWord += Segment + (sizeof(DOS_PSP) >> 4);
-        }
-
-        if (LoadType == DOS_LOAD_AND_EXECUTE)
-        {
-            /* Set the initial segment registers */
-            setDS(Segment);
-            setES(Segment);
-
-            /* Set the stack to the location from the header */
-            setSS(Segment + (sizeof(DOS_PSP) >> 4) + Header->e_ss);
-            setSP(Header->e_sp);
-
-            /* Execute */
-            CurrentPsp = Segment;
-            DiskTransferArea = MAKELONG(0x80, Segment);
-            CpuExecute(Segment + Header->e_cs + (sizeof(DOS_PSP) >> 4),
-                       Header->e_ip);
-        }
-    }
-    else
-    {
-        /* COM file */
-
-        /* Find the maximum amount of memory that can be allocated */
-        DosAllocateMemory(0xFFFF, &MaxAllocSize);
-
-        /* Make sure it's enough for the whole program and the PSP */
-        if (((DWORD)MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP)))
-        {
-            Result = ERROR_NOT_ENOUGH_MEMORY;
-            goto Cleanup;
-        }
-
-        /* Allocate all of it */
-        Segment = DosAllocateMemory(MaxAllocSize, NULL);
-        if (Segment == 0)
-        {
-            Result = DosLastError;
-            goto Cleanup;
-        }
-
-        /* The process owns its own memory */
-        DosChangeMemoryOwner(Segment, Segment);
-        DosChangeMemoryOwner(EnvBlock, Segment);
-
-        /* Copy the program to Segment:0100 */
-        RtlCopyMemory(SEG_OFF_TO_PTR(Segment, 0x100),
-                      Address,
-                      FileSize);
-
-        /* Initialize the PSP */
-        DosInitializePsp(Segment,
-                         CommandLine,
-                         MaxAllocSize,
-                         EnvBlock,
-                         ReturnAddress);
-
-        if (LoadType == DOS_LOAD_AND_EXECUTE)
-        {
-            /* Set the initial segment registers */
-            setDS(Segment);
-            setES(Segment);
-
-            /* Set the stack to the last word of the segment */
-            setSS(Segment);
-            setSP(0xFFFE);
-
-            /*
-             * Set the value on the stack to 0, so that a near return
-             * jumps to PSP:0000 which has the exit code.
-             */
-            *((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0;
-
-            /* Execute */
-            CurrentPsp = Segment;
-            DiskTransferArea = MAKELONG(0x80, Segment);
-            CpuExecute(Segment, 0x100);
-        }
-    }
-
-Cleanup:
-    if (Result != ERROR_SUCCESS)
-    {
-        /* It was not successful, cleanup the DOS memory */
-        if (EnvBlock) DosFreeMemory(EnvBlock);
-        if (Segment) DosFreeMemory(Segment);
-    }
-
-    /* Unmap the file*/
-    if (Address != NULL) UnmapViewOfFile(Address);
-
-    /* Close the file mapping object */
-    if (FileMapping != NULL) CloseHandle(FileMapping);
-
-    /* Close the file handle */
-    if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
-
-    return Result;
-}
-
-DWORD DosStartProcess(IN LPCSTR ExecutablePath,
-                      IN LPCSTR CommandLine,
-                      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 */
-    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
-    // are started.
-    IOWriteB(PS2_CONTROL_PORT, 0xD2);     // Next write is for the first PS/2 port
-    IOWriteB(PS2_DATA_PORT, 0x80 | 0x1C); // ENTER key release
-
-    /* Start simulation */
-    SetEvent(VdmTaskEvent);
-    CpuSimulate();
-
-    /* Detach from the console */
-    VidBiosDetachFromConsole();
-    ConsoleDetach();
-
-Quit:
-    return Result;
-}
-
-#ifndef STANDALONE
-WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
-                      LPCSTR ProgramName,
-                      PDOS_EXEC_PARAM_BLOCK Parameters,
-                      DWORD ReturnAddress)
-{
-    DWORD Result;
-    DWORD BinaryType;
-    LPVOID Environment = NULL;
-    VDM_COMMAND_INFO CommandInfo;
-    CHAR CmdLine[MAX_PATH];
-    CHAR AppName[MAX_PATH];
-    CHAR PifFile[MAX_PATH];
-    CHAR Desktop[MAX_PATH];
-    CHAR Title[MAX_PATH];
-    ULONG EnvSize = 256;
-    PVOID Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
-    STARTUPINFOA StartupInfo;
-    PROCESS_INFORMATION ProcessInfo;
-
-    /* Get the binary type */
-    if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
-
-    /* Did the caller specify an environment segment? */
-    if (Parameters->Environment)
-    {
-        /* Yes, use it instead of the parent one */
-        Environment = SEG_OFF_TO_PTR(Parameters->Environment, 0);
-    }
-
-    /* Set up the startup info structure */
-    RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
-    StartupInfo.cb = sizeof(StartupInfo);
-
-    /* Create the process */
-    if (!CreateProcessA(ProgramName,
-                        FAR_POINTER(Parameters->CommandLine),
-                        NULL,
-                        NULL,
-                        FALSE,
-                        0,
-                        Environment,
-                        NULL,
-                        &StartupInfo,
-                        &ProcessInfo))
-    {
-        return GetLastError();
-    }
-
-    /* Check the type of the program */
-    switch (BinaryType)
-    {
-        /* These are handled by NTVDM */
-        case SCS_DOS_BINARY:
-        case SCS_WOW_BINARY:
-        {
-            /* Clear the structure */
-            RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
-
-            /* Initialize the structure members */
-            CommandInfo.TaskId = SessionId;
-            CommandInfo.VDMState = VDM_FLAG_NESTED_TASK | VDM_FLAG_DONT_WAIT;
-            CommandInfo.CmdLine = CmdLine;
-            CommandInfo.CmdLen = sizeof(CmdLine);
-            CommandInfo.AppName = AppName;
-            CommandInfo.AppLen = sizeof(AppName);
-            CommandInfo.PifFile = PifFile;
-            CommandInfo.PifLen = sizeof(PifFile);
-            CommandInfo.Desktop = Desktop;
-            CommandInfo.DesktopLen = sizeof(Desktop);
-            CommandInfo.Title = Title;
-            CommandInfo.TitleLen = sizeof(Title);
-            CommandInfo.Env = 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);
+PDOS_SYSVARS SysVars;
 
-                    /* Repeat the request */
-                    CommandInfo.VDMState |= VDM_FLAG_RETRY;
-                    goto Command;
-                }
+/* Echo state for INT 21h, AH = 01h and AH = 3Fh */
+BOOLEAN DoEcho = FALSE;
 
-                /* Shouldn't happen */
-                ASSERT(FALSE);
-            }
+DWORD DiskTransferArea;
+WORD DosErrorLevel = 0x0000;
+WORD DosLastError = 0;
 
-            /* Load the executable */
-            Result = DosLoadExecutable(LoadType,
-                                       AppName,
-                                       CmdLine,
-                                       Env,
-                                       ReturnAddress,
-                                       &Parameters->StackLocation,
-                                       &Parameters->EntryPoint);
-            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);
-            }
+/* PRIVATE FUNCTIONS **********************************************************/
 
-            break;
-        }
+static BOOLEAN DosChangeDrive(BYTE Drive)
+{
+    WCHAR DirectoryPath[DOS_CMDLINE_LENGTH];
 
-        /* Not handled by NTVDM */
-        default:
-        {
-            /* Wait for the process to finish executing */
-            WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
-        }
-    }
+    /* Make sure the drive exists */
+    if (Drive > (LastDrive - 'A')) return FALSE;
+
+    /* Find the path to the new current directory */
+    swprintf(DirectoryPath, L"%c\\%S", Drive + 'A', CurrentDirectories[Drive]);
 
-    RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
+    /* Change the current directory of the process */
+    if (!SetCurrentDirectory(DirectoryPath)) return FALSE;
 
-    /* Close the handles */
-    CloseHandle(ProcessInfo.hProcess);
-    CloseHandle(ProcessInfo.hThread);
+    /* Set the current drive */
+    CurrentDrive = Drive;
 
-    return ERROR_SUCCESS;
+    /* Return success */
+    return TRUE;
 }
-#endif
 
-VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
+static BOOLEAN DosChangeDirectory(LPSTR Directory)
 {
-    WORD i;
-    WORD McbSegment = FIRST_MCB_SEGMENT;
-    PDOS_MCB CurrentMcb;
-    LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
-    PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
-
-    DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
-           Psp,
-           ReturnCode,
-           KeepResident);
-
-    /* Check if this PSP is it's own parent */
-    if (PspBlock->ParentPsp == Psp) goto Done;
+    BYTE DriveNumber;
+    DWORD Attributes;
+    LPSTR Path;
 
-    if (KeepResident == 0)
+    /* Make sure the directory path is not too long */
+    if (strlen(Directory) >= DOS_DIR_LENGTH)
     {
-        for (i = 0; i < PspBlock->HandleTableSize; i++)
-        {
-            /* Close the handle */
-            DosCloseHandle(i);
-        }
+        DosLastError = ERROR_PATH_NOT_FOUND;
+        return FALSE;
     }
 
-    /* Free the memory used by the process */
-    while (TRUE)
-    {
-        /* Get a pointer to the MCB */
-        CurrentMcb = SEGMENT_TO_MCB(McbSegment);
-
-        /* Make sure the MCB is valid */
-        if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') break;
-
-        /* 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);
+    /* Get the drive number */
+    DriveNumber = Directory[0] - 'A';
 
-                /* No further paragraphs need to stay resident */
-                KeepResident = 0;
-            }
-            else
-            {
-                /* Just reduce the amount of paragraphs we need to keep resident */
-                KeepResident -= CurrentMcb->Size;
-            }
-        }
+    /* Make sure the drive exists */
+    if (DriveNumber > (LastDrive - 'A'))
+    {
+        DosLastError = ERROR_PATH_NOT_FOUND;
+        return FALSE;
+    }
 
-        /* If this was the last block, quit */
-        if (CurrentMcb->BlockType == 'Z') break;
+    /* Get the file attributes */
+    Attributes = GetFileAttributesA(Directory);
 
-        /* Update the segment and continue */
-        McbSegment += CurrentMcb->Size + 1;
+    /* Make sure the path exists and is a directory */
+    if ((Attributes == INVALID_FILE_ATTRIBUTES)
+        || !(Attributes & FILE_ATTRIBUTE_DIRECTORY))
+    {
+        DosLastError = ERROR_PATH_NOT_FOUND;
+        return FALSE;
     }
 
-Done:
-    /* Restore the interrupt vectors */
-    IntVecTable[0x22] = PspBlock->TerminateAddress;
-    IntVecTable[0x23] = PspBlock->BreakAddress;
-    IntVecTable[0x24] = PspBlock->CriticalAddress;
-
-    /* Update the current PSP */
-    if (Psp == CurrentPsp)
+    /* Check if this is the current drive */
+    if (DriveNumber == CurrentDrive)
     {
-        CurrentPsp = PspBlock->ParentPsp;
-        if (CurrentPsp == SYSTEM_PSP)
+        /* Change the directory */
+        if (!SetCurrentDirectoryA(Directory))
         {
-            ResetEvent(VdmTaskEvent);
-            CpuUnsimulate();
+            DosLastError = LOWORD(GetLastError());
+            return FALSE;
         }
     }
 
-#ifndef STANDALONE
-    // FIXME: This is probably not the best way to do it
-    /* Check if this was a nested DOS task */
-    if (CurrentPsp != SYSTEM_PSP)
+    /* Get the directory part of the path */
+    Path = strchr(Directory, '\\');
+    if (Path != NULL)
     {
-        VDM_COMMAND_INFO CommandInfo;
-
-        /* Decrement the re-entry count */
-        CommandInfo.TaskId = SessionId;
-        CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
-        GetNextVDMCommand(&CommandInfo);
-
-        /* Clear the structure */
-        RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
-
-        /* Update the VDM state of the task */
-        CommandInfo.TaskId = SessionId;
-        CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
-        GetNextVDMCommand(&CommandInfo);
+        /* Skip the backslash */
+        Path++;
     }
-#endif
-
-    /* Save the return code - Normal termination */
-    DosErrorLevel = MAKEWORD(ReturnCode, 0x00);
-
-    /* Return control to the parent process */
-    CpuExecute(HIWORD(PspBlock->TerminateAddress),
-               LOWORD(PspBlock->TerminateAddress));
-}
-
-BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
-{
-    PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(FileHandle);
-    PDOS_DEVICE_NODE Node = NULL;
 
-    /* Make sure it exists */
-    if (!SftEntry)
+    /* Set the directory for the drive */
+    if (Path != NULL)
     {
-        DosLastError = ERROR_FILE_NOT_FOUND;
-        return FALSE;
+        strncpy(CurrentDirectories[DriveNumber], Path, DOS_DIR_LENGTH);
     }
-
-    if (SftEntry->Type == DOS_SFT_ENTRY_DEVICE) Node = SftEntry->DeviceNode;
-
-    switch (ControlCode)
+    else
     {
-        /* Get Device Information */
-        case 0x00:
-        {
-            WORD InfoWord = 0;
-
-            /*
-             * See Ralf Brown: http://www.ctyme.com/intr/rb-2820.htm
-             * for a list of possible flags.
-             */
-
-            if (Node)
-            {
-                /* Return the device attributes with bit 7 set */
-                InfoWord = Node->DeviceAttributes | (1 << 7);
-            }
-
-            setDX(InfoWord);
-            return TRUE;
-        }
-
-        /* Set Device Information */
-        case 0x01:
-        {
-            // TODO: NOT IMPLEMENTED
-            UNIMPLEMENTED;
-
-            return FALSE;
-        }
-
-        /* Read From Device I/O Control Channel */
-        case 0x02:
-        {
-            WORD Length = getCX();
-
-            if (Node == NULL || !(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 == NULL || !(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;
-            }
+        CurrentDirectories[DriveNumber][0] = '\0';
+    }
 
-            Node->IoctlWriteRoutine(Node, MAKELONG(getDX(), getDS()), &Length);
+    /* Return success */
+    return TRUE;
+}
 
-            setAX(Length);
-            return TRUE;
-        }
+static BOOLEAN DosControlBreak(VOID)
+{
+    setCF(0);
 
-        /* Unsupported control code */
-        default:
-        {
-            DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode);
+    /* Call interrupt 0x23 */
+    Int32Call(&DosContext, 0x23);
 
-            DosLastError = ERROR_INVALID_PARAMETER;
-            return FALSE;
-        }
+    if (getCF())
+    {
+        DosTerminateProcess(CurrentPsp, 0, 0);
+        return TRUE;
     }
+
+    return FALSE;
 }
 
+/* PUBLIC FUNCTIONS ***********************************************************/
+
 VOID WINAPI DosInt20h(LPWORD Stack)
 {
     /* This is the exit interrupt */
@@ -1400,6 +178,10 @@ VOID WINAPI DosInt21h(LPWORD Stack)
 
     (*InDos)++;
 
+    /* Save the value of SS:SP on entry in the PSP */
+    SEGMENT_TO_PSP(CurrentPsp)->LastStack =
+    MAKELONG(getSP() + (STACK_FLAGS + 1) * 2, getSS());
+
     /* Check the value in the AH register */
     switch (getAH())
     {
@@ -1606,7 +388,6 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                     {
                         /* Append it to the buffer */
                         InputBuffer->Buffer[Count] = Character;
-                        Count++; /* Carriage returns are also counted */
 
                         /* Check if this is a special character */
                         if (Character < 0x20 && Character != 0x0A && Character != 0x0D)
@@ -1621,6 +402,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                 }
 
                 if (Character == '\r') break;
+                Count++; /* Carriage returns are NOT counted */
             }
 
             /* Update the length */
@@ -1764,7 +546,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Create New PSP */
         case 0x26:
         {
-            DPRINT1("INT 21h, AH = 26h - Create New PSP is UNIMPLEMENTED\n");
+            DosClonePsp(getDX(), getCS());
             break;
         }
 
@@ -1986,7 +768,6 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         {
             setES(HIWORD(INDOS_POINTER));
             setBX(LOWORD(INDOS_POINTER));
-
             break;
         }
 
@@ -2001,6 +782,38 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             break;
         }
 
+        /* Get Free Disk Space */
+        case 0x36:
+        {
+            CHAR RootPath[3] = "X:\\";
+            DWORD SectorsPerCluster;
+            DWORD BytesPerSector;
+            DWORD NumberOfFreeClusters;
+            DWORD TotalNumberOfClusters;
+
+            if (getDL() == 0) RootPath[0] = 'A' + CurrentDrive;
+            else RootPath[0] = 'A' + getDL() - 1;
+
+            if (GetDiskFreeSpaceA(RootPath,
+                                  &SectorsPerCluster,
+                                  &BytesPerSector,
+                                  &NumberOfFreeClusters,
+                                  &TotalNumberOfClusters))
+            {
+                setAX(LOWORD(SectorsPerCluster));
+                setCX(LOWORD(BytesPerSector));
+                setBX(LOWORD(NumberOfFreeClusters));
+                setDX(LOWORD(TotalNumberOfClusters));
+            }
+            else
+            {
+                /* Error */
+                setAX(0xFFFF);
+            }
+
+            break;
+        }
+
         /* SWITCH character - AVAILDEV */
         case 0x37:
         {
@@ -2196,20 +1009,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)
             {
@@ -2391,9 +1192,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
             {
@@ -2407,57 +1211,26 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Duplicate Handle */
         case 0x45:
         {
-            WORD NewHandle;
-            PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(getBX());
-
-            if (SftEntry == NULL || SftEntry->Type == DOS_SFT_ENTRY_NONE) 
-            {
-                /* The handle is invalid */
-                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                setAX(ERROR_INVALID_HANDLE);
-                break;
-            }
+            WORD NewHandle = DosDuplicateHandle(getBX());
 
-            /* Open a new handle to the same entry */
-            switch (SftEntry->Type)
+            if (NewHandle != INVALID_DOS_HANDLE)
             {
-                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;
             }
@@ -2562,29 +1335,48 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             break;
         }
 
-#ifndef STANDALONE
         /* Execute */
         case 0x4B:
         {
-            DOS_EXEC_TYPE LoadType = (DOS_EXEC_TYPE)getAL();
+            BYTE OrgAL = getAL();
             LPSTR ProgramName = SEG_OFF_TO_PTR(getDS(), getDX());
             PDOS_EXEC_PARAM_BLOCK ParamBlock = SEG_OFF_TO_PTR(getES(), getBX());
             DWORD ReturnAddress = MAKELONG(Stack[STACK_IP], Stack[STACK_CS]);
             WORD ErrorCode;
-            
-            if (LoadType != DOS_LOAD_OVERLAY)
+
+            if (OrgAL <= DOS_LOAD_OVERLAY)
+            {
+                DOS_EXEC_TYPE LoadType = (DOS_EXEC_TYPE)OrgAL;
+
+#ifndef STANDALONE
+                if (LoadType == DOS_LOAD_AND_EXECUTE)
+                {
+                    /* Create a new process */
+                    ErrorCode = DosCreateProcess(ProgramName,
+                                                 ParamBlock,
+                                                 ReturnAddress);
+                }
+                else
+#endif
+                {
+                    /* Just load an executable */
+                    ErrorCode = DosLoadExecutable(LoadType,
+                                                  ProgramName,
+                                                  ParamBlock,
+                                                  NULL,
+                                                  NULL,
+                                                  ReturnAddress);
+                }
+            }
+            else if (OrgAL == 0x05)
             {
-                ErrorCode = DosCreateProcess(LoadType, ProgramName, ParamBlock, ReturnAddress);
+                // http://www.ctyme.com/intr/rb-2942.htm
+                DPRINT1("Set execution state is UNIMPLEMENTED\n");
+                ErrorCode = ERROR_CALL_NOT_IMPLEMENTED;
             }
             else
             {
-                ErrorCode = DosLoadExecutable(DOS_LOAD_OVERLAY,
-                                              ProgramName,
-                                              FAR_POINTER(ParamBlock->CommandLine),
-                                              SEG_OFF_TO_PTR(ParamBlock->Environment, 0),
-                                              ReturnAddress,
-                                              NULL,
-                                              NULL);
+                ErrorCode = ERROR_INVALID_FUNCTION;
             }
 
             if (ErrorCode == ERROR_SUCCESS)
@@ -2599,7 +1391,6 @@ VOID WINAPI DosInt21h(LPWORD Stack)
 
             break;
         }
-#endif
 
         /* Terminate With Return Code */
         case 0x4C:
@@ -2656,8 +1447,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Internal - Set Current Process ID (Set PSP Address) */
         case 0x50:
         {
-            // FIXME: Is it really what it's done ??
-            CurrentPsp = getBX();
+            DosSetProcessContext(getBX());
             break;
         }
 
@@ -2686,10 +1476,16 @@ 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));
+
+            break;
+        }
 
-            DPRINT1("INT 21h, AH=52h: This application requires the internal DOS List of lists (SYSVARS). UNIMPLEMENTED\n");
+        /* Create Child PSP */
+        case 0x55:
+        {
+            DosCreatePsp(getDX(), getSI());
             break;
         }
 
@@ -2850,44 +1646,30 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Lock/Unlock Region of File */
         case 0x5C:
         {
-            PDOS_SFT_ENTRY SftEntry = DosGetSftEntry(getBX());
-
-            if (SftEntry == NULL || 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(getDX(), getCX()), MAKELONG(getDI(), getSI())))
                 {
                     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(getDX(), getCX()), MAKELONG(getDI(), getSI())))
                 {
                     Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
                 }
                 else
                 {
                     Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                    setAX(GetLastError());
+                    setAX(DosLastError);
                 }
             }
             else
@@ -3038,6 +1820,11 @@ VOID WINAPI DosBreakInterrupt(LPWORD Stack)
     Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
 }
 
+VOID WINAPI DosInt27h(LPWORD Stack)
+{
+    DosTerminateProcess(getCS(), 0, (getDX() + 0x0F) >> 4);
+}
+
 VOID WINAPI DosFastConOut(LPWORD Stack)
 {
     /*
@@ -3109,13 +1896,26 @@ VOID WINAPI DosInt2Fh(LPWORD Stack)
 
 BOOLEAN DosKRNLInitialize(VOID)
 {
-
 #if 1
 
     UCHAR i;
     CHAR CurrentDirectory[MAX_PATH];
     CHAR DosDirectory[DOS_DIR_LENGTH];
     LPSTR Path;
+    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];
@@ -3169,11 +1969,34 @@ 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));
     }
 
 #endif
@@ -3187,9 +2010,16 @@ BOOLEAN DosKRNLInitialize(VOID)
 //  RegisterDosInt32(0x22, DosInt22h        ); // Termination
     RegisterDosInt32(0x23, DosBreakInterrupt); // Ctrl-C / Ctrl-Break
 //  RegisterDosInt32(0x24, DosInt24h        ); // Critical Error
+    RegisterDosInt32(0x27, DosInt27h        ); // Terminate and Stay Resident
     RegisterDosInt32(0x29, DosFastConOut    ); // DOS 2+ Fast Console Output
     RegisterDosInt32(0x2F, DosInt2Fh        );
 
+    /* Load the CON driver */
+    ConDrvInitialize();
+
+    /* Load the XMS driver (HIMEM) */
+    XmsInitialize();
+
     /* Load the EMS driver */
     if (!EmsDrvInitialize(EMS_TOTAL_PAGES))
     {
@@ -3197,12 +2027,6 @@ BOOLEAN DosKRNLInitialize(VOID)
                 "Try reducing the number of EMS pages.\n");
     }
 
-    /* Load the XMS driver (HIMEM) */
-    XmsInitialize();
-
-    /* Load the CON driver */
-    ConDrvInitialize();
-
     return TRUE;
 }