[NTVDM]
[reactos.git] / subsystems / ntvdm / dos.c
index a9d63f7..837ae25 100644 (file)
@@ -8,6 +8,8 @@
 
 /* INCLUDES *******************************************************************/
 
+#define NDEBUG
+
 #include "dos.h"
 #include "bios.h"
 #include "emulator.h"
 /* PRIVATE VARIABLES **********************************************************/
 
 static WORD CurrentPsp = SYSTEM_PSP;
+static WORD DosLastError = 0;
 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 BYTE DosAllocStrategy = DOS_ALLOC_BEST_FIT;
 static BOOLEAN DosUmbLinked = FALSE;
 
 /* PRIVATE FUNCTIONS **********************************************************/
 
+/* 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 VOID DosCombineFreeBlocks(WORD StartBlock)
 {
     PDOS_MCB CurrentMcb = SEGMENT_TO_MCB(StartBlock), NextMcb;
@@ -50,7 +81,7 @@ static VOID DosCombineFreeBlocks(WORD StartBlock)
     }
 }
 
-static WORD DosCopyEnvironmentBlock(WORD SourceSegment)
+static WORD DosCopyEnvironmentBlock(WORD SourceSegment, LPCSTR ProgramName)
 {
     PCHAR Ptr, SourceBuffer, DestBuffer = NULL;
     ULONG TotalSize = 0;
@@ -66,8 +97,11 @@ static WORD DosCopyEnvironmentBlock(WORD SourceSegment)
     }
     TotalSize++;
 
+    /* Add the string buffer size */
+    TotalSize += strlen(ProgramName) + 1;
+
     /* Allocate the memory for the environment block */
-    DestSegment = DosAllocateMemory((TotalSize + 0x0F) >> 4, NULL);
+    DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
     if (!DestSegment) return 0;
 
     Ptr = SourceBuffer;
@@ -79,15 +113,18 @@ static WORD DosCopyEnvironmentBlock(WORD SourceSegment)
         strcpy(DestBuffer, Ptr);
 
         /* Advance to the next string */
-        Ptr += strlen(Ptr) + 1;
         DestBuffer += strlen(Ptr);
+        Ptr += strlen(Ptr) + 1;
 
         /* Put a zero after the string */
         *(DestBuffer++) = 0;
     }
 
     /* Set the final zero */
-    *DestBuffer = 0;
+    *(DestBuffer++) = 0;
+
+    /* Copy the program name after the environment block */
+    strcpy(DestBuffer, ProgramName);
 
     return DestSegment;
 }
@@ -195,7 +232,7 @@ static VOID DosCopyHandleTable(LPBYTE DestinationTable)
         for (i = 0; i <= 2; i++)
         {
             /* Set the index in the SFT */
-            DestinationTable[i] = i;
+            DestinationTable[i] = (BYTE)i;
 
             /* Increase the reference count */
             DosSftRefCount[i]++;
@@ -225,6 +262,16 @@ WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable)
 {
     WORD Result = 0, Segment = FIRST_MCB_SEGMENT, MaxSize = 0;
     PDOS_MCB CurrentMcb, NextMcb;
+    BOOLEAN SearchUmb = FALSE;
+
+    DPRINT("DosAllocateMemory: Size 0x%04X\n", Size);
+
+    if (DosUmbLinked && (DosAllocStrategy & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW)))
+    {
+        /* Search UMB first */
+        Segment = UMB_START_SEGMENT;
+        SearchUmb = TRUE;
+    }
 
     while (TRUE)
     {
@@ -234,6 +281,8 @@ WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable)
         /* Make sure it's valid */
         if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z')
         {
+            DPRINT("The DOS memory arena is corrupted!\n");
+            DosLastError = ERROR_ARENA_TRASHED;
             return 0;
         }
 
@@ -249,23 +298,59 @@ WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable)
         /* Check if this block is big enough */
         if (CurrentMcb->Size < Size) goto Next;
 
-        /* It is, update the smallest found so far */
-        if ((Result == 0) || (CurrentMcb->Size < SEGMENT_TO_MCB(Result)->Size))
+        switch (DosAllocStrategy & 0x3F)
         {
-            Result = Segment;
+            case DOS_ALLOC_FIRST_FIT:
+            {
+                /* For first fit, stop immediately */
+                Result = Segment;
+                goto Done;
+            }
+
+            case DOS_ALLOC_BEST_FIT:
+            {
+                /* For best fit, update the smallest block found so far */
+                if ((Result == 0) || (CurrentMcb->Size < SEGMENT_TO_MCB(Result)->Size))
+                {
+                    Result = Segment;
+                }
+
+                break;
+            }
+
+            case DOS_ALLOC_LAST_FIT:
+            {
+                /* For last fit, make the current block the result, but keep searching */
+                Result = Segment;
+                break;
+            }
         }
 
 Next:
         /* If this was the last MCB in the chain, quit */
