[NTVDM]
authorAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 1 May 2015 15:42:54 +0000 (15:42 +0000)
committerAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 1 May 2015 15:42:54 +0000 (15:42 +0000)
Separate the process-related code from the DOS kernel.
Enable starting processes from other processes in STANDALONE mode.
Implement INT 21h, AH = 55h and INT 21h, AH = 26h (Create/Clone PSP).
Implement overlay loading.

svn path=/trunk/; revision=67498

reactos/subsystems/mvdm/ntvdm/CMakeLists.txt
reactos/subsystems/mvdm/ntvdm/dos/dem.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dosfiles.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/handle.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/memory.h
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/process.c [new file with mode: 0644]
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/process.h [new file with mode: 0644]

index f9ea31c..3941d8c 100644 (file)
@@ -36,6 +36,7 @@ list(APPEND SOURCE
     dos/dos32krnl/handle.c
     dos/dos32krnl/himem.c
     dos/dos32krnl/memory.c
+    dos/dos32krnl/process.c
     dos/mouse32.c
     dos/dem.c
     clock.c
index 92ef845..a1adf9e 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "dem.h"
 #include "dos/dos32krnl/device.h"
+#include "dos/dos32krnl/process.h"
 #include "cpu/bop.h"
 
 #include "bios/bios.h"
index 61114cc..ccf106f 100644 (file)
@@ -22,6 +22,7 @@
 #include "handle.h"
 #include "dosfiles.h"
 #include "memory.h"
+#include "process.h"
 #include "himem.h"
 
 #include "bios/bios.h"
 
 CALLBACK16 DosContext;
 
-static DWORD DiskTransferArea;
 /*static*/ BYTE CurrentDrive;
 static CHAR LastDrive = 'E';
 static CHAR CurrentDirectories[NUM_DRIVES][DOS_DIR_LENGTH];
-static WORD DosErrorLevel = 0x0000;
 static PBYTE InDos;
 
 /* PUBLIC VARIABLES ***********************************************************/
@@ -50,79 +49,13 @@ PDOS_SYSVARS SysVars;
 
 /* Echo state for INT 21h, AH = 01h and AH = 3Fh */
 BOOLEAN DoEcho = FALSE;
-WORD CurrentPsp = SYSTEM_PSP;
+
+DWORD DiskTransferArea;
+WORD DosErrorLevel = 0x0000;
 WORD DosLastError = 0;
 
 /* PRIVATE FUNCTIONS **********************************************************/
 
-static WORD DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL,
-                                    LPCSTR ProgramName)
-{
-    PCHAR Ptr, DestBuffer = NULL;
-    ULONG TotalSize = 0;
-    WORD DestSegment;
-
-    /* If we have an environment strings list, compute its size */
-    if (Environment)
-    {
-        /* Calculate the size of the environment block */
-        Ptr = (PCHAR)Environment;
-        while (*Ptr) Ptr += strlen(Ptr) + 1;
-        TotalSize = (ULONG_PTR)Ptr - (ULONG_PTR)Environment;
-    }
-    else
-    {
-        /* Empty environment string */
-        TotalSize = 1;
-    }
-    /* Add the final environment block NULL-terminator */
-    TotalSize++;
-
-    /* Add the two bytes for the program name tag */
-    TotalSize += 2;
-
-    /* Add the string buffer size */
-    TotalSize += strlen(ProgramName) + 1;
-
-    /* Allocate the memory for the environment block */
-    DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
-    if (!DestSegment) return 0;
-
-    DestBuffer = (PCHAR)SEG_OFF_TO_PTR(DestSegment, 0);
-
-    /* If we have an environment strings list, copy it */
-    if (Environment)
-    {
-        Ptr = (PCHAR)Environment;
-        while (*Ptr)
-        {
-            /* Copy the string and NULL-terminate it */
-            strcpy(DestBuffer, Ptr);
-            DestBuffer += strlen(Ptr);
-            *(DestBuffer++) = '\0';
-
-            /* Move to the next string */
-            Ptr += strlen(Ptr) + 1;
-        }
-    }
-    else
-    {
-        /* Empty environment string */
-        *(DestBuffer++) = '\0';
-    }
-    /* NULL-terminate the environment block */
-    *(DestBuffer++) = '\0';
-
-    /* Store the special program name tag */
-    *(DestBuffer++) = LOBYTE(DOS_PROGRAM_NAME_TAG);
-    *(DestBuffer++) = HIBYTE(DOS_PROGRAM_NAME_TAG);
-
-    /* Copy the program name after the environment block */
-    strcpy(DestBuffer, ProgramName);
-
-    return DestSegment;
-}
-
 static BOOLEAN DosChangeDrive(BYTE Drive)
 {
     WCHAR DirectoryPath[DOS_CMDLINE_LENGTH];
@@ -272,556 +205,13 @@ VOID DosInitializePsp(WORD PspSegment,
     PspBlock->FarCall[1] = 0x21;
     PspBlock->FarCall[2] = 0xCB; // retf
 
-    /* Set the command line */
-    PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1);
-    RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
-    PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
-}
-
-DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
-                        IN LPCSTR ExecutablePath,
-                        IN LPCSTR CommandLine,
-                        IN LPCSTR Environment OPTIONAL,
-                        IN DWORD ReturnAddress OPTIONAL,
-                        OUT PDWORD StackLocation OPTIONAL,
-                        OUT PDWORD EntryPoint OPTIONAL)
-{
-    DWORD Result = ERROR_SUCCESS;
-    HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
-    LPBYTE Address = NULL;
-    WORD Segment = 0;
-    WORD EnvBlock = 0;
-    WORD MaxAllocSize;
-    WORD ExeSegment;
-    DWORD i, FileSize, BaseSize, TotalSize;
-    PIMAGE_DOS_HEADER Header;
-    PDWORD RelocationTable;
-    PWORD RelocWord;
-    LPSTR CmdLinePtr = (LPSTR)CommandLine;
-    BOOLEAN LoadHigh = FALSE;
-
-    DPRINT1("DosLoadExecutable(%d, %s, %s, %s, 0x%08X, 0x%08X)\n",
-            LoadType,
-            ExecutablePath,
-            CommandLine,
-            Environment ? Environment : "n/a",
-            StackLocation,
-            EntryPoint);
-
-    if (LoadType == DOS_LOAD_OVERLAY)
-    {
-        DPRINT1("Overlay loading is not supported yet.\n");
-        return ERROR_NOT_SUPPORTED;
-    }
-
-    /* NULL-terminate the command line by removing the return carriage character */
-    while (*CmdLinePtr && *CmdLinePtr != '\r') CmdLinePtr++;
-    *CmdLinePtr = '\0';
-
-    /* Open a handle to the executable */
-    FileHandle = CreateFileA(ExecutablePath,
-                             GENERIC_READ,
-                             FILE_SHARE_READ,
-                             NULL,
-                             OPEN_EXISTING,
-                             FILE_ATTRIBUTE_NORMAL,
-                             NULL);
-    if (FileHandle == INVALID_HANDLE_VALUE)
-    {
-        Result = GetLastError();
-        goto Cleanup;
-    }
-
-    /* Get the file size */
-    FileSize = GetFileSize(FileHandle, NULL);
-
-    /* Create a mapping object for the file */
-    FileMapping = CreateFileMapping(FileHandle,
-                                    NULL,
-                                    PAGE_READONLY,
-                                    0,
-                                    0,
-                                    NULL);
-    if (FileMapping == NULL)
-    {
-        Result = GetLastError();
-        goto Cleanup;
-    }
-
-    /* Map the file into memory */
-    Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
-    if (Address == NULL)
-    {
-        Result = GetLastError();
-        goto Cleanup;
-    }
-
-    /* Copy the environment block to DOS memory */
-    EnvBlock = DosCopyEnvironmentBlock(Environment, ExecutablePath);
-    if (EnvBlock == 0)
-    {
-        Result = ERROR_NOT_ENOUGH_MEMORY;
-        goto Cleanup;
-    }
-
-    /* Check if this is an EXE file or a COM file */
-    if (Address[0] == 'M' && Address[1] == 'Z')
-    {
-        /* EXE file */
-
-        /* Get the MZ header */
-        Header = (PIMAGE_DOS_HEADER)Address;
-
-        /* Get the base size of the file, in paragraphs (rounded up) */
-        BaseSize = TotalSize = (((Header->e_cp - 1) * 512) + Header->e_cblp + 0x0F) >> 4;
-
-        /* Add the PSP size, in paragraphs */
-        TotalSize += sizeof(DOS_PSP) >> 4;
-
-        /* Add the maximum size that should be allocated */
-        TotalSize += Header->e_maxalloc;
-
-        if (Header->e_minalloc == 0 && Header->e_maxalloc == 0)
-        {
-            /* This program should be loaded high */
-            LoadHigh = TRUE;
-            TotalSize = 0xFFFF;
-        }
-
-        /* Make sure it does not pass 0xFFFF */
-        if (TotalSize > 0xFFFF) TotalSize = 0xFFFF;
-
-        /* Try to allocate that much memory */
-        Segment = DosAllocateMemory((WORD)TotalSize, &MaxAllocSize);
-
-        if (Segment == 0)
-        {
-            /* Check if there's at least enough memory for the minimum size */
-            if (MaxAllocSize < (BaseSize + (sizeof(DOS_PSP) >> 4) + Header->e_minalloc))
-            {
-                Result = DosLastError;
-                goto Cleanup;
-            }
-
-            /* Allocate that minimum amount */
-            TotalSize = MaxAllocSize;
-            Segment = DosAllocateMemory((WORD)TotalSize, NULL);
-            ASSERT(Segment != 0);
-        }
-
-        /* Initialize the PSP */
-        DosInitializePsp(Segment,
-                         CommandLine,
-                         (WORD)TotalSize,
-                         EnvBlock,
-                         ReturnAddress);
-
-        /* The process owns its own memory */
-        DosChangeMemoryOwner(Segment, Segment);
-        DosChangeMemoryOwner(EnvBlock, Segment);
-
-        /* Find the EXE segment */
-        if (!LoadHigh) ExeSegment = Segment + (sizeof(DOS_PSP) >> 4);
-        else ExeSegment = Segment + TotalSize - BaseSize;
-
-        /* Copy the program to the code segment */
-        RtlCopyMemory(SEG_OFF_TO_PTR(ExeSegment, 0),
-                      Address + (Header->e_cparhdr << 4),
-                      min(FileSize - (Header->e_cparhdr << 4), BaseSize << 4));
-
-        /* Get the relocation table */
-        RelocationTable = (PDWORD)(Address + Header->e_lfarlc);
-
-        /* Perform relocations */
-        for (i = 0; i < Header->e_crlc; i++)
-        {
-            /* Get a pointer to the word that needs to be patched */
-            RelocWord = (PWORD)SEG_OFF_TO_PTR(ExeSegment + HIWORD(RelocationTable[i]),
-                                              LOWORD(RelocationTable[i]));
-
-            /* Add the number of the EXE segment to it */
-            *RelocWord += ExeSegment;
-        }
-
-        if (LoadType == DOS_LOAD_AND_EXECUTE)
-        {
-            /* Set the initial segment registers */
-            setDS(Segment);
-            setES(Segment);
-
-            /* Set the stack to the location from the header */
-            setSS(ExeSegment + Header->e_ss);
-            setSP(Header->e_sp);
-
-            /* Execute */
-            CurrentPsp = Segment;
-            DiskTransferArea = MAKELONG(0x80, Segment);
-            CpuExecute(ExeSegment + Header->e_cs, Header->e_ip);
-        }
-    }
-    else
+    if (CommandLine)
     {
-        /* COM file */
-
-        /* Find the maximum amount of memory that can be allocated */
-        DosAllocateMemory(0xFFFF, &MaxAllocSize);
-
-        /* Make sure it's enough for the whole program and the PSP */
-        if (((DWORD)MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP)))
-        {
-            Result = ERROR_NOT_ENOUGH_MEMORY;
-            goto Cleanup;
-        }
-
-        /* Allocate all of it */
-        Segment = DosAllocateMemory(MaxAllocSize, NULL);
-        if (Segment == 0)
-        {
-            Result = DosLastError;
-            goto Cleanup;
-        }
-
-        /* The process owns its own memory */
-        DosChangeMemoryOwner(Segment, Segment);
-        DosChangeMemoryOwner(EnvBlock, Segment);
-
-        /* Copy the program to Segment:0100 */
-        RtlCopyMemory(SEG_OFF_TO_PTR(Segment, 0x100),
-                      Address,
-                      FileSize);
-
-        /* Initialize the PSP */
-        DosInitializePsp(Segment,
-                         CommandLine,
-                         MaxAllocSize,
-                         EnvBlock,
-                         ReturnAddress);
-
-        if (LoadType == DOS_LOAD_AND_EXECUTE)
-        {
-            /* Set the initial segment registers */
-            setDS(Segment);
-            setES(Segment);
-
-            /* Set the stack to the last word of the segment */
-            setSS(Segment);
-            setSP(0xFFFE);
-
-            /*
-             * Set the value on the stack to 0, so that a near return
-             * jumps to PSP:0000 which has the exit code.
-             */
-            *((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0;
-
-            /* Execute */
-            CurrentPsp = Segment;
-            DiskTransferArea = MAKELONG(0x80, Segment);
-            CpuExecute(Segment, 0x100);
-        }
-    }
-
-Cleanup:
-    if (Result != ERROR_SUCCESS)
-    {
-        /* It was not successful, cleanup the DOS memory */
-        if (EnvBlock) DosFreeMemory(EnvBlock);
-        if (Segment) DosFreeMemory(Segment);
+        /* Set the command line */
+        PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1);
+        RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
+        PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
     }
-
-    /* Unmap the file*/
-    if (Address != NULL) UnmapViewOfFile(Address);
-
-    /* Close the file mapping object */
-    if (FileMapping != NULL) CloseHandle(FileMapping);
-
-    /* Close the file handle */
-    if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
-
-    return Result;
-}
-
-DWORD DosStartProcess(IN LPCSTR ExecutablePath,
-                      IN LPCSTR CommandLine,
-                      IN LPCSTR Environment OPTIONAL)
-{
-    DWORD Result;
-    LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
-
-    Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
-                               ExecutablePath,
-                               CommandLine,
-                               Environment,
-                               IntVecTable[0x20], // Use INT 20h
-                               NULL,
-                               NULL);
-
-    if (Result != ERROR_SUCCESS) goto Quit;
-
-    /* Attach to the console */
-    ConsoleAttach();
-    VidBiosAttachToConsole();
-
-    // HACK: Simulate a ENTER key release scancode on the PS/2 port because
-    // some apps expect to read a key release scancode (> 0x80) when they
-    // are started.
-    IOWriteB(PS2_CONTROL_PORT, 0xD2);     // Next write is for the first PS/2 port
-    IOWriteB(PS2_DATA_PORT, 0x80 | 0x1C); // ENTER key release
-
-    /* Start simulation */
-    SetEvent(VdmTaskEvent);
-    CpuSimulate();
-
-    /* Detach from the console */
-    VidBiosDetachFromConsole();
-    ConsoleDetach();
-
-Quit:
-    return Result;
-}
-
-#ifndef STANDALONE
-WORD DosCreateProcess(DOS_EXEC_TYPE LoadType,
-                      LPCSTR ProgramName,
-                      PDOS_EXEC_PARAM_BLOCK Parameters,
-                      DWORD ReturnAddress)
-{
-    DWORD Result;
-    DWORD BinaryType;
-    LPVOID Environment = NULL;
-    VDM_COMMAND_INFO CommandInfo;
-    CHAR CmdLine[MAX_PATH];
-    CHAR AppName[MAX_PATH];
-    CHAR PifFile[MAX_PATH];
-    CHAR Desktop[MAX_PATH];
-    CHAR Title[MAX_PATH];
-    ULONG EnvSize = 256;
-    PVOID Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
-    STARTUPINFOA StartupInfo;
-    PROCESS_INFORMATION ProcessInfo;
-
-    /* Get the binary type */
-    if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
-
-    /* Did the caller specify an environment segment? */
-    if (Parameters->Environment)
-    {
-        /* Yes, use it instead of the parent one */
-        Environment = SEG_OFF_TO_PTR(Parameters->Environment, 0);
-    }
-
-    /* Set up the startup info structure */
-    RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
-    StartupInfo.cb = sizeof(StartupInfo);
-
-    /* Create the process */
-    if (!CreateProcessA(ProgramName,
-                        FAR_POINTER(Parameters->CommandLine),
-                        NULL,
-                        NULL,
-                        FALSE,
-                        0,
-                        Environment,
-                        NULL,
-                        &StartupInfo,
-                        &ProcessInfo))
-    {
-        return GetLastError();
-    }
-
-    /* Check the type of the program */
-    switch (BinaryType)
-    {
-        /* These are handled by NTVDM */
-        case SCS_DOS_BINARY:
-        case SCS_WOW_BINARY:
-        {
-            /* Clear the structure */
-            RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
-
-            /* Initialize the structure members */
-            CommandInfo.TaskId = SessionId;
-            CommandInfo.VDMState = VDM_FLAG_NESTED_TASK | VDM_FLAG_DONT_WAIT;
-            CommandInfo.CmdLine = CmdLine;
-            CommandInfo.CmdLen = sizeof(CmdLine);
-            CommandInfo.AppName = AppName;
-            CommandInfo.AppLen = sizeof(AppName);
-            CommandInfo.PifFile = PifFile;
-            CommandInfo.PifLen = sizeof(PifFile);
-            CommandInfo.Desktop = Desktop;
-            CommandInfo.DesktopLen = sizeof(Desktop);
-            CommandInfo.Title = Title;
-            CommandInfo.TitleLen = sizeof(Title);
-            CommandInfo.Env = Env;
-            CommandInfo.EnvLen = EnvSize;
-
-Command:
-            /* Get the VDM command information */
-            if (!GetNextVDMCommand(&CommandInfo))
-            {
-                if (CommandInfo.EnvLen > EnvSize)
-                {
-                    /* Expand the environment size */
-                    EnvSize = CommandInfo.EnvLen;
-                    CommandInfo.Env = Env = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Env, EnvSize);
-
-                    /* Repeat the request */
-                    CommandInfo.VDMState |= VDM_FLAG_RETRY;
-                    goto Command;
-                }
-
-                /* Shouldn't happen */
-                ASSERT(FALSE);
-            }
-
-            /* Load the executable */
-            Result = DosLoadExecutable(LoadType,
-                                       AppName,
-                                       CmdLine,
-                                       Env,
-                                       ReturnAddress,
-                                       &Parameters->StackLocation,
-                                       &Parameters->EntryPoint);
-            if (Result == ERROR_SUCCESS)
-            {
-                /* Increment the re-entry count */
-                CommandInfo.VDMState = VDM_INC_REENTER_COUNT;
-                GetNextVDMCommand(&CommandInfo);
-            }
-            else
-            {
-                DisplayMessage(L"Could not load '%S'. Error: %u", AppName, Result);
-            }
-
-            break;
-        }
-
-        /* Not handled by NTVDM */
-        default:
-        {
-            /* Wait for the process to finish executing */
-            WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
-        }
-    }
-
-    RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
-
-    /* Close the handles */
-    CloseHandle(ProcessInfo.hProcess);
-    CloseHandle(ProcessInfo.hThread);
-
-    return ERROR_SUCCESS;
-}
-#endif
-
-VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
-{
-    WORD i;
-    WORD McbSegment = FIRST_MCB_SEGMENT;
-    PDOS_MCB CurrentMcb;
-    LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
-    PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
-
-    DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
-           Psp,
-           ReturnCode,
-           KeepResident);
-
-    /* Check if this PSP is it's own parent */
-    if (PspBlock->ParentPsp == Psp) goto Done;
-
-    if (KeepResident == 0)
-    {
-        for (i = 0; i < PspBlock->HandleTableSize; i++)
-        {
-            /* Close the handle */
-            DosCloseHandle(i);
-        }
-    }
-
-    /* Free the memory used by the process */
-    while (TRUE)
-    {
-        /* Get a pointer to the MCB */
-        CurrentMcb = SEGMENT_TO_MCB(McbSegment);
-
-        /* Make sure the MCB is valid */
-        if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') break;
-
-        /* Check if this block was allocated by the process */
-        if (CurrentMcb->OwnerPsp == Psp)
-        {
-            if (KeepResident == 0)
-            {
-                /* Free this entire block */
-                DosFreeMemory(McbSegment + 1);
-            }
-            else if (KeepResident < CurrentMcb->Size)
-            {
-                /* Reduce the size of the block */
-                DosResizeMemory(McbSegment + 1, KeepResident, NULL);
-
-                /* No further paragraphs need to stay resident */
-                KeepResident = 0;
-            }
-            else
-            {
-                /* Just reduce the amount of paragraphs we need to keep resident */
-                KeepResident -= CurrentMcb->Size;
-            }
-        }
-
-        /* If this was the last block, quit */
-        if (CurrentMcb->BlockType == 'Z') break;
-
-        /* Update the segment and continue */
-        McbSegment += CurrentMcb->Size + 1;
-    }
-
-Done:
-    /* Restore the interrupt vectors */
-    IntVecTable[0x22] = PspBlock->TerminateAddress;
-    IntVecTable[0x23] = PspBlock->BreakAddress;
-    IntVecTable[0x24] = PspBlock->CriticalAddress;
-
-    /* Update the current PSP */
-    if (Psp == CurrentPsp)
-    {
-        CurrentPsp = PspBlock->ParentPsp;
-        if (CurrentPsp == SYSTEM_PSP)
-        {
-            ResetEvent(VdmTaskEvent);
-            CpuUnsimulate();
-        }
-    }
-
-#ifndef STANDALONE
-    // FIXME: This is probably not the best way to do it
-    /* Check if this was a nested DOS task */
-    if (CurrentPsp != SYSTEM_PSP)
-    {
-        VDM_COMMAND_INFO CommandInfo;
-
-        /* Decrement the re-entry count */
-        CommandInfo.TaskId = SessionId;
-        CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
-        GetNextVDMCommand(&CommandInfo);
-
-        /* Clear the structure */
-        RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
-
-        /* Update the VDM state of the task */
-        CommandInfo.TaskId = SessionId;
-        CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
-        GetNextVDMCommand(&CommandInfo);
-    }
-#endif
-
-    /* Save the return code - Normal termination */
-    DosErrorLevel = MAKEWORD(ReturnCode, 0x00);
-
-    /* Return control to the parent process */
-    CpuExecute(HIWORD(PspBlock->TerminateAddress),
-               LOWORD(PspBlock->TerminateAddress));
 }
 
 VOID WINAPI DosInt20h(LPWORD Stack)
