[NTVDM]
[reactos.git] / reactos / subsystems / mvdm / ntvdm / dos / dos32krnl / process.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: 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)
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #define NDEBUG
13
14 #include "ntvdm.h"
15 #include "emulator.h"
16 #include "cpu/cpu.h"
17
18 #include "dos.h"
19 #include "dos/dem.h"
20 #include "dosfiles.h"
21 #include "handle.h"
22 #include "process.h"
23 #include "memory.h"
24
25 #include "bios/bios.h"
26
27 #include "io.h"
28 #include "hardware/ps2.h"
29
30 /* PUBLIC VARIABLES ***********************************************************/
31
32 WORD CurrentPsp = SYSTEM_PSP;
33
34 /* PRIVATE FUNCTIONS **********************************************************/
35
36 static inline VOID DosSetPspCommandLine(WORD Segment, LPCSTR CommandLine)
37 {
38 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
39
40 /* Set the command line */
41 PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1);
42 RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
43 PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
44 }
45
46 static WORD DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL,
47 LPCSTR ProgramName)
48 {
49 PCHAR Ptr, DestBuffer = NULL;
50 ULONG TotalSize = 0;
51 WORD DestSegment;
52
53 /* If we have an environment strings list, compute its size */
54 if (Environment)
55 {
56 /* Calculate the size of the environment block */
57 Ptr = (PCHAR)Environment;
58 while (*Ptr) Ptr += strlen(Ptr) + 1;
59 TotalSize = (ULONG_PTR)Ptr - (ULONG_PTR)Environment;
60 }
61 else
62 {
63 /* Empty environment string */
64 TotalSize = 1;
65 }
66 /* Add the final environment block NULL-terminator */
67 TotalSize++;
68
69 /* Add the two bytes for the program name tag */
70 TotalSize += 2;
71
72 /* Add the string buffer size */
73 TotalSize += strlen(ProgramName) + 1;
74
75 /* Allocate the memory for the environment block */
76 DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
77 if (!DestSegment) return 0;
78
79 DestBuffer = (PCHAR)SEG_OFF_TO_PTR(DestSegment, 0);
80
81 /* If we have an environment strings list, copy it */
82 if (Environment)
83 {
84 Ptr = (PCHAR)Environment;
85 while (*Ptr)
86 {
87 /* Copy the string and NULL-terminate it */
88 strcpy(DestBuffer, Ptr);
89 DestBuffer += strlen(Ptr);
90 *(DestBuffer++) = '\0';
91
92 /* Move to the next string */
93 Ptr += strlen(Ptr) + 1;
94 }
95 }
96 else
97 {
98 /* Empty environment string */
99 *(DestBuffer++) = '\0';
100 }
101 /* NULL-terminate the environment block */
102 *(DestBuffer++) = '\0';
103
104 /* Store the special program name tag */
105 *(DestBuffer++) = LOBYTE(DOS_PROGRAM_NAME_TAG);
106 *(DestBuffer++) = HIBYTE(DOS_PROGRAM_NAME_TAG);
107
108 /* Copy the program name after the environment block */
109 strcpy(DestBuffer, ProgramName);
110
111 return DestSegment;
112 }
113
114 /* PUBLIC FUNCTIONS ***********************************************************/
115
116 VOID DosClonePsp(WORD DestSegment, WORD SourceSegment)
117 {
118 PDOS_PSP DestPsp = SEGMENT_TO_PSP(DestSegment);
119 PDOS_PSP SourcePsp = SEGMENT_TO_PSP(SourceSegment);
120 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
121
122 /* Literally copy the PSP first */
123 RtlCopyMemory(DestPsp, SourcePsp, sizeof(DOS_PSP));
124
125 /* Save the interrupt vectors */
126 DestPsp->TerminateAddress = IntVecTable[0x22];
127 DestPsp->BreakAddress = IntVecTable[0x23];
128 DestPsp->CriticalAddress = IntVecTable[0x24];
129
130 /* No parent PSP */
131 DestPsp->ParentPsp = 0;
132
133 /* Set the handle table pointers to the internal handle table */
134 DestPsp->HandleTableSize = DEFAULT_JFT_SIZE;
135 DestPsp->HandleTablePtr = MAKELONG(0x18, DestSegment);
136
137 /* Copy the parent handle table without referencing the SFT */
138 RtlCopyMemory(FAR_POINTER(DestPsp->HandleTablePtr),
139 FAR_POINTER(SourcePsp->HandleTablePtr),
140 DEFAULT_JFT_SIZE);
141 }
142
143 VOID DosCreatePsp(WORD Segment, WORD ProgramSize)
144 {
145 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
146 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
147
148 RtlZeroMemory(PspBlock, sizeof(*PspBlock));
149
150 /* Set the exit interrupt */
151 PspBlock->Exit[0] = 0xCD; // int 0x20
152 PspBlock->Exit[1] = 0x20;
153
154 /* Set the number of the last paragraph */
155 PspBlock->LastParagraph = Segment + ProgramSize - 1;
156
157 /* Save the interrupt vectors */
158 PspBlock->TerminateAddress = IntVecTable[0x22];
159 PspBlock->BreakAddress = IntVecTable[0x23];
160 PspBlock->CriticalAddress = IntVecTable[0x24];
161
162 /* Set the parent PSP */
163 PspBlock->ParentPsp = CurrentPsp;
164
165 /* No environment block yet */
166 PspBlock->EnvBlock = 0;
167
168 /* Copy the parent handle table */
169 DosCopyHandleTable(PspBlock->HandleTable);
170
171 /* Set the handle table pointers to the internal handle table */
172 PspBlock->HandleTableSize = DEFAULT_JFT_SIZE;
173 PspBlock->HandleTablePtr = MAKELONG(0x18, Segment);
174
175 /* Set the DOS version */
176 PspBlock->DosVersion = DOS_VERSION;
177
178 /* Set the far call opcodes */
179 PspBlock->FarCall[0] = 0xCD; // int 0x21
180 PspBlock->FarCall[1] = 0x21;
181 PspBlock->FarCall[2] = 0xCB; // retf
182 }
183
184 VOID DosSetProcessContext(WORD Segment)
185 {
186 CurrentPsp = Segment;
187 DiskTransferArea = MAKELONG(0x80, Segment);
188 }
189
190 DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
191 IN LPCSTR ExecutablePath,
192 IN PDOS_EXEC_PARAM_BLOCK Parameters,
193 IN LPCSTR CommandLine OPTIONAL,
194 IN LPCSTR Environment OPTIONAL,
195 IN DWORD ReturnAddress OPTIONAL)
196 {
197 DWORD Result = ERROR_SUCCESS;
198 HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
199 LPBYTE Address = NULL;
200 WORD Segment = 0;
201 WORD EnvBlock = 0;
202 WORD LoadSegment;
203 WORD MaxAllocSize;
204 DWORD i, FileSize;
205
206 DPRINT1("DosLoadExecutable(%d, %s, 0x%08X, 0x%08X)\n",
207 LoadType,
208 ExecutablePath,
209 Parameters,
210 ReturnAddress);
211
212 /* Open a handle to the executable */
213 FileHandle = CreateFileA(ExecutablePath,
214 GENERIC_READ,
215 FILE_SHARE_READ,
216 NULL,
217 OPEN_EXISTING,
218 FILE_ATTRIBUTE_NORMAL,
219 NULL);
220 if (FileHandle == INVALID_HANDLE_VALUE)
221 {
222 Result = GetLastError();
223 goto Cleanup;
224 }
225
226 /* Get the file size */
227 FileSize = GetFileSize(FileHandle, NULL);
228
229 /* Create a mapping object for the file */
230 FileMapping = CreateFileMapping(FileHandle,
231 NULL,
232 PAGE_READONLY,
233 0,
234 0,
235 NULL);
236 if (FileMapping == NULL)
237 {
238 Result = GetLastError();
239 goto Cleanup;
240 }
241
242 /* Map the file into memory */
243 Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
244 if (Address == NULL)
245 {
246 Result = GetLastError();
247 goto Cleanup;
248 }
249
250 if (LoadType != DOS_LOAD_OVERLAY)
251 {
252 LPSTR CmdLinePtr;
253
254 if (CommandLine == NULL)
255 {
256 /* Get the command line from the parameter block */
257 CommandLine = (LPCSTR)FAR_POINTER(Parameters->CommandLine);
258 }
259
260 if (Environment == NULL)
261 {
262 /* Get the environment from the parameter block */
263 Environment = (LPCSTR)SEG_OFF_TO_PTR(Parameters->Environment, 0);
264 }
265
266 /* NULL-terminate the command line by removing the return carriage character */
267 CmdLinePtr = (LPSTR)CommandLine;
268 while (*CmdLinePtr && *CmdLinePtr != '\r') CmdLinePtr++;
269 *CmdLinePtr = '\0';
270
271 /* Copy the environment block to DOS memory */
272 EnvBlock = DosCopyEnvironmentBlock(Environment, ExecutablePath);
273 if (EnvBlock == 0)
274 {
275 Result = ERROR_NOT_ENOUGH_MEMORY;
276 goto Cleanup;
277 }
278 }
279
280 /* Check if this is an EXE file or a COM file */
281 if (Address[0] == 'M' && Address[1] == 'Z')
282 {
283 /* EXE file */
284 PIMAGE_DOS_HEADER Header;
285 DWORD BaseSize;
286 PDWORD RelocationTable;
287 PWORD RelocWord;
288 WORD RelocFactor;
289 BOOLEAN LoadHigh = FALSE;
290
291 /* Get the MZ header */
292 Header = (PIMAGE_DOS_HEADER)Address;
293
294 /* Get the base size of the file, in paragraphs (rounded up) */
295 BaseSize = (((Header->e_cp - 1) * 512) + Header->e_cblp + 0x0F) >> 4;
296
297 if (LoadType != DOS_LOAD_OVERLAY)
298 {
299 DWORD TotalSize = BaseSize;
300
301 /* Add the PSP size, in paragraphs */
302 TotalSize += sizeof(DOS_PSP) >> 4;
303
304 /* Add the maximum size that should be allocated */
305 TotalSize += Header->e_maxalloc;
306
307 if (Header->e_minalloc == 0 && Header->e_maxalloc == 0)
308 {
309 /* This program should be loaded high */
310 LoadHigh = TRUE;
311 TotalSize = 0xFFFF;
312 }
313
314 /* Make sure it does not pass 0xFFFF */
315 if (TotalSize > 0xFFFF) TotalSize = 0xFFFF;
316
317 /* Try to allocate that much memory */
318 Segment = DosAllocateMemory((WORD)TotalSize, &MaxAllocSize);
319
320 if (Segment == 0)
321 {
322 /* Check if there's at least enough memory for the minimum size */
323 if (MaxAllocSize < (BaseSize + (sizeof(DOS_PSP) >> 4) + Header->e_minalloc))
324 {
325 Result = DosLastError;
326 goto Cleanup;
327 }
328
329 /* Allocate that minimum amount */
330 TotalSize = MaxAllocSize;
331 Segment = DosAllocateMemory((WORD)TotalSize, NULL);
332 ASSERT(Segment != 0);
333 }
334
335 /* The process owns its own memory */
336 DosChangeMemoryOwner(Segment, Segment);
337 DosChangeMemoryOwner(EnvBlock, Segment);
338
339 /* Set INT 22h to the return address */
340 ((PULONG)BaseAddress)[0x22] = ReturnAddress;
341
342 /* Create the PSP */
343 DosCreatePsp(Segment, (WORD)TotalSize);
344 DosSetPspCommandLine(Segment, CommandLine);
345 SEGMENT_TO_PSP(Segment)->EnvBlock = EnvBlock;
346
347 /* Calculate the segment where the program should be loaded */
348 if (!LoadHigh) LoadSegment = Segment + (sizeof(DOS_PSP) >> 4);
349 else LoadSegment = Segment + TotalSize - BaseSize;
350
351 RelocFactor = LoadSegment;
352 }
353 else
354 {
355 LoadSegment = Parameters->Overlay.Segment;
356 RelocFactor = Parameters->Overlay.RelocationFactor;
357 }
358
359 /* Copy the program to the code segment */
360 RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
361 Address + (Header->e_cparhdr << 4),
362 min(FileSize - (Header->e_cparhdr << 4), BaseSize << 4));
363
364 /* Get the relocation table */
365 RelocationTable = (PDWORD)(Address + Header->e_lfarlc);
366
367 /* Perform relocations */
368 for (i = 0; i < Header->e_crlc; i++)
369 {
370 /* Get a pointer to the word that needs to be patched */
371 RelocWord = (PWORD)SEG_OFF_TO_PTR(LoadSegment + HIWORD(RelocationTable[i]),
372 LOWORD(RelocationTable[i]));
373
374 /* Add the relocation factor to it */
375 *RelocWord += RelocFactor;
376 }
377
378 if (LoadType == DOS_LOAD_AND_EXECUTE)
379 {
380 /* Set the initial segment registers */
381 setDS(Segment);
382 setES(Segment);
383
384 /* Set the stack to the location from the header */
385 setSS(LoadSegment + Header->e_ss);
386 setSP(Header->e_sp);
387
388 /* Execute */
389 DosSetProcessContext(Segment);
390 CpuExecute(LoadSegment + Header->e_cs, Header->e_ip);
391 }
392 else if (LoadType == DOS_LOAD_ONLY)
393 {
394 Parameters->StackLocation = MAKELONG(Header->e_sp, LoadSegment + Header->e_ss);
395 Parameters->EntryPoint = MAKELONG(Header->e_ip, LoadSegment + Header->e_cs);
396 }
397 }
398 else
399 {
400 /* COM file */
401
402 if (LoadType != DOS_LOAD_OVERLAY)
403 {
404 /* Find the maximum amount of memory that can be allocated */
405 DosAllocateMemory(0xFFFF, &MaxAllocSize);
406
407 /* Make sure it's enough for the whole program and the PSP */
408 if (((DWORD)MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP)))
409 {
410 Result = ERROR_NOT_ENOUGH_MEMORY;
411 goto Cleanup;
412 }
413
414 /* Allocate all of it */
415 Segment = DosAllocateMemory(MaxAllocSize, NULL);
416 if (Segment == 0)
417 {
418 Result = DosLastError;
419 goto Cleanup;
420 }
421
422 /* The process owns its own memory */
423 DosChangeMemoryOwner(Segment, Segment);
424 DosChangeMemoryOwner(EnvBlock, Segment);
425
426 /* Set INT 22h to the return address */
427 ((PULONG)BaseAddress)[0x22] = ReturnAddress;
428
429 /* Create the PSP */
430 DosCreatePsp(Segment, MaxAllocSize);
431 DosSetPspCommandLine(Segment, CommandLine);
432 SEGMENT_TO_PSP(Segment)->EnvBlock = EnvBlock;
433
434 /* Calculate the segment where the program should be loaded */
435 LoadSegment = Segment + (sizeof(DOS_PSP) >> 4);
436 }
437 else
438 {
439 LoadSegment = Parameters->Overlay.Segment;
440 }
441
442 RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
443 Address,
444 FileSize);
445
446 if (LoadType == DOS_LOAD_AND_EXECUTE)
447 {
448 /* Set the initial segment registers */
449 setDS(Segment);
450 setES(Segment);
451
452 /* Set the stack to the last word of the segment */
453 setSS(Segment);
454 setSP(0xFFFE);
455
456 /*
457 * Set the value on the stack to 0, so that a near return
458 * jumps to PSP:0000 which has the exit code.
459 */
460 *((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0;
461
462 /* Execute */
463 DosSetProcessContext(Segment);
464 CpuExecute(Segment, 0x100);
465 }
466 else if (LoadType == DOS_LOAD_ONLY)
467 {
468 Parameters->StackLocation = MAKELONG(0xFFFE, Segment);
469 Parameters->EntryPoint = MAKELONG(0x0100, Segment);
470 }
471 }
472
473 Cleanup:
474 if (Result != ERROR_SUCCESS)
475 {
476 /* It was not successful, cleanup the DOS memory */
477 if (EnvBlock) DosFreeMemory(EnvBlock);
478 if (Segment) DosFreeMemory(Segment);
479 }
480
481 /* Unmap the file*/
482 if (Address != NULL) UnmapViewOfFile(Address);
483
484 /* Close the file mapping object */
485 if (FileMapping != NULL) CloseHandle(FileMapping);
486
487 /* Close the file handle */
488 if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
489
490 return Result;
491 }
492
493 DWORD DosStartProcess(IN LPCSTR ExecutablePath,
494 IN LPCSTR CommandLine,
495 IN LPCSTR Environment OPTIONAL)
496 {
497 DWORD Result;
498 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
499
500 Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
501 ExecutablePath,
502 NULL,
503 CommandLine,
504 Environment,
505 IntVecTable[0x20]);
506
507 if (Result != ERROR_SUCCESS) goto Quit;
508
509 /* Attach to the console */
510 ConsoleAttach();
511 VidBiosAttachToConsole();
512
513 // HACK: Simulate a ENTER key release scancode on the PS/2 port because
514 // some apps expect to read a key release scancode (> 0x80) when they
515 // are started.
516 IOWriteB(PS2_CONTROL_PORT, 0xD2); // Next write is for the first PS/2 port
517 IOWriteB(PS2_DATA_PORT, 0x80 | 0x1C); // ENTER key release
518
519 /* Start simulation */
520 SetEvent(VdmTaskEvent);
521 CpuSimulate();
522
523 /* Detach from the console */
524 VidBiosDetachFromConsole();
525 ConsoleDetach();
526
527 Quit:
528 return Result;
529 }
530
531 #ifndef STANDALONE
532 WORD DosCreateProcess(LPCSTR ProgramName,
533 PDOS_EXEC_PARAM_BLOCK Parameters,
534 DWORD ReturnAddress)
535 {
536 DWORD Result;
537 DWORD BinaryType;
538 LPVOID Environment = NULL;
539 VDM_COMMAND_INFO CommandInfo;
540 CHAR CmdLine[MAX_PATH];
541 CHAR AppName[MAX_PATH];
542 CHAR PifFile[MAX_PATH];
543 CHAR Desktop[MAX_PATH];
544 CHAR Title[MAX_PATH];
545 ULONG EnvSize = 256;
546 PVOID Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
547 STARTUPINFOA StartupInfo;
548 PROCESS_INFORMATION ProcessInfo;
549
550 /* Get the binary type */
551 if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
552
553 /* Did the caller specify an environment segment? */
554 if (Parameters->Environment)
555 {
556 /* Yes, use it instead of the parent one */
557 Environment = SEG_OFF_TO_PTR(Parameters->Environment, 0);
558 }
559
560 /* Set up the startup info structure */
561 RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
562 StartupInfo.cb = sizeof(StartupInfo);
563
564 /* Create the process */
565 if (!CreateProcessA(ProgramName,
566 FAR_POINTER(Parameters->CommandLine),
567 NULL,
568 NULL,
569 FALSE,
570 0,
571 Environment,
572 NULL,
573 &StartupInfo,
574 &ProcessInfo))
575 {
576 return GetLastError();
577 }
578
579 /* Check the type of the program */
580 switch (BinaryType)
581 {
582 /* These are handled by NTVDM */
583 case SCS_DOS_BINARY:
584 case SCS_WOW_BINARY:
585 {
586 /* Clear the structure */
587 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
588
589 /* Initialize the structure members */
590 CommandInfo.TaskId = SessionId;
591 CommandInfo.VDMState = VDM_FLAG_NESTED_TASK | VDM_FLAG_DONT_WAIT;
592 CommandInfo.CmdLine = CmdLine;
593 CommandInfo.CmdLen = sizeof(CmdLine);
594 CommandInfo.AppName = AppName;
595 CommandInfo.AppLen = sizeof(AppName);
596 CommandInfo.PifFile = PifFile;
597 CommandInfo.PifLen = sizeof(PifFile);
598 CommandInfo.Desktop = Desktop;
599 CommandInfo.DesktopLen = sizeof(Desktop);
600 CommandInfo.Title = Title;
601 CommandInfo.TitleLen = sizeof(Title);
602 CommandInfo.Env = Env;
603 CommandInfo.EnvLen = EnvSize;
604
605 Command:
606 /* Get the VDM command information */
607 if (!GetNextVDMCommand(&CommandInfo))
608 {
609 if (CommandInfo.EnvLen > EnvSize)
610 {
611 /* Expand the environment size */
612 EnvSize = CommandInfo.EnvLen;
613 CommandInfo.Env = Env = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Env, EnvSize);
614
615 /* Repeat the request */
616 CommandInfo.VDMState |= VDM_FLAG_RETRY;
617 goto Command;
618 }
619
620 /* Shouldn't happen */
621 ASSERT(FALSE);
622 }
623
624 /* Load the executable */
625 Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
626 AppName,
627 Parameters,
628 CmdLine,
629 Env,
630 ReturnAddress);
631 if (Result == ERROR_SUCCESS)
632 {
633 /* Increment the re-entry count */
634 CommandInfo.VDMState = VDM_INC_REENTER_COUNT;
635 GetNextVDMCommand(&CommandInfo);
636 }
637 else
638 {
639 DisplayMessage(L"Could not load '%S'. Error: %u", AppName, Result);
640 }
641
642 break;
643 }
644
645 /* Not handled by NTVDM */
646 default:
647 {
648 /* Wait for the process to finish executing */
649 WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
650 }
651 }
652
653 RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
654
655 /* Close the handles */
656 CloseHandle(ProcessInfo.hProcess);
657 CloseHandle(ProcessInfo.hThread);
658
659 return ERROR_SUCCESS;
660 }
661 #endif
662
663 VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
664 {
665 WORD i;
666 WORD McbSegment = FIRST_MCB_SEGMENT;
667 PDOS_MCB CurrentMcb;
668 LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
669 PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
670
671 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
672 Psp,
673 ReturnCode,
674 KeepResident);
675
676 /* Check if this PSP is it's own parent */
677 if (PspBlock->ParentPsp == Psp) goto Done;
678
679 if (KeepResident == 0)
680 {
681 for (i = 0; i < PspBlock->HandleTableSize; i++)
682 {
683 /* Close the handle */
684 DosCloseHandle(i);
685 }
686 }
687
688 /* Free the memory used by the process */
689 while (TRUE)
690 {
691 /* Get a pointer to the MCB */
692 CurrentMcb = SEGMENT_TO_MCB(McbSegment);
693
694 /* Make sure the MCB is valid */
695 if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') break;
696
697 /* Check if this block was allocated by the process */
698 if (CurrentMcb->OwnerPsp == Psp)
699 {
700 if (KeepResident == 0)
701 {
702 /* Free this entire block */
703 DosFreeMemory(McbSegment + 1);
704 }
705 else if (KeepResident < CurrentMcb->Size)
706 {
707 /* Reduce the size of the block */
708 DosResizeMemory(McbSegment + 1, KeepResident, NULL);
709
710 /* No further paragraphs need to stay resident */
711 KeepResident = 0;
712 }
713 else
714 {
715 /* Just reduce the amount of paragraphs we need to keep resident */
716 KeepResident -= CurrentMcb->Size;
717 }
718 }
719
720 /* If this was the last block, quit */
721 if (CurrentMcb->BlockType == 'Z') break;
722
723 /* Update the segment and continue */
724 McbSegment += CurrentMcb->Size + 1;
725 }
726
727 Done:
728 /* Restore the interrupt vectors */
729 IntVecTable[0x22] = PspBlock->TerminateAddress;
730 IntVecTable[0x23] = PspBlock->BreakAddress;
731 IntVecTable[0x24] = PspBlock->CriticalAddress;
732
733 /* Update the current PSP */
734 if (Psp == CurrentPsp)
735 {
736 CurrentPsp = PspBlock->ParentPsp;
737 if (CurrentPsp == SYSTEM_PSP)
738 {
739 ResetEvent(VdmTaskEvent);
740 CpuUnsimulate();
741 }
742 }
743
744 #ifndef STANDALONE
745 // FIXME: This is probably not the best way to do it
746 /* Check if this was a nested DOS task */
747 if (CurrentPsp != SYSTEM_PSP)
748 {
749 VDM_COMMAND_INFO CommandInfo;
750
751 /* Decrement the re-entry count */
752 CommandInfo.TaskId = SessionId;
753 CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
754 GetNextVDMCommand(&CommandInfo);
755
756 /* Clear the structure */
757 RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
758
759 /* Update the VDM state of the task */
760 CommandInfo.TaskId = SessionId;
761 CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
762 GetNextVDMCommand(&CommandInfo);
763 }
764 #endif
765
766 /* Save the return code - Normal termination */
767 DosErrorLevel = MAKEWORD(ReturnCode, 0x00);
768
769 /* Return control to the parent process */
770 CpuExecute(HIWORD(PspBlock->TerminateAddress),
771 LOWORD(PspBlock->TerminateAddress));
772 }
773