[NTVDM]: Zero-fill memory with RtlZeroMemory (that exists also in NT mode), and use...
[reactos.git] / reactos / subsystems / ntvdm / dos / dos32krnl / dos.c
index f1f9fff..f98ef75 100644 (file)
@@ -1,9 +1,10 @@
 /*
  * COPYRIGHT:       GPL - See COPYING in the top level directory
  * PROJECT:         ReactOS Virtual DOS Machine
- * FILE:            dos.c
- * PURPOSE:         VDM DOS Kernel
+ * FILE:            dos/dos32krnl/dos.c
+ * PURPOSE:         DOS32 Kernel
  * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
  */
 
 /* INCLUDES *******************************************************************/
 #define NDEBUG
 
 #include "emulator.h"
-#include "callback.h"
+#include "cpu/cpu.h"
+#include "int32.h"
 
 #include "dos.h"
 #include "dos/dem.h"
 
 #include "bios/bios.h"
-#include "registers.h"
 
 /* PRIVATE VARIABLES **********************************************************/
 
@@ -29,12 +30,20 @@ static DWORD DiskTransferArea;
 /*static*/ BYTE CurrentDrive;
 static CHAR LastDrive = 'E';
 static CHAR CurrentDirectories[NUM_DRIVES][DOS_DIR_LENGTH];
-static HANDLE DosSystemFileTable[DOS_SFT_SIZE];
-static WORD DosSftRefCount[DOS_SFT_SIZE];
+
+static struct
+{
+    HANDLE Handle;
+    WORD   RefCount;
+} DosSystemFileTable[DOS_SFT_SIZE];
+
 static BYTE DosAllocStrategy = DOS_ALLOC_BEST_FIT;
 static BOOLEAN DosUmbLinked = FALSE;
 static WORD DosErrorLevel = 0x0000;
 
+/* Echo state for INT 21h, AH = 01h and AH = 3Fh */
+BOOLEAN DoEcho = FALSE;
+
 /* PRIVATE FUNCTIONS **********************************************************/
 
 /*
@@ -240,6 +249,14 @@ static BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable)
         /* Set the maximum possible size of the block */
         ReturnSize += NextMcb->Size + 1;
 
+        if (ReturnSize < NewSize)
+        {
+            DPRINT("Cannot expand memory block: insufficient free segments available!\n");
+            DosLastError = ERROR_NOT_ENOUGH_MEMORY;
+            Success = FALSE;
+            goto Done;
+        }
+
         /* Maximize the current block */
         Mcb->Size = ReturnSize;
         Mcb->BlockType = NextMcb->BlockType;
@@ -402,6 +419,9 @@ static WORD DosCopyEnvironmentBlock(LPCVOID Environment, LPCSTR ProgramName)
     /* Add the string buffer size */
     TotalSize += strlen(ProgramName) + 1;
 
+    /* Add the two extra bytes */
+    TotalSize += 2;
+
     /* Allocate the memory for the environment block */
     DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
     if (!DestSegment) return 0;