@@ -1205,7 +595,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Create New PSP */
         case 0x26:
         {
-            DPRINT1("INT 21h, AH = 26h - Create New PSP is UNIMPLEMENTED\n");
+            DosClonePsp(getDX(), getCS());
             break;
         }
 
@@ -1963,7 +1353,6 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             break;
         }
 
-#ifndef STANDALONE
         /* Execute */
         case 0x4B:
         {
@@ -1973,19 +1362,24 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             DWORD ReturnAddress = MAKELONG(Stack[STACK_IP], Stack[STACK_CS]);
             WORD ErrorCode;
             
-            if (LoadType != DOS_LOAD_OVERLAY)
+#ifndef STANDALONE
+            if (LoadType == DOS_LOAD_AND_EXECUTE)
             {
-                ErrorCode = DosCreateProcess(LoadType, ProgramName, ParamBlock, ReturnAddress);
+                /* Create a new process */
+                ErrorCode = DosCreateProcess(ProgramName, ParamBlock, ReturnAddress);
             }
             else
             {
-                ErrorCode = DosLoadExecutable(DOS_LOAD_OVERLAY,
+#else
+            {
+#endif
+                /* Just load an executable */
+                ErrorCode = DosLoadExecutable(LoadType,
                                               ProgramName,
-                                              FAR_POINTER(ParamBlock->CommandLine),
-                                              SEG_OFF_TO_PTR(ParamBlock->Environment, 0),
-                                              ReturnAddress,
+                                              ParamBlock,
                                               NULL,
-                                              NULL);
+                                              NULL,
+                                              ReturnAddress);
             }
 
             if (ErrorCode == ERROR_SUCCESS)
@@ -2000,7 +1394,6 @@ VOID WINAPI DosInt21h(LPWORD Stack)
 
             break;
         }
-#endif
 
         /* Terminate With Return Code */
         case 0x4C:
@@ -2057,8 +1450,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Internal - Set Current Process ID (Set PSP Address) */
         case 0x50:
         {
-            // FIXME: Is it really what it's done ??
-            CurrentPsp = getBX();
+            DosSetProcessContext(getBX());
             break;
         }
 
@@ -2093,6 +1485,13 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             break;
         }
 
