[NTVDM]: DOS: Add some description / fix a comment.
[reactos.git] / subsystems / ntvdm / dos.c
index 48c53ab..f094fd4 100644 (file)
@@ -14,6 +14,7 @@
 #include "dos.h"
 
 #include "bios.h"
+#include "bop.h"
 #include "int32.h"
 #include "registers.h"
 
@@ -31,6 +32,10 @@ static BYTE DosAllocStrategy = DOS_ALLOC_BEST_FIT;
 static BOOLEAN DosUmbLinked = FALSE;
 static WORD DosErrorLevel = 0x0000;
 
+/* BOP Identifiers */
+#define BOP_DOS 0x50    // DOS System BOP (for NTIO.SYS and NTDOS.SYS)
+#define BOP_CMD 0x54    // DOS Command Interpreter BOP (for COMMAND.COM)
+
 /* PRIVATE FUNCTIONS **********************************************************/
 
 /* Taken from base/shell/cmd/console.c */
@@ -1372,6 +1377,11 @@ BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
         {
             WORD InfoWord = 0;
 
+            /*
+             * See Ralf Brown: http://www.ctyme.com/intr/rb-2820.htm
+             * for a list of possible flags.
+             */
+
             if (Handle == DosSystemFileTable[0])
             {
                 /* Console input */
@@ -1383,7 +1393,7 @@ BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
                 InfoWord |= 1 << 1;
             }
 
-            /* It is a character device */
+            /* It is a device */
             InfoWord |= 1 << 7;
 
             /* Return the device information word */
@@ -1402,6 +1412,94 @@ BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle)
     }
 }
 
+VOID WINAPI DosSystemBop(LPWORD Stack)
+{
+    /* Get the Function Number and skip it */
+    BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
+    setIP(getIP() + 1);
+
+    DPRINT1("Unknown DOS System BOP Function: 0x%02X\n", FuncNum);
+}
+
+VOID WINAPI DosCmdInterpreterBop(LPWORD Stack)
+{
+    /* Get the Function Number and skip it */
+    BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
+    setIP(getIP() + 1);
+
+    switch (FuncNum)
+    {
+        case 0x08: // Launch external command
+        {
+#define CMDLINE_LENGTH  1024
+
+            BOOL Result;
+            DWORD dwExitCode;
+
+            LPSTR Command = (LPSTR)SEG_OFF_TO_PTR(getDS(), getSI());
+            CHAR CommandLine[CMDLINE_LENGTH] = "";
+            STARTUPINFOA StartupInfo;
+            PROCESS_INFORMATION ProcessInformation;
+            DPRINT1("CMD Run Command '%s'\n", Command);
+
+            Command[strlen(Command)-1] = 0;
+            
+            strcpy(CommandLine, "cmd.exe /c ");
+            strcat(CommandLine, Command);
+
+            ZeroMemory(&StartupInfo, sizeof(StartupInfo));
+            ZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
+
+            StartupInfo.cb = sizeof(StartupInfo);
+
+            DosPrintCharacter('\n');
+
+            Result = CreateProcessA(NULL,
+                                    CommandLine,
+                                    NULL,
+                                    NULL,
+                                    TRUE,
+                                    0,
+                                    NULL,
+                                    NULL,
+                                    &StartupInfo,
+                                    &ProcessInformation);
+            if (Result)
+            {
+                DPRINT1("Command '%s' launched successfully\n");
+
+                /* Wait for process termination */
+                WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
+
+                /* Get the exit code */
+                GetExitCodeProcess(ProcessInformation.hProcess, &dwExitCode);
+
+                /* Close handles */
+                CloseHandle(ProcessInformation.hThread);
+                CloseHandle(ProcessInformation.hProcess);
+            }
+            else
+            {
+                DPRINT1("Failed when launched command '%s'\n");
+                dwExitCode = GetLastError();
+            }
+            
+            DosPrintCharacter('\n');
+
+            setAL((UCHAR)dwExitCode);
+
+            break;
+        }
+
+        default:
+        {
+            DPRINT1("Unknown DOS CMD Interpreter BOP Function: 0x%02X\n", FuncNum);
+            // setCF(1); // Disable, otherwise we enter an infinite loop
+            break;
+        }
+    }
+}
+
 VOID WINAPI DosInt20h(LPWORD Stack)
 {
     /* This is the exit interrupt */
@@ -1780,6 +1878,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             PDOS_PSP PspBlock = SEGMENT_TO_PSP(CurrentPsp);
 
             /*
+             * DOS 2+ - GET DOS VERSION
              * See Ralf Brown: http://www.ctyme.com/intr/rb-2711.htm
              * for more information.
              */
@@ -1808,12 +1907,49 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             setBL(0x00);
             setCX(0x0000);
 
-            /* Return DOS version: Minor:Major in AH:AL */
+            /*
+             * Return DOS version: Minor:Major in AH:AL
+             * The Windows NT DOS box returns version 5.00, subject to SETVER.
+             */
             setAX(PspBlock->DosVersion);
 
             break;
         }
 
