[NTVDM]
[reactos.git] / subsystems / ntvdm / dos.c
index df81cd7..0ada941 100644 (file)
 #include "bios.h"
 #include "emulator.h"
 
+#include "registers.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;
+static BYTE DosErrorLevel = 0;
 
 /* 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;
@@ -60,7 +90,7 @@ static WORD DosCopyEnvironmentBlock(WORD SourceSegment, LPCSTR ProgramName)
     ULONG TotalSize = 0;
     WORD DestSegment;
 
-    Ptr = SourceBuffer = (PCHAR)((ULONG_PTR)BaseAddress + TO_LINEAR(SourceSegment, 0));
+    Ptr = SourceBuffer = (PCHAR)SEG_OFF_TO_PTR(SourceSegment, 0);
 
     /* Calculate the size of the environment block */
     while (*Ptr)
@@ -79,7 +109,7 @@ static WORD DosCopyEnvironmentBlock(WORD SourceSegment, LPCSTR ProgramName)
 
     Ptr = SourceBuffer;
 
-    DestBuffer = (PCHAR)((ULONG_PTR)BaseAddress + TO_LINEAR(DestSegment, 0));
+    DestBuffer = (PCHAR)SEG_OFF_TO_PTR(DestSegment, 0);
     while (*Ptr)
     {
         /* Copy the string */
@@ -205,7 +235,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]++;
@@ -682,6 +712,7 @@ 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,
@@ -690,11 +721,23 @@ WORD DosWriteFile(WORD FileHandle, LPVOID Buffer, WORD Count, LPWORD BytesWritte
     /* Make sure the handle is valid */
     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 = (WORD)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 */
@@ -823,6 +866,87 @@ BOOLEAN DosCloseHandle(WORD DosHandle)
     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;
+}
+
 VOID DosInitializePsp(WORD PspSegment, LPCSTR CommandLine, WORD ProgramSize, WORD Environment)
 {
     PDOS_PSP PspBlock = SEGMENT_TO_PSP(PspSegment);
@@ -864,7 +988,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';
 }
@@ -874,8 +998,8 @@ 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;
     WORD Segment = 0;
     WORD MaxAllocSize;
@@ -891,11 +1015,13 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD 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++;
@@ -964,7 +1090,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;
         }
 
@@ -974,7 +1100,7 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
         /* Initialize the PSP */
         DosInitializePsp(Segment,
                          CommandLine,
-                         ExeSize,
+                         (WORD)ExeSize,
                          EnvBlock);
 
         /* The process owns its own memory */
@@ -982,8 +1108,7 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
         DosChangeMemoryOwner(EnvBlock, Segment);
 
         /* Copy the program to Segment:0100 */
