Storing an IP address in a signed int results in bugs if it starts
[wine] / dlls / winedos / dosvm.c
1 /*
2  * DOS Virtual Machine
3  *
4  * Copyright 1998 Ove Kåven
5  *
6  * This code hasn't been completely cleaned up yet.
7  */
8
9 #include "config.h"
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <signal.h>
17 #include <unistd.h>
18 #include <sys/time.h>
19 #include <sys/types.h>
20
21 #include "wine/winbase16.h"
22 #include "wine/exception.h"
23 #include "windef.h"
24 #include "winbase.h"
25 #include "wingdi.h"
26 #include "winuser.h"
27 #include "winnt.h"
28 #include "wincon.h"
29
30 #include "msdos.h"
31 #include "file.h"
32 #include "miscemu.h"
33 #include "dosexe.h"
34 #include "dosvm.h"
35 #include "stackframe.h"
36 #include "debugtools.h"
37
38 DEFAULT_DEBUG_CHANNEL(int);
39 DECLARE_DEBUG_CHANNEL(module);
40 DECLARE_DEBUG_CHANNEL(relay);
41
42 WORD DOSVM_psp = 0;
43 WORD DOSVM_retval = 0;
44
45 #ifdef MZ_SUPPORTED
46
47 #ifdef HAVE_SYS_VM86_H
48 # include <sys/vm86.h>
49 #endif
50 #ifdef HAVE_SYS_MMAN_H
51 # include <sys/mman.h>
52 #endif
53
54 #define IF_CLR(ctx)     ((ctx)->EFlags &= ~VIF_MASK)
55 #define IF_SET(ctx)     ((ctx)->EFlags |= VIF_MASK)
56 #define IF_ENABLED(ctx) ((ctx)->EFlags & VIF_MASK)
57 #define SET_PEND(ctx)   ((ctx)->EFlags |= VIP_MASK)
58 #define CLR_PEND(ctx)   ((ctx)->EFlags &= ~VIP_MASK)
59 #define IS_PEND(ctx)    ((ctx)->EFlags & VIP_MASK)
60
61 #undef TRY_PICRETURN
62
63 typedef struct _DOSEVENT {
64   int irq,priority;
65   DOSRELAY relay;
66   void *data;
67   struct _DOSEVENT *next;
68 } DOSEVENT, *LPDOSEVENT;
69
70 static CRITICAL_SECTION qcrit = CRITICAL_SECTION_INIT("DOSVM");
71 static struct _DOSEVENT *pending_event, *current_event;
72 static int sig_sent;
73 static CONTEXT86 *current_context;
74
75 static int DOSVM_SimulateInt( int vect, CONTEXT86 *context, BOOL inwine )
76 {
77   FARPROC16 handler=DOSVM_GetRMHandler(vect);
78
79   /* check for our real-mode hooks */
80   if (vect==0x31) {
81     if (context->SegCs==DOSMEM_wrap_seg) {
82       /* exit from real-mode wrapper */
83       return -1;
84     }
85     /* we could probably move some other dodgy stuff here too from dpmi.c */
86   }
87   /* check if the call is from our fake BIOS interrupt stubs */
88   if ((context->SegCs==0xf000) && !inwine) {
89     if (vect != (context->Eip/4)) {
90       TRACE("something fishy going on here (interrupt stub is %02lx)\n", context->Eip/4);
91     }
92     TRACE("builtin interrupt %02x has been branched to\n", vect);
93     DOSVM_RealModeInterrupt(vect, context);
94   }
95   /* check if the call goes to an unhooked interrupt */
96   else if (SELECTOROF(handler)==0xf000) {
97     /* if so, call it directly */
98     TRACE("builtin interrupt %02x has been invoked (through vector %02x)\n", OFFSETOF(handler)/4, vect);
99     DOSVM_RealModeInterrupt(OFFSETOF(handler)/4, context);
100   }
101   /* the interrupt is hooked, simulate interrupt in DOS space */
102   else {
103     WORD*stack= PTR_REAL_TO_LIN( context->SegSs, context->Esp );
104     WORD flag=LOWORD(context->EFlags);
105
106     TRACE_(int)("invoking hooked interrupt %02x at %04x:%04x\n", vect,
107                 SELECTOROF(handler), OFFSETOF(handler));
108     if (IF_ENABLED(context)) flag|=IF_MASK;
109     else flag&=~IF_MASK;
110
111     *(--stack)=flag;
112     *(--stack)=context->SegCs;
113     *(--stack)=LOWORD(context->Eip);
114     context->Esp-=6;
115     context->SegCs=SELECTOROF(handler);
116     context->Eip=OFFSETOF(handler);
117     IF_CLR(context);
118   }
119   return 0;
120 }
121
122 #define SHOULD_PEND(x) \
123   (x && ((!current_event) || (x->priority < current_event->priority)))
124
125 static void DOSVM_SendQueuedEvent(CONTEXT86 *context)
126 {
127   LPDOSEVENT event = pending_event;
128
129   if (SHOULD_PEND(event)) {
130     /* remove from "pending" list */
131     pending_event = event->next;
132     /* process event */
133     if (event->irq>=0) {
134       /* it's an IRQ, move it to "current" list */
135       event->next = current_event;
136       current_event = event;
137       TRACE("dispatching IRQ %d\n",event->irq);
138       /* note that if DOSVM_SimulateInt calls an internal interrupt directly,
139        * current_event might be cleared (and event freed) in this very call! */
140       DOSVM_SimulateInt((event->irq<8)?(event->irq+8):(event->irq-8+0x70),context,TRUE);
141     } else {
142       /* callback event */
143       TRACE("dispatching callback event\n");
144       (*event->relay)(context,event->data);
145       free(event);
146     }
147   }
148   if (!SHOULD_PEND(pending_event)) {
149     TRACE("clearing Pending flag\n");
150     CLR_PEND(context);
151   }
152 }
153
154 static void DOSVM_SendQueuedEvents(CONTEXT86 *context)
155 {
156   /* we will send all queued events as long as interrupts are enabled,
157    * but IRQ events will disable interrupts again */
158   while (IS_PEND(context) && IF_ENABLED(context))
159     DOSVM_SendQueuedEvent(context);
160 }
161
162 /***********************************************************************
163  *              QueueEvent (WINEDOS.@)
164  */
165 void WINAPI DOSVM_QueueEvent( INT irq, INT priority, DOSRELAY relay, LPVOID data)
166 {
167   LPDOSEVENT event, cur, prev;
168
169   if (current_context) {
170     EnterCriticalSection(&qcrit);
171     event = malloc(sizeof(DOSEVENT));
172     if (!event) {
173       ERR("out of memory allocating event entry\n");
174       return;
175     }
176     event->irq = irq; event->priority = priority;
177     event->relay = relay; event->data = data;
178
179     /* insert event into linked list, in order *after*
180      * all earlier events of higher or equal priority */
181     cur = pending_event; prev = NULL;
182     while (cur && cur->priority<=priority) {
183       prev = cur;
184       cur = cur->next;
185     }
186     event->next = cur;
187     if (prev) prev->next = event;
188     else pending_event = event;
189
190     /* alert the vm86 about the new event */
191     if (!sig_sent) {
192       TRACE("new event queued, signalling (time=%ld)\n", GetTickCount());
193       kill(dosvm_pid,SIGUSR2);
194       sig_sent++;
195     } else {
196       TRACE("new event queued (time=%ld)\n", GetTickCount());
197     }
198     LeaveCriticalSection(&qcrit);
199   } else {
200     /* DOS subsystem not running */
201     /* (this probably means that we're running a win16 app
202      *  which uses DPMI to thunk down to DOS services) */
203     if (irq<0) {
204       /* callback event, perform it with dummy context */
205       CONTEXT86 context;
206       memset(&context,0,sizeof(context));
207       (*relay)(&context,data);
208     } else {
209       ERR("IRQ without DOS task: should not happen");
210     }
211   }
212 }
213
214 static void DOSVM_ProcessConsole(void)
215 {
216   INPUT_RECORD msg;
217   DWORD res;
218   BYTE scan;
219
220   if (ReadConsoleInputA(GetStdHandle(STD_INPUT_HANDLE),&msg,1,&res)) {
221     switch (msg.EventType) {
222     case KEY_EVENT:
223       scan = msg.Event.KeyEvent.wVirtualScanCode;
224       if (!msg.Event.KeyEvent.bKeyDown) scan |= 0x80;
225
226       /* check whether extended bit is set,
227        * and if so, queue the extension prefix */
228       if (msg.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY) {
229         DOSVM_Int09SendScan(0xE0,0);
230       }
231       DOSVM_Int09SendScan(scan,msg.Event.KeyEvent.uChar.AsciiChar);
232       break;
233     default:
234       FIXME("unhandled console event: %d\n", msg.EventType);
235     }
236   }
237 }
238
239 static void DOSVM_ProcessMessage(MSG *msg)
240 {
241   BYTE scan = 0;
242
243   TRACE("got message %04x, wparam=%08x, lparam=%08lx\n",msg->message,msg->wParam,msg->lParam);
244   if ((msg->message>=WM_MOUSEFIRST)&&
245       (msg->message<=WM_MOUSELAST)) {
246     DOSVM_Int33Message(msg->message,msg->wParam,msg->lParam);
247   } else {
248     switch (msg->message) {
249     case WM_KEYUP:
250       scan = 0x80;
251     case WM_KEYDOWN:
252       scan |= (msg->lParam >> 16) & 0x7f;
253
254       /* check whether extended bit is set,
255        * and if so, queue the extension prefix */
256       if (msg->lParam & 0x1000000) {
257         /* FIXME: some keys (function keys) have
258          * extended bit set even when they shouldn't,
259          * should check for them */
260         DOSVM_Int09SendScan(0xE0,0);
261       }
262       DOSVM_Int09SendScan(scan,0);
263       break;
264     }
265   }
266 }
267
268 /***********************************************************************
269  *              Wait (WINEDOS.@)
270  */
271 void WINAPI DOSVM_Wait( INT read_pipe, HANDLE hObject )
272 {
273   MSG msg;
274   DWORD waitret;
275   HANDLE objs[2];
276   int objc;
277   BOOL got_msg = FALSE;
278
279   objs[0]=GetStdHandle(STD_INPUT_HANDLE);
280   objs[1]=hObject;
281   objc=hObject?2:1;
282   do {
283     /* check for messages (waste time before the response check below) */
284     if (PeekMessageA)
285     {
286         while (PeekMessageA(&msg,0,0,0,PM_REMOVE|PM_NOYIELD)) {
287             /* got a message */
288             DOSVM_ProcessMessage(&msg);
289             /* we don't need a TranslateMessage here */
290             DispatchMessageA(&msg);
291             got_msg = TRUE;
292         }
293     }
294 chk_console_input:
295     if (!got_msg) {
296       /* check for console input */
297       INPUT_RECORD msg;
298       DWORD num;
299       if (PeekConsoleInputA(objs[0],&msg,1,&num) && num) {
300         DOSVM_ProcessConsole();
301         got_msg = TRUE;
302       }
303     }
304     if (read_pipe == -1) {
305       /* dispatch pending events */
306       if (SHOULD_PEND(pending_event)) {
307         CONTEXT86 context = *current_context;
308         IF_SET(&context);
309         SET_PEND(&context);
310         DOSVM_SendQueuedEvents(&context);
311       }
312       if (got_msg) break;
313     } else {
314       fd_set readfds;
315       struct timeval timeout={0,0};
316       /* quick check for response from dosmod
317        * (faster than doing the full blocking wait, if data already available) */
318       FD_ZERO(&readfds); FD_SET(read_pipe,&readfds);
319       if (select(read_pipe+1,&readfds,NULL,NULL,&timeout)>0)
320         break;
321     }
322     /* nothing yet, block while waiting for something to do */
323     if (MsgWaitForMultipleObjects)
324         waitret = MsgWaitForMultipleObjects(objc,objs,FALSE,INFINITE,QS_ALLINPUT);
325     else
326         waitret = WaitForMultipleObjects(objc,objs,FALSE,INFINITE);
327
328     if (waitret==(DWORD)-1) {
329       ERR_(module)("dosvm wait error=%ld\n",GetLastError());
330     }
331     if ((read_pipe != -1) && hObject) {
332       if (waitret==(WAIT_OBJECT_0+1)) break;
333     }
334     if (waitret==WAIT_OBJECT_0)
335       goto chk_console_input;
336   } while (TRUE);
337 }
338
339 DWORD WINAPI DOSVM_Loop( LPVOID lpExtra )
340 {
341   HANDLE obj = GetStdHandle(STD_INPUT_HANDLE);
342   MSG msg;
343   DWORD waitret;
344
345   for(;;) {
346     TRACE_(int)("waiting for action\n");
347     waitret = MsgWaitForMultipleObjects(1, &obj, FALSE, INFINITE, QS_ALLINPUT);
348     if (waitret == WAIT_OBJECT_0) {
349       DOSVM_ProcessConsole();
350     }
351     else if (waitret == WAIT_OBJECT_0 + 1) {
352       GetMessageA(&msg, 0, 0, 0);
353       if (msg.hwnd) {
354         /* it's a window message */
355         DOSVM_ProcessMessage(&msg);
356         DispatchMessageA(&msg);
357       } else {
358         /* it's a thread message */
359         switch (msg.message) {
360         case WM_QUIT:
361           /* stop this madness!! */
362           return 0;
363         case WM_USER:
364           /* run passed procedure in this thread */
365           /* (sort of like APC, but we signal the completion) */
366           {
367             DOS_SPC *spc = (DOS_SPC *)msg.lParam;
368             TRACE_(int)("calling %p with arg %08x\n", spc->proc, spc->arg);
369             (spc->proc)(spc->arg);
370             TRACE_(int)("done, signalling event %d\n", msg.wParam);
371             SetEvent(msg.wParam);
372           }
373           break;
374         }
375       }
376     }
377     else break;
378   }
379   return 0;
380 }
381
382 static WINE_EXCEPTION_FILTER(exception_handler)
383 {
384   EXCEPTION_RECORD *rec = GetExceptionInformation()->ExceptionRecord;
385   CONTEXT *context = GetExceptionInformation()->ContextRecord;
386   int ret, arg = rec->ExceptionInformation[0];
387
388   switch(rec->ExceptionCode) {
389   case EXCEPTION_VM86_INTx:
390     if (TRACE_ON(relay)) {
391       DPRINTF("Call DOS int 0x%02x ret=%04lx:%04lx\n",
392               arg, context->SegCs, context->Eip );
393       DPRINTF(" eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx\n",
394               context->Eax, context->Ebx, context->Ecx, context->Edx,
395               context->Esi, context->Edi );
396       DPRINTF(" ebp=%08lx esp=%08lx ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\n",
397               context->Ebp, context->Esp, context->SegDs, context->SegEs,
398               context->SegFs, context->SegGs, context->EFlags );
399       }
400     ret = DOSVM_SimulateInt(arg, context, FALSE);
401     if (TRACE_ON(relay)) {
402       DPRINTF("Ret  DOS int 0x%02x ret=%04lx:%04lx\n",
403               arg, context->SegCs, context->Eip );
404       DPRINTF(" eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx\n",
405               context->Eax, context->Ebx, context->Ecx, context->Edx,
406               context->Esi, context->Edi );
407       DPRINTF(" ebp=%08lx esp=%08lx ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\n",
408               context->Ebp, context->Esp, context->SegDs, context->SegEs,
409               context->SegFs, context->SegGs, context->EFlags );
410     }
411     return ret ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_EXECUTION;
412
413   case EXCEPTION_VM86_STI:
414   /* case EXCEPTION_VM86_PICRETURN: */
415     IF_SET(context);
416     EnterCriticalSection(&qcrit);
417     sig_sent++;
418     while (NtCurrentTeb()->alarms) {
419       DOSVM_QueueEvent(0,DOS_PRIORITY_REALTIME,NULL,NULL);
420       /* hmm, instead of relying on this signal counter, we should
421        * probably check how many ticks have *really* passed, probably using
422        * QueryPerformanceCounter() or something like that */
423       InterlockedDecrement(&(NtCurrentTeb()->alarms));
424     }
425     TRACE_(int)("context=%p, current=%p\n", context, current_context);
426     TRACE_(int)("cs:ip=%04lx:%04lx, ss:sp=%04lx:%04lx\n", context->SegCs, context->Eip, context->SegSs, context->Esp);
427     if (!ISV86(context)) {
428       ERR_(int)("@#&*%%, winedos signal handling is *still* messed up\n");
429     }
430     TRACE_(int)("DOS task enabled interrupts %s events pending, sending events (time=%ld)\n", IS_PEND(context)?"with":"without", GetTickCount());
431     DOSVM_SendQueuedEvents(context);
432     sig_sent=0;
433     LeaveCriticalSection(&qcrit);
434     return EXCEPTION_CONTINUE_EXECUTION;
435   }
436   return EXCEPTION_CONTINUE_SEARCH;
437 }
438
439 int WINAPI DOSVM_Enter( CONTEXT86 *context )
440 {
441   CONTEXT86 *old_context = current_context;
442
443   current_context = context;
444   __TRY
445   {
446     __wine_enter_vm86( context );
447     TRACE_(module)( "vm86 returned: %s\n", strerror(errno) );
448   }
449   __EXCEPT(exception_handler)
450   {
451     TRACE_(module)( "leaving vm86 mode\n" );
452   }
453   __ENDTRY
454   current_context = old_context;
455   return 0;
456 }
457
458 /***********************************************************************
459  *              OutPIC (WINEDOS.@)
460  */
461 void WINAPI DOSVM_PIC_ioport_out( WORD port, BYTE val)
462 {
463     LPDOSEVENT event;
464
465     if ((port==0x20) && (val==0x20)) {
466       EnterCriticalSection(&qcrit);
467       if (current_event) {
468         /* EOI (End Of Interrupt) */
469         TRACE("received EOI for current IRQ, clearing\n");
470         event = current_event;
471         current_event = event->next;
472         if (event->relay)
473         (*event->relay)(NULL,event->data);
474         free(event);
475
476         if (pending_event) {
477           /* another event is pending, which we should probably
478            * be able to process now */
479           TRACE("another event pending, setting flag\n");
480           current_context->EFlags |= VIP_MASK;
481         }
482       } else {
483         WARN("EOI without active IRQ\n");
484       }
485       LeaveCriticalSection(&qcrit);
486     } else {
487       FIXME("unrecognized PIC command %02x\n",val);
488     }
489 }
490
491 /***********************************************************************
492  *              SetTimer (WINEDOS.@)
493  */
494 void WINAPI DOSVM_SetTimer( UINT ticks )
495 {
496   struct itimerval tim;
497
498   if (dosvm_pid) {
499     /* the PC clocks ticks at 1193180 Hz */
500     tim.it_interval.tv_sec=0;
501     tim.it_interval.tv_usec=MulDiv(ticks,1000000,1193180);
502     /* sanity check */
503     if (!tim.it_interval.tv_usec) tim.it_interval.tv_usec=1;
504     /* first tick value */
505     tim.it_value = tim.it_interval;
506     TRACE_(int)("setting timer tick delay to %ld us\n", tim.it_interval.tv_usec);
507     setitimer(ITIMER_REAL, &tim, NULL);
508   }
509 }
510
511 /***********************************************************************
512  *              GetTimer (WINEDOS.@)
513  */
514 UINT WINAPI DOSVM_GetTimer( void )
515 {
516   struct itimerval tim;
517
518   if (dosvm_pid) {
519     getitimer(ITIMER_REAL, &tim);
520     return MulDiv(tim.it_value.tv_usec,1193180,1000000);
521   }
522   return 0;
523 }
524
525 #else /* !MZ_SUPPORTED */
526
527 /***********************************************************************
528  *              Enter (WINEDOS.@)
529  */
530 INT WINAPI DOSVM_Enter( CONTEXT86 *context )
531 {
532  ERR_(module)("DOS realmode not supported on this architecture!\n");
533  return -1;
534 }
535
536 /***********************************************************************
537  *              Wait (WINEDOS.@)
538  */
539 void WINAPI DOSVM_Wait( INT read_pipe, HANDLE hObject) {}
540
541 /***********************************************************************
542  *              OutPIC (WINEDOS.@)
543  */
544 void WINAPI DOSVM_PIC_ioport_out( WORD port, BYTE val) {}
545
546 /***********************************************************************
547  *              SetTimer (WINEDOS.@)
548  */
549 void WINAPI DOSVM_SetTimer( UINT ticks ) {}
550
551 /***********************************************************************
552  *              GetTimer (WINEDOS.@)
553  */
554 UINT WINAPI DOSVM_GetTimer( void ) { return 0; }
555
556 /***********************************************************************
557  *              QueueEvent (WINEDOS.@)
558  */
559 void WINAPI DOSVM_QueueEvent( INT irq, INT priority, DOSRELAY relay, LPVOID data)
560 {
561   if (irq<0) {
562     /* callback event, perform it with dummy context */
563     CONTEXT86 context;
564     memset(&context,0,sizeof(context));
565     (*relay)(&context,data);
566   } else {
567     ERR("IRQ without DOS task: should not happen");
568   }
569 }
570
571 #endif
572
573 /**********************************************************************
574  *          DOSVM_GetRMHandler
575  *
576  * Return the real mode interrupt vector for a given interrupt.
577  */
578 FARPROC16 DOSVM_GetRMHandler( BYTE intnum )
579 {
580     return ((FARPROC16*)DOSMEM_SystemBase())[intnum];
581 }
582
583
584 /**********************************************************************
585  *          DOSVM_SetRMHandler
586  *
587  * Set the real mode interrupt handler for a given interrupt.
588  */
589 void DOSVM_SetRMHandler( BYTE intnum, FARPROC16 handler )
590 {
591     TRACE("Set real mode interrupt vector %02x <- %04x:%04x\n",
592                  intnum, HIWORD(handler), LOWORD(handler) );
593     ((FARPROC16*)DOSMEM_SystemBase())[intnum] = handler;
594 }
595
596
597 static const INTPROC real_mode_handlers[] =
598 {
599     /* 00 */ 0, 0, 0, 0, 0, 0, 0, 0,
600     /* 08 */ 0, DOSVM_Int09Handler, 0, 0, 0, 0, 0, 0,
601     /* 10 */ DOSVM_Int10Handler, INT_Int11Handler, INT_Int12Handler, INT_Int13Handler,
602              0, INT_Int15Handler, DOSVM_Int16Handler, DOSVM_Int17Handler,
603     /* 18 */ 0, 0, INT_Int1aHandler, 0, 0, 0, 0, 0,
604     /* 20 */ DOSVM_Int20Handler, DOSVM_Int21Handler, 0, 0, 0, INT_Int25Handler, 0, 0,
605     /* 28 */ 0, DOSVM_Int29Handler, INT_Int2aHandler, 0, 0, 0, 0, INT_Int2fHandler,
606     /* 30 */ 0, DOSVM_Int31Handler, 0, DOSVM_Int33Handler, 0, 0, 0, 0,
607     /* 38 */ 0, 0, 0, 0, 0, 0, 0, 0,
608     /* 40 */ 0, 0, 0, 0, 0, 0, 0, 0,
609     /* 48 */ 0, 0, 0, 0, 0, 0, 0, 0, 
610     /* 50 */ 0, 0, 0, 0, 0, 0, 0, 0,
611     /* 58 */ 0, 0, 0, 0, 0, 0, 0, 0,
612     /* 60 */ 0, 0, 0, 0, 0, 0, 0, DOSVM_Int67Handler
613 };
614
615
616 /**********************************************************************
617  *          DOSVM_RealModeInterrupt
618  *
619  * Handle real mode interrupts
620  */
621 void DOSVM_RealModeInterrupt( BYTE intnum, CONTEXT86 *context )
622 {
623     if (intnum < sizeof(real_mode_handlers)/sizeof(INTPROC) && real_mode_handlers[intnum])
624         (*real_mode_handlers[intnum])(context);
625     else
626     {
627         FIXME("Unknown Interrupt in DOS mode: 0x%x\n", intnum);
628         FIXME("    eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx\n",
629               context->Eax, context->Ebx, context->Ecx, context->Edx);
630         FIXME("    esi=%08lx edi=%08lx ds=%04lx es=%04lx\n",
631               context->Esi, context->Edi, context->SegDs, context->SegEs );
632     }
633 }
634
635
636 /**********************************************************************
637  *          DOSVM_Init
638  */
639 BOOL WINAPI DOSVM_Init( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
640 {
641     TRACE_(module)("(0x%08x,%ld,%p)\n", hinstDLL, fdwReason, lpvReserved);
642
643     if (fdwReason == DLL_PROCESS_ATTACH)
644     {
645         /* initialize the memory */
646         TRACE("Initializing DOS memory structures\n");
647         DOSMEM_Init( TRUE );
648         DOSDEV_InstallDOSDevices();
649     }
650     return TRUE;
651 }