[NTVDM]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / ntvdm.c
index 7a00543..7094807 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * COPYRIGHT:       GPL - See COPYING in the top level directory
  * PROJECT:         ReactOS Virtual DOS Machine
- * FILE:            ntvdm.c
+ * FILE:            subsystems/mvdm/ntvdm/ntvdm.c
  * PURPOSE:         Virtual DOS Machine
  * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
  */
@@ -16,6 +16,8 @@
 #include "bios/bios.h"
 #include "cpu/cpu.h"
 
+#include "dos/dem.h"
+
 #include "resource.h"
 
 /* VARIABLES ******************************************************************/
@@ -24,13 +26,13 @@ static HANDLE ConsoleInput  = INVALID_HANDLE_VALUE;
 static HANDLE ConsoleOutput = INVALID_HANDLE_VALUE;
 static DWORD  OrgConsoleInputMode, OrgConsoleOutputMode;
 
-HANDLE VdmTaskEvent = NULL;
+NTVDM_SETTINGS GlobalSettings;
 
 // Command line of NTVDM
 INT     NtVdmArgc;
 WCHAR** NtVdmArgv;
 
-
+HWND hConsoleWnd = NULL;
 static HMENU hConsoleMenu  = NULL;
 static INT   VdmMenuPos    = -1;
 static BOOLEAN ShowPointer = FALSE;
@@ -42,13 +44,17 @@ typedef struct _VDM_MENUITEM
 {
     UINT uID;
     const struct _VDM_MENUITEM *SubMenu;
-    WORD wCmdID;
+    UINT_PTR uCmdID;
 } VDM_MENUITEM, *PVDM_MENUITEM;
 
 static const VDM_MENUITEM VdmMenuItems[] =
 {
     { IDS_VDM_DUMPMEM_TXT, NULL, ID_VDM_DUMPMEM_TXT },
     { IDS_VDM_DUMPMEM_BIN, NULL, ID_VDM_DUMPMEM_BIN },
+    { -1, NULL, 0 },    /* Separator */
+    // { IDS_VDM_MOUNT_FLOPPY, NULL, ID_VDM_DRIVES },
+    // { IDS_VDM_EJECT_FLOPPY, NULL, ID_VDM_DRIVES },
+    { -1, NULL, 0 },    /* Separator */
     { IDS_VDM_QUIT       , NULL, ID_VDM_QUIT        },
 
     { 0, NULL, 0 }      /* End of list */
@@ -57,7 +63,7 @@ static const VDM_MENUITEM VdmMenuItems[] =
 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_HIDE_MOUSE,   NULL, ID_SHOWHIDE_MOUSE },  /* "Hide mouse"; can be renamed to "Show mouse" */
     { IDS_VDM_MENU  ,   VdmMenuItems,         0 },  /* ReactOS VDM Menu */
 
     { 0, NULL, 0 }      /* End of list */
@@ -68,7 +74,7 @@ AppendMenuItems(HMENU hMenu,
                 const VDM_MENUITEM *Items)
 {
     UINT i = 0;
-    WCHAR szMenuString[255];
+    WCHAR szMenuString[256];
     HMENU hSubMenu;
 
     do
@@ -100,7 +106,7 @@ AppendMenuItems(HMENU hMenu,
                 {
                     AppendMenuW(hMenu,
                                 MF_STRING,
-                                Items[i].wCmdID,
+                                Items[i].uCmdID,
                                 szMenuString);
                 }
             }
@@ -113,7 +119,7 @@ AppendMenuItems(HMENU hMenu,
                         NULL);
         }
         i++;
-    } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0));
+    } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].uCmdID == 0));
 }
 
 BOOL
