Don't share some protected mode interrupts in winedos.
[wine] / dlls / winedos / interrupts.c
1 /*
2  * Interrupt emulation
3  *
4  * Copyright 2002 Jukka Heinonen
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
21 #include "config.h"
22
23 #include "dosexe.h"
24 #include "wine/debug.h"
25 #include "wine/winbase16.h"
26
27 #include "thread.h"
28 #include "task.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(int);
31 WINE_DECLARE_DEBUG_CHANNEL(relay);
32
33
34 static FARPROC16     DOSVM_Vectors16[256];
35 static FARPROC48     DOSVM_Vectors48[256];
36 static const INTPROC DOSVM_VectorsBuiltin[] =
37 {
38   /* 00 */ 0,                  0,                  0,                  0,
39   /* 04 */ 0,                  0,                  0,                  0,
40   /* 08 */ DOSVM_Int08Handler, DOSVM_Int09Handler, 0,                  0,
41   /* 0C */ 0,                  0,                  0,                  0,
42   /* 10 */ DOSVM_Int10Handler, DOSVM_Int11Handler, DOSVM_Int12Handler, DOSVM_Int13Handler,
43   /* 14 */ 0,                  DOSVM_Int15Handler, DOSVM_Int16Handler, DOSVM_Int17Handler,
44   /* 18 */ 0,                  DOSVM_Int19Handler, DOSVM_Int1aHandler, 0,
45   /* 1C */ 0,                  0,                  0,                  0,
46   /* 20 */ DOSVM_Int20Handler, DOSVM_Int21Handler, 0,                  0,
47   /* 24 */ 0,                  DOSVM_Int25Handler, DOSVM_Int26Handler, 0,
48   /* 28 */ 0,                  DOSVM_Int29Handler, DOSVM_Int2aHandler, 0,
49   /* 2C */ 0,                  0,                  0,                  DOSVM_Int2fHandler,
50   /* 30 */ 0,                  DOSVM_Int31Handler, 0,                  DOSVM_Int33Handler,
51   /* 34 */ DOSVM_Int34Handler, DOSVM_Int35Handler, DOSVM_Int36Handler, DOSVM_Int37Handler,
52   /* 38 */ DOSVM_Int38Handler, DOSVM_Int39Handler, DOSVM_Int3aHandler, DOSVM_Int3bHandler,
53   /* 3C */ DOSVM_Int3cHandler, DOSVM_Int3dHandler, DOSVM_Int3eHandler, 0,
54   /* 40 */ 0,                  DOSVM_Int41Handler, 0,                  0,
55   /* 44 */ 0,                  0,                  0,                  0,
56   /* 48 */ 0,                  0,                  0,                  DOSVM_Int4bHandler,
57   /* 4C */ 0,                  0,                  0,                  0,
58   /* 50 */ 0,                  0,                  0,                  0,
59   /* 54 */ 0,                  0,                  0,                  0,
60   /* 58 */ 0,                  0,                  0,                  0,
61   /* 5C */ DOSVM_Int5cHandler, 0,                  0,                  0,
62   /* 60 */ 0,                  0,                  0,                  0,
63   /* 64 */ 0,                  0,                  0,                  DOSVM_Int67Handler
64 };
65
66
67 /*
68  * Sizes of real mode and protected mode interrupt stubs.
69  */
70 #define DOSVM_STUB_RM   4
71 #define DOSVM_STUB_PM16 5
72 #define DOSVM_STUB_PM48 6
73
74
75 /**********************************************************************
76  *         DOSVM_GetRMVector
77  *
78  * Return pointer to real mode interrupt vector. These are not at fixed 
79  * location because those Win16 programs that do not use any real mode 
80  * code have protected NULL pointer catching block at low linear memory 
81  * and interrupt vectors have been moved to another location.
82  */
83 static FARPROC16* DOSVM_GetRMVector( BYTE intnum )
84 {
85     LDT_ENTRY entry;
86     FARPROC16 proc;
87
88     proc = GetProcAddress16( GetModuleHandle16( "KERNEL" ), 
89                              (LPCSTR)(ULONG_PTR)183 );
90     wine_ldt_get_entry( LOWORD(proc), &entry );
91
92     return (FARPROC16*)wine_ldt_get_base( &entry ) + intnum;
93 }
94
95
96 /**********************************************************************
97  *         DOSVM_IsIRQ
98  *
99  * Return TRUE if interrupt is an IRQ.
100  */
101 static BOOL DOSVM_IsIRQ( BYTE intnum )
102 {
103     if (intnum >= 0x08 && intnum <= 0x0f)
104         return TRUE;
105
106     if (intnum >= 0x70 && intnum <= 0x77)
107         return TRUE;
108
109     return FALSE;
110 }
111
112
113 /**********************************************************************
114  *         DOSVM_DefaultHandler
115  *
116  * Default interrupt handler. This will be used to emulate all
117  * interrupts that don't have their own interrupt handler.
118  */
119 void WINAPI DOSVM_DefaultHandler( CONTEXT86 *context )
120 {
121 }
122
123
124 /**********************************************************************
125  *         DOSVM_GetBuiltinHandler
126  *
127  * Return Wine interrupt handler procedure for a given interrupt.
128  */
129 static INTPROC DOSVM_GetBuiltinHandler( BYTE intnum )
130 {
131     if (intnum < sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) {
132         INTPROC proc = DOSVM_VectorsBuiltin[intnum];
133         if (proc)
134             return proc;
135     }
136
137     WARN("int%x not implemented, returning dummy handler\n", intnum );
138
139     if (DOSVM_IsIRQ(intnum))
140         return DOSVM_AcknowledgeIRQ;
141
142     return DOSVM_DefaultHandler;
143 }
144
145
146 /**********************************************************************
147  *          DOSVM_IntProcRelay
148  *
149  * Simple DOSRELAY that interprets its argument as INTPROC and calls it.
150  */
151 static void DOSVM_IntProcRelay( CONTEXT86 *context, LPVOID data )
152 {
153     INTPROC proc = (INTPROC)data;
154     proc(context);
155 }
156
157
158 /**********************************************************************
159  *          DOSVM_PrepareIRQ
160  *
161  */
162 static void DOSVM_PrepareIRQ( CONTEXT86 *context, BOOL isbuiltin )
163 {
164     /* Disable virtual interrupts. */
165     NtCurrentTeb()->dpmi_vif = 0;
166
167     if (!isbuiltin)
168     {
169         DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
170                                           context->SegSs,
171                                           context->Esp);
172
173         /* Push return address to stack. */
174         *(--stack) = context->SegCs;
175         *(--stack) = context->Eip;
176         context->Esp += -8;
177
178         /* Jump to enable interrupts stub. */
179         context->SegCs = DOSVM_dpmi_segments->relay_code_sel;
180         context->Eip   = 5;
181     }
182 }
183
184
185 /**********************************************************************
186  *          DOSVM_PushFlags
187  *
188  * This routine is used to make default int25 and int26 handlers leave the 
189  * original eflags into stack. In order to do this, stack is manipulated
190  * so that it actually contains two copies of eflags, one of which is
191  * popped during return from interrupt handler.
192  */
193 static void DOSVM_PushFlags( CONTEXT86 *context, BOOL islong, BOOL isstub )
194 {
195     if (islong)
196     {
197         DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
198                                           context->SegSs, 
199                                           context->Esp);
200         context->Esp += -4; /* One item will be added to stack. */
201
202         if (isstub)
203         {
204             DWORD ip = stack[0];
205             DWORD cs = stack[1];
206             stack += 2; /* Pop ip and cs. */
207             *(--stack) = context->EFlags;
208             *(--stack) = cs;
209             *(--stack) = ip;
210         }
211         else
212             *(--stack) = context->EFlags;            
213     }
214     else
215     {
216         WORD *stack = CTX_SEG_OFF_TO_LIN(context, 
217                                          context->SegSs, 
218                                          context->Esp);
219         ADD_LOWORD( context->Esp, -2 ); /* One item will be added to stack. */
220
221         if (isstub)
222         {
223             WORD ip = stack[0];
224             WORD cs = stack[1];
225             stack += 2; /* Pop ip and cs. */
226             *(--stack) = LOWORD(context->EFlags);
227             *(--stack) = cs;
228             *(--stack) = ip;
229         }
230         else
231             *(--stack) = LOWORD(context->EFlags);
232     }
233 }
234
235
236 /**********************************************************************
237  *         DOSVM_EmulateInterruptPM
238  *
239  * Emulate software interrupt in 16-bit or 32-bit protected mode.
240  * Called from signal handler when intXX opcode is executed. 
241  *
242  * Pushes interrupt frame to stack and changes instruction 
243  * pointer to interrupt handler.
244  */
245 void WINAPI DOSVM_EmulateInterruptPM( CONTEXT86 *context, BYTE intnum ) 
246 {
247     if (TRACE_ON(relay)) 
248     {
249         DPRINTF( "Call DOS int 0x%02x ret=%04lx:%08lx\n",
250                  intnum, context->SegCs, context->Eip );
251         DPRINTF( "  eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx\n",
252                  context->Eax, context->Ebx, context->Ecx, context->Edx );
253         DPRINTF( "  esi=%08lx edi=%08lx ebp=%08lx esp=%08lx \n",
254                  context->Esi, context->Edi, context->Ebp, context->Esp );
255         DPRINTF( "  ds=%04lx es=%04lx fs=%04lx gs=%04lx ss=%04lx flags=%08lx\n",
256                  context->SegDs, context->SegEs, context->SegFs, context->SegGs,
257                  context->SegSs, context->EFlags );
258     }
259
260     if (context->SegCs == DOSVM_dpmi_segments->dpmi_sel)
261     {
262         DOSVM_BuildCallFrame( context, 
263                               DOSVM_IntProcRelay,
264                               DOSVM_RawModeSwitchHandler );
265     }
266     else if (context->SegCs == DOSVM_dpmi_segments->relay_code_sel)
267     {
268         /*
269          * This must not be called using DOSVM_BuildCallFrame.
270          */
271         DOSVM_RelayHandler( context );
272     }
273     else if (context->SegCs == DOSVM_dpmi_segments->int48_sel)
274     {
275         /* Restore original flags stored into the stack by the caller. */
276         DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
277                                           context->SegSs, context->Esp);
278         context->EFlags = stack[2];
279
280         if (intnum != context->Eip / DOSVM_STUB_PM48)
281             WARN( "interrupt stub has been modified "
282                   "(interrupt is %02x, interrupt stub is %02lx)\n",
283                   intnum, context->Eip/DOSVM_STUB_PM48 );
284
285         TRACE( "builtin interrupt %02x has been branched to\n", intnum );
286
287         if (intnum == 0x25 || intnum == 0x26)
288             DOSVM_PushFlags( context, TRUE, TRUE );
289
290         DOSVM_BuildCallFrame( context, 
291                               DOSVM_IntProcRelay,
292                               DOSVM_GetBuiltinHandler(intnum) );
293     }
294     else if (context->SegCs == DOSVM_dpmi_segments->int16_sel)
295     {
296         /* Restore original flags stored into the stack by the caller. */
297         WORD *stack = CTX_SEG_OFF_TO_LIN(context, 
298                                          context->SegSs, context->Esp);
299         context->EFlags = (DWORD)MAKELONG( stack[2], HIWORD(context->EFlags) );
300
301         if (intnum != context->Eip / DOSVM_STUB_PM16)
302             WARN( "interrupt stub has been modified "
303                   "(interrupt is %02x, interrupt stub is %02lx)\n",
304                   intnum, context->Eip/DOSVM_STUB_PM16 );
305
306         TRACE( "builtin interrupt %02x has been branched to\n", intnum );
307
308         if (intnum == 0x25 || intnum == 0x26)
309             DOSVM_PushFlags( context, FALSE, TRUE );
310
311         DOSVM_BuildCallFrame( context, 
312                               DOSVM_IntProcRelay, 
313                               DOSVM_GetBuiltinHandler(intnum) );
314     }
315     else
316     {
317         DOSVM_HardwareInterruptPM( context, intnum );
318     }
319 }
320
321
322 /**********************************************************************
323  *         DOSVM_HardwareInterruptPM
324  *
325  * Emulate call to interrupt handler in 16-bit or 32-bit protected mode.
326  *
327  * Pushes interrupt frame to stack and changes instruction 
328  * pointer to interrupt handler.
329  */
330 void DOSVM_HardwareInterruptPM( CONTEXT86 *context, BYTE intnum ) 
331 {
332     if(DOSVM_IsDos32())
333     {
334         FARPROC48 addr = DOSVM_GetPMHandler48( intnum );
335         
336         if (addr.selector == DOSVM_dpmi_segments->int48_sel)
337         {
338             TRACE( "builtin interrupt %02lx has been invoked "
339                    "(through vector %02x)\n", 
340                    addr.offset / DOSVM_STUB_PM48, intnum );
341
342             if (intnum == 0x25 || intnum == 0x26)
343                 DOSVM_PushFlags( context, TRUE, FALSE );
344             else if (DOSVM_IsIRQ(intnum))
345                 DOSVM_PrepareIRQ( context, TRUE );
346
347             DOSVM_BuildCallFrame( context,
348                                   DOSVM_IntProcRelay,
349                                   DOSVM_GetBuiltinHandler(
350                                       addr.offset/DOSVM_STUB_PM48 ) );
351         }
352         else
353         {
354             DWORD *stack;
355             
356             TRACE( "invoking hooked interrupt %02x at %04x:%08lx\n",
357                    intnum, addr.selector, addr.offset );
358             
359             if (DOSVM_IsIRQ(intnum))
360                 DOSVM_PrepareIRQ( context, FALSE );
361
362             /* Push the flags and return address on the stack */
363             stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
364             *(--stack) = context->EFlags;
365             *(--stack) = context->SegCs;
366             *(--stack) = context->Eip;
367             context->Esp += -12;
368
369             /* Jump to the interrupt handler */
370             context->SegCs  = addr.selector;
371             context->Eip = addr.offset;
372         }
373     }
374     else
375     {
376         FARPROC16 addr = DOSVM_GetPMHandler16( intnum );
377
378         if (SELECTOROF(addr) == DOSVM_dpmi_segments->int16_sel)
379         {
380             TRACE( "builtin interrupt %02x has been invoked "
381                    "(through vector %02x)\n", 
382                    OFFSETOF(addr)/DOSVM_STUB_PM16, intnum );
383
384             if (intnum == 0x25 || intnum == 0x26)
385                 DOSVM_PushFlags( context, FALSE, FALSE );
386             else if (DOSVM_IsIRQ(intnum))
387                 DOSVM_PrepareIRQ( context, TRUE );
388
389             DOSVM_BuildCallFrame( context, 
390                                   DOSVM_IntProcRelay,
391                                   DOSVM_GetBuiltinHandler(
392                                       OFFSETOF(addr)/DOSVM_STUB_PM16 ) );
393         }
394         else
395         {
396             WORD *stack;
397             
398             TRACE( "invoking hooked interrupt %02x at %04x:%04x\n", 
399                    intnum, SELECTOROF(addr), OFFSETOF(addr) );
400
401             if (DOSVM_IsIRQ(intnum))
402                 DOSVM_PrepareIRQ( context, FALSE );
403
404             /* Push the flags and return address on the stack */
405             stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
406             *(--stack) = LOWORD(context->EFlags);
407             *(--stack) = context->SegCs;
408             *(--stack) = LOWORD(context->Eip);
409             ADD_LOWORD( context->Esp, -6 );
410
411             /* Jump to the interrupt handler */
412             context->SegCs =  HIWORD(addr);
413             context->Eip = LOWORD(addr);
414         }
415     }
416 }
417
418
419 /**********************************************************************
420  *         DOSVM_EmulateInterruptRM
421  *
422  * Emulate software interrupt in real mode.
423  * Called from VM86 emulation when intXX opcode is executed. 
424  *
425  * Either calls directly builtin handler or pushes interrupt frame to 
426  * stack and changes instruction pointer to interrupt handler.
427  *
428  * Returns FALSE if this interrupt was caused by return 
429  * from real mode wrapper.
430  */
431 BOOL WINAPI DOSVM_EmulateInterruptRM( CONTEXT86 *context, BYTE intnum ) 
432 {
433     if (TRACE_ON(relay)) 
434     {
435         DPRINTF( "Call DOS int 0x%02x ret=%04lx:%08lx\n",
436                  intnum, context->SegCs, context->Eip );
437         DPRINTF( "  eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx\n",
438                  context->Eax, context->Ebx, context->Ecx, context->Edx );
439         DPRINTF( "  esi=%08lx edi=%08lx ebp=%08lx esp=%08lx \n",
440                  context->Esi, context->Edi, context->Ebp, context->Esp );
441         DPRINTF( "  ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\n",
442                  context->SegDs, context->SegEs,
443                  context->SegFs, context->SegGs, context->EFlags );
444     }
445
446     /* check for our real-mode hooks */
447     if (intnum == 0x31)
448     {
449         /* is this exit from real-mode wrapper */
450         if (context->SegCs == DOSVM_dpmi_segments->wrap_seg)
451             return FALSE;
452
453         if (DOSVM_CheckWrappers( context ))
454             return TRUE;
455     }
456
457     /* check if the call is from our fake BIOS interrupt stubs */
458     if (context->SegCs==0xf000)
459     {
460         /* Restore original flags stored into the stack by the caller. */
461         WORD *stack = CTX_SEG_OFF_TO_LIN(context, 
462                                          context->SegSs, context->Esp);
463         context->EFlags = (DWORD)MAKELONG( stack[2], HIWORD(context->EFlags) );
464
465         if (intnum != context->Eip / DOSVM_STUB_RM)
466             WARN( "interrupt stub has been modified "
467                   "(interrupt is %02x, interrupt stub is %02lx)\n",
468                   intnum, context->Eip/DOSVM_STUB_RM );
469
470         TRACE( "builtin interrupt %02x has been branched to\n", intnum );
471         
472         DOSVM_CallBuiltinHandler( context, intnum );
473
474         /* Real mode stubs use IRET so we must put flags back into stack. */
475         stack[2] = LOWORD(context->EFlags);
476     }
477     else
478     {
479         DOSVM_HardwareInterruptRM( context, intnum );
480     }
481
482     return TRUE;
483 }
484
485
486 /**********************************************************************
487  *         DOSVM_HardwareInterruptRM
488  *
489  * Emulate call to interrupt handler in real mode.
490  *
491  * Either calls directly builtin handler or pushes interrupt frame to 
492  * stack and changes instruction pointer to interrupt handler.
493  */
494 void DOSVM_HardwareInterruptRM( CONTEXT86 *context, BYTE intnum ) 
495 {
496      FARPROC16 handler = DOSVM_GetRMHandler( intnum );
497
498      /* check if the call goes to an unhooked interrupt */
499      if (SELECTOROF(handler) == 0xf000) 
500      {
501          /* if so, call it directly */
502          TRACE( "builtin interrupt %02x has been invoked "
503                 "(through vector %02x)\n", 
504                 OFFSETOF(handler)/DOSVM_STUB_RM, intnum );
505          DOSVM_CallBuiltinHandler( context, OFFSETOF(handler)/DOSVM_STUB_RM );
506      }
507      else 
508      {
509          /* the interrupt is hooked, simulate interrupt in DOS space */ 
510          WORD* stack = PTR_REAL_TO_LIN( context->SegSs, context->Esp );
511          WORD  flag  = LOWORD( context->EFlags );
512
513          TRACE( "invoking hooked interrupt %02x at %04x:%04x\n", 
514                 intnum, SELECTOROF(handler), OFFSETOF(handler) );
515
516          /* Copy virtual interrupt flag to pushed interrupt flag. */
517          if (context->EFlags & VIF_MASK)
518              flag |= IF_MASK;
519          else 
520              flag &= ~IF_MASK;
521
522          *(--stack) = flag;
523          *(--stack) = context->SegCs;
524          *(--stack) = LOWORD( context->Eip );
525          context->Esp -= 6;
526          context->SegCs = SELECTOROF( handler );
527          context->Eip   = OFFSETOF( handler );
528
529          /* Clear virtual interrupt flag. */
530          context->EFlags &= ~VIF_MASK;
531      }
532 }
533
534
535 /**********************************************************************
536  *          DOSVM_GetRMHandler
537  *
538  * Return the real mode interrupt vector for a given interrupt.
539  */
540 FARPROC16 DOSVM_GetRMHandler( BYTE intnum )
541 {
542   return *DOSVM_GetRMVector( intnum );
543 }
544
545
546 /**********************************************************************
547  *          DOSVM_SetRMHandler
548  *
549  * Set the real mode interrupt handler for a given interrupt.
550  */
551 void DOSVM_SetRMHandler( BYTE intnum, FARPROC16 handler )
552 {
553   TRACE("Set real mode interrupt vector %02x <- %04x:%04x\n",
554        intnum, HIWORD(handler), LOWORD(handler) );
555   *DOSVM_GetRMVector( intnum ) = handler;
556 }
557
558
559 /**********************************************************************
560  *          DOSVM_GetPMHandler16
561  *
562  * Return the protected mode interrupt vector for a given interrupt.
563  */
564 FARPROC16 DOSVM_GetPMHandler16( BYTE intnum )
565 {
566     TDB *pTask;
567     FARPROC16 proc = 0;
568
569     pTask = GlobalLock16(GetCurrentTask());
570     if (pTask)
571     {
572         switch( intnum )
573         {
574         case 0x00:
575             proc = pTask->int0;
576             break;
577         case 0x02:
578             proc = pTask->int2;
579             break;
580         case 0x04:
581             proc = pTask->int4;
582             break;
583         case 0x06:
584             proc = pTask->int6;
585             break;
586         case 0x07:
587             proc = pTask->int7;
588             break;
589         case 0x3e:
590             proc = pTask->int3e;
591             break;
592         case 0x75:
593             proc = pTask->int75;
594             break;
595         }
596         if( proc )
597             return proc;
598     }
599     if (!DOSVM_Vectors16[intnum])
600     {
601         proc = (FARPROC16)MAKESEGPTR( DOSVM_dpmi_segments->int16_sel,
602                                                 DOSVM_STUB_PM16 * intnum );
603         DOSVM_Vectors16[intnum] = proc;
604     }
605     return DOSVM_Vectors16[intnum];
606 }
607
608
609 /**********************************************************************
610  *          DOSVM_SetPMHandler16
611  *
612  * Set the protected mode interrupt handler for a given interrupt.
613  */
614 void DOSVM_SetPMHandler16( BYTE intnum, FARPROC16 handler )
615 {
616   TDB *pTask;
617
618   TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
619        intnum, HIWORD(handler), LOWORD(handler) );
620
621   pTask = GlobalLock16(GetCurrentTask());
622   if (!pTask)
623     return;
624   switch( intnum )
625   {
626   case 0x00:
627     pTask->int0 = handler;
628     break;
629   case 0x02:
630     pTask->int2 = handler;
631     break;
632   case 0x04:
633     pTask->int4 = handler;
634     break;
635   case 0x06:
636     pTask->int6 = handler;
637     break;
638   case 0x07:
639     pTask->int7 = handler;
640     break;
641   case 0x3e:
642     pTask->int3e = handler;
643     break;
644   case 0x75:
645     pTask->int75 = handler;
646     break;
647   default:
648     DOSVM_Vectors16[intnum] = handler;
649     break;
650   }
651 }
652
653
654 /**********************************************************************
655  *         DOSVM_GetPMHandler48
656  *
657  * Return the protected mode interrupt vector for a given interrupt.
658  * Used to get 48-bit pointer for 32-bit interrupt handlers in DPMI32.
659  */
660 FARPROC48 DOSVM_GetPMHandler48( BYTE intnum )
661 {
662   if (!DOSVM_Vectors48[intnum].selector)
663   {
664     DOSVM_Vectors48[intnum].selector = DOSVM_dpmi_segments->int48_sel;
665     DOSVM_Vectors48[intnum].offset = DOSVM_STUB_PM48 * intnum;
666   }
667   return DOSVM_Vectors48[intnum];
668 }
669
670
671 /**********************************************************************
672  *         DOSVM_SetPMHandler48
673  *
674  * Set the protected mode interrupt handler for a given interrupt.
675  * Used to set 48-bit pointer for 32-bit interrupt handlers in DPMI32.
676  */
677 void DOSVM_SetPMHandler48( BYTE intnum, FARPROC48 handler )
678 {
679   TRACE("Set 32-bit protected mode interrupt vector %02x <- %04x:%08lx\n",
680        intnum, handler.selector, handler.offset );
681   DOSVM_Vectors48[intnum] = handler;
682 }
683
684
685 /**********************************************************************
686  *         DOSVM_CallBuiltinHandler
687  *
688  * Execute Wine interrupt handler procedure.
689  */
690 void WINAPI DOSVM_CallBuiltinHandler( CONTEXT86 *context, BYTE intnum ) 
691 {
692     /*
693      * FIXME: Make all builtin interrupt calls go via this routine.
694      * FIXME: Check for PM->RM interrupt reflection.
695      * FIXME: Check for RM->PM interrupt reflection.
696      */
697
698   INTPROC proc = DOSVM_GetBuiltinHandler( intnum );
699   proc( context );
700 }