dosexe.c 21.8 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3
/*
 * DOS (MZ) loader
 *
4
 * Copyright 1998 Ove Kåven
Alexandre Julliard's avatar
Alexandre Julliard committed
5
 *
6 7 8 9 10 11 12 13 14 15 16 17
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 20
 *
 * Note: This code hasn't been completely cleaned up yet.
Alexandre Julliard's avatar
Alexandre Julliard committed
21 22
 */

23
#include "config.h"
24
#include "wine/port.h"
25

26
#include <stdarg.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
27 28 29 30 31
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
32 33 34
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
35
#include <sys/types.h>
36 37 38
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
39 40 41
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
42
#include "windef.h"
43
#include "winbase.h"
44
#include "wine/winbase16.h"
45 46
#include "wingdi.h"
#include "winuser.h"
47
#include "winerror.h"
48
#include "wine/debug.h"
49
#include "kernel16_private.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
50
#include "dosexe.h"
51
#include "vga.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
52

53
WINE_DEFAULT_DEBUG_CHANNEL(module);
54

55 56 57 58 59 60 61 62 63
static BOOL DOSVM_isdosexe;

/**********************************************************************
 *          DOSVM_IsWin16
 * 
 * Return TRUE if we are in Windows process.
 */
BOOL DOSVM_IsWin16(void)
{
64
  return !DOSVM_isdosexe;
65 66
}

67 68 69 70 71 72 73 74 75 76 77 78
/**********************************************************************
 *          DOSVM_Exit
 */
void DOSVM_Exit( WORD retval )
{
    DWORD count;

    ReleaseThunkLock( &count );
    ExitThread( retval );
}


Alexandre Julliard's avatar
Alexandre Julliard committed
79 80
#ifdef MZ_SUPPORTED

Alexandre Julliard's avatar
Alexandre Julliard committed
81 82 83 84 85 86
#define BIOS_DATA_SEGMENT 0x40
#define PSP_SIZE 0x10

#define SEG16(ptr,seg) ((LPVOID)((BYTE*)ptr+((DWORD)(seg)<<4)))
#define SEGPTR16(ptr,segptr) ((LPVOID)((BYTE*)ptr+((DWORD)SELECTOROF(segptr)<<4)+OFFSETOF(segptr)))

87 88
/* structures for EXEC */

89 90
#include "pshpack1.h"

91 92
typedef struct {
  WORD env_seg;
93 94 95
  DWORD cmdline;
  DWORD fcb1;
  DWORD fcb2;
96 97 98 99 100 101 102 103 104 105 106
  WORD init_sp;
  WORD init_ss;
  WORD init_ip;
  WORD init_cs;
} ExecBlock;

typedef struct {
  WORD load_seg;
  WORD rel_seg;
} OverlayBlock;

107 108
#include "poppack.h"

109 110
/* global variables */

111
pid_t dosvm_pid;
112

113 114 115
static WORD init_cs,init_ip,init_ss,init_sp;
static HANDLE dosvm_thread, loop_thread;
static DWORD dosvm_tid, loop_tid;
116

117
static DWORD MZ_Launch( LPCSTR cmdtail, int length );
118
static BOOL MZ_InitTask(void);
119

120
static void MZ_CreatePSP( LPVOID lpPSP, WORD env, WORD par )
Alexandre Julliard's avatar
Alexandre Julliard committed
121
{
122 123 124 125 126 127
  PDB16*psp=lpPSP;

  psp->int20=0x20CD; /* int 20 */
  /* some programs use this to calculate how much memory they need */
  psp->nextParagraph=0x9FFF; /* FIXME: use a real value */
  /* FIXME: dispatcher */
128 129 130
  psp->savedint22 = DOSVM_GetRMHandler(0x22);
  psp->savedint23 = DOSVM_GetRMHandler(0x23);
  psp->savedint24 = DOSVM_GetRMHandler(0x24);
131 132 133
  psp->parentPSP=par;
  psp->environment=env;
  /* FIXME: more PSP stuff */
134 135
}

