[NTVDM]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / dos / dos32krnl / dos.c
index 3b3c673..94c94e1 100644 (file)
 
 /* PRIVATE VARIABLES **********************************************************/
 
-#define INDOS_POINTER MAKELONG(0x00FE, 0x0070)
-
 CALLBACK16 DosContext;
 
-/*static*/ BYTE CurrentDrive;
-static CHAR LastDrive = 'E';
-static CHAR CurrentDirectories[NUM_DRIVES][DOS_DIR_LENGTH];
-static PBYTE InDos;
-
 /* PUBLIC VARIABLES ***********************************************************/
 
+/* Global DOS data area contained in guest memory */
+PDOS_DATA DosData;
+/* Easy accessors to useful DOS data area parts */
 PDOS_SYSVARS SysVars;
+PDOS_SDA Sda;
 
 /* Echo state for INT 21h, AH = 01h and AH = 3Fh */
 BOOLEAN DoEcho = FALSE;
 
-DWORD DiskTransferArea;
-WORD DosErrorLevel = 0x0000;
-WORD DosLastError = 0;
-
 /* PRIVATE FUNCTIONS **********************************************************/
 
 static BOOLEAN DosChangeDrive(BYTE Drive)
 {
-    WCHAR DirectoryPath[DOS_CMDLINE_LENGTH];
+    CHAR DirectoryPath[DOS_CMDLINE_LENGTH + 1];
 
     /* Make sure the drive exists */
-    if (Drive > (LastDrive - 'A')) return FALSE;
+    if (Drive >= SysVars->NumLocalDrives) return FALSE;
+
+    RtlZeroMemory(DirectoryPath, sizeof(DirectoryPath));
 
     /* Find the path to the new current directory */
-    swprintf(DirectoryPath, L"%c\\%S", Drive + 'A', CurrentDirectories[Drive]);
+    snprintf(DirectoryPath,
+             DOS_CMDLINE_LENGTH,
+             "%c:\\%s",
+             'A' + Drive,
+             DosData->CurrentDirectories[Drive]);
 
     /* Change the current directory of the process */
-    if (!SetCurrentDirectory(DirectoryPath)) return FALSE;
+    if (!SetCurrentDirectoryA(DirectoryPath)) return FALSE;
 
     /* Set the current drive */
-    CurrentDrive = Drive;
+    Sda->CurrentDrive = Drive;
 
     /* Return success */
     return TRUE;
@@ -81,22 +80,33 @@ static BOOLEAN DosChangeDirectory(LPSTR Directory)
     BYTE DriveNumber;
     DWORD Attributes;
     LPSTR Path;
+    CHAR CurrentDirectory[MAX_PATH];
+    CHAR DosDirectory[DOS_DIR_LENGTH];
 
     /* Make sure the directory path is not too long */
     if (strlen(Directory) >= DOS_DIR_LENGTH)
     {
-        DosLastError = ERROR_PATH_NOT_FOUND;
+        Sda->LastErrorCode = ERROR_PATH_NOT_FOUND;
         return FALSE;
     }
 
-    /* Get the drive number */
-    DriveNumber = Directory[0] - 'A';
+    /* Check whether the directory string is of format "?:..." */
+    if (strlen(Directory) >= 2 && Directory[1] == ':')
+    {
+        /* Get the drive number */
+        DriveNumber = RtlUpperChar(Directory[0]) - 'A';
 
-    /* Make sure the drive exists */
-    if (DriveNumber > (LastDrive - 'A'))
+        /* Make sure the drive exists */
+        if (DriveNumber >= SysVars->NumLocalDrives)
+        {
+            Sda->LastErrorCode = ERROR_PATH_NOT_FOUND;
+            return FALSE;
+        }
+    }
+    else
     {
-        DosLastError = ERROR_PATH_NOT_FOUND;
-        return FALSE;
+        /* Keep the current drive number */
+        DriveNumber = Sda->CurrentDrive;
     }
 
     /* Get the file attributes */
@@ -106,23 +116,37 @@ static BOOLEAN DosChangeDirectory(LPSTR Directory)
     if ((Attributes == INVALID_FILE_ATTRIBUTES)
         || !(Attributes & FILE_ATTRIBUTE_DIRECTORY))
     {
-        DosLastError = ERROR_PATH_NOT_FOUND;
+        Sda->LastErrorCode = ERROR_PATH_NOT_FOUND;
         return FALSE;
     }
 
     /* Check if this is the current drive */
-    if (DriveNumber == CurrentDrive)
+    if (DriveNumber == Sda->CurrentDrive)
     {
         /* Change the directory */
         if (!SetCurrentDirectoryA(Directory))
         {
-            DosLastError = LOWORD(GetLastError());
+            Sda->LastErrorCode = LOWORD(GetLastError());
             return FALSE;
         }
     }
 
+    /* Get the (possibly new) current directory (needed if we specified a relative directory) */
+    if (!GetCurrentDirectoryA(sizeof(CurrentDirectory), CurrentDirectory))
+    {
+        // TODO: Use some kind of default path?
+        return FALSE;
+    }
+
+    /* Convert it to a DOS path */
+    if (!GetShortPathNameA(CurrentDirectory, DosDirectory, sizeof(DosDirectory)))
+    {
+        // TODO: Use some kind of default path?
+        return FALSE;
+    }
+
     /* Get the directory part of the path */
-    Path = strchr(Directory, '\\');
+    Path = strchr(DosDirectory, '\\');
     if (Path != NULL)
     {
         /* Skip the backslash */
@@ -132,11 +156,11 @@ static BOOLEAN DosChangeDirectory(LPSTR Directory)
     /* Set the directory for the drive */
     if (Path != NULL)
     {
-        strncpy(CurrentDirectories[DriveNumber], Path, DOS_DIR_LENGTH);
+        strncpy(DosData->CurrentDirectories[DriveNumber], Path, DOS_DIR_LENGTH);
     }
     else
     {
-        CurrentDirectories[DriveNumber][0] = '\0';
+        DosData->CurrentDirectories[DriveNumber][0] = '\0';
     }
 
     /* Return success */
@@ -152,7 +176,7 @@ static BOOLEAN DosControlBreak(VOID)
 
     if (getCF())
     {
-        DosTerminateProcess(CurrentPsp, 0, 0);
+        DosTerminateProcess(Sda->CurrentPsp, 0, 0);
         return TRUE;
     }
 
@@ -173,13 +197,11 @@ VOID WINAPI DosInt21h(LPWORD Stack)
     SYSTEMTIME SystemTime;
     PCHAR String;
     PDOS_INPUT_BUFFER InputBuffer;
-    PDOS_COUNTRY_CODE_BUFFER CountryCodeBuffer;
-    INT Return;
 
-    (*InDos)++;
+    Sda->InDos++;
 
     /* Save the value of SS:SP on entry in the PSP */
-    SEGMENT_TO_PSP(CurrentPsp)->LastStack =
+    SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack =
     MAKELONG(getSP() + (STACK_FLAGS + 1) * 2, getSS());
 
     /* Check the value in the AH register */
@@ -402,6 +424,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                 }
 
                 if (Character == '\r') break;
+                if (Character == '\b') continue;
                 Count++; /* Carriage returns are NOT counted */
             }
 