+        /* Create Child PSP */
+        case 0x55:
+        {
+            DosCreatePsp(getDX(), getSI());
+            break;
+        }
+
         /* Rename File */
         case 0x56:
         {
index c0d0551..09b573f 100644 (file)
 #define DOS_ERROR_HANDLE    2
 
 #define DOS_SFT_SIZE 255
-#define SEGMENT_TO_MCB(seg) ((PDOS_MCB)((ULONG_PTR)BaseAddress + TO_LINEAR((seg), 0)))
-#define SEGMENT_TO_PSP(seg) ((PDOS_PSP)((ULONG_PTR)BaseAddress + TO_LINEAR((seg), 0)))
 #define UMB_START_SEGMENT 0xC000
 #define UMB_END_SEGMENT 0xDFFF
 #define DOS_ALLOC_HIGH 0x40
 #define DOS_ALLOC_HIGH_LOW 0x80
-#define DOS_CMDLINE_LENGTH 127
 #define DOS_DIR_LENGTH 64
 #define NUM_DRIVES ('Z' - 'A' + 1)
 #define DOS_CHAR_ATTRIBUTE 0x07
-#define DOS_PROGRAM_NAME_TAG 0x0001
 
 /* 16 MB of EMS memory */
 #define EMS_TOTAL_PAGES 1024
 
-typedef enum
-{
-    DOS_LOAD_AND_EXECUTE = 0x00,
-    DOS_LOAD_ONLY = 0x01,
-    DOS_LOAD_OVERLAY = 0x03
-} DOS_EXEC_TYPE;
-
 #pragma pack(push, 1)
 
 typedef struct _DOS_FCB
@@ -102,31 +91,6 @@ typedef struct _DOS_SYSVARS
     BYTE NullDriverRoutine[7];
 } DOS_SYSVARS, *PDOS_SYSVARS;
 