@@ -425,12 +445,21 @@ static WORD DosCopyEnvironmentBlock(LPCVOID Environment, LPCSTR ProgramName)
     /* Set the final zero */
     *(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 */
 BOOL IsConsoleHandle(HANDLE hHandle)
 {
@@ -455,7 +484,7 @@ BOOL IsConsoleHandle(HANDLE hHandle)
     return GetConsoleMode(hHandle, &dwMode);
 }
 
-static WORD DosOpenHandle(HANDLE Handle)
+WORD DosOpenHandle(HANDLE Handle)
 {
     BYTE i;
     WORD DosHandle;
@@ -482,10 +511,10 @@ static WORD DosOpenHandle(HANDLE Handle)
     for (i = 0; i < DOS_SFT_SIZE; i++)
     {
         /* Check if this is the same handle */
-        if (DosSystemFileTable[i] != Handle) continue;
+        if (DosSystemFileTable[i].Handle != Handle) continue;
 
         /* Already in the table, reference it */
-        DosSftRefCount[i]++;
+        DosSystemFileTable[i].RefCount++;
 
         /* Set the JFT entry to that SFT index */
         HandleTable[DosHandle] = i;
@@ -498,11 +527,11 @@ static WORD DosOpenHandle(HANDLE Handle)
     for (i = 0; i < DOS_SFT_SIZE; i++)
     {
         /* Make sure this is an empty table entry */
-        if (DosSystemFileTable[i] != INVALID_HANDLE_VALUE) continue;
+        if (DosSystemFileTable[i].Handle != INVALID_HANDLE_VALUE) continue;
 
         /* Initialize the empty table entry */
-        DosSystemFileTable[i] = Handle;
-        DosSftRefCount[i] = 1;
+        DosSystemFileTable[i].Handle   = Handle;
+        DosSystemFileTable[i].RefCount = 1;
 
         /* Set the JFT entry to that SFT index */
         HandleTable[DosHandle] = i;
@@ -531,7 +560,7 @@ HANDLE DosGetRealHandle(WORD DosHandle)
     if (HandleTable[DosHandle] == 0xFF) return INVALID_HANDLE_VALUE;
 
     /* Return the Win32 handle */
-    return DosSystemFileTable[HandleTable[DosHandle]];
+    return DosSystemFileTable[HandleTable[DosHandle]].Handle;
 }
 
 static VOID DosCopyHandleTable(LPBYTE DestinationTable)
@@ -553,7 +582,7 @@ static VOID DosCopyHandleTable(LPBYTE DestinationTable)
             DestinationTable[i] = (BYTE)i;
 
             /* Increase the reference count */
-            DosSftRefCount[i]++;
+            DosSystemFileTable[i].RefCount++;
         }
 
         /* Done */
@@ -570,8 +599,89 @@ static VOID DosCopyHandleTable(LPBYTE DestinationTable)
         DestinationTable[i] = SourceTable[i];
 
         /* Increase the reference count */
-        DosSftRefCount[SourceTable[i]]++;
+        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 > 20)
+    {
+        /* Get the segment of the current table */
+        Segment = (LOWORD(PspBlock->HandleTablePtr) >> 4) + HIWORD(PspBlock->HandleTablePtr);
+
+        if (NewSize <= 20)
+        {
+            /* 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 > 20)
+    {
+        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)
@@ -594,16 +704,16 @@ static BOOLEAN DosCloseHandle(WORD DosHandle)
 
     /* Decrement the reference count of the SFT entry */
     SftIndex = HandleTable[DosHandle];
-    DosSftRefCount[SftIndex]--;
+    DosSystemFileTable[SftIndex].RefCount--;
 
     /* Check if the reference count fell to zero */
-    if (!DosSftRefCount[SftIndex])
+    if (!DosSystemFileTable[SftIndex].RefCount)
     {
         /* Close the file, it's no longer needed */
-        CloseHandle(DosSystemFileTable[SftIndex]);
+        CloseHandle(DosSystemFileTable[SftIndex].Handle);
 
         /* Clear the handle */
-        DosSystemFileTable[SftIndex] = INVALID_HANDLE_VALUE;
+        DosSystemFileTable[SftIndex].Handle = INVALID_HANDLE_VALUE;
     }
 
     /* Clear the entry in the JFT */
@@ -641,7 +751,7 @@ static BOOLEAN DosDuplicateHandle(WORD OldHandle, WORD NewHandle)
 
     /* Increment the reference count of the SFT entry */
     SftIndex = HandleTable[OldHandle];
-    DosSftRefCount[SftIndex]++;
+    DosSystemFileTable[SftIndex].RefCount++;
 
     /* Make the new handle point to that SFT entry */
     HandleTable[NewHandle] = SftIndex;
@@ -650,260 +760,11 @@ static BOOLEAN DosDuplicateHandle(WORD OldHandle, WORD NewHandle)
     return TRUE;
 }
 
-static WORD DosCreateFile(LPWORD Handle, LPCSTR FilePath, WORD Attributes)
-{
-    HANDLE FileHandle;
-    WORD DosHandle;
-
-    DPRINT("DosCreateFile: FilePath \"%s\", Attributes 0x%04X\n",
-            FilePath,
-            Attributes);
-
-    /* Create the file */
-    FileHandle = CreateFileA(FilePath,
-                             GENERIC_READ | GENERIC_WRITE,
-                             FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
-                             NULL,
-                             CREATE_ALWAYS,
-                             Attributes,
-                             NULL);
-
-    if (FileHandle == INVALID_HANDLE_VALUE)
-    {
-        /* Return the error code */
-        return (WORD)GetLastError();
-    }
-
-    /* Open the DOS handle */
-    DosHandle = DosOpenHandle(FileHandle);
-
-    if (DosHandle == INVALID_DOS_HANDLE)
-    {
-        /* Close the handle */
-        CloseHandle(FileHandle);
-
-        /* Return the error code */
-        return ERROR_TOO_MANY_OPEN_FILES;
-    }
-
-    /* It was successful */
-    *Handle = DosHandle;
-    return ERROR_SUCCESS;
-}
-
-static WORD DosOpenFile(LPWORD Handle, LPCSTR FilePath, BYTE AccessMode)
-{
-    HANDLE FileHandle;
-    ACCESS_MASK Access = 0;
-    WORD DosHandle;
-
-    DPRINT("DosOpenFile: FilePath \"%s\", AccessMode 0x%04X\n",
-            FilePath,
-            AccessMode);
-
-    /* Parse the access mode */
-    switch (AccessMode & 3)
-    {
-        case 0:
-        {
-            /* Read-only */
-            Access = GENERIC_READ;
-            break;
-        }
-
-        case 1:
-        {
-            /* Write only */
-            Access = GENERIC_WRITE;
-            break;
-        }
 
-        case 2:
-        {
-            /* Read and write */
-            Access = GENERIC_READ | GENERIC_WRITE;
-            break;
-        }
 
-        default:
-        {
-            /* Invalid */
-            return ERROR_INVALID_PARAMETER;
-        }
-    }
 
-    /* Open the file */
-    FileHandle = CreateFileA(FilePath,
-                             Access,
-                             FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
-                             NULL,
-                             OPEN_EXISTING,
-                             FILE_ATTRIBUTE_NORMAL,
-                             NULL);
 
-    if (FileHandle == INVALID_HANDLE_VALUE)
-    {
-        /* Return the error code */
-        return (WORD)GetLastError();
-    }
 
-    /* Open the DOS handle */
-    DosHandle = DosOpenHandle(FileHandle);
-
-    if (DosHandle == INVALID_DOS_HANDLE)
-    {
-        /* Close the handle */
-        CloseHandle(FileHandle);
-
-        /* Return the error code */
-        return ERROR_TOO_MANY_OPEN_FILES;
-    }
-
-    /* It was successful */
-    *Handle = DosHandle;
-    return ERROR_SUCCESS;
-}
-
-WORD DosReadFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesRead)
-{
-    WORD Result = ERROR_SUCCESS;
-    DWORD BytesRead32 = 0;
-    HANDLE Handle = DosGetRealHandle(FileHandle);
-
-    DPRINT("DosReadFile: FileHandle 0x%04X, Count 0x%04X\n", FileHandle, Count);
-
-    /* Make sure the handle is valid */
-    if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
-
-    /* Read the file */
-    if (!ReadFile(Handle, Buffer, Count, &BytesRead32, NULL))
-    {
-        /* Store the error code */
-        Result = (WORD)GetLastError();
-    }
-
-    /* The number of bytes read is always 16-bit */
-    *BytesRead = LOWORD(BytesRead32);
-
-    /* Return the error code */
-    return Result;
-}
-
-WORD DosWriteFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesWritten)
-{
-    WORD Result = ERROR_SUCCESS;
-    DWORD BytesWritten32 = 0;
-    HANDLE Handle = DosGetRealHandle(FileHandle);
-    WORD i;
-
-    DPRINT("DosWriteFile: FileHandle 0x%04X, Count 0x%04X\n",
-           FileHandle,
-           Count);
-
-    /* Make sure the handle is valid */
-    if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
-
-    if (IsConsoleHandle(Handle))
-    {
-        for (i = 0; i < Count; i++)
-        {
-            /* Save AX and BX */
-            USHORT AX = getAX();
-            USHORT BX = getBX();
-
-            /* Set the parameters */
-            setAL(((PCHAR)Buffer)[i]);
-            setBL(DOS_CHAR_ATTRIBUTE);
-            setBH(Bda->VideoPage);
-
-            /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
-            setAH(0x0E);
-            Int32Call(&DosContext, BIOS_VIDEO_INTERRUPT);
-
-            /* Restore AX and BX */
-            setBX(BX);
-            setAX(AX);
-
-            BytesWritten32++;
-        }
-    }
-    else
-    {
-        /* Write the file */
-        if (!WriteFile(Handle, Buffer, Count, &BytesWritten32, NULL))
-        {
-            /* Store the error code */
-            Result = (WORD)GetLastError();
-        }
-    }
-
-    /* The number of bytes written is always 16-bit */
-    *BytesWritten = LOWORD(BytesWritten32);
-
-    /* Return the error code */
-    return Result;
-}
-
-static WORD DosSeekFile(WORD FileHandle, LONG Offset, BYTE Origin, LPDWORD NewOffset)
-{
-    WORD Result = ERROR_SUCCESS;
-    DWORD FilePointer;
-    HANDLE Handle = DosGetRealHandle(FileHandle);
-
-    DPRINT("DosSeekFile: FileHandle 0x%04X, Offset 0x%08X, Origin 0x%02X\n",
-           FileHandle,
-           Offset,
-           Origin);
-
-    /* Make sure the handle is valid */
-    if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
-
-    /* Check if the origin is valid */
-    if (Origin != FILE_BEGIN && Origin != FILE_CURRENT && Origin != FILE_END)
-    {
-        return ERROR_INVALID_FUNCTION;
-    }
-
-    /* Move the file pointer */
-    FilePointer = SetFilePointer(Handle, Offset, NULL, Origin);
-
-    /* Check if there's a possibility the operation failed */
-    if (FilePointer == INVALID_SET_FILE_POINTER)
-    {
-        /* Get the real error code */
-        Result = (WORD)GetLastError();
-    }
-
-    if (Result != ERROR_SUCCESS)
-    {
-        /* The operation did fail */
-        return Result;
-    }
-
-    /* Return the file pointer, if requested */
-    if (NewOffset) *NewOffset = FilePointer;
-
-    /* Return success */
-    return ERROR_SUCCESS;
-}
-
-static BOOLEAN DosFlushFileBuffers(WORD FileHandle)
-{
-    HANDLE Handle = DosGetRealHandle(FileHandle);
-
-    /* Make sure the handle is valid */
-    if (Handle == INVALID_HANDLE_VALUE) return FALSE;
-
-    /*
-     * No need to check whether the handle is a console handle since
-     * FlushFileBuffers() automatically does this check and calls
-     * FlushConsoleInputBuffer() for us.
-     */
-    // if (IsConsoleHandle(Handle))
-    //    return (BOOLEAN)FlushConsoleInputBuffer(Handle);
-    // else
-    return (BOOLEAN)FlushFileBuffers(Handle);
-}
 
 static BOOLEAN DosChangeDrive(BYTE Drive)
 {
@@ -999,7 +860,7 @@ VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WOR
     PDOS_PSP PspBlock = SEGMENT_TO_PSP(PspSegment);
     LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
 
-    ZeroMemory(PspBlock, sizeof(DOS_PSP));
+    RtlZeroMemory(PspBlock, sizeof(*PspBlock));
 
     /* Set the exit interrupt */
     PspBlock->Exit[0] = 0xCD; // int 0x20
@@ -1057,6 +918,7 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
     PIMAGE_DOS_HEADER Header;
     PDWORD RelocationTable;
     PWORD RelocWord;
+    LPSTR CmdLinePtr = (LPSTR)CommandLine;
 
     DPRINT1("DosLoadExecutable(%d, %s, %s, %s, 0x%08X, 0x%08X)\n",
             LoadType,
@@ -1072,6 +934,10 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
         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,
@@ -1149,7 +1015,7 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
         /* Check if at least the lowest allocation was successful */
         if (Segment == 0)
         {
-            Result = ERROR_NOT_ENOUGH_MEMORY;
+            Result = DosLastError;
             goto Cleanup;
         }
 
@@ -1190,14 +1056,14 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
             setES(Segment);
 
             /* Set the stack to the location from the header */
-            EmulatorSetStack(Segment + (sizeof(DOS_PSP) >> 4) + Header->e_ss,
-                             Header->e_sp);
+            setSS(Segment + (sizeof(DOS_PSP) >> 4) + Header->e_ss);
+            setSP(Header->e_sp);
 
             /* Execute */
             CurrentPsp = Segment;
             DiskTransferArea = MAKELONG(0x80, Segment);
-            EmulatorExecute(Segment + Header->e_cs + (sizeof(DOS_PSP) >> 4),
-                            Header->e_ip);
+            CpuExecute(Segment + Header->e_cs + (sizeof(DOS_PSP) >> 4),
+                       Header->e_ip);
         }
     }
     else
