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