module.c 21.3 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3 4
/*
 * DOS (MZ) loader
 *
 * Copyright 1998 Ove Kven
Alexandre Julliard's avatar
Alexandre Julliard committed
5
 *
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 * 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
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * 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 36
#include <sys/types.h>
#include <sys/stat.h>
37 38 39
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
40
#include "windef.h"
41
#include "winbase.h"
42
#include "wine/winbase16.h"
43 44
#include "wingdi.h"
#include "winuser.h"
45
#include "winerror.h"
46
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
47
#include "dosexe.h"
48
#include "dosvm.h"
49
#include "vga.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
50

51
WINE_DEFAULT_DEBUG_CHANNEL(module);
52

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

/**********************************************************************
 *          DOSVM_IsWin16
 * 
 * Return TRUE if we are in Windows process.
 */
BOOL DOSVM_IsWin16(void)
{
  return DOSVM_isdosexe ? FALSE : TRUE;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
65 66
#ifdef MZ_SUPPORTED

67 68 69
#ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
70

Alexandre Julliard's avatar
Alexandre Julliard committed
71 72 73 74
/* define this to try mapping through /proc/pid/mem instead of a temp file,
   but Linus doesn't like mmapping /proc/pid/mem, so it doesn't work for me */
#undef MZ_MAPSELF

Alexandre Julliard's avatar
Alexandre Julliard committed
75 76 77 78 79 80
#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)))

81 82
/* structures for EXEC */

83 84
#include "pshpack1.h"

85 86
typedef struct {
  WORD env_seg;
87 88 89
  DWORD cmdline;
  DWORD fcb1;
  DWORD fcb2;
90 91 92 93 94 95 96 97 98 99 100
  WORD init_sp;
  WORD init_ss;
  WORD init_ip;
  WORD init_cs;
} ExecBlock;

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

101 102
#include "poppack.h"

103 104
/* global variables */

105
pid_t dosvm_pid;
106

107 108 109
static WORD init_cs,init_ip,init_ss,init_sp;
static HANDLE dosvm_thread, loop_thread;
static DWORD dosvm_tid, loop_tid;
110

111
static void MZ_Launch( LPCSTR cmdtail, int length );
112
static BOOL MZ_InitTask(void);
113

114
static void MZ_CreatePSP( LPVOID lpPSP, WORD env, WORD par )
Alexandre Julliard's avatar
Alexandre Julliard committed
115
{
116 117 118 119 120 121
  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 */
122 123 124
  psp->savedint22 = DOSVM_GetRMHandler(0x22);
  psp->savedint23 = DOSVM_GetRMHandler(0x23);
  psp->savedint24 = DOSVM_GetRMHandler(0x24);
125 126 127
  psp->parentPSP=par;
  psp->environment=env;
  /* FIXME: more PSP stuff */
128 129
}

130
static void MZ_FillPSP( LPVOID lpPSP, LPCSTR cmdtail, int length )
131
{
132
    PDB16 *psp = lpPSP;
133

134 135 136 137 138
    if(length > 127) 
    {
        WARN( "Command tail truncated! (length %d)\n", length );
        length = 126;
    }
139

140
    psp->cmdLine[0] = length;
141

142
    /*
143 144 145
     * 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.
146
     */
147 148
    if(length == 127)
        length = 126;
149

150 151
    if(length > 0)
        memmove(psp->cmdLine+1, cmdtail, length);
152

153 154 155
    psp->cmdLine[length+1] = '\r';

    /* FIXME: more PSP stuff */
Alexandre Julliard's avatar
Alexandre Julliard committed
156 157
}

158
static WORD MZ_InitEnvironment( LPCSTR env, LPCSTR name )
159 160
{
 unsigned sz=0;
161
 unsigned i=0;
162 163 164 165 166
 WORD seg;
 LPSTR envblk;

 if (env) {
  /* get size of environment block */
167
  while (env[sz++]) sz+=strlen(env+sz)+1;
168 169
 } else sz++;
 /* allocate it */
170
 envblk=DOSMEM_GetBlock(sz+sizeof(WORD)+strlen(name)+1,&seg);
171 172 173 174
 /* fill it */
 if (env) {
  memcpy(envblk,env,sz);
 } else envblk[0]=0;
175 176 177 178 179 180 181 182 183 184
 /* 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;
 }
185 186 187 188 189 190 191
 /* 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;
}

192
static BOOL MZ_InitMemory(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
193
{
194 195 196
    /* initialize the memory */
    TRACE("Initializing DOS memory structures\n");
    DOSMEM_Init(TRUE);
197
    DOSDEV_InstallDOSDevices();
198

199
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
200 201
}

202
static BOOL MZ_DoLoadImage( HANDLE hFile, LPCSTR filename, OverlayBlock *oblk )
Alexandre Julliard's avatar
Alexandre Julliard committed
203
{
204 205
  IMAGE_DOS_HEADER mz_header;
  DWORD image_start,image_size,min_size,max_size,avail;
206 207
  BYTE*psp_start,*load_start,*oldenv;
  int x, old_com=0, alloc;
208
  SEGPTR reloc;
209
  WORD env_seg, load_seg, rel_seg, oldpsp_seg;
210 211
  DWORD len;

212
  if (DOSVM_psp) {
213
    /* DOS process already running, inherit from it */
214
    PDB16* par_psp = (PDB16*)((DWORD)DOSVM_psp << 4);
215 216
    alloc=0;
    oldenv = (LPBYTE)((DWORD)par_psp->environment << 4);
217
    oldpsp_seg = DOSVM_psp;
218 219
  } else {
    /* allocate new DOS process, inheriting from Wine environment */
220
    alloc=1;
221 222
    oldenv = GetEnvironmentStringsA();
    oldpsp_seg = 0;
223
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
224

225 226
 SetFilePointer(hFile,0,NULL,FILE_BEGIN);
 if (   !ReadFile(hFile,&mz_header,sizeof(mz_header),&len,NULL)
227
     || len != sizeof(mz_header)
228
     || mz_header.e_magic != IMAGE_DOS_SIGNATURE) {
229 230 231 232 233 234
  char *p = strrchr( filename, '.' );
  if (!p || strcasecmp( p, ".com" ))  /* check for .COM extension */
  {
      SetLastError(ERROR_BAD_FORMAT);
      goto load_error;
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
235 236
  old_com=1; /* assume .COM file */
  image_start=0;
237
  image_size=GetFileSize(hFile,NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
238 239 240 241 242 243 244 245
  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 */
246 247 248
  /* 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
249 250 251 252 253 254
  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);
 }

255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
  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 */
    env_seg=MZ_InitEnvironment(oldenv, filename);

    /* 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;
275
    psp_start=DOSMEM_GetBlock(avail,&DOSVM_psp);
276 277 278 279 280
    if (!psp_start) {
      ERR("error allocating DOS memory\n");
      SetLastError(ERROR_NOT_ENOUGH_MEMORY);
      goto load_error;
    }
281
    load_seg=DOSVM_psp+(old_com?0:PSP_SIZE);
282 283 284 285
    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
286 287

 /* load executable image */
288
 TRACE("loading DOS %s image, %08lx bytes\n",old_com?"COM":"EXE",image_size);
289 290
 SetFilePointer(hFile,image_start,NULL,FILE_BEGIN);
 if (!ReadFile(hFile,load_start,image_size,&len,NULL) || len != image_size) {
291 292 293 294 295 296
  /* 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;
  }
297
 }
Alexandre Julliard's avatar
Alexandre Julliard committed
298

Alexandre Julliard's avatar
Alexandre Julliard committed
299 300
 if (mz_header.e_crlc) {
  /* load relocation table */
301
  TRACE("loading DOS EXE relocation table, %d entries\n",mz_header.e_crlc);
Alexandre Julliard's avatar
Alexandre Julliard committed
302
  /* FIXME: is this too slow without read buffering? */
303
  SetFilePointer(hFile,mz_header.e_lfarlc,NULL,FILE_BEGIN);
Alexandre Julliard's avatar
Alexandre Julliard committed
304
  for (x=0; x<mz_header.e_crlc; x++) {
305
   if (!ReadFile(hFile,&reloc,sizeof(reloc),&len,NULL) || len != sizeof(reloc)) {
306
    SetLastError(ERROR_BAD_FORMAT);
307
    goto load_error;
308
   }
309
   *(WORD*)SEGPTR16(load_start,reloc)+=rel_seg;
Alexandre Julliard's avatar
Alexandre Julliard committed
310
  }
Alexandre Julliard's avatar
Alexandre Julliard committed
311 312
 }

313 314 315 316 317
  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
318 319 320 321 322
    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;
    }
323

324 325
    TRACE("entry point: %04x:%04x\n",init_cs,init_ip);
  }
326

327
  if (alloc && !MZ_InitTask()) {
328 329 330 331 332 333 334
    SetLastError(ERROR_GEN_FAILURE);
    return FALSE;
  }

  return TRUE;

load_error:
335
  DOSVM_psp = oldpsp_seg;
336 337

  return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
338 339
}

Patrik Stridvall's avatar
Patrik Stridvall committed
340
/***********************************************************************
341
 *		wine_load_dos_exe (WINEDOS.@)
342
 *
343
 * Called from WineVDM when a new real-mode DOS process is started.
344
 * Loads DOS program into memory and executes the program.
Patrik Stridvall's avatar
Patrik Stridvall committed
345
 */
346
void WINAPI wine_load_dos_exe( LPCSTR filename, LPCSTR cmdline )
347
{
348 349
    char dos_cmdtail[126];
    int  dos_length = 0;
350

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 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
    HANDLE hFile = CreateFileA( filename, GENERIC_READ, FILE_SHARE_READ, 
                                NULL, OPEN_EXISTING, 0, 0 );
    if (hFile == INVALID_HANDLE_VALUE) return;
    DOSVM_isdosexe = TRUE;

    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;
        }
    }

    if (MZ_DoLoadImage( hFile, filename, NULL )) 
        MZ_Launch( dos_cmdtail, dos_length );
420 421
}

Patrik Stridvall's avatar
Patrik Stridvall committed
422
/***********************************************************************
423
 *		MZ_Exec
424 425
 *
 * this may only be called from existing DOS processes
Patrik Stridvall's avatar
Patrik Stridvall committed
426
 */
427
BOOL WINAPI MZ_Exec( CONTEXT86 *context, LPCSTR filename, BYTE func, LPVOID paramblk )
428
{
429 430 431 432 433
  DWORD binType;
  STARTUPINFOA st;
  PROCESS_INFORMATION pe;
  HANDLE hFile;

434
  BOOL ret = FALSE;
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452

  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 */
    {
      LPBYTE 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);
453
      int    cmdLength = cmdline[0];
454

455
      /*
456 457 458
       * If cmdLength is 127, command tail is truncated and environment 
       * variable CMDLINE should contain full command line 
       * (this includes filename).
459
       */
460 461 462 463 464
      if (cmdLength == 127)
      {
          FIXME( "CMDLINE argument passing is unimplemented.\n" );
          cmdLength = 126; /* FIXME */
      }
465

466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
      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);
504
  if (hFile == INVALID_HANDLE_VALUE) return FALSE;
505

506 507 508 509 510
  switch (func) {
  case 0: /* load and execute */
  case 1: /* load but don't execute */
    {
      /* save current process's return SS:SP now */
511
      LPBYTE psp_start = (LPBYTE)((DWORD)DOSVM_psp << 4);
512
      PDB16 *psp = (PDB16 *)psp_start;
513
      psp->saveStack = (DWORD)MAKESEGPTR(context->SegSs, LOWORD(context->Esp));
514
    }
515
    ret = MZ_DoLoadImage( hFile, filename, NULL );
516
    if (ret) {
517
      /* MZ_LoadImage created a new PSP and loaded new values into it,
518
       * let's work on the new values now */
519
      LPBYTE psp_start = (LPBYTE)((DWORD)DOSVM_psp << 4);
520
      ExecBlock *blk = (ExecBlock *)paramblk;
521
      LPBYTE cmdline = PTR_REAL_TO_LIN(SELECTOROF(blk->cmdline),OFFSETOF(blk->cmdline));
522 523 524 525

      /* First character contains the length of the command line. */
      MZ_FillPSP(psp_start, cmdline + 1, cmdline[0]);

526
      /* the lame MS-DOS engineers decided that the return address should be in int22 */
527
      DOSVM_SetRMHandler(0x22, (FARPROC16)MAKESEGPTR(context->SegCs, LOWORD(context->Eip)));
528 529
      if (func) {
	/* don't execute, just return startup state */
530 531 532 533 534 535 536 537 538 539 540 541
        /*
         * 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 */
	
542 543 544 545 546 547 548 549 550 551
	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;
552 553
	context->SegDs = DOSVM_psp;
	context->SegEs = DOSVM_psp;
554 555 556 557 558 559 560
	context->Eax   = 0;
      }
    }
    break;
  case 3: /* load overlay */
    {
      OverlayBlock *blk = (OverlayBlock *)paramblk;
561
      ret = MZ_DoLoadImage( hFile, filename, blk );
562 563 564 565 566 567 568 569 570 571 572
    }
    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
573
/***********************************************************************
574
 *		MZ_AllocDPMITask
Patrik Stridvall's avatar
Patrik Stridvall committed
575
 */
576
void WINAPI MZ_AllocDPMITask( void )
577
{
578 579
  MZ_InitMemory();
  MZ_InitTask();
580 581
}

Patrik Stridvall's avatar
Patrik Stridvall committed
582
/***********************************************************************
583
 *		MZ_RunInThread
Patrik Stridvall's avatar
Patrik Stridvall committed
584
 */
585 586
void WINAPI MZ_RunInThread( PAPCFUNC proc, ULONG_PTR arg )
{
587 588 589 590 591 592
  if (loop_thread) {
    DOS_SPC spc;
    HANDLE event;

    spc.proc = proc;
    spc.arg = arg;
593
    event = CreateEventW(NULL, TRUE, FALSE, NULL);
594
    PostThreadMessageA(loop_tid, WM_USER, (WPARAM)event, (LPARAM)&spc);
595 596 597 598
    WaitForSingleObject(event, INFINITE);
    CloseHandle(event);
  } else
    proc(arg);
599 600
}

601
static DWORD WINAPI MZ_DOSVM( LPVOID lpExtra )
602
{
603 604
  CONTEXT context;
  DWORD ret;
605

606
  dosvm_pid = getpid();
607

608 609 610 611 612 613 614
  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;
615
  context.EFlags = V86_FLAG | VIF_MASK;
616 617
  DOSVM_SetTimer(0x10000);
  ret = DOSVM_Enter( &context );
618

619 620 621
  dosvm_pid = 0;
  return ret;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
622

623 624 625 626 627
static BOOL MZ_InitTask(void)
{
  if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
                       GetCurrentProcess(), &loop_thread,
                       0, FALSE, DUPLICATE_SAME_ACCESS))
628
    return FALSE;
629 630 631 632
  dosvm_thread = CreateThread(NULL, 0, MZ_DOSVM, NULL, CREATE_SUSPENDED, &dosvm_tid);
  if (!dosvm_thread) {
    CloseHandle(loop_thread);
    loop_thread = 0;
633
    return FALSE;
634
  }
635 636
  loop_tid = GetCurrentThreadId();
  return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
637 638
}

639
static void MZ_Launch( LPCSTR cmdtail, int length )
Alexandre Julliard's avatar
Alexandre Julliard committed
640
{
641
  TDB *pTask = GlobalLock16( GetCurrentTask() );
642
  BYTE *psp_start = PTR_REAL_TO_LIN( DOSVM_psp, 0 );
643
  DWORD rv;
644
  SYSLEVEL *lock;
Alexandre Julliard's avatar
Alexandre Julliard committed
645

646
  MZ_FillPSP(psp_start, cmdtail, length);
647 648
  pTask->flags |= TDBF_WINOLDAP;

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

652 653
  GetpWin16Lock( &lock );
  _LeaveSysLevel( lock );
654

655
  ResumeThread(dosvm_thread);
656
  rv = DOSVM_Loop(dosvm_thread);
Alexandre Julliard's avatar
Alexandre Julliard committed
657

658 659 660 661
  CloseHandle(dosvm_thread);
  dosvm_thread = 0; dosvm_tid = 0;
  CloseHandle(loop_thread);
  loop_thread = 0; loop_tid = 0;
662 663

  VGA_Clean();
664
  ExitProcess(rv);
665 666
}

Patrik Stridvall's avatar
Patrik Stridvall committed
667
/***********************************************************************
668
 *		MZ_Exit
Patrik Stridvall's avatar
Patrik Stridvall committed
669
 */
670
void WINAPI MZ_Exit( CONTEXT86 *context, BOOL cs_psp, WORD retval )
671
{
672 673
  if (DOSVM_psp) {
    WORD psp_seg = cs_psp ? context->SegCs : DOSVM_psp;
674 675 676 677 678
    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 */
679
      FARPROC16 retaddr = DOSVM_GetRMHandler(0x22);
680
      /* restore interrupts */
681 682 683
      DOSVM_SetRMHandler(0x22, psp->savedint22);
      DOSVM_SetRMHandler(0x23, psp->savedint23);
      DOSVM_SetRMHandler(0x24, psp->savedint24);
684 685 686
      /* FIXME: deallocate file handles etc */
      /* free process's associated memory
       * FIXME: walk memory and deallocate all blocks owned by process */
687 688
      DOSMEM_FreeBlock( PTR_REAL_TO_LIN(psp->environment,0) );
      DOSMEM_FreeBlock( PTR_REAL_TO_LIN(DOSVM_psp,0) );
689
      /* switch to parent's PSP */
690
      DOSVM_psp = parpsp;
691 692 693
      psp_start = (LPBYTE)((DWORD)parpsp << 4);
      psp = (PDB16 *)psp_start;
      /* now return to parent */
694
      DOSVM_retval = retval;
695 696 697 698 699 700
      context->SegCs = SELECTOROF(retaddr);
      context->Eip   = OFFSETOF(retaddr);
      context->SegSs = SELECTOROF(psp->saveStack);
      context->Esp   = OFFSETOF(psp->saveStack);
      return;
    } else
701
      TRACE("killing DOS task\n");
702 703 704 705
  }
  ExitThread( retval );
}