@@ -127,7 +133,7 @@ VdmMenuExists(HMENU hConsoleMenu)
     {
         if (GetMenuItemID(hConsoleMenu, i) == ID_SHOWHIDE_MOUSE)
         {
-            /* set VdmMenuPos to the position of the existing menu */
+            /* Set VdmMenuPos to the position of the existing menu */
             VdmMenuPos = i - 1;
             return TRUE;
         }
@@ -138,9 +144,15 @@ VdmMenuExists(HMENU hConsoleMenu)
 /*static*/ VOID
 CreateVdmMenu(HANDLE ConOutHandle)
 {
+    HMENU hVdmSubMenu;
+    UINT_PTR ItemID = ID_VDM_DRIVES;
+    UINT Pos;
+    WCHAR szNoMedia[100];
+    WCHAR szMenuString1[256], szMenuString2[256];
+
     hConsoleMenu = ConsoleMenuControl(ConOutHandle,
                                       ID_SHOWHIDE_MOUSE,
-                                      ID_VDM_QUIT);
+                                      ID_VDM_DRIVES + 4);
     if (hConsoleMenu == NULL) return;
 
     /* Get the position where we are going to insert our menu items */
@@ -149,8 +161,52 @@ CreateVdmMenu(HANDLE ConOutHandle)
     /* Really add the menu if it doesn't already exist (in case eg. NTVDM crashed) */
     if (!VdmMenuExists(hConsoleMenu))
     {
+        /* Add all the menu entries */
         AppendMenuItems(hConsoleMenu, VdmMainMenuItems);
-        DrawMenuBar(GetConsoleWindow());
+
+        /* Add the removable drives menu entries */
+        hVdmSubMenu = GetSubMenu(hConsoleMenu, VdmMenuPos + 2); // VdmMenuItems
+        Pos = 3; // After the 2 items and the separator in VdmMenuItems
+
+        LoadStringW(GetModuleHandle(NULL),
+                    IDS_NO_MEDIA,
+                    szNoMedia,
+                    ARRAYSIZE(szNoMedia));
+
+        LoadStringW(GetModuleHandle(NULL),
+                    IDS_VDM_MOUNT_FLOPPY,
+                    szMenuString1,
+                    ARRAYSIZE(szMenuString1));
+
+        /* Drive 0 -- Mount */
+        _snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, 0, szNoMedia);
+        szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
+        InsertMenuW(hVdmSubMenu, Pos++, MF_STRING | MF_BYPOSITION, ItemID + 0, szMenuString2);
+
+        /* Drive 1 -- Mount */
+        _snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, 1, szNoMedia);
+        szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
+        InsertMenuW(hVdmSubMenu, Pos++, MF_STRING | MF_BYPOSITION, ItemID + 2, szMenuString2);
+
+        LoadStringW(GetModuleHandle(NULL),
+                    IDS_VDM_EJECT_FLOPPY,
+                    szMenuString1,
+                    ARRAYSIZE(szMenuString1));
+
+        /* Drive 0 -- Eject */
+        _snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, 0);
+        szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
+        InsertMenuW(hVdmSubMenu, Pos++, MF_STRING | MF_BYPOSITION, ItemID + 1, szMenuString2);
+
+        /* Drive 1 -- Eject */
+        _snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, 1);
+        szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
+        InsertMenuW(hVdmSubMenu, Pos++, MF_STRING | MF_BYPOSITION, ItemID + 3, szMenuString2);
+
+        // TODO: Refresh the menu state
+
+        /* Refresh the menu */
+        DrawMenuBar(hConsoleWnd);
     }
 }
 
@@ -164,14 +220,14 @@ DestroyVdmMenu(VOID)
     {
         DeleteMenu(hConsoleMenu, VdmMenuPos, MF_BYPOSITION);
         i++;
-    } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0));
+    } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].uCmdID == 0));
 
-    DrawMenuBar(GetConsoleWindow());
+    DrawMenuBar(hConsoleWnd);
 }
 
 static VOID ShowHideMousePointer(HANDLE ConOutHandle, BOOLEAN ShowPtr)
 {
-    WCHAR szMenuString[255] = L"";
+    WCHAR szMenuString[256];
 
     if (ShowPtr)
     {
@@ -222,10 +278,282 @@ static VOID EnableExtraHardware(HANDLE ConsoleInput)
     }
 }
 
