[NTVDM]
[reactos.git] / subsystems / ntvdm / ntvdm.c
index 47cebc4..a533fe5 100644 (file)
-/* $Id$
- *
- * COPYRIGHT:       See COPYING in the top level directory
- * PROJECT:         ReactOS kernel
- * FILE:            subsys/ntvdm/ntvdm->c
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            ntvdm.c
  * PURPOSE:         Virtual DOS Machine
- * PROGRAMMER:      Robert Dickenson (robd@mok.lvcm.com)
- * UPDATE HISTORY:
- *                  Created 23/10/2002
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  */
 
-/* INCLUDES *****************************************************************/
-#define WIN32_NO_STATUS
-#include <windows.h>
-#include <stdio.h>
-#include "resource.h"
+/* INCLUDES *******************************************************************/
 
 #define NDEBUG
-#include <debug.h>
 
-/* GLOBALS ******************************************************************/
+#include "ntvdm.h"
+#include "emulator.h"
 
+#include "clock.h"
+#include "hardware/ps2.h"
+#include "hardware/vga.h"
+#include "bios/bios.h"
+#include "dos/dem.h"
 
-/* FUNCTIONS *****************************************************************/
+#include "resource.h"
 
-void PrintString(char* fmt,...)
-{
-   char buffer[512];
-   va_list ap;
+/*
+ * Activate this line if you want to run NTVDM in standalone mode with:
+ * ntvdm.exe <program>
+ */
+#define STANDALONE
 
-   va_start(ap, fmt);
-   vsprintf(buffer, fmt, ap);
-   va_end(ap);
+/* VARIABLES ******************************************************************/
 
-   OutputDebugStringA(buffer);
-}
+static HANDLE ConsoleInput  = INVALID_HANDLE_VALUE;
+static HANDLE ConsoleOutput = INVALID_HANDLE_VALUE;
+static DWORD  OrgConsoleInputMode, OrgConsoleOutputMode;
+static CONSOLE_CURSOR_INFO         OrgConsoleCursorInfo;
+static CONSOLE_SCREEN_BUFFER_INFO  OrgConsoleBufferInfo;
+
+static HMENU hConsoleMenu  = NULL;
+static INT   VdmMenuPos    = -1;
+static BOOLEAN ShowPointer = FALSE;
 
 /*
-GetVersion
-GetVolumeInformationW
-GetWindowsDirectoryA
-GlobalMemoryStatus
-HeapAlloc
-HeapCreate
-HeapDestroy
-HeapFree
-HeapReAlloc
-
-GetNextVDMCommand
-ExitVDM
-RegisterConsoleVDM
-SetVDMCurrentDirectories
-VDMConsoleOperation
-WriteConsoleInputVDMW
-
-NtSetLdtEntries
-NtTerminateProcess
-
-NtMapViewOfSection
-NtUnmapViewOfSection
-
-NtVdmControl
+ * Those menu helpers were taken from the GUI frontend in winsrv.dll
  */