@@ -445,7 +468,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Disk Reset */
         case 0x0D:
         {
-            PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp);
+            PDOS_PSP PspBlock = SEGMENT_TO_PSP(Sda->CurrentPsp);
 
             // TODO: Flush what's needed.
             DPRINT1("INT 21h, 0Dh is UNIMPLEMENTED\n");
@@ -461,7 +484,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         case 0x0E:
         {
             DosChangeDrive(getDL());
-            setAL(LastDrive - 'A' + 1);
+            setAL(SysVars->NumLocalDrives);
             break;
         }
 
@@ -484,14 +507,14 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Get Default Drive */
         case 0x19:
         {
-            setAL(CurrentDrive);
+            setAL(Sda->CurrentDrive);
             break;
         }
 
         /* Set Disk Transfer Area */
         case 0x1A:
         {
-            DiskTransferArea = MAKELONG(getDX(), getDS());
+            Sda->DiskTransferArea = MAKELONG(getDX(), getDS());
             break;
         }
 
@@ -556,8 +579,8 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             PCHAR FileName = (PCHAR)SEG_OFF_TO_PTR(getDS(), getSI());
             PDOS_FCB Fcb = (PDOS_FCB)SEG_OFF_TO_PTR(getES(), getDI());
             BYTE Options = getAL();
