Fixed some issues found by winapi_check.
[wine/multimedia.git] / dlls / winedos / interrupts.c
blobc0e9e1bee0d466124fde6bfdc8c6fe78fa904341
1 /*
2 * Interrupt emulation
4 * Copyright 2002 Jukka Heinonen
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.
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.
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
21 #include "config.h"
23 #include "dosexe.h"
24 #include "wine/debug.h"
25 #include "wine/winbase16.h"
27 #include "thread.h"
29 #ifdef HAVE_SYS_VM86_H
30 # include <sys/vm86.h>
31 #endif
33 #ifndef IF_MASK
34 #define IF_MASK 0x00000200
35 #endif
37 #ifndef VIF_MASK
38 #define VIF_MASK 0x00080000
39 #endif
42 WINE_DEFAULT_DEBUG_CHANNEL(int);
43 WINE_DECLARE_DEBUG_CHANNEL(relay);
46 static FARPROC16 DOSVM_Vectors16[256];
47 static FARPROC48 DOSVM_Vectors48[256];
48 static const INTPROC DOSVM_VectorsBuiltin[] =
50 /* 00 */ 0, 0, 0, 0,
51 /* 04 */ 0, 0, 0, 0,
52 /* 08 */ DOSVM_Int08Handler, DOSVM_Int09Handler, 0, 0,
53 /* 0C */ 0, 0, 0, 0,
54 /* 10 */ DOSVM_Int10Handler, DOSVM_Int11Handler, DOSVM_Int12Handler, DOSVM_Int13Handler,
55 /* 14 */ 0, DOSVM_Int15Handler, DOSVM_Int16Handler, DOSVM_Int17Handler,
56 /* 18 */ 0, 0, DOSVM_Int1aHandler, 0,
57 /* 1C */ 0, 0, 0, 0,
58 /* 20 */ DOSVM_Int20Handler, DOSVM_Int21Handler, 0, 0,
59 /* 24 */ 0, DOSVM_Int25Handler, DOSVM_Int26Handler, 0,
60 /* 28 */ 0, DOSVM_Int29Handler, DOSVM_Int2aHandler, 0,
61 /* 2C */ 0, 0, 0, DOSVM_Int2fHandler,
62 /* 30 */ 0, DOSVM_Int31Handler, 0, DOSVM_Int33Handler,
63 /* 34 */ DOSVM_Int34Handler, DOSVM_Int35Handler, DOSVM_Int36Handler, DOSVM_Int37Handler,
64 /* 38 */ DOSVM_Int38Handler, DOSVM_Int39Handler, DOSVM_Int3aHandler, DOSVM_Int3bHandler,
65 /* 3C */ DOSVM_Int3cHandler, DOSVM_Int3dHandler, DOSVM_Int3eHandler, 0,
66 /* 40 */ 0, DOSVM_Int41Handler, 0, 0,
67 /* 44 */ 0, 0, 0, 0,
68 /* 48 */ 0, 0, 0, DOSVM_Int4bHandler,
69 /* 4C */ 0, 0, 0, 0,
70 /* 50 */ 0, 0, 0, 0,
71 /* 54 */ 0, 0, 0, 0,
72 /* 58 */ 0, 0, 0, 0,
73 /* 5C */ DOSVM_Int5cHandler, 0, 0, 0,
74 /* 60 */ 0, 0, 0, 0,
75 /* 64 */ 0, 0, 0, DOSVM_Int67Handler
80 * Sizes of real mode and protected mode interrupt stubs.
82 #define DOSVM_STUB_RM 4
83 #define DOSVM_STUB_PM16 5
84 #define DOSVM_STUB_PM48 6
87 /**********************************************************************
88 * DOSVM_IsIRQ
90 * Return TRUE if interrupt is an IRQ.
92 static BOOL DOSVM_IsIRQ( BYTE intnum )
94 if (intnum >= 0x08 && intnum <= 0x0f)
95 return TRUE;
97 if (intnum >= 0x70 && intnum <= 0x77)
98 return TRUE;
100 return FALSE;
104 /**********************************************************************
105 * DOSVM_DefaultHandler
107 * Default interrupt handler. This will be used to emulate all
108 * interrupts that don't have their own interrupt handler.
110 void WINAPI DOSVM_DefaultHandler( CONTEXT86 *context )
115 /**********************************************************************
116 * DOSVM_GetBuiltinHandler
118 * Return Wine interrupt handler procedure for a given interrupt.
120 static INTPROC DOSVM_GetBuiltinHandler( BYTE intnum )
122 if (intnum < sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) {
123 INTPROC proc = DOSVM_VectorsBuiltin[intnum];
124 if (proc)
125 return proc;
128 WARN("int%x not implemented, returning dummy handler\n", intnum );
130 if (DOSVM_IsIRQ(intnum))
131 return DOSVM_AcknowledgeIRQ;
133 return DOSVM_DefaultHandler;
137 /**********************************************************************
138 * DOSVM_IntProcRelay
140 * Simple DOSRELAY that interprets its argument as INTPROC and calls it.
142 static void DOSVM_IntProcRelay( CONTEXT86 *context, LPVOID data )
144 INTPROC proc = (INTPROC)data;
145 proc(context);
149 /**********************************************************************
150 * DOSVM_PrepareIRQ
153 static void DOSVM_PrepareIRQ( CONTEXT86 *context, BOOL isbuiltin )
155 /* Disable virtual interrupts. */
156 NtCurrentTeb()->dpmi_vif = 0;
158 if (!isbuiltin)
160 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
161 context->SegSs,
162 context->Esp);
164 /* Push return address to stack. */
165 *(--stack) = context->SegCs;
166 *(--stack) = context->Eip;
167 context->Esp += -8;
169 /* Jump to enable interrupts stub. */
170 context->SegCs = DOSVM_dpmi_segments->relay_code_sel;
171 context->Eip = 5;
176 /**********************************************************************
177 * DOSVM_PushFlags
179 * This routine is used to make default int25 and int26 handlers leave the
180 * original eflags into stack. In order to do this, stack is manipulated
181 * so that it actually contains two copies of eflags, one of which is
182 * popped during return from interrupt handler.
184 static void DOSVM_PushFlags( CONTEXT86 *context, BOOL islong, BOOL isstub )
186 if (islong)
188 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
189 context->SegSs,
190 context->Esp);
191 context->Esp += -4; /* One item will be added to stack. */
193 if (isstub)
195 DWORD ip = stack[0];
196 DWORD cs = stack[1];
197 stack += 2; /* Pop ip and cs. */
198 *(--stack) = context->EFlags;
199 *(--stack) = cs;
200 *(--stack) = ip;
202 else
203 *(--stack) = context->EFlags;
205 else
207 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
208 context->SegSs,
209 context->Esp);
210 ADD_LOWORD( context->Esp, -2 ); /* One item will be added to stack. */
212 if (isstub)
214 WORD ip = stack[0];
215 WORD cs = stack[1];
216 stack += 2; /* Pop ip and cs. */
217 *(--stack) = LOWORD(context->EFlags);
218 *(--stack) = cs;
219 *(--stack) = ip;
221 else
222 *(--stack) = LOWORD(context->EFlags);
227 /**********************************************************************
228 * DOSVM_EmulateInterruptPM
230 * Emulate software interrupt in 16-bit or 32-bit protected mode.
231 * Called from signal handler when intXX opcode is executed.
233 * Pushes interrupt frame to stack and changes instruction
234 * pointer to interrupt handler.
236 void WINAPI DOSVM_EmulateInterruptPM( CONTEXT86 *context, BYTE intnum )
238 if (TRACE_ON(relay))
240 DPRINTF( "Call DOS int 0x%02x ret=%04lx:%08lx\n",
241 intnum, context->SegCs, context->Eip );
242 DPRINTF( " eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx\n",
243 context->Eax, context->Ebx, context->Ecx, context->Edx );
244 DPRINTF( " esi=%08lx edi=%08lx ebp=%08lx esp=%08lx \n",
245 context->Esi, context->Edi, context->Ebp, context->Esp );
246 DPRINTF( " ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\n",
247 context->SegDs, context->SegEs,
248 context->SegFs, context->SegGs, context->EFlags );
251 if (context->SegCs == DOSVM_dpmi_segments->dpmi_sel)
253 DOSVM_BuildCallFrame( context,
254 DOSVM_IntProcRelay,
255 DOSVM_RawModeSwitchHandler );
257 else if (context->SegCs == DOSVM_dpmi_segments->relay_code_sel)
260 * This must not be called using DOSVM_BuildCallFrame.
262 DOSVM_RelayHandler( context );
264 else if (context->SegCs == DOSVM_dpmi_segments->int48_sel)
266 if (intnum != context->Eip / DOSVM_STUB_PM48)
267 WARN( "interrupt stub has been modified "
268 "(interrupt is %02x, interrupt stub is %02lx)\n",
269 intnum, context->Eip/DOSVM_STUB_PM48 );
271 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
273 if (intnum == 0x25 || intnum == 0x26)
274 DOSVM_PushFlags( context, TRUE, TRUE );
276 DOSVM_BuildCallFrame( context,
277 DOSVM_IntProcRelay,
278 DOSVM_GetBuiltinHandler(intnum) );
280 else if (context->SegCs == DOSVM_dpmi_segments->int16_sel)
282 if (intnum != context->Eip / DOSVM_STUB_PM16)
283 WARN( "interrupt stub has been modified "
284 "(interrupt is %02x, interrupt stub is %02lx)\n",
285 intnum, context->Eip/DOSVM_STUB_PM16 );
287 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
289 if (intnum == 0x25 || intnum == 0x26)
290 DOSVM_PushFlags( context, FALSE, TRUE );
292 DOSVM_BuildCallFrame( context,
293 DOSVM_IntProcRelay,
294 DOSVM_GetBuiltinHandler(intnum) );
296 else
298 DOSVM_HardwareInterruptPM( context, intnum );
303 /**********************************************************************
304 * DOSVM_HardwareInterruptPM
306 * Emulate call to interrupt handler in 16-bit or 32-bit protected mode.
308 * Pushes interrupt frame to stack and changes instruction
309 * pointer to interrupt handler.
311 void DOSVM_HardwareInterruptPM( CONTEXT86 *context, BYTE intnum )
313 if(DOSVM_IsDos32())
315 FARPROC48 addr = DOSVM_GetPMHandler48( intnum );
317 if (addr.selector == DOSVM_dpmi_segments->int48_sel)
319 TRACE( "builtin interrupt %02lx has been invoked "
320 "(through vector %02x)\n",
321 addr.offset / DOSVM_STUB_PM48, intnum );
323 if (intnum == 0x25 || intnum == 0x26)
324 DOSVM_PushFlags( context, TRUE, FALSE );
325 else if (DOSVM_IsIRQ(intnum))
326 DOSVM_PrepareIRQ( context, TRUE );
328 DOSVM_BuildCallFrame( context,
329 DOSVM_IntProcRelay,
330 DOSVM_GetBuiltinHandler(
331 addr.offset/DOSVM_STUB_PM48 ) );
333 else
335 DWORD *stack;
337 TRACE( "invoking hooked interrupt %02x at %04x:%08lx\n",
338 intnum, addr.selector, addr.offset );
340 if (DOSVM_IsIRQ(intnum))
341 DOSVM_PrepareIRQ( context, FALSE );
343 /* Push the flags and return address on the stack */
344 stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
345 *(--stack) = context->EFlags;
346 *(--stack) = context->SegCs;
347 *(--stack) = context->Eip;
348 context->Esp += -12;
350 /* Jump to the interrupt handler */
351 context->SegCs = addr.selector;
352 context->Eip = addr.offset;
355 else
357 FARPROC16 addr = DOSVM_GetPMHandler16( intnum );
359 if (SELECTOROF(addr) == DOSVM_dpmi_segments->int16_sel)
361 TRACE( "builtin interrupt %02x has been invoked "
362 "(through vector %02x)\n",
363 OFFSETOF(addr)/DOSVM_STUB_PM16, intnum );
365 if (intnum == 0x25 || intnum == 0x26)
366 DOSVM_PushFlags( context, FALSE, FALSE );
367 else if (DOSVM_IsIRQ(intnum))
368 DOSVM_PrepareIRQ( context, TRUE );
370 DOSVM_BuildCallFrame( context,
371 DOSVM_IntProcRelay,
372 DOSVM_GetBuiltinHandler(
373 OFFSETOF(addr)/DOSVM_STUB_PM16 ) );
375 else
377 WORD *stack;
379 TRACE( "invoking hooked interrupt %02x at %04x:%04x\n",
380 intnum, SELECTOROF(addr), OFFSETOF(addr) );
382 if (DOSVM_IsIRQ(intnum))
383 DOSVM_PrepareIRQ( context, FALSE );
385 /* Push the flags and return address on the stack */
386 stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
387 *(--stack) = LOWORD(context->EFlags);
388 *(--stack) = context->SegCs;
389 *(--stack) = LOWORD(context->Eip);
390 ADD_LOWORD( context->Esp, -6 );
392 /* Jump to the interrupt handler */
393 context->SegCs = HIWORD(addr);
394 context->Eip = LOWORD(addr);
400 /**********************************************************************
401 * DOSVM_EmulateInterruptRM
403 * Emulate software interrupt in real mode.
404 * Called from VM86 emulation when intXX opcode is executed.
406 * Either calls directly builtin handler or pushes interrupt frame to
407 * stack and changes instruction pointer to interrupt handler.
409 * Returns FALSE if this interrupt was caused by return
410 * from real mode wrapper.
412 BOOL WINAPI DOSVM_EmulateInterruptRM( CONTEXT86 *context, BYTE intnum )
414 if (TRACE_ON(relay))
416 DPRINTF( "Call DOS int 0x%02x ret=%04lx:%08lx\n",
417 intnum, context->SegCs, context->Eip );
418 DPRINTF( " eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx\n",
419 context->Eax, context->Ebx, context->Ecx, context->Edx );
420 DPRINTF( " esi=%08lx edi=%08lx ebp=%08lx esp=%08lx \n",
421 context->Esi, context->Edi, context->Ebp, context->Esp );
422 DPRINTF( " ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\n",
423 context->SegDs, context->SegEs,
424 context->SegFs, context->SegGs, context->EFlags );
427 /* check for our real-mode hooks */
428 if (intnum == 0x31)
430 /* is this exit from real-mode wrapper */
431 if (context->SegCs == DOSVM_dpmi_segments->wrap_seg)
432 return FALSE;
434 if (DOSVM_CheckWrappers( context ))
435 return TRUE;
438 /* check if the call is from our fake BIOS interrupt stubs */
439 if (context->SegCs==0xf000)
441 if (intnum != context->Eip / DOSVM_STUB_RM)
442 WARN( "interrupt stub has been modified "
443 "(interrupt is %02x, interrupt stub is %02lx)\n",
444 intnum, context->Eip/DOSVM_STUB_RM );
446 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
448 DOSVM_CallBuiltinHandler( context, intnum );
450 else
452 DOSVM_HardwareInterruptRM( context, intnum );
455 return TRUE;
459 /**********************************************************************
460 * DOSVM_HardwareInterruptRM
462 * Emulate call to interrupt handler in real mode.
464 * Either calls directly builtin handler or pushes interrupt frame to
465 * stack and changes instruction pointer to interrupt handler.
467 void DOSVM_HardwareInterruptRM( CONTEXT86 *context, BYTE intnum )
469 FARPROC16 handler = DOSVM_GetRMHandler( intnum );
471 /* check if the call goes to an unhooked interrupt */
472 if (SELECTOROF(handler) == 0xf000)
474 /* if so, call it directly */
475 TRACE( "builtin interrupt %02x has been invoked "
476 "(through vector %02x)\n",
477 OFFSETOF(handler)/DOSVM_STUB_RM, intnum );
478 DOSVM_CallBuiltinHandler( context, OFFSETOF(handler)/DOSVM_STUB_RM );
480 else
482 /* the interrupt is hooked, simulate interrupt in DOS space */
483 WORD* stack = PTR_REAL_TO_LIN( context->SegSs, context->Esp );
484 WORD flag = LOWORD( context->EFlags );
486 TRACE( "invoking hooked interrupt %02x at %04x:%04x\n",
487 intnum, SELECTOROF(handler), OFFSETOF(handler) );
489 /* Copy virtual interrupt flag to pushed interrupt flag. */
490 if (context->EFlags & VIF_MASK)
491 flag |= IF_MASK;
492 else
493 flag &= ~IF_MASK;
495 *(--stack) = flag;
496 *(--stack) = context->SegCs;
497 *(--stack) = LOWORD( context->Eip );
498 context->Esp -= 6;
499 context->SegCs = SELECTOROF( handler );
500 context->Eip = OFFSETOF( handler );
502 /* Clear virtual interrupt flag. */
503 context->EFlags &= ~VIF_MASK;
508 /**********************************************************************
509 * DOSVM_GetRMHandler
511 * Return the real mode interrupt vector for a given interrupt.
513 FARPROC16 DOSVM_GetRMHandler( BYTE intnum )
515 return ((FARPROC16*)0)[intnum];
519 /**********************************************************************
520 * DOSVM_SetRMHandler
522 * Set the real mode interrupt handler for a given interrupt.
524 void DOSVM_SetRMHandler( BYTE intnum, FARPROC16 handler )
526 TRACE("Set real mode interrupt vector %02x <- %04x:%04x\n",
527 intnum, HIWORD(handler), LOWORD(handler) );
528 ((FARPROC16*)0)[intnum] = handler;
532 /**********************************************************************
533 * DOSVM_GetPMHandler16
535 * Return the protected mode interrupt vector for a given interrupt.
537 FARPROC16 DOSVM_GetPMHandler16( BYTE intnum )
539 if (!DOSVM_Vectors16[intnum])
541 FARPROC16 proc = (FARPROC16)MAKESEGPTR( DOSVM_dpmi_segments->int16_sel,
542 DOSVM_STUB_PM16 * intnum );
543 DOSVM_Vectors16[intnum] = proc;
545 return DOSVM_Vectors16[intnum];
549 /**********************************************************************
550 * DOSVM_SetPMHandler16
552 * Set the protected mode interrupt handler for a given interrupt.
554 void DOSVM_SetPMHandler16( BYTE intnum, FARPROC16 handler )
556 TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
557 intnum, HIWORD(handler), LOWORD(handler) );
558 DOSVM_Vectors16[intnum] = handler;
562 /**********************************************************************
563 * DOSVM_GetPMHandler48
565 * Return the protected mode interrupt vector for a given interrupt.
566 * Used to get 48-bit pointer for 32-bit interrupt handlers in DPMI32.
568 FARPROC48 DOSVM_GetPMHandler48( BYTE intnum )
570 if (!DOSVM_Vectors48[intnum].selector)
572 DOSVM_Vectors48[intnum].selector = DOSVM_dpmi_segments->int48_sel;
573 DOSVM_Vectors48[intnum].offset = DOSVM_STUB_PM48 * intnum;
575 return DOSVM_Vectors48[intnum];
579 /**********************************************************************
580 * DOSVM_SetPMHandler48
582 * Set the protected mode interrupt handler for a given interrupt.
583 * Used to set 48-bit pointer for 32-bit interrupt handlers in DPMI32.
585 void DOSVM_SetPMHandler48( BYTE intnum, FARPROC48 handler )
587 TRACE("Set 32-bit protected mode interrupt vector %02x <- %04x:%08lx\n",
588 intnum, handler.selector, handler.offset );
589 DOSVM_Vectors48[intnum] = handler;
593 /**********************************************************************
594 * DOSVM_CallBuiltinHandler
596 * Execute Wine interrupt handler procedure.
598 void WINAPI DOSVM_CallBuiltinHandler( CONTEXT86 *context, BYTE intnum )
601 * FIXME: Make all builtin interrupt calls go via this routine.
602 * FIXME: Check for PM->RM interrupt reflection.
603 * FIXME: Check for RM->PM interrupt reflection.
606 INTPROC proc = DOSVM_GetBuiltinHandler( intnum );
607 proc( context );