@@ -1218,7 +1084,7 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
         Segment = DosAllocateMemory(MaxAllocSize, NULL);
         if (Segment == 0)
         {
-            Result = ERROR_ARENA_TRASHED;
+            Result = DosLastError;
             goto Cleanup;
         }
 
@@ -1244,7 +1110,8 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
             setES(Segment);
 
             /* Set the stack to the last word of the segment */
-            EmulatorSetStack(Segment, 0xFFFE);
+            setSS(Segment);
+            setSP(0xFFFE);
 
             /*
              * Set the value on the stack to 0, so that a near return
@@ -1255,7 +1122,7 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
             /* Execute */
             CurrentPsp = Segment;
             DiskTransferArea = MAKELONG(0x80, Segment);
-            EmulatorExecute(Segment, 0x100);
+            CpuExecute(Segment, 0x100);
         }
     }
 
@@ -1279,6 +1146,36 @@ Cleanup:
     return Result;
 }
 
+DWORD DosStartProcess(IN LPCSTR ExecutablePath,
+                      IN LPCSTR CommandLine,
+                      IN PVOID Environment)
+{
+    DWORD Result;
+
+    Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
+                               ExecutablePath,
+                               CommandLine,
+                               Environment,
+                               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
+
+    /* Start simulation */
+    SetEvent(VdmTaskEvent);
+    CpuSimulate();
+
+    /* Detach from the console */
+    VidBiosDetachFromConsole(); // FIXME: And in fact, detach the full NTVDM UI from the console
+
+Quit:
+    return Result;
+}
+
+#ifndef STANDALONE
 WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
                       LPCSTR ProgramName,
                       PDOS_EXEC_PARAM_BLOCK Parameters)
