reactos/subsystems/mvdm/ntvdm/ntvdm.c

617 lines
16 KiB
C

/*
* COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
* FILE: subsystems/mvdm/ntvdm/ntvdm.c
* PURPOSE: Virtual DOS Machine
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
*/
/* INCLUDES *******************************************************************/
#include "ntvdm.h"
#define NDEBUG
#include <debug.h>
#include "emulator.h"
#include "bios/bios.h"
#include "cpu/cpu.h"
#include "dos/dem.h"
/* Extra PSDK/NDK Headers */
#include <ndk/psfuncs.h>
/* VARIABLES ******************************************************************/
NTVDM_SETTINGS GlobalSettings;
// Command line of NTVDM
INT NtVdmArgc;
WCHAR** NtVdmArgv;
/* Full directory where NTVDM resides, or the SystemRoot\System32 path */
WCHAR NtVdmPath[MAX_PATH];
ULONG NtVdmPathSize; // Length without NULL terminator.
/* PRIVATE FUNCTIONS **********************************************************/
static NTSTATUS
NTAPI
NtVdmConfigureBios(IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext)
{
PNTVDM_SETTINGS Settings = (PNTVDM_SETTINGS)Context;
UNICODE_STRING ValueString;
/* Check for the type of the value */
if (ValueType != REG_SZ)
{
RtlInitEmptyAnsiString(&Settings->BiosFileName, NULL, 0);
return STATUS_SUCCESS;
}
/* Convert the UNICODE string to ANSI and store it */
RtlInitEmptyUnicodeString(&ValueString, (PWCHAR)ValueData, ValueLength);
ValueString.Length = ValueString.MaximumLength;
RtlUnicodeStringToAnsiString(&Settings->BiosFileName, &ValueString, TRUE);
return STATUS_SUCCESS;
}
static NTSTATUS
NTAPI
NtVdmConfigureRom(IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext)
{
PNTVDM_SETTINGS Settings = (PNTVDM_SETTINGS)Context;
UNICODE_STRING ValueString;
/* Check for the type of the value */
if (ValueType != REG_MULTI_SZ)
{
RtlInitEmptyAnsiString(&Settings->RomFiles, NULL, 0);
return STATUS_SUCCESS;
}
/* Convert the UNICODE string to ANSI and store it */
RtlInitEmptyUnicodeString(&ValueString, (PWCHAR)ValueData, ValueLength);
ValueString.Length = ValueString.MaximumLength;
RtlUnicodeStringToAnsiString(&Settings->RomFiles, &ValueString, TRUE);
return STATUS_SUCCESS;
}
static NTSTATUS
NTAPI
NtVdmConfigureFloppy(IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext)
{
BOOLEAN Success;
PNTVDM_SETTINGS Settings = (PNTVDM_SETTINGS)Context;
ULONG DiskNumber = PtrToUlong(EntryContext);
ASSERT(DiskNumber < ARRAYSIZE(Settings->FloppyDisks));
/* Check whether the Hard Disk entry was not already configured */
if (Settings->FloppyDisks[DiskNumber].Buffer != NULL)
{
DPRINT1("Floppy Disk %d -- '%wZ' already configured\n", DiskNumber, &Settings->FloppyDisks[DiskNumber]);
return STATUS_SUCCESS;
}
/* Check for the type of the value */
if (ValueType != REG_SZ)
{
RtlInitEmptyUnicodeString(&Settings->FloppyDisks[DiskNumber], NULL, 0);
return STATUS_SUCCESS;
}
/* Initialize the string */
Success = RtlCreateUnicodeString(&Settings->FloppyDisks[DiskNumber], (PCWSTR)ValueData);
ASSERT(Success);
return STATUS_SUCCESS;
}
static NTSTATUS
NTAPI
NtVdmConfigureHDD(IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext)
{
BOOLEAN Success;
PNTVDM_SETTINGS Settings = (PNTVDM_SETTINGS)Context;
ULONG DiskNumber = PtrToUlong(EntryContext);
ASSERT(DiskNumber < ARRAYSIZE(Settings->HardDisks));
/* Check whether the Hard Disk entry was not already configured */
if (Settings->HardDisks[DiskNumber].Buffer != NULL)
{
DPRINT1("Hard Disk %d -- '%wZ' already configured\n", DiskNumber, &Settings->HardDisks[DiskNumber]);
return STATUS_SUCCESS;
}
/* Check for the type of the value */
if (ValueType != REG_SZ)
{
RtlInitEmptyUnicodeString(&Settings->HardDisks[DiskNumber], NULL, 0);
return STATUS_SUCCESS;
}
/* Initialize the string */
Success = RtlCreateUnicodeString(&Settings->HardDisks[DiskNumber], (PCWSTR)ValueData);
ASSERT(Success);
return STATUS_SUCCESS;
}
static RTL_QUERY_REGISTRY_TABLE
NtVdmConfigurationTable[] =
{
{
NtVdmConfigureBios,
0,
L"BiosFile",
NULL,
REG_NONE,
NULL,
0
},
{
NtVdmConfigureRom,
RTL_QUERY_REGISTRY_NOEXPAND,
L"RomFiles",
NULL,
REG_NONE,
NULL,
0
},
{
NtVdmConfigureFloppy,
0,
L"FloppyDisk0",
(PVOID)0,
REG_NONE,
NULL,
0
},
{
NtVdmConfigureFloppy,
0,
L"FloppyDisk1",
(PVOID)1,
REG_NONE,
NULL,
0
},
{
NtVdmConfigureHDD,
0,
L"HardDisk0",
(PVOID)0,
REG_NONE,
NULL,
0
},
{
NtVdmConfigureHDD,
0,
L"HardDisk1",
(PVOID)1,
REG_NONE,
NULL,
0
},
{
NtVdmConfigureHDD,
0,
L"HardDisk2",
(PVOID)2,
REG_NONE,
NULL,
0
},
{
NtVdmConfigureHDD,
0,
L"HardDisk3",
(PVOID)3,
REG_NONE,
NULL,
0
},
/* End of table */
{0}
};
static BOOL
LoadGlobalSettings(IN PNTVDM_SETTINGS Settings)
{
NTSTATUS Status;
ASSERT(Settings);
/*
* Now we can do:
* - CPU core choice
* - Video choice
* - Sound choice
* - Mem?
* - ...
* - Standalone mode?
* - Debug settings
*/
Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
L"NTVDM",
NtVdmConfigurationTable,
Settings,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("NTVDM registry settings cannot be fully initialized, using default ones. Status = 0x%08lx\n", Status);
}
return NT_SUCCESS(Status);
}
static VOID
FreeGlobalSettings(IN PNTVDM_SETTINGS Settings)
{
USHORT i;
ASSERT(Settings);
if (Settings->BiosFileName.Buffer)
RtlFreeAnsiString(&Settings->BiosFileName);
if (Settings->RomFiles.Buffer)
RtlFreeAnsiString(&Settings->RomFiles);
for (i = 0; i < ARRAYSIZE(Settings->FloppyDisks); ++i)
{
if (Settings->FloppyDisks[i].Buffer)
RtlFreeUnicodeString(&Settings->FloppyDisks[i]);
}
for (i = 0; i < ARRAYSIZE(Settings->HardDisks); ++i)
{
if (Settings->HardDisks[i].Buffer)
RtlFreeUnicodeString(&Settings->HardDisks[i]);
}
}
static VOID
ConsoleCleanup(VOID);
/** HACK!! **/
#include "./console/console.c"
/** HACK!! **/
/*static*/ VOID
VdmShutdown(BOOLEAN Immediate)
{
/*
* Immediate = TRUE: Immediate shutdown;
* FALSE: Delayed shutdown.
*/
static BOOLEAN MustShutdown = FALSE;
/* If a shutdown is ongoing, just return */
if (MustShutdown)
{
DPRINT1("Shutdown is ongoing...\n");
Sleep(INFINITE);
return;
}
/* First notify DOS to see whether we can shut down now */
MustShutdown = DosShutdown(Immediate);
/*
* In case we perform an immediate shutdown, or the DOS says
* we can shut down, do it now.
*/
MustShutdown = MustShutdown || Immediate;
if (MustShutdown)
{
EmulatorTerminate();
BiosCleanup();
EmulatorCleanup();
ConsoleCleanup();
FreeGlobalSettings(&GlobalSettings);
DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");
/* Some VDDs rely on the fact that NTVDM calls ExitProcess on Windows */
ExitProcess(0);
}
}
/* PUBLIC FUNCTIONS ***********************************************************/
VOID
DisplayMessage(IN LPCWSTR Format, ...)
{
#ifndef WIN2K_COMPLIANT
WCHAR StaticBuffer[256];
LPWSTR Buffer = StaticBuffer; // Use the static buffer by default.
#else
WCHAR Buffer[2048]; // Large enough. If not, increase it by hand.
#endif
size_t MsgLen;
va_list args;
va_start(args, Format);
#ifndef WIN2K_COMPLIANT
/*
* Retrieve the message length and if it is too long, allocate
* an auxiliary buffer; otherwise use the static buffer.
* The string is built to be NULL-terminated.
*/
MsgLen = _vscwprintf(Format, args);
if (MsgLen >= ARRAYSIZE(StaticBuffer))
{
Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, (MsgLen + 1) * sizeof(WCHAR));
if (Buffer == NULL)
{
/* Allocation failed, use the static buffer and display a suitable error message */
Buffer = StaticBuffer;
Format = L"DisplayMessage()\nOriginal message is too long and allocating an auxiliary buffer failed.";
MsgLen = wcslen(Format);
}
}
#else
MsgLen = ARRAYSIZE(Buffer) - 1;
#endif
RtlZeroMemory(Buffer, (MsgLen + 1) * sizeof(WCHAR));
_vsnwprintf(Buffer, MsgLen, Format, args);
va_end(args);
/* Display the message */
DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer);
MessageBoxW(hConsoleWnd, Buffer, L"NTVDM Subsystem", MB_OK);
#ifndef WIN2K_COMPLIANT
/* Free the buffer if needed */
if (Buffer != StaticBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
#endif
}
/*
* This function, derived from DisplayMessage, is used by the BIOS and
* the DOS to display messages to an output device. A printer function
* is given for printing the characters.
*/
VOID
PrintMessageAnsi(IN CHAR_PRINT CharPrint,
IN LPCSTR Format, ...)
{
static CHAR CurChar = 0;
LPSTR str;
#ifndef WIN2K_COMPLIANT
CHAR StaticBuffer[256];
LPSTR Buffer = StaticBuffer; // Use the static buffer by default.
#else
CHAR Buffer[2048]; // Large enough. If not, increase it by hand.
#endif
size_t MsgLen;
va_list args;
va_start(args, Format);
#ifndef WIN2K_COMPLIANT
/*
* Retrieve the message length and if it is too long, allocate
* an auxiliary buffer; otherwise use the static buffer.
* The string is built to be NULL-terminated.
*/
MsgLen = _vscprintf(Format, args);
if (MsgLen >= ARRAYSIZE(StaticBuffer))
{
Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, (MsgLen + 1) * sizeof(CHAR));
if (Buffer == NULL)
{
/* Allocation failed, use the static buffer and display a suitable error message */
Buffer = StaticBuffer;
Format = "DisplayMessageAnsi()\nOriginal message is too long and allocating an auxiliary buffer failed.";
MsgLen = strlen(Format);
}
}
#else
MsgLen = ARRAYSIZE(Buffer) - 1;
#endif
RtlZeroMemory(Buffer, (MsgLen + 1) * sizeof(CHAR));
_vsnprintf(Buffer, MsgLen, Format, args);
va_end(args);
/* Display the message */
// DPRINT1("\n\nNTVDM DOS32\n%s\n\n", Buffer);
MsgLen = strlen(Buffer);
str = Buffer;
while (MsgLen--)
{
if (*str == '\n' && CurChar != '\r')
CharPrint('\r');
CurChar = *str++;
CharPrint(CurChar);
}
#ifndef WIN2K_COMPLIANT
/* Free the buffer if needed */
if (Buffer != StaticBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
#endif
}
INT
wmain(INT argc, WCHAR *argv[])
{
BOOL Success;
#ifdef STANDALONE
if (argc < 2)
{
wprintf(L"\nReactOS Virtual DOS Machine\n\n"
L"Usage: NTVDM <executable> [<parameters>]\n");
return 0;
}
#else
/* For non-STANDALONE builds, we must be started as a VDM */
NTSTATUS Status;
ULONG VdmPower = 0;
Status = NtQueryInformationProcess(NtCurrentProcess(),
ProcessWx86Information,
&VdmPower,
sizeof(VdmPower),
NULL);
if (!NT_SUCCESS(Status) || (VdmPower == 0))
{
/* Not a VDM, bail out */
return 0;
}
#endif
NtVdmArgc = argc;
NtVdmArgv = argv;
#ifdef ADVANCED_DEBUGGING
{
INT i = 20;
printf("Waiting for debugger (10 secs)..");
while (i--)
{
printf(".");
if (IsDebuggerPresent())
{
DbgBreakPoint();
break;
}
Sleep(500);
}
printf("Continue\n");
}
#endif
DPRINT1("\n\n\n"
"NTVDM - Starting...\n"
"Command Line: '%s'\n"
"\n\n",
GetCommandLineA());
/*
* Retrieve the full directory of the current running NTVDM instance.
* In case of failure, use the default SystemRoot\System32 path.
*/
NtVdmPathSize = GetModuleFileNameW(NULL, NtVdmPath, _countof(NtVdmPath));
NtVdmPath[_countof(NtVdmPath) - 1] = UNICODE_NULL; // Ensure NULL-termination (see WinXP bug)
Success = ((NtVdmPathSize != 0) && (NtVdmPathSize < _countof(NtVdmPath)) &&
(GetLastError() != ERROR_INSUFFICIENT_BUFFER));
if (Success)
{
/* Find the last path separator, remove it as well as the file name */
PWCHAR pch = wcsrchr(NtVdmPath, L'\\');
if (pch)
*pch = UNICODE_NULL;
}
else
{
/* We failed, use the default SystemRoot\System32 path */
NtVdmPathSize = GetSystemDirectoryW(NtVdmPath, _countof(NtVdmPath));
Success = ((NtVdmPathSize != 0) && (NtVdmPathSize < _countof(NtVdmPath)));
if (!Success)
{
/* We failed again, try to do it ourselves */
NtVdmPathSize = (ULONG)wcslen(SharedUserData->NtSystemRoot) + _countof("\\System32") - 1;
Success = (NtVdmPathSize < _countof(NtVdmPath));
if (Success)
{
Success = NT_SUCCESS(RtlStringCchPrintfW(NtVdmPath,
_countof(NtVdmPath),
L"%s\\System32",
SharedUserData->NtSystemRoot));
}
if (!Success)
{
wprintf(L"FATAL: Could not retrieve NTVDM path.\n");
goto Cleanup;
}
}
}
NtVdmPathSize = (ULONG)wcslen(NtVdmPath);
/* Load the global VDM settings */
LoadGlobalSettings(&GlobalSettings);
/* Initialize the console */
if (!ConsoleInit())
{
wprintf(L"FATAL: A problem occurred when trying to initialize the console.\n");
goto Cleanup;
}
/* Initialize the emulator */
if (!EmulatorInitialize(ConsoleInput, ConsoleOutput))
{
wprintf(L"FATAL: Failed to initialize the emulator.\n");
goto Cleanup;
}
/* Initialize the system BIOS and option ROMs */
if (!BiosInitialize(GlobalSettings.BiosFileName.Buffer,
GlobalSettings.RomFiles.Buffer))
{
wprintf(L"FATAL: Failed to initialize the VDM BIOS.\n");
goto Cleanup;
}
/* Let's go! Start simulation */
CpuSimulate();
/* Quit the VDM */
Cleanup:
VdmShutdown(TRUE);
return 0;
}
/* EOF */