136
static void MZ_FillPSP( LPVOID lpPSP, LPCSTR cmdtail, int length )
137
{
138
    PDB16 *psp = lpPSP;
139

140 141 142 143 144
    if(length > 127) 
    {
        WARN( "Command tail truncated! (length %d)\n", length );
        length = 126;
    }
145

146
    psp->cmdLine[0] = length;
147

148
    /*
149 150 151
     * Length of exactly 127 bytes means that full command line is 
     * stored in environment variable CMDLINE and PSP contains 
     * command tail truncated to 126 bytes.
152
     */
153 154
    if(length == 127)
        length = 126;
155

156 157
    if(length > 0)
        memmove(psp->cmdLine+1, cmdtail, length);
158

159 160 161
    psp->cmdLine[length+1] = '\r';

    /* FIXME: more PSP stuff */
Alexandre Julliard's avatar
Alexandre Julliard committed
162 163
}

164
static WORD MZ_InitEnvironment( LPCSTR env, LPCSTR name )
165 166
{
 unsigned sz=0;
167
 unsigned i=0;
168 169 170 171 172
 WORD seg;
 LPSTR envblk;

 if (env) {
  /* get size of environment block */
173
  while (env[sz++]) sz+=strlen(env+sz)+1;
174 175
 } else sz++;
 /* allocate it */
176
 envblk=DOSMEM_AllocBlock(sz+sizeof(WORD)+strlen(name)+1,&seg);
177 178 179 180
 /* fill it */
 if (env) {
  memcpy(envblk,env,sz);
 } else envblk[0]=0;
181 182 183 184 185 186 187 188 189 190
 /* DOS environment variables are uppercase */
 while (envblk[i]){
  while (envblk[i] != '='){
   if (envblk[i]>='a' && envblk[i] <= 'z'){
    envblk[i] -= 32;
   }
   i++;
  }
  i += strlen(envblk+i) + 1;
 }
191 192 193 194 195 196 197
 /* DOS 3.x: the block contains 1 additional string */
 *(WORD*)(envblk+sz)=1;
 /* being the program name itself */
 strcpy(envblk+sz+sizeof(WORD),name);
 return seg;
}