+
+static NTSTATUS
+NTAPI
+NtVdmConfigureBios(IN PWSTR ValueName,
+                   IN ULONG ValueType,
+                   IN PVOID ValueData,
+                   IN ULONG ValueLength,
+                   IN PVOID Context,
+                   IN PVOID EntryContext)
+{
+    PNTVDM_SETTINGS Settings = (PNTVDM_SETTINGS)Context;
+    UNICODE_STRING ValueString;
+
+    /* Check for the type of the value */
+    if (ValueType != REG_SZ)
+    {
+        RtlInitEmptyAnsiString(&Settings->BiosFileName, NULL, 0);
+        return STATUS_SUCCESS;
+    }
+
+    /* Convert the UNICODE string to ANSI and store it */
+    RtlInitEmptyUnicodeString(&ValueString, (PWCHAR)ValueData, ValueLength);
+    ValueString.Length = ValueString.MaximumLength;
+    RtlUnicodeStringToAnsiString(&Settings->BiosFileName, &ValueString, TRUE);
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+NTAPI
+NtVdmConfigureRom(IN PWSTR ValueName,
+                  IN ULONG ValueType,
+                  IN PVOID ValueData,
+                  IN ULONG ValueLength,
+                  IN PVOID Context,
+                  IN PVOID EntryContext)
+{
+    PNTVDM_SETTINGS Settings = (PNTVDM_SETTINGS)Context;
+    UNICODE_STRING ValueString;
+
+    /* Check for the type of the value */
+    if (ValueType != REG_MULTI_SZ)
+    {
+        RtlInitEmptyAnsiString(&Settings->RomFiles, NULL, 0);
+        return STATUS_SUCCESS;
+    }
+
+    /* Convert the UNICODE string to ANSI and store it */
+    RtlInitEmptyUnicodeString(&ValueString, (PWCHAR)ValueData, ValueLength);
+    ValueString.Length = ValueString.MaximumLength;
+    RtlUnicodeStringToAnsiString(&Settings->RomFiles, &ValueString, TRUE);
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+NTAPI
+NtVdmConfigureFloppy(IN PWSTR ValueName,
+                     IN ULONG ValueType,
+                     IN PVOID ValueData,
+                     IN ULONG ValueLength,
+                     IN PVOID Context,
+                     IN PVOID EntryContext)
+{
+    PNTVDM_SETTINGS Settings = (PNTVDM_SETTINGS)Context;
+    UNICODE_STRING ValueString;
+    ULONG DiskNumber = (ULONG)EntryContext;
+
+    ASSERT(DiskNumber < ARRAYSIZE(Settings->FloppyDisks));
+
+    /* Check whether the Hard Disk entry was not already configured */
+    if (Settings->FloppyDisks[DiskNumber].Buffer != NULL)
+    {
+        DPRINT1("Floppy Disk %d -- '%Z' already configured\n", DiskNumber, &Settings->FloppyDisks[DiskNumber]);
+        return STATUS_SUCCESS;
+    }
+
+    /* Check for the type of the value */
+    if (ValueType != REG_SZ)
+    {
+        RtlInitEmptyAnsiString(&Settings->FloppyDisks[DiskNumber], NULL, 0);
+        return STATUS_SUCCESS;
+    }
+
+    /* Convert the UNICODE string to ANSI and store it */
+    RtlInitEmptyUnicodeString(&ValueString, (PWCHAR)ValueData, ValueLength);
+    ValueString.Length = ValueString.MaximumLength;
+    RtlUnicodeStringToAnsiString(&Settings->FloppyDisks[DiskNumber], &ValueString, TRUE);
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+NTAPI
+NtVdmConfigureHDD(IN PWSTR ValueName,
+                  IN ULONG ValueType,
+                  IN PVOID ValueData,
+                  IN ULONG ValueLength,
+                  IN PVOID Context,
+                  IN PVOID EntryContext)
+{
+    PNTVDM_SETTINGS Settings = (PNTVDM_SETTINGS)Context;
+    UNICODE_STRING ValueString;
+    ULONG DiskNumber = (ULONG)EntryContext;
+
+    ASSERT(DiskNumber < ARRAYSIZE(Settings->HardDisks));
+
+    /* Check whether the Hard Disk entry was not already configured */
+    if (Settings->HardDisks[DiskNumber].Buffer != NULL)
+    {
+        DPRINT1("Hard Disk %d -- '%Z' already configured\n", DiskNumber, &Settings->HardDisks[DiskNumber]);
+        return STATUS_SUCCESS;
+    }
+
+    /* Check for the type of the value */
+    if (ValueType != REG_SZ)
+    {
+        RtlInitEmptyAnsiString(&Settings->HardDisks[DiskNumber], NULL, 0);
+        return STATUS_SUCCESS;
+    }
+
+    /* Convert the UNICODE string to ANSI and store it */
+    RtlInitEmptyUnicodeString(&ValueString, (PWCHAR)ValueData, ValueLength);
+    ValueString.Length = ValueString.MaximumLength;
+    RtlUnicodeStringToAnsiString(&Settings->HardDisks[DiskNumber], &ValueString, TRUE);
+
+    return STATUS_SUCCESS;
+}
+
+static RTL_QUERY_REGISTRY_TABLE
+NtVdmConfigurationTable[] =
+{
+    {
+        NtVdmConfigureBios,
+        0,
+        L"BiosFile",
+        NULL,
+        REG_NONE,
+        NULL,
+        0
+    },
+
+    {
+        NtVdmConfigureRom,
+        RTL_QUERY_REGISTRY_NOEXPAND,
+        L"RomFiles",
+        NULL,
+        REG_NONE,
+        NULL,
+        0
+    },
+
+    {
+        NtVdmConfigureFloppy,
+        0,
+        L"FloppyDisk0",
+        (PVOID)0,
+        REG_NONE,
+        NULL,
+        0
+    },
+
+    {
+        NtVdmConfigureFloppy,
+        0,
+        L"FloppyDisk1",
+        (PVOID)1,
+        REG_NONE,
+        NULL,
+        0
+    },
+
+    {
+        NtVdmConfigureHDD,
+        0,
+        L"HardDisk0",
+        (PVOID)0,
+        REG_NONE,
+        NULL,
+        0
+    },
+
+    {
+        NtVdmConfigureHDD,
+        0,
+        L"HardDisk1",
+        (PVOID)1,
+        REG_NONE,
+        NULL,
+        0
+    },
+
+    {
+        NtVdmConfigureHDD,
+        0,
+        L"HardDisk2",
+        (PVOID)2,
+        REG_NONE,
+        NULL,
+        0
+    },
+
+    {
+        NtVdmConfigureHDD,
+        0,
+        L"HardDisk3",
+        (PVOID)3,
+        REG_NONE,
+        NULL,
+        0
+    },
+
+    /* End of table */
+    {0}
+};
+
+static BOOL
+LoadGlobalSettings(IN PNTVDM_SETTINGS Settings)
+{
+    NTSTATUS Status;
+
+    ASSERT(Settings);
+
+    /*
+     * Now we can do:
+     * - CPU core choice
+     * - Video choice
+     * - Sound choice
+     * - Mem?
+     * - ...
+     * - Standalone mode?
+     * - Debug settings
+     */
+    Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
+                                    L"NTVDM",
+                                    NtVdmConfigurationTable,
+                                    Settings,
+                                    NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("NTVDM registry settings cannot be fully initialized, using default ones. Status = 0x%08lx\n", Status);
+    }
+
+    return NT_SUCCESS(Status);
+}
+
+static VOID
+FreeGlobalSettings(IN PNTVDM_SETTINGS Settings)
+{
+    USHORT i;
+
+    ASSERT(Settings);
+
+    if (Settings->BiosFileName.Buffer)
+        RtlFreeAnsiString(&Settings->BiosFileName);
+
+    if (Settings->RomFiles.Buffer)
+        RtlFreeAnsiString(&Settings->RomFiles);
+
+    for (i = 0; i < ARRAYSIZE(Settings->FloppyDisks); ++i)
+    {
+        if (Settings->FloppyDisks[i].Buffer)
+            RtlFreeAnsiString(&Settings->FloppyDisks[i]);
+    }
+
+    for (i = 0; i < ARRAYSIZE(Settings->HardDisks); ++i)
+    {
+        if (Settings->HardDisks[i].Buffer)
+            RtlFreeAnsiString(&Settings->HardDisks[i]);
+    }
+}
+
 /* PUBLIC FUNCTIONS ***********************************************************/
 
 VOID
-DisplayMessage(LPCWSTR Format, ...)
+DisplayMessage(IN LPCWSTR Format, ...)
 {
 #ifndef WIN2K_COMPLIANT
     WCHAR  StaticBuffer[256];
@@ -242,11 +570,12 @@ DisplayMessage(LPCWSTR Format, ...)
     /*
      * Retrieve the message length and if it is too long, allocate
      * an auxiliary buffer; otherwise use the static buffer.
+     * The string is built to be NULL-terminated.
      */
-    MsgLen = _vscwprintf(Format, Parameters) + 1; // NULL-terminated
-    if (MsgLen > ARRAYSIZE(StaticBuffer))
+    MsgLen = _vscwprintf(Format, Parameters);
+    if (MsgLen >= ARRAYSIZE(StaticBuffer))
     {
-        Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, MsgLen * sizeof(WCHAR));
+        Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, (MsgLen + 1) * sizeof(WCHAR));
         if (Buffer == NULL)
         {
             /* Allocation failed, use the static buffer and display a suitable error message */
@@ -256,59 +585,87 @@ DisplayMessage(LPCWSTR Format, ...)
         }
     }
 #else
-    MsgLen = ARRAYSIZE(Buffer);
+    MsgLen = ARRAYSIZE(Buffer) - 1;
 #endif
 
-    /* Display the message */
+    RtlZeroMemory(Buffer, (MsgLen + 1) * sizeof(WCHAR));
     _vsnwprintf(Buffer, MsgLen, Format, Parameters);
+
+    va_end(Parameters);
+
+    /* Display the message */
     DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer);