-        if (CurrentMcb->BlockType == 'Z') break;
+        if (CurrentMcb->BlockType == 'Z')
+        {
+            /* Check if nothing was found while searching through UMBs */
+            if ((Result == 0) && SearchUmb && (DosAllocStrategy & DOS_ALLOC_HIGH_LOW))
+            {
+                /* Search low memory */
+                Segment = FIRST_MCB_SEGMENT;
+                continue;
+            }
+
+            break;
+        }
 
         /* Otherwise, update the segment and continue */
         Segment += CurrentMcb->Size + 1;
     }
 
+Done:
+
     /* If we didn't find a free block, return 0 */
     if (Result == 0)
     {
+        DosLastError = ERROR_NOT_ENOUGH_MEMORY;
         if (MaxAvailable) *MaxAvailable = MaxSize;
         return 0;
     }
@@ -302,10 +387,15 @@ BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable)
     WORD Segment = BlockData - 1, ReturnSize = 0, NextSegment;
     PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment), NextMcb;
 
+    DPRINT("DosResizeMemory: BlockData 0x%04X, NewSize 0x%04X\n",
+           BlockData,
+           NewSize);
+
     /* Make sure this is a valid, allocated block */
     if ((Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') || Mcb->OwnerPsp == 0)
     {
         Success = FALSE;
+        DosLastError = ERROR_INVALID_HANDLE;
         goto Done;
     }
 
@@ -328,6 +418,8 @@ BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable)
         /* Make sure the next segment is free */
         if (NextMcb->OwnerPsp != 0)
         {
+            DPRINT("Cannot expand memory block: next segment is not free!\n");
+            DosLastError = ERROR_NOT_ENOUGH_MEMORY;
             Success = FALSE;
             goto Done;
         }
@@ -348,6 +440,10 @@ BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable)
         /* Check if the block is larger than requested */
         if (Mcb->Size > NewSize)
         {
+            DPRINT("Block too large, reducing size from 0x%04X to 0x%04X\n",
+                   Mcb->Size,
+                   NewSize);
+
             /* It is, split it into two blocks */
             NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1);
     
@@ -363,6 +459,10 @@ BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable)
     }
     else if (NewSize < Mcb->Size)
     {
+        DPRINT("Shrinking block from 0x%04X to 0x%04X\n",
+                Mcb->Size,
+                NewSize);
+
         /* Just split the block */
         NextMcb = SEGMENT_TO_MCB(Segment + NewSize + 1);
         NextMcb->BlockType = Mcb->BlockType;
@@ -378,6 +478,9 @@ Done:
     /* Check if the operation failed */
     if (!Success)
     {
+        DPRINT("DosResizeMemory FAILED. Maximum available: 0x%04X\n",
+               ReturnSize);
+
         /* Return the maximum possible size */
         if (MaxAvailable) *MaxAvailable = ReturnSize;
     }
@@ -389,8 +492,14 @@ BOOLEAN DosFreeMemory(WORD BlockData)
 {
     PDOS_MCB Mcb = SEGMENT_TO_MCB(BlockData - 1);
 
+    DPRINT("DosFreeMemory: BlockData 0x%04X\n", BlockData);
+
     /* Make sure the MCB is valid */
-    if (Mcb->BlockType != 'M' && Mcb->BlockType != 'Z') return FALSE;
+    if (Mcb->BlockType != 'M' && Mcb->BlockType != 'Z')
+    {
+        DPRINT("MCB block type '%c' not valid!\n", Mcb->BlockType);
+        return FALSE;
+    }
 
     /* Mark the block as free */
     Mcb->OwnerPsp = 0;
@@ -398,11 +507,13 @@ BOOLEAN DosFreeMemory(WORD BlockData)
     return TRUE;
 }
 
-BOOLEAN DosLinkUmb()
+BOOLEAN DosLinkUmb(VOID)
 {
     DWORD Segment = FIRST_MCB_SEGMENT;
     PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment);
 
+    DPRINT("Linking UMB\n");
+
     /* Check if UMBs are already linked */
     if (DosUmbLinked) return FALSE;
 
@@ -423,11 +534,13 @@ BOOLEAN DosLinkUmb()
     return TRUE;
 }
 
-BOOLEAN DosUnlinkUmb()
+BOOLEAN DosUnlinkUmb(VOID)
 {
     DWORD Segment = FIRST_MCB_SEGMENT;
     PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment);
 
+    DPRINT("Unlinking UMB\n");
+
     /* Check if UMBs are already unlinked */
     if (!DosUmbLinked) return FALSE;
 