198
static BOOL MZ_InitMemory(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
199
{
200 201
    /* initialize the memory */
    TRACE("Initializing DOS memory structures\n");
202
    DOSMEM_MapDosLayout();
203
    DOSDEV_InstallDOSDevices();
204
    MSCDEX_InstallCDROM();
205

206
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
207 208
}

209
static BOOL MZ_DoLoadImage( HANDLE hFile, LPCSTR filename, OverlayBlock *oblk, WORD par_env_seg )
Alexandre Julliard's avatar
Alexandre Julliard committed
210
{
211 212
  IMAGE_DOS_HEADER mz_header;
  DWORD image_start,image_size,min_size,max_size,avail;
Mike McCormack's avatar
Mike McCormack committed
213 214
  BYTE*psp_start,*load_start;
  LPSTR oldenv = 0;
215
  int x, old_com=0, alloc;
216
  SEGPTR reloc;
217
  WORD env_seg, load_seg, rel_seg, oldpsp_seg;
218 219
  DWORD len;

220
  if (DOSVM_psp) {
221
    /* DOS process already running, inherit from it */
222
    PDB16* par_psp;
223
    alloc=0;
224
    oldpsp_seg = DOSVM_psp;
225 226
    if( !par_env_seg) {  
        par_psp = (PDB16*)((DWORD)DOSVM_psp << 4);
Mike McCormack's avatar
Mike McCormack committed
227
        oldenv = (LPSTR)((DWORD)par_psp->environment << 4);
228
    }
229 230
  } else {
    /* allocate new DOS process, inheriting from Wine environment */
231
    alloc=1;
232
    oldpsp_seg = 0;
233 234
    if( !par_env_seg)
        oldenv = GetEnvironmentStringsA();
235
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
236

237 238
 SetFilePointer(hFile,0,NULL,FILE_BEGIN);
 if (   !ReadFile(hFile,&mz_header,sizeof(mz_header),&len,NULL)
239
     || len != sizeof(mz_header)
240
     || mz_header.e_magic != IMAGE_DOS_SIGNATURE) {
241
  const char *p = strrchr( filename, '.' );
242 243 244 245 246
  if (!p || strcasecmp( p, ".com" ))  /* check for .COM extension */
  {
      SetLastError(ERROR_BAD_FORMAT);
      goto load_error;
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
247 248
  old_com=1; /* assume .COM file */
  image_start=0;
249
  image_size=GetFileSize(hFile,NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
250 251 252 253 254 255 256 257
  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 */
258 259 260
  /* 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. */
Alexandre Julliard's avatar
Alexandre Julliard committed
261 262 263 264 265 266
  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);
 }

267 268 269 270 271 272 273 274 275
  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 */
276 277 278 279
    if( par_env_seg)
        env_seg = par_env_seg;
    else
        env_seg=MZ_InitEnvironment(oldenv, filename);
280
    if (alloc)
281
        FreeEnvironmentStringsA( oldenv);
282 283

    /* allocate memory for the executable */
284
    TRACE("Allocating DOS memory (min=%d, max=%d)\n",min_size,max_size);
285 286 287 288 289 290 291
    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;
292
    psp_start=DOSMEM_AllocBlock(avail,&DOSVM_psp);
293 294 295 296 297
    if (!psp_start) {
      ERR("error allocating DOS memory\n");
      SetLastError(ERROR_NOT_ENOUGH_MEMORY);
      goto load_error;
    }
298
    load_seg=DOSVM_psp+(old_com?0:PSP_SIZE);
299 300 301 302
    rel_seg=load_seg;
    load_start=psp_start+(PSP_SIZE<<4);
    MZ_CreatePSP(psp_start, env_seg, oldpsp_seg);
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
303 304

 /* load executable image */
305
 TRACE("loading DOS %s image, %08x bytes\n",old_com?"COM":"EXE",image_size);
306 307
 SetFilePointer(hFile,image_start,NULL,FILE_BEGIN);
 if (!ReadFile(hFile,load_start,image_size,&len,NULL) || len != image_size) {
308
  /* check if this is due to the workaround for the pre-1.10 MS linker and we
309
     really had only 4 bytes on the last page */
310 311 312 313
  if (mz_header.e_cblp != 4 || image_size - len != 512 - 4) {
    SetLastError(ERROR_BAD_FORMAT);
    goto load_error;
  }
314
 }
Alexandre Julliard's avatar
Alexandre Julliard committed
315

Alexandre Julliard's avatar
Alexandre Julliard committed
316 317
 if (mz_header.e_crlc) {
  /* load relocation table */
318
  TRACE("loading DOS EXE relocation table, %d entries\n",mz_header.e_crlc);
Alexandre Julliard's avatar
Alexandre Julliard committed
319
  /* FIXME: is this too slow without read buffering? */
320
  SetFilePointer(hFile,mz_header.e_lfarlc,NULL,FILE_BEGIN);
Alexandre Julliard's avatar
Alexandre Julliard committed
321
  for (x=0; x<mz_header.e_crlc; x++) {
322
   if (!ReadFile(hFile,&reloc,sizeof(reloc),&len,NULL) || len != sizeof(reloc)) {
323
    SetLastError(ERROR_BAD_FORMAT);
324
    goto load_error;
325
   }
326
   *(WORD*)SEGPTR16(load_start,reloc)+=rel_seg;
Alexandre Julliard's avatar
Alexandre Julliard committed
327
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
328 329
 }

330 331 332 333 334
  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;
Markus Amsler's avatar
Markus Amsler committed
335 336 337 338 339
    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;
    }
340

341 342
    TRACE("entry point: %04x:%04x\n",init_cs,init_ip);
  }
343

344
  if (alloc && !MZ_InitTask()) {
345 346 347 348 349 350 351
    SetLastError(ERROR_GEN_FAILURE);
    return FALSE;
  }

  return TRUE;

load_error:
352
  DOSVM_psp = oldpsp_seg;
353 354

  return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
355 356
}

Patrik Stridvall's avatar
Patrik Stridvall committed
357
/***********************************************************************
358
 *		__wine_load_dos_exe (KERNEL.@)
359
 *
360
 * Called from WineVDM when a new real-mode DOS process is started.
361
 * Loads DOS program into memory and executes the program.
Patrik Stridvall's avatar
Patrik Stridvall committed
362
 */
363
void __wine_load_dos_exe( LPCSTR filename, LPCSTR cmdline )
364
{
365 366
    char dos_cmdtail[126];
    int  dos_length = 0;
367

368 369 370 371
    HANDLE hFile = CreateFileA( filename, GENERIC_READ, FILE_SHARE_READ, 
                                NULL, OPEN_EXISTING, 0, 0 );
    if (hFile == INVALID_HANDLE_VALUE) return;
    DOSVM_isdosexe = TRUE;
372
    DOSMEM_InitDosMemory();
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435

    if(cmdline && *cmdline)
    {
        dos_length = strlen(cmdline);
        memmove( dos_cmdtail + 1, cmdline, 
                 (dos_length < 125) ? dos_length : 125 );

        /* Non-empty command tail always starts with at least one space. */
        dos_cmdtail[0] = ' ';
        dos_length++;

        /*
         * If command tail is longer than 126 characters,
         * set tail length to 127 and fill CMDLINE environment variable 
         * with full command line (this includes filename).
         */
        if (dos_length > 126)
        {
            char *cmd = HeapAlloc( GetProcessHeap(), 0, 
                                   dos_length + strlen(filename) + 4 );
            char *ptr = cmd;

            if (!cmd)
                return;

            /*
             * Append filename. If path includes spaces, quote the path.
             */
            if (strchr(filename, ' '))
            {
                *ptr++ = '\"';
                strcpy( ptr, filename );
                ptr += strlen(filename);                   
                *ptr++ = '\"';
            }
            else
            {
                strcpy( ptr, filename );
                ptr += strlen(filename);  
            }

            /*
             * Append command tail.
             */
            if (cmdline[0] != ' ')
                *ptr++ = ' ';
            strcpy( ptr, cmdline );

            /*
             * Set environment variable. This will be passed to
             * new DOS process.
             */
            if (!SetEnvironmentVariableA( "CMDLINE", cmd ))
            {
                HeapFree(GetProcessHeap(), 0, cmd );
                return;
            }

            HeapFree(GetProcessHeap(), 0, cmd );
            dos_length = 127;
        }
    }

436 437
    AllocConsole();

438 439 440 441 442 443
    if (MZ_DoLoadImage( hFile, filename, NULL, 0 ))
    {
        DWORD err = MZ_Launch( dos_cmdtail, dos_length );
        /* if we get back here it failed */
        SetLastError( err );
    }
444 445
}

Patrik Stridvall's avatar
Patrik Stridvall committed
446
/***********************************************************************
447
 *		MZ_Exec
448 449
 *
 * this may only be called from existing DOS processes
Patrik Stridvall's avatar
Patrik Stridvall committed
450
 */
451
BOOL MZ_Exec( CONTEXT *context, LPCSTR filename, BYTE func, LPVOID paramblk )
452
{
453 454 455 456 457
  DWORD binType;
  STARTUPINFOA st;
  PROCESS_INFORMATION pe;
  HANDLE hFile;

458
  BOOL ret = FALSE;
459 460 461 462 463 464 465 466 467 468 469

  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 */
    {
Mike McCormack's avatar
Mike McCormack committed
470
      LPSTR fullCmdLine;
471 472 473
      WORD fullCmdLength;
      LPBYTE psp_start = (LPBYTE)((DWORD)DOSVM_psp << 4);
      PDB16 *psp = (PDB16 *)psp_start;
474
      ExecBlock *blk = paramblk;
475 476
      LPBYTE cmdline = PTR_REAL_TO_LIN(SELECTOROF(blk->cmdline),OFFSETOF(blk->cmdline));
      LPBYTE envblock = PTR_REAL_TO_LIN(psp->environment, 0);
477
      int    cmdLength = cmdline[0];
478

479
      /*
480 481 482
       * If cmdLength is 127, command tail is truncated and environment 
       * variable CMDLINE should contain full command line 
       * (this includes filename).
483
       */
484 485 486 487 488
      if (cmdLength == 127)
      {
          FIXME( "CMDLINE argument passing is unimplemented.\n" );
          cmdLength = 126; /* FIXME */
      }
489

490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
      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);
528
  if (hFile == INVALID_HANDLE_VALUE) return FALSE;
529

530 531 532 533 534
  switch (func) {
  case 0: /* load and execute */
  case 1: /* load but don't execute */
    {
      /* save current process's return SS:SP now */
535
      LPBYTE psp_start = (LPBYTE)((DWORD)DOSVM_psp << 4);
536
      PDB16 *psp = (PDB16 *)psp_start;
537
      psp->saveStack = (DWORD)MAKESEGPTR(context->SegSs, LOWORD(context->Esp));
538
    }
539
    ret = MZ_DoLoadImage( hFile, filename, NULL, ((ExecBlock *)paramblk)->env_seg );
540
    if (ret) {
541
      /* MZ_LoadImage created a new PSP and loaded new values into it,
542
       * let's work on the new values now */
543
      LPBYTE psp_start = (LPBYTE)((DWORD)DOSVM_psp << 4);
544
      ExecBlock *blk = paramblk;
545
      LPBYTE cmdline = PTR_REAL_TO_LIN(SELECTOROF(blk->cmdline),OFFSETOF(blk->cmdline));
546 547

      /* First character contains the length of the command line. */
Mike McCormack's avatar
Mike McCormack committed
548
      MZ_FillPSP(psp_start, (LPSTR)cmdline + 1, cmdline[0]);
549

550
      /* the lame MS-DOS engineers decided that the return address should be in int22 */
551
      DOSVM_SetRMHandler(0x22, (FARPROC16)MAKESEGPTR(context->SegCs, LOWORD(context->Eip)));
552 553
      if (func) {
	/* don't execute, just return startup state */
554 555 556 557 558 559 560
        /*
         * 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;
561
        stack = CTX_SEG_OFF_TO_LIN(context, init_ss, init_sp);
562 563 564 565
        /* FIXME: push AX correctly */
        stack[0] = 0x00;    /* push AL */
        stack[1] = 0x00;    /* push AH */
	
566 567 568 569 570 571 572 573 574 575
	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;
576 577
	context->SegDs = DOSVM_psp;
	context->SegEs = DOSVM_psp;
578 579 580 581 582 583
	context->Eax   = 0;
      }
    }
    break;
  case 3: /* load overlay */
    {
584
      OverlayBlock *blk = paramblk;
585
      ret = MZ_DoLoadImage( hFile, filename, blk, 0);
586 587 588 589 590 591 592 593 594 595 596
    }
    break;
  default:
    FIXME("EXEC load type %d not implemented\n", func);
    SetLastError(ERROR_INVALID_FUNCTION);
    break;
  }
  CloseHandle(hFile);
  return ret;
}

Patrik Stridvall's avatar
Patrik Stridvall committed
597
/***********************************************************************
598
 *		MZ_AllocDPMITask
Patrik Stridvall's avatar
Patrik Stridvall committed
599
 */
600
void MZ_AllocDPMITask( void )
601
{
602 603
  MZ_InitMemory();
  MZ_InitTask();
604 605
}

Patrik Stridvall's avatar
Patrik Stridvall committed
606
/***********************************************************************
607
 *		MZ_RunInThread
Patrik Stridvall's avatar
Patrik Stridvall committed
608
 */
609
void MZ_RunInThread( PAPCFUNC proc, ULONG_PTR arg )
610
{
611 612 613 614 615 616
  if (loop_thread) {
    DOS_SPC spc;
    HANDLE event;

    spc.proc = proc;
    spc.arg = arg;
617
    event = CreateEventW(NULL, TRUE, FALSE, NULL);
618
    PostThreadMessageA(loop_tid, WM_USER, (WPARAM)event, (LPARAM)&spc);
619 620 621 622
    WaitForSingleObject(event, INFINITE);
    CloseHandle(event);
  } else
    proc(arg);
623 624
}

625
static DWORD WINAPI MZ_DOSVM( LPVOID lpExtra )
626
{
627
  CONTEXT context;
628
  INT ret;
629

630
  dosvm_pid = getpid();
631

632 633 634 635 636 637 638
  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;
639
  context.EFlags = V86_FLAG | VIF_MASK;
640 641
  DOSVM_SetTimer(0x10000);
  ret = DOSVM_Enter( &context );
642
  if (ret == -1) ret = GetLastError();
643
  dosvm_pid = 0;
644
  return ret;
645
}
Alexandre Julliard's avatar
Alexandre Julliard committed
646

647 648 649 650 651
static BOOL MZ_InitTask(void)
{
  if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
                       GetCurrentProcess(), &loop_thread,
                       0, FALSE, DUPLICATE_SAME_ACCESS))
652
    return FALSE;
653 654 655 656
  dosvm_thread = CreateThread(NULL, 0, MZ_DOSVM, NULL, CREATE_SUSPENDED, &dosvm_tid);
  if (!dosvm_thread) {
    CloseHandle(loop_thread);
    loop_thread = 0;
657
    return FALSE;
658
  }
659 660
  loop_tid = GetCurrentThreadId();
  return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
661 662
}

