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
24 #include "wine/debug.h"
25 #include "wine/winbase16.h"
29 #ifdef HAVE_SYS_VM86_H
30 # include <sys/vm86.h>
34 #define IF_MASK 0x00000200
38 #define VIF_MASK 0x00080000
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
[] =
52 /* 08 */ DOSVM_Int08Handler
, DOSVM_Int09Handler
, 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,
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,
68 /* 48 */ 0, 0, 0, DOSVM_Int4bHandler
,
73 /* 5C */ DOSVM_Int5cHandler
, 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 /**********************************************************************
90 * Return TRUE if interrupt is an IRQ.
92 static BOOL
DOSVM_IsIRQ( BYTE intnum
)
94 if (intnum
>= 0x08 && intnum
<= 0x0f)
97 if (intnum
>= 0x70 && intnum
<= 0x77)
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
];
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 /**********************************************************************
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
;
149 /**********************************************************************
153 static void DOSVM_PrepareIRQ( CONTEXT86
*context
, BOOL isbuiltin
)
155 /* Disable virtual interrupts. */
156 NtCurrentTeb()->dpmi_vif
= 0;
160 DWORD
*stack
= CTX_SEG_OFF_TO_LIN(context
,
164 /* Push return address to stack. */
165 *(--stack
) = context
->SegCs
;
166 *(--stack
) = context
->Eip
;
169 /* Jump to enable interrupts stub. */
170 context
->SegCs
= DOSVM_dpmi_segments
->relay_code_sel
;
176 /**********************************************************************
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
)
188 DWORD
*stack
= CTX_SEG_OFF_TO_LIN(context
,
191 context
->Esp
+= -4; /* One item will be added to stack. */
197 stack
+= 2; /* Pop ip and cs. */
198 *(--stack
) = context
->EFlags
;
203 *(--stack
) = context
->EFlags
;
207 WORD
*stack
= CTX_SEG_OFF_TO_LIN(context
,
210 ADD_LOWORD( context
->Esp
, -2 ); /* One item will be added to stack. */
216 stack
+= 2; /* Pop ip and cs. */
217 *(--stack
) = LOWORD(context
->EFlags
);
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
)
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
,
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
,
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
,
294 DOSVM_GetBuiltinHandler(intnum
) );
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
)
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
,
330 DOSVM_GetBuiltinHandler(
331 addr
.offset
/DOSVM_STUB_PM48
) );
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
;
350 /* Jump to the interrupt handler */
351 context
->SegCs
= addr
.selector
;
352 context
->Eip
= addr
.offset
;
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
,
372 DOSVM_GetBuiltinHandler(
373 OFFSETOF(addr
)/DOSVM_STUB_PM16
) );
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
)
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 */
430 /* is this exit from real-mode wrapper */
431 if (context
->SegCs
== DOSVM_dpmi_segments
->wrap_seg
)
434 if (DOSVM_CheckWrappers( context
))
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
);
452 DOSVM_HardwareInterruptRM( context
, intnum
);
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
);
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
)
496 *(--stack
) = context
->SegCs
;
497 *(--stack
) = LOWORD( context
->Eip
);
499 context
->SegCs
= SELECTOROF( handler
);
500 context
->Eip
= OFFSETOF( handler
);
502 /* Clear virtual interrupt flag. */
503 context
->EFlags
&= ~VIF_MASK
;
508 /**********************************************************************
511 * Return the real mode interrupt vector for a given interrupt.
513 FARPROC16
DOSVM_GetRMHandler( BYTE intnum
)
515 return ((FARPROC16
*)0)[intnum
];
519 /**********************************************************************
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
);