@@ -1307,8 +1204,8 @@ WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
     }
 
     /* Set up the startup info structure */
-    ZeroMemory(&StartupInfo, sizeof(STARTUPINFOA));
-    StartupInfo.cb = sizeof(STARTUPINFOA);
+    RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
+    StartupInfo.cb = sizeof(StartupInfo);
 
     /* Create the process */
     if (!CreateProcessA(ProgramName,
@@ -1333,7 +1230,7 @@ WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
         case SCS_WOW_BINARY:
         {
             /* Clear the structure */
-            ZeroMemory(&CommandInfo, sizeof(CommandInfo));
+            RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
 
             /* Initialize the structure members */
             CommandInfo.TaskId = SessionId;
@@ -1363,16 +1260,17 @@ WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
             GetNextVDMCommand(&CommandInfo);
 
             /* Load the executable */
-            Result= DosLoadExecutable(LoadType,
-                                      AppName,
-                                      CmdLine,
-                                      Env,
-                                      &Parameters->StackLocation,
-                                      &Parameters->EntryPoint);
+            Result = DosLoadExecutable(LoadType,
+                                       AppName,
+                                       CmdLine,
+                                       Env,
+                                       &Parameters->StackLocation,
+                                       &Parameters->EntryPoint);
             if (Result != ERROR_SUCCESS)
             {
                 DisplayMessage(L"Could not load '%S'. Error: %u", AppName, Result);
-                break;
+                // FIXME: Decrement the reenter count. Or, instead, just increment
+                // the VDM reenter count *only* if this call succeeds...
             }
 
             break;
@@ -1392,6 +1290,7 @@ WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
 
     return ERROR_SUCCESS;
 }
+#endif
 
 VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
 {
@@ -1400,7 +1299,6 @@ VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
     PDOS_MCB CurrentMcb;
     LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
     PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
-    VDM_COMMAND_INFO CommandInfo;
 
     DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X\n",
            Psp,
@@ -1447,34 +1345,38 @@ Done:
         if (CurrentPsp == SYSTEM_PSP)
         {
             ResetEvent(VdmTaskEvent);
-            EmulatorUnsimulate();
+            CpuUnsimulate();
         }
     }
 
+#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)
     {
+        VDM_COMMAND_INFO CommandInfo;
+
         /* Decrement the re-entry count */
         CommandInfo.TaskId = SessionId;
         CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
         GetNextVDMCommand(&CommandInfo);
 
         /* Clear the structure */
-        ZeroMemory(&CommandInfo, sizeof(CommandInfo));
+        RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
 
         /* Update the VDM state of the task */
         CommandInfo.TaskId = SessionId;
         CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
         GetNextVDMCommand(&CommandInfo);
     }
+#endif
 
     /* Save the return code - Normal termination */
     DosErrorLevel = MAKEWORD(ReturnCode, 0x00);
 
     /* Return control to the parent process */
-    EmulatorExecute(HIWORD(PspBlock->TerminateAddress),
-                    LOWORD(PspBlock->TerminateAddress));
+    CpuExecute(HIWORD(PspBlock->TerminateAddress),
+               LOWORD(PspBlock->TerminateAddress));
 }
 
 BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
@@ -1500,19 +1402,22 @@ BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
              * for a list of possible flags.
              */
 
-            if (Handle == DosSystemFileTable[0])
+            if (Handle == DosSystemFileTable[DOS_INPUT_HANDLE].Handle)
             {
                 /* Console input */
                 InfoWord |= 1 << 0;
+
+                /* It is a device */
+                InfoWord |= 1 << 7;
             }
-            else if (Handle == DosSystemFileTable[1])
+            else if (Handle == DosSystemFileTable[DOS_OUTPUT_HANDLE].Handle)
             {
                 /* Console output */
                 InfoWord |= 1 << 1;
-            }
 
-            /* It is a device */
-            InfoWord |= 1 << 7;
+                /* It is a device */
+                InfoWord |= 1 << 7;
+            }
 
             /* Return the device information word */
             setDX(InfoWord);