-typedef struct _DOS_PSP
-{
-    BYTE Exit[2];
-    WORD LastParagraph;
-    BYTE Reserved0[6];
-    DWORD TerminateAddress;
-    DWORD BreakAddress;
-    DWORD CriticalAddress;
-    WORD ParentPsp;
-    BYTE HandleTable[20];
-    WORD EnvBlock;
-    DWORD LastStack;
-    WORD HandleTableSize;
-    DWORD HandleTablePtr;
-    DWORD PreviousPsp;
-    DWORD Reserved1;
-    WORD DosVersion;
-    BYTE Reserved2[14];
-    BYTE FarCall[3];
-    BYTE Reserved3[9];
-    DOS_FCB Fcb;
-    BYTE CommandLineSize;
-    CHAR CommandLine[DOS_CMDLINE_LENGTH];
-} DOS_PSP, *PDOS_PSP;
-
 typedef struct _DOS_INPUT_BUFFER
 {
     BYTE MaxLength;
@@ -150,19 +114,6 @@ typedef struct _DOS_FIND_FILE_BLOCK
     CHAR FileName[13];
 } DOS_FIND_FILE_BLOCK, *PDOS_FIND_FILE_BLOCK;
 
-typedef struct _DOS_EXEC_PARAM_BLOCK
-{
-    /* Input variables */
-    WORD Environment;
-    DWORD CommandLine;
-    DWORD FirstFcb;
-    DWORD SecondFcb;
-
-    /* Output variables */
-    DWORD StackLocation;
-    DWORD EntryPoint;
-} DOS_EXEC_PARAM_BLOCK, *PDOS_EXEC_PARAM_BLOCK;
-
 typedef struct _DOS_COUNTRY_CODE_BUFFER
 {
     WORD TimeFormat;
@@ -176,7 +127,8 @@ typedef struct _DOS_COUNTRY_CODE_BUFFER
 /* VARIABLES ******************************************************************/
 
 extern BOOLEAN DoEcho;
-extern WORD CurrentPsp;
+extern DWORD DiskTransferArea;
+extern WORD DosErrorLevel;
 extern WORD DosLastError;
 extern PDOS_SYSVARS SysVars;
 
@@ -207,36 +159,6 @@ VOID ConDrvCleanup(VOID);
  * See dos.c
  */
 
-VOID DosInitializePsp(
-    WORD PspSegment,
-    LPCSTR CommandLine,
-    WORD ProgramSize,
-    WORD Environment,
-    DWORD ReturnAddress
-);
-DWORD DosLoadExecutable(
-    IN DOS_EXEC_TYPE LoadType,
-    IN LPCSTR ExecutablePath,
-    IN LPCSTR CommandLine,
-    IN LPCSTR Environment OPTIONAL,
-    IN DWORD ReturnAddress OPTIONAL,
-    OUT PDWORD StackLocation OPTIONAL,
-    OUT PDWORD EntryPoint OPTIONAL
-);
-WORD DosCreateProcess(
-    DOS_EXEC_TYPE LoadType,
-    LPCSTR ProgramName,
-    PDOS_EXEC_PARAM_BLOCK Parameters,
-    DWORD ReturnAddress
-);
-DWORD DosStartProcess(
-    IN LPCSTR ExecutablePath,
-    IN LPCSTR CommandLine,
-    IN LPCSTR Environment OPTIONAL
-);
-VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident);
-BOOLEAN DosHandleIoctl(BYTE ControlCode, WORD FileHandle);
-
 BOOLEAN DosKRNLInitialize(VOID);
 
 #endif // _DOS_H_
index 13a8df1..a678b19 100644 (file)
@@ -19,6 +19,7 @@
 #include "dos/dem.h"
 #include "dosfiles.h"
 #include "handle.h"
+#include "process.h"
 
 #include "bios/bios.h"
 
index a4ec76d..6996566 100644 (file)
@@ -18,6 +18,7 @@
 #include "dosfiles.h"
 #include "handle.h"
 #include "memory.h"
