static VOID WINAPI DosCmdInterpreterBop(LPWORD Stack) { /* Get the Function Number and skip it */ BYTE FuncNum = *(PBYTE)SEG_OFF_TO_PTR(getCS(), getIP()); setIP(getIP() + 1); switch (FuncNum) { /* Kill the VDM */ case 0x00: { /* Stop the VDM */ EmulatorTerminate(); return; } /* * Get a new app to start * * Input * DS:DX : Data block. * * Output * CF : 0: Success; 1: Failure. */ case 0x01: { CmdStartProcess(); break; } /* * 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()); if (!GetBinaryTypeA(ProgramName, &BinaryType)) { /* An error happened, bail out */ setCF(1); setAX(LOWORD(GetLastError())); break; } // FIXME: We only support DOS binaries for now... ASSERT(BinaryType == SCS_DOS_BINARY); if (BinaryType != SCS_DOS_BINARY) { /* An error happened, bail out */ setCF(1); setAX(LOWORD(ERROR_BAD_EXE_FORMAT)); break; } /* 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; } /* * 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; } default: { DPRINT1("Unknown DOS CMD Interpreter BOP Function: 0x%02X\n", FuncNum); // setCF(1); // Disable, otherwise we enter an infinite loop break; } } }
/*********************************************************************** * MZ_Exec * * this may only be called from existing DOS processes */ BOOL WINAPI MZ_Exec( CONTEXT86 *context, LPCSTR filename, BYTE func, LPVOID paramblk ) { DWORD binType; STARTUPINFOA st; PROCESS_INFORMATION pe; HANDLE hFile; BOOL ret = FALSE; if(!GetBinaryTypeA(filename, &binType)) /* determine what kind of binary this is */ { return FALSE; /* binary is not an executable */ } /* handle non-dos executables */ if(binType != SCS_DOS_BINARY) { if(func == 0) /* load and execute */ { LPSTR fullCmdLine; WORD fullCmdLength; LPBYTE psp_start = (LPBYTE)((DWORD)DOSVM_psp << 4); PDB16 *psp = (PDB16 *)psp_start; ExecBlock *blk = (ExecBlock *)paramblk; LPBYTE cmdline = PTR_REAL_TO_LIN(SELECTOROF(blk->cmdline),OFFSETOF(blk->cmdline)); LPBYTE envblock = PTR_REAL_TO_LIN(psp->environment, 0); int cmdLength = cmdline[0]; /* * If cmdLength is 127, command tail is truncated and environment * variable CMDLINE should contain full command line * (this includes filename). */ if (cmdLength == 127) { FIXME( "CMDLINE argument passing is unimplemented.\n" ); cmdLength = 126; /* FIXME */ } fullCmdLength = (strlen(filename) + 1) + cmdLength + 1; /* filename + space + cmdline + terminating null character */ fullCmdLine = HeapAlloc(GetProcessHeap(), 0, fullCmdLength); if(!fullCmdLine) return FALSE; /* return false on memory alloc failure */ /* build the full command line from the executable file and the command line being passed in */ snprintf(fullCmdLine, fullCmdLength, "%s ", filename); /* start off with the executable filename and a space */ memcpy(fullCmdLine + strlen(fullCmdLine), cmdline + 1, cmdLength); /* append cmdline onto the end */ fullCmdLine[fullCmdLength - 1] = 0; /* null terminate string */ ZeroMemory (&st, sizeof(STARTUPINFOA)); st.cb = sizeof(STARTUPINFOA); ret = CreateProcessA (NULL, fullCmdLine, NULL, NULL, TRUE, 0, envblock, NULL, &st, &pe); /* wait for the app to finish and clean up PROCESS_INFORMATION handles */ if(ret) { WaitForSingleObject(pe.hProcess, INFINITE); /* wait here until the child process is complete */ CloseHandle(pe.hProcess); CloseHandle(pe.hThread); } HeapFree(GetProcessHeap(), 0, fullCmdLine); /* free the memory we allocated */ } else { FIXME("EXEC type of %d not implemented for non-dos executables\n", func); ret = FALSE; } return ret; } /* if(binType != SCS_DOS_BINARY) */ /* handle dos executables */ hFile = CreateFileA( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); if (hFile == INVALID_HANDLE_VALUE) return FALSE; switch (func) { case 0: /* load and execute */ case 1: /* load but don't execute */ { /* save current process's return SS:SP now */ LPBYTE psp_start = (LPBYTE)((DWORD)DOSVM_psp << 4); PDB16 *psp = (PDB16 *)psp_start; psp->saveStack = (DWORD)MAKESEGPTR(context->SegSs, LOWORD(context->Esp)); } ret = MZ_DoLoadImage( hFile, filename, NULL, ((ExecBlock *)paramblk)->env_seg ); if (ret) { /* MZ_LoadImage created a new PSP and loaded new values into it, * let's work on the new values now */ LPBYTE psp_start = (LPBYTE)((DWORD)DOSVM_psp << 4); ExecBlock *blk = (ExecBlock *)paramblk; LPBYTE cmdline = PTR_REAL_TO_LIN(SELECTOROF(blk->cmdline),OFFSETOF(blk->cmdline)); /* First character contains the length of the command line. */ MZ_FillPSP(psp_start, (LPSTR)cmdline + 1, cmdline[0]); /* the lame MS-DOS engineers decided that the return address should be in int22 */ DOSVM_SetRMHandler(0x22, (FARPROC16)MAKESEGPTR(context->SegCs, LOWORD(context->Eip))); if (func) { /* don't execute, just return startup state */ /* * From Ralph Brown: * For function 01h, the AX value to be passed to the child program * is put on top of the child's stack */ LPBYTE stack; init_sp -= 2; stack = (LPBYTE) CTX_SEG_OFF_TO_LIN(context, init_ss, init_sp); /* FIXME: push AX correctly */ stack[0] = 0x00; /* push AL */ stack[1] = 0x00; /* push AH */ blk->init_cs = init_cs; blk->init_ip = init_ip; blk->init_ss = init_ss; blk->init_sp = init_sp; } else { /* execute by making us return to new process */ context->SegCs = init_cs; context->Eip = init_ip; context->SegSs = init_ss; context->Esp = init_sp; context->SegDs = DOSVM_psp; context->SegEs = DOSVM_psp; context->Eax = 0; } } break; case 3: /* load overlay */ { OverlayBlock *blk = (OverlayBlock *)paramblk; ret = MZ_DoLoadImage( hFile, filename, blk, 0); } break; default: FIXME("EXEC load type %d not implemented\n", func); SetLastError(ERROR_INVALID_FUNCTION); break; } CloseHandle(hFile); return ret; }