@@ -457,6 +570,10 @@ 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,
@@ -469,7 +586,7 @@ WORD DosCreateFile(LPWORD Handle, LPCSTR FilePath, WORD Attributes)
     if (FileHandle == INVALID_HANDLE_VALUE)
     {
         /* Return the error code */
-        return GetLastError();
+        return (WORD)GetLastError();
     }
 
     /* Open the DOS handle */
@@ -495,6 +612,10 @@ WORD DosOpenFile(LPWORD Handle, LPCSTR FilePath, BYTE AccessMode)
     ACCESS_MASK Access = 0;
     WORD DosHandle;
 
+    DPRINT("DosOpenFile: FilePath \"%s\", AccessMode 0x%04X\n",
+            FilePath,
+            AccessMode);
+
     /* Parse the access mode */
     switch (AccessMode & 3)
     {
@@ -538,7 +659,7 @@ WORD DosOpenFile(LPWORD Handle, LPCSTR FilePath, BYTE AccessMode)
     if (FileHandle == INVALID_HANDLE_VALUE)
     {
         /* Return the error code */
-        return GetLastError();
+        return (WORD)GetLastError();
     }
 
     /* Open the DOS handle */
@@ -563,15 +684,30 @@ WORD DosReadFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesRead)
     WORD Result = ERROR_SUCCESS;
     DWORD BytesRead32 = 0;
     HANDLE Handle = DosGetRealHandle(FileHandle);
+    WORD i;
+
+    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_PARAMETER;
+    if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
 
-    /* Read the file */
-    if (!ReadFile(Handle, Buffer, Count, &BytesRead32, NULL))
+    if (IsConsoleHandle(Handle))
     {
-        /* Store the error code */
-        Result = GetLastError();
+        for (i = 0; i < Count; i++)
+        {
+            /* Call the BIOS function to read the character */
+            ((LPBYTE)Buffer)[i] = LOBYTE(BiosGetCharacter());
+            BytesRead32++;
+        }
+    }
+    else
+    {
+        /* 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 */
@@ -586,15 +722,32 @@ WORD DosWriteFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesWritte
     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_PARAMETER;
+    if (Handle == INVALID_HANDLE_VALUE) return ERROR_INVALID_HANDLE;
 
-    /* Write the file */
-    if (!WriteFile(Handle, Buffer, Count, &BytesWritten32, NULL))
+    if (IsConsoleHandle(Handle))
     {
-        /* Store the error code */
-        Result = GetLastError();
+        for (i = 0; i < Count; i++)
+        {
+            /* Call the BIOS to print the character */
+            BiosPrintCharacter(((LPBYTE)Buffer)[i], DOS_CHAR_ATTRIBUTE, Bda->VideoPage);
+            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 */
@@ -604,12 +757,95 @@ WORD DosWriteFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesWritte
     return Result;
 }
 
+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;
+}
+
+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];
+    DosSftRefCount[SftIndex]++;
+
+    /* Make the new handle point to that SFT entry */
+    HandleTable[NewHandle] = SftIndex;
+
+    /* Return success */
+    return TRUE;
+}
+
 BOOLEAN DosCloseHandle(WORD DosHandle)
 {
     BYTE SftIndex;
     PDOS_PSP PspBlock;
     LPBYTE HandleTable;
 
+    DPRINT("DosCloseHandle: DosHandle 0x%04X\n", DosHandle);
+
     /* The system PSP has no handle table */
     if (CurrentPsp == SYSTEM_PSP) return FALSE;
 
@@ -634,6 +870,90 @@ BOOLEAN DosCloseHandle(WORD DosHandle)
         DosSystemFileTable[SftIndex] = INVALID_HANDLE_VALUE;
     }
 
+    /* Clear the entry in the JFT */
+    HandleTable[DosHandle] = 0xFF;
+
+    return TRUE;
+}
+
+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;
+}
+
+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) strcpy(CurrentDirectories[DriveNumber], Path);
+    else strcpy(CurrentDirectories[DriveNumber], "");
+    
+    /* Return success */
     return TRUE;
 }
 
@@ -678,7 +998,7 @@ VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WOR
     PspBlock->FarCall[2] = 0xCB; // retf
 
     /* Set the command line */
-    PspBlock->CommandLineSize = strlen(CommandLine);
+    PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1);
     RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
     PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
 }
@@ -688,24 +1008,30 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
     BOOLEAN Success = FALSE, AllocatedEnvBlock = FALSE;
     HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
     LPBYTE Address = NULL;
-    LPSTR ProgramFilePath, Parameters[128];
-    CHAR CommandLineCopy[128];
+    LPSTR ProgramFilePath, Parameters[256];
+    CHAR CommandLineCopy[DOS_CMDLINE_LENGTH];
     INT ParamCount = 0;