+#include "process.h"
 
 /* PRIVATE FUNCTIONS **********************************************************/
 
index 4a96255..9332c55 100644 (file)
 
 #include "ntvdm.h"
 #include "emulator.h"
-#include "memory.h"
 
 #include "dos.h"
 #include "dos/dem.h"
+#include "memory.h"
+#include "process.h"
 
 /* PUBLIC VARIABLES ***********************************************************/
 
index 13adf3a..3d537be 100644 (file)
@@ -11,6 +11,8 @@
 
 /* TYPEDEFS *******************************************************************/
 
+#define SEGMENT_TO_MCB(seg) ((PDOS_MCB)((ULONG_PTR)BaseAddress + TO_LINEAR((seg), 0)))
+
 enum DOS_ALLOC_STRATEGY
 {
     DOS_ALLOC_FIRST_FIT,
diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/process.c b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/process.c
new file mode 100644 (file)
index 0000000..57024e0
--- /dev/null
@@ -0,0 +1,773 @@
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            dos/dos32krnl/process.c
+ * PURPOSE:         DOS32 Processes
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
+ */
+
+/* INCLUDES *******************************************************************/
+
+#define NDEBUG
+
+#include "ntvdm.h"
+#include "emulator.h"
+#include "cpu/cpu.h"
+
+#include "dos.h"
+#include "dos/dem.h"
+#include "dosfiles.h"
+#include "handle.h"
+#include "process.h"
+#include "memory.h"
+
+#include "bios/bios.h"
+
+#include "io.h"
+#include "hardware/ps2.h"
+
+/* PUBLIC VARIABLES ***********************************************************/
+
+WORD CurrentPsp = SYSTEM_PSP;
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+static inline VOID DosSetPspCommandLine(WORD Segment, LPCSTR CommandLine)
+{
+    PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
+
+    /* Set the command line */
+    PspBlock->CommandLineSize = (BYTE)min(strlen(CommandLine), DOS_CMDLINE_LENGTH - 1);
+    RtlCopyMemory(PspBlock->CommandLine, CommandLine, PspBlock->CommandLineSize);
+    PspBlock->CommandLine[PspBlock->CommandLineSize] = '\r';
+}
+
+static WORD DosCopyEnvironmentBlock(LPCSTR Environment OPTIONAL,
+                                    LPCSTR ProgramName)
+{
+    PCHAR Ptr, DestBuffer = NULL;
+    ULONG TotalSize = 0;
+    WORD DestSegment;
+
+    /* If we have an environment strings list, compute its size */
+    if (Environment)
+    {
+        /* Calculate the size of the environment block */
+        Ptr = (PCHAR)Environment;
+        while (*Ptr) Ptr += strlen(Ptr) + 1;
+        TotalSize = (ULONG_PTR)Ptr - (ULONG_PTR)Environment;
+    }
+    else
+    {
+        /* Empty environment string */
+        TotalSize = 1;
+    }
+    /* Add the final environment block NULL-terminator */
+    TotalSize++;
+
+    /* Add the two bytes for the program name tag */
+    TotalSize += 2;
+
+    /* Add the string buffer size */
+    TotalSize += strlen(ProgramName) + 1;
+
+    /* Allocate the memory for the environment block */
+    DestSegment = DosAllocateMemory((WORD)((TotalSize + 0x0F) >> 4), NULL);
+    if (!DestSegment) return 0;
+
+    DestBuffer = (PCHAR)SEG_OFF_TO_PTR(DestSegment, 0);
+
+    /* If we have an environment strings list, copy it */
+    if (Environment)
+    {
+        Ptr = (PCHAR)Environment;
+        while (*Ptr)
+        {
+            /* Copy the string and NULL-terminate it */
+            strcpy(DestBuffer, Ptr);
+            DestBuffer += strlen(Ptr);
+            *(DestBuffer++) = '\0';
+
+            /* Move to the next string */
+            Ptr += strlen(Ptr) + 1;
+        }
+    }
+    else
+    {
+        /* Empty environment string */
+        *(DestBuffer++) = '\0';
+    }
+    /* NULL-terminate the environment block */
+    *(DestBuffer++) = '\0';
+
+    /* Store the special program name tag */
+    *(DestBuffer++) = LOBYTE(DOS_PROGRAM_NAME_TAG);
+    *(DestBuffer++) = HIBYTE(DOS_PROGRAM_NAME_TAG);
+
+    /* Copy the program name after the environment block */
+    strcpy(DestBuffer, ProgramName);
+
+    return DestSegment;
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+VOID DosClonePsp(WORD DestSegment, WORD SourceSegment)
+{
+    PDOS_PSP DestPsp = SEGMENT_TO_PSP(DestSegment);
+    PDOS_PSP SourcePsp = SEGMENT_TO_PSP(SourceSegment);
+    LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
+
+    /* Literally copy the PSP first */
+    RtlCopyMemory(DestPsp, SourcePsp, sizeof(DOS_PSP));
+
+    /* Save the interrupt vectors */
+    DestPsp->TerminateAddress = IntVecTable[0x22];
+    DestPsp->BreakAddress     = IntVecTable[0x23];
+    DestPsp->CriticalAddress  = IntVecTable[0x24];
+
+    /* No parent PSP */
+    DestPsp->ParentPsp = 0;
+
+    /* Set the handle table pointers to the internal handle table */
+    DestPsp->HandleTableSize = DEFAULT_JFT_SIZE;
+    DestPsp->HandleTablePtr = MAKELONG(0x18, DestSegment);
+
+    /* Copy the parent handle table without referencing the SFT */
+    RtlCopyMemory(FAR_POINTER(DestPsp->HandleTablePtr),
+                  FAR_POINTER(SourcePsp->HandleTablePtr),
+                  DEFAULT_JFT_SIZE);
+}
+
+VOID DosCreatePsp(WORD Segment, WORD ProgramSize)
+{
+    PDOS_PSP PspBlock = SEGMENT_TO_PSP(Segment);
+    LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
+
+    RtlZeroMemory(PspBlock, sizeof(*PspBlock));
+
+    /* Set the exit interrupt */
+    PspBlock->Exit[0] = 0xCD; // int 0x20
+    PspBlock->Exit[1] = 0x20;
+
+    /* Set the number of the last paragraph */
+    PspBlock->LastParagraph = Segment + ProgramSize - 1;
+
+    /* Save the interrupt vectors */
+    PspBlock->TerminateAddress = IntVecTable[0x22];
+    PspBlock->BreakAddress     = IntVecTable[0x23];
+    PspBlock->CriticalAddress  = IntVecTable[0x24];
+
+    /* Set the parent PSP */
+    PspBlock->ParentPsp = CurrentPsp;
+
+    /* No environment block yet */
+    PspBlock->EnvBlock = 0;
+
+    /* Copy the parent handle table */
+    DosCopyHandleTable(PspBlock->HandleTable);
+
+    /* Set the handle table pointers to the internal handle table */
+    PspBlock->HandleTableSize = DEFAULT_JFT_SIZE;
+    PspBlock->HandleTablePtr = MAKELONG(0x18, Segment);
+
+    /* Set the DOS version */
+    PspBlock->DosVersion = DOS_VERSION;
+
+    /* Set the far call opcodes */
+    PspBlock->FarCall[0] = 0xCD; // int 0x21
+    PspBlock->FarCall[1] = 0x21;
+    PspBlock->FarCall[2] = 0xCB; // retf
+}
+
+VOID DosSetProcessContext(WORD Segment)
+{
+    CurrentPsp = Segment;
+    DiskTransferArea = MAKELONG(0x80, Segment);
+}
+
+DWORD DosLoadExecutable(IN DOS_EXEC_TYPE LoadType,
+                        IN LPCSTR ExecutablePath,
+                        IN PDOS_EXEC_PARAM_BLOCK Parameters,
+                        IN LPCSTR CommandLine OPTIONAL,
+                        IN LPCSTR Environment OPTIONAL,
+                        IN DWORD ReturnAddress OPTIONAL)
+{
+    DWORD Result = ERROR_SUCCESS;
+    HANDLE FileHandle = INVALID_HANDLE_VALUE, FileMapping = NULL;
+    LPBYTE Address = NULL;
+    WORD Segment = 0;
+    WORD EnvBlock = 0;
+    WORD LoadSegment;
+    WORD MaxAllocSize;
+    DWORD i, FileSize;
+
+    DPRINT1("DosLoadExecutable(%d, %s, 0x%08X, 0x%08X)\n",
+            LoadType,
+            ExecutablePath,
+            Parameters,
+            ReturnAddress);
+
+    /* Open a handle to the executable */
+    FileHandle = CreateFileA(ExecutablePath,
+                             GENERIC_READ,
+                             FILE_SHARE_READ,
+                             NULL,
+                             OPEN_EXISTING,
+                             FILE_ATTRIBUTE_NORMAL,
+                             NULL);
+    if (FileHandle == INVALID_HANDLE_VALUE)
+    {
+        Result = GetLastError();
+        goto Cleanup;
+    }
+
+    /* Get the file size */
+    FileSize = GetFileSize(FileHandle, NULL);
+
+    /* Create a mapping object for the file */
+    FileMapping = CreateFileMapping(FileHandle,
+                                    NULL,
+                                    PAGE_READONLY,
+                                    0,
+                                    0,
+                                    NULL);
+    if (FileMapping == NULL)
+    {
+        Result = GetLastError();
+        goto Cleanup;
+    }
+
+    /* Map the file into memory */
+    Address = (LPBYTE)MapViewOfFile(FileMapping, FILE_MAP_READ, 0, 0, 0);
+    if (Address == NULL)
+    {
+        Result = GetLastError();
+        goto Cleanup;
+    }
+
+    if (LoadType != DOS_LOAD_OVERLAY)
+    {
+        LPSTR CmdLinePtr;
+
+        if (CommandLine == NULL)
+        {
+            /* Get the command line from the parameter block */
+            CommandLine = (LPCSTR)FAR_POINTER(Parameters->CommandLine);
+        }
+
+        if (Environment == NULL)
+        {
+            /* Get the environment from the parameter block */
+            Environment = (LPCSTR)SEG_OFF_TO_PTR(Parameters->Environment, 0);
+        }
+
+        /* NULL-terminate the command line by removing the return carriage character */
+        CmdLinePtr = (LPSTR)CommandLine;
+        while (*CmdLinePtr && *CmdLinePtr != '\r') CmdLinePtr++;
+        *CmdLinePtr = '\0';
+
+        /* Copy the environment block to DOS memory */
+        EnvBlock = DosCopyEnvironmentBlock(Environment, ExecutablePath);
+        if (EnvBlock == 0)
+        {
+            Result = ERROR_NOT_ENOUGH_MEMORY;
+            goto Cleanup;
+        }
+    }
+
+    /* Check if this is an EXE file or a COM file */
+    if (Address[0] == 'M' && Address[1] == 'Z')
+    {
+        /* EXE file */
+        PIMAGE_DOS_HEADER Header;
+        DWORD BaseSize;
+        PDWORD RelocationTable;
+        PWORD RelocWord;
+        WORD RelocFactor;
+        BOOLEAN LoadHigh = FALSE;
+
+        /* Get the MZ header */
+        Header = (PIMAGE_DOS_HEADER)Address;
+
+        /* Get the base size of the file, in paragraphs (rounded up) */
+        BaseSize = (((Header->e_cp - 1) * 512) + Header->e_cblp + 0x0F) >> 4;
+
+        if (LoadType != DOS_LOAD_OVERLAY)
+        {
+            DWORD TotalSize = BaseSize;
+
+            /* Add the PSP size, in paragraphs */
+            TotalSize += sizeof(DOS_PSP) >> 4;
+
+            /* Add the maximum size that should be allocated */
+            TotalSize += Header->e_maxalloc;
+
+            if (Header->e_minalloc == 0 && Header->e_maxalloc == 0)
+            {
+                /* This program should be loaded high */
+                LoadHigh = TRUE;
+                TotalSize = 0xFFFF;
+            }
+
+            /* Make sure it does not pass 0xFFFF */
+            if (TotalSize > 0xFFFF) TotalSize = 0xFFFF;
+
+            /* Try to allocate that much memory */
+            Segment = DosAllocateMemory((WORD)TotalSize, &MaxAllocSize);
+
+            if (Segment == 0)
+            {
+                /* Check if there's at least enough memory for the minimum size */
+                if (MaxAllocSize < (BaseSize + (sizeof(DOS_PSP) >> 4) + Header->e_minalloc))
+                {
+                    Result = DosLastError;
+                    goto Cleanup;
+                }
+
+                /* Allocate that minimum amount */
+                TotalSize = MaxAllocSize;
+                Segment = DosAllocateMemory((WORD)TotalSize, NULL);
+                ASSERT(Segment != 0);
+            }
+
+            /* The process owns its own memory */
+            DosChangeMemoryOwner(Segment, Segment);
+            DosChangeMemoryOwner(EnvBlock, Segment);
+
+            /* Set INT 22h to the return address */
+            ((PULONG)BaseAddress)[0x22] = ReturnAddress;
+
+            /* Create the PSP */
+            DosCreatePsp(Segment, (WORD)TotalSize);
+            DosSetPspCommandLine(Segment, CommandLine);
+            SEGMENT_TO_PSP(Segment)->EnvBlock = EnvBlock;
+
+            /* Calculate the segment where the program should be loaded */
+            if (!LoadHigh) LoadSegment = Segment + (sizeof(DOS_PSP) >> 4);
+            else LoadSegment = Segment + TotalSize - BaseSize;
+
+            RelocFactor = LoadSegment;
+        }
+        else
+        {
+            LoadSegment = Parameters->Overlay.Segment;
+            RelocFactor = Parameters->Overlay.RelocationFactor;
+        }
+
+        /* Copy the program to the code segment */
+        RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
+                      Address + (Header->e_cparhdr << 4),
+                      min(FileSize - (Header->e_cparhdr << 4), BaseSize << 4));
+
+        /* Get the relocation table */
+        RelocationTable = (PDWORD)(Address + Header->e_lfarlc);
+
+        /* Perform relocations */
+        for (i = 0; i < Header->e_crlc; i++)
+        {
+            /* Get a pointer to the word that needs to be patched */
+            RelocWord = (PWORD)SEG_OFF_TO_PTR(LoadSegment + HIWORD(RelocationTable[i]),
+                                              LOWORD(RelocationTable[i]));
+
+            /* Add the relocation factor to it */
+            *RelocWord += RelocFactor;
+        }
+
+        if (LoadType == DOS_LOAD_AND_EXECUTE)
+        {
+            /* Set the initial segment registers */
+            setDS(Segment);
+            setES(Segment);
+
+            /* Set the stack to the location from the header */
+            setSS(LoadSegment + Header->e_ss);
+            setSP(Header->e_sp);
+
+            /* Execute */
+            DosSetProcessContext(Segment);
+            CpuExecute(LoadSegment + Header->e_cs, Header->e_ip);
+        }
+        else if (LoadType == DOS_LOAD_ONLY)
+        {
+            Parameters->StackLocation = MAKELONG(Header->e_sp, LoadSegment + Header->e_ss);
+            Parameters->EntryPoint = MAKELONG(Header->e_ip, LoadSegment + Header->e_cs);
+        }
+    }
+    else
+    {
+        /* COM file */
+
+        if (LoadType != DOS_LOAD_OVERLAY)
+        {
+            /* Find the maximum amount of memory that can be allocated */
+            DosAllocateMemory(0xFFFF, &MaxAllocSize);
+
+            /* Make sure it's enough for the whole program and the PSP */
+            if (((DWORD)MaxAllocSize << 4) < (FileSize + sizeof(DOS_PSP)))
+            {
+                Result = ERROR_NOT_ENOUGH_MEMORY;
+                goto Cleanup;
+            }
+
+            /* Allocate all of it */
+            Segment = DosAllocateMemory(MaxAllocSize, NULL);
+            if (Segment == 0)
+            {
+                Result = DosLastError;
+                goto Cleanup;
+            }
+
+            /* The process owns its own memory */
+            DosChangeMemoryOwner(Segment, Segment);
+            DosChangeMemoryOwner(EnvBlock, Segment);
+
+            /* Set INT 22h to the return address */
+            ((PULONG)BaseAddress)[0x22] = ReturnAddress;
+
+            /* Create the PSP */
+            DosCreatePsp(Segment, MaxAllocSize);
+            DosSetPspCommandLine(Segment, CommandLine);
+            SEGMENT_TO_PSP(Segment)->EnvBlock = EnvBlock;
+
+            /* Calculate the segment where the program should be loaded */
+            LoadSegment = Segment + (sizeof(DOS_PSP) >> 4);
+        }
+        else
+        {
+            LoadSegment = Parameters->Overlay.Segment;
+        }
+
+        RtlCopyMemory(SEG_OFF_TO_PTR(LoadSegment, 0),
+                      Address,
+                      FileSize);
+
+        if (LoadType == DOS_LOAD_AND_EXECUTE)
+        {
+            /* Set the initial segment registers */
+            setDS(Segment);
+            setES(Segment);
+
+            /* Set the stack to the last word of the segment */
+            setSS(Segment);
+            setSP(0xFFFE);
+
+            /*
+             * Set the value on the stack to 0, so that a near return
+             * jumps to PSP:0000 which has the exit code.
+             */
+            *((LPWORD)SEG_OFF_TO_PTR(Segment, 0xFFFE)) = 0;
+
+            /* Execute */
+            DosSetProcessContext(Segment);
+            CpuExecute(Segment, 0x100);
+        }
+        else if (LoadType == DOS_LOAD_ONLY)
+        {
+            Parameters->StackLocation = MAKELONG(0xFFFE, Segment);
+            Parameters->EntryPoint = MAKELONG(0x0100, Segment);
+        }
+    }
+
+Cleanup:
+    if (Result != ERROR_SUCCESS)
+    {
+        /* It was not successful, cleanup the DOS memory */
+        if (EnvBlock) DosFreeMemory(EnvBlock);
+        if (Segment) DosFreeMemory(Segment);
+    }
+
+    /* Unmap the file*/
+    if (Address != NULL) UnmapViewOfFile(Address);
+
+    /* Close the file mapping object */
+    if (FileMapping != NULL) CloseHandle(FileMapping);
+
+    /* Close the file handle */
+    if (FileHandle != INVALID_HANDLE_VALUE) CloseHandle(FileHandle);
+
+    return Result;
+}
+
+DWORD DosStartProcess(IN LPCSTR ExecutablePath,
+                      IN LPCSTR CommandLine,
+                      IN LPCSTR Environment OPTIONAL)
+{
+    DWORD Result;
+    LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
+
+    Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
+                               ExecutablePath,
+                               NULL,
+                               CommandLine,
+                               Environment,
+                               IntVecTable[0x20]);
+
+    if (Result != ERROR_SUCCESS) goto Quit;
+
+    /* Attach to the console */
+    ConsoleAttach();
+    VidBiosAttachToConsole();
+
+    // HACK: Simulate a ENTER key release scancode on the PS/2 port because
+    // some apps expect to read a key release scancode (> 0x80) when they
+    // are started.
+    IOWriteB(PS2_CONTROL_PORT, 0xD2);     // Next write is for the first PS/2 port
+    IOWriteB(PS2_DATA_PORT, 0x80 | 0x1C); // ENTER key release
+
+    /* Start simulation */
+    SetEvent(VdmTaskEvent);
+    CpuSimulate();
+
+    /* Detach from the console */
+    VidBiosDetachFromConsole();
+    ConsoleDetach();
+
+Quit:
+    return Result;
+}
+
+#ifndef STANDALONE
+WORD DosCreateProcess(LPCSTR ProgramName,
+                      PDOS_EXEC_PARAM_BLOCK Parameters,
+                      DWORD ReturnAddress)
+{
+    DWORD Result;
+    DWORD BinaryType;
+    LPVOID Environment = NULL;
+    VDM_COMMAND_INFO CommandInfo;
+    CHAR CmdLine[MAX_PATH];
+    CHAR AppName[MAX_PATH];
+    CHAR PifFile[MAX_PATH];
+    CHAR Desktop[MAX_PATH];
+    CHAR Title[MAX_PATH];
+    ULONG EnvSize = 256;
+    PVOID Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
+    STARTUPINFOA StartupInfo;
+    PROCESS_INFORMATION ProcessInfo;
+
+    /* Get the binary type */
+    if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
+
+    /* Did the caller specify an environment segment? */
+    if (Parameters->Environment)
+    {
+        /* Yes, use it instead of the parent one */
+        Environment = SEG_OFF_TO_PTR(Parameters->Environment, 0);
+    }
+
+    /* Set up the startup info structure */
+    RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
+    StartupInfo.cb = sizeof(StartupInfo);
+
+    /* Create the process */
+    if (!CreateProcessA(ProgramName,
+                        FAR_POINTER(Parameters->CommandLine),
+                        NULL,
+                        NULL,
+                        FALSE,
+                        0,
+                        Environment,
+                        NULL,
+                        &StartupInfo,
+                        &ProcessInfo))
+    {
+        return GetLastError();
+    }
+
+    /* Check the type of the program */
+    switch (BinaryType)
+    {
+        /* These are handled by NTVDM */
+        case SCS_DOS_BINARY:
+        case SCS_WOW_BINARY:
+        {
+            /* Clear the structure */
+            RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
+
+            /* Initialize the structure members */
+            CommandInfo.TaskId = SessionId;
+            CommandInfo.VDMState = VDM_FLAG_NESTED_TASK | VDM_FLAG_DONT_WAIT;
+            CommandInfo.CmdLine = CmdLine;
+            CommandInfo.CmdLen = sizeof(CmdLine);
+            CommandInfo.AppName = AppName;
+            CommandInfo.AppLen = sizeof(AppName);
+            CommandInfo.PifFile = PifFile;
+            CommandInfo.PifLen = sizeof(PifFile);
+            CommandInfo.Desktop = Desktop;
+            CommandInfo.DesktopLen = sizeof(Desktop);
+            CommandInfo.Title = Title;
+            CommandInfo.TitleLen = sizeof(Title);
+            CommandInfo.Env = Env;
+            CommandInfo.EnvLen = EnvSize;
+
+Command:
+            /* Get the VDM command information */
+            if (!GetNextVDMCommand(&CommandInfo))
+            {
+                if (CommandInfo.EnvLen > EnvSize)
+                {
+                    /* Expand the environment size */
+                    EnvSize = CommandInfo.EnvLen;
+                    CommandInfo.Env = Env = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Env, EnvSize);
+
+                    /* Repeat the request */
+                    CommandInfo.VDMState |= VDM_FLAG_RETRY;
+                    goto Command;
+                }
+
+                /* Shouldn't happen */
+                ASSERT(FALSE);
+            }
+
+            /* Load the executable */
+            Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
+                                       AppName,
+                                       Parameters,
+                                       CmdLine,
+                                       Env,
+                                       ReturnAddress);
+            if (Result == ERROR_SUCCESS)
+            {
+                /* Increment the re-entry count */
+                CommandInfo.VDMState = VDM_INC_REENTER_COUNT;
+                GetNextVDMCommand(&CommandInfo);
+            }
+            else
+            {
+                DisplayMessage(L"Could not load '%S'. Error: %u", AppName, Result);
+            }
+
+            break;
+        }
+
+        /* Not handled by NTVDM */
+        default:
+        {
+            /* Wait for the process to finish executing */
+            WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
+        }
+    }
+
+    RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
+
+    /* Close the handles */
+    CloseHandle(ProcessInfo.hProcess);
+    CloseHandle(ProcessInfo.hThread);
+
+    return ERROR_SUCCESS;
+}
+#endif
+
+VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
+{
+    WORD i;
+    WORD McbSegment = FIRST_MCB_SEGMENT;
+    PDOS_MCB CurrentMcb;
+    LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
+    PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
+
+    DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
+           Psp,
+           ReturnCode,
+           KeepResident);
+
+    /* Check if this PSP is it's own parent */
+    if (PspBlock->ParentPsp == Psp) goto Done;
+
+    if (KeepResident == 0)
+    {
+        for (i = 0; i < PspBlock->HandleTableSize; i++)
+        {
+            /* Close the handle */
+            DosCloseHandle(i);
+        }
+    }
+
+    /* Free the memory used by the process */
+    while (TRUE)
+    {
+        /* Get a pointer to the MCB */
+        CurrentMcb = SEGMENT_TO_MCB(McbSegment);
+
+        /* Make sure the MCB is valid */
+        if (CurrentMcb->BlockType != 'M' && CurrentMcb->BlockType != 'Z') break;
+
+        /* Check if this block was allocated by the process */
+        if (CurrentMcb->OwnerPsp == Psp)
+        {
+            if (KeepResident == 0)
+            {
+                /* Free this entire block */
+                DosFreeMemory(McbSegment + 1);
+            }
+            else if (KeepResident < CurrentMcb->Size)
+            {
+                /* Reduce the size of the block */
+                DosResizeMemory(McbSegment + 1, KeepResident, NULL);
+
+                /* No further paragraphs need to stay resident */
+                KeepResident = 0;
+            }
+            else
+            {
+                /* Just reduce the amount of paragraphs we need to keep resident */
+                KeepResident -= CurrentMcb->Size;
+            }
+        }
+
+        /* If this was the last block, quit */
+        if (CurrentMcb->BlockType == 'Z') break;
+
+        /* Update the segment and continue */
+        McbSegment += CurrentMcb->Size + 1;
+    }
+
+Done:
+    /* Restore the interrupt vectors */
+    IntVecTable[0x22] = PspBlock->TerminateAddress;
+    IntVecTable[0x23] = PspBlock->BreakAddress;
+    IntVecTable[0x24] = PspBlock->CriticalAddress;
+
+    /* Update the current PSP */
+    if (Psp == CurrentPsp)
+    {
+        CurrentPsp = PspBlock->ParentPsp;
+        if (CurrentPsp == SYSTEM_PSP)
+        {
+            ResetEvent(VdmTaskEvent);
+            CpuUnsimulate();
+        }
+    }
+
+#ifndef STANDALONE
+    // FIXME: This is probably not the best way to do it
+    /* Check if this was a nested DOS task */
+    if (CurrentPsp != SYSTEM_PSP)
+    {
+        VDM_COMMAND_INFO CommandInfo;
+
+        /* Decrement the re-entry count */
+        CommandInfo.TaskId = SessionId;
+        CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
+        GetNextVDMCommand(&CommandInfo);
+
+        /* Clear the structure */
+        RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
+
+        /* Update the VDM state of the task */
+        CommandInfo.TaskId = SessionId;
+        CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
+        GetNextVDMCommand(&CommandInfo);
+    }
+#endif
+
+    /* Save the return code - Normal termination */
+    DosErrorLevel = MAKEWORD(ReturnCode, 0x00);
+
+    /* Return control to the parent process */
+    CpuExecute(HIWORD(PspBlock->TerminateAddress),
+               LOWORD(PspBlock->TerminateAddress));
+}
+
diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/process.h b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/process.h
new file mode 100644 (file)
index 0000000..a9f5e7d
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * COPYRIGHT:       GPLv2 - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            dos/dos32krnl/process.h
+ * PURPOSE:         DOS32 Processes
+ * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ */
+
+/* DEFINITIONS ****************************************************************/
+
+#define DOS_CMDLINE_LENGTH 127
+#define DOS_PROGRAM_NAME_TAG 0x0001
+
+#define SEGMENT_TO_PSP(seg) ((PDOS_PSP)((ULONG_PTR)BaseAddress + TO_LINEAR((seg), 0)))
+
+typedef enum
+{
+    DOS_LOAD_AND_EXECUTE = 0x00,
+    DOS_LOAD_ONLY = 0x01,
+    DOS_LOAD_OVERLAY = 0x03
+} DOS_EXEC_TYPE;
+
+#pragma pack(push, 1)
+
+typedef struct _DOS_PSP
+{
+    BYTE Exit[2];
+    WORD LastParagraph;
+    BYTE Reserved0[6];
+    DWORD TerminateAddress;
+    DWORD BreakAddress;
+    DWORD CriticalAddress;
+    WORD ParentPsp;
+    BYTE HandleTable[20];
+    WORD EnvBlock;
+    DWORD LastStack;
+    WORD HandleTableSize;
+    DWORD HandleTablePtr;
+    DWORD PreviousPsp;
+    DWORD Reserved1;
+    WORD DosVersion;
+    BYTE Reserved2[14];
+    BYTE FarCall[3];
+    BYTE Reserved3[9];
+    DOS_FCB Fcb;
+    BYTE CommandLineSize;
+    CHAR CommandLine[DOS_CMDLINE_LENGTH];
+} DOS_PSP, *PDOS_PSP;
+
+typedef struct _DOS_EXEC_PARAM_BLOCK
+{
+    union
+    {
+        struct
+        {
+            /* Input variables */
+            WORD Environment;
+            DWORD CommandLine;
+            DWORD FirstFcb;
+            DWORD SecondFcb;
+
+            /* Output variables */
+            DWORD StackLocation;
+            DWORD EntryPoint;
+        };
+
+        struct
+        {
+            WORD Segment;
+            WORD RelocationFactor;
+        } Overlay;
+    };
+} DOS_EXEC_PARAM_BLOCK, *PDOS_EXEC_PARAM_BLOCK;
+
+#pragma pack(pop)
+
+/* VARIABLES ******************************************************************/
+
+extern WORD CurrentPsp;
+
+/* FUNCTIONS ******************************************************************/
+
+VOID DosClonePsp(WORD DestSegment, WORD SourceSegment);
+VOID DosCreatePsp(WORD Segment, WORD ProgramSize);
+VOID DosSetProcessContext(WORD Segment);
+
+DWORD DosLoadExecutable
+(
+    IN DOS_EXEC_TYPE LoadType,
+    IN LPCSTR ExecutablePath,
+    IN PDOS_EXEC_PARAM_BLOCK Parameters,
+    IN LPCSTR CommandLine OPTIONAL,
+    IN LPCSTR Environment OPTIONAL,
+    IN DWORD ReturnAddress OPTIONAL
+);
+
+DWORD DosStartProcess(
+    IN LPCSTR ExecutablePath,
+    IN LPCSTR CommandLine,
+    IN LPCSTR Environment OPTIONAL
+);
+
+WORD DosCreateProcess
+(
+    LPCSTR ProgramName,
+    PDOS_EXEC_PARAM_BLOCK Parameters,
+    DWORD ReturnAddress
+);
+
+VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident);