7f0885c1fce6b81e5c253de25659db992e91b7a5
[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: 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 "resource.h"
20
21 /* VARIABLES ******************************************************************/
22
23 static HANDLE ConsoleInput = INVALID_HANDLE_VALUE;
24 static HANDLE ConsoleOutput = INVALID_HANDLE_VALUE;
25 static DWORD OrgConsoleInputMode, OrgConsoleOutputMode;
26
27 // For DOS
28 #ifndef STANDALONE
29 BOOLEAN AcceptCommands = TRUE;
30 HANDLE CommandThread = NULL;
31 ULONG SessionId = 0;
32 #endif
33
34 HANDLE VdmTaskEvent = NULL;
35
36 // Command line of NTVDM
37 INT NtVdmArgc;
38 WCHAR** NtVdmArgv;
39
40
41 static HMENU hConsoleMenu = NULL;
42 static INT VdmMenuPos = -1;
43 static BOOLEAN ShowPointer = FALSE;
44
45 /*
46 * Those menu helpers were taken from the GUI frontend in winsrv.dll
47 */
48 typedef struct _VDM_MENUITEM
49 {
50 UINT uID;
51 const struct _VDM_MENUITEM *SubMenu;
52 WORD wCmdID;
53 } VDM_MENUITEM, *PVDM_MENUITEM;
54
55 static const VDM_MENUITEM VdmMenuItems[] =
56 {
57 { IDS_VDM_DUMPMEM_TXT, NULL, ID_VDM_DUMPMEM_TXT },
58 { IDS_VDM_DUMPMEM_BIN, NULL, ID_VDM_DUMPMEM_BIN },
59 { IDS_VDM_QUIT , NULL, ID_VDM_QUIT },
60
61 { 0, NULL, 0 } /* End of list */
62 };
63
64 static const VDM_MENUITEM VdmMainMenuItems[] =
65 {
66 { -1, NULL, 0 }, /* Separator */
67 { IDS_HIDE_MOUSE, NULL, ID_SHOWHIDE_MOUSE }, /* Hide mouse; can be renamed to Show mouse */
68 { IDS_VDM_MENU , VdmMenuItems, 0 }, /* ReactOS VDM Menu */
69
70 { 0, NULL, 0 } /* End of list */
71 };
72
73 static VOID
74 AppendMenuItems(HMENU hMenu,
75 const VDM_MENUITEM *Items)
76 {
77 UINT i = 0;
78 WCHAR szMenuString[255];
79 HMENU hSubMenu;
80
81 do
82 {
83 if (Items[i].uID != (UINT)-1)
84 {
85 if (LoadStringW(GetModuleHandle(NULL),
86 Items[i].uID,
87 szMenuString,
88 ARRAYSIZE(szMenuString)) > 0)
89 {
90 if (Items[i].SubMenu != NULL)
91 {
92 hSubMenu = CreatePopupMenu();
93 if (hSubMenu != NULL)
94 {
95 AppendMenuItems(hSubMenu, Items[i].SubMenu);
96
97 if (!AppendMenuW(hMenu,
98 MF_STRING | MF_POPUP,
99 (UINT_PTR)hSubMenu,
100 szMenuString))
101 {
102 DestroyMenu(hSubMenu);
103 }
104 }
105 }
106 else
107 {
108 AppendMenuW(hMenu,
109 MF_STRING,
110 Items[i].wCmdID,
111 szMenuString);
112 }
113 }
114 }
115 else
116 {
117 AppendMenuW(hMenu,
118 MF_SEPARATOR,
119 0,
120 NULL);
121 }
122 i++;
123 } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0));
124 }
125
126 /*static*/ VOID
127 CreateVdmMenu(HANDLE ConOutHandle)
128 {
129 hConsoleMenu = ConsoleMenuControl(ConOutHandle,
130 ID_SHOWHIDE_MOUSE,
131 ID_VDM_QUIT);
132 if (hConsoleMenu == NULL) return;
133
134 VdmMenuPos = GetMenuItemCount(hConsoleMenu);
135 AppendMenuItems(hConsoleMenu, VdmMainMenuItems);
136 DrawMenuBar(GetConsoleWindow());
137 }
138
139 /*static*/ VOID
140 DestroyVdmMenu(VOID)
141 {
142 UINT i = 0;
143 const VDM_MENUITEM *Items = VdmMainMenuItems;
144
145 do
146 {
147 DeleteMenu(hConsoleMenu, VdmMenuPos, MF_BYPOSITION);
148 i++;
149 } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 0));
150
151 DrawMenuBar(GetConsoleWindow());
152 }
153
154 static VOID ShowHideMousePointer(HANDLE ConOutHandle, BOOLEAN ShowPtr)
155 {
156 WCHAR szMenuString[255] = L"";
157
158 if (ShowPtr)
159 {
160 /* Be sure the cursor will be shown */
161 while (ShowConsoleCursor(ConOutHandle, TRUE) < 0) ;
162 }
163 else
164 {
165 /* Be sure the cursor will be hidden */
166 while (ShowConsoleCursor(ConOutHandle, FALSE) >= 0) ;
167 }
168
169 if (LoadStringW(GetModuleHandle(NULL),
170 (!ShowPtr ? IDS_SHOW_MOUSE : IDS_HIDE_MOUSE),
171 szMenuString,
172 ARRAYSIZE(szMenuString)) > 0)
173 {
174 ModifyMenu(hConsoleMenu, ID_SHOWHIDE_MOUSE,
175 MF_BYCOMMAND, ID_SHOWHIDE_MOUSE, szMenuString);
176 }
177 }
178
179 static VOID EnableExtraHardware(HANDLE ConsoleInput)
180 {
181 DWORD ConInMode;
182
183 if (GetConsoleMode(ConsoleInput, &ConInMode))
184 {
185 #if 0
186 // GetNumberOfConsoleMouseButtons();
187 // GetSystemMetrics(SM_CMOUSEBUTTONS);
188 // GetSystemMetrics(SM_MOUSEPRESENT);
189 if (MousePresent)
190 {
191 #endif
192 /* Support mouse input events if there is a mouse on the system */
193 ConInMode |= ENABLE_MOUSE_INPUT;
194 #if 0
195 }
196 else
197 {
198 /* Do not support mouse input events if there is no mouse on the system */
199 ConInMode &= ~ENABLE_MOUSE_INPUT;
200 }
201 #endif
202
203 SetConsoleMode(ConsoleInput, ConInMode);
204 }
205 }
206
207 /* PUBLIC FUNCTIONS ***********************************************************/
208
209 VOID
210 DisplayMessage(LPCWSTR Format, ...)
211 {
212 #ifndef WIN2K_COMPLIANT
213 WCHAR StaticBuffer[256];
214 LPWSTR Buffer = StaticBuffer; // Use the static buffer by default.
215 #else
216 WCHAR Buffer[2048]; // Large enough. If not, increase it by hand.
217 #endif
218 size_t MsgLen;
219 va_list Parameters;
220
221 va_start(Parameters, Format);
222
223 #ifndef WIN2K_COMPLIANT
224 /*
225 * Retrieve the message length and if it is too long, allocate
226 * an auxiliary buffer; otherwise use the static buffer.
227 */
228 MsgLen = _vscwprintf(Format, Parameters) + 1; // NULL-terminated
229 if (MsgLen > ARRAYSIZE(StaticBuffer))
230 {
231 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, MsgLen * sizeof(WCHAR));
232 if (Buffer == NULL)
233 {
234 /* Allocation failed, use the static buffer and display a suitable error message */
235 Buffer = StaticBuffer;
236 Format = L"DisplayMessage()\nOriginal message is too long and allocating an auxiliary buffer failed.";
237 MsgLen = wcslen(Format);
238 }
239 }
240 #else
241 MsgLen = ARRAYSIZE(Buffer);
242 #endif
243
244 /* Display the message */
245 _vsnwprintf(Buffer, MsgLen, Format, Parameters);
246 DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer);
247 MessageBoxW(NULL, Buffer, L"NTVDM Subsystem", MB_OK);
248
249 #ifndef WIN2K_COMPLIANT
250 /* Free the buffer if needed */
251 if (Buffer != StaticBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
252 #endif
253
254 va_end(Parameters);
255 }
256
257 static BOOL
258 WINAPI
259 ConsoleCtrlHandler(DWORD ControlType)
260 {
261 switch (ControlType)
262 {
263 case CTRL_LAST_CLOSE_EVENT:
264 {
265 if (WaitForSingleObject(VdmTaskEvent, 0) == WAIT_TIMEOUT)
266 {
267 /* Exit immediately */
268 #ifndef STANDALONE
269 if (CommandThread) TerminateThread(CommandThread, 0);
270 #endif
271 EmulatorTerminate();
272 }
273 #ifndef STANDALONE
274 else
275 {
276 /* Stop accepting new commands */
277 AcceptCommands = FALSE;
278 }
279 #endif
280
281 break;
282 }
283
284 default:
285 {
286 /* Stop the VDM if the user logs out or closes the console */
287 EmulatorTerminate();
288 }
289 }
290 return TRUE;
291 }
292
293 static VOID
294 ConsoleInitUI(VOID)
295 {
296 CreateVdmMenu(ConsoleOutput);
297 }
298
299 static VOID
300 ConsoleCleanupUI(VOID)
301 {
302 /* Display again properly the mouse pointer */
303 if (ShowPointer) ShowHideMousePointer(ConsoleOutput, ShowPointer);
304
305 DestroyVdmMenu();
306 }
307
308 BOOL
309 ConsoleAttach(VOID)
310 {
311 /* Save the original input and output console modes */
312 if (!GetConsoleMode(ConsoleInput , &OrgConsoleInputMode ) ||
313 !GetConsoleMode(ConsoleOutput, &OrgConsoleOutputMode))
314 {
315 CloseHandle(ConsoleOutput);
316 CloseHandle(ConsoleInput);
317 wprintf(L"FATAL: Cannot save console in/out modes\n");
318 // return FALSE;
319 }
320
321 /* Set the console input mode */
322 // FIXME: Activate ENABLE_WINDOW_INPUT when we will want to perform actions
323 // upon console window events (screen buffer resize, ...).
324 SetConsoleMode(ConsoleInput, 0 /* | ENABLE_WINDOW_INPUT */);
325 EnableExtraHardware(ConsoleInput);
326
327 /* Set the console output mode */
328 // SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
329
330 /* Initialize the UI */
331 ConsoleInitUI();
332
333 return TRUE;
334 }
335
336 VOID
337 ConsoleDetach(VOID)
338 {
339 /* Restore the original input and output console modes */
340 SetConsoleMode(ConsoleOutput, OrgConsoleOutputMode);
341 SetConsoleMode(ConsoleInput , OrgConsoleInputMode );
342
343 /* Cleanup the UI */
344 ConsoleCleanupUI();
345 }
346
347 static BOOL
348 ConsoleInit(VOID)
349 {
350 /* Set the handler routine */
351 SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
352
353 /* Enable the CTRL_LAST_CLOSE_EVENT */
354 SetLastConsoleEventActive();
355
356 /*
357 * NOTE: The CONIN$ and CONOUT$ "virtual" files
358 * always point to non-redirected console handles.
359 */
360
361 /* Get the input handle to the real console, and check for success */
362 ConsoleInput = CreateFileW(L"CONIN$",
363 GENERIC_READ | GENERIC_WRITE,
364 FILE_SHARE_READ | FILE_SHARE_WRITE,
365 NULL,
366 OPEN_EXISTING,
367 0,
368 NULL);
369 if (ConsoleInput == INVALID_HANDLE_VALUE)
370 {
371 wprintf(L"FATAL: Cannot retrieve a handle to the console input\n");
372 return FALSE;
373 }
374
375 /* Get the output handle to the real console, and check for success */
376 ConsoleOutput = CreateFileW(L"CONOUT$",
377 GENERIC_READ | GENERIC_WRITE,
378 FILE_SHARE_READ | FILE_SHARE_WRITE,
379 NULL,
380 OPEN_EXISTING,
381 0,
382 NULL);
383 if (ConsoleOutput == INVALID_HANDLE_VALUE)
384 {
385 CloseHandle(ConsoleInput);
386 wprintf(L"FATAL: Cannot retrieve a handle to the console output\n");
387 return FALSE;
388 }
389
390 /* Effectively attach to the console */
391 return ConsoleAttach();
392 }
393
394 static VOID
395 ConsoleCleanup(VOID)
396 {
397 /* Detach from the console */
398 ConsoleDetach();
399
400 /* Close the console handles */
401 if (ConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleOutput);
402 if (ConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleInput);
403 }
404
405 VOID MenuEventHandler(PMENU_EVENT_RECORD MenuEvent)
406 {
407 switch (MenuEvent->dwCommandId)
408 {
409 case ID_SHOWHIDE_MOUSE:
410 ShowHideMousePointer(ConsoleOutput, ShowPointer);
411 ShowPointer = !ShowPointer;
412 break;
413
414 case ID_VDM_DUMPMEM_TXT:
415 DumpMemory(TRUE);
416 break;
417
418 case ID_VDM_DUMPMEM_BIN:
419 DumpMemory(FALSE);
420 break;
421
422 case ID_VDM_QUIT:
423 /* Stop the VDM */
424 EmulatorTerminate();
425 break;
426
427 default:
428 break;
429 }
430 }
431
432 VOID FocusEventHandler(PFOCUS_EVENT_RECORD FocusEvent)
433 {
434 DPRINT1("Focus events not handled\n");
435 }
436
437 INT
438 wmain(INT argc, WCHAR *argv[])
439 {
440 #ifdef STANDALONE
441
442 if (argc < 2)
443 {
444 wprintf(L"\nReactOS Virtual DOS Machine\n\n"
445 L"Usage: NTVDM <executable> [<parameters>]\n");
446 return 0;
447 }
448
449 #else
450
451 INT i;
452 WCHAR *endptr;
453
454 /* Parse the command line arguments */
455 for (i = 1; i < argc; i++)
456 {
457 if (wcsncmp(argv[i], L"-i", 2) == 0)
458 {
459 /* This is the session ID */
460 SessionId = wcstoul(argv[i] + 2, &endptr, 10);
461
462 /* The VDM hasn't been started from a console, so quit when the task is done */
463 AcceptCommands = FALSE;
464 }
465 }
466
467 #endif
468
469 NtVdmArgc = argc;
470 NtVdmArgv = argv;
471
472 DPRINT1("\n\n\nNTVDM - Starting...\n\n\n");
473
474 /* Create the task event */
475 VdmTaskEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
476 ASSERT(VdmTaskEvent != NULL);
477
478 /* Initialize the console */
479 if (!ConsoleInit())
480 {
481 wprintf(L"FATAL: A problem occurred when trying to initialize the console\n");
482 goto Cleanup;
483 }
484
485 /* Initialize the emulator */
486 if (!EmulatorInitialize(ConsoleInput, ConsoleOutput))
487 {
488 wprintf(L"FATAL: Failed to initialize the emulator\n");
489 goto Cleanup;
490 }
491
492 /* Initialize the system BIOS */
493 if (!BiosInitialize(NULL))
494 {
495 wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n");
496 goto Cleanup;
497 }
498
499 /* Let's go! Start simulation */
500 CpuSimulate();
501
502 Cleanup:
503 BiosCleanup();
504 EmulatorCleanup();
505 ConsoleCleanup();
506
507 #ifndef STANDALONE
508 ExitVDM(FALSE, 0);
509 #endif
510
511 /* Quit the VDM */
512 DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");
513
514 return 0;
515 }
516
517 /* EOF */