devenum: Implement IMoniker::GetClassID().
[wine.git] / dlls / krnl386.exe16 / interrupts.c
blobe0466808e284ae45bc4c2608b190d527e4b6eefe
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "config.h"
23 #include <stdio.h>
25 #include "wine/winbase16.h"
26 #include "kernel16_private.h"
27 #include "dosexe.h"
28 #include "winternl.h"
29 #include "wine/debug.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(int);
32 WINE_DECLARE_DEBUG_CHANNEL(relay);
34 #define BCD_TO_BIN(x) ((x&15) + (x>>4)*10)
35 #define BIN_TO_BCD(x) ((x%10) + ((x/10)<<4))
37 static void WINAPI DOSVM_Int11Handler(CONTEXT*);
38 static void WINAPI DOSVM_Int12Handler(CONTEXT*);
39 static void WINAPI DOSVM_Int17Handler(CONTEXT*);
40 static void WINAPI DOSVM_Int19Handler(CONTEXT*);
41 static void WINAPI DOSVM_Int1aHandler(CONTEXT*);
42 static void WINAPI DOSVM_Int20Handler(CONTEXT*);
43 static void WINAPI DOSVM_Int29Handler(CONTEXT*);
44 static void WINAPI DOSVM_Int2aHandler(CONTEXT*);
45 static void WINAPI DOSVM_Int41Handler(CONTEXT*);
46 static void WINAPI DOSVM_Int4bHandler(CONTEXT*);
47 static void WINAPI DOSVM_Int5cHandler(CONTEXT*);
48 static void WINAPI DOSVM_DefaultHandler(CONTEXT*);
50 static FARPROC16 DOSVM_Vectors16[256];
51 static FARPROC48 DOSVM_Vectors48[256];
52 static const INTPROC DOSVM_VectorsBuiltin[] =
54 /* 00 */ 0, 0, 0, 0,
55 /* 04 */ 0, 0, 0, 0,
56 /* 08 */ DOSVM_Int08Handler, DOSVM_Int09Handler, 0, 0,
57 /* 0C */ 0, 0, 0, 0,
58 /* 10 */ DOSVM_Int10Handler, DOSVM_Int11Handler, DOSVM_Int12Handler, DOSVM_Int13Handler,
59 /* 14 */ 0, DOSVM_Int15Handler, DOSVM_Int16Handler, DOSVM_Int17Handler,
60 /* 18 */ 0, DOSVM_Int19Handler, DOSVM_Int1aHandler, 0,
61 /* 1C */ 0, 0, 0, 0,
62 /* 20 */ DOSVM_Int20Handler, DOSVM_Int21Handler, 0, 0,
63 /* 24 */ 0, DOSVM_Int25Handler, DOSVM_Int26Handler, 0,
64 /* 28 */ 0, DOSVM_Int29Handler, DOSVM_Int2aHandler, 0,
65 /* 2C */ 0, 0, 0, DOSVM_Int2fHandler,
66 /* 30 */ 0, DOSVM_Int31Handler, 0, DOSVM_Int33Handler,
67 /* 34 */ DOSVM_Int34Handler, DOSVM_Int35Handler, DOSVM_Int36Handler, DOSVM_Int37Handler,
68 /* 38 */ DOSVM_Int38Handler, DOSVM_Int39Handler, DOSVM_Int3aHandler, DOSVM_Int3bHandler,
69 /* 3C */ DOSVM_Int3cHandler, DOSVM_Int3dHandler, DOSVM_Int3eHandler, 0,
70 /* 40 */ 0, DOSVM_Int41Handler, 0, 0,
71 /* 44 */ 0, 0, 0, 0,
72 /* 48 */ 0, 0, 0, DOSVM_Int4bHandler,
73 /* 4C */ 0, 0, 0, 0,
74 /* 50 */ 0, 0, 0, 0,
75 /* 54 */ 0, 0, 0, 0,
76 /* 58 */ 0, 0, 0, 0,
77 /* 5C */ DOSVM_Int5cHandler, 0, 0, 0,
78 /* 60 */ 0, 0, 0, 0,
79 /* 64 */ 0, 0, 0, DOSVM_Int67Handler,
80 /* 68 */ DOSVM_DefaultHandler
85 * Sizes of real mode and protected mode interrupt stubs.
87 #define DOSVM_STUB_RM 4
88 #define DOSVM_STUB_PM16 5
89 #define DOSVM_STUB_PM48 6
92 /**********************************************************************
93 * DOSVM_GetRMVector
95 * Return pointer to real mode interrupt vector. These are not at fixed
96 * location because those Win16 programs that do not use any real mode
97 * code have protected NULL pointer catching block at low linear memory
98 * and interrupt vectors have been moved to another location.
100 static FARPROC16* DOSVM_GetRMVector( BYTE intnum )
102 LDT_ENTRY entry;
103 FARPROC16 proc;
105 proc = GetProcAddress16( GetModuleHandle16( "KERNEL" ),
106 (LPCSTR)(ULONG_PTR)183 );
107 wine_ldt_get_entry( LOWORD(proc), &entry );
109 return (FARPROC16*)wine_ldt_get_base( &entry ) + intnum;
113 /**********************************************************************
114 * DOSVM_IsIRQ
116 * Return TRUE if interrupt is an IRQ.
118 static BOOL DOSVM_IsIRQ( BYTE intnum )
120 if (intnum >= 0x08 && intnum <= 0x0f)
121 return TRUE;
123 if (intnum >= 0x70 && intnum <= 0x77)
124 return TRUE;
126 return FALSE;
130 /**********************************************************************
131 * DOSVM_DefaultHandler
133 * Default interrupt handler. This will be used to emulate all
134 * interrupts that don't have their own interrupt handler.
136 static void WINAPI DOSVM_DefaultHandler( CONTEXT *context )
141 /**********************************************************************
142 * DOSVM_GetBuiltinHandler
144 * Return Wine interrupt handler procedure for a given interrupt.
146 static INTPROC DOSVM_GetBuiltinHandler( BYTE intnum )
148 if (intnum < sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) {
149 INTPROC proc = DOSVM_VectorsBuiltin[intnum];
150 if (proc)
151 return proc;
154 WARN("int%x not implemented, returning dummy handler\n", intnum );
156 if (DOSVM_IsIRQ(intnum))
157 return DOSVM_AcknowledgeIRQ;
159 return DOSVM_DefaultHandler;
163 /**********************************************************************
164 * DOSVM_IntProcRelay
166 * Simple DOSRELAY that interprets its argument as INTPROC and calls it.
168 static void DOSVM_IntProcRelay( CONTEXT *context, LPVOID data )
170 INTPROC proc = (INTPROC)data;
171 proc(context);
175 /**********************************************************************
176 * DOSVM_PrepareIRQ
179 static void DOSVM_PrepareIRQ( CONTEXT *context, BOOL isbuiltin )
181 /* Disable virtual interrupts. */
182 get_vm86_teb_info()->dpmi_vif = 0;
184 if (!isbuiltin)
186 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
187 context->SegSs,
188 context->Esp);
190 /* Push return address to stack. */
191 *(--stack) = context->SegCs;
192 *(--stack) = context->Eip;
193 context->Esp += -8;
195 /* Jump to enable interrupts stub. */
196 context->SegCs = DOSVM_dpmi_segments->relay_code_sel;
197 context->Eip = 5;
202 /**********************************************************************
203 * DOSVM_PushFlags
205 * This routine is used to make default int25 and int26 handlers leave the
206 * original eflags into stack. In order to do this, stack is manipulated
207 * so that it actually contains two copies of eflags, one of which is
208 * popped during return from interrupt handler.
210 static void DOSVM_PushFlags( CONTEXT *context, BOOL islong, BOOL isstub )
212 if (islong)
214 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
215 context->SegSs,
216 context->Esp);
217 context->Esp += -4; /* One item will be added to stack. */
219 if (isstub)
221 DWORD ip = stack[0];
222 DWORD cs = stack[1];
223 stack += 2; /* Pop ip and cs. */
224 *(--stack) = context->EFlags;
225 *(--stack) = cs;
226 *(--stack) = ip;
228 else
229 *(--stack) = context->EFlags;
231 else
233 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
234 context->SegSs,
235 context->Esp);
236 ADD_LOWORD( context->Esp, -2 ); /* One item will be added to stack. */
238 if (isstub)
240 WORD ip = stack[0];
241 WORD cs = stack[1];
242 stack += 2; /* Pop ip and cs. */
243 *(--stack) = LOWORD(context->EFlags);
244 *(--stack) = cs;
245 *(--stack) = ip;
247 else
248 *(--stack) = LOWORD(context->EFlags);
253 /**********************************************************************
254 * DOSVM_EmulateInterruptPM
256 * Emulate software interrupt in 16-bit or 32-bit protected mode.
257 * Called from signal handler when intXX opcode is executed.
259 * Pushes interrupt frame to stack and changes instruction
260 * pointer to interrupt handler.
262 BOOL DOSVM_EmulateInterruptPM( CONTEXT *context, BYTE intnum )
264 TRACE_(relay)("Call DOS int 0x%02x ret=%04x:%08x\n"
265 " eax=%08x ebx=%08x ecx=%08x edx=%08x\n"
266 " esi=%08x edi=%08x ebp=%08x esp=%08x\n"
267 " ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n",
268 intnum, context->SegCs, context->Eip,
269 context->Eax, context->Ebx, context->Ecx, context->Edx,
270 context->Esi, context->Edi, context->Ebp, context->Esp,
271 context->SegDs, context->SegEs, context->SegFs, context->SegGs,
272 context->SegSs, context->EFlags );
274 DOSMEM_InitDosMemory();
276 if (context->SegCs == DOSVM_dpmi_segments->dpmi_sel)
278 DOSVM_BuildCallFrame( context,
279 DOSVM_IntProcRelay,
280 DOSVM_RawModeSwitchHandler );
282 else if (context->SegCs == DOSVM_dpmi_segments->relay_code_sel)
285 * This must not be called using DOSVM_BuildCallFrame.
287 DOSVM_RelayHandler( context );
289 else if (context->SegCs == DOSVM_dpmi_segments->int48_sel)
291 /* Restore original flags stored into the stack by the caller. */
292 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
293 context->SegSs, context->Esp);
294 context->EFlags = stack[2];
296 if (intnum != context->Eip / DOSVM_STUB_PM48)
297 WARN( "interrupt stub has been modified "
298 "(interrupt is %02x, interrupt stub is %02x)\n",
299 intnum, context->Eip/DOSVM_STUB_PM48 );
301 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
303 if (intnum == 0x25 || intnum == 0x26)
304 DOSVM_PushFlags( context, TRUE, TRUE );
306 DOSVM_BuildCallFrame( context,
307 DOSVM_IntProcRelay,
308 DOSVM_GetBuiltinHandler(intnum) );
310 else if (context->SegCs == DOSVM_dpmi_segments->int16_sel)
312 /* Restore original flags stored into the stack by the caller. */
313 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
314 context->SegSs, context->Esp);
315 context->EFlags = (DWORD)MAKELONG( stack[2], HIWORD(context->EFlags) );
317 if (intnum != context->Eip / DOSVM_STUB_PM16)
318 WARN( "interrupt stub has been modified "
319 "(interrupt is %02x, interrupt stub is %02x)\n",
320 intnum, context->Eip/DOSVM_STUB_PM16 );
322 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
324 if (intnum == 0x25 || intnum == 0x26)
325 DOSVM_PushFlags( context, FALSE, TRUE );
327 DOSVM_BuildCallFrame( context,
328 DOSVM_IntProcRelay,
329 DOSVM_GetBuiltinHandler(intnum) );
331 else if (wine_ldt_is_system(context->SegCs))
333 INTPROC proc;
334 if (intnum >= sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) return FALSE;
335 if (!(proc = DOSVM_VectorsBuiltin[intnum])) return FALSE;
336 proc( context );
338 else
340 DOSVM_HardwareInterruptPM( context, intnum );
342 return TRUE;
346 /**********************************************************************
347 * DOSVM_HardwareInterruptPM
349 * Emulate call to interrupt handler in 16-bit or 32-bit protected mode.
351 * Pushes interrupt frame to stack and changes instruction
352 * pointer to interrupt handler.
354 void DOSVM_HardwareInterruptPM( CONTEXT *context, BYTE intnum )
356 if(DOSVM_IsDos32())
358 FARPROC48 addr = DOSVM_GetPMHandler48( intnum );
360 if (addr.selector == DOSVM_dpmi_segments->int48_sel)
362 TRACE( "builtin interrupt %02x has been invoked "
363 "(through vector %02x)\n",
364 addr.offset / DOSVM_STUB_PM48, intnum );
366 if (intnum == 0x25 || intnum == 0x26)
367 DOSVM_PushFlags( context, TRUE, FALSE );
368 else if (DOSVM_IsIRQ(intnum))
369 DOSVM_PrepareIRQ( context, TRUE );
371 DOSVM_BuildCallFrame( context,
372 DOSVM_IntProcRelay,
373 DOSVM_GetBuiltinHandler(
374 addr.offset/DOSVM_STUB_PM48 ) );
376 else
378 DWORD *stack;
380 TRACE( "invoking hooked interrupt %02x at %04x:%08x\n",
381 intnum, addr.selector, addr.offset );
383 if (DOSVM_IsIRQ(intnum))
384 DOSVM_PrepareIRQ( context, FALSE );
386 /* Push the flags and return address on the stack */
387 stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
388 *(--stack) = context->EFlags;
389 *(--stack) = context->SegCs;
390 *(--stack) = context->Eip;
391 context->Esp += -12;
393 /* Jump to the interrupt handler */
394 context->SegCs = addr.selector;
395 context->Eip = addr.offset;
398 else
400 FARPROC16 addr = DOSVM_GetPMHandler16( intnum );
402 if (SELECTOROF(addr) == DOSVM_dpmi_segments->int16_sel)
404 TRACE( "builtin interrupt %02x has been invoked "
405 "(through vector %02x)\n",
406 OFFSETOF(addr)/DOSVM_STUB_PM16, intnum );
408 if (intnum == 0x25 || intnum == 0x26)
409 DOSVM_PushFlags( context, FALSE, FALSE );
410 else if (DOSVM_IsIRQ(intnum))
411 DOSVM_PrepareIRQ( context, TRUE );
413 DOSVM_BuildCallFrame( context,
414 DOSVM_IntProcRelay,
415 DOSVM_GetBuiltinHandler(
416 OFFSETOF(addr)/DOSVM_STUB_PM16 ) );
418 else
420 TRACE( "invoking hooked interrupt %02x at %04x:%04x\n",
421 intnum, SELECTOROF(addr), OFFSETOF(addr) );
423 if (DOSVM_IsIRQ(intnum))
424 DOSVM_PrepareIRQ( context, FALSE );
426 /* Push the flags and return address on the stack */
427 PUSH_WORD16( context, LOWORD(context->EFlags) );
428 PUSH_WORD16( context, context->SegCs );
429 PUSH_WORD16( context, LOWORD(context->Eip) );
431 /* Jump to the interrupt handler */
432 context->SegCs = HIWORD(addr);
433 context->Eip = LOWORD(addr);
439 /**********************************************************************
440 * DOSVM_EmulateInterruptRM
442 * Emulate software interrupt in real mode.
443 * Called from VM86 emulation when intXX opcode is executed.
445 * Either calls directly builtin handler or pushes interrupt frame to
446 * stack and changes instruction pointer to interrupt handler.
448 * Returns FALSE if this interrupt was caused by return
449 * from real mode wrapper.
451 BOOL DOSVM_EmulateInterruptRM( CONTEXT *context, BYTE intnum )
453 TRACE_(relay)("Call DOS int 0x%02x ret=%04x:%08x\n"
454 " eax=%08x ebx=%08x ecx=%08x edx=%08x\n"
455 " esi=%08x edi=%08x ebp=%08x esp=%08x\n"
456 " ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n",
457 intnum, context->SegCs, context->Eip,
458 context->Eax, context->Ebx, context->Ecx, context->Edx,
459 context->Esi, context->Edi, context->Ebp, context->Esp,
460 context->SegDs, context->SegEs, context->SegFs, context->SegGs,
461 context->SegSs, context->EFlags );
463 /* check for our real-mode hooks */
464 if (intnum == 0x31)
466 /* is this exit from real-mode wrapper */
467 if (context->SegCs == DOSVM_dpmi_segments->wrap_seg)
468 return FALSE;
470 if (DOSVM_CheckWrappers( context ))
471 return TRUE;
474 /* check if the call is from our fake BIOS interrupt stubs */
475 if (context->SegCs==0xf000)
477 /* Restore original flags stored into the stack by the caller. */
478 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
479 context->SegSs, context->Esp);
480 context->EFlags = (DWORD)MAKELONG( stack[2], HIWORD(context->EFlags) );
482 if (intnum != context->Eip / DOSVM_STUB_RM)
483 WARN( "interrupt stub has been modified "
484 "(interrupt is %02x, interrupt stub is %02x)\n",
485 intnum, context->Eip/DOSVM_STUB_RM );
487 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
489 DOSVM_CallBuiltinHandler( context, intnum );
491 /* Real mode stubs use IRET so we must put flags back into stack. */
492 stack[2] = LOWORD(context->EFlags);
494 else
496 DOSVM_HardwareInterruptRM( context, intnum );
499 return TRUE;
503 /**********************************************************************
504 * DOSVM_HardwareInterruptRM
506 * Emulate call to interrupt handler in real mode.
508 * Either calls directly builtin handler or pushes interrupt frame to
509 * stack and changes instruction pointer to interrupt handler.
511 void DOSVM_HardwareInterruptRM( CONTEXT *context, BYTE intnum )
513 FARPROC16 handler = DOSVM_GetRMHandler( intnum );
515 /* check if the call goes to an unhooked interrupt */
516 if (SELECTOROF(handler) == 0xf000)
518 /* if so, call it directly */
519 TRACE( "builtin interrupt %02x has been invoked "
520 "(through vector %02x)\n",
521 OFFSETOF(handler)/DOSVM_STUB_RM, intnum );
522 DOSVM_CallBuiltinHandler( context, OFFSETOF(handler)/DOSVM_STUB_RM );
524 else
526 /* the interrupt is hooked, simulate interrupt in DOS space */
527 WORD flag = LOWORD( context->EFlags );
529 TRACE( "invoking hooked interrupt %02x at %04x:%04x\n",
530 intnum, SELECTOROF(handler), OFFSETOF(handler) );
532 /* Copy virtual interrupt flag to pushed interrupt flag. */
533 if (context->EFlags & VIF_MASK)
534 flag |= IF_MASK;
535 else
536 flag &= ~IF_MASK;
538 PUSH_WORD16( context, flag );
539 PUSH_WORD16( context, context->SegCs );
540 PUSH_WORD16( context, LOWORD( context->Eip ));
542 context->SegCs = SELECTOROF( handler );
543 context->Eip = OFFSETOF( handler );
545 /* Clear virtual interrupt flag and trap flag. */
546 context->EFlags &= ~(VIF_MASK | TF_MASK);
551 /**********************************************************************
552 * DOSVM_GetRMHandler
554 * Return the real mode interrupt vector for a given interrupt.
556 FARPROC16 DOSVM_GetRMHandler( BYTE intnum )
558 return *DOSVM_GetRMVector( intnum );
562 /**********************************************************************
563 * DOSVM_SetRMHandler
565 * Set the real mode interrupt handler for a given interrupt.
567 void DOSVM_SetRMHandler( BYTE intnum, FARPROC16 handler )
569 TRACE("Set real mode interrupt vector %02x <- %04x:%04x\n",
570 intnum, HIWORD(handler), LOWORD(handler) );
571 *DOSVM_GetRMVector( intnum ) = handler;
575 /**********************************************************************
576 * DOSVM_GetPMHandler16
578 * Return the protected mode interrupt vector for a given interrupt.
580 FARPROC16 DOSVM_GetPMHandler16( BYTE intnum )
582 TDB *pTask;
583 FARPROC16 proc = 0;
585 pTask = GlobalLock16(GetCurrentTask());
586 if (pTask)
588 switch( intnum )
590 case 0x00:
591 proc = pTask->int0;
592 break;
593 case 0x02:
594 proc = pTask->int2;
595 break;
596 case 0x04:
597 proc = pTask->int4;
598 break;
599 case 0x06:
600 proc = pTask->int6;
601 break;
602 case 0x07:
603 proc = pTask->int7;
604 break;
605 case 0x3e:
606 proc = pTask->int3e;
607 break;
608 case 0x75:
609 proc = pTask->int75;
610 break;
612 if( proc )
613 return proc;
615 if (!DOSVM_Vectors16[intnum])
617 proc = (FARPROC16)MAKESEGPTR( DOSVM_dpmi_segments->int16_sel,
618 DOSVM_STUB_PM16 * intnum );
619 DOSVM_Vectors16[intnum] = proc;
621 return DOSVM_Vectors16[intnum];
625 /**********************************************************************
626 * DOSVM_SetPMHandler16
628 * Set the protected mode interrupt handler for a given interrupt.
630 void DOSVM_SetPMHandler16( BYTE intnum, FARPROC16 handler )
632 TDB *pTask;
634 TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
635 intnum, HIWORD(handler), LOWORD(handler) );
637 pTask = GlobalLock16(GetCurrentTask());
638 if (!pTask)
639 return;
640 switch( intnum )
642 case 0x00:
643 pTask->int0 = handler;
644 break;
645 case 0x02:
646 pTask->int2 = handler;
647 break;
648 case 0x04:
649 pTask->int4 = handler;
650 break;
651 case 0x06:
652 pTask->int6 = handler;
653 break;
654 case 0x07:
655 pTask->int7 = handler;
656 break;
657 case 0x3e:
658 pTask->int3e = handler;
659 break;
660 case 0x75:
661 pTask->int75 = handler;
662 break;
663 default:
664 DOSVM_Vectors16[intnum] = handler;
665 break;
670 /**********************************************************************
671 * DOSVM_GetPMHandler48
673 * Return the protected mode interrupt vector for a given interrupt.
674 * Used to get 48-bit pointer for 32-bit interrupt handlers in DPMI32.
676 FARPROC48 DOSVM_GetPMHandler48( BYTE intnum )
678 if (!DOSVM_Vectors48[intnum].selector)
680 DOSVM_Vectors48[intnum].selector = DOSVM_dpmi_segments->int48_sel;
681 DOSVM_Vectors48[intnum].offset = DOSVM_STUB_PM48 * intnum;
683 return DOSVM_Vectors48[intnum];
687 /**********************************************************************
688 * DOSVM_SetPMHandler48
690 * Set the protected mode interrupt handler for a given interrupt.
691 * Used to set 48-bit pointer for 32-bit interrupt handlers in DPMI32.
693 void DOSVM_SetPMHandler48( BYTE intnum, FARPROC48 handler )
695 TRACE("Set 32-bit protected mode interrupt vector %02x <- %04x:%08x\n",
696 intnum, handler.selector, handler.offset );
697 DOSVM_Vectors48[intnum] = handler;
701 /**********************************************************************
702 * DOSVM_CallBuiltinHandler
704 * Execute Wine interrupt handler procedure.
706 void DOSVM_CallBuiltinHandler( CONTEXT *context, BYTE intnum )
709 * FIXME: Make all builtin interrupt calls go via this routine.
710 * FIXME: Check for PM->RM interrupt reflection.
711 * FIXME: Check for RM->PM interrupt reflection.
714 INTPROC proc = DOSVM_GetBuiltinHandler( intnum );
715 proc( context );
719 /**********************************************************************
720 * __wine_call_int_handler (KERNEL.@)
722 void __wine_call_int_handler( CONTEXT *context, BYTE intnum )
724 DOSMEM_InitDosMemory();
725 DOSVM_CallBuiltinHandler( context, intnum );
729 /**********************************************************************
730 * DOSVM_Int11Handler
732 * Handler for int 11h (get equipment list).
735 * Borrowed from Ralph Brown's interrupt lists:
737 * bits 15-14: number of parallel devices
738 * bit 13: [Conv] Internal modem
739 * bit 12: reserved
740 * bits 11- 9: number of serial devices
741 * bit 8: reserved
742 * bits 7- 6: number of diskette drives minus one
743 * bits 5- 4: Initial video mode:
744 * 00b = EGA,VGA,PGA
745 * 01b = 40 x 25 color
746 * 10b = 80 x 25 color
747 * 11b = 80 x 25 mono
748 * bit 3: reserved
749 * bit 2: [PS] =1 if pointing device
750 * [non-PS] reserved
751 * bit 1: =1 if math co-processor
752 * bit 0: =1 if diskette available for boot
755 * Currently the only of these bits correctly set are:
757 * bits 15-14 } Added by William Owen Smith,
758 * bits 11-9 } wos@dcs.warwick.ac.uk
759 * bits 7-6
760 * bit 2 (always set) ( bit 2 = 4 )
761 * bit 1 } Robert 'Admiral' Coeyman
762 * All *nix systems either have a math processor or
763 * emulate one.
765 static void WINAPI DOSVM_Int11Handler( CONTEXT *context )
767 int diskdrives = 0;
768 int parallelports = 0;
769 int serialports = 0;
770 int x;
772 if (GetDriveTypeA("A:\\") == DRIVE_REMOVABLE) diskdrives++;
773 if (GetDriveTypeA("B:\\") == DRIVE_REMOVABLE) diskdrives++;
774 if (diskdrives) diskdrives--;
776 for (x=0; x < 9; x++)
778 HANDLE handle;
779 char file[10];
781 /* serial port name */
782 sprintf( file, "\\\\.\\COM%d", x+1 );
783 handle = CreateFileA( file, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
784 if (handle != INVALID_HANDLE_VALUE)
786 CloseHandle( handle );
787 serialports++;
790 sprintf( file, "\\\\.\\LPT%d", x+1 );
791 handle = CreateFileA( file, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
792 if (handle != INVALID_HANDLE_VALUE)
794 CloseHandle( handle );
795 parallelports++;
799 if (serialports > 7) /* 3 bits -- maximum value = 7 */
800 serialports = 7;
802 if (parallelports > 3) /* 2 bits -- maximum value = 3 */
803 parallelports = 3;
805 SET_AX( context,
806 (diskdrives << 6) | (serialports << 9) | (parallelports << 14) | 0x06 );
810 /**********************************************************************
811 * DOSVM_Int12Handler
813 * Handler for int 12h (get memory size).
815 static void WINAPI DOSVM_Int12Handler( CONTEXT *context )
817 SET_AX( context, 640 );
821 /**********************************************************************
822 * DOSVM_Int17Handler
824 * Handler for int 17h (printer - output character).
826 static void WINAPI DOSVM_Int17Handler( CONTEXT *context )
828 switch( AH_reg(context) )
830 case 0x00:/* Send character*/
831 FIXME("Send character not supported yet\n");
832 SET_AH( context, 0x00 );/*Timeout*/
833 break;
834 case 0x01: /* PRINTER - INITIALIZE */
835 FIXME("Initialize Printer - Not Supported\n");
836 SET_AH( context, 0x30 ); /* selected | out of paper */
837 break;
838 case 0x02: /* PRINTER - GET STATUS */
839 FIXME("Get Printer Status - Not Supported\n");
840 break;
841 default:
842 SET_AH( context, 0 ); /* time out */
843 INT_BARF( context, 0x17 );
848 /**********************************************************************
849 * DOSVM_Int19Handler
851 * Handler for int 19h (Reboot).
853 static void WINAPI DOSVM_Int19Handler( CONTEXT *context )
855 TRACE( "Attempted Reboot\n" );
856 ExitProcess(0);
860 /**********************************************************************
861 * DOSVM_Int1aHandler
863 * Handler for int 1ah.
865 static void WINAPI DOSVM_Int1aHandler( CONTEXT *context )
867 switch(AH_reg(context))
869 case 0x00: /* GET SYSTEM TIME */
871 BIOSDATA *data = DOSVM_BiosData();
872 SET_CX( context, HIWORD(data->Ticks) );
873 SET_DX( context, LOWORD(data->Ticks) );
874 SET_AL( context, 0 ); /* FIXME: midnight flag is unsupported */
875 TRACE( "GET SYSTEM TIME - ticks=%d\n", data->Ticks );
877 break;
879 case 0x01: /* SET SYSTEM TIME */
880 FIXME( "SET SYSTEM TIME - not allowed\n" );
881 break;
883 case 0x02: /* GET REAL-TIME CLOCK TIME */
884 TRACE( "GET REAL-TIME CLOCK TIME\n" );
886 SYSTEMTIME systime;
887 GetLocalTime( &systime );
888 SET_CH( context, BIN_TO_BCD(systime.wHour) );
889 SET_CL( context, BIN_TO_BCD(systime.wMinute) );
890 SET_DH( context, BIN_TO_BCD(systime.wSecond) );
891 SET_DL( context, 0 ); /* FIXME: assume no daylight saving */
892 RESET_CFLAG(context);
894 break;
896 case 0x03: /* SET REAL-TIME CLOCK TIME */
897 FIXME( "SET REAL-TIME CLOCK TIME - not allowed\n" );
898 break;
900 case 0x04: /* GET REAL-TIME CLOCK DATE */
901 TRACE( "GET REAL-TIME CLOCK DATE\n" );
903 SYSTEMTIME systime;
904 GetLocalTime( &systime );
905 SET_CH( context, BIN_TO_BCD(systime.wYear / 100) );
906 SET_CL( context, BIN_TO_BCD(systime.wYear % 100) );
907 SET_DH( context, BIN_TO_BCD(systime.wMonth) );
908 SET_DL( context, BIN_TO_BCD(systime.wDay) );
909 RESET_CFLAG(context);
911 break;
913 case 0x05: /* SET REAL-TIME CLOCK DATE */
914 FIXME( "SET REAL-TIME CLOCK DATE - not allowed\n" );
915 break;
917 case 0x06: /* SET ALARM */
918 FIXME( "SET ALARM - unimplemented\n" );
919 break;
921 case 0x07: /* CANCEL ALARM */
922 FIXME( "CANCEL ALARM - unimplemented\n" );
923 break;
925 case 0x08: /* SET RTC ACTIVATED POWER ON MODE */
926 case 0x09: /* READ RTC ALARM TIME AND STATUS */
927 case 0x0a: /* READ SYSTEM-TIMER DAY COUNTER */
928 case 0x0b: /* SET SYSTEM-TIMER DAY COUNTER */
929 case 0x0c: /* SET RTC DATE/TIME ACTIVATED POWER-ON MODE */
930 case 0x0d: /* RESET RTC DATE/TIME ACTIVATED POWER-ON MODE */
931 case 0x0e: /* GET RTC DATE/TIME ALARM AND STATUS */
932 case 0x0f: /* INITIALIZE REAL-TIME CLOCK */
933 INT_BARF( context, 0x1a );
934 break;
936 case 0xb0:
937 if (CX_reg(context) == 0x4d52 &&
938 DX_reg(context) == 0x4349 &&
939 AL_reg(context) == 0x01)
942 * Microsoft Real-Time Compression Interface (MRCI).
943 * Ignoring this call indicates MRCI is not supported.
945 TRACE( "Microsoft Real-Time Compression Interface - not supported\n" );
947 else
949 INT_BARF(context, 0x1a);
951 break;
953 default:
954 INT_BARF( context, 0x1a );
959 /**********************************************************************
960 * DOSVM_Int20Handler
962 * Handler for int 20h.
964 static void WINAPI DOSVM_Int20Handler( CONTEXT *context )
966 if (DOSVM_IsWin16())
967 DOSVM_Exit( 0 );
968 else if(ISV86(context))
969 MZ_Exit( context, TRUE, 0 );
970 else
971 ERR( "Called from DOS protected mode\n" );
975 /**********************************************************************
976 * DOSVM_Int29Handler
978 * Handler for int 29h (fast console output)
980 static void WINAPI DOSVM_Int29Handler( CONTEXT *context )
982 /* Yes, it seems that this is really all this interrupt does. */
983 DOSVM_PutChar(AL_reg(context));
987 /**********************************************************************
988 * DOSVM_Int2aHandler
990 * Handler for int 2ah (network).
992 static void WINAPI DOSVM_Int2aHandler( CONTEXT *context )
994 switch(AH_reg(context))
996 case 0x00: /* NETWORK INSTALLATION CHECK */
997 break;
999 default:
1000 INT_BARF( context, 0x2a );
1005 /***********************************************************************
1006 * DOSVM_Int41Handler
1008 static void WINAPI DOSVM_Int41Handler( CONTEXT *context )
1010 if ( ISV86(context) )
1012 /* Real-mode debugger services */
1013 switch ( AX_reg(context) )
1015 default:
1016 INT_BARF( context, 0x41 );
1017 break;
1020 else
1022 /* Protected-mode debugger services */
1023 switch ( AX_reg(context) )
1025 case 0x4f:
1026 case 0x50:
1027 case 0x150:
1028 case 0x51:
1029 case 0x52:
1030 case 0x152:
1031 case 0x59:
1032 case 0x5a:
1033 case 0x5b:
1034 case 0x5c:
1035 case 0x5d:
1036 /* Notifies the debugger of a lot of stuff. We simply ignore it
1037 for now, but some of the info might actually be useful ... */
1038 break;
1040 default:
1041 INT_BARF( context, 0x41 );
1042 break;
1048 /***********************************************************************
1049 * DOSVM_Int4bHandler
1052 static void WINAPI DOSVM_Int4bHandler( CONTEXT *context )
1054 switch(AH_reg(context))
1056 case 0x81: /* Virtual DMA Spec (IBM SCSI interface) */
1057 if(AL_reg(context) != 0x02) /* if not install check */
1059 SET_CFLAG(context);
1060 SET_AL( context, 0x0f ); /* function is not implemented */
1062 break;
1063 default:
1064 INT_BARF(context, 0x4b);
1069 /***********************************************************************
1070 * DOSVM_Int5cHandler
1072 * Called from NetBIOSCall16.
1074 static void WINAPI DOSVM_Int5cHandler( CONTEXT *context )
1076 BYTE* ptr;
1077 ptr = MapSL( MAKESEGPTR(context->SegEs,BX_reg(context)) );
1078 FIXME("(%p): command code %02x (ignored)\n",context, *ptr);
1079 *(ptr+0x01) = 0xFB; /* NetBIOS emulator not found */
1080 SET_AL( context, 0xFB );