RtlCopyMemory(PspBlock->CommandLine, CommandLine, DOS_CMDLINE_LENGTH);
}
+static inline VOID DosSaveState(VOID)
+{
+ PDOS_REGISTER_STATE State;
+ WORD StackPointer = getSP();
+
+ /* Allocate stack space for the registers */
+ StackPointer -= sizeof(DOS_REGISTER_STATE);
+ State = SEG_OFF_TO_PTR(getSS(), StackPointer);
+ setSP(StackPointer);
+
+ /* Save */
+ State->EAX = getEAX();
+ State->ECX = getECX();
+ State->EDX = getEDX();
+ State->EBX = getEBX();
+ State->ESP = getESP();
+ State->EBP = getEBP();
+ State->ESI = getESI();
+ State->EDI = getEDI();
+ State->DS = getDS();
+ State->ES = getES();
+ State->FS = getFS();
+ State->GS = getGS();
+ State->Flags = getEFLAGS();
+}
+
+static inline VOID DosRestoreState(VOID)
+{
+ PDOS_REGISTER_STATE State;
+
+ /* Pop the state structure from the stack */
+ State = SEG_OFF_TO_PTR(getSS(), getSP());
+ setSP(getSP() + sizeof(DOS_REGISTER_STATE));
+
+ /* Restore */
+ setEAX(State->EAX);
+ setECX(State->ECX);
+ setEDX(State->EDX);
+ setEBX(State->EBX);
+ setEBP(State->EBP);
+ setESI(State->ESI);
+ setEDI(State->EDI);
+ setDS(State->DS);
+ setES(State->ES);
+ setFS(State->FS);
+ setGS(State->GS);
+ setEFLAGS(State->Flags);
+}
+
static WORD DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL,
LPCSTR ProgramName)
{
IN LPCSTR ExecutablePath,
IN PDOS_EXEC_PARAM_BLOCK Parameters,
IN LPCSTR CommandLine OPTIONAL,
- IN LPCSTR Environment OPTIONAL,
- IN DWORD ReturnAddress OPTIONAL)
+ IN LPCSTR Environment OPTIONAL)
{
DWORD Result = ERROR_SUCCESS;
HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
WORD LoadSegment;
WORD MaxAllocSize;
DWORD i, FileSize;
+ CHAR FullPath[MAX_PATH];
+ CHAR ShortFullPath[MAX_PATH];
/* Buffer for command line conversion: 1 byte for size; 127 bytes for contents */
CHAR CmdLineBuffer[1 + DOS_CMDLINE_LENGTH];
- DPRINT1("DosLoadExecutable(%d, %s, 0x%08X, 0x%08X)\n",
+ DPRINT1("DosLoadExecutable(%d, '%s', 0x%08X, 0x%08X, 0x%08X)\n",
LoadType,
ExecutablePath,
Parameters,
- ReturnAddress);
+ CommandLine,
+ Environment);
+
+ /* Try to get the full path to the executable */
+ if (GetFullPathNameA(ExecutablePath, sizeof(FullPath), FullPath, NULL))
+ {
+ /* Try to shorten the full path */
+ if (GetShortPathNameA(FullPath, ShortFullPath, sizeof(ShortFullPath)))
+ {
+ /* Use the shortened full path from now on */
+ ExecutablePath = ShortFullPath;
+ }
+ }
/* Open a handle to the executable */
FileHandle = CreateFileA(ExecutablePath,
Header = (PIMAGE_DOS_HEADER)Address;
/* Get the base size of the file, in paragraphs (rounded up) */
- BaseSize = (((Header->e_cp - 1) * 512) + Header->e_cblp + 0x0F) >> 4;
+ BaseSize = ((((Header->e_cp - (Header->e_cblp != 0)) * 512)
+ + Header->e_cblp + 0x0F) >> 4) - Header->e_cparhdr;
if (LoadType != DOS_LOAD_OVERLAY)
{
DosChangeMemoryOwner(Segment, Segment);
DosChangeMemoryOwner(EnvBlock, Segment);
- /* Set INT 22h to the return address */
- ((PULONG)BaseAddress)[0x22] = ReturnAddress;
+ /* Set INT 22h to the current CS:IP */
+ ((PULONG)BaseAddress)[0x22] = MAKELONG(getIP(), getCS());
/* Create the PSP */
DosCreatePsp(Segment, (WORD)TotalSize);
if (LoadType == DOS_LOAD_AND_EXECUTE)
{
+ /* Save the program state */
+ if (CurrentPsp != SYSTEM_PSP)
+ {
+ /* Push the task state */
+ DosSaveState();
+
+ /* Update the last stack in the PSP */
+ SEGMENT_TO_PSP(CurrentPsp)->LastStack = MAKELONG(getSP(), getSS());
+ }
+
/* Set the initial segment registers */
setDS(Segment);
setES(Segment);
DosChangeMemoryOwner(Segment, Segment);
DosChangeMemoryOwner(EnvBlock, Segment);
- /* Set INT 22h to the return address */
- ((PULONG)BaseAddress)[0x22] = ReturnAddress;
+ /* Set INT 22h to the current CS:IP */
+ ((PULONG)BaseAddress)[0x22] = MAKELONG(getIP(), getCS());
/* Create the PSP */
DosCreatePsp(Segment, MaxAllocSize);
if (LoadType == DOS_LOAD_AND_EXECUTE)
{
+ /* Save the program state */
+ if (CurrentPsp != SYSTEM_PSP)
+ {
+ /* Push the task state */
+ DosSaveState();
+
+ /* Update the last stack in the PSP */
+ SEGMENT_TO_PSP(CurrentPsp)->LastStack = MAKELONG(getSP(), getSS());
+ }
+
/* Set the initial segment registers */
setDS(Segment);
setES(Segment);
IN LPCSTR Environment OPTIONAL)
{
DWORD Result;
- LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
+
+ SIZE_T CmdLen = strlen(CommandLine);
+ DPRINT1("Starting '%s' ('%.*s')...\n",
+ ExecutablePath,
+ /* Display the command line without the terminating 0d 0a (and skip the terminating NULL) */
+ CmdLen >= 2 ? (CommandLine[CmdLen - 2] == '\r' ? CmdLen - 2
+ : CmdLen)
+ : CmdLen,
+ CommandLine);
Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
ExecutablePath,
NULL,
CommandLine,
- Environment,
- IntVecTable[0x20]);
+ Environment);
if (Result != ERROR_SUCCESS) goto Quit;
+#ifndef STANDALONE
+ /* Update console title if we run in a separate console */
+ if (SessionId != 0)
+ SetConsoleTitleA(ExecutablePath);
+#endif
+
/* Attach to the console */
ConsoleAttach();
VidBiosAttachToConsole();
- // HACK: Simulate a ENTER key release scancode on the PS/2 port because
- // some apps expect to read a key release scancode (> 0x80) when they
- // are started.
- // (hbelusca 2 May 2015: I'm not sure it's really useful. See r65012)
- // IOWriteB(PS2_CONTROL_PORT, 0xD2); // Next write is for the first PS/2 port
- // IOWriteB(PS2_DATA_PORT, 0x80 | 0x1C); // ENTER key release
-
/* Start simulation */
SetEvent(VdmTaskEvent);
CpuSimulate();
#ifndef STANDALONE
WORD DosCreateProcess(LPCSTR ProgramName,
- PDOS_EXEC_PARAM_BLOCK Parameters,
- DWORD ReturnAddress)
+ PDOS_EXEC_PARAM_BLOCK Parameters)
{
DWORD Result;
DWORD BinaryType;
LPVOID Environment = NULL;
VDM_COMMAND_INFO CommandInfo;
- CHAR CmdLine[MAX_PATH]; // DOS_CMDLINE_LENGTH + 1
+ CHAR CmdLine[MAX_PATH + DOS_CMDLINE_LENGTH + 1];
CHAR AppName[MAX_PATH];
CHAR PifFile[MAX_PATH];
CHAR Desktop[MAX_PATH];
StartupInfo.cb = sizeof(StartupInfo);
/*
- * Convert the DOS command line to Win32-compatible format.
+ * Convert the DOS command line to Win32-compatible format, by concatenating
+ * the program name with the converted command line.
* Format of the DOS command line: 1 byte for size; 127 bytes for contents.
*/
+ CmdLinePtr = CmdLine;
+ strncpy(CmdLinePtr, ProgramName, MAX_PATH); // Concatenate the program name
+ CmdLinePtr += strlen(CmdLinePtr);
+ *CmdLinePtr++ = ' '; // Add separating space
+
CmdLineSize = min(*(PBYTE)FAR_POINTER(Parameters->CommandLine), DOS_CMDLINE_LENGTH);
- RtlCopyMemory(CmdLine,
+ RtlCopyMemory(CmdLinePtr,
(LPSTR)FAR_POINTER(Parameters->CommandLine) + 1,
CmdLineSize);
/* NULL-terminate it */
- CmdLine[CmdLineSize] = '\0';
+ CmdLinePtr[CmdLineSize] = '\0';
/* Remove any trailing return carriage character and NULL-terminate the command line */
- CmdLinePtr = CmdLine;
while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
*CmdLinePtr = '\0';
AppName,
Parameters,
CmdLine,
- Env,
- ReturnAddress);
+ Env);
if (Result == ERROR_SUCCESS)
{
/* Increment the re-entry count */
PDOS_MCB CurrentMcb;
LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
+#ifndef STANDALONE
+ VDM_COMMAND_INFO CommandInfo;
+#endif
DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
Psp,
if (KeepResident)
{
/* Check if this is the PSP block and we should reduce its size */
- if (McbSegment == Psp && KeepResident < CurrentMcb->Size)
+ if ((McbSegment + 1) == Psp && KeepResident < CurrentMcb->Size)
{
/* Reduce the size of the block */
DosResizeMemory(McbSegment + 1, KeepResident, NULL);
{
ResetEvent(VdmTaskEvent);
CpuUnsimulate();
+ return;
}
}
#ifndef STANDALONE
- // FIXME: This is probably not the best way to do it
- /* Check if this was a nested DOS task */
- if (CurrentPsp != SYSTEM_PSP)
- {
- VDM_COMMAND_INFO CommandInfo;
- /* Decrement the re-entry count */
- CommandInfo.TaskId = SessionId;
- CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
- GetNextVDMCommand(&CommandInfo);
+ /* Decrement the re-entry count */
+ CommandInfo.TaskId = SessionId;
+ CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
+ GetNextVDMCommand(&CommandInfo);
- /* Clear the structure */
- RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
+ /* Clear the structure */
+ RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
+
+ /* Update the VDM state of the task */
+ CommandInfo.TaskId = SessionId;
+ CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
+ GetNextVDMCommand(&CommandInfo);
- /* Update the VDM state of the task */
- CommandInfo.TaskId = SessionId;
- CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
- GetNextVDMCommand(&CommandInfo);
- }
#endif
/* Save the return code - Normal termination */
setSS(HIWORD(SEGMENT_TO_PSP(CurrentPsp)->LastStack));
setSP(LOWORD(SEGMENT_TO_PSP(CurrentPsp)->LastStack));
+ /* Are we returning to DOS code? */
+ if (HIWORD(PspBlock->TerminateAddress) == DOS_CODE_SEGMENT)
+ {
+ /* Pop the task state */
+ DosRestoreState();
+ }
+
/* Return control to the parent process */
CpuExecute(HIWORD(PspBlock->TerminateAddress),
LOWORD(PspBlock->TerminateAddress));