-    MessageBoxW(NULL, Buffer, L"NTVDM Subsystem", MB_OK);
+    MessageBoxW(hConsoleWnd, Buffer, L"NTVDM Subsystem", MB_OK);
 
 #ifndef WIN2K_COMPLIANT
     /* Free the buffer if needed */
     if (Buffer != StaticBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
 #endif
+}
 
-    va_end(Parameters);
+static VOID
+ConsoleCleanup(VOID);
+
+static VOID
+VdmShutdown(BOOLEAN Immediate)
+{
+    /*
+     * Immediate = TRUE:  Immediate shutdown;
+     *             FALSE: Delayed shutdown.
+     */
+    static BOOLEAN MustShutdown = FALSE;
+
+    /* If a shutdown is ongoing, just return */
+    if (MustShutdown)
+    {
+        DPRINT1("Shutdown is ongoing...\n");
+        Sleep(INFINITE);
+        return;
+    }
+
+    /* First notify DOS to see whether we can shut down now */
+    MustShutdown = DosShutdown(Immediate);
+    /*
+     * In case we perform an immediate shutdown, or the DOS says
+     * we can shut down, do it now.
+     */
+    MustShutdown = MustShutdown || Immediate;
+
+    if (MustShutdown)
+    {
+        EmulatorTerminate();
+
+        BiosCleanup();
+        EmulatorCleanup();
+        ConsoleCleanup();
+
+        FreeGlobalSettings(&GlobalSettings);
+
+        DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");
+        /* Some VDDs rely on the fact that NTVDM calls ExitProcess on Windows */
+        ExitProcess(0);
+    }
 }
 
 static BOOL
 WINAPI
 ConsoleCtrlHandler(DWORD ControlType)
 {
-// HACK: Should be removed!
-#ifndef STANDALONE
-extern BOOLEAN AcceptCommands;
-extern HANDLE CommandThread;
-#endif
-
     switch (ControlType)
     {
         case CTRL_LAST_CLOSE_EVENT:
         {
-            if (WaitForSingleObject(VdmTaskEvent, 0) == WAIT_TIMEOUT)
-            {
-                /* Nothing runs, so exit immediately */
-#ifndef STANDALONE
-                if (CommandThread) TerminateThread(CommandThread, 0);
-#endif
-                EmulatorTerminate();
-            }
-#ifndef STANDALONE
-            else
-            {
-                /* A command is running, let it run, but stop accepting new commands */
-                AcceptCommands = FALSE;
-            }
-#endif
-
+            /* Delayed shutdown */
+            DPRINT1("NTVDM delayed killing in the CTRL_LAST_CLOSE_EVENT CtrlHandler!\n");
+            VdmShutdown(FALSE);
             break;
         }
 
         default:
         {
             /* Stop the VDM if the user logs out or closes the console */
-            EmulatorTerminate();
+            DPRINT1("Killing NTVDM in the 'default' CtrlHandler!\n");
+            VdmShutdown(TRUE);
         }
     }
     return TRUE;
@@ -317,6 +674,7 @@ extern HANDLE CommandThread;
 static VOID
 ConsoleInitUI(VOID)
 {
+    hConsoleWnd = GetConsoleWindow();
     CreateVdmMenu(ConsoleOutput);
 }
 
@@ -339,7 +697,7 @@ ConsoleAttach(VOID)
         CloseHandle(ConsoleOutput);
         CloseHandle(ConsoleInput);
         wprintf(L"FATAL: Cannot save console in/out modes\n");
-        // return FALSE;
+        return FALSE;
     }
 
     /* Set the console input mode */
