IO_pp_outp: Allow to switch direction of the printer port.
[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
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     if (TRACE_ON(relay)) 
247     {
248         DPRINTF( "Call DOS int 0x%02x ret=%04lx:%08lx\n",
249                  intnum, context->SegCs, context->Eip );
250         DPRINTF( "  eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx\n",
251                  context->Eax, context->Ebx, context->Ecx, context->Edx );
252         DPRINTF( "  esi=%08lx edi=%08lx ebp=%08lx esp=%08lx \n",
253                  context->Esi, context->Edi, context->Ebp, context->Esp );
254         DPRINTF( "  ds=%04lx es=%04lx fs=%04lx gs=%04lx ss=%04lx flags=%08lx\n",
255                  context->SegDs, context->SegEs, context->SegFs, context->SegGs,
256                  context->SegSs, context->EFlags );
257     }
258
259     if (context->SegCs == DOSVM_dpmi_segments->dpmi_sel)
260     {
261         DOSVM_BuildCallFrame( context, 
262                               DOSVM_IntProcRelay,
263                               DOSVM_RawModeSwitchHandler );
264     }
265     else if (context->SegCs == DOSVM_dpmi_segments->relay_code_sel)
266     {
267         /*
268          * This must not be called using DOSVM_BuildCallFrame.
269          */
270         DOSVM_RelayHandler( context );
271     }
272     else if (context->SegCs == DOSVM_dpmi_segments->int48_sel)
273     {
274         /* Restore original flags stored into the stack by the caller. */
275         DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
276                                           context->SegSs, context->Esp);
277         context->EFlags = stack[2];
278
279         if (intnum != context->Eip / DOSVM_STUB_PM48)
280             WARN( "interrupt stub has been modified "
281                   "(interrupt is %02x, interrupt stub is %02lx)\n",
282                   intnum, context->Eip/DOSVM_STUB_PM48 );
283
284         TRACE( "builtin interrupt %02x has been branched to\n", intnum );
285
286         if (intnum == 0x25 || intnum == 0x26)
287             DOSVM_PushFlags( context, TRUE, TRUE );
288
289         DOSVM_BuildCallFrame( context, 
290                               DOSVM_IntProcRelay,
291                               DOSVM_GetBuiltinHandler(intnum) );
292     }
293     else if (context->SegCs == DOSVM_dpmi_segments->int16_sel)
294     {
295         /* Restore original flags stored into the stack by the caller. */
296         WORD *stack = CTX_SEG_OFF_TO_LIN(context, 
297                                          context->SegSs, context->Esp);
298         context->EFlags = (DWORD)MAKELONG( stack[2], HIWORD(context->EFlags) );
299
300         if (intnum != context->Eip / DOSVM_STUB_PM16)
301             WARN( "interrupt stub has been modified "
302                   "(interrupt is %02x, interrupt stub is %02lx)\n",
303                   intnum, context->Eip/DOSVM_STUB_PM16 );
304
305         TRACE( "builtin interrupt %02x has been branched to\n", intnum );
306
307         if (intnum == 0x25 || intnum == 0x26)
308             DOSVM_PushFlags( context, FALSE, TRUE );
309
310         DOSVM_BuildCallFrame( context, 
311                               DOSVM_IntProcRelay, 
312                               DOSVM_GetBuiltinHandler(intnum) );
313     }
314     else
315     {
316         DOSVM_HardwareInterruptPM( context, intnum );
317     }
318 }
319
320
321 /**********************************************************************
322  *         DOSVM_HardwareInterruptPM
323  *
324  * Emulate call to interrupt handler in 16-bit or 32-bit protected mode.
325  *
326  * Pushes interrupt frame to stack and changes instruction 
327  * pointer to interrupt handler.
328  */
329 void DOSVM_HardwareInterruptPM( CONTEXT86 *context, BYTE intnum ) 
330 {
331     if(DOSVM_IsDos32())
332     {
333         FARPROC48 addr = DOSVM_GetPMHandler48( intnum );
334         
335         if (addr.selector == DOSVM_dpmi_segments->int48_sel)
336         {
337             TRACE( "builtin interrupt %02lx has been invoked "
338                    "(through vector %02x)\n", 
339                    addr.offset / DOSVM_STUB_PM48, intnum );
340
341             if (intnum == 0x25 || intnum == 0x26)
342                 DOSVM_PushFlags( context, TRUE, FALSE );
343             else if (DOSVM_IsIRQ(intnum))
344                 DOSVM_PrepareIRQ( context, TRUE );
345
346             DOSVM_BuildCallFrame( context,
347                                   DOSVM_IntProcRelay,
348                                   DOSVM_GetBuiltinHandler(
349                                       addr.offset/DOSVM_STUB_PM48 ) );
350         }
351         else
352         {
353             DWORD *stack;
354             
355             TRACE( "invoking hooked interrupt %02x at %04x:%08lx\n",
356                    intnum, addr.selector, addr.offset );
357             
358             if (DOSVM_IsIRQ(intnum))
359                 DOSVM_PrepareIRQ( context, FALSE );
360
361             /* Push the flags and return address on the stack */
362             stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
363             *(--stack) = context->EFlags;
364             *(--stack) = context->SegCs;
365             *(--stack) = context->Eip;
366             context->Esp += -12;
367
368             /* Jump to the interrupt handler */
369             context->SegCs  = addr.selector;
370             context->Eip = addr.offset;
371         }
372     }
373     else
374     {
375         FARPROC16 addr = DOSVM_GetPMHandler16( intnum );
376
377         if (SELECTOROF(addr) == DOSVM_dpmi_segments->int16_sel)
378         {
379             TRACE( "builtin interrupt %02x has been invoked "
380                    "(through vector %02x)\n", 
381                    OFFSETOF(addr)/DOSVM_STUB_PM16, intnum );
382
383             if (intnum == 0x25 || intnum == 0x26)
384                 DOSVM_PushFlags( context, FALSE, FALSE );
385             else if (DOSVM_IsIRQ(intnum))
386                 DOSVM_PrepareIRQ( context, TRUE );
387
388             DOSVM_BuildCallFrame( context, 
389                                   DOSVM_IntProcRelay,
390                                   DOSVM_GetBuiltinHandler(
391                                       OFFSETOF(addr)/DOSVM_STUB_PM16 ) );
392         }
393         else
394         {
395             WORD *stack;
396             
397             TRACE( "invoking hooked interrupt %02x at %04x:%04x\n", 
398                    intnum, SELECTOROF(addr), OFFSETOF(addr) );
399
400             if (DOSVM_IsIRQ(intnum))
401                 DOSVM_PrepareIRQ( context, FALSE );
402
403             /* Push the flags and return address on the stack */
404             stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
405             *(--stack) = LOWORD(context->EFlags);
406             *(--stack) = context->SegCs;
407             *(--stack) = LOWORD(context->Eip);
408             ADD_LOWORD( context->Esp, -6 );
409
410             /* Jump to the interrupt handler */
411             context->SegCs =  HIWORD(addr);
412             context->Eip = LOWORD(addr);
413         }
414     }
415 }
416
417
418 /**********************************************************************
419  *         DOSVM_EmulateInterruptRM
420  *
421  * Emulate software interrupt in real mode.
422  * Called from VM86 emulation when intXX opcode is executed. 
423  *
424  * Either calls directly builtin handler or pushes interrupt frame to 
425  * stack and changes instruction pointer to interrupt handler.
426  *
427  * Returns FALSE if this interrupt was caused by return 
428  * from real mode wrapper.
429  */
430 BOOL WINAPI DOSVM_EmulateInterruptRM( CONTEXT86 *context, BYTE intnum ) 
431 {
432     if (TRACE_ON(relay)) 
433     {
434         DPRINTF( "Call DOS int 0x%02x ret=%04lx:%08lx\n",
435                  intnum, context->SegCs, context->Eip );
436         DPRINTF( "  eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx\n",
437                  context->Eax, context->Ebx, context->Ecx, context->Edx );
438         DPRINTF( "  esi=%08lx edi=%08lx ebp=%08lx esp=%08lx \n",
439                  context->Esi, context->Edi, context->Ebp, context->Esp );
440         DPRINTF( "  ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\n",
441                  context->SegDs, context->SegEs,
442                  context->SegFs, context->SegGs, context->EFlags );
443     }
444
445     /* check for our real-mode hooks */
446     if (intnum == 0x31)
447     {
448         /* is this exit from real-mode wrapper */
449         if (context->SegCs == DOSVM_dpmi_segments->wrap_seg)
450             return FALSE;
451
452         if (DOSVM_CheckWrappers( context ))
453             return TRUE;
454     }
455
456     /* check if the call is from our fake BIOS interrupt stubs */
457     if (context->SegCs==0xf000)
458     {
459         /* Restore original flags stored into the stack by the caller. */
460         WORD *stack = CTX_SEG_OFF_TO_LIN(context, 
461                                          context->SegSs, context->Esp);
462         context->EFlags = (DWORD)MAKELONG( stack[2], HIWORD(context->EFlags) );
463
464         if (intnum != context->Eip / DOSVM_STUB_RM)
465             WARN( "interrupt stub has been modified "
466                   "(interrupt is %02x, interrupt stub is %02lx)\n",
467                   intnum, context->Eip/DOSVM_STUB_RM );
468
469         TRACE( "builtin interrupt %02x has been branched to\n", intnum );
470         
471         DOSVM_CallBuiltinHandler( context, intnum );
472
473         /* Real mode stubs use IRET so we must put flags back into stack. */
474         stack[2] = LOWORD(context->EFlags);
475     }
476     else
477     {
478         DOSVM_HardwareInterruptRM( context, intnum );
479     }
480
481     return TRUE;
482 }
483
484
485 /**********************************************************************
486  *         DOSVM_HardwareInterruptRM
487  *
488  * Emulate call to interrupt handler in real mode.
489  *
490  * Either calls directly builtin handler or pushes interrupt frame to 
491  * stack and changes instruction pointer to interrupt handler.
492  */
493 void DOSVM_HardwareInterruptRM( CONTEXT86 *context, BYTE intnum ) 
494 {
495      FARPROC16 handler = DOSVM_GetRMHandler( intnum );
496
497      /* check if the call goes to an unhooked interrupt */
498      if (SELECTOROF(handler) == 0xf000) 
499      {
500          /* if so, call it directly */
501          TRACE( "builtin interrupt %02x has been invoked "
502                 "(through vector %02x)\n", 
503                 OFFSETOF(handler)/DOSVM_STUB_RM, intnum );
504          DOSVM_CallBuiltinHandler( context, OFFSETOF(handler)/DOSVM_STUB_RM );
505      }
506      else 
507      {
508          /* the interrupt is hooked, simulate interrupt in DOS space */ 
509          WORD* stack = PTR_REAL_TO_LIN( context->SegSs, context->Esp );
510          WORD  flag  = LOWORD( context->EFlags );
511
512          TRACE( "invoking hooked interrupt %02x at %04x:%04x\n", 
513                 intnum, SELECTOROF(handler), OFFSETOF(handler) );
514
515          /* Copy virtual interrupt flag to pushed interrupt flag. */
516          if (context->EFlags & VIF_MASK)
517              flag |= IF_MASK;
518          else 
519              flag &= ~IF_MASK;
520
521          *(--stack) = flag;
522          *(--stack) = context->SegCs;
523          *(--stack) = LOWORD( context->Eip );
524          context->Esp -= 6;
525          context->SegCs = SELECTOROF( handler );
526          context->Eip   = OFFSETOF( handler );
527
528          /* Clear virtual interrupt flag. */
529          context->EFlags &= ~VIF_MASK;
530      }
531 }
532
533
534 /**********************************************************************
535  *          DOSVM_GetRMHandler
536  *
537  * Return the real mode interrupt vector for a given interrupt.
538  */
539 FARPROC16 DOSVM_GetRMHandler( BYTE intnum )
540 {
541   return *DOSVM_GetRMVector( intnum );
542 }
543
544
545 /**********************************************************************
546  *          DOSVM_SetRMHandler
547  *
548  * Set the real mode interrupt handler for a given interrupt.
549  */
550 void DOSVM_SetRMHandler( BYTE intnum, FARPROC16 handler )
551 {
552   TRACE("Set real mode interrupt vector %02x <- %04x:%04x\n",
553        intnum, HIWORD(handler), LOWORD(handler) );
554   *DOSVM_GetRMVector( intnum ) = handler;
555 }
556
557
558 /**********************************************************************
559  *          DOSVM_GetPMHandler16
560  *
561  * Return the protected mode interrupt vector for a given interrupt.
562  */
563 FARPROC16 DOSVM_GetPMHandler16( BYTE intnum )
564 {
565     TDB *pTask;
566     FARPROC16 proc = 0;
567
568     pTask = GlobalLock16(GetCurrentTask());
569     if (pTask)
570     {
571         switch( intnum )
572         {
573         case 0x00:
574             proc = pTask->int0;
575             break;
576         case 0x02:
577             proc = pTask->int2;
578             break;
579         case 0x04:
580             proc = pTask->int4;
581             break;
582         case 0x06:
583             proc = pTask->int6;
584             break;
585         case 0x07:
586             proc = pTask->int7;
587             break;
588         case 0x3e:
589             proc = pTask->int3e;
590             break;
591         case 0x75:
592             proc = pTask->int75;
593             break;
594         }
595         if( proc )
596             return proc;
597     }
598     if (!DOSVM_Vectors16[intnum])
599     {
600         proc = (FARPROC16)MAKESEGPTR( DOSVM_dpmi_segments->int16_sel,
601                                                 DOSVM_STUB_PM16 * intnum );
602         DOSVM_Vectors16[intnum] = proc;
603     }
604     return DOSVM_Vectors16[intnum];
605 }
606
607
608 /**********************************************************************
609  *          DOSVM_SetPMHandler16
610  *
611  * Set the protected mode interrupt handler for a given interrupt.
612  */
613 void DOSVM_SetPMHandler16( BYTE intnum, FARPROC16 handler )
614 {
615   TDB *pTask;
616
617   TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
618        intnum, HIWORD(handler), LOWORD(handler) );
619
620   pTask = GlobalLock16(GetCurrentTask());
621   if (!pTask)
622     return;
623   switch( intnum )
624   {
625   case 0x00:
626     pTask->int0 = handler;
627     break;
628   case 0x02:
629     pTask->int2 = handler;
630     break;
631   case 0x04:
632     pTask->int4 = handler;
633     break;
634   case 0x06:
635     pTask->int6 = handler;
636     break;
637   case 0x07:
638     pTask->int7 = handler;
639     break;
640   case 0x3e:
641     pTask->int3e = handler;
642     break;
643   case 0x75:
644     pTask->int75 = handler;
645     break;
646   default:
647     DOSVM_Vectors16[intnum] = handler;
648     break;
649   }
650 }
651
652
653 /**********************************************************************
654  *         DOSVM_GetPMHandler48
655  *
656  * Return the protected mode interrupt vector for a given interrupt.
657  * Used to get 48-bit pointer for 32-bit interrupt handlers in DPMI32.
658  */
659 FARPROC48 DOSVM_GetPMHandler48( BYTE intnum )
660 {
661   if (!DOSVM_Vectors48[intnum].selector)
662   {
663     DOSVM_Vectors48[intnum].selector = DOSVM_dpmi_segments->int48_sel;
664     DOSVM_Vectors48[intnum].offset = DOSVM_STUB_PM48 * intnum;
665   }
666   return DOSVM_Vectors48[intnum];
667 }
668
669
670 /**********************************************************************
671  *         DOSVM_SetPMHandler48
672  *
673  * Set the protected mode interrupt handler for a given interrupt.
674  * Used to set 48-bit pointer for 32-bit interrupt handlers in DPMI32.
675  */
676 void DOSVM_SetPMHandler48( BYTE intnum, FARPROC48 handler )
677 {
678   TRACE("Set 32-bit protected mode interrupt vector %02x <- %04x:%08lx\n",
679        intnum, handler.selector, handler.offset );
680   DOSVM_Vectors48[intnum] = handler;
681 }
682
683
684 /**********************************************************************
685  *         DOSVM_CallBuiltinHandler
686  *
687  * Execute Wine interrupt handler procedure.
688  */
689 void WINAPI DOSVM_CallBuiltinHandler( CONTEXT86 *context, BYTE intnum ) 
690 {
691     /*
692      * FIXME: Make all builtin interrupt calls go via this routine.
693      * FIXME: Check for PM->RM interrupt reflection.
694      * FIXME: Check for RM->PM interrupt reflection.
695      */
696
697   INTPROC proc = DOSVM_GetBuiltinHandler( intnum );
698   proc( context );
699 }