@@ -1556,11 +1461,16 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Read Character from STDIN with Echo */
         case 0x01:
         {
-            Character = DosReadCharacter();
-            DosPrintCharacter(Character);
+            DPRINT("INT 21h, AH = 01h\n");
+
+            // FIXME: Under DOS 2+, input / output handle may be redirected!!!!
+            DoEcho = TRUE;
+            Character = DosReadCharacter(DOS_INPUT_HANDLE);
+            DoEcho = FALSE;
 
-            /* Let the BOP repeat if needed */
-            if (getCF()) break;
+            // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
+            // Check also Ctrl-P and set echo-to-printer flag.
+            // Ctrl-Z is not interpreted.
 
             setAL(Character);
             break;
@@ -1569,8 +1479,9 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Write Character to STDOUT */
         case 0x02:
         {
+            // FIXME: Under DOS 2+, output handle may be redirected!!!!
             Character = getDL();
-            DosPrintCharacter(Character);
+            DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
 
             /*
              * We return the output character (DOS 2.1+).
@@ -1588,7 +1499,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         {
             // FIXME: Really read it from STDAUX!
             DPRINT1("INT 16h, 03h: Read character from STDAUX is HALFPLEMENTED\n");
-            setAL(DosReadCharacter());
+            // setAL(DosReadCharacter());
             break;
         }
 
@@ -1597,7 +1508,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         {
             // FIXME: Really write it to STDAUX!
             DPRINT1("INT 16h, 04h: Write character to STDAUX is HALFPLEMENTED\n");
-            DosPrintCharacter(getDL());
+            // DosPrintCharacter(getDL());
             break;
         }
 
@@ -1616,10 +1527,12 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         {
             Character = getDL();
 
+            // FIXME: Under DOS 2+, output handle may be redirected!!!!
+
             if (Character != 0xFF)
             {
                 /* Output */
-                DosPrintCharacter(Character);
+                DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
 
                 /*
                  * We return the output character (DOS 2.1+).
@@ -1634,7 +1547,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                 if (DosCheckInput())
                 {
                     Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF;
-                    setAL(DosReadCharacter());
+                    setAL(DosReadCharacter(DOS_INPUT_HANDLE));
                 }
                 else
                 {
@@ -1651,10 +1564,17 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         case 0x07:
         case 0x08:
         {
-            Character = DosReadCharacter();
+            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;
+            // /* Let the BOP repeat if needed */
+            // if (getCF()) break;
 
             setAL(Character);
             break;
@@ -1667,7 +1587,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
 
             while (*String != '$')
             {
-                DosPrintCharacter(*String);
+                DosPrintCharacter(DOS_OUTPUT_HANDLE, *String);
                 String++;
             }
 
@@ -1676,33 +1596,39 @@ VOID WINAPI DosInt21h(LPWORD Stack)
              * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
              * for more information.
              */
-            setAL('$');
+            setAL('$'); // *String
             break;
         }
 
         /* Read Buffered Input */
         case 0x0A:
         {
+            WORD Count = 0;
             InputBuffer = (PDOS_INPUT_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX());
 
-            while (Stack[STACK_COUNTER] < InputBuffer->MaxLength)
+            DPRINT("Read Buffered Input\n");
+
+            while (Count < InputBuffer->MaxLength)
             {
-                /* Try to read a character */
-                Character = DosReadCharacter();
+                // FIXME!! This function should interpret backspaces etc...
+
+                /* Try to read a character (wait) */
+                Character = DosReadCharacter(DOS_INPUT_HANDLE);
 
-                /* If it's not ready yet, let the BOP repeat */
-                if (getCF()) break;
+                // 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(Character);
-                InputBuffer->Buffer[Stack[STACK_COUNTER]] = Character;
+                DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
+                InputBuffer->Buffer[Count] = Character;
+
+                Count++; /* Carriage returns are also counted */
 
                 if (Character == '\r') break;
-                Stack[STACK_COUNTER]++;
             }
 
             /* Update the length */
-            InputBuffer->Length = Stack[STACK_COUNTER];
+            InputBuffer->Length = Count;
+
             break;
         }
 
@@ -1719,7 +1645,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             BYTE InputFunction = getAL();
 
             /* Flush STDIN buffer */
-            DosFlushFileBuffers(DOS_INPUT_HANDLE); // Maybe just create a DosFlushInputBuffer...
+            DosFlushFileBuffers(DOS_INPUT_HANDLE);
 
             /*
              * If the input function number contained in AL is valid, i.e.
@@ -1730,13 +1656,9 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                 InputFunction == 0x07 || InputFunction == 0x08 ||
                 InputFunction == 0x0A)
             {
+                /* Call ourselves recursively */
                 setAH(InputFunction);
-                /*
-                 * Instead of calling ourselves really recursively as in:
-                 * DosInt21h(Stack);
-                 * prefer resetting the CF flag to let the BOP repeat.
-                 */
-                setCF(1);
+                DosInt21h(Stack);
             }
             break;
         }