663
static DWORD MZ_Launch( LPCSTR cmdtail, int length )
Alexandre Julliard's avatar
Alexandre Julliard committed
664
{
665
  TDB *pTask = GlobalLock16( GetCurrentTask() );
666
  BYTE *psp_start = PTR_REAL_TO_LIN( DOSVM_psp, 0 );
667
  DWORD rv;
668
  SYSLEVEL *lock;
669
  MSG msg;
Alexandre Julliard's avatar
Alexandre Julliard committed
670

671
  MZ_FillPSP(psp_start, cmdtail, length);
672 673
  pTask->flags |= TDBF_WINOLDAP;

674 675 676
  /* DTA is set to PSP:0080h when a program is started. */
  pTask->dta = MAKESEGPTR( DOSVM_psp, 0x80 );

677 678
  GetpWin16Lock( &lock );
  _LeaveSysLevel( lock );
679

680 681 682
  /* force the message queue to be created */
  PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);

683
  ResumeThread(dosvm_thread);
684
  rv = DOSVM_Loop(dosvm_thread);
Alexandre Julliard's avatar
Alexandre Julliard committed
685

686 687 688 689
  CloseHandle(dosvm_thread);
  dosvm_thread = 0; dosvm_tid = 0;
  CloseHandle(loop_thread);
  loop_thread = 0; loop_tid = 0;