-            INT i;
             CHAR FillChar = ' ';
+            UINT i;
 
             if (FileName[1] == ':')
             {
@@ -570,7 +593,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             else
             {
                 /* No drive number specified */
-                if (Options & (1 << 1)) Fcb->DriveNumber = CurrentDrive + 1;
+                if (Options & (1 << 1)) Fcb->DriveNumber = Sda->CurrentDrive + 1;
                 else Fcb->DriveNumber = 0;
             }
 
@@ -671,15 +694,15 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Get Disk Transfer Area */
         case 0x2F:
         {
-            setES(HIWORD(DiskTransferArea));
-            setBX(LOWORD(DiskTransferArea));
+            setES(HIWORD(Sda->DiskTransferArea));
+            setBX(LOWORD(Sda->DiskTransferArea));
             break;
         }
 
         /* Get DOS Version */
         case 0x30:
         {
-            PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp);
+            PDOS_PSP PspBlock = SEGMENT_TO_PSP(Sda->CurrentPsp);
 
             /*
              * DOS 2+ - GET DOS VERSION
@@ -725,7 +748,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         case 0x31:
         {
             DPRINT1("Process going resident: %u paragraphs kept\n", getDX());
-            DosTerminateProcess(CurrentPsp, getAL(), getDX());
+            DosTerminateProcess(Sda->CurrentPsp, getAL(), getDX());
             break;
         }
 
@@ -766,16 +789,15 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Get Address of InDOS flag */
         case 0x34:
         {
-            setES(HIWORD(INDOS_POINTER));
-            setBX(LOWORD(INDOS_POINTER));
-
+            setES(DOS_DATA_SEGMENT);
+            setBX(DOS_DATA_OFFSET(Sda.InDos));
             break;
         }
 
         /* Get Interrupt Vector */
         case 0x35:
         {
-            DWORD FarPointer = ((PDWORD)BaseAddress)[getAL()];
+            ULONG FarPointer = ((PULONG)BaseAddress)[getAL()];
 
             /* Read the address from the IDT into ES:BX */
             setES(HIWORD(FarPointer));
@@ -786,13 +808,13 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Get Free Disk Space */
         case 0x36:
         {
-            CHAR RootPath[3] = "X:\\";
+            CHAR RootPath[] = "?:\\";
             DWORD SectorsPerCluster;
             DWORD BytesPerSector;
             DWORD NumberOfFreeClusters;
             DWORD TotalNumberOfClusters;
 
-            if (getDL() == 0) RootPath[0] = 'A' + CurrentDrive;
+            if (getDL() == 0x00) RootPath[0] = 'A' + Sda->CurrentDrive;
             else RootPath[0] = 'A' + getDL() - 1;
 
             if (GetDiskFreeSpaceA(RootPath,
@@ -803,8 +825,8 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             {
                 setAX(LOWORD(SectorsPerCluster));
                 setCX(LOWORD(BytesPerSector));
-                setBX(LOWORD(NumberOfFreeClusters));
-                setDX(LOWORD(TotalNumberOfClusters));
+                setBX(min(NumberOfFreeClusters, 0xFFFF));
+                setDX(min(TotalNumberOfClusters, 0xFFFF));
             }
             else
             {
@@ -873,14 +895,16 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Get/Set Country-dependent Information */
         case 0x38:
         {
-            CountryCodeBuffer = (PDOS_COUNTRY_CODE_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX());
+            INT Return;
+            PDOS_COUNTRY_CODE_BUFFER CountryCodeBuffer =
+                (PDOS_COUNTRY_CODE_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX());
 
             if (getAL() == 0x00)
             {
                 /* Get */
-                Return = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDATE,
-                                       &CountryCodeBuffer->TimeFormat,
-                                       sizeof(CountryCodeBuffer->TimeFormat) / sizeof(TCHAR));
+                Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IDATE,
+                                        (LPSTR)&CountryCodeBuffer->TimeFormat,
+                                        sizeof(CountryCodeBuffer->TimeFormat));
                 if (Return == 0)
                 {
                     Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
@@ -888,9 +912,9 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                     break;
                 }
 
-                Return = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SCURRENCY,
-                                       &CountryCodeBuffer->CurrencySymbol,
-                                       sizeof(CountryCodeBuffer->CurrencySymbol) / sizeof(TCHAR));
+                Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SCURRENCY,
+                                        (LPSTR)&CountryCodeBuffer->CurrencySymbol,
+                                        sizeof(CountryCodeBuffer->CurrencySymbol));
                 if (Return == 0)
                 {
                     Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
@@ -898,9 +922,9 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                     break;
                 }
 
-                Return = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND,
-                                       &CountryCodeBuffer->ThousandSep,
-                                       sizeof(CountryCodeBuffer->ThousandSep) / sizeof(TCHAR));
+                Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND,
+                                        (LPSTR)&CountryCodeBuffer->ThousandSep,
+                                        sizeof(CountryCodeBuffer->ThousandSep));
                 if (Return == 0)
                 {
                     Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
@@ -908,9 +932,9 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                     break;
                 }
 
-                Return = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
-                                       &CountryCodeBuffer->DecimalSep,
-                                       sizeof(CountryCodeBuffer->DecimalSep) / sizeof(TCHAR));
+                Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
+                                        (LPSTR)&CountryCodeBuffer->DecimalSep,
+                                        sizeof(CountryCodeBuffer->DecimalSep));
                 if (Return == 0)
                 {
                     Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
@@ -977,7 +1001,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             else
             {
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                setAX(DosLastError);
+                setAX(Sda->LastErrorCode);
             }
 
             break;
@@ -1203,7 +1227,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             else
             {
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                setAX(DosLastError);
+                setAX(Sda->LastErrorCode);
             }
 
             break;
@@ -1222,7 +1246,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             else
             {
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                setAX(DosLastError);
+                setAX(Sda->LastErrorCode);
             }
 
             break;
@@ -1253,7 +1277,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             /* Get the real drive number */
             if (DriveNumber == 0)
             {
-                DriveNumber = CurrentDrive;
+                DriveNumber = Sda->CurrentDrive;
             }
             else
             {
@@ -1261,13 +1285,13 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                 DriveNumber--;
             }
 
-            if (DriveNumber <= LastDrive - 'A')
+            if (DriveNumber < SysVars->NumLocalDrives)
             {
                 /*
                  * Copy the current directory into the target buffer.
                  * It doesn't contain the drive letter and the backslash.
                  */
-                strncpy(String, CurrentDirectories[DriveNumber], DOS_DIR_LENGTH);
+                strncpy(String, DosData->CurrentDirectories[DriveNumber], DOS_DIR_LENGTH);
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
                 setAX(0x0100); // Undocumented, see Ralf Brown: http://www.ctyme.com/intr/rb-2933.htm
             }
@@ -1294,7 +1318,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             else
             {
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                setAX(DosLastError);
+                setAX(Sda->LastErrorCode);
                 setBX(MaxAvailable);
             }
 
@@ -1329,7 +1353,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             else
             {
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                setAX(DosLastError);
+                setAX(Sda->LastErrorCode);
                 setBX(Size);
             }
 
@@ -1342,7 +1366,6 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             BYTE OrgAL = getAL();
             LPSTR ProgramName = SEG_OFF_TO_PTR(getDS(), getDX());
             PDOS_EXEC_PARAM_BLOCK ParamBlock = SEG_OFF_TO_PTR(getES(), getBX());
-            DWORD ReturnAddress = MAKELONG(Stack[STACK_IP], Stack[STACK_CS]);
             WORD ErrorCode;
 
             if (OrgAL <= DOS_LOAD_OVERLAY)
@@ -1353,9 +1376,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                 if (LoadType == DOS_LOAD_AND_EXECUTE)
                 {
                     /* Create a new process */
-                    ErrorCode = DosCreateProcess(ProgramName,
-                                                 ParamBlock,
-                                                 ReturnAddress);
+                    ErrorCode = DosCreateProcess(ProgramName, ParamBlock);
                 }
                 else
 #endif
@@ -1365,8 +1386,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                                                   ProgramName,
                                                   ParamBlock,
                                                   NULL,
-                                                  NULL,
-                                                  ReturnAddress);
+                                                  NULL);
                 }
             }
             else if (OrgAL == 0x05)
