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
22 #include "wine/debug.h"
23 #include "wine/winbase16.h"
25 #ifdef HAVE_SYS_VM86_H
26 # include <sys/vm86.h>
30 #define IF_MASK 0x00000200
34 #define VIF_MASK 0x00080000
38 WINE_DEFAULT_DEBUG_CHANNEL(int);
39 WINE_DECLARE_DEBUG_CHANNEL(relay);
42 static FARPROC16 DOSVM_Vectors16[256];
43 static FARPROC48 DOSVM_Vectors48[256];
44 static const INTPROC DOSVM_VectorsBuiltin[] =
48 /* 08 */ 0, DOSVM_Int09Handler, 0, 0,
50 /* 10 */ DOSVM_Int10Handler, DOSVM_Int11Handler, DOSVM_Int12Handler, DOSVM_Int13Handler,
51 /* 14 */ 0, DOSVM_Int15Handler, DOSVM_Int16Handler, DOSVM_Int17Handler,
52 /* 18 */ 0, 0, DOSVM_Int1aHandler, 0,
54 /* 20 */ DOSVM_Int20Handler, DOSVM_Int21Handler, 0, 0,
55 /* 24 */ 0, DOSVM_Int25Handler, DOSVM_Int26Handler, 0,
56 /* 28 */ 0, DOSVM_Int29Handler, DOSVM_Int2aHandler, 0,
57 /* 2C */ 0, 0, 0, DOSVM_Int2fHandler,
58 /* 30 */ 0, DOSVM_Int31Handler, 0, DOSVM_Int33Handler,
59 /* 34 */ DOSVM_Int34Handler, DOSVM_Int35Handler, DOSVM_Int36Handler, DOSVM_Int37Handler,
60 /* 38 */ DOSVM_Int38Handler, DOSVM_Int39Handler, DOSVM_Int3aHandler, DOSVM_Int3bHandler,
61 /* 3C */ DOSVM_Int3cHandler, DOSVM_Int3dHandler, DOSVM_Int3eHandler, 0,
62 /* 40 */ 0, DOSVM_Int41Handler, 0, 0,
64 /* 48 */ 0, 0, 0, DOSVM_Int4bHandler,
69 /* 5C */ DOSVM_Int5cHandler, 0, 0, 0,
71 /* 64 */ 0, 0, 0, DOSVM_Int67Handler
76 * Sizes of real mode and protected mode interrupt stubs.
78 #define DOSVM_STUB_RM 4
79 #define DOSVM_STUB_PM16 5
80 #define DOSVM_STUB_PM48 6
83 /**********************************************************************
84 * DOSVM_DefaultHandler
86 * Default interrupt handler. This will be used to emulate all
87 * interrupts that don't have their own interrupt handler.
89 void WINAPI DOSVM_DefaultHandler( CONTEXT86 *context )
94 /**********************************************************************
95 * DOSVM_GetBuiltinHandler
97 * Return Wine interrupt handler procedure for a given interrupt.
99 static INTPROC DOSVM_GetBuiltinHandler( BYTE intnum )
101 if (intnum < sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) {
102 INTPROC proc = DOSVM_VectorsBuiltin[intnum];
107 WARN("int%x not implemented, returning dummy handler\n", intnum );
108 return DOSVM_DefaultHandler;
112 /**********************************************************************
115 * Simple DOSRELAY that interprets its argument as INTPROC and calls it.
117 static void DOSVM_IntProcRelay( CONTEXT86 *context, LPVOID data )
119 INTPROC proc = (INTPROC)data;
124 /**********************************************************************
127 * This routine is used to make default int25 and int26 handlers leave the
128 * original eflags into stack. In order to do this, stack is manipulated
129 * so that it actually contains two copies of eflags, one of which is
130 * popped during return from interrupt handler.
132 static void DOSVM_PushFlags( CONTEXT86 *context, BOOL islong, BOOL isstub )
136 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
139 context->Esp += -4; /* One item will be added to stack. */
145 stack += 2; /* Pop ip and cs. */
146 *(--stack) = context->EFlags;
151 *(--stack) = context->EFlags;
155 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
158 ADD_LOWORD( context->Esp, -2 ); /* One item will be added to stack. */
164 stack += 2; /* Pop ip and cs. */
165 *(--stack) = LOWORD(context->EFlags);
170 *(--stack) = LOWORD(context->EFlags);
175 /**********************************************************************
176 * DOSVM_EmulateInterruptPM
178 * Emulate software interrupt in 16-bit or 32-bit protected mode.
179 * Called from signal handler when intXX opcode is executed.
181 * Pushes interrupt frame to stack and changes instruction
182 * pointer to interrupt handler.
184 void WINAPI DOSVM_EmulateInterruptPM( CONTEXT86 *context, BYTE intnum )
188 DPRINTF( "Call DOS int 0x%02x ret=%04lx:%08lx\n",
189 intnum, context->SegCs, context->Eip );
190 DPRINTF( " eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx\n",
191 context->Eax, context->Ebx, context->Ecx, context->Edx );
192 DPRINTF( " esi=%08lx edi=%08lx ebp=%08lx esp=%08lx \n",
193 context->Esi, context->Edi, context->Ebp, context->Esp );
194 DPRINTF( " ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\n",
195 context->SegDs, context->SegEs,
196 context->SegFs, context->SegGs, context->EFlags );
199 if (context->SegCs == DOSVM_dpmi_segments->dpmi_sel)
201 DOSVM_BuildCallFrame( context,
203 DOSVM_RawModeSwitchHandler );
205 else if (context->SegCs == DOSVM_dpmi_segments->relay_code_sel)
208 * This must not be called using DOSVM_BuildCallFrame.
210 DOSVM_RelayHandler( context );
212 else if (context->SegCs == DOSVM_dpmi_segments->int48_sel)
214 if (intnum != context->Eip / DOSVM_STUB_PM48)
215 WARN( "interrupt stub has been modified "
216 "(interrupt is %02x, interrupt stub is %02lx)\n",
217 intnum, context->Eip/DOSVM_STUB_PM48 );
219 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
221 if (intnum == 0x25 || intnum == 0x26)
222 DOSVM_PushFlags( context, TRUE, TRUE );
224 DOSVM_BuildCallFrame( context,
226 DOSVM_GetBuiltinHandler(intnum) );
228 else if (context->SegCs == DOSVM_dpmi_segments->int16_sel)
230 if (intnum != context->Eip / DOSVM_STUB_PM16)
231 WARN( "interrupt stub has been modified "
232 "(interrupt is %02x, interrupt stub is %02lx)\n",
233 intnum, context->Eip/DOSVM_STUB_PM16 );
235 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
237 if (intnum == 0x25 || intnum == 0x26)
238 DOSVM_PushFlags( context, FALSE, TRUE );
240 DOSVM_BuildCallFrame( context,
242 DOSVM_GetBuiltinHandler(intnum) );
246 DOSVM_HardwareInterruptPM( context, intnum );
251 /**********************************************************************
252 * DOSVM_HardwareInterruptPM
254 * Emulate call to interrupt handler in 16-bit or 32-bit protected mode.
256 * Pushes interrupt frame to stack and changes instruction
257 * pointer to interrupt handler.
259 void DOSVM_HardwareInterruptPM( CONTEXT86 *context, BYTE intnum )
263 FARPROC48 addr = DOSVM_GetPMHandler48( intnum );
265 if (addr.selector == DOSVM_dpmi_segments->int48_sel)
267 TRACE( "builtin interrupt %02lx has been invoked "
268 "(through vector %02x)\n",
269 addr.offset / DOSVM_STUB_PM48, intnum );
271 if (intnum == 0x25 || intnum == 0x26)
272 DOSVM_PushFlags( context, TRUE, FALSE );
274 DOSVM_BuildCallFrame( context,
276 DOSVM_GetBuiltinHandler(
277 addr.offset/DOSVM_STUB_PM48 ) );
281 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
285 TRACE( "invoking hooked interrupt %02x at %04x:%08lx\n",
286 intnum, addr.selector, addr.offset );
288 /* Push the flags and return address on the stack */
289 *(--stack) = context->EFlags;
290 *(--stack) = context->SegCs;
291 *(--stack) = context->Eip;
294 /* Jump to the interrupt handler */
295 context->SegCs = addr.selector;
296 context->Eip = addr.offset;
301 FARPROC16 addr = DOSVM_GetPMHandler16( intnum );
303 if (SELECTOROF(addr) == DOSVM_dpmi_segments->int16_sel)
305 TRACE( "builtin interrupt %02x has been invoked "
306 "(through vector %02x)\n",
307 OFFSETOF(addr)/DOSVM_STUB_PM16, intnum );
309 if (intnum == 0x25 || intnum == 0x26)
310 DOSVM_PushFlags( context, FALSE, FALSE );
312 DOSVM_BuildCallFrame( context,
314 DOSVM_GetBuiltinHandler(
315 OFFSETOF(addr)/DOSVM_STUB_PM16 ) );
319 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
323 TRACE( "invoking hooked interrupt %02x at %04x:%04x\n",
324 intnum, SELECTOROF(addr), OFFSETOF(addr) );
326 /* Push the flags and return address on the stack */
327 *(--stack) = LOWORD(context->EFlags);
328 *(--stack) = context->SegCs;
329 *(--stack) = LOWORD(context->Eip);
330 ADD_LOWORD( context->Esp, -6 );
332 /* Jump to the interrupt handler */
333 context->SegCs = HIWORD(addr);
334 context->Eip = LOWORD(addr);
340 /**********************************************************************
341 * DOSVM_EmulateInterruptRM
343 * Emulate software interrupt in real mode.
344 * Called from VM86 emulation when intXX opcode is executed.
346 * Either calls directly builtin handler or pushes interrupt frame to
347 * stack and changes instruction pointer to interrupt handler.
349 * Returns FALSE if this interrupt was caused by return
350 * from real mode wrapper.
352 BOOL WINAPI DOSVM_EmulateInterruptRM( CONTEXT86 *context, BYTE intnum )
356 DPRINTF( "Call DOS int 0x%02x ret=%04lx:%08lx\n",
357 intnum, context->SegCs, context->Eip );
358 DPRINTF( " eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx\n",
359 context->Eax, context->Ebx, context->Ecx, context->Edx );
360 DPRINTF( " esi=%08lx edi=%08lx ebp=%08lx esp=%08lx \n",
361 context->Esi, context->Edi, context->Ebp, context->Esp );
362 DPRINTF( " ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\n",
363 context->SegDs, context->SegEs,
364 context->SegFs, context->SegGs, context->EFlags );
367 /* check for our real-mode hooks */
370 /* is this exit from real-mode wrapper */
371 if (context->SegCs == DOSVM_dpmi_segments->wrap_seg)
374 if (DOSVM_CheckWrappers( context ))
378 /* check if the call is from our fake BIOS interrupt stubs */
379 if (context->SegCs==0xf000)
381 if (intnum != context->Eip / DOSVM_STUB_RM)
382 WARN( "interrupt stub has been modified "
383 "(interrupt is %02x, interrupt stub is %02lx)\n",
384 intnum, context->Eip/DOSVM_STUB_RM );
386 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
388 DOSVM_CallBuiltinHandler( context, intnum );
392 DOSVM_HardwareInterruptRM( context, intnum );
399 /**********************************************************************
400 * DOSVM_HardwareInterruptRM
402 * Emulate call to interrupt handler in real mode.
404 * Either calls directly builtin handler or pushes interrupt frame to
405 * stack and changes instruction pointer to interrupt handler.
407 void DOSVM_HardwareInterruptRM( CONTEXT86 *context, BYTE intnum )
409 FARPROC16 handler = DOSVM_GetRMHandler( intnum );
411 /* check if the call goes to an unhooked interrupt */
412 if (SELECTOROF(handler) == 0xf000)
414 /* if so, call it directly */
415 TRACE( "builtin interrupt %02x has been invoked "
416 "(through vector %02x)\n",
417 OFFSETOF(handler)/DOSVM_STUB_RM, intnum );
418 DOSVM_CallBuiltinHandler( context, OFFSETOF(handler)/DOSVM_STUB_RM );
422 /* the interrupt is hooked, simulate interrupt in DOS space */
423 WORD* stack = PTR_REAL_TO_LIN( context->SegSs, context->Esp );
424 WORD flag = LOWORD( context->EFlags );
426 TRACE( "invoking hooked interrupt %02x at %04x:%04x\n",
427 intnum, SELECTOROF(handler), OFFSETOF(handler) );
429 /* Copy virtual interrupt flag to pushed interrupt flag. */
430 if (context->EFlags & VIF_MASK)
436 *(--stack) = context->SegCs;
437 *(--stack) = LOWORD( context->Eip );
439 context->SegCs = SELECTOROF( handler );
440 context->Eip = OFFSETOF( handler );
442 /* Clear virtual interrupt flag. */
443 context->EFlags &= ~VIF_MASK;
448 /**********************************************************************
451 * Return the real mode interrupt vector for a given interrupt.
453 FARPROC16 DOSVM_GetRMHandler( BYTE intnum )
455 return ((FARPROC16*)0)[intnum];
459 /**********************************************************************
462 * Set the real mode interrupt handler for a given interrupt.
464 void DOSVM_SetRMHandler( BYTE intnum, FARPROC16 handler )
466 TRACE("Set real mode interrupt vector %02x <- %04x:%04x\n",
467 intnum, HIWORD(handler), LOWORD(handler) );
468 ((FARPROC16*)0)[intnum] = handler;
472 /**********************************************************************
473 * DOSVM_GetPMHandler16
475 * Return the protected mode interrupt vector for a given interrupt.
477 FARPROC16 DOSVM_GetPMHandler16( BYTE intnum )
479 if (!DOSVM_Vectors16[intnum])
481 FARPROC16 proc = (FARPROC16)MAKESEGPTR( DOSVM_dpmi_segments->int16_sel,
482 DOSVM_STUB_PM16 * intnum );
483 DOSVM_Vectors16[intnum] = proc;
485 return DOSVM_Vectors16[intnum];
489 /**********************************************************************
490 * DOSVM_SetPMHandler16
492 * Set the protected mode interrupt handler for a given interrupt.
494 void DOSVM_SetPMHandler16( BYTE intnum, FARPROC16 handler )
496 TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
497 intnum, HIWORD(handler), LOWORD(handler) );
498 DOSVM_Vectors16[intnum] = handler;
502 /**********************************************************************
503 * DOSVM_GetPMHandler48
505 * Return the protected mode interrupt vector for a given interrupt.
506 * Used to get 48-bit pointer for 32-bit interrupt handlers in DPMI32.
508 FARPROC48 DOSVM_GetPMHandler48( BYTE intnum )
510 if (!DOSVM_Vectors48[intnum].selector)
512 DOSVM_Vectors48[intnum].selector = DOSVM_dpmi_segments->int48_sel;
513 DOSVM_Vectors48[intnum].offset = DOSVM_STUB_PM48 * intnum;
515 return DOSVM_Vectors48[intnum];
519 /**********************************************************************
520 * DOSVM_SetPMHandler48
522 * Set the protected mode interrupt handler for a given interrupt.
523 * Used to set 48-bit pointer for 32-bit interrupt handlers in DPMI32.
525 void DOSVM_SetPMHandler48( BYTE intnum, FARPROC48 handler )
527 TRACE("Set 32-bit protected mode interrupt vector %02x <- %04x:%08lx\n",
528 intnum, handler.selector, handler.offset );
529 DOSVM_Vectors48[intnum] = handler;
533 /**********************************************************************
534 * DOSVM_CallBuiltinHandler
536 * Execute Wine interrupt handler procedure.
538 void WINAPI DOSVM_CallBuiltinHandler( CONTEXT86 *context, BYTE intnum )
540 INTPROC proc = DOSVM_GetBuiltinHandler( intnum );