690
  if (rv) return rv;
691 692

  VGA_Clean();
693
  ExitProcess(0);
694 695
}

Patrik Stridvall's avatar
Patrik Stridvall committed
696
/***********************************************************************
697
 *		MZ_Exit
Patrik Stridvall's avatar
Patrik Stridvall committed
698
 */
699
void MZ_Exit( CONTEXT *context, BOOL cs_psp, WORD retval )
700
{
701 702
  if (DOSVM_psp) {
    WORD psp_seg = cs_psp ? context->SegCs : DOSVM_psp;
703 704 705 706 707
    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 */
708
      FARPROC16 retaddr = DOSVM_GetRMHandler(0x22);
709
      /* restore interrupts */
710 711 712
      DOSVM_SetRMHandler(0x22, psp->savedint22);
      DOSVM_SetRMHandler(0x23, psp->savedint23);
      DOSVM_SetRMHandler(0x24, psp->savedint24);
713 714 715
      /* FIXME: deallocate file handles etc */
      /* free process's associated memory
       * FIXME: walk memory and deallocate all blocks owned by process */
716 717
      DOSMEM_FreeBlock( PTR_REAL_TO_LIN(psp->environment,0) );
      DOSMEM_FreeBlock( PTR_REAL_TO_LIN(DOSVM_psp,0) );
718
      /* switch to parent's PSP */
719
      DOSVM_psp = parpsp;
720 721 722
      psp_start = (LPBYTE)((DWORD)parpsp << 4);
      psp = (PDB16 *)psp_start;
      /* now return to parent */
723
      DOSVM_retval = retval;
724 725 726 727 728 729
      context->SegCs = SELECTOROF(retaddr);
      context->Eip   = OFFSETOF(retaddr);
      context->SegSs = SELECTOROF(psp->saveStack);
      context->Esp   = OFFSETOF(psp->saveStack);
      return;
    } else
730
      TRACE("killing DOS task\n");
731
  }
