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)
10 /* INCLUDES *******************************************************************/
25 #include "bios/bios.h"
28 #include "hardware/ps2.h"
30 /* PUBLIC VARIABLES ***********************************************************/
32 WORD CurrentPsp
= SYSTEM_PSP
;
34 /* PRIVATE FUNCTIONS **********************************************************/
36 static inline VOID
DosSetPspCommandLine(WORD Segment
, LPCSTR CommandLine
)
38 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Segment
);
41 * Copy the command line block.
42 * Format of the CommandLine parameter: 1 byte for size; 127 bytes for contents.
44 PspBlock
->CommandLineSize
= min(*(PBYTE
)CommandLine
, DOS_CMDLINE_LENGTH
);
46 RtlCopyMemory(PspBlock
->CommandLine
, CommandLine
, DOS_CMDLINE_LENGTH
);
49 static WORD
DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL
,
52 PCHAR Ptr
, DestBuffer
= NULL
;
56 /* If we have an environment strings list, compute its size */
59 /* Calculate the size of the environment block */
60 Ptr
= (PCHAR
)Environment
;
61 while (*Ptr
) Ptr
+= strlen(Ptr
) + 1;
62 TotalSize
= (ULONG_PTR
)Ptr
- (ULONG_PTR
)Environment
;
66 /* Empty environment string */
69 /* Add the final environment block NULL-terminator */
72 /* Add the two bytes for the program name tag */
75 /* Add the string buffer size */
76 TotalSize
+= strlen(ProgramName
) + 1;
78 /* Allocate the memory for the environment block */
79 DestSegment
= DosAllocateMemory((WORD
)((TotalSize
+ 0x0F) >> 4), NULL
);
80 if (!DestSegment
) return 0;
82 DestBuffer
= (PCHAR
)SEG_OFF_TO_PTR(DestSegment
, 0);
84 /* If we have an environment strings list, copy it */
87 Ptr
= (PCHAR
)Environment
;
90 /* Copy the string and NULL-terminate it */
91 strcpy(DestBuffer
, Ptr
);
92 DestBuffer
+= strlen(Ptr
);
93 *(DestBuffer
++) = '\0';
95 /* Move to the next string */
96 Ptr
+= strlen(Ptr
) + 1;
101 /* Empty environment string */
102 *(DestBuffer
++) = '\0';
104 /* NULL-terminate the environment block */
105 *(DestBuffer
++) = '\0';
107 /* Store the special program name tag */
108 *(DestBuffer
++) = LOBYTE(DOS_PROGRAM_NAME_TAG
);
109 *(DestBuffer
++) = HIBYTE(DOS_PROGRAM_NAME_TAG
);
111 /* Copy the program name after the environment block */
112 strcpy(DestBuffer
, ProgramName
);
117 /* PUBLIC FUNCTIONS ***********************************************************/
119 VOID
DosClonePsp(WORD DestSegment
, WORD SourceSegment
)
121 PDOS_PSP DestPsp
= SEGMENT_TO_PSP(DestSegment
);
122 PDOS_PSP SourcePsp
= SEGMENT_TO_PSP(SourceSegment
);
123 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
125 /* Literally copy the PSP first */
126 RtlCopyMemory(DestPsp
, SourcePsp
, sizeof(DOS_PSP
));
128 /* Save the interrupt vectors */
129 DestPsp
->TerminateAddress
= IntVecTable
[0x22];
130 DestPsp
->BreakAddress
= IntVecTable
[0x23];
131 DestPsp
->CriticalAddress
= IntVecTable
[0x24];
134 DestPsp
->ParentPsp
= 0;
136 /* Set the handle table pointers to the internal handle table */
137 DestPsp
->HandleTableSize
= DEFAULT_JFT_SIZE
;
138 DestPsp
->HandleTablePtr
= MAKELONG(0x18, DestSegment
);
140 /* Copy the parent handle table without referencing the SFT */
141 RtlCopyMemory(FAR_POINTER(DestPsp
->HandleTablePtr
),
142 FAR_POINTER(SourcePsp
->HandleTablePtr
),
146 VOID
DosCreatePsp(WORD Segment
, WORD ProgramSize
)
148 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Segment
);
149 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
151 RtlZeroMemory(PspBlock
, sizeof(*PspBlock
));
153 /* Set the exit interrupt */
154 PspBlock
->Exit
[0] = 0xCD; // int 0x20
155 PspBlock
->Exit
[1] = 0x20;
157 /* Set the number of the last paragraph */
158 PspBlock
->LastParagraph
= Segment
+ ProgramSize
- 1;
160 /* Save the interrupt vectors */
161 PspBlock
->TerminateAddress
= IntVecTable
[0x22];
162 PspBlock
->BreakAddress
= IntVecTable
[0x23];
163 PspBlock
->CriticalAddress
= IntVecTable
[0x24];
165 /* Set the parent PSP */
166 PspBlock
->ParentPsp
= CurrentPsp
;
168 /* No environment block yet */
169 PspBlock
->EnvBlock
= 0;
171 /* Copy the parent handle table */
172 DosCopyHandleTable(PspBlock
->HandleTable
);
174 /* Set the handle table pointers to the internal handle table */
175 PspBlock
->HandleTableSize
= DEFAULT_JFT_SIZE
;
176 PspBlock
->HandleTablePtr
= MAKELONG(0x18, Segment
);
178 /* Set the DOS version */
179 PspBlock
->DosVersion
= DOS_VERSION
;
181 /* Set the far call opcodes */
182 PspBlock
->FarCall
[0] = 0xCD; // int 0x21
183 PspBlock
->FarCall
[1] = 0x21;
184 PspBlock
->FarCall
[2] = 0xCB; // retf
187 VOID
DosSetProcessContext(WORD Segment
)
189 CurrentPsp
= Segment
;
190 DiskTransferArea
= MAKELONG(0x80, Segment
);
193 DWORD
DosLoadExecutable(IN DOS_EXEC_TYPE LoadType
,
194 IN LPCSTR ExecutablePath
,
195 IN PDOS_EXEC_PARAM_BLOCK Parameters
,
196 IN LPCSTR CommandLine OPTIONAL
,
197 IN LPCSTR Environment OPTIONAL
,
198 IN DWORD ReturnAddress OPTIONAL
)
200 DWORD Result
= ERROR_SUCCESS
;
201 HANDLE FileHandle
= INVALID_HANDLE_VALUE
, FileMapping
= NULL
;
202 LPBYTE Address
= NULL
;
209 /* Buffer for command line conversion: 1 byte for size; 127 bytes for contents */
210 CHAR CmdLineBuffer
[1 + DOS_CMDLINE_LENGTH
];
212 DPRINT1("DosLoadExecutable(%d, %s, 0x%08X, 0x%08X)\n",
218 /* Open a handle to the executable */
219 FileHandle
= CreateFileA(ExecutablePath
,
224 FILE_ATTRIBUTE_NORMAL
,
226 if (FileHandle
== INVALID_HANDLE_VALUE
)
228 Result
= GetLastError();
232 /* Get the file size */
233 FileSize
= GetFileSize(FileHandle
, NULL
);
235 /* Create a mapping object for the file */
236 FileMapping
= CreateFileMapping(FileHandle
,
242 if (FileMapping
== NULL
)
244 Result
= GetLastError();
248 /* Map the file into memory */
249 Address
= (LPBYTE
)MapViewOfFile(FileMapping
, FILE_MAP_READ
, 0, 0, 0);
252 Result
= GetLastError();
256 if (LoadType
!= DOS_LOAD_OVERLAY
)
258 /* If an optional Win32 command line is given... */
261 /* ... convert it into DOS format */
264 PBYTE CmdLineSize
= (PBYTE
)CmdLineBuffer
;
265 LPSTR CmdLineStart
= CmdLineBuffer
+ 1;
266 LPSTR CmdLinePtr
= CmdLineStart
;
268 // For debugging purposes
269 RtlFillMemory(CmdLineBuffer
, sizeof(CmdLineBuffer
), 0xFF);
272 * Set the command line: it is either an empty command line or has
273 * the format: " foo bar ..." (with at least one leading whitespace),
274 * and is then always followed by '\r' (and optionally by '\n').
276 CmdLineLen
= (BYTE
)strlen(CommandLine
);
280 * Add the leading space if the command line is not empty
281 * and doesn't already start with some whitespace...
283 if (*CommandLine
&& *CommandLine
!= '\r' && *CommandLine
!= '\n' &&
284 *CommandLine
!= ' ' && *CommandLine
!= '\t')
290 /* Compute the number of characters we need to copy from the original command line */
291 CmdLineLen
= min(CmdLineLen
, DOS_CMDLINE_LENGTH
- *CmdLineSize
);
293 /* The trailing '\r' or '\n' do not count in the PSP command line size parameter */
294 while (CmdLineLen
&& (CommandLine
[CmdLineLen
- 1] == '\r' || CommandLine
[CmdLineLen
- 1] == '\n'))
299 /* Finally, set everything up */
300 *CmdLineSize
+= CmdLineLen
;
301 RtlCopyMemory(CmdLinePtr
, CommandLine
, CmdLineLen
);
302 CmdLineStart
[*CmdLineSize
] = '\r';
304 /* Finally make the pointer point to the static buffer */
305 CommandLine
= CmdLineBuffer
;
310 * ... otherwise, get the one from the parameter block.
311 * Format of the command line: 1 byte for size; 127 bytes for contents.
314 CommandLine
= (LPCSTR
)FAR_POINTER(Parameters
->CommandLine
);
317 /* If no optional environment is given... */
318 if (Environment
== NULL
)
320 /* ... get the one from the parameter block */
322 Environment
= (LPCSTR
)SEG_OFF_TO_PTR(Parameters
->Environment
, 0);
325 /* Copy the environment block to DOS memory */
326 EnvBlock
= DosCopyEnvironmentBlock(Environment
, ExecutablePath
);
329 Result
= ERROR_NOT_ENOUGH_MEMORY
;
334 /* Check if this is an EXE file or a COM file */
335 if (Address
[0] == 'M' && Address
[1] == 'Z')
338 PIMAGE_DOS_HEADER Header
;
340 PDWORD RelocationTable
;
343 BOOLEAN LoadHigh
= FALSE
;
345 /* Get the MZ header */
346 Header
= (PIMAGE_DOS_HEADER
)Address
;
348 /* Get the base size of the file, in paragraphs (rounded up) */
349 BaseSize
= (((Header
->e_cp
- 1) * 512) + Header
->e_cblp
+ 0x0F) >> 4;
351 if (LoadType
!= DOS_LOAD_OVERLAY
)
353 DWORD TotalSize
= BaseSize
;
355 /* Add the PSP size, in paragraphs */
356 TotalSize
+= sizeof(DOS_PSP
) >> 4;
358 /* Add the maximum size that should be allocated */
359 TotalSize
+= Header
->e_maxalloc
;
361 if (Header
->e_minalloc
== 0 && Header
->e_maxalloc
== 0)
363 /* This program should be loaded high */
368 /* Make sure it does not pass 0xFFFF */
369 if (TotalSize
> 0xFFFF) TotalSize
= 0xFFFF;
371 /* Try to allocate that much memory */
372 Segment
= DosAllocateMemory((WORD
)TotalSize
, &MaxAllocSize
);
376 /* Check if there's at least enough memory for the minimum size */
377 if (MaxAllocSize
< (BaseSize
+ (sizeof(DOS_PSP
) >> 4) + Header
->e_minalloc
))
379 Result
= DosLastError
;
383 /* Allocate that minimum amount */
384 TotalSize
= MaxAllocSize
;
385 Segment
= DosAllocateMemory((WORD
)TotalSize
, NULL
);
386 ASSERT(Segment
!= 0);
389 /* The process owns its own memory */
390 DosChangeMemoryOwner(Segment
, Segment
);
391 DosChangeMemoryOwner(EnvBlock
, Segment
);
393 /* Set INT 22h to the return address */
394 ((PULONG
)BaseAddress
)[0x22] = ReturnAddress
;
397 DosCreatePsp(Segment
, (WORD
)TotalSize
);
398 DosSetPspCommandLine(Segment
, CommandLine
);
399 SEGMENT_TO_PSP(Segment
)->EnvBlock
= EnvBlock
;
401 /* Calculate the segment where the program should be loaded */
402 if (!LoadHigh
) LoadSegment
= Segment
+ (sizeof(DOS_PSP
) >> 4);
403 else LoadSegment
= Segment
+ TotalSize
- BaseSize
;
405 RelocFactor
= LoadSegment
;
410 LoadSegment
= Parameters
->Overlay
.Segment
;
411 RelocFactor
= Parameters
->Overlay
.RelocationFactor
;
414 /* Copy the program to the code segment */
415 RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment
, 0),
416 Address
+ (Header
->e_cparhdr
<< 4),
417 min(FileSize
- (Header
->e_cparhdr
<< 4), BaseSize
<< 4));
419 /* Get the relocation table */
420 RelocationTable
= (PDWORD
)(Address
+ Header
->e_lfarlc
);
422 /* Perform relocations */
423 for (i
= 0; i
< Header
->e_crlc
; i
++)
425 /* Get a pointer to the word that needs to be patched */
426 RelocWord
= (PWORD
)SEG_OFF_TO_PTR(LoadSegment
+ HIWORD(RelocationTable
[i
]),
427 LOWORD(RelocationTable
[i
]));
429 /* Add the relocation factor to it */
430 *RelocWord
+= RelocFactor
;
433 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
435 /* Set the initial segment registers */
439 /* Set the stack to the location from the header */
440 setSS(LoadSegment
+ Header
->e_ss
);
444 DosSetProcessContext(Segment
);
445 CpuExecute(LoadSegment
+ Header
->e_cs
, Header
->e_ip
);
447 else if (LoadType
== DOS_LOAD_ONLY
)
450 Parameters
->StackLocation
= MAKELONG(Header
->e_sp
, LoadSegment
+ Header
->e_ss
);
451 Parameters
->EntryPoint
= MAKELONG(Header
->e_ip
, LoadSegment
+ Header
->e_cs
);
458 if (LoadType
!= DOS_LOAD_OVERLAY
)
460 /* Find the maximum amount of memory that can be allocated */
461 DosAllocateMemory(0xFFFF, &MaxAllocSize
);
463 /* Make sure it's enough for the whole program and the PSP */
464 if (((DWORD
)MaxAllocSize
<< 4) < (FileSize
+ sizeof(DOS_PSP
)))
466 Result
= ERROR_NOT_ENOUGH_MEMORY
;
470 /* Allocate all of it */
471 Segment
= DosAllocateMemory(MaxAllocSize
, NULL
);
474 Result
= DosLastError
;
478 /* The process owns its own memory */
479 DosChangeMemoryOwner(Segment
, Segment
);
480 DosChangeMemoryOwner(EnvBlock
, Segment
);
482 /* Set INT 22h to the return address */
483 ((PULONG
)BaseAddress
)[0x22] = ReturnAddress
;
486 DosCreatePsp(Segment
, MaxAllocSize
);
487 DosSetPspCommandLine(Segment
, CommandLine
);
488 SEGMENT_TO_PSP(Segment
)->EnvBlock
= EnvBlock
;
490 /* Calculate the segment where the program should be loaded */
491 LoadSegment
= Segment
+ (sizeof(DOS_PSP
) >> 4);
496 LoadSegment
= Parameters
->Overlay
.Segment
;
499 /* Copy the program to the code segment */
500 RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment
, 0),
504 if (LoadType
== DOS_LOAD_AND_EXECUTE
)
506 /* Set the initial segment registers */
510 /* Set the stack to the last word of the segment */
515 * Set the value on the stack to 0, so that a near return
516 * jumps to PSP:0000 which has the exit code.
518 *((LPWORD
)SEG_OFF_TO_PTR(Segment
, 0xFFFE)) = 0;
521 DosSetProcessContext(Segment
);
522 CpuExecute(Segment
, 0x100);
524 else if (LoadType
== DOS_LOAD_ONLY
)
527 Parameters
->StackLocation
= MAKELONG(0xFFFE, Segment
);
528 Parameters
->EntryPoint
= MAKELONG(0x0100, Segment
);
533 if (Result
!= ERROR_SUCCESS
)
535 /* It was not successful, cleanup the DOS memory */
536 if (EnvBlock
) DosFreeMemory(EnvBlock
);
537 if (Segment
) DosFreeMemory(Segment
);
541 if (Address
!= NULL
) UnmapViewOfFile(Address
);
543 /* Close the file mapping object */
544 if (FileMapping
!= NULL
) CloseHandle(FileMapping
);
546 /* Close the file handle */
547 if (FileHandle
!= INVALID_HANDLE_VALUE
) CloseHandle(FileHandle
);
552 DWORD
DosStartProcess(IN LPCSTR ExecutablePath
,
553 IN LPCSTR CommandLine
,
554 IN LPCSTR Environment OPTIONAL
)
557 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
559 Result
= DosLoadExecutable(DOS_LOAD_AND_EXECUTE
,
566 if (Result
!= ERROR_SUCCESS
) goto Quit
;
568 /* Attach to the console */
570 VidBiosAttachToConsole();
572 // HACK: Simulate a ENTER key release scancode on the PS/2 port because
573 // some apps expect to read a key release scancode (> 0x80) when they
575 // (hbelusca 2 May 2015: I'm not sure it's really useful. See r65012)
576 // IOWriteB(PS2_CONTROL_PORT, 0xD2); // Next write is for the first PS/2 port
577 // IOWriteB(PS2_DATA_PORT, 0x80 | 0x1C); // ENTER key release
579 /* Start simulation */
580 SetEvent(VdmTaskEvent
);
583 /* Detach from the console */
584 VidBiosDetachFromConsole();
592 WORD
DosCreateProcess(LPCSTR ProgramName
,
593 PDOS_EXEC_PARAM_BLOCK Parameters
,
598 LPVOID Environment
= NULL
;
599 VDM_COMMAND_INFO CommandInfo
;
600 CHAR CmdLine
[MAX_PATH
]; // DOS_CMDLINE_LENGTH + 1
601 CHAR AppName
[MAX_PATH
];
602 CHAR PifFile
[MAX_PATH
];
603 CHAR Desktop
[MAX_PATH
];
604 CHAR Title
[MAX_PATH
];
609 STARTUPINFOA StartupInfo
;
610 PROCESS_INFORMATION ProcessInfo
;
612 /* Get the binary type */
613 if (!GetBinaryTypeA(ProgramName
, &BinaryType
)) return GetLastError();
615 /* Initialize Win32-VDM environment */
616 Env
= RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, EnvSize
);
617 if (Env
== NULL
) return GetLastError();
619 /* Did the caller specify an environment segment? */
620 if (Parameters
->Environment
)
622 /* Yes, use it instead of the parent one */
623 Environment
= SEG_OFF_TO_PTR(Parameters
->Environment
, 0);
626 /* Set up the startup info structure */
627 RtlZeroMemory(&StartupInfo
, sizeof(StartupInfo
));
628 StartupInfo
.cb
= sizeof(StartupInfo
);
631 * Convert the DOS command line to Win32-compatible format.
632 * Format of the DOS command line: 1 byte for size; 127 bytes for contents.
634 CmdLineSize
= min(*(PBYTE
)FAR_POINTER(Parameters
->CommandLine
), DOS_CMDLINE_LENGTH
);
635 RtlCopyMemory(CmdLine
,
636 FAR_POINTER(Parameters
->CommandLine
) + 1,
638 /* NULL-terminate it */
639 CmdLine
[CmdLineSize
] = '\0';
641 /* Remove any trailing return carriage character and NULL-terminate the command line */
642 CmdLinePtr
= CmdLine
;
643 while (*CmdLinePtr
&& *CmdLinePtr
!= '\r' && *CmdLinePtr
!= '\n') CmdLinePtr
++;
646 /* Create the process */
647 if (!CreateProcessA(ProgramName
,
658 RtlFreeHeap(RtlGetProcessHeap(), 0, Env
);
659 return GetLastError();
662 /* Check the type of the program */
665 /* These are handled by NTVDM */
669 /* Clear the structure */
670 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
672 /* Initialize the structure members */
673 CommandInfo
.TaskId
= SessionId
;
674 CommandInfo
.VDMState
= VDM_FLAG_NESTED_TASK
| VDM_FLAG_DONT_WAIT
;
675 CommandInfo
.CmdLine
= CmdLine
;
676 CommandInfo
.CmdLen
= sizeof(CmdLine
);
677 CommandInfo
.AppName
= AppName
;
678 CommandInfo
.AppLen
= sizeof(AppName
);
679 CommandInfo
.PifFile
= PifFile
;
680 CommandInfo
.PifLen
= sizeof(PifFile
);
681 CommandInfo
.Desktop
= Desktop
;
682 CommandInfo
.DesktopLen
= sizeof(Desktop
);
683 CommandInfo
.Title
= Title
;
684 CommandInfo
.TitleLen
= sizeof(Title
);
685 CommandInfo
.Env
= Env
;
686 CommandInfo
.EnvLen
= EnvSize
;
689 /* Get the VDM command information */
690 if (!GetNextVDMCommand(&CommandInfo
))
692 if (CommandInfo
.EnvLen
> EnvSize
)
694 /* Expand the environment size */
695 EnvSize
= CommandInfo
.EnvLen
;
696 CommandInfo
.Env
= Env
= RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, Env
, EnvSize
);
698 /* Repeat the request */
699 CommandInfo
.VDMState
|= VDM_FLAG_RETRY
;
703 /* Shouldn't happen */
707 /* Load the executable */
708 Result
= DosLoadExecutable(DOS_LOAD_AND_EXECUTE
,
714 if (Result
== ERROR_SUCCESS
)
716 /* Increment the re-entry count */
717 CommandInfo
.VDMState
= VDM_INC_REENTER_COUNT
;
718 GetNextVDMCommand(&CommandInfo
);
722 DisplayMessage(L
"Could not load '%S'. Error: %u", AppName
, Result
);
728 /* Not handled by NTVDM */
731 /* Wait for the process to finish executing */
732 WaitForSingleObject(ProcessInfo
.hProcess
, INFINITE
);
736 RtlFreeHeap(RtlGetProcessHeap(), 0, Env
);
738 /* Close the handles */
739 CloseHandle(ProcessInfo
.hProcess
);
740 CloseHandle(ProcessInfo
.hThread
);
742 return ERROR_SUCCESS
;
746 VOID
DosTerminateProcess(WORD Psp
, BYTE ReturnCode
, WORD KeepResident
)
749 WORD McbSegment
= FIRST_MCB_SEGMENT
;
751 LPDWORD IntVecTable
= (LPDWORD
)((ULONG_PTR
)BaseAddress
);
752 PDOS_PSP PspBlock
= SEGMENT_TO_PSP(Psp
);
754 DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
759 /* Check if this PSP is it's own parent */
760 if (PspBlock
->ParentPsp
== Psp
) goto Done
;
762 if (KeepResident
== 0)
764 for (i
= 0; i
< PspBlock
->HandleTableSize
; i
++)
766 /* Close the handle */
771 /* Free the memory used by the process */
774 /* Get a pointer to the MCB */
775 CurrentMcb
= SEGMENT_TO_MCB(McbSegment
);
777 /* Make sure the MCB is valid */
778 if (CurrentMcb
->BlockType
!= 'M' && CurrentMcb
->BlockType
!= 'Z') break;
780 /* Check if this block was allocated by the process */
781 if (CurrentMcb
->OwnerPsp
== Psp
)
783 if (KeepResident
== 0)
785 /* Free this entire block */
786 DosFreeMemory(McbSegment
+ 1);
788 else if (KeepResident
< CurrentMcb
->Size
)
790 /* Reduce the size of the block */
791 DosResizeMemory(McbSegment
+ 1, KeepResident
, NULL
);
793 /* No further paragraphs need to stay resident */
798 /* Just reduce the amount of paragraphs we need to keep resident */
799 KeepResident
-= CurrentMcb
->Size
;
803 /* If this was the last block, quit */
804 if (CurrentMcb
->BlockType
== 'Z') break;
806 /* Update the segment and continue */
807 McbSegment
+= CurrentMcb
->Size
+ 1;
811 /* Restore the interrupt vectors */
812 IntVecTable
[0x22] = PspBlock
->TerminateAddress
;
813 IntVecTable
[0x23] = PspBlock
->BreakAddress
;
814 IntVecTable
[0x24] = PspBlock
->CriticalAddress
;
816 /* Update the current PSP */
817 if (Psp
== CurrentPsp
)
819 CurrentPsp
= PspBlock
->ParentPsp
;
820 if (CurrentPsp
== SYSTEM_PSP
)
822 ResetEvent(VdmTaskEvent
);
828 // FIXME: This is probably not the best way to do it
829 /* Check if this was a nested DOS task */
830 if (CurrentPsp
!= SYSTEM_PSP
)
832 VDM_COMMAND_INFO CommandInfo
;
834 /* Decrement the re-entry count */
835 CommandInfo
.TaskId
= SessionId
;
836 CommandInfo
.VDMState
= VDM_DEC_REENTER_COUNT
;
837 GetNextVDMCommand(&CommandInfo
);
839 /* Clear the structure */
840 RtlZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
842 /* Update the VDM state of the task */
843 CommandInfo
.TaskId
= SessionId
;
844 CommandInfo
.VDMState
= VDM_FLAG_DONT_WAIT
;
845 GetNextVDMCommand(&CommandInfo
);
849 /* Save the return code - Normal termination */
850 DosErrorLevel
= MAKEWORD(ReturnCode
, 0x00);
852 /* Return control to the parent process */
853 CpuExecute(HIWORD(PspBlock
->TerminateAddress
),
854 LOWORD(PspBlock
->TerminateAddress
));