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 *******************************************************************/
23 #include "bios/bios.h"
26 /* Extra PSDK/NDK Headers */
27 #include <ndk/obtypes.h>
29 /* PRIVATE VARIABLES **********************************************************/
31 /**/extern BYTE CurrentDrive
;/**/
33 /* DEFINES ********************************************************************/
36 #define BOP_LOAD_DOS 0x2B // DOS Loading and Initializing BOP. In parameter (following bytes) we take a NULL-terminated string indicating the name of the DOS kernel file.
37 #define BOP_START_DOS 0x2C // DOS Starting BOP. In parameter (following bytes) we take a NULL-terminated string indicating the name of the DOS kernel file.
38 #define BOP_DOS 0x50 // DOS System BOP (for NTIO.SYS and NTDOS.SYS)
39 #define BOP_CMD 0x54 // DOS Command Interpreter BOP (for COMMAND.COM)
41 /* PRIVATE FUNCTIONS **********************************************************/
43 static VOID WINAPI
DosSystemBop(LPWORD Stack
)
45 /* Get the Function Number and skip it */
46 BYTE FuncNum
= *(PBYTE
)SEG_OFF_TO_PTR(getCS(), getIP());
51 case 0x11: // Load the DOS kernel
53 BOOLEAN Success
= FALSE
;
55 ULONG ulDosKernelSize
= 0;
57 DPRINT1("You are loading Windows NT DOS!\n");
59 /* Open the DOS kernel file */
60 hDosKernel
= FileOpen("ntdos.sys", &ulDosKernelSize
);
62 /* If we failed, bail out */
63 if (hDosKernel
== NULL
) goto Quit
;
66 * Attempt to load the DOS kernel into memory.
67 * The segment where to load the DOS kernel is defined
68 * by the DOS BIOS and is found in DI:0000 .
70 Success
= FileLoadByHandle(hDosKernel
,
71 REAL_TO_PHYS(TO_LINEAR(getDI(), 0x0000)),
75 DPRINT1("Windows NT DOS loading %s at %04X:%04X, size 0x%X ; GetLastError() = %u\n",
76 (Success
? "succeeded" : "failed"),
81 /* Close the DOS kernel file */
82 FileClose(hDosKernel
);
87 /* We failed everything, stop the VDM */
96 DPRINT1("Unknown DOS System BOP Function: 0x%02X\n", FuncNum
);
97 // setCF(1); // Disable, otherwise we enter an infinite loop
103 static VOID WINAPI
DosCmdInterpreterBop(LPWORD Stack
)
105 /* Get the Function Number and skip it */
106 BYTE FuncNum
= *(PBYTE
)SEG_OFF_TO_PTR(getCS(), getIP());
111 case 0x08: // Launch external command
113 #define CMDLINE_LENGTH 1024
118 LPSTR Command
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getSI());
119 LPSTR CmdPtr
= Command
;
120 CHAR CommandLine
[CMDLINE_LENGTH
] = "";
121 STARTUPINFOA StartupInfo
;
122 PROCESS_INFORMATION ProcessInformation
;
124 /* NULL-terminate the command line by removing the return carriage character */
125 while (*CmdPtr
&& *CmdPtr
!= '\r') CmdPtr
++;
128 DPRINT1("CMD Run Command '%s'\n", Command
);
130 /* Spawn a user-defined 32-bit command preprocessor */
132 /* Build the command line */
133 // FIXME: Use COMSPEC env var!!
134 strcpy(CommandLine
, "cmd.exe /c ");
135 strcat(CommandLine
, Command
);
137 RtlZeroMemory(&StartupInfo
, sizeof(StartupInfo
));
138 RtlZeroMemory(&ProcessInformation
, sizeof(ProcessInformation
));
140 StartupInfo
.cb
= sizeof(StartupInfo
);
142 VidBiosDetachFromConsole();
144 Result
= CreateProcessA(NULL
,
153 &ProcessInformation
);
156 DPRINT1("Command '%s' launched successfully\n", Command
);
158 /* Wait for process termination */
159 WaitForSingleObject(ProcessInformation
.hProcess
, INFINITE
);
161 /* Get the exit code */
162 GetExitCodeProcess(ProcessInformation
.hProcess
, &dwExitCode
);
165 CloseHandle(ProcessInformation
.hThread
);
166 CloseHandle(ProcessInformation
.hProcess
);
170 DPRINT1("Failed when launched command '%s'\n");
171 dwExitCode
= GetLastError();
174 VidBiosAttachToConsole();
176 setAL((UCHAR
)dwExitCode
);
183 DPRINT1("Unknown DOS CMD Interpreter BOP Function: 0x%02X\n", FuncNum
);
184 // setCF(1); // Disable, otherwise we enter an infinite loop
193 CommandThreadProc(LPVOID Parameter
)
195 BOOLEAN First
= TRUE
;
197 VDM_COMMAND_INFO CommandInfo
;
198 CHAR CmdLine
[MAX_PATH
];
199 CHAR AppName
[MAX_PATH
];
200 CHAR PifFile
[MAX_PATH
];
201 CHAR Desktop
[MAX_PATH
];
202 CHAR Title
[MAX_PATH
];
204 PVOID Env
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, EnvSize
);
206 UNREFERENCED_PARAMETER(Parameter
);
211 /* Clear the structure */
212 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
214 /* Initialize the structure members */
215 CommandInfo
.TaskId
= SessionId
;
216 CommandInfo
.VDMState
= VDM_FLAG_DOS
;
217 CommandInfo
.CmdLine
= CmdLine
;
218 CommandInfo
.CmdLen
= sizeof(CmdLine
);
219 CommandInfo
.AppName
= AppName
;
220 CommandInfo
.AppLen
= sizeof(AppName
);
221 CommandInfo
.PifFile
= PifFile
;
222 CommandInfo
.PifLen
= sizeof(PifFile
);
223 CommandInfo
.Desktop
= Desktop
;
224 CommandInfo
.DesktopLen
= sizeof(Desktop
);
225 CommandInfo
.Title
= Title
;
226 CommandInfo
.TitleLen
= sizeof(Title
);
227 CommandInfo
.Env
= Env
;
228 CommandInfo
.EnvLen
= EnvSize
;
230 if (First
) CommandInfo
.VDMState
|= VDM_FLAG_FIRST_TASK
;
233 if (!GetNextVDMCommand(&CommandInfo
))
235 if (CommandInfo
.EnvLen
> EnvSize
)
237 /* Expand the environment size */
238 EnvSize
= CommandInfo
.EnvLen
;
239 Env
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, Env
, EnvSize
);
241 /* Repeat the request */
248 /* Start the process from the command line */
249 DPRINT("Starting '%s' ('%s')...\n", AppName
, CmdLine
);
250 Result
= DosStartProcess(AppName
, CmdLine
, Env
);
251 if (Result
!= ERROR_SUCCESS
)
253 DisplayMessage(L
"Could not start '%S'. Error: %u", AppName
, Result
);
260 while (AcceptCommands
);
262 HeapFree(GetProcessHeap(), 0, Env
);
267 /* PUBLIC FUNCTIONS ***********************************************************/
270 // This function (equivalent of the DOS bootsector) is called by the bootstrap
271 // loader *BEFORE* jumping at 0000:7C00. What we should do is to write at 0000:7C00
272 // a BOP call that calls DosInitialize back. Then the bootstrap loader jumps at
273 // 0000:7C00, our BOP gets called and then we can initialize the 32-bit part of the DOS.
276 /* 16-bit bootstrap code at 0000:7C00 */
277 /* Of course, this is not in real bootsector format, because we don't care */
278 static BYTE Bootsector1
[] =
280 LOBYTE(EMULATOR_BOP
), HIBYTE(EMULATOR_BOP
), BOP_LOAD_DOS
, // Call DOS Loading
282 /* This portion of code is run if we failed to load the DOS */
283 static BYTE Bootsector2
[] =
286 0x5B, 0xE0, 0x00, 0xF0, // F000:E05B /** HACK! What to do instead?? **/
289 static VOID WINAPI
DosInitialize(LPWORD Stack
);
291 VOID
DosBootsectorInitialize(VOID
)
293 /* We write the bootsector at 0000:7C00 */
294 ULONG_PTR Address
= (ULONG_PTR
)SEG_OFF_TO_PTR(0x0000, 0x7C00);
295 CHAR DosKernelFileName
[] = ""; // No DOS file name, therefore we'll load DOS32
297 DPRINT("DosBootsectorInitialize\n");
299 /* Write the "bootsector" */
300 RtlCopyMemory((PVOID
)Address
, Bootsector1
, sizeof(Bootsector1
));
301 Address
+= sizeof(Bootsector1
);
302 RtlCopyMemory((PVOID
)Address
, DosKernelFileName
, sizeof(DosKernelFileName
));
303 Address
+= sizeof(DosKernelFileName
);
304 RtlCopyMemory((PVOID
)Address
, Bootsector2
, sizeof(Bootsector2
));
306 /* Register the DOS Loading BOP */
307 RegisterBop(BOP_LOAD_DOS
, DosInitialize
);
312 // This function is called by the DOS bootsector. We finish to load
313 // the DOS, then we jump to 0070:0000.
316 /* 16-bit startup code at 0070:0000 */
317 static BYTE Startup
[] =
319 LOBYTE(EMULATOR_BOP
), HIBYTE(EMULATOR_BOP
), BOP_START_DOS
, // Call DOS Start
322 static VOID WINAPI
DosStart(LPWORD Stack
);
324 static VOID WINAPI
DosInitialize(LPWORD Stack
)
326 BOOLEAN Success
= FALSE
;
328 /* Get the DOS kernel file name (NULL-terminated) */
329 // FIXME: Isn't it possible to use some DS:SI instead??
330 LPCSTR DosKernelFileName
= (LPCSTR
)SEG_OFF_TO_PTR(getCS(), getIP());
331 setIP(getIP() + strlen(DosKernelFileName
) + 1); // Skip it
333 DPRINT("DosInitialize('%s')\n", DosKernelFileName
);
335 /* Register the DOS BOPs */
336 RegisterBop(BOP_DOS
, DosSystemBop
);
337 RegisterBop(BOP_CMD
, DosCmdInterpreterBop
);
339 if (DosKernelFileName
&& DosKernelFileName
[0] != '\0')
342 ULONG ulDosBiosSize
= 0;
344 /* Open the DOS BIOS file */
345 hDosBios
= FileOpen(DosKernelFileName
, &ulDosBiosSize
);
347 /* If we failed, bail out */
348 if (hDosBios
== NULL
) goto QuitCustom
;
350 /* Attempt to load the DOS BIOS into memory */
351 Success
= FileLoadByHandle(hDosBios
,
352 REAL_TO_PHYS(TO_LINEAR(0x0070, 0x0000)),
356 DPRINT1("DOS BIOS loading %s at %04X:%04X, size 0x%X ; GetLastError() = %u\n",
357 (Success
? "succeeded" : "failed"),
362 /* Close the DOS BIOS file */
365 if (!Success
) goto QuitCustom
;
367 /* Position execution pointers and return */
374 DisplayMessage(L
"Custom DOS '%S' loading failed, what to do??", DosKernelFileName
);
378 Success
= DosBIOSInitialize();
379 // Success &= DosKRNLInitialize();
381 if (!Success
) goto Quit32
;
383 /* Write the "bootsector" */
384 RtlCopyMemory(SEG_OFF_TO_PTR(0x0070, 0x0000), Startup
, sizeof(Startup
));
386 /* Register the DOS Starting BOP */
387 RegisterBop(BOP_START_DOS
, DosStart
);
389 /* Position execution pointers and return */
396 DisplayMessage(L
"DOS32 loading failed, what to do??");
402 * We succeeded, deregister the DOS Loading BOP
403 * so that no app will be able to call us back.
405 RegisterBop(BOP_LOAD_DOS
, NULL
);
409 static VOID WINAPI
DosStart(LPWORD Stack
)
413 CHAR ApplicationName
[MAX_PATH
];
414 CHAR CommandLine
[DOS_CMDLINE_LENGTH
];
417 DPRINT("DosStart\n");
420 * We succeeded, deregister the DOS Starting BOP
421 * so that no app will be able to call us back.
423 RegisterBop(BOP_START_DOS
, NULL
);
425 /* Load the mouse driver */
426 DosMouseInitialize();
430 /* Create the GetNextVDMCommand thread */
431 CommandThread
= CreateThread(NULL
, 0, &CommandThreadProc
, NULL
, 0, NULL
);
432 if (CommandThread
== NULL
)
434 wprintf(L
"FATAL: Failed to create the command processing thread: %d\n", GetLastError());
438 /* Wait for the command thread to exit */
439 WaitForSingleObject(CommandThread
, INFINITE
);
441 /* Close the thread handle */
442 CloseHandle(CommandThread
);
448 WideCharToMultiByte(CP_ACP
, 0, NtVdmArgv
[1], -1, ApplicationName
, sizeof(ApplicationName
), NULL
, NULL
);
451 WideCharToMultiByte(CP_ACP
, 0, NtVdmArgv
[2], -1, CommandLine
, sizeof(CommandLine
), NULL
, NULL
);
453 strcpy(CommandLine
, "");
457 DisplayMessage(L
"Invalid DOS command line\n");
461 /* Start the process from the command line */
462 DPRINT1("Starting '%s' ('%s')...\n", ApplicationName
, CommandLine
);
463 Result
= DosStartProcess(ApplicationName
,
465 SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK
, 0));
466 if (Result
!= ERROR_SUCCESS
)
468 DisplayMessage(L
"Could not start '%S'. Error: %u", ApplicationName
, Result
);
479 /* PUBLIC EXPORTED APIS *******************************************************/
482 // demLFNGetCurrentDirectory
484 // demGetFileTimeByHandle_WOW
485 // demWOWLFNAllocateSearchHandle
486 // demWOWLFNCloseSearchHandle
488 // demWOWLFNGetSearchHandle
493 demClientErrorEx(IN HANDLE FileHandle
,
498 return GetLastError();
503 demFileDelete(IN LPCSTR FileName
)
505 if (DeleteFileA(FileName
)) SetLastError(ERROR_SUCCESS
);
507 return GetLastError();
512 demFileFindFirst(OUT PVOID lpFindFileData
,
516 BOOLEAN Success
= TRUE
;
517 WIN32_FIND_DATAA FindData
;
518 PDOS_FIND_FILE_BLOCK FindFileBlock
= (PDOS_FIND_FILE_BLOCK
)lpFindFileData
;
521 FindFileBlock
->DriveLetter
= CurrentDrive
+ 'A';
522 FindFileBlock
->AttribMask
= AttribMask
;
523 FindFileBlock
->SearchHandle
= FindFirstFileA(FileName
, &FindData
);
524 if (FindFileBlock
->SearchHandle
== INVALID_HANDLE_VALUE
) return GetLastError();
528 /* Check the attributes */
529 if (!((FindData
.dwFileAttributes
& (FILE_ATTRIBUTE_HIDDEN
|
530 FILE_ATTRIBUTE_SYSTEM
|
531 FILE_ATTRIBUTE_DIRECTORY
))
537 while ((Success
= FindNextFileA(FindFileBlock
->SearchHandle
, &FindData
)));
539 if (!Success
) return GetLastError();
541 FindFileBlock
->Attributes
= LOBYTE(FindData
.dwFileAttributes
);
542 FileTimeToDosDateTime(&FindData
.ftLastWriteTime
,
543 &FindFileBlock
->FileDate
,
544 &FindFileBlock
->FileTime
);
545 FindFileBlock
->FileSize
= FindData
.nFileSizeHigh
? 0xFFFFFFFF
546 : FindData
.nFileSizeLow
;
547 strcpy(FindFileBlock
->FileName
, FindData
.cAlternateFileName
);
549 return ERROR_SUCCESS
;
554 demFileFindNext(OUT PVOID lpFindFileData
)
556 WIN32_FIND_DATAA FindData
;
557 PDOS_FIND_FILE_BLOCK FindFileBlock
= (PDOS_FIND_FILE_BLOCK
)lpFindFileData
;
561 if (!FindNextFileA(FindFileBlock
->SearchHandle
, &FindData
))
562 return GetLastError();
564 /* Update the block */
565 FindFileBlock
->Attributes
= LOBYTE(FindData
.dwFileAttributes
);
566 FileTimeToDosDateTime(&FindData
.ftLastWriteTime
,
567 &FindFileBlock
->FileDate
,
568 &FindFileBlock
->FileTime
);
569 FindFileBlock
->FileSize
= FindData
.nFileSizeHigh
? 0xFFFFFFFF
570 : FindData
.nFileSizeLow
;
571 strcpy(FindFileBlock
->FileName
, FindData
.cAlternateFileName
);
573 while((FindData
.dwFileAttributes
& (FILE_ATTRIBUTE_HIDDEN
|
574 FILE_ATTRIBUTE_SYSTEM
|
575 FILE_ATTRIBUTE_DIRECTORY
))
576 & ~FindFileBlock
->AttribMask
);
578 return ERROR_SUCCESS
;
583 demGetPhysicalDriveType(IN UCHAR DriveNumber
)
586 return DOSDEVICE_DRIVE_UNKNOWN
;
591 demIsShortPathName(IN LPCSTR Path
,
600 demSetCurrentDirectoryGetDrive(IN LPCSTR CurrentDirectory
,
601 OUT PUCHAR DriveNumber
)
604 return ERROR_SUCCESS
;