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