@@ -1834,7 +1756,8 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         case 0x25:
         {
             ULONG FarPointer = MAKELONG(getDX(), getDS());
-            DPRINT1("Setting interrupt 0x%x ...\n", getAL());
+            DPRINT1("Setting interrupt 0x%02X to %04X:%04X ...\n",
+                    getAL(), HIWORD(FarPointer), LOWORD(FarPointer));
 
             /* Write the new far pointer to the IDT */
             ((PULONG)BaseAddress)[getAL()] = FarPointer;
@@ -1844,7 +1767,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Create New PSP */
         case 0x26:
         {
-            DPRINT1("INT 21h, 26h - Create New PSP is UNIMPLEMENTED\n");
+            DPRINT1("INT 21h, AH = 26h - Create New PSP is UNIMPLEMENTED\n");
             break;
         }
 
@@ -1919,8 +1842,9 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                  * Return DOS OEM number:
                  * 0x00 for IBM PC-DOS
                  * 0x02 for packaged MS-DOS
+                 * 0xFF for NT DOS
                  */
-                setBH(0x02);
+                setBH(0xFF);
             }
 
             if (LOBYTE(PspBlock->DosVersion) >= 5 && getAL() == 0x01)
@@ -2100,15 +2024,16 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             break;
         }
 
-        /* Create File */
+        /* Create or Truncate File */
         case 0x3C:
         {
             WORD FileHandle;
             WORD ErrorCode = DosCreateFile(&FileHandle,
                                            (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
+                                           CREATE_ALWAYS,
                                            getCX());
 
-            if (ErrorCode == 0)
+            if (ErrorCode == ERROR_SUCCESS)
             {
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
                 setAX(FileHandle);
@@ -2130,7 +2055,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                                          (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
                                          getAL());
 
-            if (ErrorCode == 0)
+            if (ErrorCode == ERROR_SUCCESS)
             {
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
                 setAX(FileHandle);
@@ -2163,47 +2088,17 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Read from File or Device */
         case 0x3F:
         {
-            WORD Handle    = getBX();
-            LPBYTE Buffer  = (LPBYTE)SEG_OFF_TO_PTR(getDS(), getDX());
-            WORD Count     = getCX();
             WORD BytesRead = 0;
-            WORD ErrorCode = ERROR_SUCCESS;
-            CHAR Character;
+            WORD ErrorCode;
 
-            if (IsConsoleHandle(DosGetRealHandle(Handle)))
-            {
-                while (Stack[STACK_COUNTER] < Count)
-                {
-                    /* Read a character from the BIOS */
-                    Character = LOBYTE(BiosGetCharacter());
-
-                    /* Stop if the BOP needs to be repeated */
-                    if (getCF()) break;
-
-                    // FIXME: Security checks!
-                    DosPrintCharacter(Character);
-                    Buffer[Stack[STACK_COUNTER]++] = Character;
-
-                    if (Character == '\r')
-                    {
-                        /* Stop on first carriage return */
-                        DosPrintCharacter('\n');
-                        break;
-                    }
-                }
+            DPRINT("INT 21h, AH = 3Fh\n");
 
-                if (Character != '\r')
-                {
-                    if (Stack[STACK_COUNTER] < Count) ErrorCode = ERROR_NOT_READY;
-                    else BytesRead = Count;
-                }
-                else BytesRead = Stack[STACK_COUNTER];
-            }
-            else
-            {
-                /* Use the file reading function */
-                ErrorCode = DosReadFile(Handle, Buffer, Count, &BytesRead);
-            }
+            DoEcho = TRUE;
+            ErrorCode = DosReadFile(getBX(),
+                                    SEG_OFF_TO_PTR(getDS(), getDX()),
+                                    getCX(),
+                                    &BytesRead);
+            DoEcho = FALSE;
 
             if (ErrorCode == ERROR_SUCCESS)
             {
@@ -2215,6 +2110,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                 setAX(ErrorCode);
             }
+
             break;
         }
 
@@ -2358,7 +2254,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             WORD NewHandle;
             HANDLE Handle = DosGetRealHandle(getBX());
 
-            if (Handle != INVALID_HANDLE_VALUE)
+            if (Handle == INVALID_HANDLE_VALUE)
             {
                 /* The handle is invalid */
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
@@ -2491,6 +2387,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             break;
         }
 
+#ifndef STANDALONE
         /* Execute */
         case 0x4B:
         {
@@ -2511,6 +2408,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
 
             break;
         }
+#endif
 
         /* Terminate With Return Code */
         case 0x4C:
@@ -2540,8 +2438,11 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                                                  getCX());
 
             setAX(Result);
-            if (Result == ERROR_SUCCESS) Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
-            else Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+
+            if (Result == ERROR_SUCCESS)
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            else
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
 
             break;
         }
@@ -2552,8 +2453,11 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             WORD Result = (WORD)demFileFindNext(FAR_POINTER(DiskTransferArea));
 
             setAX(Result);
-            if (Result == ERROR_SUCCESS) Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
-            else Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+
+            if (Result == ERROR_SUCCESS)
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            else
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
 
             break;
         }
@@ -2581,6 +2485,47 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             break;
         }
 