+        /* Extended functionalities */
+        case 0x33:
+        {
+            if (getAL() == 0x06)
+            {
+                /*
+                 * DOS 5+ - GET TRUE VERSION NUMBER
+                 * This function always returns the true version number, unlike
+                 * AH=30h, whose return value may be changed with SETVER.
+                 * See Ralf Brown: http://www.ctyme.com/intr/rb-2730.htm
+                 * for more information.
+                 */
+
+                /*
+                 * Return the true DOS version: Minor:Major in BH:BL
+                 * The Windows NT DOS box returns BX=3205h (version 5.50).
+                 */
+                setBX(NTDOS_VERSION);
+
+                /* DOS revision 0 */
+                setDL(0x00);
+
+                /* Unpatched DOS */
+                setDH(0x00);
+            }
+            // else
+            // {
+                // /* Invalid subfunction */
+                // setAL(0xFF);
+            // }
+
+            break;
+        }
+
         /* Get Interrupt Vector */
         case 0x35:
         {
@@ -1873,10 +2009,11 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             }
             else
             {
+                /* Invalid subfunction */
                 setAL(0xFF);
             }
 
-             break;
+            break;
         }
 
         /* Create Directory */
@@ -2014,11 +2151,13 @@ VOID WINAPI DosInt21h(LPWORD Stack)
                     if (getCF()) break;
 
                     // FIXME: Security checks!
+                    DosPrintCharacter(Character);
                     Buffer[Stack[STACK_COUNTER]++] = Character;
 
                     if (Character == '\r')
                     {
                         /* Stop on first carriage return */
+                        DosPrintCharacter('\n');
                         break;
                     }
                 }
@@ -2438,9 +2577,28 @@ VOID WINAPI DosBreakInterrupt(LPWORD Stack)
 {
     UNREFERENCED_PARAMETER(Stack);
 
+    /* Stop the VDM */
     VdmRunning = FALSE;
 }
 
+VOID WINAPI DosFastConOut(LPWORD Stack)
+{
+    /*
+     * This is the DOS 2+ Fast Console Output Interrupt.
+     * See Ralf Brown: http://www.ctyme.com/intr/rb-4124.htm
+     * for more information.
+     */
+    UNREFERENCED_PARAMETER(Stack);
+
+    /*
+     * The default handler under DOS 2.x and 3.x simply calls INT 10/AH=0Eh.
+     * Do better and call directly BiosPrintCharacter: it's what INT 10/AH=0Eh
+     * does. Otherwise we would have to set BL to DOS_CHAR_ATTRIBUTE and
+     * BH to Bda->VideoPage.
+     */
+    BiosPrintCharacter(getAL(), DOS_CHAR_ATTRIBUTE, Bda->VideoPage);
+}
+
 VOID WINAPI DosInt2Fh(LPWORD Stack)
 {
     DPRINT1("DOS System Function INT 0x2F, AH = %xh, AL = %xh NOT IMPLEMENTED!\n",
@@ -2587,12 +2745,17 @@ BOOLEAN DosInitialize(VOID)
     DosSystemFileTable[1] = GetStdHandle(STD_OUTPUT_HANDLE);
     DosSystemFileTable[2] = GetStdHandle(STD_ERROR_HANDLE);
 
+    /* Register the DOS BOPs */
+    RegisterBop(BOP_DOS, DosSystemBop        );
+    RegisterBop(BOP_CMD, DosCmdInterpreterBop);
+
     /* Register the DOS 32-bit Interrupts */
     RegisterInt32(0x20, DosInt20h        );
     RegisterInt32(0x21, DosInt21h        );
 //  RegisterInt32(0x22, DosInt22h        ); // Termination
     RegisterInt32(0x23, DosBreakInterrupt); // Ctrl-C / Ctrl-Break
 //  RegisterInt32(0x24, DosInt24h        ); // Critical Error
+    RegisterInt32(0x29, DosFastConOut    ); // DOS 2+ Fast Console Output
     RegisterInt32(0x2F, DosInt2Fh        );
 
     return TRUE;