[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 Parameters;
566
567 va_start(Parameters, 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, Parameters);
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, Parameters);
593
594 va_end(Parameters);
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 static VOID
607 ConsoleCleanup(VOID);
608
609 static VOID
610 VdmShutdown(BOOLEAN Immediate)
611 {
612 /*
613 * Immediate = TRUE: Immediate shutdown;
614 * FALSE: Delayed shutdown.
615 */
616 static BOOLEAN MustShutdown = FALSE;
617
618 /* If a shutdown is ongoing, just return */
619 if (MustShutdown)
620 {
621 DPRINT1("Shutdown is ongoing...\n");
622 Sleep(INFINITE);
623 return;
624 }
625
626 /* First notify DOS to see whether we can shut down now */
627 MustShutdown = DosShutdown(Immediate);
628 /*
629 * In case we perform an immediate shutdown, or the DOS says
630 * we can shut down, do it now.
631 */
632 MustShutdown = MustShutdown || Immediate;
633
634 if (MustShutdown)
635 {
636 EmulatorTerminate();
637
638 BiosCleanup();
639 EmulatorCleanup();
640 ConsoleCleanup();
641
642 FreeGlobalSettings(&GlobalSettings);
643
644 DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");
645 /* Some VDDs rely on the fact that NTVDM calls ExitProcess on Windows */
646 ExitProcess(0);
647 }
648 }
649
650 static BOOL
651 WINAPI
652 ConsoleCtrlHandler(DWORD ControlType)
653 {
654 switch (ControlType)
655 {
656 case CTRL_LAST_CLOSE_EVENT:
657 {
658 /* Delayed shutdown */
659 DPRINT1("NTVDM delayed killing in the CTRL_LAST_CLOSE_EVENT CtrlHandler!\n");
660 VdmShutdown(FALSE);
661 break;
662 }
663
664 default:
665 {
666 /* Stop the VDM if the user logs out or closes the console */
667 DPRINT1("Killing NTVDM in the 'default' CtrlHandler!\n");
668 VdmShutdown(TRUE);
669 }
670 }
671 return TRUE;
672 }
673
674 static VOID
675 ConsoleInitUI(VOID)
676 {
677 hConsoleWnd = GetConsoleWindow();
678 CreateVdmMenu(ConsoleOutput);
679 }
680
681 static VOID
682 ConsoleCleanupUI(VOID)
683 {
684 /* Display again properly the mouse pointer */
685 if (ShowPointer) ShowHideMousePointer(ConsoleOutput, ShowPointer);
686
687 DestroyVdmMenu();
688 }
689
690 BOOL
691 ConsoleAttach(VOID)
692 {
693 /* Save the original input and output console modes */
694 if (!GetConsoleMode(ConsoleInput , &OrgConsoleInputMode ) ||
695 !GetConsoleMode(ConsoleOutput, &OrgConsoleOutputMode))
696 {
697 CloseHandle(ConsoleOutput);
698 CloseHandle(ConsoleInput);
699 wprintf(L"FATAL: Cannot save console in/out modes\n");
700 return FALSE;
701 }
702
703 /* Set the console input mode */
704 // FIXME: Activate ENABLE_WINDOW_INPUT when we will want to perform actions
705 // upon console window events (screen buffer resize, ...).
706 SetConsoleMode(ConsoleInput, 0 /* | ENABLE_WINDOW_INPUT */);
707 EnableExtraHardware(ConsoleInput);
708
709 /* Set the console output mode */
710 // SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
711
712 /* Initialize the UI */
713 ConsoleInitUI();
714
715 return TRUE;
716 }
717
718 VOID
719 ConsoleDetach(VOID)
720 {
721 /* Cleanup the UI */
722 ConsoleCleanupUI();
723
724 /* Restore the original input and output console modes */
725 SetConsoleMode(ConsoleOutput, OrgConsoleOutputMode);
726 SetConsoleMode(ConsoleInput , OrgConsoleInputMode );
727 }
728
729 static BOOL
730 ConsoleInit(VOID)
731 {
732 /* Set the handler routine */
733 SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
734
735 /* Enable the CTRL_LAST_CLOSE_EVENT */
736 SetLastConsoleEventActive();
737
738 /*
739 * NOTE: The CONIN$ and CONOUT$ "virtual" files
740 * always point to non-redirected console handles.
741 */
742
743 /* Get the input handle to the real console, and check for success */
744 ConsoleInput = CreateFileW(L"CONIN$",
745 GENERIC_READ | GENERIC_WRITE,
746 FILE_SHARE_READ | FILE_SHARE_WRITE,
747 NULL,
748 OPEN_EXISTING,
749 0,
750 NULL);
751 if (ConsoleInput == INVALID_HANDLE_VALUE)
752 {
753 wprintf(L"FATAL: Cannot retrieve a handle to the console input\n");
754 return FALSE;
755 }
756
757 /* Get the output handle to the real console, and check for success */
758 ConsoleOutput = CreateFileW(L"CONOUT$",
759 GENERIC_READ | GENERIC_WRITE,
760 FILE_SHARE_READ | FILE_SHARE_WRITE,
761 NULL,
762 OPEN_EXISTING,
763 0,
764 NULL);
765 if (ConsoleOutput == INVALID_HANDLE_VALUE)
766 {
767 CloseHandle(ConsoleInput);
768 wprintf(L"FATAL: Cannot retrieve a handle to the console output\n");
769 return FALSE;
770 }
771
772 /* Effectively attach to the console */
773 return ConsoleAttach();
774 }
775
776 static VOID
777 ConsoleCleanup(VOID)
778 {
779 /* Detach from the console */
780 ConsoleDetach();
781
782 /* Close the console handles */
783 if (ConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleOutput);
784 if (ConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleInput);
785 }
786
787 VOID MenuEventHandler(PMENU_EVENT_RECORD MenuEvent)
788 {
789 switch (MenuEvent->dwCommandId)
790 {
791 case ID_SHOWHIDE_MOUSE:
792 ShowHideMousePointer(ConsoleOutput, ShowPointer);
793 ShowPointer = !ShowPointer;
794 break;
795
796 case ID_VDM_DUMPMEM_TXT:
797 DumpMemory(TRUE);
798 break;
799
800 case ID_VDM_DUMPMEM_BIN:
801 DumpMemory(FALSE);
802 break;
803
804 /* Drive 0 -- Mount */
805 /* Drive 1 -- Mount */
806 case ID_VDM_DRIVES + 0:
807 case ID_VDM_DRIVES + 2:
808 {
809 ULONG DiskNumber = (MenuEvent->dwCommandId - ID_VDM_DRIVES) / 2;
810 MountFloppy(DiskNumber);
811 break;
812 }
813
814 /* Drive 0 -- Eject */
815 /* Drive 1 -- Eject */
816 case ID_VDM_DRIVES + 1:
817 case ID_VDM_DRIVES + 3:
818 {
819 ULONG DiskNumber = (MenuEvent->dwCommandId - ID_VDM_DRIVES - 1) / 2;
820 EjectFloppy(DiskNumber);
821 break;
822 }
823
824 case ID_VDM_QUIT:
825 /* Stop the VDM */
826 // EmulatorTerminate();
827
828 /* Nothing runs, so exit immediately */
829 DPRINT1("Killing NTVDM via console menu!\n");
830 VdmShutdown(TRUE);
831 break;
832
833 default:
834 break;
835 }
836 }
837
838 VOID FocusEventHandler(PFOCUS_EVENT_RECORD FocusEvent)
839 {
840 DPRINT1("Focus events not handled\n");
841 }
842
843
844 INT
845 wmain(INT argc, WCHAR *argv[])
846 {
847 NtVdmArgc = argc;
848 NtVdmArgv = argv;
849
850 #ifdef STANDALONE
851
852 if (argc < 2)
853 {
854 wprintf(L"\nReactOS Virtual DOS Machine\n\n"
855 L"Usage: NTVDM <executable> [<parameters>]\n");
856 return 0;
857 }
858
859 #endif
860
861 #ifdef ADVANCED_DEBUGGING
862 {
863 INT i = 20;
864
865 printf("Waiting for debugger (10 secs)..");
866 while (i--)
867 {
868 printf(".");
869 if (IsDebuggerPresent())
870 {
871 DbgBreakPoint();
872 break;
873 }
874 Sleep(500);
875 }
876 printf("Continue\n");
877 }
878 #endif
879
880 /* Load global VDM settings */
881 LoadGlobalSettings(&GlobalSettings);
882
883 DPRINT1("\n\n\nNTVDM - Starting...\n\n\n");
884
885 /* Initialize the console */
886 if (!ConsoleInit())
887 {
888 wprintf(L"FATAL: A problem occurred when trying to initialize the console\n");
889 goto Cleanup;
890 }
891
892 /* Initialize the emulator */
893 if (!EmulatorInitialize(ConsoleInput, ConsoleOutput))
894 {
895 wprintf(L"FATAL: Failed to initialize the emulator\n");
896 goto Cleanup;
897 }
898
899 /* Initialize the system BIOS and option ROMs */
900 if (!BiosInitialize(GlobalSettings.BiosFileName.Buffer,
901 GlobalSettings.RomFiles.Buffer))
902 {
903 wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n");
904 goto Cleanup;
905 }
906
907 /* Let's go! Start simulation */
908 CpuSimulate();
909
910 /* Quit the VDM */
911 Cleanup:
912 VdmShutdown(TRUE);
913 return 0;
914 }
915
916 /* EOF */