comctl32/listview: Revert column scrolling recent change, it should be handled differ...
[wine] / dlls / winedos / module.c
1 /*
2  * DOS (MZ) loader
3  *
4  * Copyright 1998 Ove Kåven
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * 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.
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <fcntl.h>
31 #include <signal.h>
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35 #include <sys/types.h>
36 #ifdef HAVE_SYS_STAT_H
37 # include <sys/stat.h>
38 #endif
39 #ifdef HAVE_SYS_TIME_H
40 # include <sys/time.h>
41 #endif
42 #include "windef.h"
43 #include "winbase.h"
44 #include "wine/winbase16.h"
45 #include "wingdi.h"
46 #include "winuser.h"
47 #include "winerror.h"
48 #include "wine/debug.h"
49 #include "dosexe.h"
50 #include "dosvm.h"
51 #include "vga.h"
52
53 WINE_DEFAULT_DEBUG_CHANNEL(module);
54
55 static BOOL DOSVM_isdosexe;
56
57 /**********************************************************************
58  *          DOSVM_IsWin16
59  * 
60  * Return TRUE if we are in Windows process.
61  */
62 BOOL DOSVM_IsWin16(void)
63 {
64   return DOSVM_isdosexe ? FALSE : TRUE;
65 }
66
67 /**********************************************************************
68  *          DOSVM_Exit
69  */
70 void DOSVM_Exit( WORD retval )
71 {
72     DWORD count;
73
74     ReleaseThunkLock( &count );
75     ExitThread( retval );
76 }
77
78
79 #ifdef MZ_SUPPORTED
80
81 #ifdef HAVE_SYS_MMAN_H
82 # include <sys/mman.h>
83 #endif
84
85 /* define this to try mapping through /proc/pid/mem instead of a temp file,
86    but Linus doesn't like mmapping /proc/pid/mem, so it doesn't work for me */
87 #undef MZ_MAPSELF
88
89 #define BIOS_DATA_SEGMENT 0x40
90 #define PSP_SIZE 0x10
91
92 #define SEG16(ptr,seg) ((LPVOID)((BYTE*)ptr+((DWORD)(seg)<<4)))
93 #define SEGPTR16(ptr,segptr) ((LPVOID)((BYTE*)ptr+((DWORD)SELECTOROF(segptr)<<4)+OFFSETOF(segptr)))
94
95 /* structures for EXEC */
96
97 #include "pshpack1.h"
98
99 typedef struct {
100   WORD env_seg;
101   DWORD cmdline;
102   DWORD fcb1;
103   DWORD fcb2;
104   WORD init_sp;
105   WORD init_ss;
106   WORD init_ip;
107   WORD init_cs;
108 } ExecBlock;
109
110 typedef struct {
111   WORD load_seg;
112   WORD rel_seg;
113 } OverlayBlock;
114
115 #include "poppack.h"
116
117 /* global variables */
118
119 pid_t dosvm_pid;
120
121 static WORD init_cs,init_ip,init_ss,init_sp;
122 static HANDLE dosvm_thread, loop_thread;
123 static DWORD dosvm_tid, loop_tid;
124
125 static void MZ_Launch( LPCSTR cmdtail, int length );
126 static BOOL MZ_InitTask(void);
127
128 static void MZ_CreatePSP( LPVOID lpPSP, WORD env, WORD par )
129 {
130   PDB16*psp=lpPSP;
131
132   psp->int20=0x20CD; /* int 20 */
133   /* some programs use this to calculate how much memory they need */
134   psp->nextParagraph=0x9FFF; /* FIXME: use a real value */
135   /* FIXME: dispatcher */
136   psp->savedint22 = DOSVM_GetRMHandler(0x22);
137   psp->savedint23 = DOSVM_GetRMHandler(0x23);
138   psp->savedint24 = DOSVM_GetRMHandler(0x24);
139   psp->parentPSP=par;
140   psp->environment=env;
141   /* FIXME: more PSP stuff */
142 }
143
144 static void MZ_FillPSP( LPVOID lpPSP, LPCSTR cmdtail, int length )
145 {
146     PDB16 *psp = lpPSP;
147
148     if(length > 127) 
149     {
150         WARN( "Command tail truncated! (length %d)\n", length );
151         length = 126;
152     }
153
154     psp->cmdLine[0] = length;
155
156     /*
157      * Length of exactly 127 bytes means that full command line is 
158      * stored in environment variable CMDLINE and PSP contains 
159      * command tail truncated to 126 bytes.
160      */
161     if(length == 127)
162         length = 126;
163
164     if(length > 0)
165         memmove(psp->cmdLine+1, cmdtail, length);
166
167     psp->cmdLine[length+1] = '\r';
168
169     /* FIXME: more PSP stuff */
170 }
171
172 static WORD MZ_InitEnvironment( LPCSTR env, LPCSTR name )
173 {
174  unsigned sz=0;
175  unsigned i=0;
176  WORD seg;
177  LPSTR envblk;
178
179  if (env) {
180   /* get size of environment block */
181   while (env[sz++]) sz+=strlen(env+sz)+1;
182  } else sz++;
183  /* allocate it */
184  envblk=DOSMEM_AllocBlock(sz+sizeof(WORD)+strlen(name)+1,&seg);
185  /* fill it */
186  if (env) {
187   memcpy(envblk,env,sz);
188  } else envblk[0]=0;
189  /* DOS environment variables are uppercase */
190  while (envblk[i]){
191   while (envblk[i] != '='){
192    if (envblk[i]>='a' && envblk[i] <= 'z'){
193     envblk[i] -= 32;
194    }
195    i++;
196   }
197   i += strlen(envblk+i) + 1;
198  }
199  /* DOS 3.x: the block contains 1 additional string */
200  *(WORD*)(envblk+sz)=1;
201  /* being the program name itself */
202  strcpy(envblk+sz+sizeof(WORD),name);
203  return seg;
204 }
205
206 static BOOL MZ_InitMemory(void)
207 {
208     /* initialize the memory */
209     TRACE("Initializing DOS memory structures\n");
210     DOSMEM_MapDosLayout();
211     DOSDEV_InstallDOSDevices();
212     MSCDEX_InstallCDROM();
213
214     return TRUE;
215 }
216
217 static BOOL MZ_DoLoadImage( HANDLE hFile, LPCSTR filename, OverlayBlock *oblk, WORD par_env_seg )
218 {
219   IMAGE_DOS_HEADER mz_header;
220   DWORD image_start,image_size,min_size,max_size,avail;
221   BYTE*psp_start,*load_start;
222   LPSTR oldenv = 0;
223   int x, old_com=0, alloc;
224   SEGPTR reloc;
225   WORD env_seg, load_seg, rel_seg, oldpsp_seg;
226   DWORD len;
227
228   if (DOSVM_psp) {
229     /* DOS process already running, inherit from it */
230     PDB16* par_psp;
231     alloc=0;
232     oldpsp_seg = DOSVM_psp;
233     if( !par_env_seg) {  
234         par_psp = (PDB16*)((DWORD)DOSVM_psp << 4);
235         oldenv = (LPSTR)((DWORD)par_psp->environment << 4);
236     }
237   } else {
238     /* allocate new DOS process, inheriting from Wine environment */
239     alloc=1;
240     oldpsp_seg = 0;
241     if( !par_env_seg)
242         oldenv = GetEnvironmentStringsA();
243   }
244
245  SetFilePointer(hFile,0,NULL,FILE_BEGIN);
246  if (   !ReadFile(hFile,&mz_header,sizeof(mz_header),&len,NULL)
247      || len != sizeof(mz_header)
248      || mz_header.e_magic != IMAGE_DOS_SIGNATURE) {
249   char *p = strrchr( filename, '.' );
250   if (!p || strcasecmp( p, ".com" ))  /* check for .COM extension */
251   {
252       SetLastError(ERROR_BAD_FORMAT);
253       goto load_error;
254   }
255   old_com=1; /* assume .COM file */
256   image_start=0;
257   image_size=GetFileSize(hFile,NULL);
258   min_size=0x10000; max_size=0x100000;
259   mz_header.e_crlc=0;
260   mz_header.e_ss=0; mz_header.e_sp=0xFFFE;
261   mz_header.e_cs=0; mz_header.e_ip=0x100;
262  } else {
263   /* calculate load size */
264   image_start=mz_header.e_cparhdr<<4;
265   image_size=mz_header.e_cp<<9; /* pages are 512 bytes */
266   /* From Ralf Brown Interrupt List: If the word at offset 02h is 4, it should
267    * be treated as 00h, since pre-1.10 versions of the MS linker set it that
268    * way. */
269   if ((mz_header.e_cblp!=0)&&(mz_header.e_cblp!=4)) image_size-=512-mz_header.e_cblp;
270   image_size-=image_start;
271   min_size=image_size+((DWORD)mz_header.e_minalloc<<4)+(PSP_SIZE<<4);
272   max_size=image_size+((DWORD)mz_header.e_maxalloc<<4)+(PSP_SIZE<<4);
273  }
274
275   if (alloc) MZ_InitMemory();
276
277   if (oblk) {
278     /* load overlay into preallocated memory */
279     load_seg=oblk->load_seg;
280     rel_seg=oblk->rel_seg;
281     load_start=(LPBYTE)((DWORD)load_seg<<4);
282   } else {
283     /* allocate environment block */
284     if( par_env_seg)
285         env_seg = par_env_seg;
286     else
287         env_seg=MZ_InitEnvironment(oldenv, filename);
288     if (alloc)
289         FreeEnvironmentStringsA( oldenv);
290
291     /* allocate memory for the executable */
292     TRACE("Allocating DOS memory (min=%d, max=%d)\n",min_size,max_size);
293     avail=DOSMEM_Available();
294     if (avail<min_size) {
295       ERR("insufficient DOS memory\n");
296       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
297       goto load_error;
298     }
299     if (avail>max_size) avail=max_size;
300     psp_start=DOSMEM_AllocBlock(avail,&DOSVM_psp);
301     if (!psp_start) {
302       ERR("error allocating DOS memory\n");
303       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
304       goto load_error;
305     }
306     load_seg=DOSVM_psp+(old_com?0:PSP_SIZE);
307     rel_seg=load_seg;
308     load_start=psp_start+(PSP_SIZE<<4);
309     MZ_CreatePSP(psp_start, env_seg, oldpsp_seg);
310   }
311
312  /* load executable image */
313  TRACE("loading DOS %s image, %08x bytes\n",old_com?"COM":"EXE",image_size);
314  SetFilePointer(hFile,image_start,NULL,FILE_BEGIN);
315  if (!ReadFile(hFile,load_start,image_size,&len,NULL) || len != image_size) {
316   /* check if this is due to the workaround for the pre-1.10 MS linker and we
317      really had only 4 bytes on the last page */
318   if (mz_header.e_cblp != 4 || image_size - len != 512 - 4) {
319     SetLastError(ERROR_BAD_FORMAT);
320     goto load_error;
321   }
322  }
323
324  if (mz_header.e_crlc) {
325   /* load relocation table */
326   TRACE("loading DOS EXE relocation table, %d entries\n",mz_header.e_crlc);
327   /* FIXME: is this too slow without read buffering? */
328   SetFilePointer(hFile,mz_header.e_lfarlc,NULL,FILE_BEGIN);
329   for (x=0; x<mz_header.e_crlc; x++) {
330    if (!ReadFile(hFile,&reloc,sizeof(reloc),&len,NULL) || len != sizeof(reloc)) {
331     SetLastError(ERROR_BAD_FORMAT);
332     goto load_error;
333    }
334    *(WORD*)SEGPTR16(load_start,reloc)+=rel_seg;
335   }
336  }
337
338   if (!oblk) {
339     init_cs = load_seg+mz_header.e_cs;
340     init_ip = mz_header.e_ip;
341     init_ss = load_seg+mz_header.e_ss;
342     init_sp = mz_header.e_sp;
343     if (old_com){
344       /* .COM files exit with ret. Make sure they jump to psp start (=int 20) */
345       WORD* stack = PTR_REAL_TO_LIN(init_ss, init_sp);
346       *stack = 0;
347     }
348
349     TRACE("entry point: %04x:%04x\n",init_cs,init_ip);
350   }
351
352   if (alloc && !MZ_InitTask()) {
353     SetLastError(ERROR_GEN_FAILURE);
354     return FALSE;
355   }
356
357   return TRUE;
358
359 load_error:
360   DOSVM_psp = oldpsp_seg;
361
362   return FALSE;
363 }
364
365 /***********************************************************************
366  *              wine_load_dos_exe (WINEDOS.@)
367  *
368  * Called from WineVDM when a new real-mode DOS process is started.
369  * Loads DOS program into memory and executes the program.
370  */
371 void WINAPI wine_load_dos_exe( LPCSTR filename, LPCSTR cmdline )
372 {
373     char dos_cmdtail[126];
374     int  dos_length = 0;
375
376     HANDLE hFile = CreateFileA( filename, GENERIC_READ, FILE_SHARE_READ, 
377                                 NULL, OPEN_EXISTING, 0, 0 );
378     if (hFile == INVALID_HANDLE_VALUE) return;
379     DOSVM_isdosexe = TRUE;
380
381     if(cmdline && *cmdline)
382     {
383         dos_length = strlen(cmdline);
384         memmove( dos_cmdtail + 1, cmdline, 
385                  (dos_length < 125) ? dos_length : 125 );
386
387         /* Non-empty command tail always starts with at least one space. */
388         dos_cmdtail[0] = ' ';
389         dos_length++;
390
391         /*
392          * If command tail is longer than 126 characters,
393          * set tail length to 127 and fill CMDLINE environment variable 
394          * with full command line (this includes filename).
395          */
396         if (dos_length > 126)
397         {
398             char *cmd = HeapAlloc( GetProcessHeap(), 0, 
399                                    dos_length + strlen(filename) + 4 );
400             char *ptr = cmd;
401
402             if (!cmd)
403                 return;
404
405             /*
406              * Append filename. If path includes spaces, quote the path.
407              */
408             if (strchr(filename, ' '))
409             {
410                 *ptr++ = '\"';
411                 strcpy( ptr, filename );
412                 ptr += strlen(filename);                   
413                 *ptr++ = '\"';
414             }
415             else
416             {
417                 strcpy( ptr, filename );
418                 ptr += strlen(filename);  
419             }
420
421             /*
422              * Append command tail.
423              */
424             if (cmdline[0] != ' ')
425                 *ptr++ = ' ';
426             strcpy( ptr, cmdline );
427
428             /*
429              * Set environment variable. This will be passed to
430              * new DOS process.
431              */
432             if (!SetEnvironmentVariableA( "CMDLINE", cmd ))
433             {
434                 HeapFree(GetProcessHeap(), 0, cmd );
435                 return;
436             }
437
438             HeapFree(GetProcessHeap(), 0, cmd );
439             dos_length = 127;
440         }
441     }
442
443     if (MZ_DoLoadImage( hFile, filename, NULL, 0 )) 
444         MZ_Launch( dos_cmdtail, dos_length );
445 }
446
447 /***********************************************************************
448  *              MZ_Exec
449  *
450  * this may only be called from existing DOS processes
451  */
452 BOOL WINAPI MZ_Exec( CONTEXT86 *context, LPCSTR filename, BYTE func, LPVOID paramblk )
453 {
454   DWORD binType;
455   STARTUPINFOA st;
456   PROCESS_INFORMATION pe;
457   HANDLE hFile;
458
459   BOOL ret = FALSE;
460
461   if(!GetBinaryTypeA(filename, &binType))   /* determine what kind of binary this is */
462   {
463     return FALSE; /* binary is not an executable */
464   }
465
466   /* handle non-dos executables */
467   if(binType != SCS_DOS_BINARY)
468   {
469     if(func == 0) /* load and execute */
470     {
471       LPSTR fullCmdLine;
472       WORD fullCmdLength;
473       LPBYTE psp_start = (LPBYTE)((DWORD)DOSVM_psp << 4);
474       PDB16 *psp = (PDB16 *)psp_start;
475       ExecBlock *blk = paramblk;
476       LPBYTE cmdline = PTR_REAL_TO_LIN(SELECTOROF(blk->cmdline),OFFSETOF(blk->cmdline));
477       LPBYTE envblock = PTR_REAL_TO_LIN(psp->environment, 0);
478       int    cmdLength = cmdline[0];
479
480       /*
481        * If cmdLength is 127, command tail is truncated and environment 
482        * variable CMDLINE should contain full command line 
483        * (this includes filename).
484        */
485       if (cmdLength == 127)
486       {
487           FIXME( "CMDLINE argument passing is unimplemented.\n" );
488           cmdLength = 126; /* FIXME */
489       }
490
491       fullCmdLength = (strlen(filename) + 1) + cmdLength + 1; /* filename + space + cmdline + terminating null character */
492
493       fullCmdLine = HeapAlloc(GetProcessHeap(), 0, fullCmdLength);
494       if(!fullCmdLine) return FALSE; /* return false on memory alloc failure */
495
496       /* build the full command line from the executable file and the command line being passed in */
497       snprintf(fullCmdLine, fullCmdLength, "%s ", filename); /* start off with the executable filename and a space */
498       memcpy(fullCmdLine + strlen(fullCmdLine), cmdline + 1, cmdLength); /* append cmdline onto the end */
499       fullCmdLine[fullCmdLength - 1] = 0; /* null terminate string */
500
501       ZeroMemory (&st, sizeof(STARTUPINFOA));
502       st.cb = sizeof(STARTUPINFOA);
503       ret = CreateProcessA (NULL, fullCmdLine, NULL, NULL, TRUE, 0, envblock, NULL, &st, &pe);
504
505       /* wait for the app to finish and clean up PROCESS_INFORMATION handles */
506       if(ret)
507       {
508         WaitForSingleObject(pe.hProcess, INFINITE);  /* wait here until the child process is complete */
509         CloseHandle(pe.hProcess);
510         CloseHandle(pe.hThread);
511       }
512
513       HeapFree(GetProcessHeap(), 0, fullCmdLine);  /* free the memory we allocated */
514     }
515     else
516     {
517       FIXME("EXEC type of %d not implemented for non-dos executables\n", func);
518       ret = FALSE;
519     }
520
521     return ret;
522   } /* if(binType != SCS_DOS_BINARY) */
523
524
525   /* handle dos executables */
526
527   hFile = CreateFileA( filename, GENERIC_READ, FILE_SHARE_READ,
528                              NULL, OPEN_EXISTING, 0, 0);
529   if (hFile == INVALID_HANDLE_VALUE) return FALSE;
530
531   switch (func) {
532   case 0: /* load and execute */
533   case 1: /* load but don't execute */
534     {
535       /* save current process's return SS:SP now */
536       LPBYTE psp_start = (LPBYTE)((DWORD)DOSVM_psp << 4);
537       PDB16 *psp = (PDB16 *)psp_start;
538       psp->saveStack = (DWORD)MAKESEGPTR(context->SegSs, LOWORD(context->Esp));
539     }
540     ret = MZ_DoLoadImage( hFile, filename, NULL, ((ExecBlock *)paramblk)->env_seg );
541     if (ret) {
542       /* MZ_LoadImage created a new PSP and loaded new values into it,
543        * let's work on the new values now */
544       LPBYTE psp_start = (LPBYTE)((DWORD)DOSVM_psp << 4);
545       ExecBlock *blk = paramblk;
546       LPBYTE cmdline = PTR_REAL_TO_LIN(SELECTOROF(blk->cmdline),OFFSETOF(blk->cmdline));
547
548       /* First character contains the length of the command line. */
549       MZ_FillPSP(psp_start, (LPSTR)cmdline + 1, cmdline[0]);
550
551       /* the lame MS-DOS engineers decided that the return address should be in int22 */
552       DOSVM_SetRMHandler(0x22, (FARPROC16)MAKESEGPTR(context->SegCs, LOWORD(context->Eip)));
553       if (func) {
554         /* don't execute, just return startup state */
555         /*
556          * From Ralph Brown:
557          *  For function 01h, the AX value to be passed to the child program 
558          *  is put on top of the child's stack
559          */
560         LPBYTE stack;
561         init_sp -= 2;
562         stack = CTX_SEG_OFF_TO_LIN(context, init_ss, init_sp);
563         /* FIXME: push AX correctly */
564         stack[0] = 0x00;    /* push AL */
565         stack[1] = 0x00;    /* push AH */
566         
567         blk->init_cs = init_cs;
568         blk->init_ip = init_ip;
569         blk->init_ss = init_ss;
570         blk->init_sp = init_sp;
571       } else {
572         /* execute by making us return to new process */
573         context->SegCs = init_cs;
574         context->Eip   = init_ip;
575         context->SegSs = init_ss;
576         context->Esp   = init_sp;
577         context->SegDs = DOSVM_psp;
578         context->SegEs = DOSVM_psp;
579         context->Eax   = 0;
580       }
581     }
582     break;
583   case 3: /* load overlay */
584     {
585       OverlayBlock *blk = paramblk;
586       ret = MZ_DoLoadImage( hFile, filename, blk, 0);
587     }
588     break;
589   default:
590     FIXME("EXEC load type %d not implemented\n", func);
591     SetLastError(ERROR_INVALID_FUNCTION);
592     break;
593   }
594   CloseHandle(hFile);
595   return ret;
596 }
597
598 /***********************************************************************
599  *              MZ_AllocDPMITask
600  */
601 void WINAPI MZ_AllocDPMITask( void )
602 {
603   MZ_InitMemory();
604   MZ_InitTask();
605 }
606
607 /***********************************************************************
608  *              MZ_RunInThread
609  */
610 void WINAPI MZ_RunInThread( PAPCFUNC proc, ULONG_PTR arg )
611 {
612   if (loop_thread) {
613     DOS_SPC spc;
614     HANDLE event;
615
616     spc.proc = proc;
617     spc.arg = arg;
618     event = CreateEventW(NULL, TRUE, FALSE, NULL);
619     PostThreadMessageA(loop_tid, WM_USER, (WPARAM)event, (LPARAM)&spc);
620     WaitForSingleObject(event, INFINITE);
621     CloseHandle(event);
622   } else
623     proc(arg);
624 }
625
626 static DWORD WINAPI MZ_DOSVM( LPVOID lpExtra )
627 {
628   CONTEXT context;
629   INT ret;
630
631   dosvm_pid = getpid();
632
633   memset( &context, 0, sizeof(context) );
634   context.SegCs  = init_cs;
635   context.Eip    = init_ip;
636   context.SegSs  = init_ss;
637   context.Esp    = init_sp;
638   context.SegDs  = DOSVM_psp;
639   context.SegEs  = DOSVM_psp;
640   context.EFlags = V86_FLAG | VIF_MASK;
641   DOSVM_SetTimer(0x10000);
642   ret = DOSVM_Enter( &context );
643   if (ret == -1)
644   {
645       /* fetch the app name from the environment */
646       PDB16 *psp = PTR_REAL_TO_LIN( DOSVM_psp, 0 );
647       char *env = PTR_REAL_TO_LIN( psp->environment, 0 );
648       while (*env) env += strlen(env) + 1;
649       env += 1 + sizeof(WORD);
650
651       if (GetLastError() == ERROR_NOT_SUPPORTED)
652           MESSAGE( "wine: Cannot start DOS application %s\n"
653                    "      because vm86 mode is not supported on this platform.\n",
654                    debugstr_a(env) );
655       else
656           FIXME( "vm86 mode failed error %u\n", GetLastError() );
657   }
658   dosvm_pid = 0;
659   return ret != 0;
660 }
661
662 static BOOL MZ_InitTask(void)
663 {
664   if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
665                        GetCurrentProcess(), &loop_thread,
666                        0, FALSE, DUPLICATE_SAME_ACCESS))
667     return FALSE;
668   dosvm_thread = CreateThread(NULL, 0, MZ_DOSVM, NULL, CREATE_SUSPENDED, &dosvm_tid);
669   if (!dosvm_thread) {
670     CloseHandle(loop_thread);
671     loop_thread = 0;
672     return FALSE;
673   }
674   loop_tid = GetCurrentThreadId();
675   return TRUE;
676 }
677
678 static void MZ_Launch( LPCSTR cmdtail, int length )
679 {
680   TDB *pTask = GlobalLock16( GetCurrentTask() );
681   BYTE *psp_start = PTR_REAL_TO_LIN( DOSVM_psp, 0 );
682   DWORD rv;
683   SYSLEVEL *lock;
684   MSG msg;
685
686   MZ_FillPSP(psp_start, cmdtail, length);
687   pTask->flags |= TDBF_WINOLDAP;
688
689   /* DTA is set to PSP:0080h when a program is started. */
690   pTask->dta = MAKESEGPTR( DOSVM_psp, 0x80 );
691
692   GetpWin16Lock( &lock );
693   _LeaveSysLevel( lock );
694
695   /* force the message queue to be created */
696   PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
697
698   ResumeThread(dosvm_thread);
699   rv = DOSVM_Loop(dosvm_thread);
700
701   CloseHandle(dosvm_thread);
702   dosvm_thread = 0; dosvm_tid = 0;
703   CloseHandle(loop_thread);
704   loop_thread = 0; loop_tid = 0;
705
706   VGA_Clean();
707   ExitProcess(rv);
708 }
709
710 /***********************************************************************
711  *              MZ_Exit
712  */
713 void WINAPI MZ_Exit( CONTEXT86 *context, BOOL cs_psp, WORD retval )
714 {
715   if (DOSVM_psp) {
716     WORD psp_seg = cs_psp ? context->SegCs : DOSVM_psp;
717     LPBYTE psp_start = (LPBYTE)((DWORD)psp_seg << 4);
718     PDB16 *psp = (PDB16 *)psp_start;
719     WORD parpsp = psp->parentPSP; /* check for parent DOS process */
720     if (parpsp) {
721       /* retrieve parent's return address */
722       FARPROC16 retaddr = DOSVM_GetRMHandler(0x22);
723       /* restore interrupts */
724       DOSVM_SetRMHandler(0x22, psp->savedint22);
725       DOSVM_SetRMHandler(0x23, psp->savedint23);
726       DOSVM_SetRMHandler(0x24, psp->savedint24);
727       /* FIXME: deallocate file handles etc */
728       /* free process's associated memory
729        * FIXME: walk memory and deallocate all blocks owned by process */
730       DOSMEM_FreeBlock( PTR_REAL_TO_LIN(psp->environment,0) );
731       DOSMEM_FreeBlock( PTR_REAL_TO_LIN(DOSVM_psp,0) );
732       /* switch to parent's PSP */
733       DOSVM_psp = parpsp;
734       psp_start = (LPBYTE)((DWORD)parpsp << 4);
735       psp = (PDB16 *)psp_start;
736       /* now return to parent */
737       DOSVM_retval = retval;
738       context->SegCs = SELECTOROF(retaddr);
739       context->Eip   = OFFSETOF(retaddr);
740       context->SegSs = SELECTOROF(psp->saveStack);
741       context->Esp   = OFFSETOF(psp->saveStack);
742       return;
743     } else
744       TRACE("killing DOS task\n");
745   }
746   DOSVM_Exit( retval );
747 }
748
749
750 /***********************************************************************
751  *              MZ_Current
752  */
753 BOOL WINAPI MZ_Current( void )
754 {
755   return (dosvm_pid != 0); /* FIXME: do a better check */
756 }
757
758 #else /* !MZ_SUPPORTED */
759
760 /***********************************************************************
761  *              wine_load_dos_exe (WINEDOS.@)
762  */
763 void WINAPI wine_load_dos_exe( LPCSTR filename, LPCSTR cmdline )
764 {
765     FIXME("DOS executables not supported on this platform\n");
766     SetLastError(ERROR_BAD_FORMAT);
767 }
768
769 /***********************************************************************
770  *              MZ_Exec
771  */
772 BOOL WINAPI MZ_Exec( CONTEXT86 *context, LPCSTR filename, BYTE func, LPVOID paramblk )
773 {
774   /* can't happen */
775   SetLastError(ERROR_BAD_FORMAT);
776   return FALSE;
777 }
778
779 /***********************************************************************
780  *              MZ_AllocDPMITask
781  */
782 void WINAPI MZ_AllocDPMITask( void )
783 {
784     FIXME("Actual real-mode calls not supported on this platform!\n");
785 }
786
787 /***********************************************************************
788  *              MZ_RunInThread
789  */
790 void WINAPI MZ_RunInThread( PAPCFUNC proc, ULONG_PTR arg )
791 {
792     proc(arg);
793 }
794
795 /***********************************************************************
796  *              MZ_Exit
797  */
798 void WINAPI MZ_Exit( CONTEXT86 *context, BOOL cs_psp, WORD retval )
799 {
800   DOSVM_Exit( retval );
801 }
802
803 /***********************************************************************
804  *              MZ_Current
805  */
806 BOOL WINAPI MZ_Current( void )
807 {
808     return FALSE;
809 }
810
811 #endif /* !MZ_SUPPORTED */