+        /* Internal - Get "List of lists" (SYSVARS) */
+        case 0x52:
+        {
+            /*
+             * On return, ES points at the DOS data segment (see also INT 2F/AX=1203h).
+             * See Ralf Brown: http://www.ctyme.com/intr/rb-2983.htm
+             * for more information.
+             */
+
+            /* Return the DOS "list of lists" in ES:BX */
+            setES(0x0000);
+            setBX(0x0000);
+
+            DisplayMessage(L"Required for AARD code, do you remember? :P");
+            break;
+        }
+
+        /* Rename File */
+        case 0x56:
+        {
+            LPSTR ExistingFileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
+            LPSTR NewFileName      = (LPSTR)SEG_OFF_TO_PTR(getES(), getDI());
+
+            /*
+             * See Ralf Brown: http://www.ctyme.com/intr/rb-2990.htm
+             * for more information.
+             */
+
+            if (MoveFileA(ExistingFileName, NewFileName))
+            {
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            }
+            else
+            {
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                setAX(GetLastError());
+            }
+
+            break;
+        }
+
         /* Get/Set Memory Management Options */
         case 0x58:
         {
@@ -2637,6 +2582,251 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             break;
         }
 
+        /* Get Extended Error Information */
+        case 0x59:
+        {
+            DPRINT1("INT 21h, AH = 59h, BX = %04Xh - Get Extended Error Information is UNIMPLEMENTED\n",
+                    getBX());
+            break;
+        }
+
+        /* Create Temporary File */
+        case 0x5A:
+        {
+            LPSTR PathName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
+            LPSTR FileName = PathName; // The buffer for the path and the full file name is the same.
+            UINT  uRetVal;
+            WORD  FileHandle;
+            WORD  ErrorCode;
+
+            /*
+             * See Ralf Brown: http://www.ctyme.com/intr/rb-3014.htm
+             * for more information.
+             */
+
+            // FIXME: Check for buffer validity?
+            // It should be a ASCIZ path ending with a '\' + 13 zero bytes
+            // to receive the generated filename.
+
+            /* First create the temporary file */
+            uRetVal = GetTempFileNameA(PathName, NULL, 0, FileName);
+            if (uRetVal == 0)
+            {
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                setAX(GetLastError());
+                break;
+            }
+
+            /* Now try to open it in read/write access */
+            ErrorCode = DosOpenFile(&FileHandle, FileName, 2);
+            if (ErrorCode == ERROR_SUCCESS)
+            {
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                setAX(FileHandle);
+            }
+            else
+            {
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                setAX(ErrorCode);
+            }
+
+            break;
+        }
+
+        /* Create New File */
+        case 0x5B:
+        {
+            WORD FileHandle;
+            WORD ErrorCode = DosCreateFile(&FileHandle,
+                                           (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
+                                           CREATE_NEW,
+                                           getCX());
+
+            if (ErrorCode == ERROR_SUCCESS)
+            {
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                setAX(FileHandle);
+            }
+            else
+            {
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                setAX(ErrorCode);
+            }
+
+            break;
+        }
+
+        /* Lock/Unlock Region of File */
+        case 0x5C:
+        {
+            HANDLE Handle = DosGetRealHandle(getBX());
+
+            if (Handle == INVALID_HANDLE_VALUE)
+            {
+                /* The handle is invalid */
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                setAX(ERROR_INVALID_HANDLE);
+                break;
+            }
+
+            if (getAL() == 0x00)
+            {
+                /* Lock region of file */
+                if (LockFile(Handle,
+                             MAKELONG(getCX(), getDX()), 0,
+                             MAKELONG(getSI(), getDI()), 0))
+                {
+                    Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                }
+                else
+                {
+                    Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                    setAX(GetLastError());
+                }
+            }
+            else if (getAL() == 0x01)
+            {
+                /* Unlock region of file */
+                if (UnlockFile(Handle,
+                               MAKELONG(getCX(), getDX()), 0,
+                               MAKELONG(getSI(), getDI()), 0))
+                {
+                    Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                }
+                else
+                {
+                    Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                    setAX(GetLastError());
+                }
+            }
+            else
+            {
+                /* Invalid subfunction */
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                setAX(ERROR_INVALID_FUNCTION);
+            }
+
+            break;
+        }
+
+        /* Canonicalize File Name or Path */
+        case 0x60:
+        {
+            /*
+             * See Ralf Brown: http://www.ctyme.com/intr/rb-3137.htm
+             * for more information.
+             */
+
+            /*
+             * We suppose that the DOS app gave to us a valid
+             * 128-byte long buffer for the canonicalized name.
+             */
+            DWORD dwRetVal = GetFullPathNameA(SEG_OFF_TO_PTR(getDS(), getSI()),
+                                              128,
+                                              SEG_OFF_TO_PTR(getES(), getDI()),
+                                              NULL);
+            if (dwRetVal == 0)
+            {
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                setAX(GetLastError());
+            }
+            else
+            {
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                setAX(0x0000);
+            }
+
+            // FIXME: Convert the full path name into short version.
+            // We cannot reliably use GetShortPathName, because it fails
+            // if the path name given doesn't exist. However this DOS
+            // function AH=60h should be able to work even for non-existing
+            // path and file names.
+
+            break;
+        }
+
+        /* Set Handle Count */
+        case 0x67:
+        {
+            if (!DosResizeHandleTable(getBX()))
+            {
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                setAX(DosLastError);
+            }
+            else Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+
+            break;
+        }
+
+        /* Commit File */
+        case 0x68:
+        case 0x6A:
+        {
+            /*
+             * Function 6Ah is identical to function 68h,
+             * and sets AH to 68h if success.
+             * See Ralf Brown: http://www.ctyme.com/intr/rb-3176.htm
+             * for more information.
+             */
+            setAH(0x68);
+
+            if (DosFlushFileBuffers(getBX()))
+            {
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            }
+            else
+            {
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                setAX(GetLastError());
+            }
+
+            break;
+        }
+
+        /* Extended Open/Create */
+        case 0x6C:
+        {
+            WORD FileHandle;
+            WORD CreationStatus;
+            WORD ErrorCode;
+
+            /* Check for AL == 00 */
+            if (getAL() != 0x00)
+            {
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                setAX(ERROR_INVALID_FUNCTION);
+                break;
+            }
+
+            /*
+             * See Ralf Brown: http://www.ctyme.com/intr/rb-3179.htm
+             * for the full detailed description.
+             *
+             * WARNING: BH contains some extended flags that are NOT SUPPORTED.
+             */
+
+            ErrorCode = DosCreateFileEx(&FileHandle,
+                                        &CreationStatus,
+                                        (LPCSTR)SEG_OFF_TO_PTR(getDS(), getSI()),
+                                        getBL(),
+                                        getDL(),
+                                        getCX());
+
+            if (ErrorCode == ERROR_SUCCESS)
+            {
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                setCX(CreationStatus);
+                setAX(FileHandle);
+            }
+            else
+            {
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                setAX(ErrorCode);
+            }
+
+            break;
+        }
+
         /* Unsupported */
         default:
         {
@@ -2655,48 +2845,29 @@ VOID WINAPI DosBreakInterrupt(LPWORD Stack)
 
     /* Stop the VDM task */
     ResetEvent(VdmTaskEvent);
-    EmulatorUnsimulate();
+    CpuUnsimulate();
 }
 
 VOID WINAPI DosFastConOut(LPWORD Stack)
 {
     /*
      * This is the DOS 2+ Fast Console Output Interrupt.
+     * The default handler under DOS 2.x and 3.x simply calls INT 10h/AH=0Eh.
+     *
      * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
      * for more information.
      */
 
-#if 0
-    if (Stack[STACK_COUNTER] == 0)
-    {
-        Stack[STACK_COUNTER]++;
-
-        /* Save AX and BX */
-        Stack[STACK_VAR_A] = getAX();
-        Stack[STACK_VAR_B] = getBX();
-
-        /* Rewind the BOP manually, we can't use CF because the interrupt could modify it */
-        EmulatorExecute(getCS(), getIP() - 4);
-
-        /* Call INT 0x10, AH = 0x0E */
-        setAH(0x0E);
-        setBL(DOS_CHAR_ATTRIBUTE);
-        setBH(Bda->VideoPage);
-
-        EmulatorInterrupt(0x10);
-    }
-    else
-    {
-        /* Restore AX and BX */
-        setAX(Stack[STACK_VAR_A]);
-        setBX(Stack[STACK_VAR_B]);
-    }
-#else
     /* Save AX and BX */
     USHORT AX = getAX();
     USHORT BX = getBX();
 
-    /* Set the parameters (AL = character, already set) */
+    /*
+     * Set the parameters:
+     * AL contains the character to print (already set),
+     * BL contains the character attribute,
+     * BH contains the video page to use.
+     */
     setBL(DOS_CHAR_ATTRIBUTE);
     setBH(Bda->VideoPage);
 
@@ -2707,12 +2878,11 @@ VOID WINAPI DosFastConOut(LPWORD Stack)
     /* Restore AX and BX */
     setBX(BX);
     setAX(AX);
-#endif
 }
 
 VOID WINAPI DosInt2Fh(LPWORD Stack)
 {
-    DPRINT1("DOS System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
+    DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
             getAH(), getAL());
     Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
 }
@@ -2731,7 +2901,7 @@ BOOLEAN DosKRNLInitialize(VOID)
     WCHAR Buffer[256];
 
     /* Clear the current directory buffer */
-    ZeroMemory(CurrentDirectories, sizeof(CurrentDirectories));
+    RtlZeroMemory(CurrentDirectories, sizeof(CurrentDirectories));
 
     /* Get the current directory */
     if (!GetCurrentDirectoryA(MAX_PATH, CurrentDirectory))
@@ -2778,17 +2948,19 @@ BOOLEAN DosKRNLInitialize(VOID)
     /* Initialize the SFT */
     for (i = 0; i < DOS_SFT_SIZE; i++)
     {
-        DosSystemFileTable[i] = INVALID_HANDLE_VALUE;
-        DosSftRefCount[i] = 0;
+        DosSystemFileTable[i].Handle   = INVALID_HANDLE_VALUE;
+        DosSystemFileTable[i].RefCount = 0;
     }
 
     /* Get handles to standard I/O devices */
-    DosSystemFileTable[0] = GetStdHandle(STD_INPUT_HANDLE);
-    DosSystemFileTable[1] = GetStdHandle(STD_OUTPUT_HANDLE);
-    DosSystemFileTable[2] = GetStdHandle(STD_ERROR_HANDLE);
+    DosSystemFileTable[0].Handle = GetStdHandle(STD_INPUT_HANDLE);
+    DosSystemFileTable[1].Handle = GetStdHandle(STD_OUTPUT_HANDLE);
+    DosSystemFileTable[2].Handle = GetStdHandle(STD_ERROR_HANDLE);
 
     /* Initialize the reference counts */
-    DosSftRefCount[0] = DosSftRefCount[1] = DosSftRefCount[2] = 1;
+    DosSystemFileTable[0].RefCount =
+    DosSystemFileTable[1].RefCount =
+    DosSystemFileTable[2].RefCount = 1;
 
 #endif