1 /* 2 * COPYRIGHT: GPL - See COPYING in the top level directory 3 * PROJECT: ReactOS Virtual DOS Machine 4 * FILE: subsystems/mvdm/ntvdm/dos/dem.c 5 * PURPOSE: DOS 32-bit Emulation Support Library - 6 * This library is used by the built-in NTVDM DOS32 and by 7 * the NT 16-bit DOS in Windows (via BOPs). It also exposes 8 * exported functions that can be used by VDDs. 9 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org> 10 * Hermes Belusca-Maito (hermes.belusca@sfr.fr) 11 */ 12 13 /* INCLUDES *******************************************************************/ 14 15 #include "ntvdm.h" 16 17 #define NDEBUG 18 #include <debug.h> 19 20 #include "emulator.h" 21 #include <isvbop.h> 22 23 #include "utils.h" 24 25 #include "dem.h" 26 #include "dos/dos32krnl/device.h" 27 #include "dos/dos32krnl/memory.h" 28 #include "dos/dos32krnl/process.h" 29 #include "cpu/bop.h" 30 #include "cpu/cpu.h" 31 32 #include "bios/bios.h" 33 #include "mouse32.h" 34 35 #include "vddsup.h" 36 37 /* 38 * EXPERIMENTAL! 39 * Activate this line if you want to have COMMAND.COM completely external. 40 */ 41 // #define COMSPEC_FULLY_EXTERNAL 42 43 /* PRIVATE VARIABLES **********************************************************/ 44 45 /* PRIVATE FUNCTIONS **********************************************************/ 46 47 /* PUBLIC VARIABLES ***********************************************************/ 48 49 /* PUBLIC FUNCTIONS ***********************************************************/ 50 51 52 /******************************************************************************\ 53 |** DOS DEM Kernel helpers **| 54 \******************************************************************************/ 55 56 57 VOID Dem_BiosCharPrint(CHAR Character) 58 { 59 /* Save AX and BX */ 60 USHORT AX = getAX(); 61 USHORT BX = getBX(); 62 63 /* 64 * Set the parameters: 65 * AL contains the character to print, 66 * BL contains the character attribute, 67 * BH contains the video page to use. 68 */ 69 setAL(Character); 70 setBL(DEFAULT_ATTRIBUTE); 71 setBH(Bda->VideoPage); 72 73 /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */ 74 setAH(0x0E); 75 Int32Call(&DosContext, BIOS_VIDEO_INTERRUPT); 76 77 /* Restore AX and BX */ 78 setBX(BX); 79 setAX(AX); 80 } 81 82 VOID DosCharPrint(CHAR Character) 83 { 84 DosPrintCharacter(DOS_OUTPUT_HANDLE, Character); 85 } 86 87 88 static VOID DemLoadNTDOSKernel(VOID) 89 { 90 BOOLEAN Success = FALSE; 91 LPCSTR DosKernelFileName = "ntdos.sys"; 92 HANDLE hDosKernel; 93 ULONG ulDosKernelSize = 0; 94 95 DPRINT1("You are loading Windows NT DOS!\n"); 96 97 /* Open the DOS kernel file */ 98 hDosKernel = FileOpen(DosKernelFileName, &ulDosKernelSize); 99 if (hDosKernel == NULL) goto Quit; 100 101 /* 102 * Attempt to load the DOS kernel into memory. 103 * The segment where to load the DOS kernel is defined 104 * by the DOS BIOS and is found in DI:0000 . 105 */ 106 Success = FileLoadByHandle(hDosKernel, 107 REAL_TO_PHYS(TO_LINEAR(getDI(), 0x0000)), 108 ulDosKernelSize, 109 &ulDosKernelSize); 110 111 DPRINT1("Windows NT DOS file '%s' loading %s at %04X:%04X, size 0x%X (Error: %u).\n", 112 DosKernelFileName, 113 (Success ? "succeeded" : "failed"), 114 getDI(), 0x0000, 115 ulDosKernelSize, 116 GetLastError()); 117 118 /* Close the DOS kernel file */ 119 FileClose(hDosKernel); 120 121 Quit: 122 if (!Success) 123 { 124 /* We failed everything, stop the VDM */ 125 BiosDisplayMessage("Windows NT DOS kernel file '%s' loading failed (Error: %u). The VDM will shut down.\n", 126 DosKernelFileName, GetLastError()); 127 EmulatorTerminate(); 128 return; 129 } 130 } 131 132 static VOID WINAPI DosSystemBop(LPWORD Stack) 133 { 134 /* Get the Function Number and skip it */ 135 BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP()); 136 setIP(getIP() + 1); 137 138 switch (FuncNum) 139 { 140 /* Load the DOS kernel */ 141 case 0x11: 142 { 143 DemLoadNTDOSKernel(); 144 break; 145 } 146 147 /* Call 32-bit Driver Strategy Routine */ 148 case BOP_DRV_STRATEGY: 149 { 150 DeviceStrategyBop(); 151 break; 152 } 153 154 /* Call 32-bit Driver Interrupt Routine */ 155 case BOP_DRV_INTERRUPT: 156 { 157 DeviceInterruptBop(); 158 break; 159 } 160 161 default: 162 { 163 DPRINT1("Unknown DOS System BOP Function: 0x%02X\n", FuncNum); 164 // setCF(1); // Disable, otherwise we enter an infinite loop 165 break; 166 } 167 } 168 } 169 170 171 172 173 /******************************************************************************\ 174 |** DOS Command Process management **| 175 \******************************************************************************/ 176 177 178 #ifndef STANDALONE 179 static ULONG SessionId = 0; 180 181 /* 182 * 16-bit Command Interpreter information for DOS reentry 183 */ 184 typedef struct _COMSPEC_INFO 185 { 186 LIST_ENTRY Entry; 187 DWORD dwExitCode; 188 WORD ComSpecPsp; 189 BOOLEAN Terminated; 190 } COMSPEC_INFO, *PCOMSPEC_INFO; 191 192 static COMSPEC_INFO RootCmd; 193 static DWORD ReentrancyCount = 0; 194 195 // FIXME: Should we need list locking? 196 static LIST_ENTRY ComSpecInfoList = { &ComSpecInfoList, &ComSpecInfoList }; 197 198 static PCOMSPEC_INFO 199 FindComSpecInfoByPsp(WORD Psp) 200 { 201 PLIST_ENTRY Pointer; 202 PCOMSPEC_INFO ComSpecInfo; 203 204 for (Pointer = ComSpecInfoList.Flink; Pointer != &ComSpecInfoList; Pointer = Pointer->Flink) 205 { 206 ComSpecInfo = CONTAINING_RECORD(Pointer, COMSPEC_INFO, Entry); 207 if (ComSpecInfo->ComSpecPsp == Psp) return ComSpecInfo; 208 } 209 210 return NULL; 211 } 212 213 static VOID 214 InsertComSpecInfo(PCOMSPEC_INFO ComSpecInfo) 215 { 216 InsertHeadList(&ComSpecInfoList, &ComSpecInfo->Entry); 217 } 218 219 static VOID 220 RemoveComSpecInfo(PCOMSPEC_INFO ComSpecInfo) 221 { 222 RemoveEntryList(&ComSpecInfo->Entry); 223 if (ComSpecInfo != &RootCmd) 224 RtlFreeHeap(RtlGetProcessHeap(), 0, ComSpecInfo); 225 } 226 #endif 227 228 static VOID DosProcessConsoleAttach(VOID) 229 { 230 /* Attach to the console */ 231 ConsoleAttach(); 232 VidBiosAttachToConsole(); 233 } 234 235 static VOID DosProcessConsoleDetach(VOID) 236 { 237 /* Detach from the console */ 238 VidBiosDetachFromConsole(); 239 ConsoleDetach(); 240 } 241 242 /* 243 * Data for the next DOS command to run 244 */ 245 #ifndef STANDALONE 246 static VDM_COMMAND_INFO CommandInfo; 247 static BOOLEAN Repeat = FALSE; 248 static BOOLEAN Reentry = FALSE; 249 #endif 250 static BOOLEAN First = TRUE; 251 static CHAR CmdLine[MAX_PATH] = ""; // DOS_CMDLINE_LENGTH 252 static CHAR AppName[MAX_PATH] = ""; 253 #ifndef STANDALONE 254 static CHAR PifFile[MAX_PATH] = ""; 255 static CHAR CurDirectory[MAX_PATH] = ""; 256 static CHAR Desktop[MAX_PATH] = ""; 257 static CHAR Title[MAX_PATH] = ""; 258 static ULONG EnvSize = 256; 259 static PVOID Env = NULL; 260 #endif 261 262 #pragma pack(push, 2) 263 264 /* 265 * This structure is compatible with Windows NT DOS 266 */ 267 typedef struct _NEXT_CMD 268 { 269 USHORT EnvBlockSeg; 270 USHORT EnvBlockLen; 271 USHORT CurDrive; 272 USHORT NumDrives; 273 USHORT CmdLineSeg; 274 USHORT CmdLineOff; 275 USHORT Unknown0; 276 USHORT ExitCode; 277 USHORT Unknown1; 278 ULONG Unknown2; 279 USHORT CodePage; 280 USHORT Unknown3; 281 USHORT Unknown4; 282 USHORT AppNameSeg; 283 USHORT AppNameOff; 284 USHORT AppNameLen; 285 USHORT Flags; 286 } NEXT_CMD, *PNEXT_CMD; 287 288 #pragma pack(pop) 289 290 static VOID CmdStartProcess(VOID) 291 { 292 #ifndef STANDALONE 293 PCOMSPEC_INFO ComSpecInfo; 294 #endif 295 SIZE_T CmdLen; 296 PNEXT_CMD DataStruct = (PNEXT_CMD)SEG_OFF_TO_PTR(getDS(), getDX()); 297 298 DPRINT1("CmdStartProcess -- DS:DX = %04X:%04X (DataStruct = 0x%p)\n", 299 getDS(), getDX(), DataStruct); 300 301 /* Pause the VM */ 302 EmulatorPause(); 303 304 #ifndef STANDALONE 305 /* Check whether we need to shell out now in case we were started by a 32-bit app */ 306 ComSpecInfo = FindComSpecInfoByPsp(Sda->CurrentPsp); 307 if (ComSpecInfo && ComSpecInfo->Terminated) 308 { 309 RemoveComSpecInfo(ComSpecInfo); 310 311 DPRINT1("Exit DOS from start-app BOP\n"); 312 setCF(1); 313 goto Quit; 314 } 315 316 /* Clear the structure */ 317 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo)); 318 319 /* Initialize the structure members */ 320 CommandInfo.TaskId = SessionId; 321 CommandInfo.VDMState = VDM_FLAG_DOS; 322 CommandInfo.CmdLine = CmdLine; 323 CommandInfo.CmdLen = sizeof(CmdLine); 324 CommandInfo.AppName = AppName; 325 CommandInfo.AppLen = sizeof(AppName); 326 CommandInfo.PifFile = PifFile; 327 CommandInfo.PifLen = sizeof(PifFile); 328 CommandInfo.CurDirectory = CurDirectory; 329 CommandInfo.CurDirectoryLen = sizeof(CurDirectory); 330 CommandInfo.Desktop = Desktop; 331 CommandInfo.DesktopLen = sizeof(Desktop); 332 CommandInfo.Title = Title; 333 CommandInfo.TitleLen = sizeof(Title); 334 CommandInfo.Env = Env; 335 CommandInfo.EnvLen = EnvSize; 336 337 if (First) CommandInfo.VDMState |= VDM_FLAG_FIRST_TASK; 338 339 Command: 340 341 if (Repeat) CommandInfo.VDMState |= VDM_FLAG_RETRY; 342 Repeat = FALSE; 343 344 /* Get the VDM command information */ 345 DPRINT1("Calling GetNextVDMCommand in CmdStartProcess: wait for new VDM task...\n"); 346 if (!GetNextVDMCommand(&CommandInfo)) 347 { 348 DPRINT1("CmdStartProcess - GetNextVDMCommand failed, retrying... last error = %d\n", GetLastError()); 349 if (CommandInfo.EnvLen > EnvSize) 350 { 351 /* Expand the environment size */ 352 EnvSize = CommandInfo.EnvLen; 353 CommandInfo.Env = Env = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Env, EnvSize); 354 355 /* Repeat the request */ 356 Repeat = TRUE; 357 goto Command; 358 } 359 360 /* Shouldn't happen */ 361 DisplayMessage(L"An unrecoverable failure happened from start-app BOP; exiting DOS."); 362 setCF(1); 363 goto Quit; 364 } 365 366 // FIXME: What happens if some other 32-bit app is killed while we are waiting there?? 367 368 DPRINT1("CmdStartProcess - GetNextVDMCommand succeeded, start app...\n"); 369 370 #else 371 372 if (!First) 373 { 374 DPRINT1("Exit DOS from start-app BOP\n"); 375 setCF(1); 376 goto Quit; 377 } 378 379 #endif 380 381 /* Compute the command line length, not counting the terminating "\r\n" */ 382 CmdLen = strlen(CmdLine); 383 if (CmdLen >= 2 && CmdLine[CmdLen - 2] == '\r') 384 CmdLen -= 2; 385 386 DPRINT1("Starting '%s' ('%.*s')...\n", AppName, CmdLen, CmdLine); 387 388 /* Start the process */ 389 // FIXME: Merge 'Env' with the master environment SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0) 390 // FIXME: Environment 391 RtlCopyMemory(SEG_OFF_TO_PTR(DataStruct->AppNameSeg, DataStruct->AppNameOff), AppName, MAX_PATH); 392 *(PBYTE)(SEG_OFF_TO_PTR(DataStruct->CmdLineSeg, DataStruct->CmdLineOff)) = (BYTE)CmdLen; 393 RtlCopyMemory(SEG_OFF_TO_PTR(DataStruct->CmdLineSeg, DataStruct->CmdLineOff + 1), CmdLine, DOS_CMDLINE_LENGTH); 394 395 #ifndef STANDALONE 396 /* Update console title if we run in a separate console */ 397 if (SessionId != 0) 398 SetConsoleTitleA(AppName); 399 #endif 400 401 First = FALSE; 402 setCF(0); 403 404 DPRINT1("App started!\n"); 405 406 Quit: 407 /* Resume the VM */ 408 EmulatorResume(); 409 } 410 411 static VOID CmdStartExternalCommand(VOID) 412 { 413 DWORD Result; 414 415 // TODO: improve: this code has strong similarities 416 // with the 'default' case of DosCreateProcess. 417 418 LPSTR Command = (LPSTR)SEG_OFF_TO_PTR(getDS(), getSI()); 419 CHAR CmdLine[sizeof("cmd.exe /c ") + DOS_CMDLINE_LENGTH + 1] = ""; 420 LPSTR CmdLinePtr; 421 SIZE_T CmdLineLen; 422 423 /* Spawn a user-defined 32-bit command preprocessor */ 424 425 // FIXME: Use COMSPEC env var!! 426 CmdLinePtr = CmdLine; 427 strcpy(CmdLinePtr, "cmd.exe /c "); 428 CmdLinePtr += strlen(CmdLinePtr); 429 430 /* Build a Win32-compatible command-line */ 431 CmdLineLen = min(strlen(Command), sizeof(CmdLine) - strlen(CmdLinePtr) - 1); 432 RtlCopyMemory(CmdLinePtr, Command, CmdLineLen); 433 CmdLinePtr[CmdLineLen] = '\0'; 434 435 /* Remove any trailing return carriage character and NULL-terminate the command line */ 436 while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++; 437 *CmdLinePtr = '\0'; 438 439 DPRINT1("CMD Run Command '%s' ('%s')\n", Command, CmdLine); 440 441 /* 442 * No need to prepare the stack for DosStartComSpec since we won't start it. 443 */ 444 Result = DosStartProcess32(Command, CmdLine, 445 SEG_OFF_TO_PTR(getES(), 0) /*Environment*/, 446 MAKELONG(getIP(), getCS()) /*ReturnAddress*/, 447 FALSE); 448 if (Result != ERROR_SUCCESS) 449 { 450 DosDisplayMessage("Failed to start command '%s' ('%s'). Error: %u\n", Command, CmdLine, Result); 451 setCF(0); 452 setAL((UCHAR)Result); 453 } 454 else 455 { 456 DosDisplayMessage("Command '%s' ('%s') started successfully.\n", Command, CmdLine); 457 #ifndef STANDALONE 458 setCF(Repeat); // Set CF if we need to start a 16-bit process 459 #else 460 setCF(0); 461 #endif 462 } 463 } 464 465 static VOID CmdStartComSpec32(VOID) 466 { 467 DWORD Result; 468 469 // TODO: improve: this code has strong similarities with the 470 // 'default' case of DosCreateProcess and with the 'case 0x08'. 471 472 CHAR CmdLine[sizeof("cmd.exe") + 1] = ""; 473 474 /* Spawn a user-defined 32-bit command preprocessor */ 475 476 // FIXME: Use COMSPEC env var!! 477 strcpy(CmdLine, "cmd.exe"); 478 479 DPRINT1("CMD Run 32-bit Command Interpreter '%s'\n", CmdLine); 480 481 /* 482 * No need to prepare the stack for DosStartComSpec since we won't start it. 483 */ 484 Result = DosStartProcess32(CmdLine, CmdLine, 485 SEG_OFF_TO_PTR(getES(), 0) /*Environment*/, 486 MAKELONG(getIP(), getCS()) /*ReturnAddress*/, 487 FALSE); 488 if (Result != ERROR_SUCCESS) 489 { 490 DosDisplayMessage("Failed to start 32-bit Command Interpreter '%s'. Error: %u\n", CmdLine, Result); 491 setCF(0); 492 setAL((UCHAR)Result); 493 } 494 else 495 { 496 DosDisplayMessage("32-bit Command Interpreter '%s' started successfully.\n", CmdLine); 497 #ifndef STANDALONE 498 setCF(Repeat); // Set CF if we need to start a 16-bit process 499 #else 500 setCF(0); 501 #endif 502 } 503 } 504 505 static VOID CmdSetExitCode(VOID) 506 { 507 #ifndef STANDALONE 508 BOOL Success; 509 PCOMSPEC_INFO ComSpecInfo; 510 VDM_COMMAND_INFO CommandInfo; 511 #endif 512 513 /* Pause the VM */ 514 EmulatorPause(); 515 516 #ifndef STANDALONE 517 /* 518 * Check whether we need to shell out now in case we were started by a 32-bit app, 519 * or we were started alone along with the root 32-bit app. 520 */ 521 ComSpecInfo = FindComSpecInfoByPsp(Sda->CurrentPsp); 522 if ((ComSpecInfo && ComSpecInfo->Terminated) || 523 (ComSpecInfo == &RootCmd && SessionId != 0)) 524 { 525 RemoveComSpecInfo(ComSpecInfo); 526 #endif 527 DPRINT1("Exit DOS from ExitCode (prologue)!\n"); 528 setCF(0); 529 goto Quit; 530 #ifndef STANDALONE 531 } 532 533 /* Clear the VDM structure */ 534 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo)); 535 536 Retry: 537 /* Update the VDM state of the task */ 538 // CommandInfo.TaskId = SessionId; 539 CommandInfo.ExitCode = getDX(); 540 CommandInfo.VDMState = VDM_FLAG_DONT_WAIT; 541 DPRINT1("Calling GetNextVDMCommand 32bit end of VDM task\n"); 542 Success = GetNextVDMCommand(&CommandInfo); 543 DPRINT1("GetNextVDMCommand 32bit end of VDM task success = %s, last error = %d\n", Success ? "true" : "false", GetLastError()); 544 545 /* 546 * Check whether we were awaited because the 32-bit process was stopped, 547 * or because it started a new DOS application. 548 */ 549 if (CommandInfo.CmdLen != 0 || CommandInfo.AppLen != 0 || CommandInfo.PifLen != 0) 550 { 551 DPRINT1("GetNextVDMCommand end-of-app, this is for a new VDM task - CmdLen = %d, AppLen = %d, PifLen = %d\n", 552 CommandInfo.CmdLen, CommandInfo.AppLen, CommandInfo.PifLen); 553 554 /* Repeat the request */ 555 Repeat = TRUE; 556 setCF(1); 557 } 558 else 559 { 560 DPRINT1("GetNextVDMCommand end-of-app, the app stopped\n"); 561 562 /* Check whether we need to shell out now in case we were started by a 32-bit app */ 563 ComSpecInfo = FindComSpecInfoByPsp(Sda->CurrentPsp); 564 if (!ComSpecInfo || !ComSpecInfo->Terminated) 565 { 566 DPRINT1("Not our 32-bit app, retrying...\n"); 567 goto Retry; 568 } 569 570 ASSERT(ComSpecInfo->Terminated == TRUE); 571 572 /* Record found, remove it and exit now */ 573 RemoveComSpecInfo(ComSpecInfo); 574 575 DPRINT1("Exit DOS from ExitCode wait!\n"); 576 setCF(0); 577 } 578 #endif 579 580 // FIXME: Use the retrieved exit code as the value of our exit code 581 // when COMMAND.COM will shell-out ?? 582 583 Quit: 584 /* Resume the VM */ 585 EmulatorResume(); 586 } 587 588 static VOID WINAPI DosCmdInterpreterBop(LPWORD Stack) 589 { 590 /* Get the Function Number and skip it */ 591 BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP()); 592 setIP(getIP() + 1); 593 594 switch (FuncNum) 595 { 596 /* Kill the VDM */ 597 case 0x00: 598 { 599 /* Stop the VDM */ 600 EmulatorTerminate(); 601 return; 602 } 603 604 /* 605 * Get a new app to start 606 * 607 * Input 608 * DS:DX : Data block. 609 * 610 * Output 611 * CF : 0: Success; 1: Failure. 612 */ 613 case 0x01: 614 { 615 CmdStartProcess(); 616 break; 617 } 618 619 /* 620 * Check binary format 621 * 622 * Input 623 * DS:DX : Program to check. 624 * 625 * Output 626 * CF : 0: Success; 1: Failure. 627 * AX : Error code. 628 */ 629 case 0x07: 630 { 631 DWORD BinaryType; 632 LPSTR ProgramName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX()); 633 634 if (!GetBinaryTypeA(ProgramName, &BinaryType)) 635 { 636 /* An error happened, bail out */ 637 setCF(1); 638 setAX(LOWORD(GetLastError())); 639 break; 640 } 641 642 // FIXME: We only support DOS binaries for now... 643 ASSERT(BinaryType == SCS_DOS_BINARY); 644 if (BinaryType != SCS_DOS_BINARY) 645 { 646 /* An error happened, bail out */ 647 setCF(1); 648 setAX(LOWORD(ERROR_BAD_EXE_FORMAT)); 649 break; 650 } 651 652 /* Return success: DOS application */ 653 setCF(0); 654 break; 655 } 656 657 /* 658 * Start an external command 659 * 660 * Input 661 * DS:SI : Command to start. 662 * ES : Environment block segment. 663 * AL : Current drive number. 664 * AH : 0: Directly start the command; 665 * 1: Use "cmd.exe /c" to start the command. 666 * 667 * Output 668 * CF : 0: Shell-out; 1: Continue. 669 * AL : Error/Exit code. 670 */ 671 case 0x08: 672 { 673 CmdStartExternalCommand(); 674 break; 675 } 676 677 /* 678 * Start the default 32-bit command interpreter (COMSPEC) 679 * 680 * Input 681 * ES : Environment block segment. 682 * AL : Current drive number. 683 * 684 * Output 685 * CF : 0: Shell-out; 1: Continue. 686 * AL : Error/Exit code. 687 */ 688 case 0x0A: 689 { 690 CmdStartComSpec32(); 691 break; 692 } 693 694 /* 695 * Set exit code 696 * 697 * Input 698 * DX : Exit code 699 * 700 * Output 701 * CF : 0: Shell-out; 1: Continue. 702 */ 703 case 0x0B: 704 { 705 CmdSetExitCode(); 706 break; 707 } 708 709 /* 710 * Get start information 711 * 712 * Output 713 * AL : 0 (resp. 1): Started from (resp. without) an existing console. 714 */ 715 case 0x10: 716 { 717 #ifndef STANDALONE 718 /* 719 * When a new instance of our (internal) COMMAND.COM is started, 720 * we check whether we need to run a 32-bit COMSPEC. This goes by 721 * checking whether we were started in a new console (no parent 722 * console process) or from an existing one. 723 * 724 * However COMMAND.COM can also be started in the case where a 725 * 32-bit process (started by a 16-bit parent) wants to start a new 726 * 16-bit process: to ensure DOS reentry we need to start a new 727 * instance of COMMAND.COM. On Windows the COMMAND.COM is started 728 * just before the 32-bit process (in fact, it is this COMMAND.COM 729 * which starts the 32-bit process via an undocumented command-line 730 * switch '/z', which syntax is: 731 * COMMAND.COM /z\bAPPNAME.EXE 732 * notice the '\b' character inserted in-between. Then COMMAND.COM 733 * issues a BOP_CMD 08h with AH=00h to start the process). 734 * 735 * Instead, we do the reverse, i.e. we start the 32-bit process, 736 * and *only* if needed, i.e. if this process wants to start a 737 * new 16-bit process, we start our COMMAND.COM. 738 * 739 * The problem we then face is that our COMMAND.COM will possibly 740 * want to start a new COMSPEC, however we do not want this. 741 * The chosen solution is to flag this case -- done with the 'Reentry' 742 * boolean -- so that COMMAND.COM will not attempt to start COMSPEC 743 * but instead will directly try to start the 16-bit process. 744 */ 745 // setAL(SessionId != 0); 746 setAL((SessionId != 0) && !Reentry); 747 /* Reset 'Reentry' */ 748 Reentry = FALSE; 749 #else 750 setAL(0); 751 #endif 752 break; 753 } 754 755 default: 756 { 757 DPRINT1("Unknown DOS CMD Interpreter BOP Function: 0x%02X\n", FuncNum); 758 // setCF(1); // Disable, otherwise we enter an infinite loop 759 break; 760 } 761 } 762 } 763 764 #ifndef COMSPEC_FULLY_EXTERNAL 765 /* 766 * Internal COMMAND.COM binary data in the CommandCom array. 767 */ 768 #include "command_com.h" 769 #endif 770 771 static 772 DWORD DosStartComSpec(IN BOOLEAN Permanent, 773 IN LPCSTR Environment OPTIONAL, 774 IN DWORD ReturnAddress OPTIONAL, 775 OUT PWORD ComSpecPsp OPTIONAL) 776 { 777 /* 778 * BOOLEAN Permanent 779 * TRUE to simulate the /P switch of command.com: starts AUTOEXEC.BAT/NT 780 * and makes the interpreter permanent (cannot exit). 781 */ 782 783 DWORD Result; 784 785 if (ComSpecPsp) *ComSpecPsp = 0; 786 787 Result = 788 #ifndef COMSPEC_FULLY_EXTERNAL 789 DosLoadExecutableInternal(DOS_LOAD_AND_EXECUTE, 790 CommandCom, 791 sizeof(CommandCom), 792 "COMMAND.COM", 793 #else 794 DosLoadExecutable(DOS_LOAD_AND_EXECUTE, 795 #ifndef STANDALONE // FIXME: Those values are hardcoded paths on my local test machines!! 796 "C:\\CMDCMD.COM", 797 #else 798 "H:\\DOS_tests\\CMDCMD.COM", 799 #endif // STANDALONE 800 #endif // COMSPEC_FULLY_EXTERNAL 801 NULL, 802 Permanent ? "/P" : "", 803 Environment ? Environment : "", // FIXME: Default environment! 804 ReturnAddress); 805 if (Result != ERROR_SUCCESS) return Result; 806 807 /* TODO: Read AUTOEXEC.NT/BAT */ 808 809 /* Retrieve the PSP of the COMSPEC (current PSP set by DosLoadExecutable) */ 810 if (ComSpecPsp) *ComSpecPsp = Sda->CurrentPsp; 811 812 return Result; 813 } 814 815 typedef struct _DOS_START_PROC32 816 { 817 LPSTR ExecutablePath; 818 LPSTR CommandLine; 819 LPSTR Environment OPTIONAL; 820 #ifndef STANDALONE 821 PCOMSPEC_INFO ComSpecInfo; 822 HANDLE hEvent; 823 #endif 824 } DOS_START_PROC32, *PDOS_START_PROC32; 825 826 static DWORD 827 WINAPI 828 CommandThreadProc(LPVOID Parameter) 829 { 830 BOOL Success; 831 PROCESS_INFORMATION ProcessInfo; 832 STARTUPINFOA StartupInfo; 833 DWORD dwExitCode; 834 PDOS_START_PROC32 DosStartProc32 = (PDOS_START_PROC32)Parameter; 835 #ifndef STANDALONE 836 VDM_COMMAND_INFO CommandInfo; 837 PCOMSPEC_INFO ComSpecInfo = DosStartProc32->ComSpecInfo; 838 #endif 839 840 /* Set up the VDM, startup and process info structures */ 841 #ifndef STANDALONE 842 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo)); 843 #endif 844 RtlZeroMemory(&ProcessInfo, sizeof(ProcessInfo)); 845 RtlZeroMemory(&StartupInfo, sizeof(StartupInfo)); 846 StartupInfo.cb = sizeof(StartupInfo); 847 848 // FIXME: Build suitable 32-bit environment!! 849 850 #ifndef STANDALONE 851 /* 852 * Wait for signaling a new VDM task and increment the VDM re-entry count so 853 * that we can handle 16-bit apps that may be possibly started by the 32-bit app. 854 */ 855 CommandInfo.VDMState = VDM_INC_REENTER_COUNT; 856 DPRINT1("Calling GetNextVDMCommand reenter++\n"); 857 Success = GetNextVDMCommand(&CommandInfo); 858 DPRINT1("GetNextVDMCommand reenter++ success = %s, last error = %d\n", Success ? "true" : "false", GetLastError()); 859 ++ReentrancyCount; 860 #endif 861 862 /* Start the process */ 863 Success = CreateProcessA(NULL, // ProgramName, 864 DosStartProc32->CommandLine, 865 NULL, 866 NULL, 867 TRUE, // Inherit handles 868 CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED, 869 DosStartProc32->Environment, 870 NULL, // lpCurrentDirectory, see "START" command in cmd.exe 871 &StartupInfo, 872 &ProcessInfo); 873 874 #ifndef STANDALONE 875 /* Signal our caller the process was started */ 876 SetEvent(DosStartProc32->hEvent); 877 // After this point, 'DosStartProc32' is not valid anymore. 878 #endif 879 880 if (Success) 881 { 882 /* Resume the process */ 883 ResumeThread(ProcessInfo.hThread); 884 885 /* Wait for the process to finish running and retrieve its exit code */ 886 WaitForSingleObject(ProcessInfo.hProcess, INFINITE); 887 GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode); 888 889 /* Close the handles */ 890 CloseHandle(ProcessInfo.hThread); 891 CloseHandle(ProcessInfo.hProcess); 892 } 893 else 894 { 895 dwExitCode = GetLastError(); 896 } 897 898 #ifndef STANDALONE 899 ASSERT(ComSpecInfo); 900 ComSpecInfo->Terminated = TRUE; 901 ComSpecInfo->dwExitCode = dwExitCode; 902 903 /* Decrement the VDM re-entry count */ 904 CommandInfo.VDMState = VDM_DEC_REENTER_COUNT; 905 DPRINT1("Calling GetNextVDMCommand reenter--\n"); 906 Success = GetNextVDMCommand(&CommandInfo); 907 DPRINT1("GetNextVDMCommand reenter-- success = %s, last error = %d\n", Success ? "true" : "false", GetLastError()); 908 --ReentrancyCount; 909 910 return 0; 911 #else 912 return dwExitCode; 913 #endif 914 } 915 916 DWORD DosStartProcess32(IN LPCSTR ExecutablePath, 917 IN LPCSTR CommandLine, 918 IN LPCSTR Environment OPTIONAL, 919 IN DWORD ReturnAddress OPTIONAL, 920 IN BOOLEAN StartComSpec) 921 { 922 DWORD Result = ERROR_SUCCESS; 923 HANDLE CommandThread; 924 DOS_START_PROC32 DosStartProc32; 925 #ifndef STANDALONE 926 BOOL Success; 927 VDM_COMMAND_INFO CommandInfo; 928 #endif 929 930 DosStartProc32.ExecutablePath = (LPSTR)ExecutablePath; 931 DosStartProc32.CommandLine = (LPSTR)CommandLine; 932 DosStartProc32.Environment = (LPSTR)Environment; 933 934 #ifndef STANDALONE 935 DosStartProc32.ComSpecInfo = 936 RtlAllocateHeap(RtlGetProcessHeap(), 937 HEAP_ZERO_MEMORY, 938 sizeof(*DosStartProc32.ComSpecInfo)); 939 ASSERT(DosStartProc32.ComSpecInfo); 940 941 DosStartProc32.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); 942 ASSERT(DosStartProc32.hEvent); 943 #endif 944 945 /* Pause the VM and detach from the console */ 946 EmulatorPause(); 947 DosProcessConsoleDetach(); 948 949 /* Start the 32-bit process via another thread */ 950 CommandThread = CreateThread(NULL, 0, &CommandThreadProc, &DosStartProc32, 0, NULL); 951 if (CommandThread == NULL) 952 { 953 DisplayMessage(L"FATAL: Failed to create the command processing thread: %d", GetLastError()); 954 Result = GetLastError(); 955 goto Quit; 956 } 957 958 #ifndef STANDALONE 959 /* Close the thread handle */ 960 CloseHandle(CommandThread); 961 962 /* Wait for the process to be ready to start */ 963 WaitForSingleObject(DosStartProc32.hEvent, INFINITE); 964 965 /* Wait for any potential new DOS app started by the 32-bit process */ 966 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo)); 967 968 Retry: 969 CommandInfo.VDMState = VDM_FLAG_NESTED_TASK | VDM_FLAG_DONT_WAIT; 970 DPRINT1("Calling GetNextVDMCommand 32bit for possible new VDM task...\n"); 971 Success = GetNextVDMCommand(&CommandInfo); 972 DPRINT1("GetNextVDMCommand 32bit awaited, success = %s, last error = %d\n", Success ? "true" : "false", GetLastError()); 973 974 /* 975 * Check whether we were awaited because the 32-bit process was stopped, 976 * or because it started a new DOS application. 977 */ 978 if (CommandInfo.CmdLen != 0 || CommandInfo.AppLen != 0 || CommandInfo.PifLen != 0) 979 { 980 DPRINT1("GetNextVDMCommand 32bit, this is for a new VDM task - CmdLen = %d, AppLen = %d, PifLen = %d\n", 981 CommandInfo.CmdLen, CommandInfo.AppLen, CommandInfo.PifLen); 982 983 /* Repeat the request */ 984 Repeat = TRUE; 985 986 /* 987 * Set 'Reentry' to TRUE or FALSE depending on whether we are going 988 * to reenter with a new COMMAND.COM. See the comment for: 989 * BOP_CMD 0x10 'Get start information' 990 * (dem.c!DosCmdInterpreterBop) for more details. 991 */ 992 Reentry = StartComSpec; 993 994 /* If needed, start a new command interpreter to handle the possible incoming DOS commands */ 995 if (StartComSpec) 996 { 997 // 998 // DosStartProcess32 was only called by DosCreateProcess, called from INT 21h, 999 // so the caller stack is already prepared for running a new DOS program 1000 // (Flags, CS and IP, and the extra interrupt number, are already pushed). 1001 // 1002 Result = DosStartComSpec(FALSE, Environment, ReturnAddress, 1003 &DosStartProc32.ComSpecInfo->ComSpecPsp); 1004 if (Result != ERROR_SUCCESS) 1005 { 1006 DosDisplayMessage("Failed to start a new Command Interpreter (Error: %u).\n", Result); 1007 goto Quit; 1008 } 1009 } 1010 else 1011 { 1012 /* Retrieve the PSP of the COMSPEC (current PSP set by DosLoadExecutable) */ 1013 DosStartProc32.ComSpecInfo->ComSpecPsp = Sda->CurrentPsp; 1014 Result = ERROR_SUCCESS; 1015 } 1016 1017 /* Insert the new entry in the list; it will be freed when needed by COMMAND.COM */ 1018 InsertComSpecInfo(DosStartProc32.ComSpecInfo); 1019 } 1020 else 1021 { 1022 DPRINT1("GetNextVDMCommand 32bit, 32-bit app stopped\n"); 1023 1024 /* Check whether this was our 32-bit app which was killed */ 1025 if (!DosStartProc32.ComSpecInfo->Terminated) 1026 { 1027 DPRINT1("Not our 32-bit app, retrying...\n"); 1028 goto Retry; 1029 } 1030 1031 Result = DosStartProc32.ComSpecInfo->dwExitCode; 1032 1033 /* Delete the entry */ 1034 RtlFreeHeap(RtlGetProcessHeap(), 0, DosStartProc32.ComSpecInfo); 1035 } 1036 #else 1037 /* Wait for the thread to finish */ 1038 WaitForSingleObject(CommandThread, INFINITE); 1039 GetExitCodeThread(CommandThread, &Result); 1040 1041 /* Close the thread handle */ 1042 CloseHandle(CommandThread); 1043 1044 DPRINT1("32-bit app stopped\n"); 1045 #endif 1046 1047 Quit: 1048 #ifndef STANDALONE 1049 CloseHandle(DosStartProc32.hEvent); 1050 #endif 1051 1052 /* Attach to the console and resume the VM */ 1053 DosProcessConsoleAttach(); 1054 EmulatorResume(); 1055 1056 return Result; 1057 } 1058 1059 1060 1061 1062 /******************************************************************************\ 1063 |** DOS Bootloader emulation, Startup and Shutdown **| 1064 \******************************************************************************/ 1065 1066 1067 // 1068 // This function (equivalent of the DOS bootsector) is called by the bootstrap 1069 // loader *BEFORE* jumping at 0000:7C00. What we should do is to write at 0000:7C00 1070 // a BOP call that calls DosInitialize back. Then the bootstrap loader jumps at 1071 // 0000:7C00, our BOP gets called and then we can initialize the 32-bit part of the DOS. 1072 // 1073 1074 /* 16-bit bootstrap code at 0000:7C00 */ 1075 /* Of course, this is not in real bootsector format, because we don't care about it for now */ 1076 static BYTE Bootsector1[] = 1077 { 1078 LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_LOAD_DOS 1079 }; 1080 /* This portion of code is run if we failed to load the DOS */ 1081 // NOTE: This may also be done by the BIOS32. 1082 static BYTE Bootsector2[] = 1083 { 1084 LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_UNSIMULATE 1085 }; 1086 1087 static VOID WINAPI DosInitialize(LPWORD Stack); 1088 1089 VOID DosBootsectorInitialize(VOID) 1090 { 1091 /* We write the bootsector at 0000:7C00 */ 1092 ULONG_PTR StartAddress = (ULONG_PTR)SEG_OFF_TO_PTR(0x0000, 0x7C00); 1093 ULONG_PTR Address = StartAddress; 1094 CHAR DosKernelFileName[] = ""; // No DOS BIOS file name, therefore we will load DOS32 1095 1096 DPRINT("DosBootsectorInitialize\n"); 1097 1098 /* Write the "bootsector" */ 1099 RtlCopyMemory((PVOID)Address, Bootsector1, sizeof(Bootsector1)); 1100 Address += sizeof(Bootsector1); 1101 RtlCopyMemory((PVOID)Address, DosKernelFileName, sizeof(DosKernelFileName)); 1102 Address += sizeof(DosKernelFileName); 1103 RtlCopyMemory((PVOID)Address, Bootsector2, sizeof(Bootsector2)); 1104 Address += sizeof(Bootsector2); 1105 1106 /* Initialize the callback context */ 1107 InitializeContext(&DosContext, 0x0000, 1108 (ULONG_PTR)MEM_ALIGN_UP(0x7C00 + Address - StartAddress, sizeof(WORD))); 1109 1110 /* Register the DOS Loading BOP */ 1111 RegisterBop(BOP_LOAD_DOS, DosInitialize); 1112 } 1113 1114 1115 // 1116 // This function is called by the DOS bootsector in case we load DOS32. 1117 // It sets up the DOS32 start code then jumps to 0070:0000. 1118 // 1119 1120 /* 16-bit startup code for DOS32 at 0070:0000 */ 1121 static BYTE Startup[] = 1122 { 1123 LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_START_DOS, 1124 LOBYTE(EMULATOR_BOP), HIBYTE(EMULATOR_BOP), BOP_UNSIMULATE 1125 }; 1126 1127 static VOID WINAPI DosStart(LPWORD Stack); 1128 1129 static VOID WINAPI DosInitialize(LPWORD Stack) 1130 { 1131 /* Get the DOS BIOS file name (NULL-terminated) */ 1132 // FIXME: Isn't it possible to use some DS:SI instead?? 1133 LPCSTR DosBiosFileName = (LPCSTR)SEG_OFF_TO_PTR(getCS(), getIP()); 1134 setIP(getIP() + strlen(DosBiosFileName) + 1); // Skip it 1135 1136 DPRINT("DosInitialize('%s')\n", DosBiosFileName); 1137 1138 /* 1139 * We succeeded, deregister the DOS Loading BOP 1140 * so that no app will be able to call us back. 1141 */ 1142 RegisterBop(BOP_LOAD_DOS, NULL); 1143 1144 /* Register the DOS BOPs */ 1145 RegisterBop(BOP_DOS, DosSystemBop ); 1146 RegisterBop(BOP_CMD, DosCmdInterpreterBop); 1147 1148 if (DosBiosFileName[0] != '\0') 1149 { 1150 BOOLEAN Success = FALSE; 1151 HANDLE hDosBios; 1152 ULONG ulDosBiosSize = 0; 1153 1154 /* Open the DOS BIOS file */ 1155 hDosBios = FileOpen(DosBiosFileName, &ulDosBiosSize); 1156 if (hDosBios == NULL) goto Quit; 1157 1158 /* Attempt to load the DOS BIOS into memory */ 1159 Success = FileLoadByHandle(hDosBios, 1160 REAL_TO_PHYS(TO_LINEAR(0x0070, 0x0000)), 1161 ulDosBiosSize, 1162 &ulDosBiosSize); 1163 1164 DPRINT1("DOS BIOS file '%s' loading %s at %04X:%04X, size 0x%X (Error: %u).\n", 1165 DosBiosFileName, 1166 (Success ? "succeeded" : "failed"), 1167 0x0070, 0x0000, 1168 ulDosBiosSize, 1169 GetLastError()); 1170 1171 /* Close the DOS BIOS file */ 1172 FileClose(hDosBios); 1173 1174 Quit: 1175 if (!Success) 1176 { 1177 BiosDisplayMessage("DOS BIOS file '%s' loading failed (Error: %u). The VDM will shut down.\n", 1178 DosBiosFileName, GetLastError()); 1179 return; 1180 } 1181 } 1182 else 1183 { 1184 /* Load the 16-bit startup code for DOS32 and register its Starting BOP */ 1185 RtlCopyMemory(SEG_OFF_TO_PTR(0x0070, 0x0000), Startup, sizeof(Startup)); 1186 1187 // This is the equivalent of BOP_LOAD_DOS, function 0x11 "Load the DOS kernel" 1188 // for the Windows NT DOS. 1189 RegisterBop(BOP_START_DOS, DosStart); 1190 } 1191 1192 /* Position execution pointers for DOS startup and return */ 1193 setCS(0x0070); 1194 setIP(0x0000); 1195 } 1196 1197 static VOID WINAPI DosStart(LPWORD Stack) 1198 { 1199 BOOLEAN Success; 1200 DWORD Result; 1201 #ifndef STANDALONE 1202 INT i; 1203 #endif 1204 1205 DPRINT("DosStart\n"); 1206 1207 /* 1208 * We succeeded, deregister the DOS Starting BOP 1209 * so that no app will be able to call us back. 1210 */ 1211 RegisterBop(BOP_START_DOS, NULL); 1212 1213 /* Initialize the callback context */ 1214 InitializeContext(&DosContext, BIOS_CODE_SEGMENT, 0x0010); 1215 1216 Success = DosBIOSInitialize(); 1217 // Success &= DosKRNLInitialize(); 1218 if (!Success) 1219 { 1220 BiosDisplayMessage("DOS32 loading failed (Error: %u). The VDM will shut down.\n", GetLastError()); 1221 EmulatorTerminate(); 1222 return; 1223 } 1224 1225 /* Load the mouse driver */ 1226 DosMouseInitialize(); 1227 1228 #ifndef STANDALONE 1229 1230 /* Parse the command line arguments */ 1231 for (i = 1; i < NtVdmArgc; i++) 1232 { 1233 if (wcsncmp(NtVdmArgv[i], L"-i", 2) == 0) 1234 { 1235 /* This is the session ID (hex format) */ 1236 SessionId = wcstoul(NtVdmArgv[i] + 2, NULL, 16); 1237 } 1238 } 1239 1240 /* Initialize Win32-VDM environment */ 1241 Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize); 1242 if (Env == NULL) 1243 { 1244 DosDisplayMessage("Failed to initialize the global environment (Error: %u). The VDM will shut down.\n", GetLastError()); 1245 EmulatorTerminate(); 1246 return; 1247 } 1248 1249 /* Clear the structure */ 1250 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo)); 1251 1252 /* Get the initial information */ 1253 CommandInfo.TaskId = SessionId; 1254 CommandInfo.VDMState = VDM_GET_FIRST_COMMAND | VDM_FLAG_DOS; 1255 GetNextVDMCommand(&CommandInfo); 1256 1257 #else 1258 1259 /* Retrieve the command to start */ 1260 if (NtVdmArgc >= 2) 1261 { 1262 WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[1], -1, AppName, sizeof(AppName), NULL, NULL); 1263 1264 if (NtVdmArgc >= 3) 1265 WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[2], -1, CmdLine, sizeof(CmdLine), NULL, NULL); 1266 else 1267 strcpy(CmdLine, ""); 1268 } 1269 else 1270 { 1271 DosDisplayMessage("Invalid DOS command line\n"); 1272 EmulatorTerminate(); 1273 return; 1274 } 1275 1276 #endif 1277 1278 /* 1279 * At this point, CS:IP points to the DOS BIOS exit code. If the 1280 * root command interpreter fails to start (or if it exits), DOS 1281 * exits and the VDM terminates. 1282 */ 1283 1284 /* Start the root command interpreter */ 1285 // TODO: Really interpret the 'SHELL=' line of CONFIG.NT, and use it! 1286 1287 /* 1288 * Prepare the stack for DosStartComSpec: 1289 * push Flags, CS and IP, and an extra WORD. 1290 */ 1291 setSP(getSP() - sizeof(WORD)); 1292 *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = (WORD)getEFLAGS(); 1293 setSP(getSP() - sizeof(WORD)); 1294 *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = getCS(); 1295 setSP(getSP() - sizeof(WORD)); 1296 *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = getIP(); 1297 setSP(getSP() - sizeof(WORD)); 1298 1299 Result = DosStartComSpec(TRUE, SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0), 1300 MAKELONG(getIP(), getCS()), 1301 #ifndef STANDALONE 1302 &RootCmd.ComSpecPsp 1303 #else 1304 NULL 1305 #endif 1306 ); 1307 if (Result != ERROR_SUCCESS) 1308 { 1309 /* Unprepare the stack for DosStartComSpec */ 1310 setSP(getSP() + 4*sizeof(WORD)); 1311 1312 DosDisplayMessage("Failed to start the Command Interpreter (Error: %u). The VDM will shut down.\n", Result); 1313 EmulatorTerminate(); 1314 return; 1315 } 1316 1317 #ifndef STANDALONE 1318 RootCmd.Terminated = FALSE; 1319 InsertComSpecInfo(&RootCmd); 1320 #endif 1321 1322 /**/ 1323 /* Attach to the console and resume the VM */ 1324 DosProcessConsoleAttach(); 1325 EmulatorResume(); 1326 /**/ 1327 1328 return; 1329 } 1330 1331 BOOLEAN DosShutdown(BOOLEAN Immediate) 1332 { 1333 /* 1334 * Immediate = TRUE: Immediate shutdown; 1335 * FALSE: Delayed shutdown (notification). 1336 */ 1337 1338 #ifndef STANDALONE 1339 if (Immediate) 1340 { 1341 ExitVDM(FALSE, 0); 1342 return TRUE; 1343 } 1344 else 1345 { 1346 // HACK! 1347 extern HANDLE VdmTaskEvent; // see emulator.c 1348 1349 /* 1350 * Signal the root COMMAND.COM that it should terminate 1351 * when it checks for a new command. 1352 */ 1353 RootCmd.Terminated = TRUE; 1354 1355 /* If the list is already empty, or just contains only one element, bail out */ 1356 // FIXME: Question: if the list has only one element, is it ALWAYS RootCmd ?? 1357 // FIXME: The following is hackish. 1358 if ((IsListEmpty(&ComSpecInfoList) || 1359 (ComSpecInfoList.Flink == &RootCmd.Entry && 1360 ComSpecInfoList.Blink == &RootCmd.Entry)) && 1361 ReentrancyCount == 0 && 1362 WaitForSingleObject(VdmTaskEvent, 0) == WAIT_TIMEOUT) 1363 { 1364 /* Nothing runs, so exit immediately */ 1365 ExitVDM(FALSE, 0); 1366 return TRUE; 1367 } 1368 1369 return FALSE; 1370 } 1371 #else 1372 UNREFERENCED_PARAMETER(Immediate); 1373 return TRUE; 1374 #endif 1375 } 1376 1377 1378 /* PUBLIC EXPORTED APIS *******************************************************/ 1379 1380 // demLFNCleanup 1381 // demLFNGetCurrentDirectory 1382 1383 // demGetFileTimeByHandle_WOW 1384 // demWOWLFNAllocateSearchHandle 1385 // demWOWLFNCloseSearchHandle 1386 // demWOWLFNEntry 1387 // demWOWLFNGetSearchHandle 1388 // demWOWLFNInit 1389 1390 DWORD 1391 WINAPI 1392 demClientErrorEx(IN HANDLE FileHandle, 1393 IN CHAR Unknown, 1394 IN BOOL Flag) 1395 { 1396 UNIMPLEMENTED; 1397 return GetLastError(); 1398 } 1399 1400 DWORD 1401 WINAPI 1402 demFileDelete(IN LPCSTR FileName) 1403 { 1404 if (DeleteFileA(FileName)) SetLastError(ERROR_SUCCESS); 1405 1406 return GetLastError(); 1407 } 1408 1409 DWORD 1410 WINAPI 1411 demFileFindFirst(OUT PVOID lpFindFileData, 1412 IN LPCSTR FileName, 1413 IN WORD AttribMask) 1414 { 1415 BOOLEAN Success = TRUE; 1416 WIN32_FIND_DATAA FindData; 1417 HANDLE SearchHandle; 1418 PDOS_FIND_FILE_BLOCK FindFileBlock = (PDOS_FIND_FILE_BLOCK)lpFindFileData; 1419 1420 /* Start a search */ 1421 SearchHandle = FindFirstFileA(FileName, &FindData); 1422 if (SearchHandle == INVALID_HANDLE_VALUE) return GetLastError(); 1423 1424 do 1425 { 1426 /* Check the attributes and retry as long as we haven't found a matching file */ 1427 if (!((FindData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | 1428 FILE_ATTRIBUTE_SYSTEM | 1429 FILE_ATTRIBUTE_DIRECTORY)) 1430 & ~AttribMask)) 1431 { 1432 break; 1433 } 1434 } 1435 while ((Success = FindNextFileA(SearchHandle, &FindData))); 1436 1437 /* If we failed at some point, close the search and return an error */ 1438 if (!Success) 1439 { 1440 FindClose(SearchHandle); 1441 return GetLastError(); 1442 } 1443 1444 /* Fill the block */ 1445 FindFileBlock->DriveLetter = DosData->Sda.CurrentDrive + 'A'; 1446 FindFileBlock->AttribMask = AttribMask; 1447 FindFileBlock->SearchHandle = SearchHandle; 1448 FindFileBlock->Attributes = LOBYTE(FindData.dwFileAttributes); 1449 FileTimeToDosDateTime(&FindData.ftLastWriteTime, 1450 &FindFileBlock->FileDate, 1451 &FindFileBlock->FileTime); 1452 FindFileBlock->FileSize = FindData.nFileSizeHigh ? 0xFFFFFFFF 1453 : FindData.nFileSizeLow; 1454 /* Build a short path name */ 1455 if (*FindData.cAlternateFileName) 1456 strncpy(FindFileBlock->FileName, FindData.cAlternateFileName, sizeof(FindFileBlock->FileName)); 1457 else 1458 GetShortPathNameA(FindData.cFileName, FindFileBlock->FileName, sizeof(FindFileBlock->FileName)); 1459 1460 return ERROR_SUCCESS; 1461 } 1462 1463 DWORD 1464 WINAPI 1465 demFileFindNext(OUT PVOID lpFindFileData) 1466 { 1467 WIN32_FIND_DATAA FindData; 1468 PDOS_FIND_FILE_BLOCK FindFileBlock = (PDOS_FIND_FILE_BLOCK)lpFindFileData; 1469 1470 do 1471 { 1472 /* Continue searching as long as we haven't found a matching file */ 1473 1474 /* If we failed at some point, close the search and return an error */ 1475 if (!FindNextFileA(FindFileBlock->SearchHandle, &FindData)) 1476 { 1477 FindClose(FindFileBlock->SearchHandle); 1478 return GetLastError(); 1479 } 1480 } 1481 while ((FindData.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | 1482 FILE_ATTRIBUTE_SYSTEM | 1483 FILE_ATTRIBUTE_DIRECTORY)) 1484 & ~FindFileBlock->AttribMask); 1485 1486 /* Update the block */ 1487 FindFileBlock->Attributes = LOBYTE(FindData.dwFileAttributes); 1488 FileTimeToDosDateTime(&FindData.ftLastWriteTime, 1489 &FindFileBlock->FileDate, 1490 &FindFileBlock->FileTime); 1491 FindFileBlock->FileSize = FindData.nFileSizeHigh ? 0xFFFFFFFF 1492 : FindData.nFileSizeLow; 1493 /* Build a short path name */ 1494 if (*FindData.cAlternateFileName) 1495 strncpy(FindFileBlock->FileName, FindData.cAlternateFileName, sizeof(FindFileBlock->FileName)); 1496 else 1497 GetShortPathNameA(FindData.cFileName, FindFileBlock->FileName, sizeof(FindFileBlock->FileName)); 1498 1499 return ERROR_SUCCESS; 1500 } 1501 1502 UCHAR 1503 WINAPI 1504 demGetPhysicalDriveType(IN UCHAR DriveNumber) 1505 { 1506 UNIMPLEMENTED; 1507 return DOSDEVICE_DRIVE_UNKNOWN; 1508 } 1509 1510 BOOL 1511 WINAPI 1512 demIsShortPathName(IN LPCSTR Path, 1513 IN BOOL Unknown) 1514 { 1515 UNIMPLEMENTED; 1516 return FALSE; 1517 } 1518 1519 DWORD 1520 WINAPI 1521 demSetCurrentDirectoryGetDrive(IN LPCSTR CurrentDirectory, 1522 OUT PUCHAR DriveNumber) 1523 { 1524 UNIMPLEMENTED; 1525 return ERROR_SUCCESS; 1526 } 1527 1528 /* EOF */ 1529