@@ -360,12 +718,12 @@ ConsoleAttach(VOID)
 VOID
 ConsoleDetach(VOID)
 {
+    /* Cleanup the UI */
+    ConsoleCleanupUI();
+
     /* Restore the original input and output console modes */
     SetConsoleMode(ConsoleOutput, OrgConsoleOutputMode);
     SetConsoleMode(ConsoleInput , OrgConsoleInputMode );
-
-    /* Cleanup the UI */
-    ConsoleCleanupUI();
 }
 
 static BOOL
@@ -443,9 +801,33 @@ VOID MenuEventHandler(PMENU_EVENT_RECORD MenuEvent)
             DumpMemory(FALSE);
             break;
 
+        /* Drive 0 -- Mount */
+        /* Drive 1 -- Mount */
+        case ID_VDM_DRIVES + 0:
+        case ID_VDM_DRIVES + 2:
+        {
+            ULONG DiskNumber = (MenuEvent->dwCommandId - ID_VDM_DRIVES) / 2;
+            MountFloppy(DiskNumber);
+            break;
+        }
+
+        /* Drive 0 -- Eject */
+        /* Drive 1 -- Eject */
+        case ID_VDM_DRIVES + 1:
+        case ID_VDM_DRIVES + 3:
+        {
+            ULONG DiskNumber = (MenuEvent->dwCommandId - ID_VDM_DRIVES - 1) / 2;
+            EjectFloppy(DiskNumber);
+            break;
+        }
+
         case ID_VDM_QUIT:
             /* Stop the VDM */