@@ -1396,7 +1416,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Terminate With Return Code */
         case 0x4C:
         {
-            DosTerminateProcess(CurrentPsp, getAL(), 0);
+            DosTerminateProcess(Sda->CurrentPsp, getAL(), 0);
             break;
         }
 
@@ -1408,15 +1428,15 @@ VOID WINAPI DosInt21h(LPWORD Stack)
              * DosErrorLevel is cleared after being read by this function.
              */
             Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
-            setAX(DosErrorLevel);
-            DosErrorLevel = 0x0000; // Clear it
+            setAX(Sda->ErrorLevel);
+            Sda->ErrorLevel = 0x0000; // Clear it
             break;
         }
 
         /* Find First File */
         case 0x4E:
         {
-            WORD Result = (WORD)demFileFindFirst(FAR_POINTER(DiskTransferArea),
+            WORD Result = (WORD)demFileFindFirst(FAR_POINTER(Sda->DiskTransferArea),
                                                  SEG_OFF_TO_PTR(getDS(), getDX()),
                                                  getCX());
 
@@ -1433,7 +1453,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Find Next File */
         case 0x4F:
         {
-            WORD Result = (WORD)demFileFindNext(FAR_POINTER(DiskTransferArea));
+            WORD Result = (WORD)demFileFindNext(FAR_POINTER(Sda->DiskTransferArea));
 
             setAX(Result);
 
@@ -1463,7 +1483,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
              * and http://www.ctyme.com/intr/rb-3140.htm
              * for more information.
              */
-            setBX(CurrentPsp);
+            setBX(Sda->CurrentPsp);
             break;
         }
 
@@ -1478,7 +1498,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
 
             /* Return the DOS "list of lists" in ES:BX */
             setES(DOS_DATA_SEGMENT);
-            setBX(FIELD_OFFSET(DOS_SYSVARS, FirstDpb));
+            setBX(DOS_DATA_OFFSET(SysVars.FirstDpb));
 
             break;
         }
@@ -1521,7 +1541,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             {
                 /* Get allocation strategy */
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
-                setAX(DosAllocStrategy);
+                setAX(Sda->AllocStrategy);
             }
             else if (getAL() == 0x01)
             {
@@ -1544,7 +1564,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                     break;
                 }
 
-                DosAllocStrategy = getBL();
+                Sda->AllocStrategy = getBL();
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
             }
             else if (getAL() == 0x02)