732
  DOSVM_Exit( retval );
733 734
}

735 736 737 738

/***********************************************************************
 *		MZ_Current
 */
739
BOOL MZ_Current( void )
740
{
741
  return (dosvm_pid != 0); /* FIXME: do a better check */
742 743
}

Alexandre Julliard's avatar
Alexandre Julliard committed
744
#else /* !MZ_SUPPORTED */
Alexandre Julliard's avatar
Alexandre Julliard committed
745

Patrik Stridvall's avatar
Patrik Stridvall committed
746
/***********************************************************************
747
 *		__wine_load_dos_exe (KERNEL.@)
Patrik Stridvall's avatar
Patrik Stridvall committed
748
 */
749
void __wine_load_dos_exe( LPCSTR filename, LPCSTR cmdline )
Alexandre Julliard's avatar
Alexandre Julliard committed
750
{
751
    SetLastError( ERROR_NOT_SUPPORTED );
752 753
}

Patrik Stridvall's avatar
Patrik Stridvall committed
754
/***********************************************************************
755
 *		MZ_Exec
Patrik Stridvall's avatar
Patrik Stridvall committed
756
 */
757
BOOL MZ_Exec( CONTEXT *context, LPCSTR filename, BYTE func, LPVOID paramblk )
758 759 760 761
{
  /* can't happen */
  SetLastError(ERROR_BAD_FORMAT);
  return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
762 763
}

