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);
40 static FARPROC16 DOSVM_Vectors16[256];
41 static FARPROC48 DOSVM_Vectors48[256];
42 static const INTPROC DOSVM_VectorsBuiltin[] =
46 /* 08 */ 0, DOSVM_Int09Handler, 0, 0,
48 /* 10 */ DOSVM_Int10Handler, DOSVM_Int11Handler, DOSVM_Int12Handler, DOSVM_Int13Handler,
49 /* 14 */ 0, DOSVM_Int15Handler, DOSVM_Int16Handler, DOSVM_Int17Handler,
50 /* 18 */ 0, 0, DOSVM_Int1aHandler, 0,
52 /* 20 */ DOSVM_Int20Handler, DOSVM_Int21Handler, 0, 0,
53 /* 24 */ 0, DOSVM_Int25Handler, DOSVM_Int26Handler, 0,
54 /* 28 */ 0, DOSVM_Int29Handler, DOSVM_Int2aHandler, 0,
55 /* 2C */ 0, 0, 0, DOSVM_Int2fHandler,
56 /* 30 */ 0, DOSVM_Int31Handler, 0, DOSVM_Int33Handler,
57 /* 34 */ DOSVM_Int34Handler, DOSVM_Int35Handler, DOSVM_Int36Handler, DOSVM_Int37Handler,
58 /* 38 */ DOSVM_Int38Handler, DOSVM_Int39Handler, DOSVM_Int3aHandler, DOSVM_Int3bHandler,
59 /* 3C */ DOSVM_Int3cHandler, DOSVM_Int3dHandler, DOSVM_Int3eHandler, 0,
60 /* 40 */ 0, DOSVM_Int41Handler, 0, 0,
62 /* 48 */ 0, 0, 0, DOSVM_Int4bHandler,
67 /* 5C */ DOSVM_Int5cHandler, 0, 0, 0,
69 /* 64 */ 0, 0, 0, DOSVM_Int67Handler
73 /**********************************************************************
74 * DOSVM_DefaultHandler
76 * Default interrupt handler. This will be used to emulate all
77 * interrupts that don't have their own interrupt handler.
79 void WINAPI DOSVM_DefaultHandler( CONTEXT86 *context )
84 /**********************************************************************
85 * DOSVM_GetBuiltinHandler
87 * Return Wine interrupt handler procedure for a given interrupt.
89 static INTPROC DOSVM_GetBuiltinHandler( BYTE intnum )
91 if (intnum < sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) {
92 INTPROC proc = DOSVM_VectorsBuiltin[intnum];
97 WARN("int%x not implemented, returning dummy handler\n", intnum );
98 return DOSVM_DefaultHandler;
102 /**********************************************************************
105 * Simple DOSRELAY that interprets its argument as INTPROC and calls it.
107 static void DOSVM_IntProcRelay( CONTEXT86 *context, LPVOID data )
109 INTPROC proc = (INTPROC)data;
114 /**********************************************************************
117 * This routine is used to make default int25 and int26 handlers leave the
118 * original eflags into stack. In order to do this, stack is manipulated
119 * so that it actually contains two copies of eflags, one of which is
120 * popped during return from interrupt handler.
122 static void DOSVM_PushFlags( CONTEXT86 *context, BOOL islong, BOOL isstub )
126 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
129 context->Esp += -4; /* One item will be added to stack. */
135 stack += 2; /* Pop ip and cs. */
136 *(--stack) = context->EFlags;
141 *(--stack) = context->EFlags;
145 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
148 ADD_LOWORD( context->Esp, -2 ); /* One item will be added to stack. */
154 stack += 2; /* Pop ip and cs. */
155 *(--stack) = LOWORD(context->EFlags);
160 *(--stack) = LOWORD(context->EFlags);
165 /**********************************************************************
166 * DOSVM_EmulateInterruptPM
168 * Emulate software interrupt in 16-bit or 32-bit protected mode.
169 * Called from signal handler when intXX opcode is executed.
171 * Pushes interrupt frame to stack and changes instruction
172 * pointer to interrupt handler.
174 void WINAPI DOSVM_EmulateInterruptPM( CONTEXT86 *context, BYTE intnum )
176 if (context->SegCs == DOSVM_dpmi_segments->dpmi_sel)
178 DOSVM_BuildCallFrame( context,
180 DOSVM_RawModeSwitchHandler );
182 else if (context->SegCs == DOSVM_dpmi_segments->relay_code_sel)
185 * This must not be called using DOSVM_BuildCallFrame.
187 DOSVM_RelayHandler( context );
189 else if (context->SegCs == DOSVM_dpmi_segments->int48_sel)
191 if (intnum == 0x25 || intnum == 0x26)
192 DOSVM_PushFlags( context, TRUE, TRUE );
194 DOSVM_BuildCallFrame( context,
196 DOSVM_GetBuiltinHandler(intnum) );
198 else if (context->SegCs == DOSVM_dpmi_segments->int16_sel)
200 if (intnum == 0x25 || intnum == 0x26)
201 DOSVM_PushFlags( context, FALSE, TRUE );
203 DOSVM_BuildCallFrame( context,
205 DOSVM_GetBuiltinHandler(intnum) );
209 DOSVM_HardwareInterruptPM( context, intnum );
214 /**********************************************************************
215 * DOSVM_HardwareInterruptPM
217 * Emulate call to interrupt handler in 16-bit or 32-bit protected mode.
219 * Pushes interrupt frame to stack and changes instruction
220 * pointer to interrupt handler.
222 void DOSVM_HardwareInterruptPM( CONTEXT86 *context, BYTE intnum )
226 FARPROC48 addr = DOSVM_GetPMHandler48( intnum );
228 if (addr.selector == DOSVM_dpmi_segments->int48_sel)
230 if (intnum == 0x25 || intnum == 0x26)
231 DOSVM_PushFlags( context, TRUE, FALSE );
233 DOSVM_BuildCallFrame( context,
235 DOSVM_GetBuiltinHandler(intnum) );
239 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
243 /* Push the flags and return address on the stack */
244 *(--stack) = context->EFlags;
245 *(--stack) = context->SegCs;
246 *(--stack) = context->Eip;
249 /* Jump to the interrupt handler */
250 context->SegCs = addr.selector;
251 context->Eip = addr.offset;
256 FARPROC16 addr = DOSVM_GetPMHandler16( intnum );
258 if (SELECTOROF(addr) == DOSVM_dpmi_segments->int16_sel)
260 if (intnum == 0x25 || intnum == 0x26)
261 DOSVM_PushFlags( context, FALSE, FALSE );
263 DOSVM_BuildCallFrame( context,
265 DOSVM_GetBuiltinHandler(intnum) );
269 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
273 /* Push the flags and return address on the stack */
274 *(--stack) = LOWORD(context->EFlags);
275 *(--stack) = context->SegCs;
276 *(--stack) = LOWORD(context->Eip);
277 ADD_LOWORD( context->Esp, -6 );
279 /* Jump to the interrupt handler */
280 context->SegCs = HIWORD(addr);
281 context->Eip = LOWORD(addr);
287 /**********************************************************************
288 * DOSVM_EmulateInterruptRM
290 * Emulate software interrupt in real mode.
291 * Called from VM86 emulation when intXX opcode is executed.
293 * Either calls directly builtin handler or pushes interrupt frame to
294 * stack and changes instruction pointer to interrupt handler.
296 * Returns FALSE if this interrupt was caused by return
297 * from real mode wrapper.
299 BOOL WINAPI DOSVM_EmulateInterruptRM( CONTEXT86 *context, BYTE intnum )
301 /* check for our real-mode hooks */
304 /* is this exit from real-mode wrapper */
305 if (context->SegCs == DOSVM_dpmi_segments->wrap_seg)
308 if (DOSVM_CheckWrappers( context ))
312 /* check if the call is from our fake BIOS interrupt stubs */
313 if (context->SegCs==0xf000)
315 if (intnum != (context->Eip/4))
316 TRACE( "something fishy going on here (interrupt stub is %02lx)\n",
319 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
321 DOSVM_CallBuiltinHandler( context, intnum );
325 DOSVM_HardwareInterruptRM( context, intnum );
332 /**********************************************************************
333 * DOSVM_HardwareInterruptRM
335 * Emulate call to interrupt handler in real mode.
337 * Either calls directly builtin handler or pushes interrupt frame to
338 * stack and changes instruction pointer to interrupt handler.
340 void DOSVM_HardwareInterruptRM( CONTEXT86 *context, BYTE intnum )
342 FARPROC16 handler = DOSVM_GetRMHandler( intnum );
344 /* check if the call goes to an unhooked interrupt */
345 if (SELECTOROF(handler) == 0xf000)
347 /* if so, call it directly */
348 TRACE( "builtin interrupt %02x has been invoked (through vector %02x)\n",
349 OFFSETOF(handler)/4, intnum );
350 DOSVM_CallBuiltinHandler( context, OFFSETOF(handler)/4 );
354 /* the interrupt is hooked, simulate interrupt in DOS space */
355 WORD* stack = PTR_REAL_TO_LIN( context->SegSs, context->Esp );
356 WORD flag = LOWORD( context->EFlags );
358 TRACE_(int)( "invoking hooked interrupt %02x at %04x:%04x\n",
359 intnum, SELECTOROF(handler), OFFSETOF(handler) );
361 /* Copy virtual interrupt flag to pushed interrupt flag. */
362 if (context->EFlags & VIF_MASK)
368 *(--stack) = context->SegCs;
369 *(--stack) = LOWORD( context->Eip );
371 context->SegCs = SELECTOROF( handler );
372 context->Eip = OFFSETOF( handler );
374 /* Clear virtual interrupt flag. */
375 context->EFlags &= ~VIF_MASK;
380 /**********************************************************************
383 * Return the real mode interrupt vector for a given interrupt.
385 FARPROC16 DOSVM_GetRMHandler( BYTE intnum )
387 return ((FARPROC16*)0)[intnum];
390 /**********************************************************************
393 * Set the real mode interrupt handler for a given interrupt.
395 void DOSVM_SetRMHandler( BYTE intnum, FARPROC16 handler )
397 TRACE("Set real mode interrupt vector %02x <- %04x:%04x\n",
398 intnum, HIWORD(handler), LOWORD(handler) );
399 ((FARPROC16*)0)[intnum] = handler;
403 /**********************************************************************
404 * DOSVM_GetPMHandler16
406 * Return the protected mode interrupt vector for a given interrupt.
408 FARPROC16 DOSVM_GetPMHandler16( BYTE intnum )
410 if (!DOSVM_Vectors16[intnum])
412 FARPROC16 proc = (FARPROC16)MAKESEGPTR( DOSVM_dpmi_segments->int16_sel,
414 DOSVM_Vectors16[intnum] = proc;
416 return DOSVM_Vectors16[intnum];
420 /**********************************************************************
421 * DOSVM_SetPMHandler16
423 * Set the protected mode interrupt handler for a given interrupt.
425 void DOSVM_SetPMHandler16( BYTE intnum, FARPROC16 handler )
427 TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
428 intnum, HIWORD(handler), LOWORD(handler) );
429 DOSVM_Vectors16[intnum] = handler;
432 /**********************************************************************
433 * DOSVM_GetPMHandler48
435 * Return the protected mode interrupt vector for a given interrupt.
436 * Used to get 48-bit pointer for 32-bit interrupt handlers in DPMI32.
438 FARPROC48 DOSVM_GetPMHandler48( BYTE intnum )
440 if (!DOSVM_Vectors48[intnum].selector)
442 DOSVM_Vectors48[intnum].selector = DOSVM_dpmi_segments->int48_sel;
443 DOSVM_Vectors48[intnum].offset = 6 * intnum;
445 return DOSVM_Vectors48[intnum];
448 /**********************************************************************
449 * DOSVM_SetPMHandler48
451 * Set the protected mode interrupt handler for a given interrupt.
452 * Used to set 48-bit pointer for 32-bit interrupt handlers in DPMI32.
454 void DOSVM_SetPMHandler48( BYTE intnum, FARPROC48 handler )
456 TRACE("Set 32-bit protected mode interrupt vector %02x <- %04x:%08lx\n",
457 intnum, handler.selector, handler.offset );
458 DOSVM_Vectors48[intnum] = handler;
462 /**********************************************************************
463 * DOSVM_CallBuiltinHandler
465 * Execute Wine interrupt handler procedure.
467 void WINAPI DOSVM_CallBuiltinHandler( CONTEXT86 *context, BYTE intnum )
469 INTPROC proc = DOSVM_GetBuiltinHandler( intnum );