@@ -1650,27 +1670,27 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             if (getAL() == 0x00)
             {
                 /* Lock region of file */
-                if (DosLockFile(getBX(), MAKELONG(getCX(), getDX()), MAKELONG(getSI(), getDI())))
+                if (DosLockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
                 {
                     Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
                 }
                 else
                 {
                     Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                    setAX(DosLastError);
+                    setAX(Sda->LastErrorCode);
                 }
             }
             else if (getAL() == 0x01)
             {
                 /* Unlock region of file */
-                if (DosUnlockFile(getBX(), MAKELONG(getCX(), getDX()), MAKELONG(getSI(), getDI())))
+                if (DosUnlockFile(getBX(), MAKELONG(getDX(), getCX()), MAKELONG(getDI(), getSI())))
                 {
                     Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
                 }
                 else
                 {
                     Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                    setAX(DosLastError);
+                    setAX(Sda->LastErrorCode);
                 }
             }
             else
@@ -1719,13 +1739,40 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             break;
         }
 
+        /* Miscellaneous Internal Functions */
+        case 0x5D:
+        {
+            switch (getAL())
+            {
+                /* Get Swappable Data Area */
+                case 0x06:
+                {
+                    setDS(DOS_DATA_SEGMENT);
+                    setSI(DOS_DATA_OFFSET(Sda.ErrorMode));
+                    setCX(sizeof(DOS_SDA));
+                    setDX(FIELD_OFFSET(DOS_SDA, LastAX));
+
+                    Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                    break;
+                }
+
+                default:
+                {
+                    DPRINT1("INT 21h, AH = 5Dh, subfunction AL = %Xh NOT IMPLEMENTED\n",
+                            getAL());
+                }
+            }
+
+            break;
+        }
+
         /* Set Handle Count */
         case 0x67:
         {
             if (!DosResizeHandleTable(getBX()))
             {
                 Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                setAX(DosLastError);
+                setAX(Sda->LastErrorCode);
             }
             else Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
 
@@ -1812,7 +1859,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         }
     }
 