-typedef struct tag_VDM_CONFIG {
-    int dos_options;
-    int files;
-    int buffers;
-    WCHAR** device_list;
-//dos=high, umb
-//device=%SystemRoot%\system32\himem.sys
-//files=40
-} VDM_CONFIG, *PVDM_CONFIG;
-
-typedef struct tag_VDM_AUTOEXEC {
-    WCHAR** load_list;
-//lh %SystemRoot%\system32\mscdexnt.exe
-//lh %SystemRoot%\system32\redir
-//lh %SystemRoot%\system32\dosx
-} VDM_AUTOEXEC, *PVDM_AUTOEXEC;
-
-typedef struct tag_VDM_CONTROL_BLOCK {
-    HANDLE hHeap;
-    PVOID ImageMem;
-    VDM_CONFIG vdmConfig;
-    VDM_AUTOEXEC vdmAutoexec;
-    PROCESS_INFORMATION ProcessInformation;
-    CHAR CommandLine[MAX_PATH];
-    CHAR CurrentDirectory[MAX_PATH];
-
-} VDM_CONTROL_BLOCK, *PVDM_CONTROL_BLOCK;
-
-
-BOOL
-StartVDM(PVDM_CONTROL_BLOCK vdm)
+typedef struct _VDM_MENUITEM
 {
-   BOOL Result;
-   STARTUPINFOA StartupInfo;
-
-   StartupInfo.cb = sizeof(StartupInfo);
-   StartupInfo.lpReserved = NULL;
-   StartupInfo.lpDesktop = NULL;
-   StartupInfo.lpTitle = NULL;
-   StartupInfo.dwFlags = 0;
-   StartupInfo.cbReserved2 = 0;
-   StartupInfo.lpReserved2 = 0;
-
-   Result = CreateProcessA(vdm->CommandLine,
-                          NULL,
-                          NULL,
-                          NULL,
-                          FALSE,
-                          DETACHED_PROCESS,
-                          NULL,
-                          NULL,
-                          &StartupInfo,
-                          &vdm->ProcessInformation);
-    if (!Result) {
-        PrintString("VDM: Failed to execute target process\n");
-        return FALSE;
-    }
-    WaitForSingleObject(vdm->ProcessInformation.hProcess, INFINITE);
-    CloseHandle(vdm->ProcessInformation.hProcess);
-    CloseHandle(vdm->ProcessInformation.hThread);
-    return TRUE;
-}
+    UINT uID;
+    const struct _VDM_MENUITEM *SubMenu;
+    WORD wCmdID;
+} VDM_MENUITEM, *PVDM_MENUITEM;
 
