2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/dos/dos32krnl/process.c
5 * PURPOSE: DOS32 Processes
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
10 /* INCLUDES *******************************************************************/
27 #include "bios/bios.h"
30 #include "hardware/ps2.h"
34 /* PRIVATE FUNCTIONS **********************************************************/
36 static VOID
DosInitPsp(IN WORD Segment
,
38 IN LPCSTR CommandLine
,
39 IN LPCSTR ProgramName
)
41 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Segment
);
42 PDOS_MCB Mcb
= SEGMENT_TO_MCB(Segment
- 1);
46 /* Link the environment block */
47 PspBlock
->EnvBlock
= EnvBlock
;
50 * Copy the command line.
51 * Format of the CommandLine parameter: 1 byte for size; 127 bytes for contents.
53 PspBlock
->CommandLineSize
= min(*(PBYTE
)CommandLine
, DOS_CMDLINE_LENGTH
);
55 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, DOS_CMDLINE_LENGTH
);
58 * Initialize the owner name of the MCB of the PSP.
61 /* Find the start of the file name, skipping all the path elements */
62 PspName
= ProgramName
;
65 switch (*ProgramName
++)
67 /* Path delimiter, skip it */
68 case ':': case '\\': case '/':
69 PspName
= ProgramName
;
73 /* Copy the file name up to the extension... */
74 for (i
= 0; i
< sizeof(Mcb
->Name
) && PspName
[i
] != '.' && PspName
[i
] != '\0'; ++i
)
76 Mcb
->Name
[i
] = RtlUpperChar(PspName
[i
]);
78 /* ... and NULL-terminate if needed */
79 if (i
< sizeof(Mcb
->Name
)) Mcb
->Name
[i
] = '\0';
81 // FIXME: Initialize the FCBs
84 static inline VOID
DosSaveState(VOID
)
86 PDOS_REGISTER_STATE State
;
87 WORD StackPointer
= getSP();
90 "DosSaveState(before) -- SS:SP == %04X:%04X\n"
91 "Original CPU State =\n"
92 "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
93 "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
96 getDS(), getES(), getAX(), getCX(),
97 getDX(), getBX(), getBP(), getSI(), getDI());
100 * Allocate stack space for the registers. Note that we
101 * already have one word allocated (the interrupt number).
103 StackPointer
-= sizeof(DOS_REGISTER_STATE
) - sizeof(WORD
);
104 State
= SEG_OFF_TO_PTR(getSS(), StackPointer
);
119 "DosSaveState(after) -- SS:SP == %04X:%04X\n"
121 "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
122 "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
125 State
->DS
, State
->ES
, State
->AX
, State
->CX
,
126 State
->DX
, State
->BX
, State
->BP
, State
->SI
, State
->DI
);
129 static inline VOID
DosRestoreState(VOID
)
131 PDOS_REGISTER_STATE State
;
134 * Pop the state structure from the stack. Note that we
135 * already have one word allocated (the interrupt number).
137 State
= SEG_OFF_TO_PTR(getSS(), getSP());
140 "DosRestoreState(before) -- SS:SP == %04X:%04X\n"
142 "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
143 "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
146 State
->DS
, State
->ES
, State
->AX
, State
->CX
,
147 State
->DX
, State
->BX
, State
->BP
, State
->SI
, State
->DI
);
149 setSP(getSP() + sizeof(DOS_REGISTER_STATE
) - sizeof(WORD
));
163 "DosRestoreState(after) -- SS:SP == %04X:%04X\n"
164 "Restored CPU State =\n"
165 "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
166 "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
169 getDS(), getES(), getAX(), getCX(),
170 getDX(), getBX(), getBP(), getSI(), getDI());
173 static WORD
DosCopyEnvironmentBlock(IN LPCSTR Environment OPTIONAL
,
174 IN LPCSTR ProgramName
)
176 PCHAR Ptr
, DestBuffer
= NULL
;
177 SIZE_T TotalSize
= 0;
180 /* If we have an environment strings list, compute its size */
183 /* Calculate the size of the environment block */
184 Ptr
= (PCHAR
)Environment
;
185 while (*Ptr
) Ptr
+= strlen(Ptr
) + 1;
186 TotalSize
= (ULONG_PTR
)Ptr
- (ULONG_PTR
)Environment
;
190 /* Empty environment string */
193 /* Add the final environment block NULL-terminator */
196 /* Add the two bytes for the program name tag */
199 /* Add the string buffer size */
200 TotalSize
+= strlen(ProgramName
) + 1;
202 /* Allocate the memory for the environment block */
203 DestSegment
= DosAllocateMemory((WORD
)((TotalSize
+ 0x0F) >> 4), NULL
);
204 if (!DestSegment
) return 0;
206 DestBuffer
= (PCHAR
)SEG_OFF_TO_PTR(DestSegment
, 0);
208 /* If we have an environment strings list, copy it */
211 Ptr
= (PCHAR
)Environment
;
214 /* Copy the string and NULL-terminate it */
215 strcpy(DestBuffer
, Ptr
);
216 DestBuffer
+= strlen(Ptr
);
217 *(DestBuffer
++) = '\0';
219 /* Move to the next string */
220 Ptr
+= strlen(Ptr
) + 1;
225 /* Empty environment string */
226 *(DestBuffer
++) = '\0';
228 /* NULL-terminate the environment block */
229 *(DestBuffer
++) = '\0';
231 /* Store the special program name tag */
232 *(DestBuffer
++) = LOBYTE(DOS_PROGRAM_NAME_TAG
);
233 *(DestBuffer
++) = HIBYTE(DOS_PROGRAM_NAME_TAG
);
235 /* Copy the program name after the environment block */
236 strcpy(DestBuffer
, ProgramName
);
241 /* PUBLIC FUNCTIONS ***********************************************************/
243 VOID
DosClonePsp(WORD DestSegment
, WORD SourceSegment
)
245 PDOS_PSP DestPsp
= SEGMENT_TO_PSP(DestSegment
);
246 PDOS_PSP SourcePsp
= SEGMENT_TO_PSP(SourceSegment
);
247 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
249 /* Literally copy the PSP first */
250 RtlCopyMemory(DestPsp
, SourcePsp
, sizeof(*DestPsp
));
252 /* Save the interrupt vectors */
253 DestPsp
->TerminateAddress
= IntVecTable
[0x22];
254 DestPsp
->BreakAddress
= IntVecTable
[0x23];
255 DestPsp
->CriticalAddress
= IntVecTable
[0x24];
258 DestPsp
->ParentPsp
= 0;
260 /* Set the handle table pointers to the internal handle table */
261 DestPsp
->HandleTableSize
= DEFAULT_JFT_SIZE
;
262 DestPsp
->HandleTablePtr
= MAKELONG(0x18, DestSegment
);
264 /* Copy the parent handle table without referencing the SFT */
265 RtlCopyMemory(FAR_POINTER(DestPsp
->HandleTablePtr
),
266 FAR_POINTER(SourcePsp
->HandleTablePtr
),
270 VOID
DosCreatePsp(WORD Segment
, WORD ProgramSize
)
272 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Segment
);
273 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
275 RtlZeroMemory(PspBlock
, sizeof(*PspBlock
));
277 /* Set the exit interrupt */
278 PspBlock
->Exit
[0] = 0xCD; // int 0x20
279 PspBlock
->Exit
[1] = 0x20;
281 /* Set the number of the last paragraph */
282 PspBlock
->LastParagraph
= Segment
+ ProgramSize
;
284 /* Save the interrupt vectors */
285 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
286 PspBlock
->BreakAddress
= IntVecTable
[0x23];
287 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
289 /* Set the parent PSP */
290 PspBlock
->ParentPsp
= Sda
->CurrentPsp
;
292 if (Sda
->CurrentPsp
!= SYSTEM_PSP
)
294 /* Link to the parent's environment block */
295 PspBlock
->EnvBlock
= SEGMENT_TO_PSP(Sda
->CurrentPsp
)->EnvBlock
;
300 PspBlock->EnvBlock = SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0);
304 /* Copy the parent handle table */
305 DosCopyHandleTable(PspBlock
->HandleTable
);
307 /* Set the handle table pointers to the internal handle table */
308 PspBlock
->HandleTableSize
= DEFAULT_JFT_SIZE
;
309 PspBlock
->HandleTablePtr
= MAKELONG(0x18, Segment
);
311 /* Set the DOS version */
312 // FIXME: This is here that SETVER stuff enters into action!
313 PspBlock
->DosVersion
= DosData
->DosVersion
;
315 /* Set the far call opcodes */
316 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
317 PspBlock
->FarCall
[1] = 0x21;
318 PspBlock
->FarCall
[2] = 0xCB; // retf
321 VOID
DosSetProcessContext(WORD Segment
)
323 Sda
->CurrentPsp
= Segment
;
324 Sda
->DiskTransferArea
= MAKELONG(0x80, Segment
);
327 DWORD
DosLoadExecutableInternal(IN DOS_EXEC_TYPE LoadType
,
329 IN DWORD ExeBufferSize
,
331 IN PDOS_EXEC_PARAM_BLOCK Parameters
,
332 IN LPCSTR CommandLine OPTIONAL
,
333 IN LPCSTR Environment OPTIONAL
,
334 IN DWORD ReturnAddress OPTIONAL
)
336 DWORD Result
= ERROR_SUCCESS
;
343 WORD FinalSS
, FinalSP
;
344 WORD FinalCS
, FinalIP
;
346 /* Buffer for command line conversion: 1 byte for size; 127 bytes for contents */
347 CHAR CmdLineBuffer
[1 + DOS_CMDLINE_LENGTH
];
349 DPRINT1("DosLoadExecutableInternal(%d, 0x%p, '%s', 0x%p, 0x%p, 0x%p)\n",
350 LoadType
, ExeBuffer
, ExePath
, Parameters
, CommandLine
, Environment
);
352 if (LoadType
!= DOS_LOAD_OVERLAY
)
354 /* If an optional Win32 command line is given... */
357 /* ... convert it into DOS format */
360 PBYTE CmdLineSize
= (PBYTE
)CmdLineBuffer
;
361 LPSTR CmdLineStart
= CmdLineBuffer
+ 1;
362 LPSTR CmdLinePtr
= CmdLineStart
;
364 // For debugging purposes
365 RtlFillMemory(CmdLineBuffer
, sizeof(CmdLineBuffer
), 0xFF);
368 * Set the command line: it is either an empty command line or has
369 * the format: " foo bar ..." (with at least one leading whitespace),
370 * and is then always followed by '\r' (and optionally by '\n').
372 CmdLineLen
= (BYTE
)strlen(CommandLine
);
376 * Add the leading space if the command line is not empty
377 * and doesn't already start with some whitespace...
379 if (*CommandLine
&& *CommandLine
!= '\r' && *CommandLine
!= '\n' &&
380 *CommandLine
!= ' ' && *CommandLine
!= '\t')
386 /* Compute the number of characters we need to copy from the original command line */
387 CmdLineLen
= min(CmdLineLen
, DOS_CMDLINE_LENGTH
- *CmdLineSize
);
389 /* The trailing '\r' or '\n' do not count in the PSP command line size parameter */
390 while (CmdLineLen
&& (CommandLine
[CmdLineLen
- 1] == '\r' || CommandLine
[CmdLineLen
- 1] == '\n'))
395 /* Finally, set everything up */
396 *CmdLineSize
+= CmdLineLen
;
397 RtlCopyMemory(CmdLinePtr
, CommandLine
, CmdLineLen
);
398 CmdLineStart
[*CmdLineSize
] = '\r';
400 /* Finally make the pointer point to the static buffer */
401 CommandLine
= CmdLineBuffer
;
406 * ... otherwise, get the one from the parameter block.
407 * Format of the command line: 1 byte for size; 127 bytes for contents.
410 CommandLine
= (LPCSTR
)FAR_POINTER(Parameters
->CommandLine
);
413 /* If no optional environment is given... */
414 if (Environment
== NULL
)
417 /* ... get the one from the parameter block (if not NULL)... */
418 if (Parameters
->Environment
)
419 Environment
= (LPCSTR
)SEG_OFF_TO_PTR(Parameters
->Environment
, 0);
420 /* ... or the one from the parent (otherwise) */
422 Environment
= (LPCSTR
)SEG_OFF_TO_PTR(SEGMENT_TO_PSP(Sda
->CurrentPsp
)->EnvBlock
, 0);
425 /* Copy the environment block to DOS memory */
426 EnvBlock
= DosCopyEnvironmentBlock(Environment
, ExePath
);
429 Result
= Sda
->LastErrorCode
;
435 * Check if this is an EXE file or a COM file by looking
436 * at the MZ signature:
437 * 0x4D5A 'MZ': old signature (stored as 0x5A, 0x4D)
438 * 0x5A4D 'ZM': new signature (stored as 0x4D, 0x5A)
440 ExeSignature
= *(PWORD
)ExeBuffer
;
441 if (ExeSignature
== 'MZ' || ExeSignature
== 'ZM')
444 PIMAGE_DOS_HEADER Header
;
446 PDWORD RelocationTable
;
451 /* Get the MZ header */
452 Header
= (PIMAGE_DOS_HEADER
)ExeBuffer
;
454 /* Get the base size of the file, in paragraphs (rounded up) */
455 #if 0 // Normally this is not needed to check for the number of bytes in the last pages.
456 BaseSize
= ((((Header
->e_cp
- (Header
->e_cblp
!= 0)) * 512) + Header
->e_cblp
) >> 4)
459 // e_cp is the number of 512-byte blocks. 512 == (1 << 9)
460 // so this corresponds to (1 << 5) number of paragraphs.
462 // For DOS compatibility we need to truncate BaseSize to a WORD value.
463 // This fact is exploited by some EXEs which are bigger than 1 Mb while
464 // being able to load on DOS, the main EXE code loads the remaining data.
466 BaseSize
= ((Header
->e_cp
<< 5) - Header
->e_cparhdr
) & 0xFFFF;
469 if (LoadType
!= DOS_LOAD_OVERLAY
)
471 BOOLEAN LoadHigh
= FALSE
;
474 /* Find the maximum amount of memory that can be allocated */
475 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
477 /* Compute the total needed size, in paragraphs */
478 TotalSize
= BaseSize
+ (sizeof(DOS_PSP
) >> 4);
480 /* We must have the required minimum amount of memory. If not, bail out. */
481 if (MaxAllocSize
< TotalSize
+ Header
->e_minalloc
)
483 Result
= ERROR_NOT_ENOUGH_MEMORY
;
487 /* Check if the program should be loaded high */
488 if (Header
->e_minalloc
== 0 && Header
->e_maxalloc
== 0)
490 /* Yes it should. Use all the available memory. */
492 TotalSize
= MaxAllocSize
;
496 /* Compute the maximum memory size that can be allocated */
497 if (Header
->e_maxalloc
!= 0)
498 TotalSize
= min(TotalSize
+ Header
->e_maxalloc
, MaxAllocSize
);
500 TotalSize
= MaxAllocSize
; // Use all the available memory
503 /* Try to allocate that much memory */
504 Segment
= DosAllocateMemory((WORD
)TotalSize
, NULL
);
507 Result
= Sda
->LastErrorCode
;
511 /* The process owns its memory */
512 DosChangeMemoryOwner(Segment
, Segment
);
513 DosChangeMemoryOwner(EnvBlock
, Segment
);
515 /* Set INT 22h to the return address */
516 ((PULONG
)BaseAddress
)[0x22] = ReturnAddress
;
518 /* Create the PSP and initialize it */
519 DosCreatePsp(Segment
, (WORD
)TotalSize
);
520 DosInitPsp(Segment
, EnvBlock
, CommandLine
, ExePath
);
522 /* Calculate the segment where the program should be loaded */
524 LoadSegment
= Segment
+ (sizeof(DOS_PSP
) >> 4);
526 LoadSegment
= Segment
+ TotalSize
- BaseSize
;
528 RelocFactor
= LoadSegment
;
533 LoadSegment
= Parameters
->Overlay
.Segment
;
534 RelocFactor
= Parameters
->Overlay
.RelocationFactor
;
537 /* Copy the program to the code segment */
538 RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment
, 0),
539 ExeBuffer
+ (Header
->e_cparhdr
<< 4),
540 min(ExeBufferSize
- (Header
->e_cparhdr
<< 4), BaseSize
<< 4));
542 /* Get the relocation table */
543 RelocationTable
= (PDWORD
)(ExeBuffer
+ Header
->e_lfarlc
);
545 /* Perform relocations */
546 for (i
= 0; i
< Header
->e_crlc
; i
++)
548 /* Get a pointer to the word that needs to be patched */
549 RelocWord
= (PWORD
)SEG_OFF_TO_PTR(LoadSegment
+ HIWORD(RelocationTable
[i
]),
550 LOWORD(RelocationTable
[i
]));
552 /* Add the relocation factor to it */
553 *RelocWord
+= RelocFactor
;
556 /* Set the stack to the location from the header */
557 FinalSS
= LoadSegment
+ Header
->e_ss
;
558 FinalSP
= Header
->e_sp
;
560 /* Set the code segment/pointer */
561 FinalCS
= LoadSegment
+ Header
->e_cs
;
562 FinalIP
= Header
->e_ip
;
568 if (LoadType
!= DOS_LOAD_OVERLAY
)
570 /* Find the maximum amount of memory that can be allocated */
571 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
573 /* Make sure it's enough for the whole program and the PSP */
574 if (((DWORD
)MaxAllocSize
<< 4) < (ExeBufferSize
+ sizeof(DOS_PSP
)))
576 Result
= ERROR_NOT_ENOUGH_MEMORY
;
580 /* Allocate all of it */
581 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
584 Result
= Sda
->LastErrorCode
;
588 /* The process owns its memory */
589 DosChangeMemoryOwner(Segment
, Segment
);
590 DosChangeMemoryOwner(EnvBlock
, Segment
);
592 /* Set INT 22h to the return address */
593 ((PULONG
)BaseAddress
)[0x22] = ReturnAddress
;
595 /* Create the PSP and initialize it */
596 DosCreatePsp(Segment
, MaxAllocSize
);
597 DosInitPsp(Segment
, EnvBlock
, CommandLine
, ExePath
);
599 /* Calculate the segment where the program should be loaded */
600 LoadSegment
= Segment
+ (sizeof(DOS_PSP
) >> 4);
605 LoadSegment
= Parameters
->Overlay
.Segment
;
608 /* Copy the program to the code segment */
609 RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment
, 0),
610 ExeBuffer
, ExeBufferSize
);
612 /* Set the stack to the last word of the segment */
617 * Set the value on the stack to 0x0000, so that a near return
618 * jumps to PSP:0000 which has the exit code.
620 *((LPWORD
)SEG_OFF_TO_PTR(Segment
, 0xFFFE)) = 0x0000;
622 /* Set the code segment/pointer */
627 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
629 /* Save the program state */
630 if (Sda
->CurrentPsp
!= SYSTEM_PSP
)
632 /* Push the task state */
635 DPRINT1("Sda->CurrentPsp = 0x%04x; Old LastStack = 0x%08x, New LastStack = 0x%08x\n",
636 Sda
->CurrentPsp
, SEGMENT_TO_PSP(Sda
->CurrentPsp
)->LastStack
, MAKELONG(getSP(), getSS()));
638 /* Update the last stack in the PSP */
639 SEGMENT_TO_PSP(Sda
->CurrentPsp
)->LastStack
= MAKELONG(getSP(), getSS());
642 /* Set the initial segment registers */
651 * Set the other registers as in real DOS: some demos expect them so!
652 * See http://www.fysnet.net/yourhelp.htm
653 * and http://www.beroset.com/asm/showregs.asm
657 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.
660 setAX(0/*0xFFFF*/); // FIXME: fcbcode
661 setBX(0/*0xFFFF*/); // FIXME: fcbcode
665 * Keep critical flags, clear test flags (OF, SF, ZF, AF, PF, CF)
666 * and explicitely set the interrupt flag.
668 setEFLAGS((getEFLAGS() & ~0x08D5) | 0x0200);
670 /* Notify VDDs of process execution */
671 VDDCreateUserHook(Segment
);
674 DosSetProcessContext(Segment
);
675 CpuExecute(FinalCS
, FinalIP
);
677 else if (LoadType
== DOS_LOAD_ONLY
)
680 Parameters
->StackLocation
= MAKELONG(FinalSP
, FinalSS
);
681 Parameters
->EntryPoint
= MAKELONG(FinalIP
, FinalCS
);
685 if (Result
!= ERROR_SUCCESS
)
687 /* It was not successful, cleanup the DOS memory */
688 if (EnvBlock
) DosFreeMemory(EnvBlock
);
689 if (Segment
) DosFreeMemory(Segment
);
695 DWORD
DosLoadExecutable(IN DOS_EXEC_TYPE LoadType
,
696 IN LPCSTR ExecutablePath
,
697 IN PDOS_EXEC_PARAM_BLOCK Parameters
,
698 IN LPCSTR CommandLine OPTIONAL
,
699 IN LPCSTR Environment OPTIONAL
,
700 IN DWORD ReturnAddress OPTIONAL
)
702 DWORD Result
= ERROR_SUCCESS
;
703 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
705 LPBYTE Address
= NULL
;
706 CHAR FullPath
[MAX_PATH
];
707 CHAR ShortFullPath
[MAX_PATH
];
709 DPRINT1("DosLoadExecutable(%d, '%s', 0x%p, 0x%p, 0x%p)\n",
710 LoadType
, ExecutablePath
, Parameters
, CommandLine
, Environment
);
712 /* Try to get the full path to the executable */
713 if (GetFullPathNameA(ExecutablePath
, sizeof(FullPath
), FullPath
, NULL
))
715 /* Get the corresponding short path */
716 if (GetShortPathNameA(FullPath
, ShortFullPath
, sizeof(ShortFullPath
)))
718 /* Use the shortened full path from now on */
719 ExecutablePath
= ShortFullPath
;
723 /* Open a handle to the executable */
724 FileHandle
= CreateFileA(ExecutablePath
,
729 FILE_ATTRIBUTE_NORMAL
,
731 if (FileHandle
== INVALID_HANDLE_VALUE
)
733 Result
= GetLastError();
737 /* Get the file size */
738 FileSize
= GetFileSize(FileHandle
, NULL
);
740 /* Create a mapping object for the file */
741 FileMapping
= CreateFileMapping(FileHandle
,
747 if (FileMapping
== NULL
)
749 Result
= GetLastError();
753 /* Map the file into memory */
754 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
757 Result
= GetLastError();
761 Result
= DosLoadExecutableInternal(LoadType
,
772 if (Address
!= NULL
) UnmapViewOfFile(Address
);
774 /* Close the file mapping object */
775 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
777 /* Close the file handle */
778 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
783 WORD
DosCreateProcess(IN LPCSTR ProgramName
,
784 IN PDOS_EXEC_PARAM_BLOCK Parameters
,
785 IN DWORD ReturnAddress OPTIONAL
)
787 DWORD Result
= ERROR_SUCCESS
;
790 /* Get the binary type */
791 if (!GetBinaryTypeA(ProgramName
, &BinaryType
)) return GetLastError();
793 /* Check the type of the program */
796 /* Those are handled by NTVDM */
799 DisplayMessage(L
"Trying to load '%S'.\n"
800 L
"WOW16 applications are not supported internally by NTVDM at the moment.\n"
801 L
"Press 'OK' to continue.",
807 /* Load the executable */
808 Result
= DosLoadExecutable(DOS_LOAD_AND_EXECUTE
,
814 if (Result
!= ERROR_SUCCESS
)
816 DisplayMessage(L
"Could not load '%S'. Error: %u", ProgramName
, Result
);
822 /* Not handled by NTVDM */
825 LPSTR Environment
= NULL
;
826 CHAR CmdLine
[MAX_PATH
+ DOS_CMDLINE_LENGTH
+ 1];
830 /* Did the caller specify an environment segment? */
831 if (Parameters
->Environment
)
833 /* Yes, use it instead of the parent one */
834 Environment
= (LPSTR
)SEG_OFF_TO_PTR(Parameters
->Environment
, 0);
838 * Convert the DOS command line to Win32-compatible format, by concatenating
839 * the program name with the converted command line.
840 * Format of the DOS command line: 1 byte for size; 127 bytes for contents.
842 CmdLinePtr
= CmdLine
;
843 strncpy(CmdLinePtr
, ProgramName
, MAX_PATH
); // Concatenate the program name
844 CmdLinePtr
+= strlen(CmdLinePtr
);
845 *CmdLinePtr
++ = ' '; // Add separating space
847 CmdLineSize
= min(*(PBYTE
)FAR_POINTER(Parameters
->CommandLine
), DOS_CMDLINE_LENGTH
);
848 RtlCopyMemory(CmdLinePtr
,
849 (LPSTR
)FAR_POINTER(Parameters
->CommandLine
) + 1,
851 /* NULL-terminate it */
852 CmdLinePtr
[CmdLineSize
] = '\0';
854 /* Remove any trailing return carriage character and NULL-terminate the command line */
855 while (*CmdLinePtr
&& *CmdLinePtr
!= '\r' && *CmdLinePtr
!= '\n') CmdLinePtr
++;
858 Result
= DosStartProcess32(ProgramName
, CmdLine
,
859 Environment
, ReturnAddress
,
861 if (Result
!= ERROR_SUCCESS
)
863 DisplayMessage(L
"Could not load 32-bit '%S'. Error: %u", ProgramName
, Result
);
873 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
, WORD KeepResident
)
875 WORD McbSegment
= SysVars
->FirstMcb
;
877 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
878 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
880 BYTE TerminationType
;
882 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
883 Psp
, ReturnCode
, KeepResident
);
885 /* Notify VDDs of process termination */
886 VDDTerminateUserHook(Psp
);
888 /* Check if this PSP is its own parent */
889 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
891 if (KeepResident
== 0)
894 for (i
= 0; i
< PspBlock
->HandleTableSize
; i
++)
896 /* Close the handle */
901 /* Free the memory used by the process */
904 /* Get a pointer to the MCB */
905 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
907 /* Make sure the MCB is valid */
908 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z') break;
910 /* Check if this block was allocated by the process */
911 if (CurrentMcb
->OwnerPsp
== Psp
)
915 /* Check if this is the PSP block and we should reduce its size */
916 if ((McbSegment
+ 1) == Psp
&& KeepResident
< CurrentMcb
->Size
)
918 /* Reduce the size of the block */
919 DosResizeMemory(McbSegment
+ 1, KeepResident
, NULL
);
925 /* Free this entire block */
926 DosFreeMemory(McbSegment
+ 1);
930 /* If this was the last block, quit */
931 if (CurrentMcb
->BlockType
== 'Z') break;
933 /* Update the segment and continue */
934 McbSegment
+= CurrentMcb
->Size
+ 1;
938 /* Restore the interrupt vectors */
939 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
940 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
941 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
943 /* Update the current PSP with the parent's one */
944 if (Psp
== Sda
->CurrentPsp
)
946 DosSetProcessContext(PspBlock
->ParentPsp
);
947 if (Sda
->CurrentPsp
== SYSTEM_PSP
)
949 // NOTE: we can also use the DOS BIOS exit code.
955 /* Save the return code - Normal termination or TSR */
956 TerminationType
= (KeepResident
!= 0 ? 0x03 : 0x00);
957 Sda
->ErrorLevel
= MAKEWORD(ReturnCode
, TerminationType
);
959 DPRINT1("PspBlock->ParentPsp = 0x%04x; Sda->CurrentPsp = 0x%04x\n",
960 PspBlock
->ParentPsp
, Sda
->CurrentPsp
);
962 if (Sda
->CurrentPsp
!= SYSTEM_PSP
)
964 DPRINT1("Sda->CurrentPsp = 0x%04x; Old SS:SP = %04X:%04X going to be LastStack = 0x%08x\n",
965 Sda
->CurrentPsp
, getSS(), getSP(), SEGMENT_TO_PSP(Sda
->CurrentPsp
)->LastStack
);
967 /* Restore the parent's stack */
968 setSS(HIWORD(SEGMENT_TO_PSP(Sda
->CurrentPsp
)->LastStack
));
969 setSP(LOWORD(SEGMENT_TO_PSP(Sda
->CurrentPsp
)->LastStack
));
971 /* Pop the task state */
975 /* Return control to the parent process */
976 Stack
= (LPWORD
)SEG_OFF_TO_PTR(getSS(), getSP());
977 Stack
[STACK_CS
] = HIWORD(PspBlock
->TerminateAddress
);
978 Stack
[STACK_IP
] = LOWORD(PspBlock
->TerminateAddress
);