Better defaults for heap and stack sizes.
[wine/wine64.git] / dlls / winedos / interrupts.c
blobbddbeda7d78921854fe3e9c9a8a67f5c6ad3cf57
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"
28 #include "task.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(int);
31 WINE_DECLARE_DEBUG_CHANNEL(relay);
34 static FARPROC16 DOSVM_Vectors16[256];
35 static FARPROC48 DOSVM_Vectors48[256];
36 static const INTPROC DOSVM_VectorsBuiltin[] =
38 /* 00 */ 0, 0, 0, 0,
39 /* 04 */ 0, 0, 0, 0,
40 /* 08 */ DOSVM_Int08Handler, DOSVM_Int09Handler, 0, 0,
41 /* 0C */ 0, 0, 0, 0,
42 /* 10 */ DOSVM_Int10Handler, DOSVM_Int11Handler, DOSVM_Int12Handler, DOSVM_Int13Handler,
43 /* 14 */ 0, DOSVM_Int15Handler, DOSVM_Int16Handler, DOSVM_Int17Handler,
44 /* 18 */ 0, DOSVM_Int19Handler, DOSVM_Int1aHandler, 0,
45 /* 1C */ 0, 0, 0, 0,
46 /* 20 */ DOSVM_Int20Handler, DOSVM_Int21Handler, 0, 0,
47 /* 24 */ 0, DOSVM_Int25Handler, DOSVM_Int26Handler, 0,
48 /* 28 */ 0, DOSVM_Int29Handler, DOSVM_Int2aHandler, 0,
49 /* 2C */ 0, 0, 0, DOSVM_Int2fHandler,
50 /* 30 */ 0, DOSVM_Int31Handler, 0, DOSVM_Int33Handler,
51 /* 34 */ DOSVM_Int34Handler, DOSVM_Int35Handler, DOSVM_Int36Handler, DOSVM_Int37Handler,
52 /* 38 */ DOSVM_Int38Handler, DOSVM_Int39Handler, DOSVM_Int3aHandler, DOSVM_Int3bHandler,
53 /* 3C */ DOSVM_Int3cHandler, DOSVM_Int3dHandler, DOSVM_Int3eHandler, 0,
54 /* 40 */ 0, DOSVM_Int41Handler, 0, 0,
55 /* 44 */ 0, 0, 0, 0,
56 /* 48 */ 0, 0, 0, DOSVM_Int4bHandler,
57 /* 4C */ 0, 0, 0, 0,
58 /* 50 */ 0, 0, 0, 0,
59 /* 54 */ 0, 0, 0, 0,
60 /* 58 */ 0, 0, 0, 0,
61 /* 5C */ DOSVM_Int5cHandler, 0, 0, 0,
62 /* 60 */ 0, 0, 0, 0,
63 /* 64 */ 0, 0, 0, DOSVM_Int67Handler
68 * Sizes of real mode and protected mode interrupt stubs.
70 #define DOSVM_STUB_RM 4
71 #define DOSVM_STUB_PM16 5
72 #define DOSVM_STUB_PM48 6
75 /**********************************************************************
76 * DOSVM_GetRMVector
78 * Return pointer to real mode interrupt vector. These are not at fixed
79 * location because those Win16 programs that do not use any real mode
80 * code have protected NULL pointer catching block at low linear memory
81 * and interrupt vectors have been moved to another location.
83 static FARPROC16* DOSVM_GetRMVector( BYTE intnum )
85 LDT_ENTRY entry;
86 FARPROC16 proc;
88 proc = GetProcAddress16( GetModuleHandle16( "KERNEL" ),
89 (LPCSTR)(ULONG_PTR)183 );
90 wine_ldt_get_entry( LOWORD(proc), &entry );
92 return (FARPROC16*)wine_ldt_get_base( &entry ) + intnum;
96 /**********************************************************************
97 * DOSVM_IsIRQ
99 * Return TRUE if interrupt is an IRQ.
101 static BOOL DOSVM_IsIRQ( BYTE intnum )
103 if (intnum >= 0x08 && intnum <= 0x0f)
104 return TRUE;
106 if (intnum >= 0x70 && intnum <= 0x77)
107 return TRUE;
109 return FALSE;
113 /**********************************************************************
114 * DOSVM_DefaultHandler
116 * Default interrupt handler. This will be used to emulate all
117 * interrupts that don't have their own interrupt handler.
119 void WINAPI DOSVM_DefaultHandler( CONTEXT86 *context )
124 /**********************************************************************
125 * DOSVM_GetBuiltinHandler
127 * Return Wine interrupt handler procedure for a given interrupt.
129 static INTPROC DOSVM_GetBuiltinHandler( BYTE intnum )
131 if (intnum < sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) {
132 INTPROC proc = DOSVM_VectorsBuiltin[intnum];
133 if (proc)
134 return proc;
137 WARN("int%x not implemented, returning dummy handler\n", intnum );
139 if (DOSVM_IsIRQ(intnum))
140 return DOSVM_AcknowledgeIRQ;
142 return DOSVM_DefaultHandler;
146 /**********************************************************************
147 * DOSVM_IntProcRelay
149 * Simple DOSRELAY that interprets its argument as INTPROC and calls it.
151 static void DOSVM_IntProcRelay( CONTEXT86 *context, LPVOID data )
153 INTPROC proc = (INTPROC)data;
154 proc(context);
158 /**********************************************************************
159 * DOSVM_PrepareIRQ
162 static void DOSVM_PrepareIRQ( CONTEXT86 *context, BOOL isbuiltin )
164 /* Disable virtual interrupts. */
165 NtCurrentTeb()->dpmi_vif = 0;
167 if (!isbuiltin)
169 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
170 context->SegSs,
171 context->Esp);
173 /* Push return address to stack. */
174 *(--stack) = context->SegCs;
175 *(--stack) = context->Eip;
176 context->Esp += -8;
178 /* Jump to enable interrupts stub. */
179 context->SegCs = DOSVM_dpmi_segments->relay_code_sel;
180 context->Eip = 5;
185 /**********************************************************************
186 * DOSVM_PushFlags
188 * This routine is used to make default int25 and int26 handlers leave the
189 * original eflags into stack. In order to do this, stack is manipulated
190 * so that it actually contains two copies of eflags, one of which is
191 * popped during return from interrupt handler.
193 static void DOSVM_PushFlags( CONTEXT86 *context, BOOL islong, BOOL isstub )
195 if (islong)
197 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
198 context->SegSs,
199 context->Esp);
200 context->Esp += -4; /* One item will be added to stack. */
202 if (isstub)
204 DWORD ip = stack[0];
205 DWORD cs = stack[1];
206 stack += 2; /* Pop ip and cs. */
207 *(--stack) = context->EFlags;
208 *(--stack) = cs;
209 *(--stack) = ip;
211 else
212 *(--stack) = context->EFlags;
214 else
216 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
217 context->SegSs,
218 context->Esp);
219 ADD_LOWORD( context->Esp, -2 ); /* One item will be added to stack. */
221 if (isstub)
223 WORD ip = stack[0];
224 WORD cs = stack[1];
225 stack += 2; /* Pop ip and cs. */
226 *(--stack) = LOWORD(context->EFlags);
227 *(--stack) = cs;
228 *(--stack) = ip;
230 else
231 *(--stack) = LOWORD(context->EFlags);
236 /**********************************************************************
237 * DOSVM_EmulateInterruptPM
239 * Emulate software interrupt in 16-bit or 32-bit protected mode.
240 * Called from signal handler when intXX opcode is executed.
242 * Pushes interrupt frame to stack and changes instruction
243 * pointer to interrupt handler.
245 void WINAPI DOSVM_EmulateInterruptPM( CONTEXT86 *context, BYTE intnum )
247 if (TRACE_ON(relay))
249 DPRINTF( "Call DOS int 0x%02x ret=%04lx:%08lx\n",
250 intnum, context->SegCs, context->Eip );
251 DPRINTF( " eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx\n",
252 context->Eax, context->Ebx, context->Ecx, context->Edx );
253 DPRINTF( " esi=%08lx edi=%08lx ebp=%08lx esp=%08lx \n",
254 context->Esi, context->Edi, context->Ebp, context->Esp );
255 DPRINTF( " ds=%04lx es=%04lx fs=%04lx gs=%04lx ss=%04lx flags=%08lx\n",
256 context->SegDs, context->SegEs, context->SegFs, context->SegGs,
257 context->SegSs, context->EFlags );
260 if (context->SegCs == DOSVM_dpmi_segments->dpmi_sel)
262 DOSVM_BuildCallFrame( context,
263 DOSVM_IntProcRelay,
264 DOSVM_RawModeSwitchHandler );
266 else if (context->SegCs == DOSVM_dpmi_segments->relay_code_sel)
269 * This must not be called using DOSVM_BuildCallFrame.
271 DOSVM_RelayHandler( context );
273 else if (context->SegCs == DOSVM_dpmi_segments->int48_sel)
275 /* Restore original flags stored into the stack by the caller. */
276 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
277 context->SegSs, context->Esp);
278 context->EFlags = stack[2];
280 if (intnum != context->Eip / DOSVM_STUB_PM48)
281 WARN( "interrupt stub has been modified "
282 "(interrupt is %02x, interrupt stub is %02lx)\n",
283 intnum, context->Eip/DOSVM_STUB_PM48 );
285 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
287 if (intnum == 0x25 || intnum == 0x26)
288 DOSVM_PushFlags( context, TRUE, TRUE );
290 DOSVM_BuildCallFrame( context,
291 DOSVM_IntProcRelay,
292 DOSVM_GetBuiltinHandler(intnum) );
294 else if (context->SegCs == DOSVM_dpmi_segments->int16_sel)
296 /* Restore original flags stored into the stack by the caller. */
297 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
298 context->SegSs, context->Esp);
299 context->EFlags = (DWORD)MAKELONG( stack[2], HIWORD(context->EFlags) );
301 if (intnum != context->Eip / DOSVM_STUB_PM16)
302 WARN( "interrupt stub has been modified "
303 "(interrupt is %02x, interrupt stub is %02lx)\n",
304 intnum, context->Eip/DOSVM_STUB_PM16 );
306 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
308 if (intnum == 0x25 || intnum == 0x26)
309 DOSVM_PushFlags( context, FALSE, TRUE );
311 DOSVM_BuildCallFrame( context,
312 DOSVM_IntProcRelay,
313 DOSVM_GetBuiltinHandler(intnum) );
315 else
317 DOSVM_HardwareInterruptPM( context, intnum );
322 /**********************************************************************
323 * DOSVM_HardwareInterruptPM
325 * Emulate call to interrupt handler in 16-bit or 32-bit protected mode.
327 * Pushes interrupt frame to stack and changes instruction
328 * pointer to interrupt handler.
330 void DOSVM_HardwareInterruptPM( CONTEXT86 *context, BYTE intnum )
332 if(DOSVM_IsDos32())
334 FARPROC48 addr = DOSVM_GetPMHandler48( intnum );
336 if (addr.selector == DOSVM_dpmi_segments->int48_sel)
338 TRACE( "builtin interrupt %02lx has been invoked "
339 "(through vector %02x)\n",
340 addr.offset / DOSVM_STUB_PM48, intnum );
342 if (intnum == 0x25 || intnum == 0x26)
343 DOSVM_PushFlags( context, TRUE, FALSE );
344 else if (DOSVM_IsIRQ(intnum))
345 DOSVM_PrepareIRQ( context, TRUE );
347 DOSVM_BuildCallFrame( context,
348 DOSVM_IntProcRelay,
349 DOSVM_GetBuiltinHandler(
350 addr.offset/DOSVM_STUB_PM48 ) );
352 else
354 DWORD *stack;
356 TRACE( "invoking hooked interrupt %02x at %04x:%08lx\n",
357 intnum, addr.selector, addr.offset );
359 if (DOSVM_IsIRQ(intnum))
360 DOSVM_PrepareIRQ( context, FALSE );
362 /* Push the flags and return address on the stack */
363 stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
364 *(--stack) = context->EFlags;
365 *(--stack) = context->SegCs;
366 *(--stack) = context->Eip;
367 context->Esp += -12;
369 /* Jump to the interrupt handler */
370 context->SegCs = addr.selector;
371 context->Eip = addr.offset;
374 else
376 FARPROC16 addr = DOSVM_GetPMHandler16( intnum );
378 if (SELECTOROF(addr) == DOSVM_dpmi_segments->int16_sel)
380 TRACE( "builtin interrupt %02x has been invoked "
381 "(through vector %02x)\n",
382 OFFSETOF(addr)/DOSVM_STUB_PM16, intnum );
384 if (intnum == 0x25 || intnum == 0x26)
385 DOSVM_PushFlags( context, FALSE, FALSE );
386 else if (DOSVM_IsIRQ(intnum))
387 DOSVM_PrepareIRQ( context, TRUE );
389 DOSVM_BuildCallFrame( context,
390 DOSVM_IntProcRelay,
391 DOSVM_GetBuiltinHandler(
392 OFFSETOF(addr)/DOSVM_STUB_PM16 ) );
394 else
396 WORD *stack;
398 TRACE( "invoking hooked interrupt %02x at %04x:%04x\n",
399 intnum, SELECTOROF(addr), OFFSETOF(addr) );
401 if (DOSVM_IsIRQ(intnum))
402 DOSVM_PrepareIRQ( context, FALSE );
404 /* Push the flags and return address on the stack */
405 stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
406 *(--stack) = LOWORD(context->EFlags);
407 *(--stack) = context->SegCs;
408 *(--stack) = LOWORD(context->Eip);
409 ADD_LOWORD( context->Esp, -6 );
411 /* Jump to the interrupt handler */
412 context->SegCs = HIWORD(addr);
413 context->Eip = LOWORD(addr);
419 /**********************************************************************
420 * DOSVM_EmulateInterruptRM
422 * Emulate software interrupt in real mode.
423 * Called from VM86 emulation when intXX opcode is executed.
425 * Either calls directly builtin handler or pushes interrupt frame to
426 * stack and changes instruction pointer to interrupt handler.
428 * Returns FALSE if this interrupt was caused by return
429 * from real mode wrapper.
431 BOOL WINAPI DOSVM_EmulateInterruptRM( CONTEXT86 *context, BYTE intnum )
433 if (TRACE_ON(relay))
435 DPRINTF( "Call DOS int 0x%02x ret=%04lx:%08lx\n",
436 intnum, context->SegCs, context->Eip );
437 DPRINTF( " eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx\n",
438 context->Eax, context->Ebx, context->Ecx, context->Edx );
439 DPRINTF( " esi=%08lx edi=%08lx ebp=%08lx esp=%08lx \n",
440 context->Esi, context->Edi, context->Ebp, context->Esp );
441 DPRINTF( " ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\n",
442 context->SegDs, context->SegEs,
443 context->SegFs, context->SegGs, context->EFlags );
446 /* check for our real-mode hooks */
447 if (intnum == 0x31)
449 /* is this exit from real-mode wrapper */
450 if (context->SegCs == DOSVM_dpmi_segments->wrap_seg)
451 return FALSE;
453 if (DOSVM_CheckWrappers( context ))
454 return TRUE;
457 /* check if the call is from our fake BIOS interrupt stubs */
458 if (context->SegCs==0xf000)
460 /* Restore original flags stored into the stack by the caller. */
461 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
462 context->SegSs, context->Esp);
463 context->EFlags = (DWORD)MAKELONG( stack[2], HIWORD(context->EFlags) );
465 if (intnum != context->Eip / DOSVM_STUB_RM)
466 WARN( "interrupt stub has been modified "
467 "(interrupt is %02x, interrupt stub is %02lx)\n",
468 intnum, context->Eip/DOSVM_STUB_RM );
470 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
472 DOSVM_CallBuiltinHandler( context, intnum );
474 /* Real mode stubs use IRET so we must put flags back into stack. */
475 stack[2] = LOWORD(context->EFlags);
477 else
479 DOSVM_HardwareInterruptRM( context, intnum );
482 return TRUE;
486 /**********************************************************************
487 * DOSVM_HardwareInterruptRM
489 * Emulate call to interrupt handler in real mode.
491 * Either calls directly builtin handler or pushes interrupt frame to
492 * stack and changes instruction pointer to interrupt handler.
494 void DOSVM_HardwareInterruptRM( CONTEXT86 *context, BYTE intnum )
496 FARPROC16 handler = DOSVM_GetRMHandler( intnum );
498 /* check if the call goes to an unhooked interrupt */
499 if (SELECTOROF(handler) == 0xf000)
501 /* if so, call it directly */
502 TRACE( "builtin interrupt %02x has been invoked "
503 "(through vector %02x)\n",
504 OFFSETOF(handler)/DOSVM_STUB_RM, intnum );
505 DOSVM_CallBuiltinHandler( context, OFFSETOF(handler)/DOSVM_STUB_RM );
507 else
509 /* the interrupt is hooked, simulate interrupt in DOS space */
510 WORD* stack = PTR_REAL_TO_LIN( context->SegSs, context->Esp );
511 WORD flag = LOWORD( context->EFlags );
513 TRACE( "invoking hooked interrupt %02x at %04x:%04x\n",
514 intnum, SELECTOROF(handler), OFFSETOF(handler) );
516 /* Copy virtual interrupt flag to pushed interrupt flag. */
517 if (context->EFlags & VIF_MASK)
518 flag |= IF_MASK;
519 else
520 flag &= ~IF_MASK;
522 *(--stack) = flag;
523 *(--stack) = context->SegCs;
524 *(--stack) = LOWORD( context->Eip );
525 context->Esp -= 6;
526 context->SegCs = SELECTOROF( handler );
527 context->Eip = OFFSETOF( handler );
529 /* Clear virtual interrupt flag. */
530 context->EFlags &= ~VIF_MASK;
535 /**********************************************************************
536 * DOSVM_GetRMHandler
538 * Return the real mode interrupt vector for a given interrupt.
540 FARPROC16 DOSVM_GetRMHandler( BYTE intnum )
542 return *DOSVM_GetRMVector( intnum );
546 /**********************************************************************
547 * DOSVM_SetRMHandler
549 * Set the real mode interrupt handler for a given interrupt.
551 void DOSVM_SetRMHandler( BYTE intnum, FARPROC16 handler )
553 TRACE("Set real mode interrupt vector %02x <- %04x:%04x\n",
554 intnum, HIWORD(handler), LOWORD(handler) );
555 *DOSVM_GetRMVector( intnum ) = handler;
559 /**********************************************************************
560 * DOSVM_GetPMHandler16
562 * Return the protected mode interrupt vector for a given interrupt.
564 FARPROC16 DOSVM_GetPMHandler16( BYTE intnum )
566 TDB *pTask;
567 FARPROC16 proc = 0;
569 pTask = GlobalLock16(GetCurrentTask());
570 if (pTask)
572 switch( intnum )
574 case 0x00:
575 proc = pTask->int0;
576 break;
577 case 0x02:
578 proc = pTask->int2;
579 break;
580 case 0x04:
581 proc = pTask->int4;
582 break;
583 case 0x06:
584 proc = pTask->int6;
585 break;
586 case 0x07:
587 proc = pTask->int7;
588 break;
589 case 0x3e:
590 proc = pTask->int3e;
591 break;
592 case 0x75:
593 proc = pTask->int75;
594 break;
596 if( proc )
597 return proc;
599 if (!DOSVM_Vectors16[intnum])
601 proc = (FARPROC16)MAKESEGPTR( DOSVM_dpmi_segments->int16_sel,
602 DOSVM_STUB_PM16 * intnum );
603 DOSVM_Vectors16[intnum] = proc;
605 return DOSVM_Vectors16[intnum];
609 /**********************************************************************
610 * DOSVM_SetPMHandler16
612 * Set the protected mode interrupt handler for a given interrupt.
614 void DOSVM_SetPMHandler16( BYTE intnum, FARPROC16 handler )
616 TDB *pTask;
618 TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
619 intnum, HIWORD(handler), LOWORD(handler) );
621 pTask = GlobalLock16(GetCurrentTask());
622 if (!pTask)
623 return;
624 switch( intnum )
626 case 0x00:
627 pTask->int0 = handler;
628 break;
629 case 0x02:
630 pTask->int2 = handler;
631 break;
632 case 0x04:
633 pTask->int4 = handler;
634 break;
635 case 0x06:
636 pTask->int6 = handler;
637 break;
638 case 0x07:
639 pTask->int7 = handler;
640 break;
641 case 0x3e:
642 pTask->int3e = handler;
643 break;
644 case 0x75:
645 pTask->int75 = handler;
646 break;
647 default:
648 DOSVM_Vectors16[intnum] = handler;
649 break;
654 /**********************************************************************
655 * DOSVM_GetPMHandler48
657 * Return the protected mode interrupt vector for a given interrupt.
658 * Used to get 48-bit pointer for 32-bit interrupt handlers in DPMI32.
660 FARPROC48 DOSVM_GetPMHandler48( BYTE intnum )
662 if (!DOSVM_Vectors48[intnum].selector)
664 DOSVM_Vectors48[intnum].selector = DOSVM_dpmi_segments->int48_sel;
665 DOSVM_Vectors48[intnum].offset = DOSVM_STUB_PM48 * intnum;
667 return DOSVM_Vectors48[intnum];
671 /**********************************************************************
672 * DOSVM_SetPMHandler48
674 * Set the protected mode interrupt handler for a given interrupt.
675 * Used to set 48-bit pointer for 32-bit interrupt handlers in DPMI32.
677 void DOSVM_SetPMHandler48( BYTE intnum, FARPROC48 handler )
679 TRACE("Set 32-bit protected mode interrupt vector %02x <- %04x:%08lx\n",
680 intnum, handler.selector, handler.offset );
681 DOSVM_Vectors48[intnum] = handler;
685 /**********************************************************************
686 * DOSVM_CallBuiltinHandler
688 * Execute Wine interrupt handler procedure.
690 void WINAPI DOSVM_CallBuiltinHandler( CONTEXT86 *context, BYTE intnum )
693 * FIXME: Make all builtin interrupt calls go via this routine.
694 * FIXME: Check for PM->RM interrupt reflection.
695 * FIXME: Check for RM->PM interrupt reflection.
698 INTPROC proc = DOSVM_GetBuiltinHandler( intnum );
699 proc( context );