/* * COPYRIGHT: GPL - See COPYING in the top level directory * PROJECT: ReactOS Virtual DOS Machine * FILE: subsystems/mvdm/ntvdm/dos/dos32krnl/process.c * PURPOSE: DOS32 Processes * PROGRAMMERS: Aleksandar Andrejevic * Hermes Belusca-Maito (hermes.belusca@sfr.fr) */ /* INCLUDES *******************************************************************/ #include "ntvdm.h" #define NDEBUG #include #include "emulator.h" #include "cpu/cpu.h" #include "dos.h" #include "dos/dem.h" #include "dosfiles.h" #include "handle.h" #include "process.h" #include "memory.h" #include "bios/bios.h" #include "io.h" #include "hardware/ps2.h" #include "vddsup.h" /* PRIVATE FUNCTIONS **********************************************************/ static VOID DosInitPsp(IN WORD Segment, IN WORD EnvBlock, IN LPCSTR CommandLine, IN LPCSTR ProgramName) { PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment); PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment - 1); LPCSTR PspName; USHORT i; /* Link the environment block */ PspBlock->EnvBlock = EnvBlock; /* * Copy the command line. * 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); /* * Initialize the owner name of the MCB of the PSP. */ /* Find the start of the file name, skipping all the path elements */ PspName = ProgramName; while (*ProgramName) { switch (*ProgramName++) { /* Path delimiter, skip it */ case ':': case '\\': case '/': PspName = ProgramName; break; } } /* Copy the file name up to the extension... */ for (i = 0; i < sizeof(Mcb->Name) && PspName[i] != '.' && PspName[i] != '\0'; ++i) { Mcb->Name[i] = RtlUpperChar(PspName[i]); } /* ... and NULL-terminate if needed */ if (i < sizeof(Mcb->Name)) Mcb->Name[i] = '\0'; // FIXME: Initialize the FCBs } static inline VOID DosSaveState(VOID) { PDOS_REGISTER_STATE State; WORD StackPointer = getSP(); DPRINT1("\n" "DosSaveState(before) -- SS:SP == %04X:%04X\n" "Original CPU State =\n" "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n" "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X" "\n", getSS(), getSP(), getDS(), getES(), getAX(), getCX(), getDX(), getBX(), getBP(), getSI(), getDI()); /* * Allocate stack space for the registers. Note that we * already have one word allocated (the interrupt number). */ StackPointer -= sizeof(DOS_REGISTER_STATE) - sizeof(WORD); State = SEG_OFF_TO_PTR(getSS(), StackPointer); setSP(StackPointer); /* Save */ State->DS = getDS(); State->ES = getES(); State->AX = getAX(); State->CX = getCX(); State->DX = getDX(); State->BX = getBX(); State->BP = getBP(); State->SI = getSI(); State->DI = getDI(); DPRINT1("\n" "DosSaveState(after) -- SS:SP == %04X:%04X\n" "Saved State =\n" "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n" "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X" "\n", getSS(), getSP(), State->DS, State->ES, State->AX, State->CX, State->DX, State->BX, State->BP, State->SI, State->DI); } static inline VOID DosRestoreState(VOID) { PDOS_REGISTER_STATE State; /* * Pop the state structure from the stack. Note that we * already have one word allocated (the interrupt number). */ State = SEG_OFF_TO_PTR(getSS(), getSP()); DPRINT1("\n" "DosRestoreState(before) -- SS:SP == %04X:%04X\n" "Saved State =\n" "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n" "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X" "\n", getSS(), getSP(), State->DS, State->ES, State->AX, State->CX, State->DX, State->BX, State->BP, State->SI, State->DI); setSP(getSP() + sizeof(DOS_REGISTER_STATE) - sizeof(WORD)); /* Restore */ setDS(State->DS); setES(State->ES); setAX(State->AX); setCX(State->CX); setDX(State->DX); setBX(State->BX); setBP(State->BP); setSI(State->SI); setDI(State->DI); DPRINT1("\n" "DosRestoreState(after) -- SS:SP == %04X:%04X\n" "Restored CPU State =\n" "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n" "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X" "\n", getSS(), getSP(), getDS(), getES(), getAX(), getCX(), getDX(), getBX(), getBP(), getSI(), getDI()); } static WORD DosCopyEnvironmentBlock(IN LPCSTR Environment OPTIONAL, IN LPCSTR ProgramName) { PCHAR Ptr, DestBuffer = NULL; SIZE_T TotalSize = 0; WORD DestSegment; /* If we have an environment strings list, compute its size */ if (Environment) { /* Calculate the size of the environment block */ Ptr = (PCHAR)Environment; while (*Ptr) Ptr += strlen(Ptr) + 1; TotalSize = (ULONG_PTR)Ptr - (ULONG_PTR)Environment; } else { /* Empty environment string */ TotalSize = 1; } /* Add the final environment block NULL-terminator */ TotalSize++; /* Add the two bytes for the program name tag */ TotalSize += 2; /* Add the string buffer size */ TotalSize += strlen(ProgramName) + 1; /* Allocate the memory for the environment block */ DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL); if (!DestSegment) return 0; DestBuffer = (PCHAR)SEG_OFF_TO_PTR(DestSegment, 0); /* If we have an environment strings list, copy it */ if (Environment) { Ptr = (PCHAR)Environment; while (*Ptr) { /* Copy the string and NULL-terminate it */ strcpy(DestBuffer, Ptr); DestBuffer += strlen(Ptr); *(DestBuffer++) = '\0'; /* Move to the next string */ Ptr += strlen(Ptr) + 1; } } else { /* Empty environment string */ *(DestBuffer++) = '\0'; } /* NULL-terminate the environment block */ *(DestBuffer++) = '\0'; /* Store the special program name tag */ *(DestBuffer++) = LOBYTE(DOS_PROGRAM_NAME_TAG); *(DestBuffer++) = HIBYTE(DOS_PROGRAM_NAME_TAG); /* Copy the program name after the environment block */ strcpy(DestBuffer, ProgramName); return DestSegment; } /* PUBLIC FUNCTIONS ***********************************************************/ VOID DosClonePsp(WORD DestSegment, WORD 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 */ RtlCopyMemory(DestPsp, SourcePsp, sizeof(*DestPsp)); /* Save the interrupt vectors */ DestPsp->TerminateAddress = IntVecTable[0x22]; DestPsp->BreakAddress = IntVecTable[0x23]; DestPsp->CriticalAddress = IntVecTable[0x24]; /* No parent PSP */ DestPsp->ParentPsp = 0; /* Set the handle table pointers to the internal handle table */ DestPsp->HandleTableSize = DEFAULT_JFT_SIZE; DestPsp->HandleTablePtr = MAKELONG(0x18, DestSegment); /* Copy the parent handle table without referencing the SFT */ RtlCopyMemory(FAR_POINTER(DestPsp->HandleTablePtr), FAR_POINTER(SourcePsp->HandleTablePtr), DEFAULT_JFT_SIZE); } VOID DosCreatePsp(WORD Segment, WORD ProgramSize) { PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment); LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress); RtlZeroMemory(PspBlock, sizeof(*PspBlock)); /* Set the exit interrupt */ PspBlock->Exit[0] = 0xCD; // int 0x20 PspBlock->Exit[1] = 0x20; /* Set the number of the last paragraph */ PspBlock->LastParagraph = Segment + ProgramSize; /* Save the interrupt vectors */ PspBlock->TerminateAddress = IntVecTable[0x22]; PspBlock->BreakAddress = IntVecTable[0x23]; PspBlock->CriticalAddress = IntVecTable[0x24]; /* Set the parent PSP */ PspBlock->ParentPsp = Sda->CurrentPsp; if (Sda->CurrentPsp != SYSTEM_PSP) { /* Link to the parent's environment block */ PspBlock->EnvBlock = SEGMENT_TO_PSP(Sda->CurrentPsp)->EnvBlock; } /* else { PspBlock->EnvBlock = SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0); } */ /* Copy the parent handle table */ DosCopyHandleTable(PspBlock->HandleTable); /* Set the handle table pointers to the internal handle table */ PspBlock->HandleTableSize = DEFAULT_JFT_SIZE; PspBlock->HandleTablePtr = MAKELONG(0x18, Segment); /* Set the DOS version */ // FIXME: This is here that SETVER stuff enters into action! PspBlock->DosVersion = DosData->DosVersion; /* Set the far call opcodes */ PspBlock->FarCall[0] = 0xCD; // int 0x21 PspBlock->FarCall[1] = 0x21; PspBlock->FarCall[2] = 0xCB; // retf } VOID DosSetProcessContext(WORD Segment) { Sda->CurrentPsp = Segment; Sda->DiskTransferArea = MAKELONG(0x80, Segment); } DWORD DosLoadExecutableInternal(IN DOS_EXEC_TYPE LoadType, IN LPBYTE ExeBuffer, IN DWORD ExeBufferSize, IN LPCSTR ExePath, IN PDOS_EXEC_PARAM_BLOCK Parameters, IN LPCSTR CommandLine OPTIONAL, IN LPCSTR Environment OPTIONAL, IN DWORD ReturnAddress OPTIONAL) { DWORD Result = ERROR_SUCCESS; WORD Segment = 0; WORD EnvBlock = 0; WORD ExeSignature; WORD LoadSegment; WORD MaxAllocSize; WORD FinalSS, FinalSP; WORD FinalCS, FinalIP; /* Buffer for command line conversion: 1 byte for size; 127 bytes for contents */ CHAR CmdLineBuffer[1 + DOS_CMDLINE_LENGTH]; DPRINT1("DosLoadExecutableInternal(%d, 0x%p, '%s', 0x%p, 0x%p, 0x%p)\n", LoadType, ExeBuffer, ExePath, Parameters, CommandLine, Environment); if (LoadType != DOS_LOAD_OVERLAY) { /* 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'; /* Finally make the pointer point to the static buffer */ CommandLine = CmdLineBuffer; } else { /* * ... 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) { ASSERT(Parameters); /* ... get the one from the parameter block (if not NULL)... */ if (Parameters->Environment) Environment = (LPCSTR)SEG_OFF_TO_PTR(Parameters->Environment, 0); /* ... or the one from the parent (otherwise) */ else Environment = (LPCSTR)SEG_OFF_TO_PTR(SEGMENT_TO_PSP(Sda->CurrentPsp)->EnvBlock, 0); } /* Copy the environment block to DOS memory */ EnvBlock = DosCopyEnvironmentBlock(Environment, ExePath); if (EnvBlock == 0) { Result = Sda->LastErrorCode; goto Cleanup; } } /* * Check if this is an EXE file or a COM file by looking * at the MZ signature: * 0x4D5A 'MZ': old signature (stored as 0x5A, 0x4D) * 0x5A4D 'ZM': new signature (stored as 0x4D, 0x5A) */ ExeSignature = *(PWORD)ExeBuffer; if (ExeSignature == 'MZ' || ExeSignature == 'ZM') { /* EXE file */ PIMAGE_DOS_HEADER Header; DWORD BaseSize; PDWORD RelocationTable; PWORD RelocWord; WORD RelocFactor; WORD i; /* Get the MZ header */ Header = (PIMAGE_DOS_HEADER)ExeBuffer; /* Get the base size of the file, in paragraphs (rounded up) */ #if 0 // Normally this is not needed to check for the number of bytes in the last pages. BaseSize = ((((Header->e_cp - (Header->e_cblp != 0)) * 512) + Header->e_cblp) >> 4) - Header->e_cparhdr; #else // e_cp is the number of 512-byte blocks. 512 == (1 << 9) // so this corresponds to (1 << 5) number of paragraphs. // // For DOS compatibility we need to truncate BaseSize to a WORD value. // This fact is exploited by some EXEs which are bigger than 1 Mb while // being able to load on DOS, the main EXE code loads the remaining data. BaseSize = ((Header->e_cp << 5) - Header->e_cparhdr) & 0xFFFF; #endif if (LoadType != DOS_LOAD_OVERLAY) { BOOLEAN LoadHigh = FALSE; DWORD TotalSize; /* Find the maximum amount of memory that can be allocated */ DosAllocateMemory(0xFFFF, &MaxAllocSize); /* Compute the total needed size, in paragraphs */ TotalSize = BaseSize + (sizeof(DOS_PSP) >> 4); /* We must have the required minimum amount of memory. If not, bail out. */ if (MaxAllocSize < TotalSize + Header->e_minalloc) { Result = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } /* Check if the program should be loaded high */ if (Header->e_minalloc == 0 && Header->e_maxalloc == 0) { /* Yes it should. Use all the available memory. */ LoadHigh = TRUE; TotalSize = MaxAllocSize; } else { /* Compute the maximum memory size that can be allocated */ if (Header->e_maxalloc != 0) TotalSize = min(TotalSize + Header->e_maxalloc, MaxAllocSize); else TotalSize = MaxAllocSize; // Use all the available memory } /* Try to allocate that much memory */ Segment = DosAllocateMemory((WORD)TotalSize, NULL); if (Segment == 0) { Result = Sda->LastErrorCode; goto Cleanup; } /* The process owns its memory */ DosChangeMemoryOwner(Segment , Segment); DosChangeMemoryOwner(EnvBlock, Segment); /* Set INT 22h to the return address */ ((PULONG)BaseAddress)[0x22] = ReturnAddress; /* Create the PSP and initialize it */ DosCreatePsp(Segment, (WORD)TotalSize); DosInitPsp(Segment, EnvBlock, CommandLine, ExePath); /* Calculate the segment where the program should be loaded */ if (!LoadHigh) LoadSegment = Segment + (sizeof(DOS_PSP) >> 4); else LoadSegment = Segment + TotalSize - BaseSize; RelocFactor = LoadSegment; } else { ASSERT(Parameters); LoadSegment = Parameters->Overlay.Segment; RelocFactor = Parameters->Overlay.RelocationFactor; } /* Copy the program to the code segment */ RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0), ExeBuffer + (Header->e_cparhdr << 4), min(ExeBufferSize - (Header->e_cparhdr << 4), BaseSize << 4)); /* Get the relocation table */ RelocationTable = (PDWORD)(ExeBuffer + Header->e_lfarlc); /* Perform relocations */ for (i = 0; i < Header->e_crlc; i++) { /* Get a pointer to the word that needs to be patched */ RelocWord = (PWORD)SEG_OFF_TO_PTR(LoadSegment + HIWORD(RelocationTable[i]), LOWORD(RelocationTable[i])); /* Add the relocation factor to it */ *RelocWord += RelocFactor; } /* Set the stack to the location from the header */ FinalSS = LoadSegment + Header->e_ss; FinalSP = Header->e_sp; /* Set the code segment/pointer */ FinalCS = LoadSegment + Header->e_cs; FinalIP = Header->e_ip; } else { /* COM file */ if (LoadType != DOS_LOAD_OVERLAY) { /* Find the maximum amount of memory that can be allocated */ DosAllocateMemory(0xFFFF, &MaxAllocSize); /* Make sure it's enough for the whole program and the PSP */ if (((DWORD)MaxAllocSize << 4) < (ExeBufferSize + sizeof(DOS_PSP))) { Result = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; } /* Allocate all of it */ Segment = DosAllocateMemory(MaxAllocSize, NULL); if (Segment == 0) { Result = Sda->LastErrorCode; goto Cleanup; } /* The process owns its memory */ DosChangeMemoryOwner(Segment , Segment); DosChangeMemoryOwner(EnvBlock, Segment); /* Set INT 22h to the return address */ ((PULONG)BaseAddress)[0x22] = ReturnAddress; /* Create the PSP and initialize it */ DosCreatePsp(Segment, MaxAllocSize); DosInitPsp(Segment, EnvBlock, CommandLine, ExePath); /* Calculate the segment where the program should be loaded */ LoadSegment = Segment + (sizeof(DOS_PSP) >> 4); } else { ASSERT(Parameters); LoadSegment = Parameters->Overlay.Segment; } /* Copy the program to the code segment */ RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0), ExeBuffer, ExeBufferSize); /* Set the stack to the last word of the segment */ FinalSS = Segment; FinalSP = 0xFFFE; /* * Set the value on the stack to 0x0000, so that a near return * jumps to PSP:0000 which has the exit code. */ *((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0x0000; /* Set the code segment/pointer */ FinalCS = Segment; FinalIP = 0x0100; } if (LoadType == DOS_LOAD_AND_EXECUTE) { /* Save the program state */ if (Sda->CurrentPsp != SYSTEM_PSP) { /* Push the task state */ DosSaveState(); DPRINT1("Sda->CurrentPsp = 0x%04x; Old LastStack = 0x%08x, New LastStack = 0x%08x\n", Sda->CurrentPsp, SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack, MAKELONG(getSP(), getSS())); /* Update the last stack in the PSP */ SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack = MAKELONG(getSP(), getSS()); } /* Set the initial segment registers */ setDS(Segment); setES(Segment); /* Set the stack */ setSS(FinalSS); setSP(FinalSP); /* * Set the other registers as in real DOS: some demos expect them so! * See http://www.fysnet.net/yourhelp.htm * and http://www.beroset.com/asm/showregs.asm */ setDX(Segment); setDI(FinalSP); setBP(0x091E); // DOS base stack pointer relic value. In MS-DOS 5.0 and Windows' NTVDM it's 0x091C. This is in fact the old SP value inside DosData disk stack. setSI(FinalIP); setAX(0/*0xFFFF*/); // FIXME: fcbcode setBX(0/*0xFFFF*/); // FIXME: fcbcode setCX(0x00FF); /* * Keep critical flags, clear test flags (OF, SF, ZF, AF, PF, CF) * and explicitely set the interrupt flag. */ setEFLAGS((getEFLAGS() & ~0x08D5) | 0x0200); /* Notify VDDs of process execution */ VDDCreateUserHook(Segment); /* Execute */ DosSetProcessContext(Segment); CpuExecute(FinalCS, FinalIP); } else if (LoadType == DOS_LOAD_ONLY) { ASSERT(Parameters); Parameters->StackLocation = MAKELONG(FinalSP, FinalSS); Parameters->EntryPoint = MAKELONG(FinalIP, FinalCS); } Cleanup: if (Result != ERROR_SUCCESS) { /* It was not successful, cleanup the DOS memory */ if (EnvBlock) DosFreeMemory(EnvBlock); if (Segment) DosFreeMemory(Segment); } return Result; } DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType, IN LPCSTR ExecutablePath, IN PDOS_EXEC_PARAM_BLOCK Parameters, IN LPCSTR CommandLine OPTIONAL, IN LPCSTR Environment OPTIONAL, IN DWORD ReturnAddress OPTIONAL) { DWORD Result = ERROR_SUCCESS; HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL; DWORD FileSize; LPBYTE Address = NULL; CHAR FullPath[MAX_PATH]; CHAR ShortFullPath[MAX_PATH]; DPRINT1("DosLoadExecutable(%d, '%s', 0x%p, 0x%p, 0x%p)\n", LoadType, ExecutablePath, Parameters, CommandLine, Environment); /* Try to get the full path to the executable */ if (GetFullPathNameA(ExecutablePath, sizeof(FullPath), FullPath, NULL)) { /* Get the corresponding short 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, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (FileHandle == INVALID_HANDLE_VALUE) { Result = GetLastError(); goto Cleanup; } /* Get the file size */ FileSize = GetFileSize(FileHandle, NULL); /* Create a mapping object for the file */ FileMapping = CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL); if (FileMapping == NULL) { Result = GetLastError(); goto Cleanup; } /* Map the file into memory */ Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0); if (Address == NULL) { Result = GetLastError(); goto Cleanup; } Result = DosLoadExecutableInternal(LoadType, Address, FileSize, ExecutablePath, Parameters, CommandLine, Environment, ReturnAddress); Cleanup: /* Unmap the file*/ if (Address != NULL) UnmapViewOfFile(Address); /* Close the file mapping object */ if (FileMapping != NULL) CloseHandle(FileMapping); /* Close the file handle */ if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle); return Result; } WORD DosCreateProcess(IN LPCSTR ProgramName, IN PDOS_EXEC_PARAM_BLOCK Parameters, IN DWORD ReturnAddress OPTIONAL) { DWORD Result = ERROR_SUCCESS; DWORD BinaryType; /* Get the binary type */ if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError(); /* Check the type of the program */ switch (BinaryType) { /* Those are handled by NTVDM */ case SCS_WOW_BINARY: { DisplayMessage(L"Trying to load '%S'.\n" L"WOW16 applications are not supported internally by NTVDM at the moment.\n" L"Press 'OK' to continue.", ProgramName); // Fall through } case SCS_DOS_BINARY: { /* Load the executable */ Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE, ProgramName, Parameters, NULL, NULL, ReturnAddress); if (Result != ERROR_SUCCESS) { DisplayMessage(L"Could not load '%S'. Error: %u", ProgramName, Result); } break; } /* Not handled by NTVDM */ default: { LPSTR Environment = NULL; CHAR CmdLine[MAX_PATH + DOS_CMDLINE_LENGTH + 1]; LPSTR CmdLinePtr; ULONG CmdLineSize; /* Did the caller specify an environment segment? */ if (Parameters->Environment) { /* Yes, use it instead of the parent one */ Environment = (LPSTR)SEG_OFF_TO_PTR(Parameters->Environment, 0); } /* * 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'; Result = DosStartProcess32(ProgramName, CmdLine, Environment, ReturnAddress, TRUE); if (Result != ERROR_SUCCESS) { DisplayMessage(L"Could not load 32-bit '%S'. Error: %u", ProgramName, Result); } break; } } return Result; } VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident) { WORD McbSegment = SysVars->FirstMcb; PDOS_MCB CurrentMcb; LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress); PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp); LPWORD Stack; BYTE TerminationType; DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n", Psp, ReturnCode, KeepResident); /* Notify VDDs of process termination */ VDDTerminateUserHook(Psp); /* Check if this PSP is its own parent */ if (PspBlock->ParentPsp == Psp) goto Done; if (KeepResident == 0) { WORD i; for (i = 0; i < PspBlock->HandleTableSize; i++) { /* Close the handle */ DosCloseHandle(i); } } /* Free the memory used by the process */ while (TRUE) { /* Get a pointer to the MCB */ CurrentMcb = SEGMENT_TO_MCB(McbSegment); /* Make sure the MCB is valid */ if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') break; /* Check if this block was allocated by the process */ if (CurrentMcb->OwnerPsp == Psp) { if (KeepResident) { /* 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 { /* Free this entire block */ DosFreeMemory(McbSegment + 1); } } /* If this was the last block, quit */ if (CurrentMcb->BlockType == 'Z') break; /* Update the segment and continue */ McbSegment += CurrentMcb->Size + 1; } Done: /* Restore the interrupt vectors */ IntVecTable[0x22] = PspBlock->TerminateAddress; IntVecTable[0x23] = PspBlock->BreakAddress; IntVecTable[0x24] = PspBlock->CriticalAddress; /* Update the current PSP with the parent's one */ if (Psp == Sda->CurrentPsp) { DosSetProcessContext(PspBlock->ParentPsp); if (Sda->CurrentPsp == SYSTEM_PSP) { // NOTE: we can also use the DOS BIOS exit code. CpuUnsimulate(); return; } } /* Save the return code - Normal termination or TSR */ TerminationType = (KeepResident != 0 ? 0x03 : 0x00); Sda->ErrorLevel = MAKEWORD(ReturnCode, TerminationType); DPRINT1("PspBlock->ParentPsp = 0x%04x; Sda->CurrentPsp = 0x%04x\n", PspBlock->ParentPsp, Sda->CurrentPsp); if (Sda->CurrentPsp != SYSTEM_PSP) { DPRINT1("Sda->CurrentPsp = 0x%04x; Old SS:SP = %04X:%04X going to be LastStack = 0x%08x\n", Sda->CurrentPsp, getSS(), getSP(), SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack); /* Restore the parent's stack */ setSS(HIWORD(SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack)); setSP(LOWORD(SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack)); /* Pop the task state */ DosRestoreState(); } /* Return control to the parent process */ Stack = (LPWORD)SEG_OFF_TO_PTR(getSS(), getSP()); Stack[STACK_CS] = HIWORD(PspBlock->TerminateAddress); Stack[STACK_IP] = LOWORD(PspBlock->TerminateAddress); } /* EOF */