-    (*InDos)--;
+    Sda->InDos--;
 }
 
 VOID WINAPI DosBreakInterrupt(LPWORD Stack)
@@ -1821,11 +1868,58 @@ VOID WINAPI DosBreakInterrupt(LPWORD Stack)
     Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
 }
 
+VOID WINAPI DosAbsoluteRead(LPWORD Stack)
+{
+    /*
+     * This call should leave the flags on the stack for some reason,
+     * so move the stack by one word.
+     */
+    Stack[STACK_INT_NUM] = Stack[STACK_IP];
+    Stack[STACK_IP] = Stack[STACK_CS];
+    Stack[STACK_CS] = Stack[STACK_FLAGS];
+    setSP(LOWORD(getSP() - 2));
+
+    // TODO: NOT IMPLEMENTED;
+    UNIMPLEMENTED;
+
+    /* General failure */
+    setAX(0x800C);
+    Stack[STACK_FLAGS - 1] |= EMULATOR_FLAG_CF;
+}
+
+VOID WINAPI DosAbsoluteWrite(LPWORD Stack)
+{
+    /*
+     * This call should leave the flags on the stack for some reason,
+     * so move the stack by one word.
+     */
+    Stack[STACK_INT_NUM] = Stack[STACK_IP];
+    Stack[STACK_IP] = Stack[STACK_CS];
+    Stack[STACK_CS] = Stack[STACK_FLAGS];
+    setSP(LOWORD(getSP() - 2));
+
+    // TODO: NOT IMPLEMENTED;
+    UNIMPLEMENTED;
+
+    /* General failure */
+    setAX(0x800C);
+    Stack[STACK_FLAGS - 1] |= EMULATOR_FLAG_CF;
+}
+
 VOID WINAPI DosInt27h(LPWORD Stack)
 {
     DosTerminateProcess(getCS(), 0, (getDX() + 0x0F) >> 4);
 }
 
