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 *******************************************************************/
22 #include "dos/dos32krnl/device.h"
23 #include "dos/dos32krnl/process.h"
26 #include "bios/bios.h"
29 /* PRIVATE VARIABLES **********************************************************/
31 /**/extern BYTE CurrentDrive
;/**/
33 /* PRIVATE FUNCTIONS **********************************************************/
35 static VOID WINAPI
DosSystemBop(LPWORD Stack
)
37 /* Get the Function Number and skip it */
38 BYTE FuncNum
= *(PBYTE
)SEG_OFF_TO_PTR(getCS(), getIP());
43 case 0x11: // Load the DOS kernel
45 BOOLEAN Success
= FALSE
;
47 ULONG ulDosKernelSize
= 0;
49 DPRINT1("You are loading Windows NT DOS!\n");
51 /* Open the DOS kernel file */
52 hDosKernel
= FileOpen("ntdos.sys", &ulDosKernelSize
);
54 /* If we failed, bail out */
55 if (hDosKernel
== NULL
) goto Quit
;
58 * Attempt to load the DOS kernel into memory.
59 * The segment where to load the DOS kernel is defined
60 * by the DOS BIOS and is found in DI:0000 .
62 Success
= FileLoadByHandle(hDosKernel
,
63 REAL_TO_PHYS(TO_LINEAR(getDI(), 0x0000)),
67 DPRINT1("Windows NT DOS loading %s at %04X:%04X, size 0x%X ; GetLastError() = %u\n",
68 (Success
? "succeeded" : "failed"),
73 /* Close the DOS kernel file */
74 FileClose(hDosKernel
);
79 /* We failed everything, stop the VDM */
86 /* Call 32-bit Driver Strategy Routine */
87 case BOP_DRV_STRATEGY
:
93 /* Call 32-bit Driver Interrupt Routine */
94 case BOP_DRV_INTERRUPT
:
102 DPRINT1("Unknown DOS System BOP Function: 0x%02X\n", FuncNum
);
103 // setCF(1); // Disable, otherwise we enter an infinite loop
109 static VOID WINAPI
DosCmdInterpreterBop(LPWORD Stack
)
111 /* Get the Function Number and skip it */
112 BYTE FuncNum
= *(PBYTE
)SEG_OFF_TO_PTR(getCS(), getIP());
117 case 0x08: // Launch external command
122 LPSTR Command
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getSI());
123 CHAR CmdLine
[sizeof("cmd.exe /c ") + DOS_CMDLINE_LENGTH
+ 1] = "";
126 STARTUPINFOA StartupInfo
;
127 PROCESS_INFORMATION ProcessInformation
;
129 /* Spawn a user-defined 32-bit command preprocessor */
131 // FIXME: Use COMSPEC env var!!
132 CmdLinePtr
= CmdLine
;
133 strcpy(CmdLinePtr
, "cmd.exe /c ");
134 CmdLinePtr
+= strlen(CmdLinePtr
);
136 /* Build a Win32-compatible command-line */
137 CmdLineLen
= min(strlen(Command
), sizeof(CmdLine
) - strlen(CmdLinePtr
) - 1);
138 RtlCopyMemory(CmdLinePtr
, Command
, CmdLineLen
);
139 CmdLinePtr
[CmdLineLen
] = '\0';
141 /* Remove any trailing return carriage character and NULL-terminate the command line */
142 while (*CmdLinePtr
&& *CmdLinePtr
!= '\r' && *CmdLinePtr
!= '\n') CmdLinePtr
++;
145 DPRINT1("CMD Run Command '%s' ('%s')\n", Command
, CmdLine
);
147 RtlZeroMemory(&StartupInfo
, sizeof(StartupInfo
));
148 RtlZeroMemory(&ProcessInformation
, sizeof(ProcessInformation
));
150 StartupInfo
.cb
= sizeof(StartupInfo
);
152 VidBiosDetachFromConsole();
154 Result
= CreateProcessA(NULL
,
163 &ProcessInformation
);
166 DPRINT1("Command '%s' ('%s') launched successfully\n", Command
, CmdLine
);
168 /* Wait for process termination */
169 WaitForSingleObject(ProcessInformation
.hProcess
, INFINITE
);
171 /* Get the exit code */
172 GetExitCodeProcess(ProcessInformation
.hProcess
, &dwExitCode
);
175 CloseHandle(ProcessInformation
.hThread
);
176 CloseHandle(ProcessInformation
.hProcess
);
180 DPRINT1("Failed when launched command '%s' ('%s')\n", Command
, CmdLine
);
181 dwExitCode
= GetLastError();
184 VidBiosAttachToConsole();
186 setAL((UCHAR
)dwExitCode
);
193 DPRINT1("Unknown DOS CMD Interpreter BOP Function: 0x%02X\n", FuncNum
);
194 // setCF(1); // Disable, otherwise we enter an infinite loop
203 CommandThreadProc(LPVOID Parameter
)
205 BOOLEAN First
= TRUE
;
207 VDM_COMMAND_INFO CommandInfo
;
208 CHAR CmdLine
[MAX_PATH
];
209 CHAR AppName
[MAX_PATH
];
210 CHAR PifFile
[MAX_PATH
];
211 CHAR Desktop
[MAX_PATH
];
212 CHAR Title
[MAX_PATH
];
214 PVOID Env
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, EnvSize
);
216 UNREFERENCED_PARAMETER(Parameter
);
221 /* Clear the structure */
222 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
224 /* Initialize the structure members */
225 CommandInfo
.TaskId
= SessionId
;
226 CommandInfo
.VDMState
= VDM_FLAG_DOS
;
227 CommandInfo
.CmdLine
= CmdLine
;
228 CommandInfo
.CmdLen
= sizeof(CmdLine
);
229 CommandInfo
.AppName
= AppName
;
230 CommandInfo
.AppLen
= sizeof(AppName
);
231 CommandInfo
.PifFile
= PifFile
;
232 CommandInfo
.PifLen
= sizeof(PifFile
);
233 CommandInfo
.Desktop
= Desktop
;
234 CommandInfo
.DesktopLen
= sizeof(Desktop
);
235 CommandInfo
.Title
= Title
;
236 CommandInfo
.TitleLen
= sizeof(Title
);
237 CommandInfo
.Env
= Env
;
238 CommandInfo
.EnvLen
= EnvSize
;
240 if (First
) CommandInfo
.VDMState
|= VDM_FLAG_FIRST_TASK
;
243 if (!GetNextVDMCommand(&CommandInfo
))
245 if (CommandInfo
.EnvLen
> EnvSize
)
247 /* Expand the environment size */
248 EnvSize
= CommandInfo
.EnvLen
;
249 CommandInfo
.Env
= Env
= RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, Env
, EnvSize
);
251 /* Repeat the request */
252 CommandInfo
.VDMState
|= VDM_FLAG_RETRY
;
259 /* Start the process from the command line */
260 Result
= DosStartProcess(AppName
, CmdLine
, Env
);
261 if (Result
!= ERROR_SUCCESS
)
263 DisplayMessage(L
"Could not start '%S'. Error: %u", AppName
, Result
);
270 while (AcceptCommands
);
272 RtlFreeHeap(RtlGetProcessHeap(), 0, Env
);
277 /* PUBLIC VARIABLES ***********************************************************/
280 BOOLEAN AcceptCommands
= TRUE
;
281 HANDLE CommandThread
= NULL
;
285 /* PUBLIC FUNCTIONS ***********************************************************/
288 // This function (equivalent of the DOS bootsector) is called by the bootstrap
289 // loader *BEFORE* jumping at 0000:7C00. What we should do is to write at 0000:7C00
290 // a BOP call that calls DosInitialize back. Then the bootstrap loader jumps at
291 // 0000:7C00, our BOP gets called and then we can initialize the 32-bit part of the DOS.
294 /* 16-bit bootstrap code at 0000:7C00 */
295 /* Of course, this is not in real bootsector format, because we don't care */
296 static BYTE Bootsector1
[] =
298 LOBYTE(EMULATOR_BOP
), HIBYTE(EMULATOR_BOP
), BOP_LOAD_DOS
, // Call DOS Loading
300 /* This portion of code is run if we failed to load the DOS */
301 static BYTE Bootsector2
[] =
304 0x5B, 0xE0, 0x00, 0xF0, // F000:E05B /** HACK! What to do instead?? **/
307 static VOID WINAPI
DosInitialize(LPWORD Stack
);
309 VOID
DosBootsectorInitialize(VOID
)
311 /* We write the bootsector at 0000:7C00 */
312 ULONG_PTR Address
= (ULONG_PTR
)SEG_OFF_TO_PTR(0x0000, 0x7C00);
313 CHAR DosKernelFileName
[] = ""; // No DOS file name, therefore we'll load DOS32
315 DPRINT("DosBootsectorInitialize\n");
317 /* Write the "bootsector" */
318 RtlCopyMemory((PVOID
)Address
, Bootsector1
, sizeof(Bootsector1
));
319 Address
+= sizeof(Bootsector1
);
320 RtlCopyMemory((PVOID
)Address
, DosKernelFileName
, sizeof(DosKernelFileName
));
321 Address
+= sizeof(DosKernelFileName
);
322 RtlCopyMemory((PVOID
)Address
, Bootsector2
, sizeof(Bootsector2
));
324 /* Register the DOS Loading BOP */
325 RegisterBop(BOP_LOAD_DOS
, DosInitialize
);
330 // This function is called by the DOS bootsector. We finish to load
331 // the DOS, then we jump to 0070:0000.
334 /* 16-bit startup code at 0070:0000 */
335 static BYTE Startup
[] =
337 LOBYTE(EMULATOR_BOP
), HIBYTE(EMULATOR_BOP
), BOP_START_DOS
, // Call DOS Start
340 static VOID WINAPI
DosStart(LPWORD Stack
);
342 static VOID WINAPI
DosInitialize(LPWORD Stack
)
344 BOOLEAN Success
= FALSE
;
346 /* Get the DOS kernel file name (NULL-terminated) */
347 // FIXME: Isn't it possible to use some DS:SI instead??
348 LPCSTR DosKernelFileName
= (LPCSTR
)SEG_OFF_TO_PTR(getCS(), getIP());
349 setIP(getIP() + strlen(DosKernelFileName
) + 1); // Skip it
351 DPRINT("DosInitialize('%s')\n", DosKernelFileName
);
353 /* Register the DOS BOPs */
354 RegisterBop(BOP_DOS
, DosSystemBop
);
355 RegisterBop(BOP_CMD
, DosCmdInterpreterBop
);
357 if (DosKernelFileName
&& DosKernelFileName
[0] != '\0')
360 ULONG ulDosBiosSize
= 0;
362 /* Open the DOS BIOS file */
363 hDosBios
= FileOpen(DosKernelFileName
, &ulDosBiosSize
);
365 /* If we failed, bail out */
366 if (hDosBios
== NULL
) goto QuitCustom
;
368 /* Attempt to load the DOS BIOS into memory */
369 Success
= FileLoadByHandle(hDosBios
,
370 REAL_TO_PHYS(TO_LINEAR(0x0070, 0x0000)),
374 DPRINT1("DOS BIOS loading %s at %04X:%04X, size 0x%X ; GetLastError() = %u\n",
375 (Success
? "succeeded" : "failed"),
380 /* Close the DOS BIOS file */
383 if (!Success
) goto QuitCustom
;
385 /* Position execution pointers and return */
392 DisplayMessage(L
"Custom DOS '%S' loading failed, what to do??", DosKernelFileName
);
396 Success
= DosBIOSInitialize();
397 // Success &= DosKRNLInitialize();
399 if (!Success
) goto Quit32
;
401 /* Write the "bootsector" */
402 RtlCopyMemory(SEG_OFF_TO_PTR(0x0070, 0x0000), Startup
, sizeof(Startup
));
404 /* Register the DOS Starting BOP */
405 RegisterBop(BOP_START_DOS
, DosStart
);
407 /* Position execution pointers and return */
414 DisplayMessage(L
"DOS32 loading failed, what to do??");
420 * We succeeded, deregister the DOS Loading BOP
421 * so that no app will be able to call us back.
423 RegisterBop(BOP_LOAD_DOS
, NULL
);
427 static VOID WINAPI
DosStart(LPWORD Stack
)
431 CHAR ApplicationName
[MAX_PATH
];
432 CHAR CommandLine
[DOS_CMDLINE_LENGTH
];
437 DPRINT("DosStart\n");
440 * We succeeded, deregister the DOS Starting BOP
441 * so that no app will be able to call us back.
443 RegisterBop(BOP_START_DOS
, NULL
);
445 /* Load the mouse driver */
446 DosMouseInitialize();
450 /* Parse the command line arguments */
451 for (i
= 1; i
< NtVdmArgc
; i
++)
453 if (wcsncmp(NtVdmArgv
[i
], L
"-i", 2) == 0)
455 /* This is the session ID */
456 SessionId
= wcstoul(NtVdmArgv
[i
] + 2, NULL
, 10);
458 /* The VDM hasn't been started from a console, so quit when the task is done */
459 AcceptCommands
= FALSE
;
463 /* Create the GetNextVDMCommand thread */
464 CommandThread
= CreateThread(NULL
, 0, &CommandThreadProc
, NULL
, 0, NULL
);
465 if (CommandThread
== NULL
)
467 wprintf(L
"FATAL: Failed to create the command processing thread: %d\n", GetLastError());
471 /* Wait for the command thread to exit */
472 WaitForSingleObject(CommandThread
, INFINITE
);
474 /* Close the thread handle */
475 CloseHandle(CommandThread
);
481 WideCharToMultiByte(CP_ACP
, 0, NtVdmArgv
[1], -1, ApplicationName
, sizeof(ApplicationName
), NULL
, NULL
);
484 WideCharToMultiByte(CP_ACP
, 0, NtVdmArgv
[2], -1, CommandLine
, sizeof(CommandLine
), NULL
, NULL
);
486 strcpy(CommandLine
, "");
490 DisplayMessage(L
"Invalid DOS command line\n");
494 /* Start the process from the command line */
495 Result
= DosStartProcess(ApplicationName
, CommandLine
,
496 SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK
, 0));
497 if (Result
!= ERROR_SUCCESS
)
499 DisplayMessage(L
"Could not start '%S'. Error: %u", ApplicationName
, Result
);
510 /* PUBLIC EXPORTED APIS *******************************************************/
513 // demLFNGetCurrentDirectory
515 // demGetFileTimeByHandle_WOW
516 // demWOWLFNAllocateSearchHandle
517 // demWOWLFNCloseSearchHandle
519 // demWOWLFNGetSearchHandle
524 demClientErrorEx(IN HANDLE FileHandle
,
529 return GetLastError();
534 demFileDelete(IN LPCSTR FileName
)
536 if (DeleteFileA(FileName
)) SetLastError(ERROR_SUCCESS
);
538 return GetLastError();
543 demFileFindFirst(OUT PVOID lpFindFileData
,
547 BOOLEAN Success
= TRUE
;
548 WIN32_FIND_DATAA FindData
;
549 PDOS_FIND_FILE_BLOCK FindFileBlock
= (PDOS_FIND_FILE_BLOCK
)lpFindFileData
;
552 FindFileBlock
->DriveLetter
= CurrentDrive
+ 'A';
553 FindFileBlock
->AttribMask
= AttribMask
;
554 FindFileBlock
->SearchHandle
= FindFirstFileA(FileName
, &FindData
);
555 if (FindFileBlock
->SearchHandle
== INVALID_HANDLE_VALUE
) return GetLastError();
559 /* Check the attributes */
560 if (!((FindData
.dwFileAttributes
& (FILE_ATTRIBUTE_HIDDEN
|
561 FILE_ATTRIBUTE_SYSTEM
|
562 FILE_ATTRIBUTE_DIRECTORY
))
568 while ((Success
= FindNextFileA(FindFileBlock
->SearchHandle
, &FindData
)));
570 if (!Success
) return GetLastError();
572 FindFileBlock
->Attributes
= LOBYTE(FindData
.dwFileAttributes
);
573 FileTimeToDosDateTime(&FindData
.ftLastWriteTime
,
574 &FindFileBlock
->FileDate
,
575 &FindFileBlock
->FileTime
);
576 FindFileBlock
->FileSize
= FindData
.nFileSizeHigh
? 0xFFFFFFFF
577 : FindData
.nFileSizeLow
;
578 strcpy(FindFileBlock
->FileName
, FindData
.cAlternateFileName
);
580 return ERROR_SUCCESS
;
585 demFileFindNext(OUT PVOID lpFindFileData
)
587 WIN32_FIND_DATAA FindData
;
588 PDOS_FIND_FILE_BLOCK FindFileBlock
= (PDOS_FIND_FILE_BLOCK
)lpFindFileData
;
592 if (!FindNextFileA(FindFileBlock
->SearchHandle
, &FindData
))
593 return GetLastError();
595 /* Update the block */
596 FindFileBlock
->Attributes
= LOBYTE(FindData
.dwFileAttributes
);
597 FileTimeToDosDateTime(&FindData
.ftLastWriteTime
,
598 &FindFileBlock
->FileDate
,
599 &FindFileBlock
->FileTime
);
600 FindFileBlock
->FileSize
= FindData
.nFileSizeHigh
? 0xFFFFFFFF
601 : FindData
.nFileSizeLow
;
602 strcpy(FindFileBlock
->FileName
, FindData
.cAlternateFileName
);
604 while((FindData
.dwFileAttributes
& (FILE_ATTRIBUTE_HIDDEN
|
605 FILE_ATTRIBUTE_SYSTEM
|
606 FILE_ATTRIBUTE_DIRECTORY
))
607 & ~FindFileBlock
->AttribMask
);
609 return ERROR_SUCCESS
;
614 demGetPhysicalDriveType(IN UCHAR DriveNumber
)
617 return DOSDEVICE_DRIVE_UNKNOWN
;
622 demIsShortPathName(IN LPCSTR Path
,
631 demSetCurrentDirectoryGetDrive(IN LPCSTR CurrentDirectory
,
632 OUT PUCHAR DriveNumber
)
635 return ERROR_SUCCESS
;