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>
9 /* INCLUDES *******************************************************************/
18 #include "bios/bios.h"
23 /* Extra PSDK/NDK Headers */
24 #include <ndk/psfuncs.h>
26 /* VARIABLES ******************************************************************/
28 NTVDM_SETTINGS GlobalSettings
;
30 // Command line of NTVDM
34 /* PRIVATE FUNCTIONS **********************************************************/
38 NtVdmConfigureBios(IN PWSTR ValueName
,
43 IN PVOID EntryContext
)
45 PNTVDM_SETTINGS Settings
= (PNTVDM_SETTINGS
)Context
;
46 UNICODE_STRING ValueString
;
48 /* Check for the type of the value */
49 if (ValueType
!= REG_SZ
)
51 RtlInitEmptyAnsiString(&Settings
->BiosFileName
, NULL
, 0);
52 return STATUS_SUCCESS
;
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
);
60 return STATUS_SUCCESS
;
65 NtVdmConfigureRom(IN PWSTR ValueName
,
70 IN PVOID EntryContext
)
72 PNTVDM_SETTINGS Settings
= (PNTVDM_SETTINGS
)Context
;
73 UNICODE_STRING ValueString
;
75 /* Check for the type of the value */
76 if (ValueType
!= REG_MULTI_SZ
)
78 RtlInitEmptyAnsiString(&Settings
->RomFiles
, NULL
, 0);
79 return STATUS_SUCCESS
;
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
);
87 return STATUS_SUCCESS
;
92 NtVdmConfigureFloppy(IN PWSTR ValueName
,
97 IN PVOID EntryContext
)
100 PNTVDM_SETTINGS Settings
= (PNTVDM_SETTINGS
)Context
;
101 ULONG DiskNumber
= PtrToUlong(EntryContext
);
103 ASSERT(DiskNumber
< ARRAYSIZE(Settings
->FloppyDisks
));
105 /* Check whether the Hard Disk entry was not already configured */
106 if (Settings
->FloppyDisks
[DiskNumber
].Buffer
!= NULL
)
108 DPRINT1("Floppy Disk %d -- '%wZ' already configured\n", DiskNumber
, &Settings
->FloppyDisks
[DiskNumber
]);
109 return STATUS_SUCCESS
;
112 /* Check for the type of the value */
113 if (ValueType
!= REG_SZ
)
115 RtlInitEmptyUnicodeString(&Settings
->FloppyDisks
[DiskNumber
], NULL
, 0);
116 return STATUS_SUCCESS
;
119 /* Initialize the string */
120 Success
= RtlCreateUnicodeString(&Settings
->FloppyDisks
[DiskNumber
], (PCWSTR
)ValueData
);
123 return STATUS_SUCCESS
;
128 NtVdmConfigureHDD(IN PWSTR ValueName
,
131 IN ULONG ValueLength
,
133 IN PVOID EntryContext
)
136 PNTVDM_SETTINGS Settings
= (PNTVDM_SETTINGS
)Context
;
137 ULONG DiskNumber
= PtrToUlong(EntryContext
);
139 ASSERT(DiskNumber
< ARRAYSIZE(Settings
->HardDisks
));
141 /* Check whether the Hard Disk entry was not already configured */
142 if (Settings
->HardDisks
[DiskNumber
].Buffer
!= NULL
)
144 DPRINT1("Hard Disk %d -- '%wZ' already configured\n", DiskNumber
, &Settings
->HardDisks
[DiskNumber
]);
145 return STATUS_SUCCESS
;
148 /* Check for the type of the value */
149 if (ValueType
!= REG_SZ
)
151 RtlInitEmptyUnicodeString(&Settings
->HardDisks
[DiskNumber
], NULL
, 0);
152 return STATUS_SUCCESS
;
155 /* Initialize the string */
156 Success
= RtlCreateUnicodeString(&Settings
->HardDisks
[DiskNumber
], (PCWSTR
)ValueData
);
159 return STATUS_SUCCESS
;
162 static RTL_QUERY_REGISTRY_TABLE
163 NtVdmConfigurationTable
[] =
177 RTL_QUERY_REGISTRY_NOEXPAND
,
186 NtVdmConfigureFloppy
,
196 NtVdmConfigureFloppy
,
250 LoadGlobalSettings(IN PNTVDM_SETTINGS Settings
)
266 Status
= RtlQueryRegistryValues(RTL_REGISTRY_CONTROL
,
268 NtVdmConfigurationTable
,
271 if (!NT_SUCCESS(Status
))
273 DPRINT1("NTVDM registry settings cannot be fully initialized, using default ones. Status = 0x%08lx\n", Status
);
276 return NT_SUCCESS(Status
);
280 FreeGlobalSettings(IN PNTVDM_SETTINGS Settings
)
286 if (Settings
->BiosFileName
.Buffer
)
287 RtlFreeAnsiString(&Settings
->BiosFileName
);
289 if (Settings
->RomFiles
.Buffer
)
290 RtlFreeAnsiString(&Settings
->RomFiles
);
292 for (i
= 0; i
< ARRAYSIZE(Settings
->FloppyDisks
); ++i
)
294 if (Settings
->FloppyDisks
[i
].Buffer
)
295 RtlFreeUnicodeString(&Settings
->FloppyDisks
[i
]);
298 for (i
= 0; i
< ARRAYSIZE(Settings
->HardDisks
); ++i
)
300 if (Settings
->HardDisks
[i
].Buffer
)
301 RtlFreeUnicodeString(&Settings
->HardDisks
[i
]);
306 ConsoleCleanup(VOID
);
309 #include "./console/console.c"
313 VdmShutdown(BOOLEAN Immediate
)
316 * Immediate = TRUE: Immediate shutdown;
317 * FALSE: Delayed shutdown.
319 static BOOLEAN MustShutdown
= FALSE
;
321 /* If a shutdown is ongoing, just return */
324 DPRINT1("Shutdown is ongoing...\n");
329 /* First notify DOS to see whether we can shut down now */
330 MustShutdown
= DosShutdown(Immediate
);
332 * In case we perform an immediate shutdown, or the DOS says
333 * we can shut down, do it now.
335 MustShutdown
= MustShutdown
|| Immediate
;
345 FreeGlobalSettings(&GlobalSettings
);
347 DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");
348 /* Some VDDs rely on the fact that NTVDM calls ExitProcess on Windows */
353 /* PUBLIC FUNCTIONS ***********************************************************/
356 DisplayMessage(IN LPCWSTR Format
, ...)
358 #ifndef WIN2K_COMPLIANT
359 WCHAR StaticBuffer
[256];
360 LPWSTR Buffer
= StaticBuffer
; // Use the static buffer by default.
362 WCHAR Buffer
[2048]; // Large enough. If not, increase it by hand.
367 va_start(args
, Format
);
369 #ifndef WIN2K_COMPLIANT
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.
375 MsgLen
= _vscwprintf(Format
, args
);
376 if (MsgLen
>= ARRAYSIZE(StaticBuffer
))
378 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, (MsgLen
+ 1) * sizeof(WCHAR
));
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
);
388 MsgLen
= ARRAYSIZE(Buffer
) - 1;
391 RtlZeroMemory(Buffer
, (MsgLen
+ 1) * sizeof(WCHAR
));
392 _vsnwprintf(Buffer
, MsgLen
, Format
, args
);
396 /* Display the message */
397 DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer
);
398 MessageBoxW(hConsoleWnd
, Buffer
, L
"NTVDM Subsystem", MB_OK
);
400 #ifndef WIN2K_COMPLIANT
401 /* Free the buffer if needed */
402 if (Buffer
!= StaticBuffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
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.
412 PrintMessageAnsi(IN CHAR_PRINT CharPrint
,
413 IN LPCSTR Format
, ...)
415 static CHAR CurChar
= 0;
418 #ifndef WIN2K_COMPLIANT
419 CHAR StaticBuffer
[256];
420 LPSTR Buffer
= StaticBuffer
; // Use the static buffer by default.
422 CHAR Buffer
[2048]; // Large enough. If not, increase it by hand.
427 va_start(args
, Format
);
429 #ifndef WIN2K_COMPLIANT
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.
435 MsgLen
= _vscprintf(Format
, args
);
436 if (MsgLen
>= ARRAYSIZE(StaticBuffer
))
438 Buffer
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, (MsgLen
+ 1) * sizeof(CHAR
));
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
);
448 MsgLen
= ARRAYSIZE(Buffer
) - 1;
451 RtlZeroMemory(Buffer
, (MsgLen
+ 1) * sizeof(CHAR
));
452 _vsnprintf(Buffer
, MsgLen
, Format
, args
);
456 /* Display the message */
457 // DPRINT1("\n\nNTVDM DOS32\n%s\n\n", Buffer);
459 MsgLen
= strlen(Buffer
);
463 if (*str
== '\n' && CurChar
!= '\r')
470 #ifndef WIN2K_COMPLIANT
471 /* Free the buffer if needed */
472 if (Buffer
!= StaticBuffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer
);
477 wmain(INT argc
, WCHAR
*argv
[])
483 wprintf(L
"\nReactOS Virtual DOS Machine\n\n"
484 L
"Usage: NTVDM <executable> [<parameters>]\n");
490 /* For non-STANDALONE builds, we must be started as a VDM */
493 Status
= NtQueryInformationProcess(NtCurrentProcess(),
494 ProcessWx86Information
,
498 if (!NT_SUCCESS(Status
) || (VdmPower
== 0))
500 /* Not a VDM, bail out */
509 #ifdef ADVANCED_DEBUGGING
513 printf("Waiting for debugger (10 secs)..");
517 if (IsDebuggerPresent())
524 printf("Continue\n");
529 "NTVDM - Starting...\n"
530 "Command Line: '%s'\n"
534 /* Load the global VDM settings */
535 LoadGlobalSettings(&GlobalSettings
);
537 /* Initialize the console */
540 wprintf(L
"FATAL: A problem occurred when trying to initialize the console\n");
544 /* Initialize the emulator */
545 if (!EmulatorInitialize(ConsoleInput
, ConsoleOutput
))
547 wprintf(L
"FATAL: Failed to initialize the emulator\n");
551 /* Initialize the system BIOS and option ROMs */
552 if (!BiosInitialize(GlobalSettings
.BiosFileName
.Buffer
,
553 GlobalSettings
.RomFiles
.Buffer
))
555 wprintf(L
"FATAL: Failed to initialize the VDM BIOS.\n");
559 /* Let's go! Start simulation */