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 PDOS_DATA DosData
;
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 */
87 /* Call 32-bit Driver Strategy Routine */
88 case BOP_DRV_STRATEGY
:
94 /* Call 32-bit Driver Interrupt Routine */
95 case BOP_DRV_INTERRUPT
:
103 DPRINT1("Unknown DOS System BOP Function: 0x%02X\n", FuncNum
);
104 // setCF(1); // Disable, otherwise we enter an infinite loop
110 static VOID WINAPI
DosCmdInterpreterBop(LPWORD Stack
)
112 /* Get the Function Number and skip it */
113 BYTE FuncNum
= *(PBYTE
)SEG_OFF_TO_PTR(getCS(), getIP());
118 case 0x08: // Launch external command
123 LPSTR Command
= (LPSTR
)SEG_OFF_TO_PTR(getDS(), getSI());
124 CHAR CmdLine
[sizeof("cmd.exe /c ") + DOS_CMDLINE_LENGTH
+ 1] = "";
127 STARTUPINFOA StartupInfo
;
128 PROCESS_INFORMATION ProcessInformation
;
130 /* Spawn a user-defined 32-bit command preprocessor */
132 // FIXME: Use COMSPEC env var!!
133 CmdLinePtr
= CmdLine
;
134 strcpy(CmdLinePtr
, "cmd.exe /c ");
135 CmdLinePtr
+= strlen(CmdLinePtr
);
137 /* Build a Win32-compatible command-line */
138 CmdLineLen
= min(strlen(Command
), sizeof(CmdLine
) - strlen(CmdLinePtr
) - 1);
139 RtlCopyMemory(CmdLinePtr
, Command
, CmdLineLen
);
140 CmdLinePtr
[CmdLineLen
] = '\0';
142 /* Remove any trailing return carriage character and NULL-terminate the command line */
143 while (*CmdLinePtr
&& *CmdLinePtr
!= '\r' && *CmdLinePtr
!= '\n') CmdLinePtr
++;
146 DPRINT1("CMD Run Command '%s' ('%s')\n", Command
, CmdLine
);
148 RtlZeroMemory(&StartupInfo
, sizeof(StartupInfo
));
149 RtlZeroMemory(&ProcessInformation
, sizeof(ProcessInformation
));
151 StartupInfo
.cb
= sizeof(StartupInfo
);
153 VidBiosDetachFromConsole();
155 Result
= CreateProcessA(NULL
,
164 &ProcessInformation
);
167 DPRINT1("Command '%s' ('%s') launched successfully\n", Command
, CmdLine
);
169 /* Wait for process termination */
170 WaitForSingleObject(ProcessInformation
.hProcess
, INFINITE
);
172 /* Get the exit code */
173 GetExitCodeProcess(ProcessInformation
.hProcess
, &dwExitCode
);
176 CloseHandle(ProcessInformation
.hThread
);
177 CloseHandle(ProcessInformation
.hProcess
);
181 DPRINT1("Failed when launched command '%s' ('%s')\n", Command
, CmdLine
);
182 dwExitCode
= GetLastError();
185 VidBiosAttachToConsole();
187 setAL((UCHAR
)dwExitCode
);
194 DPRINT1("Unknown DOS CMD Interpreter BOP Function: 0x%02X\n", FuncNum
);
195 // setCF(1); // Disable, otherwise we enter an infinite loop
204 CommandThreadProc(LPVOID Parameter
)
206 BOOLEAN First
= TRUE
;
208 VDM_COMMAND_INFO CommandInfo
;
209 CHAR CmdLine
[MAX_PATH
];
210 CHAR AppName
[MAX_PATH
];
211 CHAR PifFile
[MAX_PATH
];
212 CHAR Desktop
[MAX_PATH
];
213 CHAR Title
[MAX_PATH
];
215 PVOID Env
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, EnvSize
);
217 UNREFERENCED_PARAMETER(Parameter
);
220 /* Clear the structure */
221 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
223 /* Get the initial information */
224 CommandInfo
.TaskId
= SessionId
;
225 CommandInfo
.VDMState
= VDM_GET_FIRST_COMMAND
| VDM_FLAG_DOS
;
226 GetNextVDMCommand(&CommandInfo
);
230 /* Clear the structure */
231 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
233 /* Initialize the structure members */
234 CommandInfo
.TaskId
= SessionId
;
235 CommandInfo
.VDMState
= VDM_FLAG_DOS
;
236 CommandInfo
.CmdLine
= CmdLine
;
237 CommandInfo
.CmdLen
= sizeof(CmdLine
);
238 CommandInfo
.AppName
= AppName
;
239 CommandInfo
.AppLen
= sizeof(AppName
);
240 CommandInfo
.PifFile
= PifFile
;
241 CommandInfo
.PifLen
= sizeof(PifFile
);
242 CommandInfo
.Desktop
= Desktop
;
243 CommandInfo
.DesktopLen
= sizeof(Desktop
);
244 CommandInfo
.Title
= Title
;
245 CommandInfo
.TitleLen
= sizeof(Title
);
246 CommandInfo
.Env
= Env
;
247 CommandInfo
.EnvLen
= EnvSize
;
249 if (First
) CommandInfo
.VDMState
|= VDM_FLAG_FIRST_TASK
;
252 if (!GetNextVDMCommand(&CommandInfo
))
254 if (CommandInfo
.EnvLen
> EnvSize
)
256 /* Expand the environment size */
257 EnvSize
= CommandInfo
.EnvLen
;
258 CommandInfo
.Env
= Env
= RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, Env
, EnvSize
);
260 /* Repeat the request */
261 CommandInfo
.VDMState
|= VDM_FLAG_RETRY
;
268 /* Start the process from the command line */
269 Result
= DosStartProcess(AppName
, CmdLine
, Env
, 0);
270 if (Result
!= ERROR_SUCCESS
)
272 DisplayMessage(L
"Could not start '%S'. Error: %u", AppName
, Result
);
279 while (AcceptCommands
);
281 RtlFreeHeap(RtlGetProcessHeap(), 0, Env
);
286 /* PUBLIC VARIABLES ***********************************************************/
289 BOOLEAN AcceptCommands
= TRUE
;
290 HANDLE CommandThread
= NULL
;
294 /* PUBLIC FUNCTIONS ***********************************************************/
297 // This function (equivalent of the DOS bootsector) is called by the bootstrap
298 // loader *BEFORE* jumping at 0000:7C00. What we should do is to write at 0000:7C00
299 // a BOP call that calls DosInitialize back. Then the bootstrap loader jumps at
300 // 0000:7C00, our BOP gets called and then we can initialize the 32-bit part of the DOS.
303 /* 16-bit bootstrap code at 0000:7C00 */
304 /* Of course, this is not in real bootsector format, because we don't care */
305 static BYTE Bootsector1
[] =
307 LOBYTE(EMULATOR_BOP
), HIBYTE(EMULATOR_BOP
), BOP_LOAD_DOS
, // Call DOS Loading
309 /* This portion of code is run if we failed to load the DOS */
310 static BYTE Bootsector2
[] =
313 0x5B, 0xE0, 0x00, 0xF0, // F000:E05B /** HACK! What to do instead?? **/
316 static VOID WINAPI
DosInitialize(LPWORD Stack
);
318 VOID
DosBootsectorInitialize(VOID
)
320 /* We write the bootsector at 0000:7C00 */
321 ULONG_PTR Address
= (ULONG_PTR
)SEG_OFF_TO_PTR(0x0000, 0x7C00);
322 CHAR DosKernelFileName
[] = ""; // No DOS file name, therefore we'll load DOS32
324 DPRINT("DosBootsectorInitialize\n");
326 /* Write the "bootsector" */
327 RtlCopyMemory((PVOID
)Address
, Bootsector1
, sizeof(Bootsector1
));
328 Address
+= sizeof(Bootsector1
);
329 RtlCopyMemory((PVOID
)Address
, DosKernelFileName
, sizeof(DosKernelFileName
));
330 Address
+= sizeof(DosKernelFileName
);
331 RtlCopyMemory((PVOID
)Address
, Bootsector2
, sizeof(Bootsector2
));
333 /* Register the DOS Loading BOP */
334 RegisterBop(BOP_LOAD_DOS
, DosInitialize
);
339 // This function is called by the DOS bootsector. We finish to load
340 // the DOS, then we jump to 0070:0000.
343 /* 16-bit startup code at 0070:0000 */
344 static BYTE Startup
[] =
346 LOBYTE(EMULATOR_BOP
), HIBYTE(EMULATOR_BOP
), BOP_START_DOS
, // Call DOS Start
349 static VOID WINAPI
DosStart(LPWORD Stack
);
351 static VOID WINAPI
DosInitialize(LPWORD Stack
)
353 BOOLEAN Success
= FALSE
;
355 /* Get the DOS kernel file name (NULL-terminated) */
356 // FIXME: Isn't it possible to use some DS:SI instead??
357 LPCSTR DosKernelFileName
= (LPCSTR
)SEG_OFF_TO_PTR(getCS(), getIP());
358 setIP(getIP() + strlen(DosKernelFileName
) + 1); // Skip it
360 DPRINT("DosInitialize('%s')\n", DosKernelFileName
);
362 /* Register the DOS BOPs */
363 RegisterBop(BOP_DOS
, DosSystemBop
);
364 RegisterBop(BOP_CMD
, DosCmdInterpreterBop
);
366 if (DosKernelFileName
&& DosKernelFileName
[0] != '\0')
369 ULONG ulDosBiosSize
= 0;
371 /* Open the DOS BIOS file */
372 hDosBios
= FileOpen(DosKernelFileName
, &ulDosBiosSize
);
374 /* If we failed, bail out */
375 if (hDosBios
== NULL
) goto QuitCustom
;
377 /* Attempt to load the DOS BIOS into memory */
378 Success
= FileLoadByHandle(hDosBios
,
379 REAL_TO_PHYS(TO_LINEAR(0x0070, 0x0000)),
383 DPRINT1("DOS BIOS loading %s at %04X:%04X, size 0x%X ; GetLastError() = %u\n",
384 (Success
? "succeeded" : "failed"),
389 /* Close the DOS BIOS file */
392 if (!Success
) goto QuitCustom
;
394 /* Position execution pointers and return */
401 DisplayMessage(L
"Custom DOS '%S' loading failed, what to do??", DosKernelFileName
);
405 Success
= DosBIOSInitialize();
406 // Success &= DosKRNLInitialize();
408 if (!Success
) goto Quit32
;
410 /* Write the "bootsector" */
411 RtlCopyMemory(SEG_OFF_TO_PTR(0x0070, 0x0000), Startup
, sizeof(Startup
));
413 /* Register the DOS Starting BOP */
414 RegisterBop(BOP_START_DOS
, DosStart
);
416 /* Position execution pointers and return */
423 DisplayMessage(L
"DOS32 loading failed, what to do??");
429 * We succeeded, deregister the DOS Loading BOP
430 * so that no app will be able to call us back.
432 RegisterBop(BOP_LOAD_DOS
, NULL
);
436 static VOID WINAPI
DosStart(LPWORD Stack
)
440 CHAR ApplicationName
[MAX_PATH
];
441 CHAR CommandLine
[DOS_CMDLINE_LENGTH
];
446 DPRINT("DosStart\n");
449 * We succeeded, deregister the DOS Starting BOP
450 * so that no app will be able to call us back.
452 RegisterBop(BOP_START_DOS
, NULL
);
454 /* Load the mouse driver */
455 DosMouseInitialize();
459 /* Parse the command line arguments */
460 for (i
= 1; i
< NtVdmArgc
; i
++)
462 if (wcsncmp(NtVdmArgv
[i
], L
"-i", 2) == 0)
464 /* This is the session ID */
465 SessionId
= wcstoul(NtVdmArgv
[i
] + 2, NULL
, 10);
467 /* The VDM hasn't been started from a console, so quit when the task is done */
468 AcceptCommands
= FALSE
;
472 /* Create the GetNextVDMCommand thread */
473 CommandThread
= CreateThread(NULL
, 0, &CommandThreadProc
, NULL
, 0, NULL
);
474 if (CommandThread
== NULL
)
476 wprintf(L
"FATAL: Failed to create the command processing thread: %d\n", GetLastError());
480 /* Wait for the command thread to exit */
481 WaitForSingleObject(CommandThread
, INFINITE
);
483 /* Close the thread handle */
484 CloseHandle(CommandThread
);
490 WideCharToMultiByte(CP_ACP
, 0, NtVdmArgv
[1], -1, ApplicationName
, sizeof(ApplicationName
), NULL
, NULL
);
493 WideCharToMultiByte(CP_ACP
, 0, NtVdmArgv
[2], -1, CommandLine
, sizeof(CommandLine
), NULL
, NULL
);
495 strcpy(CommandLine
, "");
499 DisplayMessage(L
"Invalid DOS command line\n");
503 /* Start the process from the command line */
504 Result
= DosStartProcess(ApplicationName
, CommandLine
,
505 SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK
, 0));
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
;