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