+VOID WINAPI DosIdle(LPWORD Stack)
+{
+    /*
+     * This will set the carry flag on the first call (to repeat the BOP),
+     * and clear it in the next, so that exactly one HLT occurs.
+     */
+    setCF(!getCF());
+}
+
 VOID WINAPI DosFastConOut(LPWORD Stack)
 {
     /*
@@ -1885,7 +1979,7 @@ VOID WINAPI DosInt2Fh(LPWORD Stack)
 
             break;
         }
-        
+
         default:
         {
             DPRINT1("DOS Internal System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
@@ -1900,10 +1994,10 @@ BOOLEAN DosKRNLInitialize(VOID)
 #if 1
 
     UCHAR i;
+    PDOS_SFT Sft;
+    LPSTR Path;
     CHAR CurrentDirectory[MAX_PATH];
     CHAR DosDirectory[DOS_DIR_LENGTH];
-    LPSTR Path;
-    PDOS_SFT Sft;
 
     const BYTE NullDriverRoutine[] = {
         /* Strategy routine entry */
@@ -1921,29 +2015,53 @@ BOOLEAN DosKRNLInitialize(VOID)
     FILE *Stream;
     WCHAR Buffer[256];
 
-    /* Setup the InDOS flag */
-    InDos = (PBYTE)FAR_POINTER(INDOS_POINTER);
-    *InDos = 0;
+    /* Initialize the global DOS data area */
+    DosData = (PDOS_DATA)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT, 0x0000);
+    RtlZeroMemory(DosData, sizeof(*DosData));
+
+    /* Initialize the list of lists */
+    SysVars = &DosData->SysVars;
+    RtlZeroMemory(SysVars, sizeof(*SysVars));
+    SysVars->FirstMcb = FIRST_MCB_SEGMENT;
+    SysVars->FirstSft = MAKELONG(DOS_DATA_OFFSET(Sft), DOS_DATA_SEGMENT);
+    SysVars->CurrentDirs = MAKELONG(DOS_DATA_OFFSET(CurrentDirectories),
+                                    DOS_DATA_SEGMENT);
+    /* The last drive can be redefined with the LASTDRIVE command. At the moment, set the real maximum possible, 'Z'. */
+    SysVars->NumLocalDrives = 'Z' - 'A' + 1;
+
+    /* Initialize the NUL device driver */
+    SysVars->NullDevice.Link = 0xFFFFFFFF;
+    SysVars->NullDevice.DeviceAttributes = DOS_DEVATTR_NUL | DOS_DEVATTR_CHARACTER;
+    SysVars->NullDevice.StrategyRoutine = FIELD_OFFSET(DOS_SYSVARS, NullDriverRoutine);
+    SysVars->NullDevice.InterruptRoutine = SysVars->NullDevice.StrategyRoutine + 6;
+    RtlFillMemory(SysVars->NullDevice.DeviceName,
+                  sizeof(SysVars->NullDevice.DeviceName),
+                  ' ');
+    RtlCopyMemory(SysVars->NullDevice.DeviceName, "NUL", strlen("NUL"));
+    RtlCopyMemory(SysVars->NullDriverRoutine,
+                  NullDriverRoutine,
+                  sizeof(NullDriverRoutine));
 
-    /* Clear the current directory buffer */
-    RtlZeroMemory(CurrentDirectories, sizeof(CurrentDirectories));
+    /* Initialize the swappable data area */
+    Sda = &DosData->Sda;
+    RtlZeroMemory(Sda, sizeof(*Sda));
 
     /* Get the current directory */
-    if (!GetCurrentDirectoryA(MAX_PATH, CurrentDirectory))
+    if (!GetCurrentDirectoryA(sizeof(CurrentDirectory), CurrentDirectory))
     {
         // TODO: Use some kind of default path?
         return FALSE;
     }
 
-    /* Convert that to a DOS path */
-    if (!GetShortPathNameA(CurrentDirectory, DosDirectory, DOS_DIR_LENGTH))
+    /* Convert it to a DOS path */
+    if (!GetShortPathNameA(CurrentDirectory, DosDirectory, sizeof(DosDirectory)))
     {
         // TODO: Use some kind of default path?
         return FALSE;
     }
 
     /* Set the drive */
-    CurrentDrive = DosDirectory[0] - 'A';
+    Sda->CurrentDrive = RtlUpperChar(DosDirectory[0]) - 'A';
 
     /* Get the directory part of the path */
     Path = strchr(DosDirectory, '\\');
@@ -1956,38 +2074,14 @@ BOOLEAN DosKRNLInitialize(VOID)
     /* Set the directory */
     if (Path != NULL)
     {
-        strncpy(CurrentDirectories[CurrentDrive], Path, DOS_DIR_LENGTH);
+        strncpy(DosData->CurrentDirectories[Sda->CurrentDrive], Path, DOS_DIR_LENGTH);
     }
 
-    /* Read CONFIG.SYS */
-    Stream = _wfopen(DOS_CONFIG_PATH, L"r");
-    if (Stream != NULL)
-    {
-        while (fgetws(Buffer, 256, Stream))
-        {
-            // TODO: Parse the line
-        }
-        fclose(Stream);
-    }
-
-    /* Initialize the list of lists */
-    SysVars = (PDOS_SYSVARS)SEG_OFF_TO_PTR(DOS_DATA_SEGMENT, 0);
-    RtlZeroMemory(SysVars, sizeof(DOS_SYSVARS));
-    SysVars->FirstMcb = FIRST_MCB_SEGMENT;
-    SysVars->FirstSft = MAKELONG(MASTER_SFT_OFFSET, DOS_DATA_SEGMENT);
+    /* Set the current PSP to the system PSP */
+    Sda->CurrentPsp = SYSTEM_PSP;
 
