{
PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
- /* Set the command line */
- PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1);
- RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
- PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
+ /*
+ * Copy the command line block.
+ * Format of the CommandLine parameter: 1 byte for size; 127 bytes for contents.
+ */
+ PspBlock->CommandLineSize = min(*(PBYTE)CommandLine, DOS_CMDLINE_LENGTH);
+ CommandLine++;
+ 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,
VOID DosClonePsp(WORD DestSegment, WORD SourceSegment)
{
- PDOS_PSP DestPsp = SEGMENT_TO_PSP(DestSegment);
- PDOS_PSP SourcePsp = SEGMENT_TO_PSP(SourceSegment);
+ PDOS_PSP DestPsp = SEGMENT_TO_PSP(DestSegment);
+ PDOS_PSP SourcePsp = SEGMENT_TO_PSP(SourceSegment);
LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
/* Literally copy the PSP first */
/* Set the handle table pointers to the internal handle table */
DestPsp->HandleTableSize = DEFAULT_JFT_SIZE;
- DestPsp->HandleTablePtr = MAKELONG(0x18, DestSegment);
+ DestPsp->HandleTablePtr = MAKELONG(0x18, DestSegment);
/* Copy the parent handle table without referencing the SFT */
RtlCopyMemory(FAR_POINTER(DestPsp->HandleTablePtr),
VOID DosCreatePsp(WORD Segment, WORD ProgramSize)
{
- PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
+ PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
RtlZeroMemory(PspBlock, sizeof(*PspBlock));
/* Set the handle table pointers to the internal handle table */
PspBlock->HandleTableSize = DEFAULT_JFT_SIZE;
- PspBlock->HandleTablePtr = MAKELONG(0x18, Segment);
+ PspBlock->HandleTablePtr = MAKELONG(0x18, Segment);
/* Set the DOS version */
PspBlock->DosVersion = DOS_VERSION;
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];
- DPRINT1("DosLoadExecutable(%d, %s, 0x%08X, 0x%08X)\n",
+ /* 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, 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,
if (LoadType != DOS_LOAD_OVERLAY)
{
- LPSTR CmdLinePtr;
+ /* If an optional Win32 command line is given... */
+ if (CommandLine)
+ {
+ /* ... convert it into DOS format */
+ BYTE CmdLineLen;
+
+ PBYTE CmdLineSize = (PBYTE)CmdLineBuffer;
+ LPSTR CmdLineStart = CmdLineBuffer + 1;
+ LPSTR CmdLinePtr = CmdLineStart;
+
+ // For debugging purposes
+ RtlFillMemory(CmdLineBuffer, sizeof(CmdLineBuffer), 0xFF);
+
+ /*
+ * Set the command line: it is either an empty command line or has
+ * the format: " foo bar ..." (with at least one leading whitespace),
+ * and is then always followed by '\r' (and optionally by '\n').
+ */
+ CmdLineLen = (BYTE)strlen(CommandLine);
+ *CmdLineSize = 0;
+
+ /*
+ * Add the leading space if the command line is not empty
+ * and doesn't already start with some whitespace...
+ */
+ if (*CommandLine && *CommandLine != '\r' && *CommandLine != '\n' &&
+ *CommandLine != ' ' && *CommandLine != '\t')
+ {
+ (*CmdLineSize)++;
+ *CmdLinePtr++ = ' ';
+ }
+
+ /* Compute the number of characters we need to copy from the original command line */
+ CmdLineLen = min(CmdLineLen, DOS_CMDLINE_LENGTH - *CmdLineSize);
+
+ /* The trailing '\r' or '\n' do not count in the PSP command line size parameter */
+ while (CmdLineLen && (CommandLine[CmdLineLen - 1] == '\r' || CommandLine[CmdLineLen - 1] == '\n'))
+ {
+ CmdLineLen--;
+ }
+
+ /* Finally, set everything up */
+ *CmdLineSize += CmdLineLen;
+ RtlCopyMemory(CmdLinePtr, CommandLine, CmdLineLen);
+ CmdLineStart[*CmdLineSize] = '\r';
- if (CommandLine == NULL)
+ /* Finally make the pointer point to the static buffer */
+ CommandLine = CmdLineBuffer;
+ }
+ else
{
- /* Get the command line from the parameter block */
+ /*
+ * ... otherwise, get the one from the parameter block.
+ * Format of the command line: 1 byte for size; 127 bytes for contents.
+ */
+ ASSERT(Parameters);
CommandLine = (LPCSTR)FAR_POINTER(Parameters->CommandLine);
}
+ /* If no optional environment is given... */
if (Environment == NULL)
{
- /* Get the environment from the parameter block */
+ /* ... get the one from the parameter block */
+ ASSERT(Parameters);
Environment = (LPCSTR)SEG_OFF_TO_PTR(Parameters->Environment, 0);
}
- /* NULL-terminate the command line by removing the return carriage character */
- CmdLinePtr = (LPSTR)CommandLine;
- while (*CmdLinePtr && *CmdLinePtr != '\r') CmdLinePtr++;
- *CmdLinePtr = '\0';
-
/* Copy the environment block to DOS memory */
EnvBlock = DosCopyEnvironmentBlock(Environment, ExecutablePath);
if (EnvBlock == 0)
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);
}
else
{
+ ASSERT(Parameters);
LoadSegment = Parameters->Overlay.Segment;
RelocFactor = Parameters->Overlay.RelocationFactor;
}
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);
}
else if (LoadType == DOS_LOAD_ONLY)
{
+ ASSERT(Parameters);
Parameters->StackLocation = MAKELONG(Header->e_sp, LoadSegment + Header->e_ss);
- Parameters->EntryPoint = MAKELONG(Header->e_ip, LoadSegment + Header->e_cs);
+ Parameters->EntryPoint = MAKELONG(Header->e_ip, LoadSegment + Header->e_cs);
}
}
else
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);
}
else
{
+ ASSERT(Parameters);
LoadSegment = Parameters->Overlay.Segment;
}
+ /* Copy the program to the code segment */
RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
Address,
FileSize);
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);
}
else if (LoadType == DOS_LOAD_ONLY)
{
+ ASSERT(Parameters);
Parameters->StackLocation = MAKELONG(0xFFFE, Segment);
- Parameters->EntryPoint = MAKELONG(0x0100, Segment);
+ Parameters->EntryPoint = MAKELONG(0x0100, 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.
- 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];
+ CHAR CmdLine[MAX_PATH + DOS_CMDLINE_LENGTH + 1];
CHAR AppName[MAX_PATH];
CHAR PifFile[MAX_PATH];
CHAR Desktop[MAX_PATH];
CHAR Title[MAX_PATH];
+ LPSTR CmdLinePtr;
+ ULONG CmdLineSize;
ULONG EnvSize = 256;
- PVOID Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
+ PVOID Env;
STARTUPINFOA StartupInfo;
PROCESS_INFORMATION ProcessInfo;
/* Get the binary type */
if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
+ /* Initialize Win32-VDM environment */
+ Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
+ if (Env == NULL) return GetLastError();
+
/* Did the caller specify an environment segment? */
if (Parameters->Environment)
{
RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
+ /*
+ * 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(CmdLinePtr,
+ (LPSTR)FAR_POINTER(Parameters->CommandLine) + 1,
+ CmdLineSize);
+ /* NULL-terminate it */
+ CmdLinePtr[CmdLineSize] = '\0';
+
+ /* Remove any trailing return carriage character and NULL-terminate the command line */
+ while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
+ *CmdLinePtr = '\0';
+
/* Create the process */
if (!CreateProcessA(ProgramName,
- FAR_POINTER(Parameters->CommandLine),
+ CmdLine,
NULL,
NULL,
FALSE,
&StartupInfo,
&ProcessInfo))
{
+ RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
return GetLastError();
}
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,
/* Check if this block was allocated by the process */
if (CurrentMcb->OwnerPsp == Psp)
{
- if (KeepResident == 0)
- {
- /* Free this entire block */
- DosFreeMemory(McbSegment + 1);
- }
- else if (KeepResident < CurrentMcb->Size)
+ if (KeepResident)
{
- /* Reduce the size of the block */
- DosResizeMemory(McbSegment + 1, KeepResident, NULL);
-
- /* No further paragraphs need to stay resident */
- KeepResident = 0;
+ /* Check if this is the PSP block and we should reduce its size */
+ if ((McbSegment + 1) == Psp && KeepResident < CurrentMcb->Size)
+ {
+ /* Reduce the size of the block */
+ DosResizeMemory(McbSegment + 1, KeepResident, NULL);
+ break;
+ }
}
else
{
- /* Just reduce the amount of paragraphs we need to keep resident */
- KeepResident -= CurrentMcb->Size;
+ /* Free this entire block */
+ DosFreeMemory(McbSegment + 1);
}
}
{
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 */
DosErrorLevel = MAKEWORD(ReturnCode, 0x00);
+ /* Restore the old stack */
+ 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));