[NTVDM]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / ntvdm.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/ntvdm.c
5 * PURPOSE: Virtual DOS Machine
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #define NDEBUG
12
13 #include "ntvdm.h"
14 #include "emulator.h"
15
16 #include "bios/bios.h"
17 #include "cpu/cpu.h"
18
19 #include "dos/dem.h"
20
21 #include "resource.h"
22
23 /* VARIABLES ******************************************************************/
24
25 static HANDLE ConsoleInput = INVALID_HANDLE_VALUE;
26 static HANDLE ConsoleOutput = INVALID_HANDLE_VALUE;
27 static DWORD OrgConsoleInputMode, OrgConsoleOutputMode;
28
29 NTVDM_SETTINGS GlobalSettings;
30
31 // Command line of NTVDM
32 INT NtVdmArgc;
33 WCHAR** NtVdmArgv;
34
35 HWND hConsoleWnd = NULL;
36 static HMENU hConsoleMenu = NULL;
37 static INT VdmMenuPos = -1;
38 static BOOLEAN ShowPointer = FALSE;
39
40 /*
41 * Those menu helpers were taken from the GUI frontend in winsrv.dll
42 */
43 typedef struct _VDM_MENUITEM
44 {
45 UINT uID;
46 const struct _VDM_MENUITEM *SubMenu;
47 UINT_PTR uCmdID;
48 } VDM_MENUITEM, *PVDM_MENUITEM;
49
50 static const VDM_MENUITEM VdmMenuItems[] =
51 {
52 { IDS_VDM_DUMPMEM_TXT, NULL, ID_VDM_DUMPMEM_TXT },
53 { IDS_VDM_DUMPMEM_BIN, NULL, ID_VDM_DUMPMEM_BIN },
54 { -1, NULL, 0 }, /* Separator */
55 // { IDS_VDM_MOUNT_FLOPPY, NULL, ID_VDM_DRIVES },
56 // { IDS_VDM_EJECT_FLOPPY, NULL, ID_VDM_DRIVES },
57 { -1, NULL, 0 }, /* Separator */
58 { IDS_VDM_QUIT , NULL, ID_VDM_QUIT },
59
60 { 0, NULL, 0 } /* End of list */
61 };
62
63 static const VDM_MENUITEM VdmMainMenuItems[] =
64 {
65 { -1, NULL, 0 }, /* Separator */
66 { IDS_HIDE_MOUSE, NULL, ID_SHOWHIDE_MOUSE }, /* "Hide mouse"; can be renamed to "Show mouse" */
67 { IDS_VDM_MENU , VdmMenuItems, 0 }, /* ReactOS VDM Menu */
68
69 { 0, NULL, 0 } /* End of list */
70 };
71
72 static VOID
73 AppendMenuItems(HMENU hMenu,
74 const VDM_MENUITEM *Items)
75 {
76 UINT i = 0;
77 WCHAR szMenuString[256];
78 HMENU hSubMenu;
79
80 do
81 {
82 if (Items[i].uID != (UINT)-1)
83 {
84 if (LoadStringW(GetModuleHandle(NULL),
85 Items[i].uID,
86 szMenuString,
87 ARRAYSIZE(szMenuString)) > 0)
88 {
89 if (Items[i].SubMenu != NULL)
90 {
91 hSubMenu = CreatePopupMenu();
92 if (hSubMenu != NULL)
93 {
94 AppendMenuItems(hSubMenu, Items[i].SubMenu);
95
96 if (!AppendMenuW(hMenu,
97 MF_STRING | MF_POPUP,
98 (UINT_PTR)hSubMenu,
99 szMenuString))
100 {
101 DestroyMenu(hSubMenu);
102 }
103 }
104 }
105 else
106 {
107 AppendMenuW(hMenu,
108 MF_STRING,
109 Items[i].uCmdID,
110 szMenuString);
111 }
112 }
113 }
114 else
115 {
116 AppendMenuW(hMenu,
117 MF_SEPARATOR,
118 0,
119 NULL);
120 }
121 i++;
122 } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].uCmdID == 0));
123 }
124
125 BOOL
126 VdmMenuExists(HMENU hConsoleMenu)
127 {
128 INT MenuPos, i;
129 MenuPos = GetMenuItemCount(hConsoleMenu);
130
131 /* Check for the presence of one of the VDM menu items */
132 for (i = 0; i <= MenuPos; i++)
133 {
134 if (GetMenuItemID(hConsoleMenu, i) == ID_SHOWHIDE_MOUSE)
135 {
136 /* Set VdmMenuPos to the position of the existing menu */
137 VdmMenuPos = i - 1;
138 return TRUE;
139 }
140 }
141 return FALSE;
142 }
143
144 /*static*/ VOID
145 CreateVdmMenu(HANDLE ConOutHandle)
146 {
147 HMENU hVdmSubMenu;
148 UINT_PTR ItemID = ID_VDM_DRIVES;
149 UINT Pos;
150 WCHAR szNoMedia[100];
151 WCHAR szMenuString1[256], szMenuString2[256];
152
153 hConsoleMenu = ConsoleMenuControl(ConOutHandle,
154 ID_SHOWHIDE_MOUSE,
155 ID_VDM_DRIVES + 4);
156 if (hConsoleMenu == NULL) return;
157
158 /* Get the position where we are going to insert our menu items */
159 VdmMenuPos = GetMenuItemCount(hConsoleMenu);
160
161 /* Really add the menu if it doesn't already exist (in case eg. NTVDM crashed) */
162 if (!VdmMenuExists(hConsoleMenu))
163 {
164 /* Add all the menu entries */
165 AppendMenuItems(hConsoleMenu, VdmMainMenuItems);
166
167 /* Add the removable drives menu entries */
168 hVdmSubMenu = GetSubMenu(hConsoleMenu, VdmMenuPos + 2); // VdmMenuItems
169 Pos = 3; // After the 2 items and the separator in VdmMenuItems
170
171 LoadStringW(GetModuleHandle(NULL),
172 IDS_NO_MEDIA,
173 szNoMedia,
174 ARRAYSIZE(szNoMedia));
175
176 LoadStringW(GetModuleHandle(NULL),
177 IDS_VDM_MOUNT_FLOPPY,
178 szMenuString1,
179 ARRAYSIZE(szMenuString1));
180
181 /* Drive 0 -- Mount */
182 _snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, 0, szNoMedia);
183 szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
184 InsertMenuW(hVdmSubMenu, Pos++, MF_STRING | MF_BYPOSITION, ItemID + 0, szMenuString2);
185
186 /* Drive 1 -- Mount */
187 _snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, 1, szNoMedia);
188 szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
189 InsertMenuW(hVdmSubMenu, Pos++, MF_STRING | MF_BYPOSITION, ItemID + 2, szMenuString2);
190
191 LoadStringW(GetModuleHandle(NULL),
192 IDS_VDM_EJECT_FLOPPY,
193 szMenuString1,
194 ARRAYSIZE(szMenuString1));
195
196 /* Drive 0 -- Eject */
197 _snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, 0);
198 szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
199 InsertMenuW(hVdmSubMenu, Pos++, MF_STRING | MF_BYPOSITION, ItemID + 1, szMenuString2);
200
201 /* Drive 1 -- Eject */
202 _snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, 1);
203 szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
204 InsertMenuW(hVdmSubMenu, Pos++, MF_STRING | MF_BYPOSITION, ItemID + 3, szMenuString2);
205
206 // TODO: Refresh the menu state
207
208 /* Refresh the menu */
209 DrawMenuBar(hConsoleWnd);
210 }
211 }
212
213 /*static*/ VOID
214 DestroyVdmMenu(VOID)
215 {
216 UINT i = 0;
217 const VDM_MENUITEM *Items = VdmMainMenuItems;
218
219 do
220 {
221 DeleteMenu(hConsoleMenu, VdmMenuPos, MF_BYPOSITION);
222 i++;
223 } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].uCmdID == 0));
224
225 DrawMenuBar(hConsoleWnd);
226 }
227
228 static VOID ShowHideMousePointer(HANDLE ConOutHandle, BOOLEAN ShowPtr)
229 {
230 WCHAR szMenuString[256];
231
232 if (ShowPtr)
233 {
234 /* Be sure the cursor will be shown */
235 while (ShowConsoleCursor(ConOutHandle, TRUE) < 0) ;
236 }
237 else
238 {
239 /* Be sure the cursor will be hidden */
240 while (ShowConsoleCursor(ConOutHandle, FALSE) >= 0) ;
241 }
242
243 if (LoadStringW(GetModuleHandle(NULL),
244 (!ShowPtr ? IDS_SHOW_MOUSE : IDS_HIDE_MOUSE),
245 szMenuString,
246 ARRAYSIZE(szMenuString)) > 0)
247 {
248 ModifyMenu(hConsoleMenu, ID_SHOWHIDE_MOUSE,
249 MF_BYCOMMAND, ID_SHOWHIDE_MOUSE, szMenuString);
250 }
251 }
252
253 static VOID EnableExtraHardware(HANDLE ConsoleInput)
254 {
255 DWORD ConInMode;
256
257 if (GetConsoleMode(ConsoleInput, &ConInMode))
258 {
259 #if 0
260 // GetNumberOfConsoleMouseButtons();
261 // GetSystemMetrics(SM_CMOUSEBUTTONS);
262 // GetSystemMetrics(SM_MOUSEPRESENT);
263 if (MousePresent)
264 {
265 #endif
266 /* Support mouse input events if there is a mouse on the system */
267 ConInMode |= ENABLE_MOUSE_INPUT;
268 #if 0
269 }
270 else
271 {
272 /* Do not support mouse input events if there is no mouse on the system */
273 ConInMode &= ~ENABLE_MOUSE_INPUT;
274 }
275 #endif
276
277 SetConsoleMode(ConsoleInput, ConInMode);
278 }
279 }
280
281
282 static NTSTATUS
283 NTAPI
284 NtVdmConfigureBios(IN PWSTR ValueName,
285 IN ULONG ValueType,
286 IN PVOID ValueData,
287 IN ULONG ValueLength,
288 IN PVOID Context,
289 IN PVOID EntryContext)
290 {
291 PNTVDM_SETTINGS Settings = (PNTVDM_SETTINGS)Context;
292 UNICODE_STRING ValueString;
293
294 /* Check for the type of the value */
295 if (ValueType != REG_SZ)
296 {
297 RtlInitEmptyAnsiString(&Settings->BiosFileName, NULL, 0);
298 return STATUS_SUCCESS;
299 }
300
301 /* Convert the UNICODE string to ANSI and store it */
302 RtlInitEmptyUnicodeString(&ValueString, (PWCHAR)ValueData, ValueLength);
303 ValueString.Length = ValueString.MaximumLength;
304 RtlUnicodeStringToAnsiString(&Settings->BiosFileName, &ValueString, TRUE);
305
306 return STATUS_SUCCESS;
307 }
308
309 static NTSTATUS
310 NTAPI
311 NtVdmConfigureRom(IN PWSTR ValueName,
312 IN ULONG ValueType,
313 IN PVOID ValueData,
314 IN ULONG ValueLength,
315 IN PVOID Context,
316 IN PVOID EntryContext)
317 {
318 PNTVDM_SETTINGS Settings = (PNTVDM_SETTINGS)Context;
319 UNICODE_STRING ValueString;
320
321 /* Check for the type of the value */
322 if (ValueType != REG_MULTI_SZ)
323 {
324 RtlInitEmptyAnsiString(&Settings->RomFiles, NULL, 0);
325 return STATUS_SUCCESS;
326 }
327
328 /* Convert the UNICODE string to ANSI and store it */
329 RtlInitEmptyUnicodeString(&ValueString, (PWCHAR)ValueData, ValueLength);
330 ValueString.Length = ValueString.MaximumLength;
331 RtlUnicodeStringToAnsiString(&Settings->RomFiles, &ValueString, TRUE);
332
333 return STATUS_SUCCESS;
334 }
335
336 static NTSTATUS
337 NTAPI
338 NtVdmConfigureFloppy(IN PWSTR ValueName,
339 IN ULONG ValueType,
340 IN PVOID ValueData,
341 IN ULONG ValueLength,
342 IN PVOID Context,
343 IN PVOID EntryContext)
344 {
345 PNTVDM_SETTINGS Settings = (PNTVDM_SETTINGS)Context;
346 UNICODE_STRING ValueString;
347 ULONG DiskNumber = (ULONG)EntryContext;
348
349 ASSERT(DiskNumber < ARRAYSIZE(Settings->FloppyDisks));
350
351 /* Check whether the Hard Disk entry was not already configured */
352 if (Settings->FloppyDisks[DiskNumber].Buffer != NULL)
353 {
354 DPRINT1("Floppy Disk %d -- '%Z' already configured\n", DiskNumber, &Settings->FloppyDisks[DiskNumber]);
355 return STATUS_SUCCESS;
356 }
357
358 /* Check for the type of the value */
359 if (ValueType != REG_SZ)
360 {
361 RtlInitEmptyAnsiString(&Settings->FloppyDisks[DiskNumber], NULL, 0);
362 return STATUS_SUCCESS;
363 }
364
365 /* Convert the UNICODE string to ANSI and store it */
366 RtlInitEmptyUnicodeString(&ValueString, (PWCHAR)ValueData, ValueLength);
367 ValueString.Length = ValueString.MaximumLength;
368 RtlUnicodeStringToAnsiString(&Settings->FloppyDisks[DiskNumber], &ValueString, TRUE);
369
370 return STATUS_SUCCESS;
371 }
372
373 static NTSTATUS
374 NTAPI
375 NtVdmConfigureHDD(IN PWSTR ValueName,
376 IN ULONG ValueType,
377 IN PVOID ValueData,
378 IN ULONG ValueLength,
379 IN PVOID Context,
380 IN PVOID EntryContext)
381 {
382 PNTVDM_SETTINGS Settings = (PNTVDM_SETTINGS)Context;
383 UNICODE_STRING ValueString;
384 ULONG DiskNumber = (ULONG)EntryContext;
385
386 ASSERT(DiskNumber < ARRAYSIZE(Settings->HardDisks));
387
388 /* Check whether the Hard Disk entry was not already configured */
389 if (Settings->HardDisks[DiskNumber].Buffer != NULL)
390 {
391 DPRINT1("Hard Disk %d -- '%Z' already configured\n", DiskNumber, &Settings->HardDisks[DiskNumber]);
392 return STATUS_SUCCESS;
393 }
394
395 /* Check for the type of the value */
396 if (ValueType != REG_SZ)
397 {
398 RtlInitEmptyAnsiString(&Settings->HardDisks[DiskNumber], NULL, 0);
399 return STATUS_SUCCESS;
400 }
401
402 /* Convert the UNICODE string to ANSI and store it */
403 RtlInitEmptyUnicodeString(&ValueString, (PWCHAR)ValueData, ValueLength);
404 ValueString.Length = ValueString.MaximumLength;
405 RtlUnicodeStringToAnsiString(&Settings->HardDisks[DiskNumber], &ValueString, TRUE);
406
407 return STATUS_SUCCESS;
408 }
409
410 static RTL_QUERY_REGISTRY_TABLE
411 NtVdmConfigurationTable[] =
412 {
413 {
414 NtVdmConfigureBios,
415 0,
416 L"BiosFile",
417 NULL,
418 REG_NONE,
419 NULL,
420 0
421 },
422
423 {
424 NtVdmConfigureRom,
425 RTL_QUERY_REGISTRY_NOEXPAND,
426 L"RomFiles",
427 NULL,
428 REG_NONE,
429 NULL,
430 0
431 },
432
433 {
434 NtVdmConfigureFloppy,
435 0,
436 L"FloppyDisk0",
437 (PVOID)0,
438 REG_NONE,
439 NULL,
440 0
441 },
442
443 {
444 NtVdmConfigureFloppy,
445 0,
446 L"FloppyDisk1",
447 (PVOID)1,
448 REG_NONE,
449 NULL,
450 0
451 },
452
453 {
454 NtVdmConfigureHDD,
455 0,
456 L"HardDisk0",
457 (PVOID)0,
458 REG_NONE,
459 NULL,
460 0
461 },
462
463 {
464 NtVdmConfigureHDD,
465 0,
466 L"HardDisk1",
467 (PVOID)1,
468 REG_NONE,
469 NULL,
470 0
471 },
472
473 {
474 NtVdmConfigureHDD,
475 0,
476 L"HardDisk2",
477 (PVOID)2,
478 REG_NONE,
479 NULL,
480 0
481 },
482
483 {
484 NtVdmConfigureHDD,
485 0,
486 L"HardDisk3",
487 (PVOID)3,
488 REG_NONE,
489 NULL,
490 0
491 },
492
493 /* End of table */
494 {0}
495 };
496
497 static BOOL
498 LoadGlobalSettings(IN PNTVDM_SETTINGS Settings)
499 {
500 NTSTATUS Status;
501
502 ASSERT(Settings);
503
504 /*
505 * Now we can do:
506 * - CPU core choice
507 * - Video choice
508 * - Sound choice
509 * - Mem?
510 * - ...
511 * - Standalone mode?
512 * - Debug settings
513 */
514 Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
515 L"NTVDM",
516 NtVdmConfigurationTable,
517 Settings,
518 NULL);
519 if (!NT_SUCCESS(Status))
520 {
521 DPRINT1("NTVDM registry settings cannot be fully initialized, using default ones. Status = 0x%08lx\n", Status);
522 }
523
524 return NT_SUCCESS(Status);
525 }
526
527 static VOID
528 FreeGlobalSettings(IN PNTVDM_SETTINGS Settings)
529 {
530 USHORT i;
531
532 ASSERT(Settings);
533
534 if (Settings->BiosFileName.Buffer)
535 RtlFreeAnsiString(&Settings->BiosFileName);
536
537 if (Settings->RomFiles.Buffer)
538 RtlFreeAnsiString(&Settings->RomFiles);
539
540 for (i = 0; i < ARRAYSIZE(Settings->FloppyDisks); ++i)
541 {
542 if (Settings->FloppyDisks[i].Buffer)
543 RtlFreeAnsiString(&Settings->FloppyDisks[i]);
544 }
545
546 for (i = 0; i < ARRAYSIZE(Settings->HardDisks); ++i)
547 {
548 if (Settings->HardDisks[i].Buffer)
549 RtlFreeAnsiString(&Settings->HardDisks[i]);
550 }
551 }
552
553 /* PUBLIC FUNCTIONS ***********************************************************/
554
555 VOID
556 DisplayMessage(IN LPCWSTR Format, ...)
557 {
558 #ifndef WIN2K_COMPLIANT
559 WCHAR StaticBuffer[256];
560 LPWSTR Buffer = StaticBuffer; // Use the static buffer by default.
561 #else
562 WCHAR Buffer[2048]; // Large enough. If not, increase it by hand.
563 #endif
564 size_t MsgLen;
565 va_list args;
566
567 va_start(args, Format);
568
569 #ifndef WIN2K_COMPLIANT
570 /*
571 * Retrieve the message length and if it is too long, allocate
572 * an auxiliary buffer; otherwise use the static buffer.
573 * The string is built to be NULL-terminated.
574 */
575 MsgLen = _vscwprintf(Format, args);
576 if (MsgLen >= ARRAYSIZE(StaticBuffer))
577 {
578 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, (MsgLen + 1) * sizeof(WCHAR));
579 if (Buffer == NULL)
580 {
581 /* Allocation failed, use the static buffer and display a suitable error message */
582 Buffer = StaticBuffer;
583 Format = L"DisplayMessage()\nOriginal message is too long and allocating an auxiliary buffer failed.";
584 MsgLen = wcslen(Format);
585 }
586 }
587 #else
588 MsgLen = ARRAYSIZE(Buffer) - 1;
589 #endif
590
591 RtlZeroMemory(Buffer, (MsgLen + 1) * sizeof(WCHAR));
592 _vsnwprintf(Buffer, MsgLen, Format, args);
593
594 va_end(args);
595
596 /* Display the message */
597 DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer);
598 MessageBoxW(hConsoleWnd, Buffer, L"NTVDM Subsystem", MB_OK);
599
600 #ifndef WIN2K_COMPLIANT
601 /* Free the buffer if needed */
602 if (Buffer != StaticBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
603 #endif
604 }
605
606 /*
607 * This function, derived from DisplayMessage, is used by the BIOS and
608 * the DOS to display messages to an output device. A printer function
609 * is given for printing the characters.
610 */
611 VOID
612 PrintMessageAnsi(IN CHAR_PRINT CharPrint,
613 IN LPCSTR Format, ...)
614 {
615 static CHAR CurChar = 0;
616 LPSTR str;
617
618 #ifndef WIN2K_COMPLIANT
619 CHAR StaticBuffer[256];
620 LPSTR Buffer = StaticBuffer; // Use the static buffer by default.
621 #else
622 CHAR Buffer[2048]; // Large enough. If not, increase it by hand.
623 #endif
624 size_t MsgLen;
625 va_list args;
626
627 va_start(args, Format);
628
629 #ifndef WIN2K_COMPLIANT
630 /*
631 * Retrieve the message length and if it is too long, allocate
632 * an auxiliary buffer; otherwise use the static buffer.
633 * The string is built to be NULL-terminated.
634 */
635 MsgLen = _vscprintf(Format, args);
636 if (MsgLen >= ARRAYSIZE(StaticBuffer))
637 {
638 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, (MsgLen + 1) * sizeof(CHAR));
639 if (Buffer == NULL)
640 {
641 /* Allocation failed, use the static buffer and display a suitable error message */
642 Buffer = StaticBuffer;
643 Format = "DisplayMessageAnsi()\nOriginal message is too long and allocating an auxiliary buffer failed.";
644 MsgLen = strlen(Format);
645 }
646 }
647 #else
648 MsgLen = ARRAYSIZE(Buffer) - 1;
649 #endif
650
651 RtlZeroMemory(Buffer, (MsgLen + 1) * sizeof(CHAR));
652 _vsnprintf(Buffer, MsgLen, Format, args);
653
654 va_end(args);
655
656 /* Display the message */
657 // DPRINT1("\n\nNTVDM DOS32\n%s\n\n", Buffer);
658
659 MsgLen = strlen(Buffer);
660 str = Buffer;
661 while (MsgLen--)
662 {
663 if (*str == '\n' && CurChar != '\r')
664 CharPrint('\r');
665
666 CurChar = *str++;
667 CharPrint(CurChar);
668 }
669
670 #ifndef WIN2K_COMPLIANT
671 /* Free the buffer if needed */
672 if (Buffer != StaticBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
673 #endif
674 }
675
676 static VOID
677 ConsoleCleanup(VOID);
678
679 static VOID
680 VdmShutdown(BOOLEAN Immediate)
681 {
682 /*
683 * Immediate = TRUE: Immediate shutdown;
684 * FALSE: Delayed shutdown.
685 */
686 static BOOLEAN MustShutdown = FALSE;
687
688 /* If a shutdown is ongoing, just return */
689 if (MustShutdown)
690 {
691 DPRINT1("Shutdown is ongoing...\n");
692 Sleep(INFINITE);
693 return;
694 }
695
696 /* First notify DOS to see whether we can shut down now */
697 MustShutdown = DosShutdown(Immediate);
698 /*
699 * In case we perform an immediate shutdown, or the DOS says
700 * we can shut down, do it now.
701 */
702 MustShutdown = MustShutdown || Immediate;
703
704 if (MustShutdown)
705 {
706 EmulatorTerminate();
707
708 BiosCleanup();
709 EmulatorCleanup();
710 ConsoleCleanup();
711
712 FreeGlobalSettings(&GlobalSettings);
713
714 DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");
715 /* Some VDDs rely on the fact that NTVDM calls ExitProcess on Windows */
716 ExitProcess(0);
717 }
718 }
719
720 static BOOL
721 WINAPI
722 ConsoleCtrlHandler(DWORD ControlType)
723 {
724 switch (ControlType)
725 {
726 case CTRL_LAST_CLOSE_EVENT:
727 {
728 /* Delayed shutdown */
729 DPRINT1("NTVDM delayed killing in the CTRL_LAST_CLOSE_EVENT CtrlHandler!\n");
730 VdmShutdown(FALSE);
731 break;
732 }
733
734 default:
735 {
736 /* Stop the VDM if the user logs out or closes the console */
737 DPRINT1("Killing NTVDM in the 'default' CtrlHandler!\n");
738 VdmShutdown(TRUE);
739 }
740 }
741 return TRUE;
742 }
743
744 static VOID
745 ConsoleInitUI(VOID)
746 {
747 hConsoleWnd = GetConsoleWindow();
748 CreateVdmMenu(ConsoleOutput);
749 }
750
751 static VOID
752 ConsoleCleanupUI(VOID)
753 {
754 /* Display again properly the mouse pointer */
755 if (ShowPointer) ShowHideMousePointer(ConsoleOutput, ShowPointer);
756
757 DestroyVdmMenu();
758 }
759
760 BOOL
761 ConsoleAttach(VOID)
762 {
763 /* Save the original input and output console modes */
764 if (!GetConsoleMode(ConsoleInput , &OrgConsoleInputMode ) ||
765 !GetConsoleMode(ConsoleOutput, &OrgConsoleOutputMode))
766 {
767 CloseHandle(ConsoleOutput);
768 CloseHandle(ConsoleInput);
769 wprintf(L"FATAL: Cannot save console in/out modes\n");
770 return FALSE;
771 }
772
773 /* Set the console input mode */
774 // FIXME: Activate ENABLE_WINDOW_INPUT when we will want to perform actions
775 // upon console window events (screen buffer resize, ...).
776 SetConsoleMode(ConsoleInput, 0 /* | ENABLE_WINDOW_INPUT */);
777 EnableExtraHardware(ConsoleInput);
778
779 /* Set the console output mode */
780 // SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
781
782 /* Initialize the UI */
783 ConsoleInitUI();
784
785 return TRUE;
786 }
787
788 VOID
789 ConsoleDetach(VOID)
790 {
791 /* Cleanup the UI */
792 ConsoleCleanupUI();
793
794 /* Restore the original input and output console modes */
795 SetConsoleMode(ConsoleOutput, OrgConsoleOutputMode);
796 SetConsoleMode(ConsoleInput , OrgConsoleInputMode );
797 }
798
799 static BOOL
800 ConsoleInit(VOID)
801 {
802 /* Set the handler routine */
803 SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
804
805 /* Enable the CTRL_LAST_CLOSE_EVENT */
806 SetLastConsoleEventActive();
807
808 /*
809 * NOTE: The CONIN$ and CONOUT$ "virtual" files
810 * always point to non-redirected console handles.
811 */
812
813 /* Get the input handle to the real console, and check for success */
814 ConsoleInput = CreateFileW(L"CONIN$",
815 GENERIC_READ | GENERIC_WRITE,
816 FILE_SHARE_READ | FILE_SHARE_WRITE,
817 NULL,
818 OPEN_EXISTING,
819 0,
820 NULL);
821 if (ConsoleInput == INVALID_HANDLE_VALUE)
822 {
823 wprintf(L"FATAL: Cannot retrieve a handle to the console input\n");
824 return FALSE;
825 }
826
827 /* Get the output handle to the real console, and check for success */
828 ConsoleOutput = CreateFileW(L"CONOUT$",
829 GENERIC_READ | GENERIC_WRITE,
830 FILE_SHARE_READ | FILE_SHARE_WRITE,
831 NULL,
832 OPEN_EXISTING,
833 0,
834 NULL);
835 if (ConsoleOutput == INVALID_HANDLE_VALUE)
836 {
837 CloseHandle(ConsoleInput);
838 wprintf(L"FATAL: Cannot retrieve a handle to the console output\n");
839 return FALSE;
840 }
841
842 /* Effectively attach to the console */
843 return ConsoleAttach();
844 }
845
846 static VOID
847 ConsoleCleanup(VOID)
848 {
849 /* Detach from the console */
850 ConsoleDetach();
851
852 /* Close the console handles */
853 if (ConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleOutput);
854 if (ConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleInput);
855 }
856
857 VOID MenuEventHandler(PMENU_EVENT_RECORD MenuEvent)
858 {
859 switch (MenuEvent->dwCommandId)
860 {
861 case ID_SHOWHIDE_MOUSE:
862 ShowHideMousePointer(ConsoleOutput, ShowPointer);
863 ShowPointer = !ShowPointer;
864 break;
865
866 case ID_VDM_DUMPMEM_TXT:
867 DumpMemory(TRUE);
868 break;
869
870 case ID_VDM_DUMPMEM_BIN:
871 DumpMemory(FALSE);
872 break;
873
874 /* Drive 0 -- Mount */
875 /* Drive 1 -- Mount */
876 case ID_VDM_DRIVES + 0:
877 case ID_VDM_DRIVES + 2:
878 {
879 ULONG DiskNumber = (MenuEvent->dwCommandId - ID_VDM_DRIVES) / 2;
880 MountFloppy(DiskNumber);
881 break;
882 }
883
884 /* Drive 0 -- Eject */
885 /* Drive 1 -- Eject */
886 case ID_VDM_DRIVES + 1:
887 case ID_VDM_DRIVES + 3:
888 {
889 ULONG DiskNumber = (MenuEvent->dwCommandId - ID_VDM_DRIVES - 1) / 2;
890 EjectFloppy(DiskNumber);
891 break;
892 }
893
894 case ID_VDM_QUIT:
895 /* Stop the VDM */
896 // EmulatorTerminate();
897
898 /* Nothing runs, so exit immediately */
899 DPRINT1("Killing NTVDM via console menu!\n");
900 VdmShutdown(TRUE);
901 break;
902
903 default:
904 break;
905 }
906 }
907
908 VOID FocusEventHandler(PFOCUS_EVENT_RECORD FocusEvent)
909 {
910 DPRINT1("Focus events not handled\n");
911 }
912
913
914 INT
915 wmain(INT argc, WCHAR *argv[])
916 {
917 NtVdmArgc = argc;
918 NtVdmArgv = argv;
919
920 #ifdef STANDALONE
921
922 if (argc < 2)
923 {
924 wprintf(L"\nReactOS Virtual DOS Machine\n\n"
925 L"Usage: NTVDM <executable> [<parameters>]\n");
926 return 0;
927 }
928
929 #endif
930
931 #ifdef ADVANCED_DEBUGGING
932 {
933 INT i = 20;
934
935 printf("Waiting for debugger (10 secs)..");
936 while (i--)
937 {
938 printf(".");
939 if (IsDebuggerPresent())
940 {
941 DbgBreakPoint();
942 break;
943 }
944 Sleep(500);
945 }
946 printf("Continue\n");
947 }
948 #endif
949
950 /* Load the global VDM settings */
951 LoadGlobalSettings(&GlobalSettings);
952
953 DPRINT1("\n\n\nNTVDM - Starting...\n\n\n");
954
955 /* Initialize the console */
956 if (!ConsoleInit())
957 {
958 wprintf(L"FATAL: A problem occurred when trying to initialize the console\n");
959 goto Cleanup;
960 }
961
962 /* Initialize the emulator */
963 if (!EmulatorInitialize(ConsoleInput, ConsoleOutput))
964 {
965 wprintf(L"FATAL: Failed to initialize the emulator\n");
966 goto Cleanup;
967 }
968
969 /* Initialize the system BIOS and option ROMs */
970 if (!BiosInitialize(GlobalSettings.BiosFileName.Buffer,
971 GlobalSettings.RomFiles.Buffer))
972 {
973 wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n");
974 goto Cleanup;
975 }
976
977 /* Let's go! Start simulation */
978 CpuSimulate();
979
980 /* Quit the VDM */
981 Cleanup:
982 VdmShutdown(TRUE);
983 return 0;
984 }
985
986 /* EOF */