-        RtlCopyMemory((PVOID)((ULONG_PTR)BaseAddress
-                      + TO_LINEAR(Segment, 0x100)),
+        RtlCopyMemory(SEG_OFF_TO_PTR(Segment, 0x100),
                       Address + (Header->e_cparhdr << 4),
                       min(FileSize - (Header->e_cparhdr << 4),
                           (ExeSize << 4) - sizeof(DOS_PSP)));
@@ -995,17 +1120,16 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
         for (i = 0; i < Header->e_crlc; i++)
         {
             /* Get a pointer to the word that needs to be patched */
-            RelocWord = (PWORD)((ULONG_PTR)BaseAddress
-                                + TO_LINEAR(Segment + HIWORD(RelocationTable[i]),
-                                            0x100 + LOWORD(RelocationTable[i])));
+            RelocWord = (PWORD)SEG_OFF_TO_PTR(Segment + HIWORD(RelocationTable[i]),
+                                                0x100 + LOWORD(RelocationTable[i]));
 
             /* Add the number of the EXE segment to it */
             *RelocWord += Segment + (sizeof(DOS_PSP) >> 4);
         }
 
         /* Set the initial segment registers */
-        EmulatorSetRegister(EMULATOR_REG_DS, Segment);
-        EmulatorSetRegister(EMULATOR_REG_ES, Segment);
+        setDS(Segment);
+        setES(Segment);
 
         /* Set the stack to the location from the header */
         EmulatorSetStack(Segment + (sizeof(DOS_PSP) >> 4) + Header->e_ss,
@@ -1038,20 +1162,19 @@ BOOLEAN DosCreateProcess(LPCSTR CommandLine, WORD EnvBlock)
         DosChangeMemoryOwner(EnvBlock, Segment);
 
         /* Copy the program to Segment:0100 */
-        RtlCopyMemory((PVOID)((ULONG_PTR)BaseAddress
-                      + TO_LINEAR(Segment, 0x100)),
+        RtlCopyMemory(SEG_OFF_TO_PTR(Segment, 0x100),
                       Address,
                       FileSize);
 
         /* Initialize the PSP */
         DosInitializePsp(Segment,
                          CommandLine,
-                         (FileSize + sizeof(DOS_PSP)) >> 4,
+                         (WORD)((FileSize + sizeof(DOS_PSP)) >> 4),
                          EnvBlock);
 
         /* Set the initial segment registers */
-        EmulatorSetRegister(EMULATOR_REG_DS, Segment);
-        EmulatorSetRegister(EMULATOR_REG_ES, Segment);
+        setDS(Segment);
+        setES(Segment);
 
         /* Set the stack to the last word of the segment */
         EmulatorSetStack(Segment, 0xFFFE);
@@ -1115,7 +1238,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;
@@ -1137,6 +1260,9 @@ Done:
         if (CurrentPsp == SYSTEM_PSP) VdmRunning = FALSE;
     }
 
+    /* Save the return code */
+    DosErrorLevel = ReturnCode;
+
     /* Return control to the parent process */
     EmulatorExecute(HIWORD(PspBlock->TerminateAddress),
                     LOWORD(PspBlock->TerminateAddress));
@@ -1147,12 +1273,40 @@ CHAR DosReadCharacter(VOID)
     CHAR Character = '\0';
     WORD BytesRead;
 
-    /* Use the file reading function */
-    DosReadFile(DOS_INPUT_HANDLE, &Character, sizeof(CHAR), &BytesRead);
+    if (IsConsoleHandle(DosGetRealHandle(DOS_INPUT_HANDLE)))
+    {
+        /* Call the BIOS */
+        Character = LOBYTE(BiosGetCharacter());
+    }
+    else
+    {
+        /* Use the file reading function */
+        DosReadFile(DOS_INPUT_HANDLE, &Character, sizeof(CHAR), &BytesRead);
+    }
 
     return Character;
 }
 
+BOOLEAN DosCheckInput(VOID)
+{
+    HANDLE Handle = DosGetRealHandle(DOS_INPUT_HANDLE);
+
+    if (IsConsoleHandle(Handle))
+    {
+        /* Call the BIOS */
+        return (BiosPeekCharacter() != 0xFFFF);
+    }
+    else
+    {
+        DWORD FileSizeHigh;
+        DWORD FileSize = GetFileSize(Handle, &FileSizeHigh);
+        LONG LocationHigh = 0;
+        DWORD Location = SetFilePointer(Handle, 0, &LocationHigh, FILE_CURRENT);
+
+        return ((Location != FileSize) || (LocationHigh != FileSizeHigh));
+    }
+}
+
 VOID DosPrintCharacter(CHAR Character)
 {
     WORD BytesWritten;
@@ -1194,8 +1348,7 @@ BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
             InfoWord |= 1 << 7;
 
             /* Return the device information word */
-            EmulatorSetRegister(EMULATOR_REG_DX, InfoWord);
-
+            setDX(InfoWord);
             return TRUE;
         }
 
@@ -1218,20 +1371,13 @@ VOID DosInt20h(LPWORD Stack)
 
 VOID DosInt21h(LPWORD Stack)
 {
-    INT i;
     CHAR Character;
     SYSTEMTIME SystemTime;
     PCHAR String;
     PDOS_INPUT_BUFFER InputBuffer;
-    DWORD Eax = EmulatorGetRegister(EMULATOR_REG_AX);
-    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);
 
     /* Check the value in the AH register */
-    switch (HIBYTE(Eax))
+    switch (getAH())
     {
         /* Terminate Program */
         case 0x00:
@@ -1245,14 +1391,62 @@ VOID DosInt21h(LPWORD Stack)
         {
             Character = DosReadCharacter();
             DosPrintCharacter(Character);
-            EmulatorSetRegister(EMULATOR_REG_AX, (Eax & 0xFFFFFF00) | Character);
+
+            /* Let the BOP repeat if needed */
+            if (EmulatorGetFlag(EMULATOR_FLAG_CF)) break;
+
+            setAL(Character);
             break;
         }
 
         /* Print Character */
         case 0x02:
         {
-            DosPrintCharacter(LOBYTE(Edx));
+            BYTE Character = getDL();
+            DosPrintCharacter(Character);
+
+            /*
+             * We return the output character (DOS 2.1+), see:
+             * http://www.delorie.com/djgpp/doc/rbinter/id/65/25.html
+             * for more information.
+             */
+            setAL(Character);
+            break;
+        }
+
+        /* Direct Console I/O */
+        case 0x06:
+        {
+            BYTE Character = getDL();
+
+            if (Character != 0xFF)
+            {
+                /* Output */
+                DosPrintCharacter(Character);
+
+                /*
+                 * We return the output character (DOS 2.1+), see:
+                 * http://www.delorie.com/djgpp/doc/rbinter/id/69/25.html
+                 * for more information.
+                 */
+                setAL(Character);
+            }
+            else
+            {
+                /* Input */
+                if (DosCheckInput())
+                {
+                    Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_ZF;
+                    setAL(DosReadCharacter());
+                }
+                else
+                {
+                    /* No character available */
+                    Stack[STACK_FLAGS] |= EMULATOR_FLAG_ZF;
+                    setAL(0);
+                }
+            }
+
             break;
         }
 
@@ -1260,60 +1454,97 @@ VOID DosInt21h(LPWORD Stack)
         case 0x07:
         case 0x08:
         {
-            EmulatorSetRegister(EMULATOR_REG_AX,
-                               (Eax & 0xFFFFFF00) | DosReadCharacter());
+            Character = DosReadCharacter();
+
+            /* Let the BOP repeat if needed */
+            if (EmulatorGetFlag(EMULATOR_FLAG_CF)) break;
+
+            setAL(Character);
             break;
         }
 
         /* Print String */
         case 0x09:
         {
-            String = (PCHAR)((ULONG_PTR)BaseAddress
-                     + TO_LINEAR(DataSegment, LOWORD(Edx)));
+            String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
 
-            while ((*String) != '$')
+            while (*String != '$')
             {
                 DosPrintCharacter(*String);
                 String++;
             }
 
+            /*
+             * We return the output character (DOS 2.1+), see:
+             * http://www.delorie.com/djgpp/doc/rbinter/id/73/25.html
+             * for more information.
+             */
+            setAL('$');
             break;
         }
 
         /* Read Buffered Input */
         case 0x0A:
         {
-            InputBuffer = (PDOS_INPUT_BUFFER)((ULONG_PTR)BaseAddress
-                                              + TO_LINEAR(DataSegment,
-                                                          LOWORD(Edx)));
+            InputBuffer = (PDOS_INPUT_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX());
 
-            InputBuffer->Length = 0;
-            for (i = 0; i < InputBuffer->MaxLength; i ++)
+            while (Stack[STACK_COUNTER] < InputBuffer->MaxLength)
             {
+                /* Try to read a character */
                 Character = DosReadCharacter();
+
+                /* If it's not ready yet, let the BOP repeat */
+                if (EmulatorGetFlag(EMULATOR_FLAG_CF)) break;
+
+                /* Echo the character and append it to the buffer */
                 DosPrintCharacter(Character);
-                InputBuffer->Buffer[InputBuffer->Length] = Character;
+                InputBuffer->Buffer[Stack[STACK_COUNTER]] = Character;
+
                 if (Character == '\r') break;
-                InputBuffer->Length++;
+                Stack[STACK_COUNTER]++;
             }
 
+            /* Update the length */
+            InputBuffer->Length = Stack[STACK_COUNTER];
+            break;
+        }
+
+        /* Get STDIN Status */
+        case 0x0B:
+        {
+            setAL(DosCheckInput() ? 0xFF : 0x00);
+            break;
+        }
+
+        /* Set Default Drive  */
+        case 0x0E:
+        {
+            DosChangeDrive(getDL());
+            setAL(LastDrive - 'A' + 1);
+            break;
+        }
+
+        /* Get Default Drive */
+        case 0x19:
+        {
+            setAL(CurrentDrive);
             break;
         }
 
         /* Set Disk Transfer Area */
         case 0x1A:
         {
-            DiskTransferArea = MAKELONG(LOWORD(Edx), DataSegment);
+            DiskTransferArea = MAKELONG(getDX(), getDS());
             break;
         }
 
         /* Set Interrupt Vector */
         case 0x25:
         {
-            DWORD FarPointer = MAKELONG(LOWORD(Edx), DataSegment);
+            DWORD FarPointer = MAKELONG(getDX(), getDS());
 
             /* Write the new far pointer to the IDT */
-            ((PDWORD)BaseAddress)[LOBYTE(Eax)] = FarPointer;
+            ((PDWORD)BaseAddress)[getAL()] = FarPointer;
 
             break;
         }
@@ -1322,14 +1553,9 @@ VOID DosInt21h(LPWORD Stack)
         case 0x2A:
         {
             GetLocalTime(&SystemTime);
-            EmulatorSetRegister(EMULATOR_REG_CX,
-                                (Ecx & 0xFFFF0000) | SystemTime.wYear);
-            EmulatorSetRegister(EMULATOR_REG_DX,
-                                (Edx & 0xFFFF0000)
-                                | (SystemTime.wMonth << 8)
-                                | SystemTime.wDay);
-            EmulatorSetRegister(EMULATOR_REG_AX,
-                                (Eax & 0xFFFFFF00) | SystemTime.wDayOfWeek);
+            setCX(SystemTime.wYear);
+            setDX(MAKEWORD(SystemTime.wDay, SystemTime.wMonth));
+            setAL(SystemTime.wDayOfWeek);
             break;
         }
 
@@ -1337,21 +1563,12 @@ VOID DosInt21h(LPWORD Stack)
         case 0x2B:
         {
             GetLocalTime(&SystemTime);
-            SystemTime.wYear = LOWORD(Ecx);
-            SystemTime.wMonth = HIBYTE(Edx);
-            SystemTime.wDay = LOBYTE(Edx);
-
-            if (SetLocalTime(&SystemTime))
-            {
-                /* Return success */
-                EmulatorSetRegister(EMULATOR_REG_AX, Eax & 0xFFFFFF00);
-            }
-            else
-            {
-                /* Return failure */
-                EmulatorSetRegister(EMULATOR_REG_AX, Eax | 0xFF);
-            }
+            SystemTime.wYear  = getCX();
+            SystemTime.wMonth = getDH();
+            SystemTime.wDay   = getDL();
 
+            /* Return success or failure */
+            setAL(SetLocalTime(&SystemTime) ? 0x00 : 0xFF);
             break;
         }
 
@@ -1359,14 +1576,8 @@ VOID DosInt21h(LPWORD Stack)
         case 0x2C:
         {
             GetLocalTime(&SystemTime);
-            EmulatorSetRegister(EMULATOR_REG_CX,
-                                (Ecx & 0xFFFF0000)
-                                | (SystemTime.wHour << 8)
-                                | SystemTime.wMinute);
-            EmulatorSetRegister(EMULATOR_REG_DX,
-                                (Edx & 0xFFFF0000)
-                                | (SystemTime.wSecond << 8)
-                                | (SystemTime.wMilliseconds / 10));
+            setCX(MAKEWORD(SystemTime.wMinute, SystemTime.wHour));
+            setDX(MAKEWORD(SystemTime.wMilliseconds / 10, SystemTime.wSecond));
             break;
         }
 
@@ -1374,31 +1585,21 @@ VOID DosInt21h(LPWORD Stack)
         case 0x2D:
         {
             GetLocalTime(&SystemTime);
-            SystemTime.wHour = HIBYTE(Ecx);
-            SystemTime.wMinute = LOBYTE(Ecx);
-            SystemTime.wSecond = HIBYTE(Edx);
-            SystemTime.wMilliseconds = LOBYTE(Edx) * 10;
-
-            if (SetLocalTime(&SystemTime))
-            {
-                /* Return success */
-                EmulatorSetRegister(EMULATOR_REG_AX, Eax & 0xFFFFFF00);
-            }
-            else
-            {
-                /* Return failure */
-                EmulatorSetRegister(EMULATOR_REG_AX, Eax | 0xFF);
-            }
+            SystemTime.wHour         = getCH();
+            SystemTime.wMinute       = getCL();
+            SystemTime.wSecond       = getDH();
+            SystemTime.wMilliseconds = getDL() * 10; // In hundredths of seconds
 
+            /* Return success or failure */
+            setAL(SetLocalTime(&SystemTime) ? 0x00 : 0xFF);
             break;
         }
 
         /* Get Disk Transfer Area */
         case 0x2F:
         {
-            EmulatorSetRegister(EMULATOR_REG_ES, HIWORD(DiskTransferArea));
-            EmulatorSetRegister(EMULATOR_REG_BX, LOWORD(DiskTransferArea));
-
+            setES(HIWORD(DiskTransferArea));
+            setBX(LOWORD(DiskTransferArea));
             break;
         }
 
@@ -1407,27 +1608,44 @@ VOID DosInt21h(LPWORD Stack)
         {
             PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp);
 
-            EmulatorSetRegister(EMULATOR_REG_AX, PspBlock->DosVersion);
+            if (LOBYTE(PspBlock->DosVersion) < 5 || getAL() == 0)
+            {
+                /* Return DOS 24-bit user serial number in BL:CX */
+                setBL(0x00);
+                setCX(0x0000);
+            }
+
+            if (LOBYTE(PspBlock->DosVersion) >= 5 && getAL() == 1)
+            {
+                /*
+                 * Return DOS OEM number:
+                 * 0x00 for IBM PC-DOS
+                 * 0xFF for MS-DOS
+                 */
+                setBH(0xFF);
+            }
+
+            /* Return DOS version: Minor:Major in AH:AL */
+            setAX(PspBlock->DosVersion);
+
             break;
         }
 
         /* Get Interrupt Vector */
         case 0x35:
         {
-            DWORD FarPointer = ((PDWORD)BaseAddress)[LOBYTE(Eax)];
+            DWORD FarPointer = ((PDWORD)BaseAddress)[getAL()];
 
             /* Read the address from the IDT into ES:BX */
-            EmulatorSetRegister(EMULATOR_REG_ES, HIWORD(FarPointer));
-            EmulatorSetRegister(EMULATOR_REG_BX, LOWORD(FarPointer));
-
+            setES(HIWORD(FarPointer));
+            setBX(LOWORD(FarPointer));
             break;
         }
 
         /* Create Directory */
         case 0x39:
         {
-            String = (PCHAR)((ULONG_PTR)BaseAddress
-                     + TO_LINEAR(DataSegment, LOWORD(Edx)));
+            String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
 
             if (CreateDirectoryA(String, NULL))
             {
@@ -1436,8 +1654,7 @@ VOID DosInt21h(LPWORD Stack)
             else
             {
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                EmulatorSetRegister(EMULATOR_REG_AX,
-                                    (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
+                setAX(LOWORD(GetLastError()));
             }
 
             break;
@@ -1446,8 +1663,7 @@ VOID DosInt21h(LPWORD Stack)
         /* Remove Directory */
         case 0x3A:
         {
-            String = (PCHAR)((ULONG_PTR)BaseAddress
-                     + TO_LINEAR(DataSegment, LOWORD(Edx)));
+            String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
 
             if (RemoveDirectoryA(String))
             {
@@ -1456,29 +1672,25 @@ VOID DosInt21h(LPWORD Stack)
             else
             {
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                EmulatorSetRegister(EMULATOR_REG_AX,
-                                    (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
+                setAX(LOWORD(GetLastError()));
             }
 
-
             break;
         }
 
         /* Set Current Directory */
         case 0x3B:
         {
-            String = (PCHAR)((ULONG_PTR)BaseAddress
-                     + TO_LINEAR(DataSegment, LOWORD(Edx)));
+            String = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
 
-            if (SetCurrentDirectoryA(String))
+            if (DosChangeDirectory(String))
             {
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
             else
             {
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                EmulatorSetRegister(EMULATOR_REG_AX,
-                                    (Eax & 0xFFFF0000) | LOWORD(GetLastError()));
+                setAX(DosLastError);
             }
 
             break;
@@ -1489,27 +1701,18 @@ VOID DosInt21h(LPWORD Stack)
         {
             WORD FileHandle;
             WORD ErrorCode = DosCreateFile(&FileHandle,
-                                           (LPCSTR)(ULONG_PTR)BaseAddress
-                                           + TO_LINEAR(DataSegment, LOWORD(Edx)),
-                                           LOWORD(Ecx));
+                                           (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
+                                           getCX());
 
             if (ErrorCode == 0)
             {
-                /* Clear CF */
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
-
-                /* Return the handle in AX */
-                EmulatorSetRegister(EMULATOR_REG_AX,
-                                    (Eax & 0xFFFF0000) | FileHandle);
+                setAX(FileHandle);
             }
             else
             {
-                /* Set CF */
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-
-                /* Return the error code in AX */
-                EmulatorSetRegister(EMULATOR_REG_AX,
-                                    (Eax & 0xFFFF0000) | ErrorCode);
+                setAX(ErrorCode);
             }
 
             break;
@@ -1519,28 +1722,19 @@ VOID DosInt21h(LPWORD Stack)
         case 0x3D:
         {
             WORD FileHandle;
-            WORD ErrorCode = DosCreateFile(&FileHandle,
-                                           (LPCSTR)(ULONG_PTR)BaseAddress
-                                           + TO_LINEAR(DataSegment, LOWORD(Edx)),
-                                           LOBYTE(Eax));
+            WORD ErrorCode = DosOpenFile(&FileHandle,
+                                         (LPCSTR)SEG_OFF_TO_PTR(getDS(), getDX()),
+                                         getAL());
 
             if (ErrorCode == 0)
             {
-                /* Clear CF */
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
-
-                /* Return the handle in AX */
-                EmulatorSetRegister(EMULATOR_REG_AX,
-                                    (Eax & 0xFFFF0000) | FileHandle);
+                setAX(FileHandle);
             }
             else
             {
-                /* Set CF */
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-
-                /* Return the error code in AX */
-                EmulatorSetRegister(EMULATOR_REG_AX,
-                                    (Eax & 0xFFFF0000) | ErrorCode);
+                setAX(ErrorCode);
             }
 
             break;
@@ -1549,19 +1743,14 @@ VOID DosInt21h(LPWORD Stack)
         /* Close File */
         case 0x3E:
         {
-            if (DosCloseHandle(LOWORD(Ebx)))
+            if (DosCloseHandle(getBX()))
             {
-                /* Clear CF */
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
             else
             {
-                /* Set CF */
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-
-                /* Return the error code in AX */
-                EmulatorSetRegister(EMULATOR_REG_AX,
-                                    (Eax & 0xFFFF0000) | ERROR_INVALID_HANDLE);
+                setAX(ERROR_INVALID_HANDLE);
             }
 
             break;
@@ -1570,30 +1759,44 @@ VOID DosInt21h(LPWORD Stack)
         /* Read File */
         case 0x3F:
         {
+            WORD Handle    = getBX();
+            LPBYTE Buffer  = (LPBYTE)SEG_OFF_TO_PTR(getDS(), getDX());
+            WORD Count     = getCX();
             WORD BytesRead = 0;
-            WORD ErrorCode = DosReadFile(LOWORD(Ebx),
-                                         (LPVOID)((ULONG_PTR)BaseAddress
-                                         + TO_LINEAR(DataSegment, LOWORD(Edx))),
-                                         LOWORD(Ecx),
-                                         &BytesRead);
+            WORD ErrorCode = ERROR_SUCCESS;
 
-            if (ErrorCode == 0)
+            if (IsConsoleHandle(DosGetRealHandle(Handle)))
             {
-                /* Clear CF */
-                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                while (Stack[STACK_COUNTER] < Count)
+                {
+                    /* Read a character from the BIOS */
+                    Buffer[Stack[STACK_COUNTER]] = LOBYTE(BiosGetCharacter()); // FIXME: Security checks!
+
+                    /* Stop if the BOP needs to be repeated */
+                    if (EmulatorGetFlag(EMULATOR_FLAG_CF)) break;
 
-                /* Return the number of bytes read in AX */
-                EmulatorSetRegister(EMULATOR_REG_AX,
-                                    (Eax & 0xFFFF0000) | BytesRead);
+                    /* Increment the counter */
+                    Stack[STACK_COUNTER]++;
+                }
+
+                if (Stack[STACK_COUNTER] < Count) ErrorCode = ERROR_NOT_READY;
+                else BytesRead = Count;
             }
             else
             {
-                /* Set CF */
-                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                /* Use the file reading function */
+                ErrorCode = DosReadFile(Handle, Buffer, Count, &BytesRead);
+            }
 
-                /* Return the error code in AX */
-                EmulatorSetRegister(EMULATOR_REG_AX,
-                                    (Eax & 0xFFFF0000) | ErrorCode);
+            if (ErrorCode == 0)
+            {
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                setAX(BytesRead);
+            }
+            else if (ErrorCode != ERROR_NOT_READY)
+            {
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                setAX(ErrorCode);
             }
             break;
         }
@@ -1602,29 +1805,20 @@ VOID DosInt21h(LPWORD Stack)
         case 0x40:
         {
             WORD BytesWritten = 0;
-            WORD ErrorCode = DosWriteFile(LOWORD(Ebx),
-                                          (LPVOID)((ULONG_PTR)BaseAddress
-                                          + TO_LINEAR(DataSegment, LOWORD(Edx))),
-                                          LOWORD(Ecx),
+            WORD ErrorCode = DosWriteFile(getBX(),
+                                          SEG_OFF_TO_PTR(getDS(), getDX()),
+                                          getCX(),
                                           &BytesWritten);
 
             if (ErrorCode == 0)
             {
-                /* Clear CF */
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
-
-                /* Return the number of bytes written in AX */
-                EmulatorSetRegister(EMULATOR_REG_AX,
-                                    (Eax & 0xFFFF0000) | BytesWritten);
+                setAX(BytesWritten);
             }
             else
             {
-                /* Set CF */
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-
-                /* Return the error code in AX */
-                EmulatorSetRegister(EMULATOR_REG_AX,
-                                    (Eax & 0xFFFF0000) | ErrorCode);
+                setAX(ErrorCode);
             }
 
             break;
@@ -1633,14 +1827,17 @@ VOID DosInt21h(LPWORD Stack)
         /* Delete File */
         case 0x41:
         {
-            LPSTR FileName = (LPSTR)((ULONG_PTR)BaseAddress + TO_LINEAR(DataSegment, Edx));
+            LPSTR FileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
 
             /* Call the API function */
-            if (DeleteFileA(FileName)) Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            if (DeleteFileA(FileName))
+            {
+                Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+            }
             else
             {
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                EmulatorSetRegister(EMULATOR_REG_AX, GetLastError());
+                setAX(GetLastError());
             }
 
             break;
@@ -1650,30 +1847,23 @@ VOID DosInt21h(LPWORD Stack)
         case 0x42:
         {
             DWORD NewLocation;
-            WORD ErrorCode = DosSeekFile(LOWORD(Ebx),
-                                         MAKELONG(LOWORD(Edx), LOWORD(Ecx)),
-                                         LOBYTE(Eax),
+            WORD ErrorCode = DosSeekFile(getBX(),
+                                         MAKELONG(getDX(), getCX()),
+                                         getAL(),
                                          &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));
+                setDX(HIWORD(NewLocation));
+                setAX(LOWORD(NewLocation));
             }
             else
             {
-                /* Set CF */
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-
-                /* Return the error code in AX */
-                EmulatorSetRegister(EMULATOR_REG_AX,
-                                    (Eax & 0xFFFF0000) | ErrorCode);
+                setAX(ErrorCode);
             }
 
             break;
@@ -1683,9 +1873,9 @@ VOID DosInt21h(LPWORD Stack)
         case 0x43:
         {
             DWORD Attributes;
-            LPSTR FileName = (LPSTR)((ULONG_PTR)BaseAddress + TO_LINEAR(DataSegment, Edx));
+            LPSTR FileName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
 
-            if (LOBYTE(Eax) == 0x00)
+            if (getAL() == 0x00)
             {
                 /* Get the attributes */
                 Attributes = GetFileAttributesA(FileName);
@@ -1694,33 +1884,31 @@ VOID DosInt21h(LPWORD Stack)
                 if (Attributes == INVALID_FILE_ATTRIBUTES)
                 {
                     Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                    EmulatorSetRegister(EMULATOR_REG_AX, GetLastError());
-
+                    setAX(GetLastError());
                     break;
                 }
 
                 /* Return the attributes that DOS can understand */
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
-                EmulatorSetRegister(EMULATOR_REG_CX,
-                                    (Ecx & 0xFFFFFF00) | LOBYTE(Attributes));
+                setCL(LOBYTE(Attributes));
             }
-            else if (LOBYTE(Eax) == 0x01)
+            else if (getAL() == 0x01)
             {
                 /* Try to set the attributes */
-                if (SetFileAttributesA(FileName, LOBYTE(Ecx)))
+                if (SetFileAttributesA(FileName, getCL()))
                 {
                     Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
                 }
                 else
                 {
                     Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                    EmulatorSetRegister(EMULATOR_REG_AX, GetLastError());
+                    setAX(GetLastError());
                 }
             }
             else
             {
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_FUNCTION);
+                setAX(ERROR_INVALID_FUNCTION);
             }
 
             break;
@@ -1729,14 +1917,14 @@ VOID DosInt21h(LPWORD Stack)
         /* IOCTL */
         case 0x44:
         {
-            if (DosHandleIoctl(LOBYTE(Eax), LOWORD(Ebx)))
+            if (DosHandleIoctl(getAL(), getBX()))
             {
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
             else
             {
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                EmulatorSetRegister(EMULATOR_REG_AX, DosLastError);
+                setAX(DosLastError);
             }
 
             break;
@@ -1746,14 +1934,13 @@ VOID DosInt21h(LPWORD Stack)
         case 0x45:
         {
             WORD NewHandle;
-            HANDLE Handle = DosGetRealHandle(LOWORD(Ebx));
+            HANDLE Handle = DosGetRealHandle(getBX());
 
             if (Handle != INVALID_HANDLE_VALUE)
             {
                 /* The handle is invalid */
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_HANDLE);
-
+                setAX(ERROR_INVALID_HANDLE);
                 break;
             }
 
@@ -1764,29 +1951,27 @@ VOID DosInt21h(LPWORD Stack)
             {
                 /* Too many files open */
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                EmulatorSetRegister(EMULATOR_REG_AX, ERROR_TOO_MANY_OPEN_FILES);
-
+                setAX(ERROR_TOO_MANY_OPEN_FILES);
                 break;
             }
 
             /* Return the result */
             Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
-            EmulatorSetRegister(EMULATOR_REG_AX, NewHandle);
-
+            setAX(NewHandle);
             break;
         }
 
         /* Force Duplicate Handle */
         case 0x46:
         {
-            if (DosDuplicateHandle(LOWORD(Ebx), LOWORD(Ecx)))
+            if (DosDuplicateHandle(getBX(), getCX()))
             {
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
             else
             {
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_HANDLE);
+                setAX(ERROR_INVALID_HANDLE);
             }
 
             break;
@@ -1796,18 +1981,18 @@ VOID DosInt21h(LPWORD Stack)
         case 0x48:
         {
             WORD MaxAvailable = 0;
-            WORD Segment = DosAllocateMemory(LOWORD(Ebx), &MaxAvailable);
+            WORD Segment = DosAllocateMemory(getBX(), &MaxAvailable);
 
             if (Segment != 0)
             {
-                EmulatorSetRegister(EMULATOR_REG_AX, Segment);
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                setAX(Segment);
             }
             else
             {
-                EmulatorSetRegister(EMULATOR_REG_AX, DosLastError);
-                EmulatorSetRegister(EMULATOR_REG_BX, MaxAvailable);
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                setAX(DosLastError);
+                setBX(MaxAvailable);
             }
 
             break;
@@ -1816,14 +2001,14 @@ VOID DosInt21h(LPWORD Stack)
         /* Free Memory */
         case 0x49:
         {
-            if (DosFreeMemory(ExtSegment))
+            if (DosFreeMemory(getES()))
             {
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
             else
             {
-                EmulatorSetRegister(EMULATOR_REG_AX, ERROR_ARENA_TRASHED);
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                setAX(ERROR_ARENA_TRASHED);
             }
 
             break;
@@ -1834,15 +2019,15 @@ VOID DosInt21h(LPWORD Stack)
         {
             WORD Size;
 
-            if (DosResizeMemory(ExtSegment, LOWORD(Ebx), &Size))
+            if (DosResizeMemory(getES(), getBX(), &Size))
             {
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
             else
             {
-                EmulatorSetRegister(EMULATOR_REG_AX, DosLastError);
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                EmulatorSetRegister(EMULATOR_REG_BX, Size);
+                setAX(DosLastError);
+                setBX(Size);
             }
 
             break;
@@ -1851,75 +2036,68 @@ VOID DosInt21h(LPWORD Stack)
         /* Terminate With Return Code */
         case 0x4C:
         {
-            DosTerminateProcess(CurrentPsp, LOBYTE(Eax));
+            DosTerminateProcess(CurrentPsp, getAL());
             break;
         }
 
         /* Get Current Process */
         case 0x51:
         {
-            EmulatorSetRegister(EMULATOR_REG_BX, CurrentPsp);
-
+            setBX(CurrentPsp);
             break;
         }
 
         /* Get/Set Memory Management Options */
         case 0x58:
         {
-            if (LOBYTE(Eax) == 0x00)
+            if (getAL() == 0x00)
             {
                 /* Get allocation strategy */
-
-                EmulatorSetRegister(EMULATOR_REG_AX, DosAllocStrategy);
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                setAX(DosAllocStrategy);
             }
-            else if (LOBYTE(Eax) == 0x01)
+            else if (getAL() == 0x01)
             {
                 /* Set allocation strategy */
 
-                if ((LOBYTE(Ebx) & (DOS_ALLOC_HIGH | DOS_ALLOC_HIGH_LOW))
+                if ((getBL() & (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;
+                    setAX(ERROR_INVALID_PARAMETER);
                     break;
                 }
 
-                if ((LOBYTE(Ebx) & 0x3F) > DOS_ALLOC_LAST_FIT)
+                if ((getBL() & 0x3F) > DOS_ALLOC_LAST_FIT)
                 {
                     /* Invalid allocation strategy */
-                    EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_PARAMETER);
                     Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                    setAX(ERROR_INVALID_PARAMETER);
                     break;
                 }
 
-                DosAllocStrategy = LOBYTE(Ebx);
+                DosAllocStrategy = getBL();
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
-            else if (LOBYTE(Eax) == 0x02)
+            else if (getAL() == 0x02)
             {
                 /* Get UMB link state */
-
-                Eax &= 0xFFFFFF00;
-                if (DosUmbLinked) Eax |= 1;
-                EmulatorSetRegister(EMULATOR_REG_AX, Eax);
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                setAL(DosUmbLinked ? 0x01 : 0x00);
             }
-            else if (LOBYTE(Eax) == 0x03)
+            else if (getAL() == 0x03)
             {
                 /* Set UMB link state */
-
-                if (Ebx) DosLinkUmb();
+                if (getBX()) DosLinkUmb();
                 else DosUnlinkUmb();
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
             else
             {
                 /* Invalid or unsupported function */
-
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                EmulatorSetRegister(EMULATOR_REG_AX, ERROR_INVALID_FUNCTION);
+                setAX(ERROR_INVALID_FUNCTION);
             }
 
             break;
@@ -1928,7 +2106,7 @@ VOID DosInt21h(LPWORD Stack)
         /* Unsupported */
         default:
         {
-            DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", HIBYTE(Eax));
+            DPRINT1("DOS Function INT 0x21, AH = 0x%02X NOT IMPLEMENTED!\n", getAH());
             Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
         }
     }
@@ -1936,6 +2114,8 @@ VOID DosInt21h(LPWORD Stack)
 
 VOID DosBreakInterrupt(LPWORD Stack)
 {
+    UNREFERENCED_PARAMETER(Stack);
+
     VdmRunning = FALSE;
 }
 
@@ -1947,8 +2127,11 @@ BOOLEAN DosInitialize(VOID)
     WCHAR Buffer[256];
     LPWSTR SourcePtr, Environment;
     LPSTR AsciiString;
-    LPSTR DestPtr = (LPSTR)((ULONG_PTR)BaseAddress + TO_LINEAR(SYSTEM_ENV_BLOCK, 0));
+    LPSTR DestPtr = (LPSTR)SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0);
     DWORD AsciiSize;
+    CHAR CurrentDirectory[MAX_PATH];
+    CHAR DosDirectory[DOS_DIR_LENGTH];
+    LPSTR Path;
 
     /* Initialize the MCB */
     Mcb->BlockType = 'Z';
@@ -2018,6 +2201,37 @@ BOOLEAN DosInitialize(VOID)
     /* 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)