2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
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)
13 /* INCLUDES *******************************************************************/
24 #include "dos/dos32krnl/device.h"
25 #include "dos/dos32krnl/process.h"
28 #include "bios/bios.h"
31 /* PRIVATE VARIABLES **********************************************************/
33 extern PDOS_DATA DosData
;
35 /* PRIVATE FUNCTIONS **********************************************************/
37 static VOID WINAPI
DosSystemBop(LPWORD Stack
)
39 /* Get the Function Number and skip it */
40 BYTE FuncNum
= *(PBYTE
)SEG_OFF_TO_PTR(getCS(), getIP());
45 /* Load the DOS kernel */
48 BOOLEAN Success
= FALSE
;
49 LPCSTR DosKernelFileName
= "ntdos.sys";
51 ULONG ulDosKernelSize
= 0;
53 DPRINT1("You are loading Windows NT DOS!\n");
55 /* Open the DOS kernel file */
56 hDosKernel
= FileOpen(DosKernelFileName
, &ulDosKernelSize
);
57 if (hDosKernel
== NULL
) goto Quit
;
60 * Attempt to load the DOS kernel into memory.
61 * The segment where to load the DOS kernel is defined
62 * by the DOS BIOS and is found in DI:0000 .
64 Success
= FileLoadByHandle(hDosKernel
,
65 REAL_TO_PHYS(TO_LINEAR(getDI(), 0x0000)),
69 DPRINT1("Windows NT DOS file '%s' loading %s at %04X:%04X, size 0x%X (Error: %u).\n",
71 (Success
? "succeeded" : "failed"),
76 /* Close the DOS kernel file */
77 FileClose(hDosKernel
);
82 /* We failed everything, stop the VDM */
83 DisplayMessage(L
"Windows NT DOS kernel file '%S' loading failed (Error: %u). The VDM will shut down.",
84 DosKernelFileName
, GetLastError());
92 /* Call 32-bit Driver Strategy Routine */
93 case BOP_DRV_STRATEGY
:
99 /* Call 32-bit Driver Interrupt Routine */
100 case BOP_DRV_INTERRUPT
:
102 DeviceInterruptBop();
108 DPRINT1("Unknown DOS System BOP Function: 0x%02X\n", FuncNum
);
109 // setCF(1); // Disable, otherwise we enter an infinite loop
115 static VOID WINAPI
DosCmdInterpreterBop(LPWORD Stack
)
117 /* Get the Function Number and skip it */
118 BYTE FuncNum
= *(PBYTE
)SEG_OFF_TO_PTR(getCS(), getIP());
123 case 0x08: // Launch external command
128 LPSTR Command
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getSI());
129 CHAR CmdLine
[sizeof("cmd.exe /c ") + DOS_CMDLINE_LENGTH
+ 1] = "";
132 STARTUPINFOA StartupInfo
;
133 PROCESS_INFORMATION ProcessInformation
;
135 /* Spawn a user-defined 32-bit command preprocessor */
137 // FIXME: Use COMSPEC env var!!
138 CmdLinePtr
= CmdLine
;
139 strcpy(CmdLinePtr
, "cmd.exe /c ");
140 CmdLinePtr
+= strlen(CmdLinePtr
);
142 /* Build a Win32-compatible command-line */
143 CmdLineLen
= min(strlen(Command
), sizeof(CmdLine
) - strlen(CmdLinePtr
) - 1);
144 RtlCopyMemory(CmdLinePtr
, Command
, CmdLineLen
);
145 CmdLinePtr
[CmdLineLen
] = '\0';
147 /* Remove any trailing return carriage character and NULL-terminate the command line */
148 while (*CmdLinePtr
&& *CmdLinePtr
!= '\r' && *CmdLinePtr
!= '\n') CmdLinePtr
++;
151 DPRINT1("CMD Run Command '%s' ('%s')\n", Command
, CmdLine
);
153 RtlZeroMemory(&StartupInfo
, sizeof(StartupInfo
));
154 RtlZeroMemory(&ProcessInformation
, sizeof(ProcessInformation
));
156 StartupInfo
.cb
= sizeof(StartupInfo
);
158 VidBiosDetachFromConsole();
160 Result
= CreateProcessA(NULL
,
169 &ProcessInformation
);
172 DPRINT1("Command '%s' ('%s') launched successfully\n", Command
, CmdLine
);
174 /* Wait for process termination */
175 WaitForSingleObject(ProcessInformation
.hProcess
, INFINITE
);
177 /* Get the exit code */
178 GetExitCodeProcess(ProcessInformation
.hProcess
, &dwExitCode
);
181 CloseHandle(ProcessInformation
.hThread
);
182 CloseHandle(ProcessInformation
.hProcess
);
186 DPRINT1("Failed when launched command '%s' ('%s')\n", Command
, CmdLine
);
187 dwExitCode
= GetLastError();
190 VidBiosAttachToConsole();
192 setAL((UCHAR
)dwExitCode
);
199 DPRINT1("Unknown DOS CMD Interpreter BOP Function: 0x%02X\n", FuncNum
);
200 // setCF(1); // Disable, otherwise we enter an infinite loop
209 CommandThreadProc(LPVOID Parameter
)
211 BOOLEAN First
= TRUE
;
213 VDM_COMMAND_INFO CommandInfo
;
214 CHAR CmdLine
[MAX_PATH
];
215 CHAR AppName
[MAX_PATH
];
216 CHAR PifFile
[MAX_PATH
];
217 CHAR Desktop
[MAX_PATH
];
218 CHAR Title
[MAX_PATH
];
220 PVOID Env
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, EnvSize
);
222 UNREFERENCED_PARAMETER(Parameter
);
225 /* Clear the structure */
226 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
228 /* Get the initial information */
229 CommandInfo
.TaskId
= SessionId
;
230 CommandInfo
.VDMState
= VDM_GET_FIRST_COMMAND
| VDM_FLAG_DOS
;
231 GetNextVDMCommand(&CommandInfo
);
235 /* Clear the structure */
236 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
238 /* Initialize the structure members */
239 CommandInfo
.TaskId
= SessionId
;
240 CommandInfo
.VDMState
= VDM_FLAG_DOS
;
241 CommandInfo
.CmdLine
= CmdLine
;
242 CommandInfo
.CmdLen
= sizeof(CmdLine
);
243 CommandInfo
.AppName
= AppName
;
244 CommandInfo
.AppLen
= sizeof(AppName
);
245 CommandInfo
.PifFile
= PifFile
;
246 CommandInfo
.PifLen
= sizeof(PifFile
);
247 CommandInfo
.Desktop
= Desktop
;
248 CommandInfo
.DesktopLen
= sizeof(Desktop
);
249 CommandInfo
.Title
= Title
;
250 CommandInfo
.TitleLen
= sizeof(Title
);
251 CommandInfo
.Env
= Env
;
252 CommandInfo
.EnvLen
= EnvSize
;
254 if (First
) CommandInfo
.VDMState
|= VDM_FLAG_FIRST_TASK
;
257 if (!GetNextVDMCommand(&CommandInfo
))
259 if (CommandInfo
.EnvLen
> EnvSize
)
261 /* Expand the environment size */
262 EnvSize
= CommandInfo
.EnvLen
;
263 CommandInfo
.Env
= Env
= RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, Env
, EnvSize
);
265 /* Repeat the request */
266 CommandInfo
.VDMState
|= VDM_FLAG_RETRY
;
273 /* Start the process from the command line */
274 Result
= DosStartProcess(AppName
, CmdLine
, Env
, MAKELONG(getIP(), getCS()));
275 if (Result
!= ERROR_SUCCESS
)
277 DisplayMessage(L
"Could not start '%S'. Error: %u", AppName
, Result
);
284 while (AcceptCommands
);
286 RtlFreeHeap(RtlGetProcessHeap(), 0, Env
);
291 /* PUBLIC VARIABLES ***********************************************************/
294 BOOLEAN AcceptCommands
= TRUE
;
295 HANDLE CommandThread
= NULL
;
299 /* PUBLIC FUNCTIONS ***********************************************************/
302 // This function (equivalent of the DOS bootsector) is called by the bootstrap
303 // loader *BEFORE* jumping at 0000:7C00. What we should do is to write at 0000:7C00
304 // a BOP call that calls DosInitialize back. Then the bootstrap loader jumps at
305 // 0000:7C00, our BOP gets called and then we can initialize the 32-bit part of the DOS.
308 /* 16-bit bootstrap code at 0000:7C00 */
309 /* Of course, this is not in real bootsector format, because we don't care about it for now */
310 static BYTE Bootsector1
[] =
312 LOBYTE(EMULATOR_BOP
), HIBYTE(EMULATOR_BOP
), BOP_LOAD_DOS
314 /* This portion of code is run if we failed to load the DOS */
315 // NOTE: This may also be done by the BIOS32.
316 static BYTE Bootsector2
[] =
318 LOBYTE(EMULATOR_BOP
), HIBYTE(EMULATOR_BOP
), BOP_UNSIMULATE
321 static VOID WINAPI
DosInitialize(LPWORD Stack
);
323 VOID
DosBootsectorInitialize(VOID
)
325 /* We write the bootsector at 0000:7C00 */
326 ULONG_PTR Address
= (ULONG_PTR
)SEG_OFF_TO_PTR(0x0000, 0x7C00);
327 CHAR DosKernelFileName
[] = ""; // No DOS BIOS file name, therefore we will load DOS32
329 DPRINT("DosBootsectorInitialize\n");
331 /* Write the "bootsector" */
332 RtlCopyMemory((PVOID
)Address
, Bootsector1
, sizeof(Bootsector1
));
333 Address
+= sizeof(Bootsector1
);
334 RtlCopyMemory((PVOID
)Address
, DosKernelFileName
, sizeof(DosKernelFileName
));
335 Address
+= sizeof(DosKernelFileName
);
336 RtlCopyMemory((PVOID
)Address
, Bootsector2
, sizeof(Bootsector2
));
338 /* Register the DOS Loading BOP */
339 RegisterBop(BOP_LOAD_DOS
, DosInitialize
);
344 // This function is called by the DOS bootsector in case we load DOS32.
345 // It sets up the DOS32 start code then jumps to 0070:0000.
348 /* 16-bit startup code for DOS32 at 0070:0000 */
349 static BYTE Startup
[] =
351 LOBYTE(EMULATOR_BOP
), HIBYTE(EMULATOR_BOP
), BOP_START_DOS
,
352 LOBYTE(EMULATOR_BOP
), HIBYTE(EMULATOR_BOP
), BOP_UNSIMULATE
355 static VOID WINAPI
DosStart(LPWORD Stack
);
357 static VOID WINAPI
DosInitialize(LPWORD Stack
)
359 /* Get the DOS BIOS file name (NULL-terminated) */
360 // FIXME: Isn't it possible to use some DS:SI instead??
361 LPCSTR DosBiosFileName
= (LPCSTR
)SEG_OFF_TO_PTR(getCS(), getIP());
362 setIP(getIP() + strlen(DosBiosFileName
) + 1); // Skip it
364 DPRINT("DosInitialize('%s')\n", DosBiosFileName
);
367 * We succeeded, deregister the DOS Loading BOP
368 * so that no app will be able to call us back.
370 RegisterBop(BOP_LOAD_DOS
, NULL
);
372 /* Register the DOS BOPs */
373 RegisterBop(BOP_DOS
, DosSystemBop
);
374 RegisterBop(BOP_CMD
, DosCmdInterpreterBop
);
376 if (DosBiosFileName
&& DosBiosFileName
[0] != '\0')
378 BOOLEAN Success
= FALSE
;
380 ULONG ulDosBiosSize
= 0;
382 /* Open the DOS BIOS file */
383 hDosBios
= FileOpen(DosBiosFileName
, &ulDosBiosSize
);
384 if (hDosBios
== NULL
) goto Quit
;
386 /* Attempt to load the DOS BIOS into memory */
387 Success
= FileLoadByHandle(hDosBios
,
388 REAL_TO_PHYS(TO_LINEAR(0x0070, 0x0000)),
392 DPRINT1("DOS BIOS file '%s' loading %s at %04X:%04X, size 0x%X (Error: %u).\n",
394 (Success
? "succeeded" : "failed"),
399 /* Close the DOS BIOS file */
405 DisplayMessage(L
"DOS BIOS file '%S' loading failed (Error: %u). The VDM will shut down.",
406 DosBiosFileName
, GetLastError());
412 /* Load the 16-bit startup code for DOS32 and register its Starting BOP */
413 RtlCopyMemory(SEG_OFF_TO_PTR(0x0070, 0x0000), Startup
, sizeof(Startup
));
415 // This is the equivalent of BOP_LOAD_DOS, function 0x11 "Load the DOS kernel"
416 // for the Windows NT DOS.
417 RegisterBop(BOP_START_DOS
, DosStart
);
420 /* Position execution pointers for DOS startup and return */
425 static VOID WINAPI
DosStart(LPWORD Stack
)
430 CHAR ApplicationName
[MAX_PATH
];
431 CHAR CommandLine
[DOS_CMDLINE_LENGTH
];
436 DPRINT("DosStart\n");
439 * We succeeded, deregister the DOS Starting BOP
440 * so that no app will be able to call us back.
442 RegisterBop(BOP_START_DOS
, NULL
);
444 Success
= DosBIOSInitialize();
445 // Success &= DosKRNLInitialize();
448 DisplayMessage(L
"DOS32 loading failed (Error: %u). The VDM will shut down.", GetLastError());
453 /* Load the mouse driver */
454 DosMouseInitialize();
458 /* Parse the command line arguments */
459 for (i
= 1; i
< NtVdmArgc
; i
++)
461 if (wcsncmp(NtVdmArgv
[i
], L
"-i", 2) == 0)
463 /* This is the session ID (hex format) */
464 SessionId
= wcstoul(NtVdmArgv
[i
] + 2, NULL
, 16);
466 /* The VDM hasn't been started from a console, so quit when the task is done */
467 AcceptCommands
= FALSE
;
471 /* Create the GetNextVDMCommand thread */
472 CommandThread
= CreateThread(NULL
, 0, &CommandThreadProc
, NULL
, 0, NULL
);
473 if (CommandThread
== NULL
)
475 wprintf(L
"FATAL: Failed to create the command processing thread: %d\n", GetLastError());
479 /* Wait for the command thread to exit */
480 WaitForSingleObject(CommandThread
, INFINITE
);
482 /* Close the thread handle */
483 CloseHandle(CommandThread
);
489 WideCharToMultiByte(CP_ACP
, 0, NtVdmArgv
[1], -1, ApplicationName
, sizeof(ApplicationName
), NULL
, NULL
);
492 WideCharToMultiByte(CP_ACP
, 0, NtVdmArgv
[2], -1, CommandLine
, sizeof(CommandLine
), NULL
, NULL
);
494 strcpy(CommandLine
, "");
498 DisplayMessage(L
"Invalid DOS command line\n");
502 /* Start the process from the command line */
503 Result
= DosStartProcess(ApplicationName
, CommandLine
,
504 SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK
, 0),
505 MAKELONG(getIP(), getCS()));
506 if (Result
!= ERROR_SUCCESS
)
508 DisplayMessage(L
"Could not start '%S'. Error: %u", ApplicationName
, Result
);
519 /* PUBLIC EXPORTED APIS *******************************************************/
522 // demLFNGetCurrentDirectory
524 // demGetFileTimeByHandle_WOW
525 // demWOWLFNAllocateSearchHandle
526 // demWOWLFNCloseSearchHandle
528 // demWOWLFNGetSearchHandle
533 demClientErrorEx(IN HANDLE FileHandle
,
538 return GetLastError();
543 demFileDelete(IN LPCSTR FileName
)
545 if (DeleteFileA(FileName
)) SetLastError(ERROR_SUCCESS
);
547 return GetLastError();
552 demFileFindFirst(OUT PVOID lpFindFileData
,
556 BOOLEAN Success
= TRUE
;
557 WIN32_FIND_DATAA FindData
;
559 PDOS_FIND_FILE_BLOCK FindFileBlock
= (PDOS_FIND_FILE_BLOCK
)lpFindFileData
;
562 SearchHandle
= FindFirstFileA(FileName
, &FindData
);
563 if (SearchHandle
== INVALID_HANDLE_VALUE
) return GetLastError();
567 /* Check the attributes and retry as long as we haven't found a matching file */
568 if (!((FindData
.dwFileAttributes
& (FILE_ATTRIBUTE_HIDDEN
|
569 FILE_ATTRIBUTE_SYSTEM
|
570 FILE_ATTRIBUTE_DIRECTORY
))
576 while ((Success
= FindNextFileA(SearchHandle
, &FindData
)));
578 /* If we failed at some point, close the search and return an error */
581 FindClose(SearchHandle
);
582 return GetLastError();
586 FindFileBlock
->DriveLetter
= DosData
->Sda
.CurrentDrive
+ 'A';
587 FindFileBlock
->AttribMask
= AttribMask
;
588 FindFileBlock
->SearchHandle
= SearchHandle
;
589 FindFileBlock
->Attributes
= LOBYTE(FindData
.dwFileAttributes
);
590 FileTimeToDosDateTime(&FindData
.ftLastWriteTime
,
591 &FindFileBlock
->FileDate
,
592 &FindFileBlock
->FileTime
);
593 FindFileBlock
->FileSize
= FindData
.nFileSizeHigh
? 0xFFFFFFFF
594 : FindData
.nFileSizeLow
;
595 /* Build a short path name */
596 if (*FindData
.cAlternateFileName
)
597 strncpy(FindFileBlock
->FileName
, FindData
.cAlternateFileName
, sizeof(FindFileBlock
->FileName
));
599 GetShortPathNameA(FindData
.cFileName
, FindFileBlock
->FileName
, sizeof(FindFileBlock
->FileName
));
601 return ERROR_SUCCESS
;
606 demFileFindNext(OUT PVOID lpFindFileData
)
608 WIN32_FIND_DATAA FindData
;
609 PDOS_FIND_FILE_BLOCK FindFileBlock
= (PDOS_FIND_FILE_BLOCK
)lpFindFileData
;
613 /* Continue searching as long as we haven't found a matching file */
615 /* If we failed at some point, close the search and return an error */
616 if (!FindNextFileA(FindFileBlock
->SearchHandle
, &FindData
))
618 FindClose(FindFileBlock
->SearchHandle
);
619 return GetLastError();
622 while ((FindData
.dwFileAttributes
& (FILE_ATTRIBUTE_HIDDEN
|
623 FILE_ATTRIBUTE_SYSTEM
|
624 FILE_ATTRIBUTE_DIRECTORY
))
625 & ~FindFileBlock
->AttribMask
);
627 /* Update the block */
628 FindFileBlock
->Attributes
= LOBYTE(FindData
.dwFileAttributes
);
629 FileTimeToDosDateTime(&FindData
.ftLastWriteTime
,
630 &FindFileBlock
->FileDate
,
631 &FindFileBlock
->FileTime
);
632 FindFileBlock
->FileSize
= FindData
.nFileSizeHigh
? 0xFFFFFFFF
633 : FindData
.nFileSizeLow
;
634 /* Build a short path name */
635 if (*FindData
.cAlternateFileName
)
636 strncpy(FindFileBlock
->FileName
, FindData
.cAlternateFileName
, sizeof(FindFileBlock
->FileName
));
638 GetShortPathNameA(FindData
.cFileName
, FindFileBlock
->FileName
, sizeof(FindFileBlock
->FileName
));
640 return ERROR_SUCCESS
;
645 demGetPhysicalDriveType(IN UCHAR DriveNumber
)
648 return DOSDEVICE_DRIVE_UNKNOWN
;
653 demIsShortPathName(IN LPCSTR Path
,
662 demSetCurrentDirectoryGetDrive(IN LPCSTR CurrentDirectory
,
663 OUT PUCHAR DriveNumber
)
666 return ERROR_SUCCESS
;