-    DWORD Segment = 0;
+    WORD Segment = 0;
     WORD MaxAllocSize;
     DWORD i, FileSize, ExeSize;
     PIMAGE_DOS_HEADER Header;
     PDWORD RelocationTable;
     PWORD RelocWord;
 
+    DPRINT("DosCreateProcess: CommandLine \"%s\", EnvBlock 0x%04X\n",
+           CommandLine,
+           EnvBlock);
+
     /* Save a copy of the command line */
     strcpy(CommandLineCopy, CommandLine);
 
+    // FIXME: Improve parsing (especially: "some_path\with spaces\program.exe" options)
+
     /* Get the file name of the executable */
     ProgramFilePath = strtok(CommandLineCopy, " \t");
 
     /* Load the parameters in the local array */
-    while ((ParamCount < 256)
+    while ((ParamCount < sizeof(Parameters)/sizeof(Parameters[0]))
            && ((Parameters[ParamCount] = strtok(NULL, " \t")) != NULL))
     {
         ParamCount++;
@@ -746,7 +1072,8 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
         /* No, copy the one from the parent */
         EnvBlock = DosCopyEnvironmentBlock((CurrentPsp != SYSTEM_PSP)
                                            ? SEGMENT_TO_PSP(CurrentPsp)->EnvBlock
-                                           : SYSTEM_ENV_BLOCK);
+                                           : SYSTEM_ENV_BLOCK,
+                                           ProgramFilePath);
     }
 
     /* Check if this is an EXE file or a COM file */
@@ -773,7 +1100,7 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
         for (i = Header->e_maxalloc; i >= Header->e_minalloc; i--, ExeSize--)
         {
             /* Try to allocate that much memory */
-            Segment = DosAllocateMemory(ExeSize, NULL);
+            Segment = DosAllocateMemory((WORD)ExeSize, NULL);
             if (Segment != 0) break;
         }
 
@@ -783,7 +1110,7 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
         /* Initialize the PSP */
         DosInitializePsp(Segment,
                          CommandLine,
-                         ExeSize,
+                         (WORD)ExeSize,
                          EnvBlock);
 
         /* The process owns its own memory */
@@ -836,7 +1163,7 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
         DosAllocateMemory(0xFFFF, &MaxAllocSize);
 
         /* Make sure it's enough for the whole program and the PSP */
-        if ((MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP))) goto Cleanup;
+        if (((DWORD)MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP))) goto Cleanup;
 
         /* Allocate all of it */
         Segment = DosAllocateMemory(MaxAllocSize, NULL);
@@ -855,7 +1182,7 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
         /* Initialize the PSP */
         DosInitializePsp(Segment,
                          CommandLine,
-                         (FileSize + sizeof(DOS_PSP)) >> 4,
+                         (WORD)((FileSize + sizeof(DOS_PSP)) >> 4),
                          EnvBlock);
 
         /* Set the initial segment registers */
@@ -895,15 +1222,24 @@ Cleanup:
 
 VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
 {
+    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\n",
+           Psp,
+           ReturnCode);
+
     /* Check if this PSP is it's own parent */
     if (PspBlock->ParentPsp == Psp) goto Done;
 
-    // TODO: Close all handles opened by the process
+    for (i = 0; i < PspBlock->HandleTableSize; i++)
+    {
+        /* Close the handle */
+        DosCloseHandle(i);
+    }
 
     /* Free the memory used by the process */
     while (TRUE)
@@ -915,7 +1251,7 @@ VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode)
         if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType !='Z') break;
 
         /* If this block was allocated by the process, free it */
-        if (CurrentMcb->OwnerPsp == Psp) DosFreeMemory(McbSegment);
+        if (CurrentMcb->OwnerPsp == Psp) DosFreeMemory(McbSegment + 1);
 
         /* If this was the last block, quit */
         if (CurrentMcb->BlockType == 'Z') break;
@@ -942,7 +1278,7 @@ Done:
                     LOWORD(PspBlock->TerminateAddress));
 }
 
-CHAR DosReadCharacter()
+CHAR DosReadCharacter(VOID)
 {
     CHAR Character = '\0';
     WORD BytesRead;
@@ -961,15 +1297,15 @@ VOID DosPrintCharacter(CHAR Character)
     DosWriteFile(DOS_OUTPUT_HANDLE, &Character, sizeof(CHAR), &BytesWritten);
 }
 
