xref: /reactos/subsystems/mvdm/ntvdm/ntvdm.c (revision d5399189)
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 = PtrToUlong(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 = PtrToUlong(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 */
546