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 WINE_DEFAULT_DEBUG_CHANNEL(int);
30 WINE_DECLARE_DEBUG_CHANNEL(relay
);
33 static FARPROC16 DOSVM_Vectors16
[256];
34 static FARPROC48 DOSVM_Vectors48
[256];
35 static const INTPROC DOSVM_VectorsBuiltin
[] =
39 /* 08 */ DOSVM_Int08Handler
, DOSVM_Int09Handler
, 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,
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,
55 /* 48 */ 0, 0, 0, DOSVM_Int4bHandler
,
60 /* 5C */ DOSVM_Int5cHandler
, 0, 0, 0,
62 /* 64 */ 0, 0, 0, DOSVM_Int67Handler
67 * Sizes of real mode and protected mode interrupt stubs.
69 #define DOSVM_STUB_RM 4
70 #define DOSVM_STUB_PM16 5
71 #define DOSVM_STUB_PM48 6
74 /**********************************************************************
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.
82 static FARPROC16
* DOSVM_GetRMVector( BYTE intnum
)
87 proc
= GetProcAddress16( GetModuleHandle16( "KERNEL" ),
88 (LPCSTR
)(ULONG_PTR
)183 );
89 wine_ldt_get_entry( LOWORD(proc
), &entry
);
91 return (FARPROC16
*)wine_ldt_get_base( &entry
) + intnum
;
95 /**********************************************************************
98 * Return TRUE if interrupt is an IRQ.
100 static BOOL
DOSVM_IsIRQ( BYTE intnum
)
102 if (intnum
>= 0x08 && intnum
<= 0x0f)
105 if (intnum
>= 0x70 && intnum
<= 0x77)
112 /**********************************************************************
113 * DOSVM_DefaultHandler
115 * Default interrupt handler. This will be used to emulate all
116 * interrupts that don't have their own interrupt handler.
118 void WINAPI
DOSVM_DefaultHandler( CONTEXT86
*context
)
123 /**********************************************************************
124 * DOSVM_GetBuiltinHandler
126 * Return Wine interrupt handler procedure for a given interrupt.
128 static INTPROC
DOSVM_GetBuiltinHandler( BYTE intnum
)
130 if (intnum
< sizeof(DOSVM_VectorsBuiltin
)/sizeof(INTPROC
)) {
131 INTPROC proc
= DOSVM_VectorsBuiltin
[intnum
];
136 WARN("int%x not implemented, returning dummy handler\n", intnum
);
138 if (DOSVM_IsIRQ(intnum
))
139 return DOSVM_AcknowledgeIRQ
;
141 return DOSVM_DefaultHandler
;
145 /**********************************************************************
148 * Simple DOSRELAY that interprets its argument as INTPROC and calls it.
150 static void DOSVM_IntProcRelay( CONTEXT86
*context
, LPVOID data
)
152 INTPROC proc
= (INTPROC
)data
;
157 /**********************************************************************
161 static void DOSVM_PrepareIRQ( CONTEXT86
*context
, BOOL isbuiltin
)
163 /* Disable virtual interrupts. */
164 NtCurrentTeb()->dpmi_vif
= 0;
168 DWORD
*stack
= CTX_SEG_OFF_TO_LIN(context
,
172 /* Push return address to stack. */
173 *(--stack
) = context
->SegCs
;
174 *(--stack
) = context
->Eip
;
177 /* Jump to enable interrupts stub. */
178 context
->SegCs
= DOSVM_dpmi_segments
->relay_code_sel
;
184 /**********************************************************************
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.
192 static void DOSVM_PushFlags( CONTEXT86
*context
, BOOL islong
, BOOL isstub
)
196 DWORD
*stack
= CTX_SEG_OFF_TO_LIN(context
,
199 context
->Esp
+= -4; /* One item will be added to stack. */
205 stack
+= 2; /* Pop ip and cs. */
206 *(--stack
) = context
->EFlags
;
211 *(--stack
) = context
->EFlags
;
215 WORD
*stack
= CTX_SEG_OFF_TO_LIN(context
,
218 ADD_LOWORD( context
->Esp
, -2 ); /* One item will be added to stack. */
224 stack
+= 2; /* Pop ip and cs. */
225 *(--stack
) = LOWORD(context
->EFlags
);
230 *(--stack
) = LOWORD(context
->EFlags
);
235 /**********************************************************************
236 * DOSVM_EmulateInterruptPM
238 * Emulate software interrupt in 16-bit or 32-bit protected mode.
239 * Called from signal handler when intXX opcode is executed.
241 * Pushes interrupt frame to stack and changes instruction
242 * pointer to interrupt handler.
244 void WINAPI
DOSVM_EmulateInterruptPM( CONTEXT86
*context
, BYTE intnum
)
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
);
259 if (context
->SegCs
== DOSVM_dpmi_segments
->dpmi_sel
)
261 DOSVM_BuildCallFrame( context
,
263 DOSVM_RawModeSwitchHandler
);
265 else if (context
->SegCs
== DOSVM_dpmi_segments
->relay_code_sel
)
268 * This must not be called using DOSVM_BuildCallFrame.
270 DOSVM_RelayHandler( context
);
272 else if (context
->SegCs
== DOSVM_dpmi_segments
->int48_sel
)
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];
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
);
284 TRACE( "builtin interrupt %02x has been branched to\n", intnum
);
286 if (intnum
== 0x25 || intnum
== 0x26)
287 DOSVM_PushFlags( context
, TRUE
, TRUE
);
289 DOSVM_BuildCallFrame( context
,
291 DOSVM_GetBuiltinHandler(intnum
) );
293 else if (context
->SegCs
== DOSVM_dpmi_segments
->int16_sel
)
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
) );
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
);
305 TRACE( "builtin interrupt %02x has been branched to\n", intnum
);
307 if (intnum
== 0x25 || intnum
== 0x26)
308 DOSVM_PushFlags( context
, FALSE
, TRUE
);
310 DOSVM_BuildCallFrame( context
,
312 DOSVM_GetBuiltinHandler(intnum
) );
316 DOSVM_HardwareInterruptPM( context
, intnum
);
321 /**********************************************************************
322 * DOSVM_HardwareInterruptPM
324 * Emulate call to interrupt handler in 16-bit or 32-bit protected mode.
326 * Pushes interrupt frame to stack and changes instruction
327 * pointer to interrupt handler.
329 void DOSVM_HardwareInterruptPM( CONTEXT86
*context
, BYTE intnum
)
333 FARPROC48 addr
= DOSVM_GetPMHandler48( intnum
);
335 if (addr
.selector
== DOSVM_dpmi_segments
->int48_sel
)
337 TRACE( "builtin interrupt %02lx has been invoked "
338 "(through vector %02x)\n",
339 addr
.offset
/ DOSVM_STUB_PM48
, intnum
);
341 if (intnum
== 0x25 || intnum
== 0x26)
342 DOSVM_PushFlags( context
, TRUE
, FALSE
);
343 else if (DOSVM_IsIRQ(intnum
))
344 DOSVM_PrepareIRQ( context
, TRUE
);
346 DOSVM_BuildCallFrame( context
,
348 DOSVM_GetBuiltinHandler(
349 addr
.offset
/DOSVM_STUB_PM48
) );
355 TRACE( "invoking hooked interrupt %02x at %04x:%08lx\n",
356 intnum
, addr
.selector
, addr
.offset
);
358 if (DOSVM_IsIRQ(intnum
))
359 DOSVM_PrepareIRQ( context
, FALSE
);
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
;
368 /* Jump to the interrupt handler */
369 context
->SegCs
= addr
.selector
;
370 context
->Eip
= addr
.offset
;
375 FARPROC16 addr
= DOSVM_GetPMHandler16( intnum
);
377 if (SELECTOROF(addr
) == DOSVM_dpmi_segments
->int16_sel
)
379 TRACE( "builtin interrupt %02x has been invoked "
380 "(through vector %02x)\n",
381 OFFSETOF(addr
)/DOSVM_STUB_PM16
, intnum
);
383 if (intnum
== 0x25 || intnum
== 0x26)
384 DOSVM_PushFlags( context
, FALSE
, FALSE
);
385 else if (DOSVM_IsIRQ(intnum
))
386 DOSVM_PrepareIRQ( context
, TRUE
);
388 DOSVM_BuildCallFrame( context
,
390 DOSVM_GetBuiltinHandler(
391 OFFSETOF(addr
)/DOSVM_STUB_PM16
) );
397 TRACE( "invoking hooked interrupt %02x at %04x:%04x\n",
398 intnum
, SELECTOROF(addr
), OFFSETOF(addr
) );
400 if (DOSVM_IsIRQ(intnum
))
401 DOSVM_PrepareIRQ( context
, FALSE
);
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 );
410 /* Jump to the interrupt handler */
411 context
->SegCs
= HIWORD(addr
);
412 context
->Eip
= LOWORD(addr
);
418 /**********************************************************************
419 * DOSVM_EmulateInterruptRM
421 * Emulate software interrupt in real mode.
422 * Called from VM86 emulation when intXX opcode is executed.
424 * Either calls directly builtin handler or pushes interrupt frame to
425 * stack and changes instruction pointer to interrupt handler.
427 * Returns FALSE if this interrupt was caused by return
428 * from real mode wrapper.
430 BOOL WINAPI
DOSVM_EmulateInterruptRM( CONTEXT86
*context
, BYTE intnum
)
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
);
445 /* check for our real-mode hooks */
448 /* is this exit from real-mode wrapper */
449 if (context
->SegCs
== DOSVM_dpmi_segments
->wrap_seg
)
452 if (DOSVM_CheckWrappers( context
))
456 /* check if the call is from our fake BIOS interrupt stubs */
457 if (context
->SegCs
==0xf000)
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
) );
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
);
469 TRACE( "builtin interrupt %02x has been branched to\n", intnum
);
471 DOSVM_CallBuiltinHandler( context
, intnum
);
473 /* Real mode stubs use IRET so we must put flags back into stack. */
474 stack
[2] = LOWORD(context
->EFlags
);
478 DOSVM_HardwareInterruptRM( context
, intnum
);
485 /**********************************************************************
486 * DOSVM_HardwareInterruptRM
488 * Emulate call to interrupt handler in real mode.
490 * Either calls directly builtin handler or pushes interrupt frame to
491 * stack and changes instruction pointer to interrupt handler.
493 void DOSVM_HardwareInterruptRM( CONTEXT86
*context
, BYTE intnum
)
495 FARPROC16 handler
= DOSVM_GetRMHandler( intnum
);
497 /* check if the call goes to an unhooked interrupt */
498 if (SELECTOROF(handler
) == 0xf000)
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
);
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
);
512 TRACE( "invoking hooked interrupt %02x at %04x:%04x\n",
513 intnum
, SELECTOROF(handler
), OFFSETOF(handler
) );
515 /* Copy virtual interrupt flag to pushed interrupt flag. */
516 if (context
->EFlags
& VIF_MASK
)
522 *(--stack
) = context
->SegCs
;
523 *(--stack
) = LOWORD( context
->Eip
);
525 context
->SegCs
= SELECTOROF( handler
);
526 context
->Eip
= OFFSETOF( handler
);
528 /* Clear virtual interrupt flag. */
529 context
->EFlags
&= ~VIF_MASK
;
534 /**********************************************************************
537 * Return the real mode interrupt vector for a given interrupt.
539 FARPROC16
DOSVM_GetRMHandler( BYTE intnum
)
541 return *DOSVM_GetRMVector( intnum
);
545 /**********************************************************************
548 * Set the real mode interrupt handler for a given interrupt.
550 void DOSVM_SetRMHandler( BYTE intnum
, FARPROC16 handler
)
552 TRACE("Set real mode interrupt vector %02x <- %04x:%04x\n",
553 intnum
, HIWORD(handler
), LOWORD(handler
) );
554 *DOSVM_GetRMVector( intnum
) = handler
;
558 /**********************************************************************
559 * DOSVM_GetPMHandler16
561 * Return the protected mode interrupt vector for a given interrupt.
563 FARPROC16
DOSVM_GetPMHandler16( BYTE intnum
)
568 pTask
= GlobalLock16(GetCurrentTask());
598 if (!DOSVM_Vectors16
[intnum
])
600 proc
= (FARPROC16
)MAKESEGPTR( DOSVM_dpmi_segments
->int16_sel
,
601 DOSVM_STUB_PM16
* intnum
);
602 DOSVM_Vectors16
[intnum
] = proc
;
604 return DOSVM_Vectors16
[intnum
];
608 /**********************************************************************
609 * DOSVM_SetPMHandler16
611 * Set the protected mode interrupt handler for a given interrupt.
613 void DOSVM_SetPMHandler16( BYTE intnum
, FARPROC16 handler
)
617 TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
618 intnum
, HIWORD(handler
), LOWORD(handler
) );
620 pTask
= GlobalLock16(GetCurrentTask());
626 pTask
->int0
= handler
;
629 pTask
->int2
= handler
;
632 pTask
->int4
= handler
;
635 pTask
->int6
= handler
;
638 pTask
->int7
= handler
;
641 pTask
->int3e
= handler
;
644 pTask
->int75
= handler
;
647 DOSVM_Vectors16
[intnum
] = handler
;
653 /**********************************************************************
654 * DOSVM_GetPMHandler48
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.
659 FARPROC48
DOSVM_GetPMHandler48( BYTE intnum
)
661 if (!DOSVM_Vectors48
[intnum
].selector
)
663 DOSVM_Vectors48
[intnum
].selector
= DOSVM_dpmi_segments
->int48_sel
;
664 DOSVM_Vectors48
[intnum
].offset
= DOSVM_STUB_PM48
* intnum
;
666 return DOSVM_Vectors48
[intnum
];
670 /**********************************************************************
671 * DOSVM_SetPMHandler48
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.
676 void DOSVM_SetPMHandler48( BYTE intnum
, FARPROC48 handler
)
678 TRACE("Set 32-bit protected mode interrupt vector %02x <- %04x:%08lx\n",
679 intnum
, handler
.selector
, handler
.offset
);
680 DOSVM_Vectors48
[intnum
] = handler
;
684 /**********************************************************************
685 * DOSVM_CallBuiltinHandler
687 * Execute Wine interrupt handler procedure.
689 void WINAPI
DOSVM_CallBuiltinHandler( CONTEXT86
*context
, BYTE intnum
)
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.
697 INTPROC proc
= DOSVM_GetBuiltinHandler( intnum
);