706 707 708 709 710 711

/***********************************************************************
 *		MZ_Current
 */
BOOL WINAPI MZ_Current( void )
{
712
  return (dosvm_pid != 0); /* FIXME: do a better check */
713 714
}

Alexandre Julliard's avatar
Alexandre Julliard committed
715
#else /* !MZ_SUPPORTED */
Alexandre Julliard's avatar
Alexandre Julliard committed
716

Patrik Stridvall's avatar
Patrik Stridvall committed
717
/***********************************************************************
718
 *		wine_load_dos_exe (WINEDOS.@)
Patrik Stridvall's avatar
Patrik Stridvall committed
719
 */
720
void WINAPI wine_load_dos_exe( LPCSTR filename, LPCSTR cmdline )
Alexandre Julliard's avatar
Alexandre Julliard committed
721
{
722 723 724 725
  WARN("DOS executables not supported on this platform\n");
  SetLastError(ERROR_BAD_FORMAT);
}

Patrik Stridvall's avatar
Patrik Stridvall committed
726
/***********************************************************************
727
 *		MZ_Exec
Patrik Stridvall's avatar
Patrik Stridvall committed
728
 */
729
BOOL WINAPI MZ_Exec( CONTEXT86 *context, LPCSTR filename, BYTE func, LPVOID paramblk )
730 731 732 733
{
  /* can't happen */
  SetLastError(ERROR_BAD_FORMAT);
  return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
734 735
}