-VOID DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
+BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
 {
     HANDLE Handle = DosGetRealHandle(FileHandle);
 
     if (Handle == INVALID_HANDLE_VALUE)
     {
         /* Doesn't exist */
-        EmulatorSetFlag(EMULATOR_FLAG_CF);
-        EmulatorSetRegister(EMULATOR_REG_AX, ERROR_FILE_NOT_FOUND);
+        DosLastError = ERROR_FILE_NOT_FOUND;
+        return FALSE;
     }
 
     switch (ControlCode)
@@ -994,10 +1330,9 @@ VOID DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
             InfoWord |= 1 << 7;
 
             /* Return the device information word */
-            EmulatorClearFlag(EMULATOR_FLAG_CF);
             EmulatorSetRegister(EMULATOR_REG_DX, InfoWord);
 
-            break;
+            return TRUE;
         }
 
         /* Unsupported control code */
@@ -1005,19 +1340,19 @@ VOID DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
         {
             DPRINT1("Unsupported IOCTL: 0x%02X\n", ControlCode);
 
-            EmulatorSetFlag(EMULATOR_FLAG_CF);
-            EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_PARAMETER);
+            DosLastError = ERROR_INVALID_PARAMETER;
+            return FALSE;
         }
     }
 }
 
-VOID DosInt20h(WORD CodeSegment)
+VOID DosInt20h(LPWORD Stack)
 {
     /* This is the exit interrupt */
-    DosTerminateProcess(CodeSegment, 0);
+    DosTerminateProcess(Stack[STACK_CS], 0);
 }
 
-VOID DosInt21h(WORD CodeSegment)
+VOID DosInt21h(LPWORD Stack)
 {
     INT i;
     CHAR Character;
@@ -1028,8 +1363,8 @@ VOID DosInt21h(WORD CodeSegment)
     DWORD Ecx = EmulatorGetRegister(EMULATOR_REG_CX);
     DWORD Edx = EmulatorGetRegister(EMULATOR_REG_DX);
     DWORD Ebx = EmulatorGetRegister(EMULATOR_REG_BX);
-    WORD DataSegment = EmulatorGetRegister(EMULATOR_REG_DS);
-    WORD ExtSegment = EmulatorGetRegister(EMULATOR_REG_ES);
+    WORD DataSegment = (WORD)EmulatorGetRegister(EMULATOR_REG_DS);
+    WORD ExtSegment = (WORD)EmulatorGetRegister(EMULATOR_REG_ES);
 
     /* Check the value in the AH register */
     switch (HIBYTE(Eax))
@@ -1037,7 +1372,7 @@ VOID DosInt21h(WORD CodeSegment)
         /* Terminate Program */
         case 0x00:
         {
-            DosTerminateProcess(CodeSegment, 0);
+            DosTerminateProcess(Stack[STACK_CS], 0);
             break;
         }
 
@@ -1101,6 +1436,25 @@ VOID DosInt21h(WORD CodeSegment)
             break;
         }
 
