--- /dev/null
+/*
+ * COPYRIGHT: GPL - See COPYING in the top level directory
+ * PROJECT: ReactOS Virtual DOS Machine
+ * FILE: command.S
+ * PURPOSE: DOS32 command.com for NTVDM
+ * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
+ */
+
+//
+// See http://www.ganino.com/games/scripts/dos-6.0%20source%20code%3F/dev/smartdrv/umbload.asm
+// and https://books.google.fr/books?id=rtmJgtfaxz8C&pg=PA256&lpg=PA256&dq=int+21+4b+program+exec+block&source=bl&ots=OfAF7qFwfl&sig=PrW73CE1dzFm3rwrTsYZkC77U4Y&hl=en&sa=X&redir_esc=y#v=onepage&q=int%2021%204b%20program%20exec%20block&f=false
+//
+
+/* INCLUDES *******************************************************************/
+
+#include <asm.inc>
+#include "asmxtras.inc"
+#include <isvbop.inc>
+
+/* DEFINES ********************************************************************/
+
+#define MAX_PATH 260
+#define DOS_CMDLINE_LENGTH 127
+
+#define DOS_VERSION HEX(0005) // MAKEWORD(5, 00)
+#define NTDOS_VERSION HEX(3205) // MAKEWORD(5, 50)
+
+#define SYSTEM_PSP HEX(08)
+
+#define BOP .byte HEX(C4), HEX(C4),
+// #define BOP_START_DOS HEX(2C)
+#define BOP_CMD HEX(54)
+
+/**** PSP MEMBERS ****/
+#define PSP_VAR(x) es:[x]
+#define PSP HEX(0000)
+#define ParentPsp HEX(0016) // word
+#define EnvBlock HEX(002C) // word
+#define CmdLineLen HEX(0080) // byte
+#define CmdLineStr HEX(0081) // byte[DOS_CMDLINE_LENGTH]
+
+/**** DATA stored inside the stack ****/
+#define CmdLine BaseStack // Command line for the program to be started
+#define PgmName [CmdLine + (1 + DOS_CMDLINE_LENGTH)]
+
+
+// WARNING! Using the VAL(x) macro doesn't work with GCC/GAS (because it inserts
+// a spurious space in front of the parameter when the macro is expanded).
+// So that: 'VAL(foo)' is expanded to: '\ foo', instead of '\foo' as one would expect.
+
+/* NEXT_CMD structure */
+STRUCT(NEXT_CMD, 2)
+ FIELD_DECL(EnvBlockSeg, word, 0)
+ FIELD_DECL(EnvBlockLen, word, 0)
+ FIELD_DECL(CurDrive , word, 0)
+ FIELD_DECL(NumDrives , word, 1)
+ FIELD_DECL(CmdLineSeg , word, 0)
+ FIELD_DECL(CmdLineOff , word, 0)
+ FIELD_DECL(Unknown0 , word, 2)
+ FIELD_DECL(ExitCode , word, 3)
+ FIELD_DECL(Unknown1 , word, 4)
+ FIELD_DECL(Unknown2 , long, 5)
+ FIELD_DECL(CodePage , word, 6)
+ FIELD_DECL(Unknown3 , word, 7)
+ FIELD_DECL(Unknown4 , word, 8)
+ FIELD_DECL(AppNameSeg , word, 0)
+ FIELD_DECL(AppNameOff , word, 0)
+ FIELD_DECL(AppNameLen , word, 0)
+ FIELD_DECL(Flags , word, 0)
+ENDS(NEXT_CMD)
+
+/* DOS_EXEC_PARAM_BLOCK structure */
+STRUCT(DOS_EXEC_PARAM_BLOCK, 2)
+ FIELD_DECL(EnvSeg, word, 0) // Use parent's environment (ours)
+ FIELD_DECL(CmdLineOff, word, OFF(CmdLine))
+ FIELD_DECL(CmdLineSeg, word, 0)
+ FIELD_DECL(Fcb1Off, word, OFF(Fcb1))
+ FIELD_DECL(Fcb1Seg, word, 0)
+ FIELD_DECL(Fcb2Off, word, OFF(Fcb2))
+ FIELD_DECL(Fcb2Seg, word, 0)
+ENDS(DOS_EXEC_PARAM_BLOCK)
+
+
+
+/* RESIDENT CODE AND DATA *****************************************************/
+
+.code16
+// .org HEX(0100)
+ASSUME CS:.text, DS:.text, ES:.text
+
+
+/* CODE *******************************/
+
+EntryPoint:
+ jmp Main
+.align 2
+
+ResidentMain:
+ /*
+ * Relocate our stack.
+ * Our local stack is big enough for holding the command line and the path
+ * to the executable to start, plus a full DOS_REGISTER_STATE and for one pusha 16-bit.
+ *
+ * FIXME: enlarge it for pushing the needed Load&Exec data.
+ */
+ cli
+ mov ax, ds
+ mov ss, ax
+ mov bp, offset BaseStack
+ lea sp, [bp + (1 + DOS_CMDLINE_LENGTH) + MAX_PATH + 255]
+ sti
+
+ /* Resize ourselves */
+ mov bx, sp // Get size in bytes...
+// sub bx, offset PSP_VAR(PSP)
+ add bx, HEX(0F) // (for rounding to the next paragraph)
+ shr bx, 4 // ... then the number of paragraphs
+ mov ah, HEX(4A)
+ int HEX(21)
+
+ /* Check whether we need to start the 32-bit command interpreter */
+ BOP BOP_CMD, HEX(10) // Check whether we were started from a new console (SessionId != 0)
+ test al, al // and we are not reentering (32-bit process starting a 16-bit process).
+ jz Run
+ cmp word ptr OldParentPsp, SYSTEM_PSP // Check whether our parent is SYSTEM
+ je Run
+/********************************/
+ mov dx, offset Msg1
+ mov ah, HEX(09)
+ int HEX(21)
+/********************************/
+ BOP BOP_CMD, HEX(0A) // Start 32-bit COMSPEC
+ jnc Quit
+
+ /* Loop for new commands to run */
+Run:
+ /* Initialize the NextCmd structure */
+ mov word ptr FIELD(NextCmd, EnvBlockSeg), ds
+ mov word ptr FIELD(NextCmd, EnvBlockLen), 0 // FIXME
+ mov word ptr FIELD(NextCmd, CmdLineSeg), ds
+ mov word ptr FIELD(NextCmd, CmdLineOff), offset CmdLine
+ mov word ptr FIELD(NextCmd, AppNameSeg), ds
+ mov word ptr FIELD(NextCmd, AppNameOff), offset PgmName
+
+ /* Wait for the next command */
+/********************************/
+ mov dx, offset Msg2
+ mov ah, HEX(09)
+ int HEX(21)
+/********************************/
+ // FIXME: Initialize memory with structure for holding CmdLine etc...
+// mov ds, seg NextCmd
+ mov dx, offset NextCmd
+ BOP BOP_CMD, HEX(01)
+ /* Quit if we shell-out */
+ jc Quit
+
+ /* Initialize the DosLoadExec structure */
+// mov word ptr FIELD(DosLoadExec, EnvSeg), 0
+ mov word ptr FIELD(DosLoadExec, CmdLineSeg), ds
+ mov word ptr FIELD(DosLoadExec, Fcb1Seg), ds
+ mov word ptr FIELD(DosLoadExec, Fcb2Seg), ds
+
+ /* Run the command */
+ mov ds, word ptr FIELD(NextCmd, AppNameSeg)
+ mov dx, word ptr FIELD(NextCmd, AppNameOff)
+// mov es, seg DosLoadExec
+ mov bx, offset DosLoadExec
+ pusha // Save the registers in case stuff go wrong
+ // FIXME: Save also SS !!
+ mov ax, HEX(4B00)
+ int HEX(21)
+ popa // Restore the registers
+ // FIXME: Restore also SS !!
+
+ /* Retrieve and set its exit code. Also detect whether
+ * we need to continue or whether we need to quit. */
+ // xor ax, ax
+ // mov ah, HEX(4D)
+ mov ax, HEX(4D00)
+ int HEX(21)
+ /* Send exit code back to NTVDM */
+ mov dx, ax
+ BOP BOP_CMD, HEX(0B)
+
+ /* If we don't shell-out, go and get a new app! */
+ jc Run
+
+ mov al, HEX(00) // ERROR_SUCCESS
+
+Quit:
+ mov bl, al // Save AL in BL
+/********************************/
+ cmp al, HEX(0A)
+ jne XXXX
+ mov dx, offset Msg3
+ mov ah, HEX(09)
+ int HEX(21)
+XXXX:
+/********************************/
+ /* Say bye-bye */
+// mov ds, seg QuitMsg
+ mov dx, offset QuitMsg
+ mov ah, HEX(09)
+ int HEX(21)
+
+ /* Restore our old parent PSP */
+ mov ax, word ptr OldParentPsp
+ mov PSP_VAR(ParentPsp), ax
+
+ mov al, bl // Restore AL from BL
+
+Exit:
+ /* Return to caller (with possible error code in AL) */
+ mov ah, HEX(4C)
+ int HEX(21)
+ int 3
+
+ /* Does not return */
+
+/* DATA *******************************/
+
+QuitMsg:
+ .ascii "Bye bye!", CR, LF, "$"
+
+/********************************/
+Msg1: .ascii "Starting COMSPEC...", CR, LF, "$"
+Msg2: .ascii "Waiting for new command...", CR, LF, "$"
+Msg3: .ascii "Bad environment!", CR, LF, "$"
+/********************************/
+
+OldParentPsp: .word 0
+CurrentPsp: .word 0
+
+// BOP_CMD, HEX(01) "Get a new app to start" structure
+VAR_STRUCT(NextCmd, NEXT_CMD)
+
+// DOS INT 21h, AH=4Bh "Load and Execute" structure
+VAR_STRUCT(DosLoadExec, DOS_EXEC_PARAM_BLOCK)
+
+// Blank FCB blocks needed for DOS INT 21h, AH=4Bh
+Fcb1:
+ .byte 0
+ .space 11, ' '
+ .space 25, 0
+Fcb2:
+ .byte 0
+ .space 11, ' '
+ .space 25, 0
+
+// The stack resides at the end of the resident code+data
+// and it overwrites the transient part.
+BaseStack:
+
+
+/* TRANSIENT CODE AND DATA ****************************************************/
+
+/* DATA *******************************/
+
+WelcomeMsg:
+ .ascii "ReactOS DOS32 Command", CR, LF, \
+ "Copyright (C) ReactOS Team 2015" , CR, LF, "$"
+VerErrMsg:
+ .ascii "Incorrect DOS version", CR, LF, "$"
+
+/* CODE *******************************/
+
+.align 2
+
+Main:
+ /* Setup segment registers */
+ mov ax, cs // cs contains the PSP segment on entry
+ mov ds, ax
+ mov es, ax
+// mov fs, ax
+// mov gs, ax
+ /* Stack is set to cs:FFFE down to cs:09xx by the DOS.
+ * We will need to relocate it before we resize ourselves. */
+
+ /*
+ * Verify DOS version:
+ * - Check whether we are on DOS 5+ (subject to SETVER);
+ * - If so, check our real DOS version and see
+ * whether we are running on NTVDM (version 5.50).
+ */
+ mov ax, HEX(3000)
+ int HEX(21)
+ cmp ax, DOS_VERSION // AH:AL contains minor:major version number
+ jne VerErr
+
+ mov ax, HEX(3306)
+ int HEX(21)
+ cmp bx, NTDOS_VERSION // BH:BL contains minor:major version number
+ je Continue
+
+VerErr:
+ /* Display wrong version error message and exit */
+// mov ds, seg VerErrMsg
+ mov dx, offset VerErrMsg
+ mov ah, HEX(09)
+ int HEX(21)
+ jmp Exit
+
+Continue:
+ /* Save our PSP */
+ mov word ptr CurrentPsp, cs
+/*
+ * The DOS way:
+ *
+ * mov ah, HEX(51) // DOS 2+ internal, or HEX(62) since DOS 3+
+ * int HEX(21)
+ * mov word ptr CurrentPsp, bx
+ */
+
+ /* Save our old parent PSP */
+ mov ax, PSP_VAR(ParentPsp)
+ mov word ptr OldParentPsp, ax
+
+ /* Give us shell privileges: set our PSP as our new parent */
+ mov ax, word ptr CurrentPsp
+ mov PSP_VAR(ParentPsp), ax
+
+ /* Say hello */
+// mov ds, seg WelcomeMsg
+ mov dx, offset WelcomeMsg
+ mov ah, HEX(09)
+ int HEX(21)
+
+ /* Jump to resident code */
+ jmp ResidentMain
+
+.endcode16
+END
+
+/* EOF */
#include "dem.h"
#include "dos/dos32krnl/device.h"
+#include "dos/dos32krnl/memory.h"
#include "dos/dos32krnl/process.h"
#include "cpu/bop.h"
+#include "cpu/cpu.h"
#include "bios/bios.h"
#include "mouse32.h"
-/* PRIVATE VARIABLES **********************************************************/
+#include "vddsup.h"
+
+/*
+ * EXPERIMENTAL!
+ * Activate this line if you want to have COMMAND.COM completely external.
+ */
+// #define COMSPEC_FULLY_EXTERNAL
-extern PDOS_DATA DosData;
+/* PRIVATE VARIABLES **********************************************************/
/* PRIVATE FUNCTIONS **********************************************************/
-static VOID WINAPI DosSystemBop(LPWORD Stack)
+/* PUBLIC VARIABLES ***********************************************************/
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+
+/******************************************************************************\
+|** DOS DEM Kernel helpers **|
+\******************************************************************************/
+
+
+VOID BiosCharPrint(CHAR Character)
{
- /* Get the Function Number and skip it */
- BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
- setIP(getIP() + 1);
+ /* Save AX and BX */
+ USHORT AX = getAX();
+ USHORT BX = getBX();
- switch (FuncNum)
+ /*
+ * Set the parameters:
+ * AL contains the character to print,
+ * BL contains the character attribute,
+ * BH contains the video page to use.
+ */
+ setAL(Character);
+ setBL(DEFAULT_ATTRIBUTE);
+ setBH(Bda->VideoPage);
+
+ /* Call the BIOS INT 10h, AH=0Eh "Teletype Output" */
+ setAH(0x0E);
+ Int32Call(&DosContext, BIOS_VIDEO_INTERRUPT);
+
+ /* Restore AX and BX */
+ setBX(BX);
+ setAX(AX);
+}
+
+VOID DosCharPrint(CHAR Character)
+{
+ DosPrintCharacter(DOS_OUTPUT_HANDLE, Character);
+}
+
+/*
+ * This function, derived from ntvdm.c!DisplayMessage, is used by the BIOS and
+ * the DOS to display messages to an output device. A printer function is given
+ * for printing the characters.
+ */
+static VOID
+DisplayMessageAnsiV(IN CHAR_PRINT CharPrint,
+ IN LPCSTR Format,
+ IN va_list args)
+{
+ static CHAR CurChar = 0;
+ LPSTR str;
+
+#ifndef WIN2K_COMPLIANT
+ CHAR StaticBuffer[256];
+ LPSTR Buffer = StaticBuffer; // Use the static buffer by default.
+#else
+ CHAR Buffer[2048]; // Large enough. If not, increase it by hand.
+#endif
+ size_t MsgLen;
+
+#ifndef WIN2K_COMPLIANT
+ /*
+ * Retrieve the message length and if it is too long, allocate
+ * an auxiliary buffer; otherwise use the static buffer.
+ * The string is built to be NULL-terminated.
+ */
+ MsgLen = _vscprintf(Format, args);
+ if (MsgLen >= ARRAYSIZE(StaticBuffer))
{
- /* Load the DOS kernel */
- case 0x11:
+ Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, (MsgLen + 1) * sizeof(CHAR));
+ if (Buffer == NULL)
{
- BOOLEAN Success = FALSE;
- LPCSTR DosKernelFileName = "ntdos.sys";
- HANDLE hDosKernel;
- ULONG ulDosKernelSize = 0;
+ /* Allocation failed, use the static buffer and display a suitable error message */
+ Buffer = StaticBuffer;
+ Format = "DisplayMessageAnsi()\nOriginal message is too long and allocating an auxiliary buffer failed.";
+ MsgLen = strlen(Format);
+ }
+ }
+#else
+ MsgLen = ARRAYSIZE(Buffer) - 1;
+#endif
- DPRINT1("You are loading Windows NT DOS!\n");
+ RtlZeroMemory(Buffer, (MsgLen + 1) * sizeof(CHAR));
+ _vsnprintf(Buffer, MsgLen, Format, args);
- /* Open the DOS kernel file */
- hDosKernel = FileOpen(DosKernelFileName, &ulDosKernelSize);
- if (hDosKernel == NULL) goto Quit;
+ /* Display the message */
+ DPRINT1("\n\nNTVDM DOS32\n%s\n\n", Buffer);
- /*
- * Attempt to load the DOS kernel into memory.
- * The segment where to load the DOS kernel is defined
- * by the DOS BIOS and is found in DI:0000 .
- */
- Success = FileLoadByHandle(hDosKernel,
- REAL_TO_PHYS(TO_LINEAR(getDI(), 0x0000)),
- ulDosKernelSize,
- &ulDosKernelSize);
+ MsgLen = strlen(Buffer);
+ str = Buffer;
+ while (MsgLen--)
+ {
+ if (*str == '\n' && CurChar != '\r')
+ CharPrint('\r');
+
+ CurChar = *str++;
+ CharPrint(CurChar);
+ }
+
+#ifndef WIN2K_COMPLIANT
+ /* Free the buffer if needed */
+ if (Buffer != StaticBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
+#endif
+}
+
+VOID
+DemDisplayMessage(IN CHAR_PRINT CharPrint,
+ IN LPCSTR Format, ...)
+{
+ va_list Parameters;
+
+ va_start(Parameters, Format);
+ DisplayMessageAnsiV(CharPrint, Format, Parameters);
+ va_end(Parameters);
+}
- DPRINT1("Windows NT DOS file '%s' loading %s at %04X:%04X, size 0x%X (Error: %u).\n",
- DosKernelFileName,
- (Success ? "succeeded" : "failed"),
- getDI(), 0x0000,
- ulDosKernelSize,
- GetLastError());
- /* Close the DOS kernel file */
- FileClose(hDosKernel);
+static VOID DemLoadNTDOSKernel(VOID)
+{
+ BOOLEAN Success = FALSE;
+ LPCSTR DosKernelFileName = "ntdos.sys";
+ HANDLE hDosKernel;
+ ULONG ulDosKernelSize = 0;
+
+ DPRINT1("You are loading Windows NT DOS!\n");
+
+ /* Open the DOS kernel file */
+ hDosKernel = FileOpen(DosKernelFileName, &ulDosKernelSize);
+ if (hDosKernel == NULL) goto Quit;
+
+ /*
+ * Attempt to load the DOS kernel into memory.
+ * The segment where to load the DOS kernel is defined
+ * by the DOS BIOS and is found in DI:0000 .
+ */
+ Success = FileLoadByHandle(hDosKernel,
+ REAL_TO_PHYS(TO_LINEAR(getDI(), 0x0000)),
+ ulDosKernelSize,
+ &ulDosKernelSize);
+
+ DPRINT1("Windows NT DOS file '%s' loading %s at %04X:%04X, size 0x%X (Error: %u).\n",
+ DosKernelFileName,
+ (Success ? "succeeded" : "failed"),
+ getDI(), 0x0000,
+ ulDosKernelSize,
+ GetLastError());
+
+ /* Close the DOS kernel file */
+ FileClose(hDosKernel);
Quit:
- if (!Success)
- {
- /* We failed everything, stop the VDM */
- DisplayMessage(L"Windows NT DOS kernel file '%S' loading failed (Error: %u). The VDM will shut down.",
- DosKernelFileName, GetLastError());
- EmulatorTerminate();
- return;
- }
+ if (!Success)
+ {
+ /* We failed everything, stop the VDM */
+ BiosDisplayMessage("Windows NT DOS kernel file '%s' loading failed (Error: %u). The VDM will shut down.\n",
+ DosKernelFileName, GetLastError());
+ EmulatorTerminate();
+ return;
+ }
+}
+
+static VOID WINAPI DosSystemBop(LPWORD Stack)
+{
+ /* Get the Function Number and skip it */
+ BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP());
+ setIP(getIP() + 1);
+ switch (FuncNum)
+ {
+ /* Load the DOS kernel */
+ case 0x11:
+ {
+ DemLoadNTDOSKernel();
break;
}
}
}
+
+
+
+/******************************************************************************\
+|** DOS Command Process management **|
+\******************************************************************************/
+
+
+#ifndef STANDALONE
+static ULONG SessionId = 0;
+
+/*
+ * 16-bit Command Interpreter information for DOS reentry
+ */
+typedef struct _COMSPEC_INFO
+{
+ LIST_ENTRY Entry;
+ DWORD dwExitCode;
+ WORD ComSpecPsp;
+ BOOLEAN Terminated;
+} COMSPEC_INFO, *PCOMSPEC_INFO;
+
+static COMSPEC_INFO RootCmd;
+static DWORD ReentrancyCount = 0;
+
+// FIXME: Should we need list locking?
+static LIST_ENTRY ComSpecInfoList = { &ComSpecInfoList, &ComSpecInfoList };
+
+static PCOMSPEC_INFO
+FindComSpecInfoByPsp(WORD Psp)
+{
+ PLIST_ENTRY Pointer;
+ PCOMSPEC_INFO ComSpecInfo;
+
+ for (Pointer = ComSpecInfoList.Flink; Pointer != &ComSpecInfoList; Pointer = Pointer->Flink)
+ {
+ ComSpecInfo = CONTAINING_RECORD(Pointer, COMSPEC_INFO, Entry);
+ if (ComSpecInfo->ComSpecPsp == Psp) return ComSpecInfo;
+ }
+
+ return NULL;
+}
+
+static VOID
+InsertComSpecInfo(PCOMSPEC_INFO ComSpecInfo)
+{
+ InsertHeadList(&ComSpecInfoList, &ComSpecInfo->Entry);
+}
+
+static VOID
+RemoveComSpecInfo(PCOMSPEC_INFO ComSpecInfo)
+{
+ RemoveEntryList(&ComSpecInfo->Entry);
+ if (ComSpecInfo != &RootCmd)
+ RtlFreeHeap(RtlGetProcessHeap(), 0, ComSpecInfo);
+}
+#endif
+
+static VOID DosProcessConsoleAttach(VOID)
+{
+ /* Attach to the console */
+ ConsoleAttach();
+ VidBiosAttachToConsole();
+}
+
+static VOID DosProcessConsoleDetach(VOID)
+{
+ /* Detach from the console */
+ VidBiosDetachFromConsole();
+ ConsoleDetach();
+}
+
+/*
+ * Data for the next DOS command to run
+ */
+#ifndef STANDALONE
+static VDM_COMMAND_INFO CommandInfo;
+static BOOLEAN Repeat = FALSE;
+static BOOLEAN Reentry = FALSE;
+#endif
+static BOOLEAN First = TRUE;
+static CHAR CmdLine[MAX_PATH] = ""; // DOS_CMDLINE_LENGTH
+static CHAR AppName[MAX_PATH] = "";
+#ifndef STANDALONE
+static CHAR PifFile[MAX_PATH] = "";
+static CHAR CurDirectory[MAX_PATH] = "";
+static CHAR Desktop[MAX_PATH] = "";
+static CHAR Title[MAX_PATH] = "";
+static ULONG EnvSize = 256;
+static PVOID Env = NULL;
+#endif
+
+#pragma pack(push, 2)
+
+/*
+ * This structure is compatible with Windows NT DOS
+ */
+typedef struct _NEXT_CMD
+{
+ USHORT EnvBlockSeg;
+ USHORT EnvBlockLen;
+ USHORT CurDrive;
+ USHORT NumDrives;
+ USHORT CmdLineSeg;
+ USHORT CmdLineOff;
+ USHORT Unknown0;
+ USHORT ExitCode;
+ USHORT Unknown1;
+ ULONG Unknown2;
+ USHORT CodePage;
+ USHORT Unknown3;
+ USHORT Unknown4;
+ USHORT AppNameSeg;
+ USHORT AppNameOff;
+ USHORT AppNameLen;
+ USHORT Flags;
+} NEXT_CMD, *PNEXT_CMD;
+
+#pragma pack(pop)
+
+static VOID CmdStartProcess(VOID)
+{
+#ifndef STANDALONE
+ PCOMSPEC_INFO ComSpecInfo;
+#endif
+ SIZE_T CmdLen;
+ PNEXT_CMD DataStruct = (PNEXT_CMD)SEG_OFF_TO_PTR(getDS(), getDX());
+
+ DPRINT1("CmdStartProcess -- DS:DX = %04X:%04X (DataStruct = 0x%p)\n",
+ getDS(), getDX(), DataStruct);
+
+ /* Pause the VM */
+ EmulatorPause();
+
+#ifndef STANDALONE
+ /* Check whether we need to shell out now in case we were started by a 32-bit app */
+ ComSpecInfo = FindComSpecInfoByPsp(Sda->CurrentPsp);
+ if (ComSpecInfo && ComSpecInfo->Terminated)
+ {
+ RemoveComSpecInfo(ComSpecInfo);
+
+ DPRINT1("Exit DOS from start-app BOP\n");
+ setCF(1);
+ goto Quit;
+ }
+
+ /* Clear the structure */
+ RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
+
+ /* Initialize the structure members */
+ CommandInfo.TaskId = SessionId;
+ CommandInfo.VDMState = VDM_FLAG_DOS;
+ CommandInfo.CmdLine = CmdLine;
+ CommandInfo.CmdLen = sizeof(CmdLine);
+ CommandInfo.AppName = AppName;
+ CommandInfo.AppLen = sizeof(AppName);
+ CommandInfo.PifFile = PifFile;
+ CommandInfo.PifLen = sizeof(PifFile);
+ CommandInfo.CurDirectory = CurDirectory;
+ CommandInfo.CurDirectoryLen = sizeof(CurDirectory);
+ CommandInfo.Desktop = Desktop;
+ CommandInfo.DesktopLen = sizeof(Desktop);
+ CommandInfo.Title = Title;
+ CommandInfo.TitleLen = sizeof(Title);
+ CommandInfo.Env = Env;
+ CommandInfo.EnvLen = EnvSize;
+
+ if (First) CommandInfo.VDMState |= VDM_FLAG_FIRST_TASK;
+
+Command:
+
+ if (Repeat) CommandInfo.VDMState |= VDM_FLAG_RETRY;
+ Repeat = FALSE;
+
+ /* Get the VDM command information */
+ DPRINT1("Calling GetNextVDMCommand in CmdStartProcess: wait for new VDM task...\n");
+ if (!GetNextVDMCommand(&CommandInfo))
+ {
+ DPRINT1("CmdStartProcess - GetNextVDMCommand failed, retrying... last error = %d\n", GetLastError());
+ if (CommandInfo.EnvLen > EnvSize)
+ {
+ /* Expand the environment size */
+ EnvSize = CommandInfo.EnvLen;
+ CommandInfo.Env = Env = RtlReAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, Env, EnvSize);
+
+ /* Repeat the request */
+ Repeat = TRUE;
+ goto Command;
+ }
+
+ /* Shouldn't happen */
+ DisplayMessage(L"An unrecoverable failure happened from start-app BOP; exiting DOS.");
+ setCF(1);
+ goto Quit;
+ }
+
+ // FIXME: What happens if some other 32-bit app is killed while we are waiting there??
+
+ DPRINT1("CmdStartProcess - GetNextVDMCommand succeeded, start app...\n");
+
+#else
+
+ if (!First)
+ {
+ DPRINT1("Exit DOS from start-app BOP\n");
+ setCF(1);
+ goto Quit;
+ }
+
+#endif
+
+ CmdLen = strlen(CmdLine);
+ DPRINT1("Starting '%s' ('%.*s')...\n",
+ AppName,
+ /* Display the command line without the terminating 0d 0a (and skip the terminating NULL) */
+ CmdLen >= 2 ? (CmdLine[CmdLen - 2] == '\r' ? CmdLen - 2
+ : CmdLen)
+ : CmdLen,
+ CmdLine);
+
+ /* Start the process */
+ // FIXME: Merge 'Env' with the master environment SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0)
+ // FIXME: Environment
+ RtlCopyMemory(SEG_OFF_TO_PTR(DataStruct->AppNameSeg, DataStruct->AppNameOff), AppName, MAX_PATH);
+ *(PBYTE)(SEG_OFF_TO_PTR(DataStruct->CmdLineSeg, DataStruct->CmdLineOff)) = (BYTE)(strlen(CmdLine) - 2);
+ RtlCopyMemory(SEG_OFF_TO_PTR(DataStruct->CmdLineSeg, DataStruct->CmdLineOff + 1), CmdLine, DOS_CMDLINE_LENGTH);
+
+#ifndef STANDALONE
+ /* Update console title if we run in a separate console */
+ if (SessionId != 0)
+ SetConsoleTitleA(AppName);
+#endif
+
+ First = FALSE;
+ setCF(0);
+
+ DPRINT1("App started!\n");
+
+Quit:
+ /* Resume the VM */
+ EmulatorResume();
+}
+
+static VOID CmdStartExternalCommand(VOID)
+{
+ DWORD Result;
+
+ // TODO: improve: this code has strong similarities
+ // with the 'default' case of DosCreateProcess.
+
+ LPSTR Command = (LPSTR)SEG_OFF_TO_PTR(getDS(), getSI());
+ CHAR CmdLine[sizeof("cmd.exe /c ") + DOS_CMDLINE_LENGTH + 1] = "";
+ LPSTR CmdLinePtr;
+ ULONG CmdLineLen;
+
+ /* Spawn a user-defined 32-bit command preprocessor */
+
+ // FIXME: Use COMSPEC env var!!
+ CmdLinePtr = CmdLine;
+ strcpy(CmdLinePtr, "cmd.exe /c ");
+ CmdLinePtr += strlen(CmdLinePtr);
+
+ /* Build a Win32-compatible command-line */
+ CmdLineLen = min(strlen(Command), sizeof(CmdLine) - strlen(CmdLinePtr) - 1);
+ RtlCopyMemory(CmdLinePtr, Command, CmdLineLen);
+ CmdLinePtr[CmdLineLen] = '\0';
+
+ /* Remove any trailing return carriage character and NULL-terminate the command line */
+ while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
+ *CmdLinePtr = '\0';
+
+ DPRINT1("CMD Run Command '%s' ('%s')\n", Command, CmdLine);
+
+ /*
+ * No need to prepare the stack for DosStartComSpec since we won't start it.
+ */
+ Result = DosStartProcess32(Command, CmdLine,
+ SEG_OFF_TO_PTR(getES(), 0) /*Environment*/,
+ MAKELONG(getIP(), getCS()) /*ReturnAddress*/,
+ FALSE);
+ if (Result != ERROR_SUCCESS)
+ {
+ DosDisplayMessage("Failed to start command '%s' ('%s'). Error: %u\n", Command, CmdLine, Result);
+ setCF(0);
+ setAL((UCHAR)Result);
+ }
+ else
+ {
+ DosDisplayMessage("Command '%s' ('%s') started successfully.\n", Command, CmdLine);
+#ifndef STANDALONE
+ setCF(Repeat); // Set CF if we need to start a 16-bit process
+#else
+ setCF(0);
+#endif
+ }
+}
+
+static VOID CmdStartComSpec32(VOID)
+{
+ DWORD Result;
+
+ // TODO: improve: this code has strong similarities with the
+ // 'default' case of DosCreateProcess and with the 'case 0x08'.
+
+ CHAR CmdLine[sizeof("cmd.exe") + 1] = "";
+
+ /* Spawn a user-defined 32-bit command preprocessor */
+
+ // FIXME: Use COMSPEC env var!!
+ strcpy(CmdLine, "cmd.exe");
+
+ DPRINT1("CMD Run 32-bit Command Interpreter '%s'\n", CmdLine);
+
+ /*
+ * No need to prepare the stack for DosStartComSpec since we won't start it.
+ */
+ Result = DosStartProcess32(CmdLine, CmdLine,
+ SEG_OFF_TO_PTR(getES(), 0) /*Environment*/,
+ MAKELONG(getIP(), getCS()) /*ReturnAddress*/,
+ FALSE);
+ if (Result != ERROR_SUCCESS)
+ {
+ DosDisplayMessage("Failed to start 32-bit Command Interpreter '%s'. Error: %u\n", CmdLine, Result);
+ setCF(0);
+ setAL((UCHAR)Result);
+ }
+ else
+ {
+ DosDisplayMessage("32-bit Command Interpreter '%s' started successfully.\n", CmdLine);
+#ifndef STANDALONE
+ setCF(Repeat); // Set CF if we need to start a 16-bit process
+#else
+ setCF(0);
+#endif
+ }
+}
+
+static VOID CmdSetExitCode(VOID)
+{
+#ifndef STANDALONE
+ BOOL Success;
+ PCOMSPEC_INFO ComSpecInfo;
+ VDM_COMMAND_INFO CommandInfo;
+#endif
+
+ /* Pause the VM */
+ EmulatorPause();
+
+#ifndef STANDALONE
+ /*
+ * Check whether we need to shell out now in case we were started by a 32-bit app,
+ * or we were started alone along with the root 32-bit app.
+ */
+ ComSpecInfo = FindComSpecInfoByPsp(Sda->CurrentPsp);
+ if ((ComSpecInfo && ComSpecInfo->Terminated) ||
+ (ComSpecInfo == &RootCmd && SessionId != 0))
+ {
+ RemoveComSpecInfo(ComSpecInfo);
+#endif
+ DPRINT1("Exit DOS from ExitCode (prologue)!");
+ setCF(0);
+ goto Quit;
+#ifndef STANDALONE
+ }
+
+ /* Clear the VDM structure */
+ RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
+
+Retry:
+ /* Update the VDM state of the task */
+ // CommandInfo.TaskId = SessionId;
+ CommandInfo.ExitCode = getDX();
+ CommandInfo.VDMState = VDM_FLAG_DONT_WAIT;
+ DPRINT1("Calling GetNextVDMCommand 32bit end of VDM task\n");
+ Success = GetNextVDMCommand(&CommandInfo);
+ DPRINT1("GetNextVDMCommand 32bit end of VDM task success = %s, last error = %d\n", Success ? "true" : "false", GetLastError());
+
+ /*
+ * Check whether we were awaited because the 32-bit process was stopped,
+ * or because it started a new DOS application.
+ */
+ if (CommandInfo.CmdLen != 0 || CommandInfo.AppLen != 0 || CommandInfo.PifLen != 0)
+ {
+ DPRINT1("GetNextVDMCommand end-of-app, this is for a new VDM task - CmdLen = %d, AppLen = %d, PifLen = %d\n",
+ CommandInfo.CmdLen, CommandInfo.AppLen, CommandInfo.PifLen);
+
+ /* Repeat the request */
+ Repeat = TRUE;
+ setCF(1);
+ }
+ else
+ {
+ DPRINT1("GetNextVDMCommand end-of-app, the app stopped\n");
+
+ /* Check whether we need to shell out now in case we were started by a 32-bit app */
+ ComSpecInfo = FindComSpecInfoByPsp(Sda->CurrentPsp);
+ if (!ComSpecInfo || !ComSpecInfo->Terminated)
+ {
+ DPRINT1("Not our 32-bit app, retrying...\n");
+ goto Retry;
+ }
+
+ ASSERT(ComSpecInfo->Terminated == TRUE);
+
+ /* Record found, remove it and exit now */
+ RemoveComSpecInfo(ComSpecInfo);
+
+ DPRINT1("Exit DOS from ExitCode wait!\n");
+ setCF(0);
+ }
+#endif
+
+ // FIXME: Use the retrieved exit code as the value of our exit code
+ // when COMMAND.COM will shell-out ??
+
+Quit:
+ /* Resume the VM */
+ EmulatorResume();
+}
+
static VOID WINAPI DosCmdInterpreterBop(LPWORD Stack)
{
/* Get the Function Number and skip it */
switch (FuncNum)
{
- case 0x08: // Launch external command
+ /* Kill the VDM */
+ case 0x00:
{
- BOOL Result;
- DWORD dwExitCode;
-
- LPSTR Command = (LPSTR)SEG_OFF_TO_PTR(getDS(), getSI());
- CHAR CmdLine[sizeof("cmd.exe /c ") + DOS_CMDLINE_LENGTH + 1] = "";
- LPSTR CmdLinePtr;
- ULONG CmdLineLen;
- STARTUPINFOA StartupInfo;
- PROCESS_INFORMATION ProcessInformation;
-
- /* Spawn a user-defined 32-bit command preprocessor */
-
- // FIXME: Use COMSPEC env var!!
- CmdLinePtr = CmdLine;
- strcpy(CmdLinePtr, "cmd.exe /c ");
- CmdLinePtr += strlen(CmdLinePtr);
-
- /* Build a Win32-compatible command-line */
- CmdLineLen = min(strlen(Command), sizeof(CmdLine) - strlen(CmdLinePtr) - 1);
- RtlCopyMemory(CmdLinePtr, Command, CmdLineLen);
- CmdLinePtr[CmdLineLen] = '\0';
-
- /* Remove any trailing return carriage character and NULL-terminate the command line */
- while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
- *CmdLinePtr = '\0';
-
- DPRINT1("CMD Run Command '%s' ('%s')\n", Command, CmdLine);
-
- RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
- RtlZeroMemory(&ProcessInformation, sizeof(ProcessInformation));
-
- StartupInfo.cb = sizeof(StartupInfo);
-
- VidBiosDetachFromConsole();
-
- Result = CreateProcessA(NULL,
- CmdLine,
- NULL,
- NULL,
- TRUE,
- 0,
- NULL,
- NULL,
- &StartupInfo,
- &ProcessInformation);
- if (Result)
- {
- DPRINT1("Command '%s' ('%s') launched successfully\n", Command, CmdLine);
+ /* Stop the VDM */
+ EmulatorTerminate();
+ return;
+ }
- /* Wait for process termination */
- WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
+ /*
+ * Get a new app to start
+ *
+ * Input
+ * DS:DX : Data block.
+ *
+ * Output
+ * CF : 0: Success; 1: Failure.
+ */
+ case 0x01:
+ {
+ CmdStartProcess();
+ break;
+ }
- /* Get the exit code */
- GetExitCodeProcess(ProcessInformation.hProcess, &dwExitCode);
+ /*
+ * Check binary format
+ *
+ * Input
+ * DS:DX : Program to check.
+ *
+ * Output
+ * CF : 0: Success; 1: Failure.
+ * AX : Error code.
+ */
+ case 0x07:
+ {
+ DWORD BinaryType;
+ LPSTR ProgramName = (LPSTR)SEG_OFF_TO_PTR(getDS(), getDX());
- /* Close handles */
- CloseHandle(ProcessInformation.hThread);
- CloseHandle(ProcessInformation.hProcess);
+ if (!GetBinaryTypeA(ProgramName, &BinaryType))
+ {
+ /* An error happened, bail out */
+ setCF(1);
+ setAX(LOWORD(GetLastError()));
+ break;
}
- else
+
+ // FIXME: We only support DOS binaries for now...
+ ASSERT(BinaryType == SCS_DOS_BINARY);
+ if (BinaryType != SCS_DOS_BINARY)
{
- DPRINT1("Failed when launched command '%s' ('%s')\n", Command, CmdLine);
- dwExitCode = GetLastError();
+ /* An error happened, bail out */
+ setCF(1);
+ setAX(LOWORD(ERROR_BAD_EXE_FORMAT));
+ break;
}
- VidBiosAttachToConsole();
+ /* Return success: DOS application */
+ setCF(0);
+ break;
+ }
+
+ /*
+ * Start an external command
+ *
+ * Input
+ * DS:SI : Command to start.
+ * ES : Environment block segment.
+ * AL : Current drive number.
+ * AH : 0: Directly start the command;
+ * 1: Use "cmd.exe /c" to start the command.
+ *
+ * Output
+ * CF : 0: Shell-out; 1: Continue.
+ * AL : Error/Exit code.
+ */
+ case 0x08:
+ {
+ CmdStartExternalCommand();
+ break;
+ }
+
+ /*
+ * Start the default 32-bit command interpreter (COMSPEC)
+ *
+ * Input
+ * ES : Environment block segment.
+ * AL : Current drive number.
+ *
+ * Output
+ * CF : 0: Shell-out; 1: Continue.
+ * AL : Error/Exit code.
+ */
+ case 0x0A:
+ {
+ CmdStartComSpec32();
+ break;
+ }
- setAL((UCHAR)dwExitCode);
+ /*
+ * Set exit code
+ *
+ * Input
+ * DX : Exit code
+ *
+ * Output
+ * CF : 0: Shell-out; 1: Continue.
+ */
+ case 0x0B:
+ {
+ CmdSetExitCode();
+ break;
+ }
+ /*
+ * Get start information
+ *
+ * Output
+ * AL : 0 (resp. 1): Started from (resp. without) an existing console.
+ */
+ case 0x10:
+ {
+#ifndef STANDALONE
+ /*
+ * When a new instance of our (internal) COMMAND.COM is started,
+ * we check whether we need to run a 32-bit COMSPEC. This goes by
+ * checking whether we were started in a new console (no parent
+ * console process) or from an existing one.
+ *
+ * However COMMAND.COM can also be started in the case where a
+ * 32-bit process (started by a 16-bit parent) wants to start a new
+ * 16-bit process: to ensure DOS reentry we need to start a new
+ * instance of COMMAND.COM. On Windows the COMMAND.COM is started
+ * just before the 32-bit process (in fact, it is this COMMAND.COM
+ * which starts the 32-bit process via an undocumented command-line
+ * switch '/z', which syntax is:
+ * COMMAND.COM /z\bAPPNAME.EXE
+ * notice the '\b' character inserted in-between. Then COMMAND.COM
+ * issues a BOP_CMD 08h with AH=00h to start the process).
+ *
+ * Instead, we do the reverse, i.e. we start the 32-bit process,
+ * and *only* if needed, i.e. if this process wants to start a
+ * new 16-bit process, we start our COMMAND.COM.
+ *
+ * The problem we then face is that our COMMAND.COM will possibly
+ * want to start a new COMSPEC, however we do not want this.
+ * The chosen solution is to flag this case -- done with the 'Reentry'
+ * boolean -- so that COMMAND.COM will not attempt to start COMSPEC
+ * but instead will directly try to start the 16-bit process.
+ */
+ // setAL(SessionId != 0);
+ setAL((SessionId != 0) && !Reentry);
+ /* Reset 'Reentry' */
+ Reentry = FALSE;
+#else
+ setAL(0);
+#endif
break;
}
}
}
+#ifndef COMSPEC_FULLY_EXTERNAL
+/*
+ * Internal COMMAND.COM binary data in the CommandCom array.
+ */
+#include "command_com.h"
+#endif
+
+static
+DWORD DosStartComSpec(IN BOOLEAN Permanent,
+ IN LPCSTR Environment OPTIONAL,
+ IN DWORD ReturnAddress OPTIONAL,
+ OUT PWORD ComSpecPsp OPTIONAL)
+{
+ /*
+ * BOOLEAN Permanent
+ * TRUE to simulate the /P switch of command.com: starts AUTOEXEC.BAT/NT
+ * and makes the interpreter permanent (cannot exit).
+ */
+
+ DWORD Result;
+
+ if (ComSpecPsp) *ComSpecPsp = 0;
+
+ Result =
+#ifndef COMSPEC_FULLY_EXTERNAL
+ DosLoadExecutableInternal(DOS_LOAD_AND_EXECUTE,
+ CommandCom,
+ sizeof(CommandCom),
+ "COMMAND.COM",
+#else
+ DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
+#ifndef STANDALONE // FIXME: Those values are hardcoded paths on my local test machines!!
+ "C:\\CMDCMD.COM",
+#else
+ "H:\\DOS_tests\\CMDCMD.COM",
+#endif // STANDALONE
+#endif // COMSPEC_FULLY_EXTERNAL
+ NULL,
+ Permanent ? "/P" : "",
+ Environment ? Environment : "", // FIXME: Default environment!
+ ReturnAddress);
+ if (Result != ERROR_SUCCESS) return Result;
+
+ /* TODO: Read AUTOEXEC.NT/BAT */
+
+ /* Retrieve the PSP of the COMSPEC (current PSP set by DosLoadExecutable) */
+ if (ComSpecPsp) *ComSpecPsp = Sda->CurrentPsp;
+
+ return Result;
+}
+
+typedef struct _DOS_START_PROC32
+{
+ LPSTR ExecutablePath;
+ LPSTR CommandLine;
+ LPSTR Environment OPTIONAL;
#ifndef STANDALONE
+ PCOMSPEC_INFO ComSpecInfo;
+ HANDLE hEvent;
+#endif
+} DOS_START_PROC32, *PDOS_START_PROC32;
+
static DWORD
WINAPI
CommandThreadProc(LPVOID Parameter)
{
- BOOLEAN First = TRUE;
- DWORD Result;
+ BOOL Success;
+ PROCESS_INFORMATION ProcessInfo;
+ STARTUPINFOA StartupInfo;
+ DWORD dwExitCode;
+ PDOS_START_PROC32 DosStartProc32 = (PDOS_START_PROC32)Parameter;
+#ifndef STANDALONE
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);
-
- UNREFERENCED_PARAMETER(Parameter);
- ASSERT(Env);
+ PCOMSPEC_INFO ComSpecInfo = DosStartProc32->ComSpecInfo;
+#endif
- /* Clear the structure */
+ /* Set up the VDM, startup and process info structures */
+#ifndef STANDALONE
RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
+#endif
+ RtlZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
+ RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
+ StartupInfo.cb = sizeof(StartupInfo);
- /* Get the initial information */
- CommandInfo.TaskId = SessionId;
- CommandInfo.VDMState = VDM_GET_FIRST_COMMAND | VDM_FLAG_DOS;
- GetNextVDMCommand(&CommandInfo);
+ // FIXME: Build suitable 32-bit environment!!
- do
+#ifndef STANDALONE
+ /*
+ * Wait for signaling a new VDM task and increment the VDM re-entry count so
+ * that we can handle 16-bit apps that may be possibly started by the 32-bit app.
+ */
+ CommandInfo.VDMState = VDM_INC_REENTER_COUNT;
+ DPRINT1("Calling GetNextVDMCommand reenter++\n");
+ Success = GetNextVDMCommand(&CommandInfo);
+ DPRINT1("GetNextVDMCommand reenter++ success = %s, last error = %d\n", Success ? "true" : "false", GetLastError());
+ ++ReentrancyCount;
+#endif
+
+ /* Start the process */
+ Success = CreateProcessA(NULL, // ProgramName,
+ DosStartProc32->CommandLine,
+ NULL,
+ NULL,
+ TRUE, // Inherit handles
+ CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED,
+ DosStartProc32->Environment,
+ NULL, // lpCurrentDirectory, see "START" command in cmd.exe
+ &StartupInfo,
+ &ProcessInfo);
+
+#ifndef STANDALONE
+ /* Signal our caller the process was started */
+ SetEvent(DosStartProc32->hEvent);
+ // After this point, 'DosStartProc32' is not valid anymore.
+#endif
+
+ if (Success)
{
- /* Clear the structure */
- RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
-
- /* Initialize the structure members */
- CommandInfo.TaskId = SessionId;
- CommandInfo.VDMState = VDM_FLAG_DOS;
- 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;
-
- if (First) CommandInfo.VDMState |= VDM_FLAG_FIRST_TASK;
+ /* Resume the process */
+ ResumeThread(ProcessInfo.hThread);
-Command:
- if (!GetNextVDMCommand(&CommandInfo))
+ /* Wait for the process to finish running and retrieve its exit code */
+ WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
+ GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode);
+
+ /* Close the handles */
+ CloseHandle(ProcessInfo.hThread);
+ CloseHandle(ProcessInfo.hProcess);
+ }
+ else
+ {
+ dwExitCode = GetLastError();
+ }
+
+#ifndef STANDALONE
+ ASSERT(ComSpecInfo);
+ ComSpecInfo->Terminated = TRUE;
+ ComSpecInfo->dwExitCode = dwExitCode;
+
+ /* Decrement the VDM re-entry count */
+ CommandInfo.VDMState = VDM_DEC_REENTER_COUNT;
+ DPRINT1("Calling GetNextVDMCommand reenter--\n");
+ Success = GetNextVDMCommand(&CommandInfo);
+ DPRINT1("GetNextVDMCommand reenter-- success = %s, last error = %d\n", Success ? "true" : "false", GetLastError());
+ --ReentrancyCount;
+
+ return 0;
+#else
+ return dwExitCode;
+#endif
+}
+
+DWORD DosStartProcess32(IN LPCSTR ExecutablePath,
+ IN LPCSTR CommandLine,
+ IN LPCSTR Environment OPTIONAL,
+ IN DWORD ReturnAddress OPTIONAL,
+ IN BOOLEAN StartComSpec)
+{
+ DWORD Result = ERROR_SUCCESS;
+ HANDLE CommandThread;
+ DOS_START_PROC32 DosStartProc32;
+#ifndef STANDALONE
+ BOOL Success;
+ VDM_COMMAND_INFO CommandInfo;
+#endif
+
+ DosStartProc32.ExecutablePath = (LPSTR)ExecutablePath;
+ DosStartProc32.CommandLine = (LPSTR)CommandLine;
+ DosStartProc32.Environment = (LPSTR)Environment;
+
+#ifndef STANDALONE
+ DosStartProc32.ComSpecInfo =
+ RtlAllocateHeap(RtlGetProcessHeap(),
+ HEAP_ZERO_MEMORY,
+ sizeof(*DosStartProc32.ComSpecInfo));
+ ASSERT(DosStartProc32.ComSpecInfo);
+
+ DosStartProc32.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ ASSERT(DosStartProc32.hEvent);
+#endif
+
+ /* Pause the VM and detach from the console */
+ EmulatorPause();
+ DosProcessConsoleDetach();
+
+ /* Start the 32-bit process via another thread */
+ CommandThread = CreateThread(NULL, 0, &CommandThreadProc, &DosStartProc32, 0, NULL);
+ if (CommandThread == NULL)
+ {
+ DisplayMessage(L"FATAL: Failed to create the command processing thread: %d", GetLastError());
+ Result = GetLastError();
+ goto Quit;
+ }
+
+#ifndef STANDALONE
+ /* Close the thread handle */
+ CloseHandle(CommandThread);
+
+ /* Wait for the process to be ready to start */
+ WaitForSingleObject(DosStartProc32.hEvent, INFINITE);
+
+ /* Wait for any potential new DOS app started by the 32-bit process */
+ RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
+
+Retry:
+ CommandInfo.VDMState = VDM_FLAG_NESTED_TASK | VDM_FLAG_DONT_WAIT;
+ DPRINT1("Calling GetNextVDMCommand 32bit for possible new VDM task...\n");
+ Success = GetNextVDMCommand(&CommandInfo);
+ DPRINT1("GetNextVDMCommand 32bit awaited, success = %s, last error = %d\n", Success ? "true" : "false", GetLastError());
+
+ /*
+ * Check whether we were awaited because the 32-bit process was stopped,
+ * or because it started a new DOS application.
+ */
+ if (CommandInfo.CmdLen != 0 || CommandInfo.AppLen != 0 || CommandInfo.PifLen != 0)
+ {
+ DPRINT1("GetNextVDMCommand 32bit, this is for a new VDM task - CmdLen = %d, AppLen = %d, PifLen = %d\n",
+ CommandInfo.CmdLen, CommandInfo.AppLen, CommandInfo.PifLen);
+
+ /* Repeat the request */
+ Repeat = TRUE;
+
+ /*
+ * Set 'Reentry' to TRUE or FALSE depending on whether we are going
+ * to reenter with a new COMMAND.COM. See the comment for:
+ * BOP_CMD 0x10 'Get start information'
+ * (dem.c!DosCmdInterpreterBop) for more details.
+ */
+ Reentry = StartComSpec;
+
+ /* If needed, start a new command interpreter to handle the possible incoming DOS commands */
+ if (StartComSpec)
{
- if (CommandInfo.EnvLen > EnvSize)
+ //
+ // DosStartProcess32 was only called by DosCreateProcess, called from INT 21h,
+ // so the caller stack is already prepared for running a new DOS program
+ // (Flags, CS and IP, and the extra interrupt number, are already pushed).
+ //
+ Result = DosStartComSpec(FALSE, Environment, ReturnAddress,
+ &DosStartProc32.ComSpecInfo->ComSpecPsp);
+ if (Result != ERROR_SUCCESS)
{
- /* 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;
+ DosDisplayMessage("Failed to start a new Command Interpreter (Error: %u).\n", Result);
+ goto Quit;
}
-
- break;
}
+ else
+ {
+ /* Retrieve the PSP of the COMSPEC (current PSP set by DosLoadExecutable) */
+ DosStartProc32.ComSpecInfo->ComSpecPsp = Sda->CurrentPsp;
+ Result = ERROR_SUCCESS;
+ }
+
+ /* Insert the new entry in the list; it will be freed when needed by COMMAND.COM */
+ InsertComSpecInfo(DosStartProc32.ComSpecInfo);
+ }
+ else
+ {
+ DPRINT1("GetNextVDMCommand 32bit, 32-bit app stopped\n");
- /* Start the process from the command line */
- Result = DosStartProcess(AppName, CmdLine, Env, MAKELONG(getIP(), getCS()));
- if (Result != ERROR_SUCCESS)
+ /* Check whether this was our 32-bit app which was killed */
+ if (!DosStartProc32.ComSpecInfo->Terminated)
{
- DisplayMessage(L"Could not start '%S'. Error: %u", AppName, Result);
- // break;
- continue;
+ DPRINT1("Not our 32-bit app, retrying...\n");
+ goto Retry;
}
- First = FALSE;
+ Result = DosStartProc32.ComSpecInfo->dwExitCode;
+
+ /* Delete the entry */
+ RtlFreeHeap(RtlGetProcessHeap(), 0, DosStartProc32.ComSpecInfo);
}
- while (AcceptCommands);
+#else
+ /* Wait for the thread to finish */
+ WaitForSingleObject(CommandThread, INFINITE);
+ GetExitCodeThread(CommandThread, &Result);
- RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
- return 0;
-}
-#endif
+ /* Close the thread handle */
+ CloseHandle(CommandThread);
-/* PUBLIC VARIABLES ***********************************************************/
+ DPRINT1("32-bit app stopped\n");
+#endif
+Quit:
#ifndef STANDALONE
-BOOLEAN AcceptCommands = TRUE;
-HANDLE CommandThread = NULL;
-ULONG SessionId = 0;
+ CloseHandle(DosStartProc32.hEvent);
#endif
-/* PUBLIC FUNCTIONS ***********************************************************/
+ /* Attach to the console and resume the VM */
+ DosProcessConsoleAttach();
+ EmulatorResume();
+
+ return Result;
+}
+
+
+
+
+/******************************************************************************\
+|** DOS Bootloader emulation, Startup and Shutdown **|
+\******************************************************************************/
+
//
// This function (equivalent of the DOS bootsector) is called by the bootstrap
VOID DosBootsectorInitialize(VOID)
{
/* We write the bootsector at 0000:7C00 */
- ULONG_PTR Address = (ULONG_PTR)SEG_OFF_TO_PTR(0x0000, 0x7C00);
+ ULONG_PTR StartAddress = (ULONG_PTR)SEG_OFF_TO_PTR(0x0000, 0x7C00);
+ ULONG_PTR Address = StartAddress;
CHAR DosKernelFileName[] = ""; // No DOS BIOS file name, therefore we will load DOS32
DPRINT("DosBootsectorInitialize\n");
RtlCopyMemory((PVOID)Address, DosKernelFileName, sizeof(DosKernelFileName));
Address += sizeof(DosKernelFileName);
RtlCopyMemory((PVOID)Address, Bootsector2, sizeof(Bootsector2));
+ Address += sizeof(Bootsector2);
+
+ /* Initialize the callback context */
+ InitializeContext(&DosContext, 0x0000,
+ (ULONG_PTR)MEM_ALIGN_UP(0x7C00 + Address - StartAddress, sizeof(WORD)));
/* Register the DOS Loading BOP */
RegisterBop(BOP_LOAD_DOS, DosInitialize);
Quit:
if (!Success)
{
- DisplayMessage(L"DOS BIOS file '%S' loading failed (Error: %u). The VDM will shut down.",
- DosBiosFileName, GetLastError());
+ BiosDisplayMessage("DOS BIOS file '%s' loading failed (Error: %u). The VDM will shut down.\n",
+ DosBiosFileName, GetLastError());
return;
}
}
static VOID WINAPI DosStart(LPWORD Stack)
{
BOOLEAN Success;
-#ifdef STANDALONE
DWORD Result;
- CHAR ApplicationName[MAX_PATH];
- CHAR CommandLine[DOS_CMDLINE_LENGTH];
-#else
+#ifndef STANDALONE
INT i;
#endif
*/
RegisterBop(BOP_START_DOS, NULL);
+ /* Initialize the callback context */
+ InitializeContext(&DosContext, BIOS_CODE_SEGMENT, 0x0010);
+
Success = DosBIOSInitialize();
// Success &= DosKRNLInitialize();
if (!Success)
{
- DisplayMessage(L"DOS32 loading failed (Error: %u). The VDM will shut down.", GetLastError());
+ BiosDisplayMessage("DOS32 loading failed (Error: %u). The VDM will shut down.\n", GetLastError());
EmulatorTerminate();
return;
}
/* Load the mouse driver */
- DosMouseInitialize();
+ // DosMouseInitialize();
#ifndef STANDALONE
{
/* This is the session ID (hex format) */
SessionId = wcstoul(NtVdmArgv[i] + 2, NULL, 16);
-
- /* The VDM hasn't been started from a console, so quit when the task is done */
- AcceptCommands = FALSE;
}
}
- /* Create the GetNextVDMCommand thread */
- CommandThread = CreateThread(NULL, 0, &CommandThreadProc, NULL, 0, NULL);
- if (CommandThread == NULL)
+ /* Initialize Win32-VDM environment */
+ Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
+ if (Env == NULL)
{
- wprintf(L"FATAL: Failed to create the command processing thread: %d\n", GetLastError());
- goto Quit;
+ DosDisplayMessage("Failed to initialize the global environment (Error: %u). The VDM will shut down.\n", GetLastError());
+ EmulatorTerminate();
+ return;
}
- /* Wait for the command thread to exit */
- WaitForSingleObject(CommandThread, INFINITE);
+ /* Clear the structure */
+ RtlZeroMemory(&CommandInfo, sizeof(CommandInfo));
- /* Close the thread handle */
- CloseHandle(CommandThread);
+ /* Get the initial information */
+ CommandInfo.TaskId = SessionId;
+ CommandInfo.VDMState = VDM_GET_FIRST_COMMAND | VDM_FLAG_DOS;
+ GetNextVDMCommand(&CommandInfo);
#else
+ /* Retrieve the command to start */
if (NtVdmArgc >= 2)
{
- WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[1], -1, ApplicationName, sizeof(ApplicationName), NULL, NULL);
+ WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[1], -1, AppName, sizeof(AppName), NULL, NULL);
if (NtVdmArgc >= 3)
- WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[2], -1, CommandLine, sizeof(CommandLine), NULL, NULL);
+ WideCharToMultiByte(CP_ACP, 0, NtVdmArgv[2], -1, CmdLine, sizeof(CmdLine), NULL, NULL);
else
- strcpy(CommandLine, "");
+ strcpy(CmdLine, "");
}
else
{
- DisplayMessage(L"Invalid DOS command line\n");
- goto Quit;
+ DosDisplayMessage("Invalid DOS command line\n");
+ EmulatorTerminate();
+ return;
}
- /* Start the process from the command line */
- Result = DosStartProcess(ApplicationName, CommandLine,
- SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0),
- MAKELONG(getIP(), getCS()));
+#endif
+
+ /*
+ * At this point, CS:IP points to the DOS BIOS exit code. If the
+ * root command interpreter fails to start (or if it exits), DOS
+ * exits and the VDM terminates.
+ */
+
+ /* Start the root command interpreter */
+ // TODO: Really interpret the 'SHELL=' line of CONFIG.NT, and use it!
+
+ /*
+ * Prepare the stack for DosStartComSpec:
+ * push Flags, CS and IP, and an extra WORD.
+ */
+ setSP(getSP() - sizeof(WORD));
+ *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = (WORD)getEFLAGS();
+ setSP(getSP() - sizeof(WORD));
+ *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = getCS();
+ setSP(getSP() - sizeof(WORD));
+ *((LPWORD)SEG_OFF_TO_PTR(getSS(), getSP())) = getIP();
+ setSP(getSP() - sizeof(WORD));
+
+ Result = DosStartComSpec(TRUE, SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0),
+ MAKELONG(getIP(), getCS()),
+#ifndef STANDALONE
+ &RootCmd.ComSpecPsp
+#else
+ NULL
+#endif
+ );
if (Result != ERROR_SUCCESS)
{
- DisplayMessage(L"Could not start '%S'. Error: %u", ApplicationName, Result);
- goto Quit;
+ /* Unprepare the stack for DosStartComSpec */
+ setSP(getSP() + 4*sizeof(WORD));
+
+ DosDisplayMessage("Failed to start the Command Interpreter (Error: %u). The VDM will shut down.\n", Result);
+ EmulatorTerminate();
+ return;
}
+#ifndef STANDALONE
+ RootCmd.Terminated = FALSE;
+ InsertComSpecInfo(&RootCmd);
#endif
-Quit:
- /* Stop the VDM */
- EmulatorTerminate();
+ /**/
+ /* Attach to the console and resume the VM */
+ DosProcessConsoleAttach();
+ EmulatorResume();
+ /**/
+
+ return;
+}
+
+BOOLEAN DosShutdown(BOOLEAN Immediate)
+{
+ /*
+ * Immediate = TRUE: Immediate shutdown;
+ * FALSE: Delayed shutdown (notification).
+ */
+
+#ifndef STANDALONE
+ if (Immediate)
+ {
+ ExitVDM(FALSE, 0);
+ return TRUE;
+ }
+ else
+ {
+// HACK!
+extern HANDLE VdmTaskEvent; // see emulator.c
+
+ /*
+ * Signal the root COMMAND.COM that it should terminate
+ * when it checks for a new command.
+ */
+ RootCmd.Terminated = TRUE;
+
+ /* If the list is already empty, or just contains only one element, bail out */
+ // FIXME: Question: if the list has only one element, is it ALWAYS RootCmd ??
+ // FIXME: The following is hackish.
+ if (IsListEmpty(&ComSpecInfoList) ||
+ (ComSpecInfoList.Flink == &RootCmd.Entry &&
+ ComSpecInfoList.Blink == &RootCmd.Entry) &&
+ ReentrancyCount == 0 &&
+ (WaitForSingleObject(VdmTaskEvent, 0) == WAIT_TIMEOUT))
+ {
+ /* Nothing runs, so exit immediately */
+ ExitVDM(FALSE, 0);
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+#else
+ UNREFERENCED_PARAMETER(Immediate);
+ return TRUE;
+#endif
}
+
/* PUBLIC EXPORTED APIS *******************************************************/
// demLFNCleanup
PDOS_REGISTER_STATE State;
WORD StackPointer = getSP();
+ DPRINT1("\n"
+ "DosSaveState(before) -- SS:SP == %04X:%04X\n"
+ "Original CPU State =\n"
+ "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
+ "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
+ "\n",
+ getSS(), getSP(),
+ getDS(), getES(), getAX(), getCX(),
+ getDX(), getBX(), getBP(), getSI(), getDI());
+
/*
* Allocate stack space for the registers. Note that we
* already have one word allocated (the interrupt number).
State->BP = getBP();
State->SI = getSI();
State->DI = getDI();
+
+ DPRINT1("\n"
+ "DosSaveState(after) -- SS:SP == %04X:%04X\n"
+ "Saved State =\n"
+ "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
+ "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
+ "\n",
+ getSS(), getSP(),
+ State->DS, State->ES, State->AX, State->CX,
+ State->DX, State->BX, State->BP, State->SI, State->DI);
}
static inline VOID DosRestoreState(VOID)
* already have one word allocated (the interrupt number).
*/
State = SEG_OFF_TO_PTR(getSS(), getSP());
+
+ DPRINT1("\n"
+ "DosRestoreState(before) -- SS:SP == %04X:%04X\n"
+ "Saved State =\n"
+ "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
+ "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
+ "\n",
+ getSS(), getSP(),
+ State->DS, State->ES, State->AX, State->CX,
+ State->DX, State->BX, State->BP, State->SI, State->DI);
+
setSP(getSP() + sizeof(DOS_REGISTER_STATE) - sizeof(WORD));
/* Restore */
setBP(State->BP);
setSI(State->SI);
setDI(State->DI);
+
+ DPRINT1("\n"
+ "DosRestoreState(after) -- SS:SP == %04X:%04X\n"
+ "Restored CPU State =\n"
+ "DS = %04X; ES = %04X; AX = %04X; CX = %04X\n"
+ "DX = %04X; BX = %04X; BP = %04X; SI = %04X; DI = %04X"
+ "\n",
+ getSS(), getSP(),
+ getDS(), getES(), getAX(), getCX(),
+ getDX(), getBX(), getBP(), getSI(), getDI());
}
static WORD DosCopyEnvironmentBlock(IN LPCSTR Environment OPTIONAL,
/* Link to the parent's environment block */
PspBlock->EnvBlock = SEGMENT_TO_PSP(Sda->CurrentPsp)->EnvBlock;
}
+/*
+ else
+ {
+ PspBlock->EnvBlock = SEG_OFF_TO_PTR(SYSTEM_ENV_BLOCK, 0);
+ }
+*/
/* Copy the parent handle table */
DosCopyHandleTable(PspBlock->HandleTable);
/* Push the task state */
DosSaveState();
+ DPRINT1("Sda->CurrentPsp = 0x%04x; Old LastStack = 0x%08x, New LastStack = 0x%08x\n",
+ Sda->CurrentPsp, SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack, MAKELONG(getSP(), getSS()));
+
/* Update the last stack in the PSP */
SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack = MAKELONG(getSP(), getSS());
}
return Result;
}
-DWORD DosStartProcess(IN LPCSTR ExecutablePath,
- IN LPCSTR CommandLine,
- IN LPCSTR Environment OPTIONAL,
- IN DWORD ReturnAddress OPTIONAL)
-{
- DWORD Result;
-
- SIZE_T CmdLen = strlen(CommandLine);
- DPRINT1("Starting '%s' ('%.*s')...\n",
- ExecutablePath,
- /* Display the command line without the terminating 0d 0a (and skip the terminating NULL) */
- CmdLen >= 2 ? (CommandLine[CmdLen - 2] == '\r' ? CmdLen - 2
- : CmdLen)
- : CmdLen,
- CommandLine);
-
- Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
- ExecutablePath,
- NULL,
- CommandLine,
- Environment,
- ReturnAddress);
-
- if (Result != ERROR_SUCCESS) goto Quit;
-
-#ifndef STANDALONE
- /* Update console title if we run in a separate console */
- if (SessionId != 0)
- SetConsoleTitleA(ExecutablePath);
-#endif
-
- /* Attach to the console */
- ConsoleAttach();
- VidBiosAttachToConsole();
-
- /* Start simulation */
- SetEvent(VdmTaskEvent);
- CpuSimulate();
-
- /* Detach from the console */
- VidBiosDetachFromConsole();
- ConsoleDetach();
-
-Quit:
- return Result;
-}
-
-#ifndef STANDALONE
WORD DosCreateProcess(IN LPCSTR ProgramName,
IN PDOS_EXEC_PARAM_BLOCK Parameters,
IN DWORD ReturnAddress OPTIONAL)
{
- DWORD Result;
+ DWORD Result = ERROR_SUCCESS;
DWORD BinaryType;
- LPVOID Environment = NULL;
- VDM_COMMAND_INFO CommandInfo;
- CHAR CmdLine[MAX_PATH + DOS_CMDLINE_LENGTH + 1];
- CHAR AppName[MAX_PATH];
- CHAR PifFile[MAX_PATH];
- CHAR Desktop[MAX_PATH];
- CHAR Title[MAX_PATH];
- LPSTR CmdLinePtr;
- ULONG CmdLineSize;
- ULONG EnvSize = 256;
- PVOID Env;
- STARTUPINFOA StartupInfo;
- PROCESS_INFORMATION ProcessInfo;
/* Get the binary type */
if (!GetBinaryTypeA(ProgramName, &BinaryType)) return GetLastError();
- /* Initialize Win32-VDM environment */
- Env = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EnvSize);
- if (Env == NULL) 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);
-
- /*
- * Convert the DOS command line to Win32-compatible format, by concatenating
- * the program name with the converted command line.
- * Format of the DOS command line: 1 byte for size; 127 bytes for contents.
- */
- CmdLinePtr = CmdLine;
- strncpy(CmdLinePtr, ProgramName, MAX_PATH); // Concatenate the program name
- CmdLinePtr += strlen(CmdLinePtr);
- *CmdLinePtr++ = ' '; // Add separating space
-
- CmdLineSize = min(*(PBYTE)FAR_POINTER(Parameters->CommandLine), DOS_CMDLINE_LENGTH);
- RtlCopyMemory(CmdLinePtr,
- (LPSTR)FAR_POINTER(Parameters->CommandLine) + 1,
- CmdLineSize);
- /* NULL-terminate it */
- CmdLinePtr[CmdLineSize] = '\0';
-
- /* Remove any trailing return carriage character and NULL-terminate the command line */
- while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
- *CmdLinePtr = '\0';
-
- /* Create the process */
- if (!CreateProcessA(ProgramName,
- CmdLine,
- NULL,
- NULL,
- FALSE,
- 0,
- Environment,
- NULL,
- &StartupInfo,
- &ProcessInfo))
- {
- RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
- return GetLastError();
- }
-
/* Check the type of the program */
switch (BinaryType)
{
- /* These are handled by NTVDM */
- case SCS_DOS_BINARY:
+ /* Those are handled by NTVDM */
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);
- }
-
+ DisplayMessage(L"Trying to load '%S'. WOW16 applications are not supported at the moment.",
+ ProgramName);
+ // Fall through
+ }
+ case SCS_DOS_BINARY:
+ {
/* Load the executable */
Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
- AppName,
+ ProgramName,
Parameters,
- CmdLine,
- Env,
+ NULL,
+ NULL,
ReturnAddress);
- if (Result == ERROR_SUCCESS)
+ 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);
+ DisplayMessage(L"Could not load '%S'. Error: %u", ProgramName, Result);
}
break;
/* Not handled by NTVDM */
default:
{
- /* Wait for the process to finish executing */
- WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
- }
- }
+ LPSTR Environment = NULL;
+ CHAR CmdLine[MAX_PATH + DOS_CMDLINE_LENGTH + 1];
+ LPSTR CmdLinePtr;
+ ULONG CmdLineSize;
+
+ /* Did the caller specify an environment segment? */
+ if (Parameters->Environment)
+ {
+ /* Yes, use it instead of the parent one */
+ Environment = (LPSTR)SEG_OFF_TO_PTR(Parameters->Environment, 0);
+ }
- RtlFreeHeap(RtlGetProcessHeap(), 0, Env);
+ /*
+ * Convert the DOS command line to Win32-compatible format, by concatenating
+ * the program name with the converted command line.
+ * Format of the DOS command line: 1 byte for size; 127 bytes for contents.
+ */
+ CmdLinePtr = CmdLine;
+ strncpy(CmdLinePtr, ProgramName, MAX_PATH); // Concatenate the program name
+ CmdLinePtr += strlen(CmdLinePtr);
+ *CmdLinePtr++ = ' '; // Add separating space
+
+ CmdLineSize = min(*(PBYTE)FAR_POINTER(Parameters->CommandLine), DOS_CMDLINE_LENGTH);
+ RtlCopyMemory(CmdLinePtr,
+ (LPSTR)FAR_POINTER(Parameters->CommandLine) + 1,
+ CmdLineSize);
+ /* NULL-terminate it */
+ CmdLinePtr[CmdLineSize] = '\0';
+
+ /* Remove any trailing return carriage character and NULL-terminate the command line */
+ while (*CmdLinePtr && *CmdLinePtr != '\r' && *CmdLinePtr != '\n') CmdLinePtr++;
+ *CmdLinePtr = '\0';
+
+ Result = DosStartProcess32(ProgramName, CmdLine,
+ Environment, ReturnAddress,
+ TRUE);
+ if (Result != ERROR_SUCCESS)
+ {
+ DisplayMessage(L"Could not load 32-bit '%S'. Error: %u", ProgramName, Result);
+ }
- /* Close the handles */
- CloseHandle(ProcessInfo.hProcess);
- CloseHandle(ProcessInfo.hThread);
+ break;
+ }
+ }
- return ERROR_SUCCESS;
+ return Result;
}
-#endif
VOID DosTerminateProcess(WORD Psp, BYTE ReturnCode, WORD KeepResident)
{
PDOS_PSP PspBlock = SEGMENT_TO_PSP(Psp);
LPWORD Stack;
BYTE TerminationType;
-#ifndef STANDALONE
- VDM_COMMAND_INFO CommandInfo;
-#endif
DPRINT("DosTerminateProcess: Psp 0x%04X, ReturnCode 0x%02X, KeepResident 0x%04X\n",
Psp, ReturnCode, KeepResident);
DosSetProcessContext(PspBlock->ParentPsp);
if (Sda->CurrentPsp == SYSTEM_PSP)
{
- ResetEvent(VdmTaskEvent);
+ // NOTE: we can also use the DOS BIOS exit code.
CpuUnsimulate();
return;
}
}
-#ifndef STANDALONE
-
- /* 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 or TSR */
TerminationType = (KeepResident != 0 ? 0x03 : 0x00);
Sda->ErrorLevel = MAKEWORD(ReturnCode, TerminationType);
+ DPRINT1("PspBlock->ParentPsp = 0x%04x; Sda->CurrentPsp = 0x%04x\n",
+ PspBlock->ParentPsp, Sda->CurrentPsp);
+
if (Sda->CurrentPsp != SYSTEM_PSP)
{
+ DPRINT1("Sda->CurrentPsp = 0x%04x; Old SS:SP = %04X:%04X going to be LastStack = 0x%08x\n",
+ Sda->CurrentPsp, getSS(), getSP(), SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack);
+
/* Restore the parent's stack */
setSS(HIWORD(SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack));
setSP(LOWORD(SEGMENT_TO_PSP(Sda->CurrentPsp)->LastStack));