-    /* Initialize the NUL device driver */
-    SysVars->NullDevice.Link = 0xFFFFFFFF;
-    SysVars->NullDevice.DeviceAttributes = DOS_DEVATTR_NUL | DOS_DEVATTR_CHARACTER;
-    SysVars->NullDevice.StrategyRoutine = FIELD_OFFSET(DOS_SYSVARS, NullDriverRoutine);
-    SysVars->NullDevice.InterruptRoutine = SysVars->NullDevice.StrategyRoutine + 6;
-    RtlFillMemory(SysVars->NullDevice.DeviceName,
-                  sizeof(SysVars->NullDevice.DeviceName),
-                  ' ');
-    RtlCopyMemory(SysVars->NullDevice.DeviceName, "NUL", strlen("NUL"));
-    RtlCopyMemory(SysVars->NullDriverRoutine,
-                  NullDriverRoutine,
-                  sizeof(NullDriverRoutine));
+    /* Set the initial allocation strategy to "best fit" */
+    Sda->AllocStrategy = DOS_ALLOC_BEST_FIT;
 
     /* Initialize the SFT */
     Sft = (PDOS_SFT)FAR_POINTER(SysVars->FirstSft);
@@ -2000,10 +2094,21 @@ BOOLEAN DosKRNLInitialize(VOID)
         RtlZeroMemory(&Sft->FileDescriptors[i], sizeof(DOS_FILE_DESCRIPTOR));
     }
 
+    /* Read CONFIG.SYS */
+    Stream = _wfopen(DOS_CONFIG_PATH, L"r");
+    if (Stream != NULL)
+    {
+        while (fgetws(Buffer, 256, Stream))
+        {
+            // TODO: Parse the line
+        }
+        fclose(Stream);
+    }
+
 #endif
 
     /* Initialize the callback context */
-    InitializeContext(&DosContext, 0x0070, 0x0000);
+    InitializeContext(&DosContext, DOS_CODE_SEGMENT, 0x0000);
 
     /* Register the DOS 32-bit Interrupts */
     RegisterDosInt32(0x20, DosInt20h        );
@@ -2011,10 +2116,22 @@ BOOLEAN DosKRNLInitialize(VOID)
 //  RegisterDosInt32(0x22, DosInt22h        ); // Termination
     RegisterDosInt32(0x23, DosBreakInterrupt); // Ctrl-C / Ctrl-Break
 //  RegisterDosInt32(0x24, DosInt24h        ); // Critical Error
+    RegisterDosInt32(0x25, DosAbsoluteRead  ); // Absolute Disk Read
+    RegisterDosInt32(0x26, DosAbsoluteWrite ); // Absolute Disk Write
     RegisterDosInt32(0x27, DosInt27h        ); // Terminate and Stay Resident
+    RegisterDosInt32(0x28, DosIdle          ); // DOS Idle Interrupt
     RegisterDosInt32(0x29, DosFastConOut    ); // DOS 2+ Fast Console Output
     RegisterDosInt32(0x2F, DosInt2Fh        );
 
+    /* Unimplemented DOS interrupts */
+    RegisterDosInt32(0x2A, NULL); // Network - Installation Check
+
+    /* Load the CON driver */
+    ConDrvInitialize();
+
+    /* Load the XMS driver (HIMEM) */
+    XmsInitialize();
+
     /* Load the EMS driver */
     if (!EmsDrvInitialize(EMS_TOTAL_PAGES))
     {
@@ -2022,12 +2139,6 @@ BOOLEAN DosKRNLInitialize(VOID)
                 "Try reducing the number of EMS pages.\n");
     }
 
-    /* Load the XMS driver (HIMEM) */
-    XmsInitialize();
-
-    /* Load the CON driver */
-    ConDrvInitialize();
-
     return TRUE;
 }