bedc20ea7ea310cb3f23c9c43b4d6dd2472c2b54
[reactos.git] / 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 #include "ntvdm.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 #include "emulator.h"
17
18 #include "bios/bios.h"
19 #include "cpu/cpu.h"
20
21 #include "dos/dem.h"
22
23 /* Extra PSDK/NDK Headers */
24 #include <ndk/psfuncs.h>
25
26 /* VARIABLES ******************************************************************/
27
28 NTVDM_SETTINGS GlobalSettings;
29
30 // Command line of NTVDM
31 INT NtVdmArgc;
32 WCHAR** NtVdmArgv;
33
34 /* PRIVATE FUNCTIONS **********************************************************/
35
36 static NTSTATUS
37 NTAPI
38 NtVdmConfigureBios(IN PWSTR ValueName,
39 IN ULONG ValueType,
40 IN PVOID ValueData,
41 IN ULONG ValueLength,
42 IN PVOID Context,
43 IN PVOID EntryContext)
44 {
45 PNTVDM_SETTINGS Settings = (PNTVDM_SETTINGS)Context;
46 UNICODE_STRING ValueString;
47
48 /* Check for the type of the value */
49 if (ValueType != REG_SZ)
50 {
51 RtlInitEmptyAnsiString(&Settings->BiosFileName, NULL, 0);
52 return STATUS_SUCCESS;
53 }
54
55 /* Convert the UNICODE string to ANSI and store it */
56 RtlInitEmptyUnicodeString(&ValueString, (PWCHAR)ValueData, ValueLength);
57 ValueString.Length = ValueString.MaximumLength;
58 RtlUnicodeStringToAnsiString(&Settings->BiosFileName, &ValueString, TRUE);
59
60 return STATUS_SUCCESS;
61 }
62
63 static NTSTATUS
64 NTAPI
65 NtVdmConfigureRom(IN PWSTR ValueName,
66 IN ULONG ValueType,
67 IN PVOID ValueData,
68 IN ULONG ValueLength,
69 IN PVOID Context,
70 IN PVOID EntryContext)
71 {
72 PNTVDM_SETTINGS Settings = (PNTVDM_SETTINGS)Context;
73 UNICODE_STRING ValueString;
74
75 /* Check for the type of the value */
76 if (ValueType != REG_MULTI_SZ)
77 {
78 RtlInitEmptyAnsiString(&Settings->RomFiles, NULL, 0);
79 return STATUS_SUCCESS;
80 }
81
82 /* Convert the UNICODE string to ANSI and store it */
83 RtlInitEmptyUnicodeString(&ValueString, (PWCHAR)ValueData, ValueLength);
84 ValueString.Length = ValueString.MaximumLength;
85 RtlUnicodeStringToAnsiString(&Settings->RomFiles, &ValueString, TRUE);
86
87 return STATUS_SUCCESS;
88 }
89
90 static NTSTATUS
91 NTAPI
92 NtVdmConfigureFloppy(IN PWSTR ValueName,
93 IN ULONG ValueType,
94 IN PVOID ValueData,
95 IN ULONG ValueLength,
96 IN PVOID Context,
97 IN PVOID EntryContext)
98 {
99 BOOLEAN Success;
100 PNTVDM_SETTINGS Settings = (PNTVDM_SETTINGS)Context;
101 ULONG DiskNumber = PtrToUlong(EntryContext);
102
103 ASSERT(DiskNumber < ARRAYSIZE(Settings->FloppyDisks));
104
105 /* Check whether the Hard Disk entry was not already configured */
106 if (Settings->FloppyDisks[DiskNumber].Buffer != NULL)
107 {
108 DPRINT1("Floppy Disk %d -- '%wZ' already configured\n", DiskNumber, &Settings->FloppyDisks[DiskNumber]);
109 return STATUS_SUCCESS;
110 }
111
112 /* Check for the type of the value */
113 if (ValueType != REG_SZ)
114 {
115 RtlInitEmptyUnicodeString(&Settings->FloppyDisks[DiskNumber], NULL, 0);
116 return STATUS_SUCCESS;
117 }
118
119 /* Initialize the string */
120 Success = RtlCreateUnicodeString(&Settings->FloppyDisks[DiskNumber], (PCWSTR)ValueData);
121 ASSERT(Success);
122
123 return STATUS_SUCCESS;
124 }
125
126 static NTSTATUS
127 NTAPI
128 NtVdmConfigureHDD(IN PWSTR ValueName,
129 IN ULONG ValueType,
130 IN PVOID ValueData,
131 IN ULONG ValueLength,
132 IN PVOID Context,
133 IN PVOID EntryContext)
134 {
135 BOOLEAN Success;
136 PNTVDM_SETTINGS Settings = (PNTVDM_SETTINGS)Context;
137 ULONG DiskNumber = PtrToUlong(EntryContext);
138
139 ASSERT(DiskNumber < ARRAYSIZE(Settings->HardDisks));
140
141 /* Check whether the Hard Disk entry was not already configured */
142 if (Settings->HardDisks[DiskNumber].Buffer != NULL)
143 {
144 DPRINT1("Hard Disk %d -- '%wZ' already configured\n", DiskNumber, &Settings->HardDisks[DiskNumber]);
145 return STATUS_SUCCESS;
146 }
147
148 /* Check for the type of the value */
149 if (ValueType != REG_SZ)
150 {
151 RtlInitEmptyUnicodeString(&Settings->HardDisks[DiskNumber], NULL, 0);
152 return STATUS_SUCCESS;
153 }
154
155 /* Initialize the string */
156 Success = RtlCreateUnicodeString(&Settings->HardDisks[DiskNumber], (PCWSTR)ValueData);
157 ASSERT(Success);
158
159 return STATUS_SUCCESS;
160 }
161
162 static RTL_QUERY_REGISTRY_TABLE
163 NtVdmConfigurationTable[] =
164 {
165 {
166 NtVdmConfigureBios,
167 0,
168 L"BiosFile",
169 NULL,
170 REG_NONE,
171 NULL,
172 0
173 },
174
175 {
176 NtVdmConfigureRom,
177 RTL_QUERY_REGISTRY_NOEXPAND,
178 L"RomFiles",
179 NULL,
180 REG_NONE,
181 NULL,
182 0
183 },
184
185 {
186 NtVdmConfigureFloppy,
187 0,
188 L"FloppyDisk0",
189 (PVOID)0,
190 REG_NONE,
191 NULL,
192 0
193 },
194
195 {
196 NtVdmConfigureFloppy,
197 0,
198 L"FloppyDisk1",
199 (PVOID)1,
200 REG_NONE,
201 NULL,
202 0
203 },
204
205 {
206 NtVdmConfigureHDD,
207 0,
208 L"HardDisk0",
209 (PVOID)0,
210 REG_NONE,
211 NULL,
212 0
213 },
214
215 {
216 NtVdmConfigureHDD,
217 0,
218 L"HardDisk1",
219 (PVOID)1,
220 REG_NONE,
221 NULL,
222 0
223 },
224
225 {
226 NtVdmConfigureHDD,
227 0,
228 L"HardDisk2",
229 (PVOID)2,
230 REG_NONE,
231 NULL,
232 0
233 },
234
235 {
236 NtVdmConfigureHDD,
237 0,
238 L"HardDisk3",
239 (PVOID)3,
240 REG_NONE,
241 NULL,
242 0
243 },
244
245 /* End of table */
246 {0}
247 };
248
249 static BOOL
250 LoadGlobalSettings(IN PNTVDM_SETTINGS Settings)
251 {
252 NTSTATUS Status;
253
254 ASSERT(Settings);
255
256 /*
257 * Now we can do:
258 * - CPU core choice
259 * - Video choice
260 * - Sound choice
261 * - Mem?
262 * - ...
263 * - Standalone mode?
264 * - Debug settings
265 */
266 Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
267 L"NTVDM",
268 NtVdmConfigurationTable,
269 Settings,
270 NULL);
271 if (!NT_SUCCESS(Status))
272 {
273 DPRINT1("NTVDM registry settings cannot be fully initialized, using default ones. Status = 0x%08lx\n", Status);
274 }
275
276 return NT_SUCCESS(Status);
277 }
278
279 static VOID
280 FreeGlobalSettings(IN PNTVDM_SETTINGS Settings)
281 {
282 USHORT i;
283
284 ASSERT(Settings);
285
286 if (Settings->BiosFileName.Buffer)
287 RtlFreeAnsiString(&Settings->BiosFileName);
288
289 if (Settings->RomFiles.Buffer)
290 RtlFreeAnsiString(&Settings->RomFiles);
291
292 for (i = 0; i < ARRAYSIZE(Settings->FloppyDisks); ++i)
293 {
294 if (Settings->FloppyDisks[i].Buffer)
295 RtlFreeUnicodeString(&Settings->FloppyDisks[i]);
296 }
297
298 for (i = 0; i < ARRAYSIZE(Settings->HardDisks); ++i)
299 {
300 if (Settings->HardDisks[i].Buffer)
301 RtlFreeUnicodeString(&Settings->HardDisks[i]);
302 }
303 }
304
305 static VOID
306 ConsoleCleanup(VOID);
307
308 /** HACK!! **/
309 #include "./console/console.c"
310 /** HACK!! **/
311
312 /*static*/ VOID
313 VdmShutdown(BOOLEAN Immediate)
314 {
315 /*
316 * Immediate = TRUE: Immediate shutdown;
317 * FALSE: Delayed shutdown.
318 */
319 static BOOLEAN MustShutdown = FALSE;
320
321 /* If a shutdown is ongoing, just return */
322 if (MustShutdown)
323 {
324 DPRINT1("Shutdown is ongoing...\n");
325 Sleep(INFINITE);
326 return;
327 }
328
329 /* First notify DOS to see whether we can shut down now */
330 MustShutdown = DosShutdown(Immediate);
331 /*
332 * In case we perform an immediate shutdown, or the DOS says
333 * we can shut down, do it now.
334 */
335 MustShutdown = MustShutdown || Immediate;
336
337 if (MustShutdown)
338 {
339 EmulatorTerminate();
340
341 BiosCleanup();
342 EmulatorCleanup();
343 ConsoleCleanup();
344
345 FreeGlobalSettings(&GlobalSettings);
346
347 DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");
348 /* Some VDDs rely on the fact that NTVDM calls ExitProcess on Windows */
349 ExitProcess(0);
350 }
351 }
352
353 /* PUBLIC FUNCTIONS ***********************************************************/
354
355 VOID
356 DisplayMessage(IN LPCWSTR Format, ...)
357 {
358 #ifndef WIN2K_COMPLIANT
359 WCHAR StaticBuffer[256];
360 LPWSTR Buffer = StaticBuffer; // Use the static buffer by default.
361 #else
362 WCHAR Buffer[2048]; // Large enough. If not, increase it by hand.
363 #endif
364 size_t MsgLen;
365 va_list args;
366
367 va_start(args, Format);
368
369 #ifndef WIN2K_COMPLIANT
370 /*
371 * Retrieve the message length and if it is too long, allocate
372 * an auxiliary buffer; otherwise use the static buffer.
373 * The string is built to be NULL-terminated.
374 */
375 MsgLen = _vscwprintf(Format, args);
376 if (MsgLen >= ARRAYSIZE(StaticBuffer))
377 {
378 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, (MsgLen + 1) * sizeof(WCHAR));
379 if (Buffer == NULL)
380 {
381 /* Allocation failed, use the static buffer and display a suitable error message */
382 Buffer = StaticBuffer;
383 Format = L"DisplayMessage()\nOriginal message is too long and allocating an auxiliary buffer failed.";
384 MsgLen = wcslen(Format);
385 }
386 }
387 #else
388 MsgLen = ARRAYSIZE(Buffer) - 1;
389 #endif
390
391 RtlZeroMemory(Buffer, (MsgLen + 1) * sizeof(WCHAR));
392 _vsnwprintf(Buffer, MsgLen, Format, args);
393
394 va_end(args);
395
396 /* Display the message */
397 DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer);
398 MessageBoxW(hConsoleWnd, Buffer, L"NTVDM Subsystem", MB_OK);
399
400 #ifndef WIN2K_COMPLIANT
401 /* Free the buffer if needed */
402 if (Buffer != StaticBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
403 #endif
404 }
405
406 /*
407 * This function, derived from DisplayMessage, is used by the BIOS and
408 * the DOS to display messages to an output device. A printer function
409 * is given for printing the characters.
410 */
411 VOID
412 PrintMessageAnsi(IN CHAR_PRINT CharPrint,
413 IN LPCSTR Format, ...)
414 {
415 static CHAR CurChar = 0;
416 LPSTR str;
417
418 #ifndef WIN2K_COMPLIANT
419 CHAR StaticBuffer[256];
420 LPSTR Buffer = StaticBuffer; // Use the static buffer by default.
421 #else
422 CHAR Buffer[2048]; // Large enough. If not, increase it by hand.
423 #endif
424 size_t MsgLen;
425 va_list args;
426
427 va_start(args, Format);
428
429 #ifndef WIN2K_COMPLIANT
430 /*
431 * Retrieve the message length and if it is too long, allocate
432 * an auxiliary buffer; otherwise use the static buffer.
433 * The string is built to be NULL-terminated.
434 */
435 MsgLen = _vscprintf(Format, args);
436 if (MsgLen >= ARRAYSIZE(StaticBuffer))
437 {
438 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, (MsgLen + 1) * sizeof(CHAR));
439 if (Buffer == NULL)
440 {
441 /* Allocation failed, use the static buffer and display a suitable error message */
442 Buffer = StaticBuffer;
443 Format = "DisplayMessageAnsi()\nOriginal message is too long and allocating an auxiliary buffer failed.";
444 MsgLen = strlen(Format);
445 }
446 }
447 #else
448 MsgLen = ARRAYSIZE(Buffer) - 1;
449 #endif
450
451 RtlZeroMemory(Buffer, (MsgLen + 1) * sizeof(CHAR));
452 _vsnprintf(Buffer, MsgLen, Format, args);
453
454 va_end(args);
455
456 /* Display the message */
457 // DPRINT1("\n\nNTVDM DOS32\n%s\n\n", Buffer);
458
459 MsgLen = strlen(Buffer);
460 str = Buffer;
461 while (MsgLen--)
462 {
463 if (*str == '\n' && CurChar != '\r')
464 CharPrint('\r');
465
466 CurChar = *str++;
467 CharPrint(CurChar);
468 }
469
470 #ifndef WIN2K_COMPLIANT
471 /* Free the buffer if needed */
472 if (Buffer != StaticBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
473 #endif
474 }
475
476 INT
477 wmain(INT argc, WCHAR *argv[])
478 {
479 #ifdef STANDALONE
480
481 if (argc < 2)
482 {
483 wprintf(L"\nReactOS Virtual DOS Machine\n\n"
484 L"Usage: NTVDM <executable> [<parameters>]\n");
485 return 0;
486 }
487
488 #else
489
490 /* For non-STANDALONE builds, we must be started as a VDM */
491 NTSTATUS Status;
492 ULONG VdmPower = 0;
493 Status = NtQueryInformationProcess(NtCurrentProcess(),
494 ProcessWx86Information,
495 &VdmPower,
496 sizeof(VdmPower),
497 NULL);
498 if (!NT_SUCCESS(Status) || (VdmPower == 0))
499 {
500 /* Not a VDM, bail out */
501 return 0;
502 }
503
504 #endif
505
506 NtVdmArgc = argc;
507 NtVdmArgv = argv;
508
509 #ifdef ADVANCED_DEBUGGING
510 {
511 INT i = 20;
512
513 printf("Waiting for debugger (10 secs)..");
514 while (i--)
515 {
516 printf(".");
517 if (IsDebuggerPresent())
518 {
519 DbgBreakPoint();
520 break;
521 }
522 Sleep(500);
523 }
524 printf("Continue\n");
525 }
526 #endif
527
528 DPRINT1("\n\n\n"
529 "NTVDM - Starting...\n"
530 "Command Line: '%s'\n"
531 "\n\n",
532 GetCommandLineA());
533
534 /* Load the global VDM settings */
535 LoadGlobalSettings(&GlobalSettings);
536
537 /* Initialize the console */
538 if (!ConsoleInit())
539 {
540 wprintf(L"FATAL: A problem occurred when trying to initialize the console\n");
541 goto Cleanup;
542 }
543
544 /* Initialize the emulator */
545 if (!EmulatorInitialize(ConsoleInput, ConsoleOutput))
546 {
547 wprintf(L"FATAL: Failed to initialize the emulator\n");
548 goto Cleanup;
549 }
550
551 /* Initialize the system BIOS and option ROMs */
552 if (!BiosInitialize(GlobalSettings.BiosFileName.Buffer,
553 GlobalSettings.RomFiles.Buffer))
554 {
555 wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n");
556 goto Cleanup;
557 }
558
559 /* Let's go! Start simulation */
560 CpuSimulate();
561
562 /* Quit the VDM */
563 Cleanup:
564 VdmShutdown(TRUE);
565 return 0;
566 }
567
568 /* EOF */