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