Patrik Stridvall's avatar
Patrik Stridvall committed
764
/***********************************************************************
765
 *		MZ_AllocDPMITask
Patrik Stridvall's avatar
Patrik Stridvall committed
766
 */
767
void MZ_AllocDPMITask( void )
768
{
769
    FIXME("Actual real-mode calls not supported on this platform!\n");
770 771
}

772 773 774
/***********************************************************************
 *		MZ_RunInThread
 */
775
void MZ_RunInThread( PAPCFUNC proc, ULONG_PTR arg )
776 777 778 779
{
    proc(arg);
}

Patrik Stridvall's avatar
Patrik Stridvall committed
780
/***********************************************************************
781
 *		MZ_Exit
Patrik Stridvall's avatar
Patrik Stridvall committed
782
 */
783
void MZ_Exit( CONTEXT *context, BOOL cs_psp, WORD retval )
784
{
785
  DOSVM_Exit( retval );
786 787
}

Patrik Stridvall's avatar
Patrik Stridvall committed
788
/***********************************************************************
789
 *		MZ_Current
Patrik Stridvall's avatar
Patrik Stridvall committed
790
 */
791
BOOL MZ_Current( void )
792
{
793
    return FALSE;
794
}
795 796

#endif /* !MZ_SUPPORTED */