Avoid copying invalid data on error.
[wine/wine-kai.git] / dlls / winedos / interrupts.c
blobadccaabbdf0ebe89718eb77210406c4a36a55469
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"
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[] =
37 /* 00 */ 0, 0, 0, 0,
38 /* 04 */ 0, 0, 0, 0,
39 /* 08 */ DOSVM_Int08Handler, DOSVM_Int09Handler, 0, 0,
40 /* 0C */ 0, 0, 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,
44 /* 1C */ 0, 0, 0, 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,
54 /* 44 */ 0, 0, 0, 0,
55 /* 48 */ 0, 0, 0, DOSVM_Int4bHandler,
56 /* 4C */ 0, 0, 0, 0,
57 /* 50 */ 0, 0, 0, 0,
58 /* 54 */ 0, 0, 0, 0,
59 /* 58 */ 0, 0, 0, 0,
60 /* 5C */ DOSVM_Int5cHandler, 0, 0, 0,
61 /* 60 */ 0, 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 /**********************************************************************
75 * DOSVM_GetRMVector
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 )
84 LDT_ENTRY entry;
85 FARPROC16 proc;
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 /**********************************************************************
96 * DOSVM_IsIRQ
98 * Return TRUE if interrupt is an IRQ.
100 static BOOL DOSVM_IsIRQ( BYTE intnum )
102 if (intnum >= 0x08 && intnum <= 0x0f)
103 return TRUE;
105 if (intnum >= 0x70 && intnum <= 0x77)
106 return TRUE;
108 return FALSE;
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];
132 if (proc)
133 return proc;
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 /**********************************************************************
146 * DOSVM_IntProcRelay
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;
153 proc(context);
157 /**********************************************************************
158 * DOSVM_PrepareIRQ
161 static void DOSVM_PrepareIRQ( CONTEXT86 *context, BOOL isbuiltin )
163 /* Disable virtual interrupts. */
164 NtCurrentTeb()->dpmi_vif = 0;
166 if (!isbuiltin)
168 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
169 context->SegSs,
170 context->Esp);
172 /* Push return address to stack. */
173 *(--stack) = context->SegCs;
174 *(--stack) = context->Eip;
175 context->Esp += -8;
177 /* Jump to enable interrupts stub. */
178 context->SegCs = DOSVM_dpmi_segments->relay_code_sel;
179 context->Eip = 5;
184 /**********************************************************************
185 * DOSVM_PushFlags
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 )
194 if (islong)
196 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
197 context->SegSs,
198 context->Esp);
199 context->Esp += -4; /* One item will be added to stack. */
201 if (isstub)
203 DWORD ip = stack[0];
204 DWORD cs = stack[1];
205 stack += 2; /* Pop ip and cs. */
206 *(--stack) = context->EFlags;
207 *(--stack) = cs;
208 *(--stack) = ip;
210 else
211 *(--stack) = context->EFlags;
213 else
215 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
216 context->SegSs,
217 context->Esp);
218 ADD_LOWORD( context->Esp, -2 ); /* One item will be added to stack. */
220 if (isstub)
222 WORD ip = stack[0];
223 WORD cs = stack[1];
224 stack += 2; /* Pop ip and cs. */
225 *(--stack) = LOWORD(context->EFlags);
226 *(--stack) = cs;
227 *(--stack) = ip;
229 else
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 )
246 if (TRACE_ON(relay))
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,
262 DOSVM_IntProcRelay,
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,
290 DOSVM_IntProcRelay,
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,
311 DOSVM_IntProcRelay,
312 DOSVM_GetBuiltinHandler(intnum) );
314 else
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 )
331 if(DOSVM_IsDos32())
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,
347 DOSVM_IntProcRelay,
348 DOSVM_GetBuiltinHandler(
349 addr.offset/DOSVM_STUB_PM48 ) );
351 else
353 DWORD *stack;
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;
366 context->Esp += -12;
368 /* Jump to the interrupt handler */
369 context->SegCs = addr.selector;
370 context->Eip = addr.offset;
373 else
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,
389 DOSVM_IntProcRelay,
390 DOSVM_GetBuiltinHandler(
391 OFFSETOF(addr)/DOSVM_STUB_PM16 ) );
393 else
395 TRACE( "invoking hooked interrupt %02x at %04x:%04x\n",
396 intnum, SELECTOROF(addr), OFFSETOF(addr) );
398 if (DOSVM_IsIRQ(intnum))
399 DOSVM_PrepareIRQ( context, FALSE );
401 /* Push the flags and return address on the stack */
402 PUSH_WORD16( context, LOWORD(context->EFlags) );
403 PUSH_WORD16( context, context->SegCs );
404 PUSH_WORD16( context, LOWORD(context->Eip) );
406 /* Jump to the interrupt handler */
407 context->SegCs = HIWORD(addr);
408 context->Eip = LOWORD(addr);
414 /**********************************************************************
415 * DOSVM_EmulateInterruptRM
417 * Emulate software interrupt in real mode.
418 * Called from VM86 emulation when intXX opcode is executed.
420 * Either calls directly builtin handler or pushes interrupt frame to
421 * stack and changes instruction pointer to interrupt handler.
423 * Returns FALSE if this interrupt was caused by return
424 * from real mode wrapper.
426 BOOL WINAPI DOSVM_EmulateInterruptRM( CONTEXT86 *context, BYTE intnum )
428 if (TRACE_ON(relay))
430 DPRINTF( "Call DOS int 0x%02x ret=%04lx:%08lx\n",
431 intnum, context->SegCs, context->Eip );
432 DPRINTF( " eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx\n",
433 context->Eax, context->Ebx, context->Ecx, context->Edx );
434 DPRINTF( " esi=%08lx edi=%08lx ebp=%08lx esp=%08lx \n",
435 context->Esi, context->Edi, context->Ebp, context->Esp );
436 DPRINTF( " ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\n",
437 context->SegDs, context->SegEs,
438 context->SegFs, context->SegGs, context->EFlags );
441 /* check for our real-mode hooks */
442 if (intnum == 0x31)
444 /* is this exit from real-mode wrapper */
445 if (context->SegCs == DOSVM_dpmi_segments->wrap_seg)
446 return FALSE;
448 if (DOSVM_CheckWrappers( context ))
449 return TRUE;
452 /* check if the call is from our fake BIOS interrupt stubs */
453 if (context->SegCs==0xf000)
455 /* Restore original flags stored into the stack by the caller. */
456 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
457 context->SegSs, context->Esp);
458 context->EFlags = (DWORD)MAKELONG( stack[2], HIWORD(context->EFlags) );
460 if (intnum != context->Eip / DOSVM_STUB_RM)
461 WARN( "interrupt stub has been modified "
462 "(interrupt is %02x, interrupt stub is %02lx)\n",
463 intnum, context->Eip/DOSVM_STUB_RM );
465 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
467 DOSVM_CallBuiltinHandler( context, intnum );
469 /* Real mode stubs use IRET so we must put flags back into stack. */
470 stack[2] = LOWORD(context->EFlags);
472 else
474 DOSVM_HardwareInterruptRM( context, intnum );
477 return TRUE;
481 /**********************************************************************
482 * DOSVM_HardwareInterruptRM
484 * Emulate call to interrupt handler in real mode.
486 * Either calls directly builtin handler or pushes interrupt frame to
487 * stack and changes instruction pointer to interrupt handler.
489 void DOSVM_HardwareInterruptRM( CONTEXT86 *context, BYTE intnum )
491 FARPROC16 handler = DOSVM_GetRMHandler( intnum );
493 /* check if the call goes to an unhooked interrupt */
494 if (SELECTOROF(handler) == 0xf000)
496 /* if so, call it directly */
497 TRACE( "builtin interrupt %02x has been invoked "
498 "(through vector %02x)\n",
499 OFFSETOF(handler)/DOSVM_STUB_RM, intnum );
500 DOSVM_CallBuiltinHandler( context, OFFSETOF(handler)/DOSVM_STUB_RM );
502 else
504 /* the interrupt is hooked, simulate interrupt in DOS space */
505 WORD flag = LOWORD( context->EFlags );
507 TRACE( "invoking hooked interrupt %02x at %04x:%04x\n",
508 intnum, SELECTOROF(handler), OFFSETOF(handler) );
510 /* Copy virtual interrupt flag to pushed interrupt flag. */
511 if (context->EFlags & VIF_MASK)
512 flag |= IF_MASK;
513 else
514 flag &= ~IF_MASK;
516 PUSH_WORD16( context, flag );
517 PUSH_WORD16( context, context->SegCs );
518 PUSH_WORD16( context, LOWORD( context->Eip ));
520 context->SegCs = SELECTOROF( handler );
521 context->Eip = OFFSETOF( handler );
523 /* Clear virtual interrupt flag. */
524 context->EFlags &= ~VIF_MASK;
529 /**********************************************************************
530 * DOSVM_GetRMHandler
532 * Return the real mode interrupt vector for a given interrupt.
534 FARPROC16 DOSVM_GetRMHandler( BYTE intnum )
536 return *DOSVM_GetRMVector( intnum );
540 /**********************************************************************
541 * DOSVM_SetRMHandler
543 * Set the real mode interrupt handler for a given interrupt.
545 void DOSVM_SetRMHandler( BYTE intnum, FARPROC16 handler )
547 TRACE("Set real mode interrupt vector %02x <- %04x:%04x\n",
548 intnum, HIWORD(handler), LOWORD(handler) );
549 *DOSVM_GetRMVector( intnum ) = handler;
553 /**********************************************************************
554 * DOSVM_GetPMHandler16
556 * Return the protected mode interrupt vector for a given interrupt.
558 FARPROC16 DOSVM_GetPMHandler16( BYTE intnum )
560 TDB *pTask;
561 FARPROC16 proc = 0;
563 pTask = GlobalLock16(GetCurrentTask());
564 if (pTask)
566 switch( intnum )
568 case 0x00:
569 proc = pTask->int0;
570 break;
571 case 0x02:
572 proc = pTask->int2;
573 break;
574 case 0x04:
575 proc = pTask->int4;
576 break;
577 case 0x06:
578 proc = pTask->int6;
579 break;
580 case 0x07:
581 proc = pTask->int7;
582 break;
583 case 0x3e:
584 proc = pTask->int3e;
585 break;
586 case 0x75:
587 proc = pTask->int75;
588 break;
590 if( proc )
591 return proc;
593 if (!DOSVM_Vectors16[intnum])
595 proc = (FARPROC16)MAKESEGPTR( DOSVM_dpmi_segments->int16_sel,
596 DOSVM_STUB_PM16 * intnum );
597 DOSVM_Vectors16[intnum] = proc;
599 return DOSVM_Vectors16[intnum];
603 /**********************************************************************
604 * DOSVM_SetPMHandler16
606 * Set the protected mode interrupt handler for a given interrupt.
608 void DOSVM_SetPMHandler16( BYTE intnum, FARPROC16 handler )
610 TDB *pTask;
612 TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
613 intnum, HIWORD(handler), LOWORD(handler) );
615 pTask = GlobalLock16(GetCurrentTask());
616 if (!pTask)
617 return;
618 switch( intnum )
620 case 0x00:
621 pTask->int0 = handler;
622 break;
623 case 0x02:
624 pTask->int2 = handler;
625 break;
626 case 0x04:
627 pTask->int4 = handler;
628 break;
629 case 0x06:
630 pTask->int6 = handler;
631 break;
632 case 0x07:
633 pTask->int7 = handler;
634 break;
635 case 0x3e:
636 pTask->int3e = handler;
637 break;
638 case 0x75:
639 pTask->int75 = handler;
640 break;
641 default:
642 DOSVM_Vectors16[intnum] = handler;
643 break;
648 /**********************************************************************
649 * DOSVM_GetPMHandler48
651 * Return the protected mode interrupt vector for a given interrupt.
652 * Used to get 48-bit pointer for 32-bit interrupt handlers in DPMI32.
654 FARPROC48 DOSVM_GetPMHandler48( BYTE intnum )
656 if (!DOSVM_Vectors48[intnum].selector)
658 DOSVM_Vectors48[intnum].selector = DOSVM_dpmi_segments->int48_sel;
659 DOSVM_Vectors48[intnum].offset = DOSVM_STUB_PM48 * intnum;
661 return DOSVM_Vectors48[intnum];
665 /**********************************************************************
666 * DOSVM_SetPMHandler48
668 * Set the protected mode interrupt handler for a given interrupt.
669 * Used to set 48-bit pointer for 32-bit interrupt handlers in DPMI32.
671 void DOSVM_SetPMHandler48( BYTE intnum, FARPROC48 handler )
673 TRACE("Set 32-bit protected mode interrupt vector %02x <- %04x:%08lx\n",
674 intnum, handler.selector, handler.offset );
675 DOSVM_Vectors48[intnum] = handler;
679 /**********************************************************************
680 * DOSVM_CallBuiltinHandler
682 * Execute Wine interrupt handler procedure.
684 void WINAPI DOSVM_CallBuiltinHandler( CONTEXT86 *context, BYTE intnum )
687 * FIXME: Make all builtin interrupt calls go via this routine.
688 * FIXME: Check for PM->RM interrupt reflection.
689 * FIXME: Check for RM->PM interrupt reflection.
692 INTPROC proc = DOSVM_GetBuiltinHandler( intnum );
693 proc( context );