static DWORD WINAPI MZ_DOSVM( LPVOID lpExtra ) { CONTEXT context; INT ret; dosvm_pid = getpid(); memset( &context, 0, sizeof(context) ); 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.EFlags = V86_FLAG | VIF_MASK; DOSVM_SetTimer(0x10000); ret = DOSVM_Enter( &context ); if (ret == -1) { /* fetch the app name from the environment */ PDB16 *psp = PTR_REAL_TO_LIN( DOSVM_psp, 0 ); char *env = PTR_REAL_TO_LIN( psp->environment, 0 ); while (*env) env += strlen(env) + 1; env += 1 + sizeof(WORD); if (GetLastError() == ERROR_NOT_SUPPORTED) MESSAGE( "wine: Cannot start DOS application %s\n" " because vm86 mode is not supported on this platform.\n", debugstr_a(env) ); else FIXME( "vm86 mode failed error %u\n", GetLastError() ); } dosvm_pid = 0; return ret != 0; }
/********************************************************************** * EMS_access_name * * Get/set handle name. */ static void EMS_access_name( CONTEXT86 *context ) { char *ptr; int hindex = DX_reg(context); if(hindex < 0 || hindex >= EMS_MAX_HANDLES) { SET_AH( context, 0x83 ); /* invalid handle */ return; } switch AL_reg(context) { case 0x00: /* get name */ ptr = PTR_REAL_TO_LIN(context->SegEs, DI_reg(context)); memcpy(ptr, EMS_record->handle[hindex].name, 8); SET_AH( context, 0 ); break; case 0x01: /* set name */ ptr = PTR_REAL_TO_LIN(context->SegDs, SI_reg(context)); memcpy(EMS_record->handle[hindex].name, ptr, 8); SET_AH( context, 0 ); break; default: INT_BARF(context,0x67); break; } }
/*********************************************************************** * MZ_Exit */ void WINAPI MZ_Exit( CONTEXT86 *context, BOOL cs_psp, WORD retval ) { if (DOSVM_psp) { WORD psp_seg = cs_psp ? context->SegCs : DOSVM_psp; LPBYTE psp_start = (LPBYTE)((DWORD)psp_seg << 4); PDB16 *psp = (PDB16 *)psp_start; WORD parpsp = psp->parentPSP; /* check for parent DOS process */ if (parpsp) { /* retrieve parent's return address */ FARPROC16 retaddr = DOSVM_GetRMHandler(0x22); /* restore interrupts */ DOSVM_SetRMHandler(0x22, psp->savedint22); DOSVM_SetRMHandler(0x23, psp->savedint23); DOSVM_SetRMHandler(0x24, psp->savedint24); /* FIXME: deallocate file handles etc */ /* free process's associated memory * FIXME: walk memory and deallocate all blocks owned by process */ DOSMEM_FreeBlock( PTR_REAL_TO_LIN(psp->environment,0) ); DOSMEM_FreeBlock( PTR_REAL_TO_LIN(DOSVM_psp,0) ); /* switch to parent's PSP */ DOSVM_psp = parpsp; psp_start = (LPBYTE)((DWORD)parpsp << 4); psp = (PDB16 *)psp_start; /* now return to parent */ DOSVM_retval = retval; context->SegCs = SELECTOROF(retaddr); context->Eip = OFFSETOF(retaddr); context->SegSs = SELECTOROF(psp->saveStack); context->Esp = OFFSETOF(psp->saveStack); return; } else TRACE("killing DOS task\n"); } ExitThread( retval ); }
static void MZ_Launch( LPCSTR cmdtail, int length ) { TDB *pTask = GlobalLock16( GetCurrentTask() ); BYTE *psp_start = PTR_REAL_TO_LIN( DOSVM_psp, 0 ); DWORD rv; SYSLEVEL *lock; MZ_FillPSP(psp_start, cmdtail, length); pTask->flags |= TDBF_WINOLDAP; /* DTA is set to PSP:0080h when a program is started. */ pTask->dta = MAKESEGPTR( DOSVM_psp, 0x80 ); GetpWin16Lock( &lock ); _LeaveSysLevel( lock ); ResumeThread(dosvm_thread); rv = DOSVM_Loop(dosvm_thread); CloseHandle(dosvm_thread); dosvm_thread = 0; dosvm_tid = 0; CloseHandle(loop_thread); loop_thread = 0; loop_tid = 0; VGA_Clean(); ExitProcess(rv); }
static DWORD MZ_Launch( LPCSTR cmdtail, int length ) { TDB *pTask = GlobalLock16( GetCurrentTask() ); BYTE *psp_start = PTR_REAL_TO_LIN( DOSVM_psp, 0 ); DWORD rv; SYSLEVEL *lock; MSG msg; MZ_FillPSP(psp_start, cmdtail, length); pTask->flags |= TDBF_WINOLDAP; /* DTA is set to PSP:0080h when a program is started. */ pTask->dta = MAKESEGPTR( DOSVM_psp, 0x80 ); GetpWin16Lock( &lock ); _LeaveSysLevel( lock ); /* force the message queue to be created */ PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); ResumeThread(dosvm_thread); rv = DOSVM_Loop(dosvm_thread); CloseHandle(dosvm_thread); dosvm_thread = 0; dosvm_tid = 0; CloseHandle(loop_thread); loop_thread = 0; loop_tid = 0; if (rv) return rv; VGA_Clean(); ExitProcess(0); }
void DOSDEV_SetupDevice(const WINEDEV * devinfo, WORD seg, WORD off_dev, WORD off_thunk) { DOS_DEVICE_HEADER *dev = PTR_REAL_TO_LIN(seg, off_dev); WINEDEV_THUNK *thunk = PTR_REAL_TO_LIN(seg, off_thunk); DOS_DATASEG *dataseg = (DOS_DATASEG*)DOSMEM_LOL(); dev->attr = devinfo->attr; dev->strategy = off_thunk + FIELD_OFFSET(WINEDEV_THUNK, ljmp1); dev->interrupt = off_thunk + FIELD_OFFSET(WINEDEV_THUNK, ljmp2); memcpy(dev->name, devinfo->name, 8); thunk->ljmp1 = LJMP; thunk->strategy = DPMI_AllocInternalRMCB(devinfo->strategy); thunk->ljmp2 = LJMP; thunk->interrupt = DPMI_AllocInternalRMCB(devinfo->interrupt); dev->next_dev = NONEXT; if (dataseg->last_dev) dataseg->last_dev->next_dev = MAKESEGPTR(seg, off_dev); dataseg->last_dev = dev; }
DWORD DOSDEV_FindCharDevice(char*name) { SEGPTR cur_ptr = MAKESEGPTR(HIWORD(DOS_LOLSeg), FIELD_OFFSET(DOS_LISTOFLISTS,NUL_dev)); DOS_DEVICE_HEADER *cur = PTR_REAL_TO_LIN(SELECTOROF(cur_ptr),OFFSETOF(cur_ptr)); char dname[8]; int cnt; /* get first 8 characters */ /* if less than 8 characters, pad with spaces */ for (cnt=0; name[cnt] && cnt<8; cnt++) dname[cnt]=name[cnt]; while(cnt<8) dname[cnt++] = ' '; /* search for char devices with the right name */ while (cur && ((!(cur->attr & ATTR_CHAR)) || memcmp(cur->name,dname,8))) { cur_ptr = cur->next_dev; if (cur_ptr == NONEXT) cur=NULL; else cur = PTR_REAL_TO_LIN(SELECTOROF(cur_ptr),OFFSETOF(cur_ptr)); } return cur_ptr; }
static void DOSDEV_DoReq(void*req, DWORD dev) { REQUEST_HEADER *hdr = (REQUEST_HEADER *)req; DOS_DEVICE_HEADER *dhdr; CONTEXT86 ctx; char *phdr; dhdr = PTR_REAL_TO_LIN(SELECTOROF(dev),OFFSETOF(dev)); phdr = ((char*)DOSMEM_LOL()) + DOS_DATASEG_OFF(req); /* copy request to request scratch area */ memcpy(phdr, req, hdr->size); /* prepare to call device driver */ memset(&ctx, 0, sizeof(ctx)); ctx.EFlags |= V86_FLAG; /* ES:BX points to request for strategy routine */ ctx.SegEs = HIWORD(DOS_LOLSeg); ctx.Ebx = DOS_DATASEG_OFF(req); /* call strategy routine */ ctx.SegCs = SELECTOROF(dev); ctx.Eip = dhdr->strategy; DPMI_CallRMProc(&ctx, 0, 0, 0); /* call interrupt routine */ ctx.SegCs = SELECTOROF(dev); ctx.Eip = dhdr->interrupt; DPMI_CallRMProc(&ctx, 0, 0, 0); /* completed, copy request back */ memcpy(req, phdr, hdr->size); if (hdr->status & STAT_ERROR) { switch (hdr->status & STAT_MASK) { case 0x0F: /* invalid disk change */ /* this error seems to fit the bill */ SetLastError(ERROR_NOT_SAME_DEVICE); break; default: SetLastError((hdr->status & STAT_MASK) + 0x13); break; } } }
/********************************************************************** * EMS_map_multiple * * Map multiple logical pages into physical pages. */ static void EMS_map_multiple( CONTEXT86 *context ) { WORD *ptr = PTR_REAL_TO_LIN(context->SegDs, SI_reg(context)); BYTE status = 0; int i; for(i=0; i<CX_reg(context) && !status; i++, ptr += 2) switch(AL_reg(context)) { case 0x00: status = EMS_map( ptr[1], DX_reg(context), ptr[0] ); break; case 0x01: status = EMS_map( (ptr[1] - EMS_record->frame_selector) >> 10, DX_reg(context), ptr[0] ); break; default: status = 0x8f; /* status: undefined subfunction */ } SET_AH( context, status ); }
static int DPMI_FreeRMCB( DWORD address ) { RMCB *CurrRMCB = FirstRMCB; RMCB *PrevRMCB = NULL; while (CurrRMCB && (CurrRMCB->address != address)) { PrevRMCB = CurrRMCB; CurrRMCB = CurrRMCB->next; } if (CurrRMCB) { if (PrevRMCB) PrevRMCB->next = CurrRMCB->next; else FirstRMCB = CurrRMCB->next; DOSMEM_FreeBlock(PTR_REAL_TO_LIN(SELECTOROF(CurrRMCB->address),OFFSETOF(CurrRMCB->address))); HeapFree(GetProcessHeap(), 0, CurrRMCB); return 0; } return 1; }
static BYTE * XMS_Offset( MOVEOFS *ofs ) { if (ofs->Handle) return (BYTE*)GlobalLock16(ofs->Handle)+ofs->Offset; else return (BYTE*)PTR_REAL_TO_LIN(SELECTOROF(ofs->Offset),OFFSETOF(ofs->Offset)); }
static struct _DOS_LISTOFLISTS * DOSMEM_LOL(void) { return PTR_REAL_TO_LIN(HIWORD(DOS_LOLSeg),0); }
/*********************************************************************** * 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; }
static BOOL MZ_DoLoadImage( HANDLE hFile, LPCSTR filename, OverlayBlock *oblk, WORD par_env_seg ) { IMAGE_DOS_HEADER mz_header; DWORD image_start,image_size,min_size,max_size,avail; BYTE*psp_start,*load_start; LPSTR oldenv = 0; int x, old_com=0, alloc; SEGPTR reloc; WORD env_seg, load_seg, rel_seg, oldpsp_seg; DWORD len; if (DOSVM_psp) { /* DOS process already running, inherit from it */ PDB16* par_psp; alloc=0; oldpsp_seg = DOSVM_psp; if( !par_env_seg) { par_psp = (PDB16*)((DWORD)DOSVM_psp << 4); oldenv = (LPSTR)((DWORD)par_psp->environment << 4); } } else { /* allocate new DOS process, inheriting from Wine environment */ alloc=1; oldpsp_seg = 0; if( !par_env_seg) oldenv = GetEnvironmentStringsA(); } SetFilePointer(hFile,0,NULL,FILE_BEGIN); if ( !ReadFile(hFile,&mz_header,sizeof(mz_header),&len,NULL) || len != sizeof(mz_header) || mz_header.e_magic != IMAGE_DOS_SIGNATURE) { char *p = strrchr( filename, '.' ); if (!p || strcasecmp( p, ".com" )) /* check for .COM extension */ { SetLastError(ERROR_BAD_FORMAT); goto load_error; } old_com=1; /* assume .COM file */ image_start=0; image_size=GetFileSize(hFile,NULL); min_size=0x10000; max_size=0x100000; mz_header.e_crlc=0; mz_header.e_ss=0; mz_header.e_sp=0xFFFE; mz_header.e_cs=0; mz_header.e_ip=0x100; } else { /* calculate load size */ image_start=mz_header.e_cparhdr<<4; image_size=mz_header.e_cp<<9; /* pages are 512 bytes */ /* From Ralf Brown Interrupt List: If the word at offset 02h is 4, it should * be treated as 00h, since pre-1.10 versions of the MS linker set it that * way. */ if ((mz_header.e_cblp!=0)&&(mz_header.e_cblp!=4)) image_size-=512-mz_header.e_cblp; image_size-=image_start; min_size=image_size+((DWORD)mz_header.e_minalloc<<4)+(PSP_SIZE<<4); max_size=image_size+((DWORD)mz_header.e_maxalloc<<4)+(PSP_SIZE<<4); } if (alloc) MZ_InitMemory(); if (oblk) { /* load overlay into preallocated memory */ load_seg=oblk->load_seg; rel_seg=oblk->rel_seg; load_start=(LPBYTE)((DWORD)load_seg<<4); } else { /* allocate environment block */ if( par_env_seg) env_seg = par_env_seg; else env_seg=MZ_InitEnvironment(oldenv, filename); if( alloc && oldenv) FreeEnvironmentStringsA( oldenv); /* allocate memory for the executable */ TRACE("Allocating DOS memory (min=%ld, max=%ld)\n",min_size,max_size); avail=DOSMEM_Available(); if (avail<min_size) { ERR("insufficient DOS memory\n"); SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto load_error; } if (avail>max_size) avail=max_size; psp_start=DOSMEM_AllocBlock(avail,&DOSVM_psp); if (!psp_start) { ERR("error allocating DOS memory\n"); SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto load_error; } load_seg=DOSVM_psp+(old_com?0:PSP_SIZE); rel_seg=load_seg; load_start=psp_start+(PSP_SIZE<<4); MZ_CreatePSP(psp_start, env_seg, oldpsp_seg); } /* load executable image */ TRACE("loading DOS %s image, %08lx bytes\n",old_com?"COM":"EXE",image_size); SetFilePointer(hFile,image_start,NULL,FILE_BEGIN); if (!ReadFile(hFile,load_start,image_size,&len,NULL) || len != image_size) { /* check if this is due to the workaround for the pre-1.10 MS linker and we realy had only 4 bytes on the last page */ if (mz_header.e_cblp != 4 || image_size - len != 512 - 4) { SetLastError(ERROR_BAD_FORMAT); goto load_error; } } if (mz_header.e_crlc) { /* load relocation table */ TRACE("loading DOS EXE relocation table, %d entries\n",mz_header.e_crlc); /* FIXME: is this too slow without read buffering? */ SetFilePointer(hFile,mz_header.e_lfarlc,NULL,FILE_BEGIN); for (x=0; x<mz_header.e_crlc; x++) { if (!ReadFile(hFile,&reloc,sizeof(reloc),&len,NULL) || len != sizeof(reloc)) { SetLastError(ERROR_BAD_FORMAT); goto load_error; } *(WORD*)SEGPTR16(load_start,reloc)+=rel_seg; } } if (!oblk) { init_cs = load_seg+mz_header.e_cs; init_ip = mz_header.e_ip; init_ss = load_seg+mz_header.e_ss; init_sp = mz_header.e_sp; if (old_com){ /* .COM files exit with ret. Make sure they jump to psp start (=int 20) */ WORD* stack = PTR_REAL_TO_LIN(init_ss, init_sp); *stack = 0; } TRACE("entry point: %04x:%04x\n",init_cs,init_ip); } if (alloc && !MZ_InitTask()) { SetLastError(ERROR_GEN_FAILURE); return FALSE; } return TRUE; load_error: DOSVM_psp = oldpsp_seg; return FALSE; }