* Sync up to trunk head (r64921).
[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 WCHAR Buffer[256];
189 va_list Parameters;
190
191 va_start(Parameters, Format);
192 _vsnwprintf(Buffer, 256, Format, Parameters);
193 DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer);
194 MessageBoxW(NULL, Buffer, L"NTVDM Subsystem", MB_OK);
195 va_end(Parameters);
196 }
197
198 static BOOL
199 WINAPI
200 ConsoleCtrlHandler(DWORD ControlType)
201 {
202 switch (ControlType)
203 {
204 case CTRL_C_EVENT:
205 case CTRL_BREAK_EVENT:
206 {
207 /* Call INT 23h */
208 DPRINT1("Ctrl-C/Break: Call INT 23h\n");
209 EmulatorInterrupt(0x23);
210 break;
211 }
212 case CTRL_LAST_CLOSE_EVENT:
213 {
214 if (WaitForSingleObject(VdmTaskEvent, 0) == WAIT_TIMEOUT)
215 {
216 /* Exit immediately */
217 #ifndef STANDALONE
218 if (CommandThread) TerminateThread(CommandThread, 0);
219 #endif
220 EmulatorTerminate();
221 }
222 #ifndef STANDALONE
223 else
224 {
225 /* Stop accepting new commands */
226 AcceptCommands = FALSE;
227 }
228 #endif
229
230 break;
231 }
232 default:
233 {
234 /* Stop the VDM if the user logs out or closes the console */
235 EmulatorTerminate();
236 }
237 }
238 return TRUE;
239 }
240
241 static VOID
242 ConsoleInitUI(VOID)
243 {
244 CreateVdmMenu(ConsoleOutput);
245 }
246
247 static VOID
248 ConsoleCleanupUI(VOID)
249 {
250 /* Display again properly the mouse pointer */
251 if (ShowPointer) ShowHideMousePointer(ConsoleOutput, ShowPointer);
252
253 DestroyVdmMenu();
254 }
255
256 static BOOL
257 ConsoleAttach(VOID)
258 {
259 /* Save the original input and output console modes */
260 if (!GetConsoleMode(ConsoleInput , &OrgConsoleInputMode ) ||
261 !GetConsoleMode(ConsoleOutput, &OrgConsoleOutputMode))
262 {
263 CloseHandle(ConsoleOutput);
264 CloseHandle(ConsoleInput);
265 wprintf(L"FATAL: Cannot save console in/out modes\n");
266 // return FALSE;
267 }
268
269 /* Initialize the UI */
270 ConsoleInitUI();
271
272 return TRUE;
273 }
274
275 static VOID
276 ConsoleDetach(VOID)
277 {
278 /* Restore the original input and output console modes */
279 SetConsoleMode(ConsoleOutput, OrgConsoleOutputMode);
280 SetConsoleMode(ConsoleInput , OrgConsoleInputMode );
281
282 /* Cleanup the UI */
283 ConsoleCleanupUI();
284 }
285
286 static BOOL
287 ConsoleInit(VOID)
288 {
289 /* Set the handler routine */
290 SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
291
292 /* Enable the CTRL_LAST_CLOSE_EVENT */
293 SetLastConsoleEventActive();
294
295 /*
296 * NOTE: The CONIN$ and CONOUT$ "virtual" files
297 * always point to non-redirected console handles.
298 */
299
300 /* Get the input handle to the real console, and check for success */
301 ConsoleInput = CreateFileW(L"CONIN$",
302 GENERIC_READ | GENERIC_WRITE,
303 FILE_SHARE_READ | FILE_SHARE_WRITE,
304 NULL,
305 OPEN_EXISTING,
306 0,
307 NULL);
308 if (ConsoleInput == INVALID_HANDLE_VALUE)
309 {
310 wprintf(L"FATAL: Cannot retrieve a handle to the console input\n");
311 return FALSE;
312 }
313
314 /* Get the output handle to the real console, and check for success */
315 ConsoleOutput = CreateFileW(L"CONOUT$",
316 GENERIC_READ | GENERIC_WRITE,
317 FILE_SHARE_READ | FILE_SHARE_WRITE,
318 NULL,
319 OPEN_EXISTING,
320 0,
321 NULL);
322 if (ConsoleOutput == INVALID_HANDLE_VALUE)
323 {
324 CloseHandle(ConsoleInput);
325 wprintf(L"FATAL: Cannot retrieve a handle to the console output\n");
326 return FALSE;
327 }
328
329 /* Effectively attach to the console */
330 return ConsoleAttach();
331 }
332
333 static VOID
334 ConsoleCleanup(VOID)
335 {
336 /* Detach from the console */
337 ConsoleDetach();
338
339 /* Close the console handles */
340 if (ConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleOutput);
341 if (ConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleInput);
342 }
343
344 VOID MenuEventHandler(PMENU_EVENT_RECORD MenuEvent)
345 {
346 switch (MenuEvent->dwCommandId)
347 {
348 case ID_SHOWHIDE_MOUSE:
349 ShowHideMousePointer(ConsoleOutput, ShowPointer);
350 ShowPointer = !ShowPointer;
351 break;
352
353 case ID_VDM_DUMPMEM_TXT:
354 DumpMemory(TRUE);
355 break;
356
357 case ID_VDM_DUMPMEM_BIN:
358 DumpMemory(FALSE);
359 break;
360
361 case ID_VDM_QUIT:
362 /* Stop the VDM */
363 EmulatorTerminate();
364 break;
365
366 default:
367 break;
368 }
369 }
370
371 VOID FocusEventHandler(PFOCUS_EVENT_RECORD FocusEvent)
372 {
373 DPRINT1("Focus events not handled\n");
374 }
375
376 INT
377 wmain(INT argc, WCHAR *argv[])
378 {
379 #ifdef STANDALONE
380
381 if (argc < 2)
382 {
383 wprintf(L"\nReactOS Virtual DOS Machine\n\n"
384 L"Usage: NTVDM <executable> [<parameters>]\n");
385 return 0;
386 }
387
388 #else
389
390 INT i;
391 WCHAR *endptr;
392
393 /* Parse the command line arguments */
394 for (i = 1; i < argc; i++)
395 {
396 if (wcsncmp(argv[i], L"-i", 2) == 0)
397 {
398 /* This is the session ID */
399 SessionId = wcstoul(argv[i] + 2, &endptr, 10);
400
401 /* The VDM hasn't been started from a console, so quit when the task is done */
402 AcceptCommands = FALSE;
403 }
404 }
405
406 #endif
407
408 NtVdmArgc = argc;
409 NtVdmArgv = argv;
410
411 DPRINT1("\n\n\nNTVDM - Starting...\n\n\n");
412
413 /* Create the task event */
414 VdmTaskEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
415 ASSERT(VdmTaskEvent != NULL);
416
417 /* Initialize the console */
418 if (!ConsoleInit())
419 {
420 wprintf(L"FATAL: A problem occurred when trying to initialize the console\n");
421 goto Cleanup;
422 }
423
424 /* Initialize the emulator */
425 if (!EmulatorInitialize(ConsoleInput, ConsoleOutput))
426 {
427 wprintf(L"FATAL: Failed to initialize the emulator\n");
428 goto Cleanup;
429 }
430
431 /* Initialize the system BIOS */
432 if (!BiosInitialize(NULL))
433 {
434 wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n");
435 goto Cleanup;
436 }
437
438 /* Let's go! Start simulation */
439 CpuSimulate();
440
441 Cleanup:
442 BiosCleanup();
443 EmulatorCleanup();
444 ConsoleCleanup();
445
446 #ifndef STANDALONE
447 ExitVDM(FALSE, 0);
448 #endif
449
450 /* Quit the VDM */
451 DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");
452
453 return 0;
454 }
455
456 /* EOF */