+        /* Set Default Drive  */
+        case 0x0E:
+        {
+            DosChangeDrive(LOBYTE(Edx));
+            EmulatorSetRegister(EMULATOR_REG_AX,
+                                (Eax & 0xFFFFFF00) | (LastDrive - 'A' + 1));
+
+            break;
+        }
+
+        /* Get Default Drive */
+        case 0x19:
+        {
+            EmulatorSetRegister(EMULATOR_REG_AX,
+                                (Eax & 0xFFFFFF00) | CurrentDrive);
+
+            break;
+        }
+
         /* Set Disk Transfer Area */
         case 0x1A:
         {
@@ -1232,11 +1586,11 @@ VOID DosInt21h(WORD CodeSegment)
 
             if (CreateDirectoryA(String, NULL))
             {
-                EmulatorClearFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
             else
             {
-                EmulatorSetFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                 EmulatorSetRegister(EMULATOR_REG_AX,
                                     (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
             }
@@ -1252,11 +1606,11 @@ VOID DosInt21h(WORD CodeSegment)
 
             if (RemoveDirectoryA(String))
             {
-                EmulatorClearFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
             else
             {
-                EmulatorSetFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                 EmulatorSetRegister(EMULATOR_REG_AX,
                                     (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
             }
@@ -1271,15 +1625,15 @@ VOID DosInt21h(WORD CodeSegment)
             String = (PCHAR)((ULONG_PTR)BaseAddress
                      + TO_LINEAR(DataSegment, LOWORD(Edx)));
 
-            if (SetCurrentDirectoryA(String))
+            if (DosChangeDirectory(String))
             {
-                EmulatorClearFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
             else
             {
-                EmulatorSetFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                 EmulatorSetRegister(EMULATOR_REG_AX,
-                                    (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
+                                    (Eax & 0xFFFF0000) | DosLastError);
             }
 
             break;
@@ -1297,7 +1651,7 @@ VOID DosInt21h(WORD CodeSegment)
             if (ErrorCode == 0)
             {
                 /* Clear CF */
-                EmulatorClearFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
 
                 /* Return the handle in AX */
                 EmulatorSetRegister(EMULATOR_REG_AX,
@@ -1306,7 +1660,7 @@ VOID DosInt21h(WORD CodeSegment)
             else
             {
                 /* Set CF */
-                EmulatorSetFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
 
                 /* Return the error code in AX */
                 EmulatorSetRegister(EMULATOR_REG_AX,
@@ -1320,15 +1674,15 @@ VOID DosInt21h(WORD CodeSegment)
         case 0x3D:
         {
             WORD FileHandle;
-            WORD ErrorCode = DosCreateFile(&FileHandle,
-                                           (LPCSTR)(ULONG_PTR)BaseAddress
-                                           + TO_LINEAR(DataSegment, LOWORD(Edx)),
-                                           LOBYTE(Eax));
+            WORD ErrorCode = DosOpenFile(&FileHandle,
+                                         (LPCSTR)(ULONG_PTR)BaseAddress
+                                         + TO_LINEAR(DataSegment, LOWORD(Edx)),
+                                         LOBYTE(Eax));
 
             if (ErrorCode == 0)
             {
                 /* Clear CF */
-                EmulatorClearFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
 
                 /* Return the handle in AX */
                 EmulatorSetRegister(EMULATOR_REG_AX,
@@ -1337,7 +1691,7 @@ VOID DosInt21h(WORD CodeSegment)
             else
             {
                 /* Set CF */
-                EmulatorSetFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
 
                 /* Return the error code in AX */
                 EmulatorSetRegister(EMULATOR_REG_AX,
@@ -1353,16 +1707,16 @@ VOID DosInt21h(WORD CodeSegment)
             if (DosCloseHandle(LOWORD(Ebx)))
             {
                 /* Clear CF */
-                EmulatorClearFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
             else
             {
                 /* Set CF */
-                EmulatorSetFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
 
                 /* Return the error code in AX */
                 EmulatorSetRegister(EMULATOR_REG_AX,
-                                    (Eax & 0xFFFF0000) | ERROR_INVALID_PARAMETER);
+                                    (Eax & 0xFFFF0000) | ERROR_INVALID_HANDLE);
             }
 
             break;
@@ -1381,7 +1735,7 @@ VOID DosInt21h(WORD CodeSegment)
             if (ErrorCode == 0)
             {
                 /* Clear CF */
-                EmulatorClearFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
 
                 /* Return the number of bytes read in AX */
                 EmulatorSetRegister(EMULATOR_REG_AX,
@@ -1390,7 +1744,7 @@ VOID DosInt21h(WORD CodeSegment)
             else
             {
                 /* Set CF */
-                EmulatorSetFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
 
                 /* Return the error code in AX */
                 EmulatorSetRegister(EMULATOR_REG_AX,
@@ -1412,7 +1766,7 @@ VOID DosInt21h(WORD CodeSegment)
             if (ErrorCode == 0)
             {
                 /* Clear CF */
-                EmulatorClearFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
 
                 /* Return the number of bytes written in AX */
                 EmulatorSetRegister(EMULATOR_REG_AX,
@@ -1421,7 +1775,56 @@ VOID DosInt21h(WORD CodeSegment)
             else
             {
                 /* Set CF */
-                EmulatorSetFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+
+                /* Return the error code in AX */
+                EmulatorSetRegister(EMULATOR_REG_AX,
+                                    (Eax & 0xFFFF0000) | ErrorCode);
+            }
+
+            break;
+        }
+
+        /* Delete File */
+        case 0x41:
+        {
+            LPSTR FileName = (LPSTR)((ULONG_PTR)BaseAddress + TO_LINEAR(DataSegment, Edx));
+
+            /* Call the API function */
+            if (DeleteFileA(FileName)) Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            else
+            {
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                EmulatorSetRegister(EMULATOR_REG_AX, GetLastError());
+            }
+
+            break;
+        }
+
+        /* Seek File */
+        case 0x42:
+        {
+            DWORD NewLocation;
+            WORD ErrorCode = DosSeekFile(LOWORD(Ebx),
+                                         MAKELONG(LOWORD(Edx), LOWORD(Ecx)),
+                                         LOBYTE(Eax),
+                                         &NewLocation);
+
+            if (ErrorCode == 0)
+            {
+                /* Clear CF */
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+
+                /* Return the new offset in DX:AX */
+                EmulatorSetRegister(EMULATOR_REG_DX,
+                                    (Edx & 0xFFFF0000) | HIWORD(NewLocation));
+                EmulatorSetRegister(EMULATOR_REG_AX,
+                                    (Eax & 0xFFFF0000) | LOWORD(NewLocation));
+            }
+            else
+            {
+                /* Set CF */
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
 
                 /* Return the error code in AX */
                 EmulatorSetRegister(EMULATOR_REG_AX,
@@ -1431,10 +1834,115 @@ VOID DosInt21h(WORD CodeSegment)
             break;
         }
 
+        /* Get/Set File Attributes */
+        case 0x43:
+        {
+            DWORD Attributes;
+            LPSTR FileName = (LPSTR)((ULONG_PTR)BaseAddress + TO_LINEAR(DataSegment, Edx));
+
+            if (LOBYTE(Eax) == 0x00)
+            {
+                /* Get the attributes */
+                Attributes = GetFileAttributesA(FileName);
+
+                /* Check if it failed */
+                if (Attributes == INVALID_FILE_ATTRIBUTES)
+                {
+                    Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                    EmulatorSetRegister(EMULATOR_REG_AX, GetLastError());
+
+                    break;
+                }
+
+                /* Return the attributes that DOS can understand */
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                EmulatorSetRegister(EMULATOR_REG_CX,
+                                    (Ecx & 0xFFFFFF00) | LOBYTE(Attributes));
+            }
+            else if (LOBYTE(Eax) == 0x01)
+            {
+                /* Try to set the attributes */
+                if (SetFileAttributesA(FileName, LOBYTE(Ecx)))
+                {
+                    Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                }
+                else
+                {
+                    Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                    EmulatorSetRegister(EMULATOR_REG_AX, GetLastError());
+                }
+            }
+            else
+            {
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_FUNCTION);
+            }
+
+            break;
+        }
+
         /* IOCTL */
         case 0x44:
         {
-            DosHandleIoctl(LOBYTE(Eax), LOWORD(Ebx));
+            if (DosHandleIoctl(LOBYTE(Eax), LOWORD(Ebx)))
+            {
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            }
+            else
+            {
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                EmulatorSetRegister(EMULATOR_REG_AX, DosLastError);
+            }
+
+            break;
+        }
+
+        /* Duplicate Handle */
+        case 0x45:
+        {
+            WORD NewHandle;
+            HANDLE Handle = DosGetRealHandle(LOWORD(Ebx));
+
+            if (Handle != INVALID_HANDLE_VALUE)
+            {
+                /* The handle is invalid */
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_HANDLE);
+
+                break;
+            }
+
+            /* Open a new handle to the same entry */
+            NewHandle = DosOpenHandle(Handle);
+
+            if (NewHandle == INVALID_DOS_HANDLE)
+            {
+                /* Too many files open */
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                EmulatorSetRegister(EMULATOR_REG_AX, ERROR_TOO_MANY_OPEN_FILES);
+
+                break;
+            }
+
+            /* Return the result */
+            Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            EmulatorSetRegister(EMULATOR_REG_AX, NewHandle);
+
+            break;
+        }
+
+        /* Force Duplicate Handle */
+        case 0x46:
+        {
+            if (DosDuplicateHandle(LOWORD(Ebx), LOWORD(Ecx)))
+            {
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            }
+            else
+            {
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_HANDLE);
+            }
 
             break;
         }
@@ -1448,13 +1956,13 @@ VOID DosInt21h(WORD CodeSegment)
             if (Segment != 0)
             {
                 EmulatorSetRegister(EMULATOR_REG_AX, Segment);
-                EmulatorClearFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
             else
             {
-                EmulatorSetRegister(EMULATOR_REG_AX, ERROR_NOT_ENOUGH_MEMORY);
+                EmulatorSetRegister(EMULATOR_REG_AX, DosLastError);
                 EmulatorSetRegister(EMULATOR_REG_BX, MaxAvailable);
-                EmulatorSetFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
             }
 
             break;
@@ -1465,12 +1973,12 @@ VOID DosInt21h(WORD CodeSegment)
         {
             if (DosFreeMemory(ExtSegment))
             {
-                EmulatorClearFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
             else
             {
                 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_ARENA_TRASHED);
-                EmulatorSetFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
             }
 
             break;
@@ -1483,11 +1991,12 @@ VOID DosInt21h(WORD CodeSegment)
 
             if (DosResizeMemory(ExtSegment, LOWORD(Ebx), &Size))
             {
-                EmulatorClearFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
             else
             {
-                EmulatorSetFlag(EMULATOR_FLAG_CF);
+                EmulatorSetRegister(EMULATOR_REG_AX, DosLastError);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                 EmulatorSetRegister(EMULATOR_REG_BX, Size);
             }
 
@@ -1501,16 +2010,56 @@ VOID DosInt21h(WORD CodeSegment)
             break;
         }
 
+        /* Get Current Process */
+        case 0x51:
+        {
+            EmulatorSetRegister(EMULATOR_REG_BX, CurrentPsp);
+
+            break;
+        }
+
         /* Get/Set Memory Management Options */
         case 0x58:
         {
-            if (LOBYTE(Eax) == 0x02)
+            if (LOBYTE(Eax) == 0x00)
+            {
+                /* Get allocation strategy */
+
+                EmulatorSetRegister(EMULATOR_REG_AX, DosAllocStrategy);
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            }
+            else if (LOBYTE(Eax) == 0x01)
+            {
+                /* Set allocation strategy */
+
+                if ((LOBYTE(Ebx) & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
+                    == (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
+                {
+                    /* Can't set both */
+                    EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_PARAMETER);
+                    Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                    break;
+                }
+
+                if ((LOBYTE(Ebx) & 0x3F) > DOS_ALLOC_LAST_FIT)
+                {
+                    /* Invalid allocation strategy */
+                    EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_PARAMETER);
+                    Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                    break;
+                }
+
+                DosAllocStrategy = LOBYTE(Ebx);
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            }
+            else if (LOBYTE(Eax) == 0x02)
             {
                 /* Get UMB link state */
 
                 Eax &= 0xFFFFFF00;
                 if (DosUmbLinked) Eax |= 1;
                 EmulatorSetRegister(EMULATOR_REG_AX, Eax);
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
             else if (LOBYTE(Eax) == 0x03)
             {
@@ -1518,12 +2067,13 @@ VOID DosInt21h(WORD CodeSegment)
 
                 if (Ebx) DosLinkUmb();
                 else DosUnlinkUmb();
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
             else
             {
                 /* Invalid or unsupported function */
 
-                EmulatorSetFlag(EMULATOR_FLAG_CF);
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
                 EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_FUNCTION);
             }
 
@@ -1534,17 +2084,19 @@ VOID DosInt21h(WORD CodeSegment)
         default:
         {
             DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", HIBYTE(Eax));
-            EmulatorSetFlag(EMULATOR_FLAG_CF);
+            Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
         }
     }
 }
 
-VOID DosBreakInterrupt()
+VOID DosBreakInterrupt(LPWORD Stack)
 {
+    UNREFERENCED_PARAMETER(Stack);
+
     VdmRunning = FALSE;
 }
 
-BOOLEAN DosInitialize()
+BOOLEAN DosInitialize(VOID)
 {
     BYTE i;
     PDOS_MCB Mcb = SEGMENT_TO_MCB(FIRST_MCB_SEGMENT);
@@ -1554,6 +2106,9 @@ BOOLEAN DosInitialize()
     LPSTR AsciiString;
     LPSTR DestPtr = (LPSTR)((ULONG_PTR)BaseAddress + TO_LINEAR(SYSTEM_ENV_BLOCK, 0));
     DWORD AsciiSize;
+    CHAR CurrentDirectory[MAX_PATH];
+    CHAR DosDirectory[DOS_DIR_LENGTH];
+    LPSTR Path;
 
     /* Initialize the MCB */
     Mcb->BlockType = 'Z';
@@ -1610,19 +2165,50 @@ BOOLEAN DosInitialize()
         /* Copy the string into DOS memory */
         strcpy(DestPtr, AsciiString);
 
-        /* Free the memory */
-        HeapFree(GetProcessHeap(), 0, AsciiString);
-
         /* Move to the next string */
         SourcePtr += wcslen(SourcePtr) + 1;
         DestPtr += strlen(AsciiString);
         *(DestPtr++) = 0;
+
+        /* Free the memory */
+        HeapFree(GetProcessHeap(), 0, AsciiString);
     }
     *DestPtr = 0;
 
     /* Free the memory allocated for environment strings */
     FreeEnvironmentStringsW(Environment);
 
+    /* Clear the current directory buffer */
+    ZeroMemory(CurrentDirectories, sizeof(CurrentDirectories));
+
+    /* Get the current directory */
+    if (!GetCurrentDirectoryA(MAX_PATH, CurrentDirectory))
+    {
+        // TODO: Use some kind of default path?
+        return FALSE;
+    }
+
+    /* Convert that to a DOS path */
+    if (!GetShortPathNameA(CurrentDirectory, DosDirectory, DOS_DIR_LENGTH))
+    {
+        // TODO: Use some kind of default path?
+        return FALSE;
+    }
+
+    /* Set the drive */
+    CurrentDrive = DosDirectory[0] - 'A';
+
+    /* Get the path */
+    Path = strchr(DosDirectory, '\\');
+    if (Path != NULL)
+    {
+        /* Skip the backslash */
+        Path++;
+    }
+
+    /* Set the directory */
+    if (Path != NULL) strcpy(CurrentDirectories[CurrentDrive], Path);
+
     /* Read CONFIG.SYS */
     Stream = _wfopen(DOS_CONFIG_PATH, L"r");
     if (Stream != NULL)