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