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 FUNCTIONS ***********************************************************/
280 // This function (equivalent of the DOS bootsector) is called by the bootstrap
281 // loader *BEFORE* jumping at 0000:7C00. What we should do is to write at 0000:7C00
282 // a BOP call that calls DosInitialize back. Then the bootstrap loader jumps at
283 // 0000:7C00, our BOP gets called and then we can initialize the 32-bit part of the DOS.
286 /* 16-bit bootstrap code at 0000:7C00 */
287 /* Of course, this is not in real bootsector format, because we don't care */
288 static BYTE Bootsector1
[] =
290 LOBYTE(EMULATOR_BOP
), HIBYTE(EMULATOR_BOP
), BOP_LOAD_DOS
, // Call DOS Loading
292 /* This portion of code is run if we failed to load the DOS */
293 static BYTE Bootsector2
[] =
296 0x5B, 0xE0, 0x00, 0xF0, // F000:E05B /** HACK! What to do instead?? **/
299 static VOID WINAPI
DosInitialize(LPWORD Stack
);
301 VOID
DosBootsectorInitialize(VOID
)
303 /* We write the bootsector at 0000:7C00 */
304 ULONG_PTR Address
= (ULONG_PTR
)SEG_OFF_TO_PTR(0x0000, 0x7C00);
305 CHAR DosKernelFileName
[] = ""; // No DOS file name, therefore we'll load DOS32
307 DPRINT("DosBootsectorInitialize\n");
309 /* Write the "bootsector" */
310 RtlCopyMemory((PVOID
)Address
, Bootsector1
, sizeof(Bootsector1
));
311 Address
+= sizeof(Bootsector1
);
312 RtlCopyMemory((PVOID
)Address
, DosKernelFileName
, sizeof(DosKernelFileName
));
313 Address
+= sizeof(DosKernelFileName
);
314 RtlCopyMemory((PVOID
)Address
, Bootsector2
, sizeof(Bootsector2
));
316 /* Register the DOS Loading BOP */
317 RegisterBop(BOP_LOAD_DOS
, DosInitialize
);
322 // This function is called by the DOS bootsector. We finish to load
323 // the DOS, then we jump to 0070:0000.
326 /* 16-bit startup code at 0070:0000 */
327 static BYTE Startup
[] =
329 LOBYTE(EMULATOR_BOP
), HIBYTE(EMULATOR_BOP
), BOP_START_DOS
, // Call DOS Start
332 static VOID WINAPI
DosStart(LPWORD Stack
);
334 static VOID WINAPI
DosInitialize(LPWORD Stack
)
336 BOOLEAN Success
= FALSE
;
338 /* Get the DOS kernel file name (NULL-terminated) */
339 // FIXME: Isn't it possible to use some DS:SI instead??
340 LPCSTR DosKernelFileName
= (LPCSTR
)SEG_OFF_TO_PTR(getCS(), getIP());
341 setIP(getIP() + strlen(DosKernelFileName
) + 1); // Skip it
343 DPRINT("DosInitialize('%s')\n", DosKernelFileName
);
345 /* Register the DOS BOPs */
346 RegisterBop(BOP_DOS
, DosSystemBop
);
347 RegisterBop(BOP_CMD
, DosCmdInterpreterBop
);
349 if (DosKernelFileName
&& DosKernelFileName
[0] != '\0')
352 ULONG ulDosBiosSize
= 0;
354 /* Open the DOS BIOS file */
355 hDosBios
= FileOpen(DosKernelFileName
, &ulDosBiosSize
);
357 /* If we failed, bail out */
358 if (hDosBios
== NULL
) goto QuitCustom
;
360 /* Attempt to load the DOS BIOS into memory */
361 Success
= FileLoadByHandle(hDosBios
,
362 REAL_TO_PHYS(TO_LINEAR(0x0070, 0x0000)),
366 DPRINT1("DOS BIOS loading %s at %04X:%04X, size 0x%X ; GetLastError() = %u\n",
367 (Success
? "succeeded" : "failed"),
372 /* Close the DOS BIOS file */
375 if (!Success
) goto QuitCustom
;
377 /* Position execution pointers and return */
384 DisplayMessage(L
"Custom DOS '%S' loading failed, what to do??", DosKernelFileName
);
388 Success
= DosBIOSInitialize();
389 // Success &= DosKRNLInitialize();
391 if (!Success
) goto Quit32
;
393 /* Write the "bootsector" */
394 RtlCopyMemory(SEG_OFF_TO_PTR(0x0070, 0x0000), Startup
, sizeof(Startup
));
396 /* Register the DOS Starting BOP */
397 RegisterBop(BOP_START_DOS
, DosStart
);
399 /* Position execution pointers and return */
406 DisplayMessage(L
"DOS32 loading failed, what to do??");
412 * We succeeded, deregister the DOS Loading BOP
413 * so that no app will be able to call us back.
415 RegisterBop(BOP_LOAD_DOS
, NULL
);
419 static VOID WINAPI
DosStart(LPWORD Stack
)
423 CHAR ApplicationName
[MAX_PATH
];
424 CHAR CommandLine
[DOS_CMDLINE_LENGTH
];
427 DPRINT("DosStart\n");
430 * We succeeded, deregister the DOS Starting BOP
431 * so that no app will be able to call us back.
433 RegisterBop(BOP_START_DOS
, NULL
);
435 /* Load the mouse driver */
436 DosMouseInitialize();
440 /* Create the GetNextVDMCommand thread */
441 CommandThread
= CreateThread(NULL
, 0, &CommandThreadProc
, NULL
, 0, NULL
);
442 if (CommandThread
== NULL
)
444 wprintf(L
"FATAL: Failed to create the command processing thread: %d\n", GetLastError());
448 /* Wait for the command thread to exit */
449 WaitForSingleObject(CommandThread
, INFINITE
);
451 /* Close the thread handle */
452 CloseHandle(CommandThread
);
458 WideCharToMultiByte(CP_ACP
, 0, NtVdmArgv
[1], -1, ApplicationName
, sizeof(ApplicationName
), NULL
, NULL
);
461 WideCharToMultiByte(CP_ACP
, 0, NtVdmArgv
[2], -1, CommandLine
, sizeof(CommandLine
), NULL
, NULL
);
463 strcpy(CommandLine
, "");
467 DisplayMessage(L
"Invalid DOS command line\n");
471 /* Start the process from the command line */
472 Result
= DosStartProcess(ApplicationName
, CommandLine
,
473 SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK
, 0));
474 if (Result
!= ERROR_SUCCESS
)
476 DisplayMessage(L
"Could not start '%S'. Error: %u", ApplicationName
, Result
);
487 /* PUBLIC EXPORTED APIS *******************************************************/
490 // demLFNGetCurrentDirectory
492 // demGetFileTimeByHandle_WOW
493 // demWOWLFNAllocateSearchHandle
494 // demWOWLFNCloseSearchHandle
496 // demWOWLFNGetSearchHandle
501 demClientErrorEx(IN HANDLE FileHandle
,
506 return GetLastError();
511 demFileDelete(IN LPCSTR FileName
)
513 if (DeleteFileA(FileName
)) SetLastError(ERROR_SUCCESS
);
515 return GetLastError();
520 demFileFindFirst(OUT PVOID lpFindFileData
,
524 BOOLEAN Success
= TRUE
;
525 WIN32_FIND_DATAA FindData
;
526 PDOS_FIND_FILE_BLOCK FindFileBlock
= (PDOS_FIND_FILE_BLOCK
)lpFindFileData
;
529 FindFileBlock
->DriveLetter
= CurrentDrive
+ 'A';
530 FindFileBlock
->AttribMask
= AttribMask
;
531 FindFileBlock
->SearchHandle
= FindFirstFileA(FileName
, &FindData
);
532 if (FindFileBlock
->SearchHandle
== INVALID_HANDLE_VALUE
) return GetLastError();
536 /* Check the attributes */
537 if (!((FindData
.dwFileAttributes
& (FILE_ATTRIBUTE_HIDDEN
|
538 FILE_ATTRIBUTE_SYSTEM
|
539 FILE_ATTRIBUTE_DIRECTORY
))
545 while ((Success
= FindNextFileA(FindFileBlock
->SearchHandle
, &FindData
)));
547 if (!Success
) return GetLastError();
549 FindFileBlock
->Attributes
= LOBYTE(FindData
.dwFileAttributes
);
550 FileTimeToDosDateTime(&FindData
.ftLastWriteTime
,
551 &FindFileBlock
->FileDate
,
552 &FindFileBlock
->FileTime
);
553 FindFileBlock
->FileSize
= FindData
.nFileSizeHigh
? 0xFFFFFFFF
554 : FindData
.nFileSizeLow
;
555 strcpy(FindFileBlock
->FileName
, FindData
.cAlternateFileName
);
557 return ERROR_SUCCESS
;
562 demFileFindNext(OUT PVOID lpFindFileData
)
564 WIN32_FIND_DATAA FindData
;
565 PDOS_FIND_FILE_BLOCK FindFileBlock
= (PDOS_FIND_FILE_BLOCK
)lpFindFileData
;
569 if (!FindNextFileA(FindFileBlock
->SearchHandle
, &FindData
))
570 return GetLastError();
572 /* Update the block */
573 FindFileBlock
->Attributes
= LOBYTE(FindData
.dwFileAttributes
);
574 FileTimeToDosDateTime(&FindData
.ftLastWriteTime
,
575 &FindFileBlock
->FileDate
,
576 &FindFileBlock
->FileTime
);
577 FindFileBlock
->FileSize
= FindData
.nFileSizeHigh
? 0xFFFFFFFF
578 : FindData
.nFileSizeLow
;
579 strcpy(FindFileBlock
->FileName
, FindData
.cAlternateFileName
);
581 while((FindData
.dwFileAttributes
& (FILE_ATTRIBUTE_HIDDEN
|
582 FILE_ATTRIBUTE_SYSTEM
|
583 FILE_ATTRIBUTE_DIRECTORY
))
584 & ~FindFileBlock
->AttribMask
);
586 return ERROR_SUCCESS
;
591 demGetPhysicalDriveType(IN UCHAR DriveNumber
)
594 return DOSDEVICE_DRIVE_UNKNOWN
;
599 demIsShortPathName(IN LPCSTR Path
,
608 demSetCurrentDirectoryGetDrive(IN LPCSTR CurrentDirectory
,
609 OUT PUCHAR DriveNumber
)
612 return ERROR_SUCCESS
;