-            EmulatorTerminate();
+            // EmulatorTerminate();
+
+            /* Nothing runs, so exit immediately */
+            DPRINT1("Killing NTVDM via console menu!\n");
+            VdmShutdown(TRUE);
             break;
 
         default:
@@ -458,53 +840,6 @@ VOID FocusEventHandler(PFOCUS_EVENT_RECORD FocusEvent)
     DPRINT1("Focus events not handled\n");
 }
 
-static BOOL
-LoadGlobalSettings(VOID)
-{
-// FIXME: These strings should be localized.
-#define ERROR_MEMORYVDD L"Insufficient memory to load installable Virtual Device Drivers."
-#define ERROR_REGVDD    L"Virtual Device Driver format in the registry is invalid."
-#define ERROR_LOADVDD   L"An installable Virtual Device Driver failed Dll initialization."
-
-    BOOL  Success = TRUE;
-    LONG  Error   = 0;
-
-    HKEY    hNTVDMKey;
-    LPCWSTR NTVDMKeyName   = L"SYSTEM\\CurrentControlSet\\Control\\NTVDM";
-
-    /* Try to open the NTVDM registry key */
-    Error = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
-                          NTVDMKeyName,
-                          0,
-                          KEY_QUERY_VALUE,
-                          &hNTVDMKey);
-    if (Error == ERROR_FILE_NOT_FOUND)
-    {
-        /* If the key just doesn't exist, don't do anything else */
-        return TRUE;
-    }
-    else if (Error != ERROR_SUCCESS)
-    {
-        /* The key exists but there was an access error: display an error and quit */
-        DisplayMessage(ERROR_REGVDD);
-        return FALSE;
-    }
-
-    /*
-     * Now we can do:
-     * - CPU core choice
-     * - Video choice
-     * - Sound choice
-     * - Mem?
-     * - ...
-     * - Standalone mode?
-     * - Debug settings
-     */
-
-// Quit:
-    RegCloseKey(hNTVDMKey);
-    return Success;
-}
 
 INT
 wmain(INT argc, WCHAR *argv[])
