[NTVDM]: Zero-fill memory with RtlZeroMemory (that exists also in NT mode), and use...
[reactos.git] / reactos / subsystems / ntvdm / dos / dos32krnl / dos.c
index 6ce7e3f..f98ef75 100644 (file)
@@ -2,8 +2,9 @@
  * COPYRIGHT:       GPL - See COPYING in the top level directory
  * PROJECT:         ReactOS Virtual DOS Machine
  * FILE:            dos/dos32krnl/dos.c
- * PURPOSE:         VDM DOS Kernel
+ * 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 **********************************************************/
 
@@ -40,6 +41,9 @@ 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 **********************************************************/
 
 /*
@@ -245,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;
@@ -407,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;
@@ -430,6 +445,10 @@ 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);
 
@@ -584,6 +603,87 @@ static VOID DosCopyHandleTable(LPBYTE DestinationTable)
     }
 }
 
+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)
 {
     BYTE SftIndex;
@@ -760,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
@@ -818,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,
@@ -833,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,
@@ -910,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;
         }
 
@@ -951,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
@@ -979,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;
         }
 
@@ -1005,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
@@ -1016,7 +1122,7 @@ DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
             /* Execute */
             CurrentPsp = Segment;
             DiskTransferArea = MAKELONG(0x80, Segment);
-            EmulatorExecute(Segment, 0x100);
+            CpuExecute(Segment, 0x100);
         }
     }
 
@@ -1060,7 +1166,7 @@ DWORD DosStartProcess(IN LPCSTR ExecutablePath,
 
     /* Start simulation */
     SetEvent(VdmTaskEvent);
-    EmulatorSimulate();
+    CpuSimulate();
 
     /* Detach from the console */
     VidBiosDetachFromConsole(); // FIXME: And in fact, detach the full NTVDM UI from the console
@@ -1098,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,
@@ -1124,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;
@@ -1239,7 +1345,7 @@ Done:
         if (CurrentPsp == SYSTEM_PSP)
         {
             ResetEvent(VdmTaskEvent);
-            EmulatorUnsimulate();
+            CpuUnsimulate();
         }
     }
 
@@ -1256,7 +1362,7 @@ Done:
         GetNextVDMCommand(&CommandInfo);
 
         /* Clear the structure */
-        ZeroMemory(&CommandInfo, sizeof(CommandInfo));
+        RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
 
         /* Update the VDM state of the task */
         CommandInfo.TaskId = SessionId;
@@ -1269,8 +1375,8 @@ Done:
     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)
@@ -1300,15 +1406,18 @@ BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
             {
                 /* Console input */
                 InfoWord |= 1 << 0;
+
+                /* It is a device */
+                InfoWord |= 1 << 7;
             }
             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);
@@ -1352,12 +1461,16 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Read Character from STDIN with Echo */
         case 0x01:
         {
+            DPRINT("INT 21h, AH = 01h\n");
+
             // FIXME: Under DOS 2+, input / output handle may be redirected!!!!
+            DoEcho = TRUE;
             Character = DosReadCharacter(DOS_INPUT_HANDLE);
-            DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
+            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;
@@ -1451,6 +1564,8 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         case 0x07:
         case 0x08:
         {
+            DPRINT("Char input without echo\n");
+
             // FIXME: Under DOS 2+, input handle may be redirected!!!!
             Character = DosReadCharacter(DOS_INPUT_HANDLE);
 
@@ -1481,7 +1596,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
              * See Ralf Brown: http://www.ctyme.com/intr/rb-2562.htm
              * for more information.
              */
-            setAL('$');
+            setAL('$'); // *String
             break;
         }
 
@@ -1491,19 +1606,24 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             WORD Count = 0;
             InputBuffer = (PDOS_INPUT_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX());
 
-            DPRINT1("Read Buffered Input\n");
+            DPRINT("Read Buffered Input\n");
 
             while (Count < InputBuffer->MaxLength)
             {
+                // FIXME!! This function should interpret backspaces etc...
+
                 /* Try to read a character (wait) */
                 Character = DosReadCharacter(DOS_INPUT_HANDLE);
 
+                // FIXME: Check whether Ctrl-C / Ctrl-Break is pressed, and call INT 23h if so.
+
                 /* Echo the character and append it to the buffer */
                 DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
                 InputBuffer->Buffer[Count] = Character;
 
+                Count++; /* Carriage returns are also counted */
+
                 if (Character == '\r') break;
-                Count++;
             }
 
             /* Update the length */
@@ -1536,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;
         }
@@ -1640,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;
@@ -1650,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;
         }
 
@@ -1725,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)
@@ -1906,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);
@@ -1936,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);
@@ -1970,10 +2089,16 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         case 0x3F:
         {
             WORD BytesRead = 0;
-            WORD ErrorCode = DosReadFile(getBX(),
-                                         SEG_OFF_TO_PTR(getDS(), getDX()),
-                                         getCX(),
-                                         &BytesRead);
+            WORD ErrorCode;
+
+            DPRINT("INT 21h, AH = 3Fh\n");
+
+            DoEcho = TRUE;
+            ErrorCode = DosReadFile(getBX(),
+                                    SEG_OFF_TO_PTR(getDS(), getDX()),
+                                    getCX(),
+                                    &BytesRead);
+            DoEcho = FALSE;
 
             if (ErrorCode == ERROR_SUCCESS)
             {
@@ -2377,6 +2502,30 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             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:
         {
@@ -2433,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:
         {
@@ -2451,13 +2845,15 @@ 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.
      */
@@ -2466,7 +2862,12 @@ VOID WINAPI DosFastConOut(LPWORD Stack)
     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);
 
@@ -2500,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))