Patrik Stridvall's avatar
Patrik Stridvall committed
736
/***********************************************************************
737
 *		MZ_AllocDPMITask
Patrik Stridvall's avatar
Patrik Stridvall committed
738
 */
739
void WINAPI MZ_AllocDPMITask( void )
740
{
741
    ERR("Actual real-mode calls not supported on this platform!\n");
742 743
}

744 745 746 747 748 749 750 751
/***********************************************************************
 *		MZ_RunInThread
 */
void WINAPI MZ_RunInThread( PAPCFUNC proc, ULONG_PTR arg )
{
    proc(arg);
}

Patrik Stridvall's avatar
Patrik Stridvall committed
752
/***********************************************************************
753
 *		MZ_Exit
Patrik Stridvall's avatar
Patrik Stridvall committed
754
 */
755
void WINAPI MZ_Exit( CONTEXT86 *context, BOOL cs_psp, WORD retval )
756 757 758 759
{
  ExitThread( retval );
}

Patrik Stridvall's avatar
Patrik Stridvall committed
760
/***********************************************************************
761
 *		MZ_Current
Patrik Stridvall's avatar
Patrik Stridvall committed
762
 */
763
BOOL WINAPI MZ_Current( void )
764
{
765
    return FALSE;
766
}
767 768

#endif /* !MZ_SUPPORTED */