@@ -523,15 +858,30 @@ wmain(INT argc, WCHAR *argv[])
 
 #endif
 
+#ifdef ADVANCED_DEBUGGING
+    {
+    INT i = 20;
+
+    printf("Waiting for debugger (10 secs)..");
+    while (i--)
+    {
+        printf(".");
+        if (IsDebuggerPresent())
+        {
+            DbgBreakPoint();
+            break;
+        }
+        Sleep(500);
+    }
+    printf("Continue\n");
+    }
+#endif
+
     /* Load global VDM settings */
-    LoadGlobalSettings();
+    LoadGlobalSettings(&GlobalSettings);
 
     DPRINT1("\n\n\nNTVDM - Starting...\n\n\n");
 
-    /* Create the task event */
-    VdmTaskEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
-    ASSERT(VdmTaskEvent != NULL);
-
     /* Initialize the console */
     if (!ConsoleInit())
     {
@@ -546,8 +896,9 @@ wmain(INT argc, WCHAR *argv[])
         goto Cleanup;
     }
 
-    /* Initialize the system BIOS */
-    if (!BiosInitialize(NULL))
+    /* Initialize the system BIOS and option ROMs */
+    if (!BiosInitialize(GlobalSettings.BiosFileName.Buffer,
+                        GlobalSettings.RomFiles.Buffer))
     {
         wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n");
         goto Cleanup;
@@ -556,19 +907,9 @@ wmain(INT argc, WCHAR *argv[])
     /* Let's go! Start simulation */
     CpuSimulate();
 
-Cleanup:
-    BiosCleanup();
-    EmulatorCleanup();
-    ConsoleCleanup();
-
-#ifndef STANDALONE
-    ExitVDM(FALSE, 0);
-#endif
-
     /* Quit the VDM */
-    DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");
-    /* Some VDDs rely on the fact that NTVDM calls ExitProcess on Windows */
-    ExitProcess(0);
+Cleanup:
+    VdmShutdown(TRUE);
     return 0;
 }