-BOOL
-ShutdownVDM(PVDM_CONTROL_BLOCK vdm)
+static const VDM_MENUITEM VdmMenuItems[] =
 {
-    BOOL result = TRUE;
+    { IDS_VDM_QUIT, NULL, ID_VDM_QUIT },
 
-    return result;
-}
+    { 0, NULL, 0 }      /* End of list */
+};
+
+static const VDM_MENUITEM VdmMainMenuItems[] =
+{
+    { -1, NULL, 0 },    /* Separator */
+    { IDS_HIDE_MOUSE,   NULL, ID_SHOWHIDE_MOUSE },  /* Hide mouse; can be renamed to Show mouse */
+    { IDS_VDM_MENU  ,   VdmMenuItems,         0 },  /* ReactOS VDM Menu */
 
-BOOL ReadConfigForVDM(PVDM_CONTROL_BLOCK vdm)
+    { 0, NULL, 0 }      /* End of list */
+};
+
+static VOID
+AppendMenuItems(HMENU hMenu,
+                const VDM_MENUITEM *Items)
 {
-    BOOL result = TRUE;
-    DWORD dwError;
-    HANDLE hFile;
-
-    hFile = CreateFileW(L"\\system32\\config.nt",
-                        GENERIC_READ,
-                        FILE_SHARE_READ,
-                        NULL,
-                        OPEN_ALWAYS /*OPEN_EXISTING*/,
-                        FILE_ATTRIBUTE_NORMAL,
-                        0);
-    dwError = GetLastError();
-    if (hFile == INVALID_HANDLE_VALUE) {
-        // error with file path or system problem?
-    } else {
-        if (dwError == 0L) {
-            // we just created a new file, perhaps we should set/write some defaults?
+    UINT i = 0;
+    WCHAR szMenuString[255];
+    HMENU hSubMenu;
+
+    do
+    {
+        if (Items[i].uID != (UINT)-1)
+        {
+            if (LoadStringW(GetModuleHandle(NULL),
+                            Items[i].uID,
+                            szMenuString,
+                            sizeof(szMenuString) / sizeof(szMenuString[0])) > 0)
+            {
+                if (Items[i].SubMenu != NULL)
+                {
+                    hSubMenu = CreatePopupMenu();
+                    if (hSubMenu != NULL)
+                    {
+                        AppendMenuItems(hSubMenu, Items[i].SubMenu);
+
+                        if (!AppendMenuW(hMenu,
+                                         MF_STRING | MF_POPUP,
+                                         (UINT_PTR)hSubMenu,
+                                         szMenuString))
+                        {
+                            DestroyMenu(hSubMenu);
+                        }
+                    }
+                }
+                else
+                {
+                    AppendMenuW(hMenu,
+                                MF_STRING,
+                                Items[i].wCmdID,
+                                szMenuString);
+                }
+            }
         }
-        if (dwError == ERROR_ALREADY_EXISTS) {
-            // read the line entries and cache in some struct...
+        else
+        {
+            AppendMenuW(hMenu,
+                        MF_SEPARATOR,
+                        0,
+                        NULL);
         }
-        CloseHandle(hFile);
+        i++;
+    } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0));
+}
+
+static VOID
+CreateVdmMenu(HANDLE ConOutHandle)
+{
+    hConsoleMenu = ConsoleMenuControl(ConsoleOutput,
+                                      ID_SHOWHIDE_MOUSE,
+                                      ID_VDM_QUIT);
+    if (hConsoleMenu == NULL) return;
+
+    VdmMenuPos = GetMenuItemCount(hConsoleMenu);
+    AppendMenuItems(hConsoleMenu, VdmMainMenuItems);
+    DrawMenuBar(GetConsoleWindow());
+}
+
+static VOID
+DestroyVdmMenu(VOID)
+{
+    UINT i = 0;
+    const VDM_MENUITEM *Items = VdmMainMenuItems;
+
+    do
+    {
+        DeleteMenu(hConsoleMenu, VdmMenuPos, MF_BYPOSITION);
+        i++;
+    } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0));
+
+    DrawMenuBar(GetConsoleWindow());
+}
+
+static VOID ShowHideMousePointer(HANDLE ConOutHandle, BOOLEAN ShowPtr)
+{
+    WCHAR szMenuString[255] = L"";
+
+    if (ShowPtr)
+    {
+        /* Be sure the cursor will be shown */
+        while (ShowConsoleCursor(ConOutHandle, TRUE) < 0) ;
+    }
+    else
+    {
+        /* Be sure the cursor will be hidden */
+        while (ShowConsoleCursor(ConOutHandle, FALSE) >= 0) ;
+    }
+
+    if (LoadStringW(GetModuleHandle(NULL),
+                    (!ShowPtr ? IDS_SHOW_MOUSE : IDS_HIDE_MOUSE),
+                    szMenuString,
+                    sizeof(szMenuString) / sizeof(szMenuString[0])) > 0)
+    {
+        ModifyMenu(hConsoleMenu, ID_SHOWHIDE_MOUSE,
+                   MF_BYCOMMAND, ID_SHOWHIDE_MOUSE, szMenuString);
     }
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
 
-    hFile = CreateFileW(L"\\system32\\autoexec.nt",
-                        GENERIC_READ,
-                        FILE_SHARE_READ,
-                        NULL,
-                        OPEN_ALWAYS,
-                        FILE_ATTRIBUTE_NORMAL,
-                        0);
-    dwError = GetLastError();
-    if (hFile == INVALID_HANDLE_VALUE) {
-        // error with file path or system problem?
-    } else {
-        if (dwError == 0L) {
-            // we just created a new file, perhaps we should set/write some defaults?
+VOID DisplayMessage(LPCWSTR Format, ...)
+{
+    WCHAR Buffer[256];
+    va_list Parameters;
+
+    va_start(Parameters, Format);
+    _vsnwprintf(Buffer, 256, Format, Parameters);
+    DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer);
+    MessageBoxW(NULL, Buffer, L"NTVDM Subsystem", MB_OK);
+    va_end(Parameters);
+}
+
+BOOL WINAPI ConsoleCtrlHandler(DWORD ControlType)
+{
+    switch (ControlType)
+    {
+        case CTRL_C_EVENT:
+        case CTRL_BREAK_EVENT:
+        {
+            /* Call INT 23h */
+            EmulatorInterrupt(0x23);
+            break;
         }
-        if (dwError == ERROR_ALREADY_EXISTS) {
-            // read the line entries and cache in some struct...
+        default:
+        {
+            /* Stop the VDM if the user logs out or closes the console */
+            EmulatorTerminate();
         }
-        CloseHandle(hFile);
     }
+    return TRUE;
+}
 
-       return result;
+VOID ConsoleInitUI(VOID)
+{
+    CreateVdmMenu(ConsoleOutput);
 }
 
-BOOL
-LoadConfigDriversForVDM(PVDM_CONFIG vdmConfig)
+VOID ConsoleCleanupUI(VOID)
 {
-    BOOL result = TRUE;
+    /* Display again properly the mouse pointer */
+    if (ShowPointer) ShowHideMousePointer(ConsoleOutput, ShowPointer);
 
-       return result;
+    DestroyVdmMenu();
 }
 
-BOOL
-SetConfigOptionsForVDM(PVDM_AUTOEXEC vdmAutoexec)
+DWORD WINAPI PumpConsoleInput(LPVOID Parameter)
 {
-    BOOL result = TRUE;
+    HANDLE ConsoleInput = (HANDLE)Parameter;
+    INPUT_RECORD InputRecord;
+    DWORD Count;
+
+    while (VdmRunning)
+    {
+        /* Wait for an input record */
+        if (!ReadConsoleInput(ConsoleInput, &InputRecord, 1, &Count))
+        {
+            DWORD LastError = GetLastError();
+            DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput, Count, LastError);
+            return LastError;
+        }
 
-       return result;
+        ASSERT(Count != 0);
+
+        /* Check the event type */
+        switch (InputRecord.EventType)
+        {
+            case KEY_EVENT:
+            case MOUSE_EVENT:
+                /* Send it to the PS/2 controller */
+                PS2Dispatch(&InputRecord);
+                break;
+
+            case MENU_EVENT:
+            {
+                switch (InputRecord.Event.MenuEvent.dwCommandId)
+                {
+                    case ID_SHOWHIDE_MOUSE:
+                        ShowHideMousePointer(ConsoleOutput, ShowPointer);
+                        ShowPointer = !ShowPointer;
+                        break;
+
+                    case ID_VDM_QUIT:
+                        /* Stop the VDM */
+                        EmulatorTerminate();
+                        break;
+
+                    default:
+                        break;
+                }
+
+                break;
+            }
+
+            default:
+                break;
+        }
+    }
+
+    return 0;
 }
 
-BOOL
-CreateVDM(PVDM_CONTROL_BLOCK vdm)
+BOOL ConsoleInit(VOID)
 {
-//    BOOL result = TRUE;
-    SYSTEM_INFO inf;
-    MEMORYSTATUS stat;
-
-
-    GlobalMemoryStatus(&stat);
-    if (stat.dwLength != sizeof(MEMORYSTATUS)) {
-        printf("WARNING: GlobalMemoryStatus() returned unknown structure version, size %ld, expected %d.\n", stat.dwLength, sizeof(stat));
-    } else {
-        printf("Memory Load: %ld percent in use.\n", stat.dwMemoryLoad);
-        printf("\t%ld total bytes physical memory.\n", stat.dwTotalPhys);
-        printf("\t%ld available physical memory.\n", stat.dwAvailPhys);
-        printf("\t%ld total bytes paging file.\n", stat.dwTotalPageFile);
-        printf("\t%ld available paging file.\n", stat.dwAvailPageFile);
-        printf("\t%lx total bytes virtual memory.\n", stat.dwTotalVirtual);
-        printf("\t%lx available bytes virtual memory.\n", stat.dwAvailVirtual);
-
-#define OUT_OF_HEADROOM 90
-        if (stat.dwMemoryLoad > OUT_OF_HEADROOM) {
-            DPRINT("VDM: system resources deemed to low to start VDM.\n");
-            //SetLastError();
-            return FALSE;
-        }
+    /* Set the handler routine */
+    SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
+
+    /* Get the input handle to the real console, and check for success */
+    ConsoleInput = CreateFileW(L"CONIN$",
+                               GENERIC_READ | GENERIC_WRITE,
+                               FILE_SHARE_READ | FILE_SHARE_WRITE,
+                               NULL,
+                               OPEN_EXISTING,
+                               0,
+                               NULL);
+    if (ConsoleInput == INVALID_HANDLE_VALUE)
+    {
+        wprintf(L"FATAL: Cannot retrieve a handle to the console input\n");
+        return FALSE;
+    }
 
+    /* Get the output handle to the real console, and check for success */
+    ConsoleOutput = CreateFileW(L"CONOUT$",
+                                GENERIC_READ | GENERIC_WRITE,
+                                FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                NULL,
+                                OPEN_EXISTING,
+                                0,
+                                NULL);
+    if (ConsoleOutput == INVALID_HANDLE_VALUE)
+    {
+        CloseHandle(ConsoleInput);
+        wprintf(L"FATAL: Cannot retrieve a handle to the console output\n");
+        return FALSE;
     }
 
-    GetSystemInfo(&inf);
-    vdm->hHeap = HeapCreate(0, inf.dwAllocationGranularity, 0);
-    if (vdm->hHeap == NULL) {
-        DPRINT("VDM: failed to create heap.\n");
+    /* Save the original input and output console modes */
+    if (!GetConsoleMode(ConsoleInput , &OrgConsoleInputMode ) ||
+        !GetConsoleMode(ConsoleOutput, &OrgConsoleOutputMode))
+    {
+        CloseHandle(ConsoleOutput);
+        CloseHandle(ConsoleInput);
+        wprintf(L"FATAL: Cannot save console in/out modes\n");
         return FALSE;
     }
 
-#define DEFAULT_VDM_IMAGE_SIZE 2000000
-    vdm->ImageMem = HeapAlloc(vdm->hHeap, 0, DEFAULT_VDM_IMAGE_SIZE);
-    if (vdm->ImageMem == NULL) {
-        DPRINT("VDM: failed to allocate image memory from heap %x.\n", vdm->hHeap);
-        HeapDestroy(vdm->hHeap);
-        vdm->hHeap = NULL;
+    /* Save the original cursor and console screen buffer information */
+    if (!GetConsoleCursorInfo(ConsoleOutput, &OrgConsoleCursorInfo) ||
+        !GetConsoleScreenBufferInfo(ConsoleOutput, &OrgConsoleBufferInfo))
+    {
+        CloseHandle(ConsoleOutput);
+        CloseHandle(ConsoleInput);
+        wprintf(L"FATAL: Cannot save console cursor/screen-buffer info\n");
         return FALSE;
     }
+
+    /* Initialize the UI */
+    ConsoleInitUI();
+
     return TRUE;
 }
 
-BOOL
-DestroyVDM(PVDM_CONTROL_BLOCK vdm)
+VOID ConsoleCleanup(VOID)
 {
-    BOOL result = TRUE;
-
-    if (vdm->ImageMem != NULL) {
-        if (HeapFree(vdm->hHeap, 0, vdm->ImageMem) != FALSE) {
-            DPRINT("VDM: failed to free memory from heap %x.\n", vdm->hHeap);
-            result = FALSE;
-        }
-        vdm->ImageMem = NULL;
-    }
-    if (vdm->hHeap != NULL) {
-        if (!HeapDestroy(vdm->hHeap)) {
-            DPRINT("VDM: failed to destroy heap %x.\n", vdm->hHeap);
-            result = FALSE;
-        }
-        vdm->hHeap = NULL;
-    }
-    return result;
+    SMALL_RECT ConRect;
+
+    /* Restore the old screen buffer */
+    SetConsoleActiveScreenBuffer(ConsoleOutput);
+
+    /* Restore the original console size */
+    ConRect.Left   = 0;
+    ConRect.Top    = 0;
+    ConRect.Right  = ConRect.Left + OrgConsoleBufferInfo.srWindow.Right  - OrgConsoleBufferInfo.srWindow.Left;
+    ConRect.Bottom = ConRect.Top  + OrgConsoleBufferInfo.srWindow.Bottom - OrgConsoleBufferInfo.srWindow.Top ;
+    /*
+     * See the following trick explanation in vga.c:VgaEnterTextMode() .
+     */
+    SetConsoleScreenBufferSize(ConsoleOutput, OrgConsoleBufferInfo.dwSize);
+    SetConsoleWindowInfo(ConsoleOutput, TRUE, &ConRect);
+    SetConsoleScreenBufferSize(ConsoleOutput, OrgConsoleBufferInfo.dwSize);
+
+    /* Restore the original cursor shape */
+    SetConsoleCursorInfo(ConsoleOutput, &OrgConsoleCursorInfo);
+
+    /* Restore the original input and output console modes */
+    SetConsoleMode(ConsoleOutput, OrgConsoleOutputMode);
+    SetConsoleMode(ConsoleInput , OrgConsoleInputMode );
+
+    /* Cleanup the UI */
+    ConsoleCleanupUI();
+
+    /* Close the console handles */
+    if (ConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleOutput);
+    if (ConsoleInput  != INVALID_HANDLE_VALUE) CloseHandle(ConsoleInput);
 }
 
-int WINAPI
-WinMain(HINSTANCE hInstance,  HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
+INT wmain(INT argc, WCHAR *argv[])
 {
-    VDM_CONTROL_BLOCK VdmCB;
-    DWORD Result;
-    ULONG i;
-    BOOL vdmStarted = FALSE;
+#ifndef STANDALONE
+
+    VDM_COMMAND_INFO CommandInfo;
+    CHAR CmdLine[MAX_PATH];
+    CHAR AppName[MAX_PATH];
+    CHAR PifFile[MAX_PATH];
+    CHAR Desktop[MAX_PATH];
+    CHAR Title[MAX_PATH];
 
-    WCHAR WelcomeMsg[RC_STRING_MAX_SIZE];
-    WCHAR PromptMsg[RC_STRING_MAX_SIZE];
-    CHAR InputBuffer[255];
+#else
 
-    LoadStringW( GetModuleHandle(NULL), STRING_WelcomeMsg, WelcomeMsg,sizeof(WelcomeMsg) / sizeof(WelcomeMsg[0]));
-    LoadStringW( GetModuleHandle(NULL), STRING_PromptMsg, PromptMsg ,sizeof(PromptMsg) / sizeof(PromptMsg[0]));
+    CHAR CommandLine[DOS_CMDLINE_LENGTH];
 
-    AllocConsole();
-    SetConsoleTitleW(L"ntvdm");
+    if (argc == 2 && argv[1] != NULL)
+    {
+        WideCharToMultiByte(CP_ACP, 0, argv[1], -1, CommandLine, sizeof(CommandLine), NULL, NULL);
+    }
+    else
+    {
+        wprintf(L"\nReactOS Virtual DOS Machine\n\n"
+                L"Usage: NTVDM <executable>\n");
+        return 0;
+    }
+
+    DPRINT1("\n\n\nNTVDM - Starting '%s'...\n\n\n", CommandLine);
 
-    WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
-                 WelcomeMsg, lstrlenW(WelcomeMsg),  // wcslen(WelcomeMsg),
-                 &Result, NULL);
+#endif
 
-    if (!CreateVDM(&VdmCB)) {
-        DPRINT("VDM: failed to create VDM.\n");
-        //SetLastError();
-        return 2;
+    /* Initialize the console */
+    if (!ConsoleInit())
+    {
+        wprintf(L"FATAL: A problem occurred when trying to initialize the console\n");
+        goto Cleanup;
     }
 
-       ReadConfigForVDM(&VdmCB);
+    /* Initialize the emulator */
+    if (!EmulatorInitialize(ConsoleInput, ConsoleOutput))
+    {
+        wprintf(L"FATAL: Failed to initialize the emulator\n");
+        goto Cleanup;
+    }
 
-    if (!LoadConfigDriversForVDM(&(VdmCB.vdmConfig))) {
-        DPRINT("VDM: failed to load configuration drivers.\n");
-        //SetLastError();
-        return 2;
+    /* Initialize the system BIOS */
+    if (!BiosInitialize(NULL))
+    {
+        wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n");
+        goto Cleanup;
     }
-    if (!SetConfigOptionsForVDM(&(VdmCB.vdmAutoexec))) {
-        DPRINT("VDM: failed to set configuration options.\n");
-        //SetLastError();
-        return 3;
+
+    /* Initialize the VDM DOS kernel */
+    if (!DosInitialize(NULL))
+    {
+        wprintf(L"FATAL: Failed to initialize the VDM DOS kernel.\n");
+        goto Cleanup;
     }
 
-    GetSystemDirectoryA(VdmCB.CommandLine, MAX_PATH);
-    strcat(VdmCB.CommandLine, "\\hello.exe");
-    GetWindowsDirectoryA(VdmCB.CurrentDirectory, MAX_PATH);
-
-    for (;;) {
-        WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
-                     PromptMsg, lstrlenW(PromptMsg),  // wcslen(PromptMsg),
-                     &Result, NULL);
-        i = 0;
-        do {
-            ReadConsoleA(GetStdHandle(STD_INPUT_HANDLE),
-                        &InputBuffer[i], 1,
-                        &Result, NULL);
-            if (++i >= (sizeof(InputBuffer) - 1)) {
-                break;
-            }
-        } while (InputBuffer[i - 1] != '\n');
-        InputBuffer[i - 1] = '\0';
-
-        if (InputBuffer[0] == 'r' || InputBuffer[0] == 'R') {
-            if (!vdmStarted) {
-                if (StartVDM(&VdmCB)) {
-                    vdmStarted = TRUE;
-                } else {
-                    DPRINT("VDM: failed to start.\n");
-                }
-            } else {
-                DPRINT("VDM: already started.\n");
-            }
-        }
-        if (InputBuffer[0] == 's' || InputBuffer[0] == 'S') {
-            if (vdmStarted) {
-                if (ShutdownVDM(&VdmCB)) {
-                    vdmStarted = FALSE;
-                } else {
-                    DPRINT("VDM: failed to shutdown.\n");
-                }
-            } else {
-                DPRINT("VDM: not started.\n");
-            }
+#ifndef STANDALONE
+
+    while (TRUE)
+    {
+        /* Clear the structure */
+        ZeroMemory(&CommandInfo, sizeof(CommandInfo));
+
+        /* Initialize the structure members */
+        CommandInfo.VDMState = VDM_NOT_LOADED;
+        CommandInfo.CmdLine = CmdLine;
+        CommandInfo.CmdLen = sizeof(CmdLine);
+        CommandInfo.AppName = AppName;
+        CommandInfo.AppLen = sizeof(AppName);
+        CommandInfo.PifFile = PifFile;
+        CommandInfo.PifLen = sizeof(PifFile);
+        CommandInfo.Desktop = Desktop;
+        CommandInfo.DesktopLen = sizeof(Desktop);
+        CommandInfo.Title = Title;
+        CommandInfo.TitleLen = sizeof(Title);
+
+        if (!GetNextVDMCommand(&CommandInfo)) break;
+
+        /* Start the process from the command line */
+        if (!DosCreateProcess(AppName, 0))
+        {
+            DisplayMessage(L"Could not start program: %S", AppName);
+            goto Cleanup;
         }
-        if (InputBuffer[0] == 'q' || InputBuffer[0] == 'Q') {
-            break;
-        }
-    }
 
-    if (!ShutdownVDM(&VdmCB)) {
-        DPRINT("VDM: failed to cleanly shutdown VDM.\n");
-        //SetLastError();
-        return 5;
+        /* Start simulation */
+        EmulatorSimulate();
+
+        /* Perform another screen refresh */
+        VgaRefreshDisplay();
     }
 
-    if (!DestroyVDM(&VdmCB)) {
-        DPRINT("VDM: failed to cleanly destroy VDM.\n");
-        //SetLastError();
-        return 6;
+#else
+
+    /* Start the process from the command line */
+    if (!DosCreateProcess(CommandLine, 0))
+    {
+        DisplayMessage(L"Could not start program: %S", CommandLine);
+        goto Cleanup;
     }
 
-    ExitProcess(0);
+    /* Start simulation */
+    EmulatorSimulate();
+
+#endif
+
+    /* Perform another screen refresh */
+    VgaRefreshDisplay();
+
+Cleanup:
+    BiosCleanup();
+    EmulatorCleanup();
+    ConsoleCleanup();
+
+    /* Quit the VDM */
+    DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");
+    ExitVDM(FALSE, 0);
+
     return 0;
 }
+
+/* EOF */