ReactOS  0.4.11-dev-195-gef016bf
process.c
Go to the documentation of this file.
1 /*
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)
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include "ntvdm.h"
13 
14 #define NDEBUG
15 #include <debug.h>
16 
17 #include "emulator.h"
18 #include "cpu/cpu.h"
19 
20 #include "dos.h"
21 #include "dos/dem.h"
22 #include "dosfiles.h"
23 #include "handle.h"
24 #include "process.h"
25 #include "memory.h"
26 
27 #include "bios/bios.h"
28 
29 #include "io.h"
30 #include "hardware/ps2.h"
31 
32 #include "vddsup.h"
33 
34 /* PRIVATE FUNCTIONS **********************************************************/
35 
37  IN WORD EnvBlock,
38  IN LPCSTR CommandLine,
39  IN LPCSTR ProgramName)
40 {
41  PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
42  PDOS_MCB Mcb = SEGMENT_TO_MCB(Segment - 1);
43  LPCSTR PspName;
44  USHORT i;
45 
46  /* Link the environment block */
47  PspBlock->EnvBlock = EnvBlock;
48 
49  /*
50  * Copy the command line.
51  * Format of the CommandLine parameter: 1 byte for size; 127 bytes for contents.
52  */
53  PspBlock->CommandLineSize = min(*(PBYTE)CommandLine, DOS_CMDLINE_LENGTH);
54  CommandLine++;
55  RtlCopyMemory(PspBlock->CommandLine, CommandLine, DOS_CMDLINE_LENGTH);
56 
57  /*
58  * Initialize the owner name of the MCB of the PSP.
59  */
60 
61  /* Find the start of the file name, skipping all the path elements */
62  PspName = ProgramName;
63  while (*ProgramName)
64  {
65  switch (*ProgramName++)
66  {
67  /* Path delimiter, skip it */
68  case ':': case '\\': case '/':
69  PspName = ProgramName;
70  break;
71  }
72  }
73  /* Copy the file name up to the extension... */
74  for (i = 0; i < sizeof(Mcb->Name) && PspName[i] != '.' && PspName[i] != '\0'; ++i)
75  {
76  Mcb->Name[i] = RtlUpperChar(PspName[i]);
77  }
78  /* ... and NULL-terminate if needed */
79  if (i < sizeof(Mcb->Name)) Mcb->Name[i] = '\0';
80 
81  // FIXME: Initialize the FCBs
82 }
83 
84 static inline VOID DosSaveState(VOID)
85 {
87  WORD StackPointer = getSP();
88 
89  DPRINT1("\n"
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"
94  "\n",
95  getSS(), getSP(),
96  getDS(), getES(), getAX(), getCX(),
97  getDX(), getBX(), getBP(), getSI(), getDI());
98 
99  /*
100  * Allocate stack space for the registers. Note that we
101  * already have one word allocated (the interrupt number).
102  */
103  StackPointer -= sizeof(DOS_REGISTER_STATE) - sizeof(WORD);
104  State = SEG_OFF_TO_PTR(getSS(), StackPointer);
105  setSP(StackPointer);
106 
107  /* Save */
108  State->DS = getDS();
109  State->ES = getES();
110  State->AX = getAX();
111  State->CX = getCX();
112  State->DX = getDX();
113  State->BX = getBX();
114  State->BP = getBP();
115  State->SI = getSI();
116  State->DI = getDI();
117 
118  DPRINT1("\n"
119  "DosSaveState(after) -- SS:SP == %04X:%04X\n"
120  "Saved State =\n"
121  "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
122  "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
123  "\n",
124  getSS(), getSP(),
125  State->DS, State->ES, State->AX, State->CX,
126  State->DX, State->BX, State->BP, State->SI, State->DI);
127 }
128 
129 static inline VOID DosRestoreState(VOID)
130 {
132 
133  /*
134  * Pop the state structure from the stack. Note that we
135  * already have one word allocated (the interrupt number).
136  */
137  State = SEG_OFF_TO_PTR(getSS(), getSP());
138 
139  DPRINT1("\n"
140  "DosRestoreState(before) -- SS:SP == %04X:%04X\n"
141  "Saved State =\n"
142  "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
143  "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
144  "\n",
145  getSS(), getSP(),
146  State->DS, State->ES, State->AX, State->CX,
147  State->DX, State->BX, State->BP, State->SI, State->DI);
148 
149  setSP(getSP() + sizeof(DOS_REGISTER_STATE) - sizeof(WORD));
150 
151  /* Restore */
152  setDS(State->DS);
153  setES(State->ES);
154  setAX(State->AX);
155  setCX(State->CX);
156  setDX(State->DX);
157  setBX(State->BX);
158  setBP(State->BP);
159  setSI(State->SI);
160  setDI(State->DI);
161 
162  DPRINT1("\n"
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"
167  "\n",
168  getSS(), getSP(),
169  getDS(), getES(), getAX(), getCX(),
170  getDX(), getBX(), getBP(), getSI(), getDI());
171 }
172 
174  IN LPCSTR ProgramName)
175 {
176  PCHAR Ptr, DestBuffer = NULL;
177  SIZE_T TotalSize = 0;
178  WORD DestSegment;
179 
180  /* If we have an environment strings list, compute its size */
181  if (Environment)
182  {
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;
187  }
188  else
189  {
190  /* Empty environment string */
191  TotalSize = 1;
192  }
193  /* Add the final environment block NULL-terminator */
194  TotalSize++;
195 
196  /* Add the two bytes for the program name tag */
197  TotalSize += 2;
198 
199  /* Add the string buffer size */
200  TotalSize += strlen(ProgramName) + 1;
201 
202  /* Allocate the memory for the environment block */
203  DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
204  if (!DestSegment) return 0;
205 
206  DestBuffer = (PCHAR)SEG_OFF_TO_PTR(DestSegment, 0);
207 
208  /* If we have an environment strings list, copy it */
209  if (Environment)
210  {
211  Ptr = (PCHAR)Environment;
212  while (*Ptr)
213  {
214  /* Copy the string and NULL-terminate it */
215  strcpy(DestBuffer, Ptr);
216  DestBuffer += strlen(Ptr);
217  *(DestBuffer++) = '\0';
218 
219  /* Move to the next string */
220  Ptr += strlen(Ptr) + 1;
221  }
222  }
223  else
224  {
225  /* Empty environment string */
226  *(DestBuffer++) = '\0';
227  }
228  /* NULL-terminate the environment block */
229  *(DestBuffer++) = '\0';
230 
231  /* Store the special program name tag */
232  *(DestBuffer++) = LOBYTE(DOS_PROGRAM_NAME_TAG);
233  *(DestBuffer++) = HIBYTE(DOS_PROGRAM_NAME_TAG);
234 
235  /* Copy the program name after the environment block */
236  strcpy(DestBuffer, ProgramName);
237 
238  return DestSegment;
239 }
240 
241 /* PUBLIC FUNCTIONS ***********************************************************/
242 
243 VOID DosClonePsp(WORD DestSegment, WORD SourceSegment)
244 {
245  PDOS_PSP DestPsp = SEGMENT_TO_PSP(DestSegment);
246  PDOS_PSP SourcePsp = SEGMENT_TO_PSP(SourceSegment);
247  LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
248 
249  /* Literally copy the PSP first */
250  RtlCopyMemory(DestPsp, SourcePsp, sizeof(*DestPsp));
251 
252  /* Save the interrupt vectors */
253  DestPsp->TerminateAddress = IntVecTable[0x22];
254  DestPsp->BreakAddress = IntVecTable[0x23];
255  DestPsp->CriticalAddress = IntVecTable[0x24];
256 
257  /* No parent PSP */
258  DestPsp->ParentPsp = 0;
259 
260  /* Set the handle table pointers to the internal handle table */
262  DestPsp->HandleTablePtr = MAKELONG(0x18, DestSegment);
263 
264  /* Copy the parent handle table without referencing the SFT */
266  FAR_POINTER(SourcePsp->HandleTablePtr),
268 }
269 
271 {
272  PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
273  LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
274 
275  RtlZeroMemory(PspBlock, sizeof(*PspBlock));
276 
277  /* Set the exit interrupt */
278  PspBlock->Exit[0] = 0xCD; // int 0x20
279  PspBlock->Exit[1] = 0x20;
280 
281  /* Set the number of the last paragraph */
282  PspBlock->LastParagraph = Segment + ProgramSize;
283 
284  /* Save the interrupt vectors */
285  PspBlock->TerminateAddress = IntVecTable[0x22];
286  PspBlock->BreakAddress = IntVecTable[0x23];
287  PspBlock->CriticalAddress = IntVecTable[0x24];
288 
289  /* Set the parent PSP */
290  PspBlock->ParentPsp = Sda->CurrentPsp;
291 
292  if (Sda->CurrentPsp != SYSTEM_PSP)
293  {
294  /* Link to the parent's environment block */
295  PspBlock->EnvBlock = SEGMENT_TO_PSP(Sda->CurrentPsp)->EnvBlock;
296  }
297 /*
298  else
299  {
300  PspBlock->EnvBlock = SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0);
301  }
302 */
303 
304  /* Copy the parent handle table */
305  DosCopyHandleTable(PspBlock->HandleTable);
306 
307  /* Set the handle table pointers to the internal handle table */
308  PspBlock->HandleTableSize = DEFAULT_JFT_SIZE;
309  PspBlock->HandleTablePtr = MAKELONG(0x18, Segment);
310 
311  /* Set the DOS version */
312  // FIXME: This is here that SETVER stuff enters into action!
313  PspBlock->DosVersion = DosData->DosVersion;
314 
315  /* Set the far call opcodes */
316  PspBlock->FarCall[0] = 0xCD; // int 0x21
317  PspBlock->FarCall[1] = 0x21;
318  PspBlock->FarCall[2] = 0xCB; // retf
319 }
320 
322 {
324  Sda->DiskTransferArea = MAKELONG(0x80, Segment);
325 }
326 
328  IN LPBYTE ExeBuffer,
329  IN DWORD ExeBufferSize,
330  IN LPCSTR ExePath,
332  IN LPCSTR CommandLine OPTIONAL,
333  IN LPCSTR Environment OPTIONAL,
334  IN DWORD ReturnAddress OPTIONAL)
335 {
337  WORD Segment = 0;
338  WORD EnvBlock = 0;
339  WORD ExeSignature;
340  WORD LoadSegment;
341  WORD MaxAllocSize;
342 
343  WORD FinalSS, FinalSP;
344  WORD FinalCS, FinalIP;
345 
346  /* Buffer for command line conversion: 1 byte for size; 127 bytes for contents */
347  CHAR CmdLineBuffer[1 + DOS_CMDLINE_LENGTH];
348 
349  DPRINT1("DosLoadExecutableInternal(%d, 0x%p, '%s', 0x%p, 0x%p, 0x%p)\n",
350  LoadType, ExeBuffer, ExePath, Parameters, CommandLine, Environment);
351 
352  if (LoadType != DOS_LOAD_OVERLAY)
353  {
354  /* If an optional Win32 command line is given... */
355  if (CommandLine)
356  {
357  /* ... convert it into DOS format */
358  BYTE CmdLineLen;
359 
360  PBYTE CmdLineSize = (PBYTE)CmdLineBuffer;
361  LPSTR CmdLineStart = CmdLineBuffer + 1;
362  LPSTR CmdLinePtr = CmdLineStart;
363 
364  // For debugging purposes
365  RtlFillMemory(CmdLineBuffer, sizeof(CmdLineBuffer), 0xFF);
366 
367  /*
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').
371  */
372  CmdLineLen = (BYTE)strlen(CommandLine);
373  *CmdLineSize = 0;
374 
375  /*
376  * Add the leading space if the command line is not empty
377  * and doesn't already start with some whitespace...
378  */
379  if (*CommandLine && *CommandLine != '\r' && *CommandLine != '\n' &&
380  *CommandLine != ' ' && *CommandLine != '\t')
381  {
382  (*CmdLineSize)++;
383  *CmdLinePtr++ = ' ';
384  }
385 
386  /* Compute the number of characters we need to copy from the original command line */
387  CmdLineLen = min(CmdLineLen, DOS_CMDLINE_LENGTH - *CmdLineSize);
388 
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'))
391  {
392  CmdLineLen--;
393  }
394 
395  /* Finally, set everything up */
396  *CmdLineSize += CmdLineLen;
397  RtlCopyMemory(CmdLinePtr, CommandLine, CmdLineLen);
398  CmdLineStart[*CmdLineSize] = '\r';
399 
400  /* Finally make the pointer point to the static buffer */
401  CommandLine = CmdLineBuffer;
402  }
403  else
404  {
405  /*
406  * ... otherwise, get the one from the parameter block.
407  * Format of the command line: 1 byte for size; 127 bytes for contents.
408  */
409  ASSERT(Parameters);
410  CommandLine = (LPCSTR)FAR_POINTER(Parameters->CommandLine);
411  }
412 
413  /* If no optional environment is given... */
414  if (Environment == NULL)
415  {
416  ASSERT(Parameters);
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) */
421  else
423  }
424 
425  /* Copy the environment block to DOS memory */
426  EnvBlock = DosCopyEnvironmentBlock(Environment, ExePath);
427  if (EnvBlock == 0)
428  {
429  Result = Sda->LastErrorCode;
430  goto Cleanup;
431  }
432  }
433 
434  /*
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)
439  */
440  ExeSignature = *(PWORD)ExeBuffer;
441  if (ExeSignature == 'MZ' || ExeSignature == 'ZM')
442  {
443  /* EXE file */
445  DWORD BaseSize;
446  PDWORD RelocationTable;
447  PWORD RelocWord;
448  WORD RelocFactor;
449  WORD i;
450 
451  /* Get the MZ header */
452  Header = (PIMAGE_DOS_HEADER)ExeBuffer;
453 
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)
457  - Header->e_cparhdr;
458 #else
459  // e_cp is the number of 512-byte blocks. 512 == (1 << 9)
460  // so this corresponds to (1 << 5) number of paragraphs.
461  //
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.
465 
466  BaseSize = ((Header->e_cp << 5) - Header->e_cparhdr) & 0xFFFF;
467 #endif
468 
469  if (LoadType != DOS_LOAD_OVERLAY)
470  {
471  BOOLEAN LoadHigh = FALSE;
472  DWORD TotalSize;
473 
474  /* Find the maximum amount of memory that can be allocated */
475  DosAllocateMemory(0xFFFF, &MaxAllocSize);
476 
477  /* Compute the total needed size, in paragraphs */
478  TotalSize = BaseSize + (sizeof(DOS_PSP) >> 4);
479 
480  /* We must have the required minimum amount of memory. If not, bail out. */
481  if (MaxAllocSize < TotalSize + Header->e_minalloc)
482  {
483  Result = ERROR_NOT_ENOUGH_MEMORY;
484  goto Cleanup;
485  }
486 
487  /* Check if the program should be loaded high */
488  if (Header->e_minalloc == 0 && Header->e_maxalloc == 0)
489  {
490  /* Yes it should. Use all the available memory. */
491  LoadHigh = TRUE;
492  TotalSize = MaxAllocSize;
493  }
494  else
495  {
496  /* Compute the maximum memory size that can be allocated */
497  if (Header->e_maxalloc != 0)
498  TotalSize = min(TotalSize + Header->e_maxalloc, MaxAllocSize);
499  else
500  TotalSize = MaxAllocSize; // Use all the available memory
501  }
502 
503  /* Try to allocate that much memory */
504  Segment = DosAllocateMemory((WORD)TotalSize, NULL);
505  if (Segment == 0)
506  {
507  Result = Sda->LastErrorCode;
508  goto Cleanup;
509  }
510 
511  /* The process owns its memory */
512  DosChangeMemoryOwner(Segment , Segment);
513  DosChangeMemoryOwner(EnvBlock, Segment);
514 
515  /* Set INT 22h to the return address */
516  ((PULONG)BaseAddress)[0x22] = ReturnAddress;
517 
518  /* Create the PSP and initialize it */
519  DosCreatePsp(Segment, (WORD)TotalSize);
520  DosInitPsp(Segment, EnvBlock, CommandLine, ExePath);
521 
522  /* Calculate the segment where the program should be loaded */
523  if (!LoadHigh)
524  LoadSegment = Segment + (sizeof(DOS_PSP) >> 4);
525  else
526  LoadSegment = Segment + TotalSize - BaseSize;
527 
528  RelocFactor = LoadSegment;
529  }
530  else
531  {
532  ASSERT(Parameters);
533  LoadSegment = Parameters->Overlay.Segment;
534  RelocFactor = Parameters->Overlay.RelocationFactor;
535  }
536 
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));
541 
542  /* Get the relocation table */
543  RelocationTable = (PDWORD)(ExeBuffer + Header->e_lfarlc);
544 
545  /* Perform relocations */
546  for (i = 0; i < Header->e_crlc; i++)
547  {
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]));
551 
552  /* Add the relocation factor to it */
553  *RelocWord += RelocFactor;
554  }
555 
556  /* Set the stack to the location from the header */
557  FinalSS = LoadSegment + Header->e_ss;
558  FinalSP = Header->e_sp;
559 
560  /* Set the code segment/pointer */
561  FinalCS = LoadSegment + Header->e_cs;
562  FinalIP = Header->e_ip;
563  }
564  else
565  {
566  /* COM file */
567 
568  if (LoadType != DOS_LOAD_OVERLAY)
569  {
570  /* Find the maximum amount of memory that can be allocated */
571  DosAllocateMemory(0xFFFF, &MaxAllocSize);
572 
573  /* Make sure it's enough for the whole program and the PSP */
574  if (((DWORD)MaxAllocSize << 4) < (ExeBufferSize + sizeof(DOS_PSP)))
575  {
576  Result = ERROR_NOT_ENOUGH_MEMORY;
577  goto Cleanup;
578  }
579 
580  /* Allocate all of it */
581  Segment = DosAllocateMemory(MaxAllocSize, NULL);
582  if (Segment == 0)
583  {
584  Result = Sda->LastErrorCode;
585  goto Cleanup;
586  }
587 
588  /* The process owns its memory */
589  DosChangeMemoryOwner(Segment , Segment);
590  DosChangeMemoryOwner(EnvBlock, Segment);
591 
592  /* Set INT 22h to the return address */
593  ((PULONG)BaseAddress)[0x22] = ReturnAddress;
594 
595  /* Create the PSP and initialize it */
596  DosCreatePsp(Segment, MaxAllocSize);
597  DosInitPsp(Segment, EnvBlock, CommandLine, ExePath);
598 
599  /* Calculate the segment where the program should be loaded */
600  LoadSegment = Segment + (sizeof(DOS_PSP) >> 4);
601  }
602  else
603  {
604  ASSERT(Parameters);
605  LoadSegment = Parameters->Overlay.Segment;
606  }
607 
608  /* Copy the program to the code segment */
609  RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
610  ExeBuffer, ExeBufferSize);
611 
612  /* Set the stack to the last word of the segment */
613  FinalSS = Segment;
614  FinalSP = 0xFFFE;
615 
616  /*
617  * Set the value on the stack to 0x0000, so that a near return
618  * jumps to PSP:0000 which has the exit code.
619  */
620  *((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0x0000;
621 
622  /* Set the code segment/pointer */
623  FinalCS = Segment;
624  FinalIP = 0x0100;
625  }
626 
627  if (LoadType == DOS_LOAD_AND_EXECUTE)
628  {
629  /* Save the program state */
630  if (Sda->CurrentPsp != SYSTEM_PSP)
631  {
632  /* Push the task state */
633  DosSaveState();
634 
635  DPRINT1("Sda->CurrentPsp = 0x%04x; Old LastStack = 0x%08x, New LastStack = 0x%08x\n",
637 
638  /* Update the last stack in the PSP */
639  SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack = MAKELONG(getSP(), getSS());
640  }
641 
642  /* Set the initial segment registers */
643  setDS(Segment);
644  setES(Segment);
645 
646  /* Set the stack */
647  setSS(FinalSS);
648  setSP(FinalSP);
649 
650  /*
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
654  */
655  setDX(Segment);
656  setDI(FinalSP);
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.
658  setSI(FinalIP);
659 
660  setAX(0/*0xFFFF*/); // FIXME: fcbcode
661  setBX(0/*0xFFFF*/); // FIXME: fcbcode
662  setCX(0x00FF);
663 
664  /*
665  * Keep critical flags, clear test flags (OF, SF, ZF, AF, PF, CF)
666  * and explicitely set the interrupt flag.
667  */
668  setEFLAGS((getEFLAGS() & ~0x08D5) | 0x0200);
669 
670  /* Notify VDDs of process execution */
671  VDDCreateUserHook(Segment);
672 
673  /* Execute */
674  DosSetProcessContext(Segment);
675  CpuExecute(FinalCS, FinalIP);
676  }
677  else if (LoadType == DOS_LOAD_ONLY)
678  {
679  ASSERT(Parameters);
680  Parameters->StackLocation = MAKELONG(FinalSP, FinalSS);
681  Parameters->EntryPoint = MAKELONG(FinalIP, FinalCS);
682  }
683 
684 Cleanup:
685  if (Result != ERROR_SUCCESS)
686  {
687  /* It was not successful, cleanup the DOS memory */
688  if (EnvBlock) DosFreeMemory(EnvBlock);
689  if (Segment) DosFreeMemory(Segment);
690  }
691 
692  return Result;
693 }
694 
696  IN LPCSTR ExecutablePath,
698  IN LPCSTR CommandLine OPTIONAL,
699  IN LPCSTR Environment OPTIONAL,
700  IN DWORD ReturnAddress OPTIONAL)
701 {
703  HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
704  DWORD FileSize;
705  LPBYTE Address = NULL;
706  CHAR FullPath[MAX_PATH];
707  CHAR ShortFullPath[MAX_PATH];
708 
709  DPRINT1("DosLoadExecutable(%d, '%s', 0x%p, 0x%p, 0x%p)\n",
710  LoadType, ExecutablePath, Parameters, CommandLine, Environment);
711 
712  /* Try to get the full path to the executable */
713  if (GetFullPathNameA(ExecutablePath, sizeof(FullPath), FullPath, NULL))
714  {
715  /* Get the corresponding short path */
716  if (GetShortPathNameA(FullPath, ShortFullPath, sizeof(ShortFullPath)))
717  {
718  /* Use the shortened full path from now on */
719  ExecutablePath = ShortFullPath;
720  }
721  }
722 
723  /* Open a handle to the executable */
724  FileHandle = CreateFileA(ExecutablePath,
725  GENERIC_READ,
727  NULL,
730  NULL);
731  if (FileHandle == INVALID_HANDLE_VALUE)
732  {
733  Result = GetLastError();
734  goto Cleanup;
735  }
736 
737  /* Get the file size */
738  FileSize = GetFileSize(FileHandle, NULL);
739 
740  /* Create a mapping object for the file */
741  FileMapping = CreateFileMapping(FileHandle,
742  NULL,
744  0,
745  0,
746  NULL);
747  if (FileMapping == NULL)
748  {
749  Result = GetLastError();
750  goto Cleanup;
751  }
752 
753  /* Map the file into memory */
754  Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
755  if (Address == NULL)
756  {
757  Result = GetLastError();
758  goto Cleanup;
759  }
760 
761  Result = DosLoadExecutableInternal(LoadType,
762  Address,
763  FileSize,
764  ExecutablePath,
765  Parameters,
766  CommandLine,
767  Environment,
768  ReturnAddress);
769 
770 Cleanup:
771  /* Unmap the file*/
772  if (Address != NULL) UnmapViewOfFile(Address);
773 
774  /* Close the file mapping object */
775  if (FileMapping != NULL) CloseHandle(FileMapping);
776 
777  /* Close the file handle */
778  if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
779 
780  return Result;
781 }
782 
785  IN DWORD ReturnAddress OPTIONAL)
786 {
788  DWORD BinaryType;
789 
790  /* Get the binary type */
791  if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
792 
793  /* Check the type of the program */
794  switch (BinaryType)
795  {
796  /* Those are handled by NTVDM */
797  case SCS_WOW_BINARY:
798  {
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.",
802  ProgramName);
803  // Fall through
804  }
805  case SCS_DOS_BINARY:
806  {
807  /* Load the executable */
809  ProgramName,
810  Parameters,
811  NULL,
812  NULL,
813  ReturnAddress);
814  if (Result != ERROR_SUCCESS)
815  {
816  DisplayMessage(L"Could not load '%S'. Error: %u", ProgramName, Result);
817  }
818 
819  break;
820  }
821 
822  /* Not handled by NTVDM */
823  default:
824  {
827  LPSTR CmdLinePtr;
828  ULONG CmdLineSize;
829 
830  /* Did the caller specify an environment segment? */
831  if (Parameters->Environment)
832  {
833  /* Yes, use it instead of the parent one */
834  Environment = (LPSTR)SEG_OFF_TO_PTR(Parameters->Environment, 0);
835  }
836 
837  /*
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.
841  */
842  CmdLinePtr = CmdLine;
843  strncpy(CmdLinePtr, ProgramName, MAX_PATH); // Concatenate the program name
844  CmdLinePtr += strlen(CmdLinePtr);
845  *CmdLinePtr++ = ' '; // Add separating space
846 
847  CmdLineSize = min(*(PBYTE)FAR_POINTER(Parameters->CommandLine), DOS_CMDLINE_LENGTH);
848  RtlCopyMemory(CmdLinePtr,
849  (LPSTR)FAR_POINTER(Parameters->CommandLine) + 1,
850  CmdLineSize);
851  /* NULL-terminate it */
852  CmdLinePtr[CmdLineSize] = '\0';
853 
854  /* Remove any trailing return carriage character and NULL-terminate the command line */
855  while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
856  *CmdLinePtr = '\0';
857 
858  Result = DosStartProcess32(ProgramName, CmdLine,
859  Environment, ReturnAddress,
860  TRUE);
861  if (Result != ERROR_SUCCESS)
862  {
863  DisplayMessage(L"Could not load 32-bit '%S'. Error: %u", ProgramName, Result);
864  }
865 
866  break;
867  }
868  }
869 
870  return Result;
871 }
872 
873 VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
874 {
875  WORD McbSegment = SysVars->FirstMcb;
876  PDOS_MCB CurrentMcb;
877  LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
878  PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
879  LPWORD Stack;
880  BYTE TerminationType;
881 
882  DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
883  Psp, ReturnCode, KeepResident);
884 
885  /* Notify VDDs of process termination */
887 
888  /* Check if this PSP is its own parent */
889  if (PspBlock->ParentPsp == Psp) goto Done;
890 
891  if (KeepResident == 0)
892  {
893  WORD i;
894  for (i = 0; i < PspBlock->HandleTableSize; i++)
895  {
896  /* Close the handle */
897  DosCloseHandle(i);
898  }
899  }
900 
901  /* Free the memory used by the process */
902  while (TRUE)
903  {
904  /* Get a pointer to the MCB */
905  CurrentMcb = SEGMENT_TO_MCB(McbSegment);
906 
907  /* Make sure the MCB is valid */
908  if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') break;
909 
910  /* Check if this block was allocated by the process */
911  if (CurrentMcb->OwnerPsp == Psp)
912  {
913  if (KeepResident)
914  {
915  /* Check if this is the PSP block and we should reduce its size */
916  if ((McbSegment + 1) == Psp && KeepResident < CurrentMcb->Size)
917  {
918  /* Reduce the size of the block */
919  DosResizeMemory(McbSegment + 1, KeepResident, NULL);
920  break;
921  }
922  }
923  else
924  {
925  /* Free this entire block */
926  DosFreeMemory(McbSegment + 1);
927  }
928  }
929 
930  /* If this was the last block, quit */
931  if (CurrentMcb->BlockType == 'Z') break;
932 
933  /* Update the segment and continue */
934  McbSegment += CurrentMcb->Size + 1;
935  }
936 
937 Done:
938  /* Restore the interrupt vectors */
939  IntVecTable[0x22] = PspBlock->TerminateAddress;
940  IntVecTable[0x23] = PspBlock->BreakAddress;
941  IntVecTable[0x24] = PspBlock->CriticalAddress;
942 
943  /* Update the current PSP with the parent's one */
944  if (Psp == Sda->CurrentPsp)
945  {
946  DosSetProcessContext(PspBlock->ParentPsp);
947  if (Sda->CurrentPsp == SYSTEM_PSP)
948  {
949  // NOTE: we can also use the DOS BIOS exit code.
950  CpuUnsimulate();
951  return;
952  }
953  }
954 
955  /* Save the return code - Normal termination or TSR */
956  TerminationType = (KeepResident != 0 ? 0x03 : 0x00);
957  Sda->ErrorLevel = MAKEWORD(ReturnCode, TerminationType);
958 
959  DPRINT1("PspBlock->ParentPsp = 0x%04x; Sda->CurrentPsp = 0x%04x\n",
960  PspBlock->ParentPsp, Sda->CurrentPsp);
961 
962  if (Sda->CurrentPsp != SYSTEM_PSP)
963  {
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);
966 
967  /* Restore the parent's stack */
968  setSS(HIWORD(SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack));
969  setSP(LOWORD(SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack));
970 
971  /* Pop the task state */
972  DosRestoreState();
973  }
974 
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);
979 }
980 
981 /* EOF */
WORD LastErrorCode
Definition: dos.h:151
signed char * PCHAR
Definition: retypes.h:7
BOOLEAN DosResizeMemory(WORD BlockData, WORD NewSize, WORD *MaxAvailable)
Definition: memory.c:289
PVOID PVOID PWCHAR PVOID Environment
Definition: env.c:45
char CmdLine[0x100]
Definition: mach.c:35
#define IN
Definition: typedefs.h:38
unsigned short WORD
Definition: ntddk_ex.h:93
USHORT WINAPI getBX(VOID)
Definition: registers.c:170
WORD CurrentPsp
Definition: dos.h:156
#define TRUE
Definition: types.h:120
NTSYSAPI VOID NTAPI RtlCopyMemory(VOID UNALIGNED *Destination, CONST VOID UNALIGNED *Source, ULONG Length)
#define CloseHandle
Definition: compat.h:398
PVOID ULONG Address
Definition: oprghdlr.h:14
WORD DosCreateProcess(IN LPCSTR ProgramName, IN PDOS_EXEC_PARAM_BLOCK Parameters, IN DWORD ReturnAddress OPTIONAL)
Definition: process.c:783
#define ERROR_SUCCESS
Definition: deptool.c:10
#define MapViewOfFile
Definition: compat.h:402
USHORT WINAPI getSI(VOID)
Definition: registers.c:404
#define LOBYTE(W)
Definition: jmemdos.c:487
ACPI_SIZE strlen(const char *String)
Definition: utclib.c:269
#define MAKEWORD(a, b)
Definition: typedefs.h:247
USHORT WINAPI getCX(VOID)
Definition: registers.c:228
struct _DOS_REGISTER_STATE DOS_REGISTER_STATE
ASSERT((InvokeOnSuccess||InvokeOnError||InvokeOnCancel)?(CompletionRoutine!=NULL):TRUE)
DOS_EXEC_TYPE
Definition: process.h:18
static SIZE_T FileSize
Definition: cabinet.c:52
static VOID DosRestoreState(VOID)
Definition: process.c:129
char * strncpy(char *DstString, const char *SrcString, ACPI_SIZE Count)
Definition: utclib.c:427
VOID WINAPI setAX(USHORT)
Definition: registers.c:121
unsigned char * LPBYTE
Definition: typedefs.h:52
char CHAR
Definition: xmlstorage.h:175
_Must_inspect_result_ _In_ PFSRTL_PER_STREAM_CONTEXT Ptr
Definition: fsrtlfuncs.h:898
VOID WINAPI setDS(USHORT)
Definition: registers.c:515
BOOLEAN DosFreeMemory(WORD BlockData)
Definition: memory.c:418
#define DEFAULT_JFT_SIZE
Definition: handle.h:13
#define CreateFileMapping
Definition: winbase.h:3564
#define HIBYTE(W)
Definition: jmemdos.c:486
#define ERROR_NOT_ENOUGH_MEMORY
Definition: dderror.h:7
#define DOS_CMDLINE_LENGTH
Definition: process.h:13
#define INVALID_HANDLE_VALUE
Definition: compat.h:391
DWORD WINAPI GetLastError(VOID)
Definition: except.c:1059
USHORT WINAPI getSS(VOID)
Definition: registers.c:494
_At_(*)(_In_ PWSK_CLIENT Client, _In_opt_ PUNICODE_STRING NodeName, _In_opt_ PUNICODE_STRING ServiceName, _In_opt_ ULONG NameSpace, _In_opt_ GUID *Provider, _In_opt_ PADDRINFOEXW Hints, _Outptr_ PADDRINFOEXW *Result, _In_opt_ PEPROCESS OwningProcess, _In_opt_ PETHREAD OwningThread, _Inout_ PIRP Irp Result)(Mem)) NTSTATUS(WSKAPI *PFN_WSK_GET_ADDRESS_INFO
Definition: wsk.h:426
BYTE HandleTable[20]
Definition: process.h:36
char * LPSTR
Definition: xmlstorage.h:182
#define SEG_OFF_TO_PTR(seg, off)
Definition: emulator.h:28
DWORD DWORD
Definition: winlogon.h:84
#define FILE_SHARE_READ
Definition: compat.h:125
BYTE CommandLineSize
Definition: process.h:48
PDOS_DATA DosData
Definition: dos.c:45
VOID VDDTerminateUserHook(USHORT DosPDB)
Definition: vddsup.c:466
uint32_t ULONG_PTR
Definition: typedefs.h:63
DWORD WINAPI GetFullPathNameA(IN LPCSTR lpFileName, IN DWORD nBufferLength, OUT LPSTR lpBuffer, OUT LPSTR *lpFilePart)
Definition: path.c:992
#define SEGMENT_TO_PSP(seg)
Definition: process.h:16
VOID WINAPI setES(USHORT)
Definition: registers.c:529
VOID WINAPI setSP(USHORT)
Definition: registers.c:351
GLenum GLclampf GLint i
Definition: glfuncs.h:14
#define SCS_DOS_BINARY
Definition: winbase.h:235
#define FALSE
Definition: types.h:117
WORD LastParagraph
Definition: process.h:30
Definition: Header.h:8
ULONG WINAPI getEFLAGS(VOID)
Definition: registers.c:680
VOID VDDCreateUserHook(USHORT DosPDB)
Definition: vddsup.c:453
VOID WINAPI setSS(USHORT)
Definition: registers.c:501
VOID WINAPI setSI(USHORT)
Definition: registers.c:411
CHAR CommandLine[DOS_CMDLINE_LENGTH]
Definition: process.h:49
#define SCS_WOW_BINARY
Definition: winbase.h:239
#define MAKELONG(a, b)
Definition: typedefs.h:248
VOID DosCopyHandleTable(LPBYTE DestinationTable)
Definition: handle.c:27
smooth NULL
Definition: ftsmooth.c:416
struct _IMAGE_DOS_HEADER * PIMAGE_DOS_HEADER
WORD DosAllocateMemory(WORD Size, WORD *MaxAvailable)
Definition: memory.c:136
void DPRINT(...)
Definition: polytest.cpp:61
#define FAR_POINTER(x)
Definition: emulator.h:31
#define FILE_MAP_READ
Definition: compat.h:427
VOID DosSetProcessContext(WORD Segment)
Definition: process.c:321
const char * LPCSTR
Definition: xmlstorage.h:183
static PDWORD
Definition: process.c:68
static VOID DosSaveState(VOID)
Definition: process.c:84
WORD HandleTableSize
Definition: process.h:39
#define OPEN_EXISTING
Definition: compat.h:426
_Inout_ PVOID Segment
Definition: exfuncs.h:893
DWORD HandleTablePtr
Definition: process.h:40
static WORD DosCopyEnvironmentBlock(IN LPCSTR Environment OPTIONAL, IN LPCSTR ProgramName)
Definition: process.c:173
UINTN Size
Definition: acefiex.h:555
#define PCHAR
Definition: match.c:90
WORD FirstMcb
Definition: dos.h:77
WORD * PWORD
Definition: pedump.c:67
unsigned char BOOLEAN
_In_ HANDLE _Outptr_result_bytebuffer_ ViewSize PVOID * BaseAddress
Definition: mmfuncs.h:404
struct _DOS_PSP DOS_PSP
#define DOS_PROGRAM_NAME_TAG
Definition: process.h:14
#define LPDWORD
Definition: nt_native.h:46
USHORT WINAPI getES(VOID)
Definition: registers.c:522
static PULONG
Definition: process.c:83
DWORD WINAPI GetShortPathNameA(IN LPCSTR lpszLongPath, OUT LPSTR lpszShortPath, IN DWORD cchBuffer)
Definition: path.c:1751
#define MAX_PATH
Definition: compat.h:26
static VOID DosInitPsp(IN WORD Segment, IN WORD EnvBlock, IN LPCSTR CommandLine, IN LPCSTR ProgramName)
Definition: process.c:36
BYTE Exit[2]
Definition: process.h:29
DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh)
Definition: fileinfo.c:481
DWORD TerminateAddress
Definition: process.h:32
VOID WINAPI setBP(USHORT)
Definition: registers.c:381
VOID DosChangeMemoryOwner(WORD Segment, WORD NewOwner)
Definition: memory.c:532
NTSYSAPI CHAR NTAPI RtlUpperChar(CHAR Character)
VOID WINAPI setDX(USHORT)
Definition: registers.c:293
VOID DosCreatePsp(WORD Segment, WORD ProgramSize)
Definition: process.c:270
VOID CpuUnsimulate(VOID)
Definition: cpu.c:203
BOOL WINAPI GetBinaryTypeA(IN LPCSTR lpApplicationName, OUT LPDWORD lpBinaryType)
Definition: vdm.c:1327
#define FILE_ATTRIBUTE_NORMAL
Definition: compat.h:126
VOID CpuExecute(WORD Segment, WORD Offset)
Definition: cpu.c:102
static const WCHAR L[]
Definition: oid.c:1087
USHORT WINAPI getDX(VOID)
Definition: registers.c:286
WORD OwnerPsp
Definition: memory.h:31
WORD ParentPsp
Definition: process.h:35
USHORT WINAPI getAX(VOID)
Definition: registers.c:114
DWORD DosStartProcess32(IN LPCSTR ExecutablePath, IN LPCSTR CommandLine, IN LPCSTR Environment OPTIONAL, IN DWORD ReturnAddress OPTIONAL, IN BOOLEAN StartComSpec)
Definition: dem.c:916
VOID DosClonePsp(WORD DestSegment, WORD SourceSegment)
Definition: process.c:243
PDOS_SDA Sda
Definition: dos.c:48
VOID WINAPI setEFLAGS(ULONG)
Definition: registers.c:687
#define GENERIC_READ
Definition: compat.h:124
#define STACK_CS
Definition: int32.h:34
static const WCHAR Cleanup[]
Definition: register.c:80
VOID WINAPI setBX(USHORT)
Definition: registers.c:177
WORD ErrorLevel
Definition: dos.h:158
unsigned char BYTE
Definition: ntddk_ex.h:96
uint16_t * LPWORD
Definition: typedefs.h:54
DWORD DiskTransferArea
Definition: dos.h:155
VOID WINAPI setDI(USHORT)
Definition: registers.c:441
static HANDLE FileHandle
Definition: cabinet.c:48
ULONG_PTR SIZE_T
Definition: typedefs.h:78
VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
Definition: process.c:873
enum State_ State
Definition: pofuncs.h:54
DWORD *typedef HANDLE
Definition: winlogon.h:61
unsigned short USHORT
Definition: pedump.c:61
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)
Definition: process.c:327
USHORT WINAPI getDI(VOID)
Definition: registers.c:434
PDOS_SYSVARS SysVars
Definition: dos.c:47
IN PVCB IN ULONG IN OUT PULONG IN BOOLEAN OUT PLARGE_MCB Mcb
Definition: fatprocs.h:334
#define SYSTEM_PSP
Definition: dos.h:39
#define min(a, b)
Definition: monoChain.cc:55
CHAR BlockType
Definition: memory.h:30
#define PAGE_READONLY
Definition: compat.h:127
DWORD * PDWORD
Definition: pedump.c:68
VOID WINAPI setCX(USHORT)
Definition: registers.c:235
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)
Definition: process.c:695
DWORD BreakAddress
Definition: process.h:33
#define DPRINT1
Definition: precomp.h:8
BYTE FarCall[3]
Definition: process.h:45
USHORT WINAPI getDS(VOID)
Definition: registers.c:508
CHAR Name[8]
Definition: memory.h:34
uint32_t * LPDWORD
Definition: typedefs.h:57
char * strcpy(char *DstString, const char *SrcString)
Definition: utclib.c:388
WORD DosVersion
Definition: process.h:43
#define HIWORD(l)
Definition: typedefs.h:246
unsigned int ULONG
Definition: retypes.h:1
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:261
#define ULONG_PTR
Definition: config.h:101
DWORD CriticalAddress
Definition: process.h:34
USHORT WINAPI getBP(VOID)
Definition: registers.c:374
#define CreateFileA(a, b, c, d, e, f, g)
Definition: compat.h:399
USHORT WINAPI getSP(VOID)
Definition: registers.c:344
WORD Size
Definition: memory.h:32
WORD DosVersion
Definition: dos.h:246
void DisplayMessage(BOOL bConsole, BOOL bSilent, LPCTSTR lpMessage, LPCTSTR lpTitle, UINT uType)
Definition: regsvr32.c:239
BOOLEAN DosCloseHandle(WORD DosHandle)
Definition: handle.c:311
#define UnmapViewOfFile
Definition: compat.h:403
#define RtlFillMemory(Dest, Length, Fill)
Definition: winternl.h:593
#define STACK_IP
Definition: int32.h:33
#define SEGMENT_TO_MCB(seg)
Definition: memory.h:15
BYTE * PBYTE
Definition: pedump.c:66
#define LOWORD(l)
Definition: pedump.c:82
IN HDEVINFO IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
Definition: devinst.c:44
IN SCSI_ADAPTER_CONTROL_TYPE IN PVOID Parameters
Definition: srb.h:488
WORD EnvBlock
Definition: process.h:37
static LPSTR
Definition: process.c:74