winedos: Release the 16-bit lock before calling ExitThread.
[wine/hacks.git] / dlls / winedos / interrupts.c
blob81cb2bb0e75b204d917cbf99ebc6b9751d763ae2
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 "dosexe.h"
26 #include "winternl.h"
27 #include "wine/debug.h"
28 #include "wine/winbase16.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(int);
31 WINE_DECLARE_DEBUG_CHANNEL(relay);
33 #define BCD_TO_BIN(x) ((x&15) + (x>>4)*10)
34 #define BIN_TO_BCD(x) ((x%10) + ((x/10)<<4))
36 static void WINAPI DOSVM_Int11Handler(CONTEXT86*);
37 static void WINAPI DOSVM_Int12Handler(CONTEXT86*);
38 static void WINAPI DOSVM_Int17Handler(CONTEXT86*);
39 static void WINAPI DOSVM_Int19Handler(CONTEXT86*);
40 static void WINAPI DOSVM_Int1aHandler(CONTEXT86*);
41 static void WINAPI DOSVM_Int20Handler(CONTEXT86*);
42 static void WINAPI DOSVM_Int29Handler(CONTEXT86*);
43 static void WINAPI DOSVM_Int2aHandler(CONTEXT86*);
44 static void WINAPI DOSVM_Int41Handler(CONTEXT86*);
45 static void WINAPI DOSVM_Int4bHandler(CONTEXT86*);
46 static void WINAPI DOSVM_Int5cHandler(CONTEXT86*);
47 static void WINAPI DOSVM_DefaultHandler(CONTEXT86*);
49 static FARPROC16 DOSVM_Vectors16[256];
50 static FARPROC48 DOSVM_Vectors48[256];
51 static const INTPROC DOSVM_VectorsBuiltin[] =
53 /* 00 */ 0, 0, 0, 0,
54 /* 04 */ 0, 0, 0, 0,
55 /* 08 */ DOSVM_Int08Handler, DOSVM_Int09Handler, 0, 0,
56 /* 0C */ 0, 0, 0, 0,
57 /* 10 */ DOSVM_Int10Handler, DOSVM_Int11Handler, DOSVM_Int12Handler, DOSVM_Int13Handler,
58 /* 14 */ 0, DOSVM_Int15Handler, DOSVM_Int16Handler, DOSVM_Int17Handler,
59 /* 18 */ 0, DOSVM_Int19Handler, DOSVM_Int1aHandler, 0,
60 /* 1C */ 0, 0, 0, 0,
61 /* 20 */ DOSVM_Int20Handler, DOSVM_Int21Handler, 0, 0,
62 /* 24 */ 0, DOSVM_Int25Handler, DOSVM_Int26Handler, 0,
63 /* 28 */ 0, DOSVM_Int29Handler, DOSVM_Int2aHandler, 0,
64 /* 2C */ 0, 0, 0, DOSVM_Int2fHandler,
65 /* 30 */ 0, DOSVM_Int31Handler, 0, DOSVM_Int33Handler,
66 /* 34 */ DOSVM_Int34Handler, DOSVM_Int35Handler, DOSVM_Int36Handler, DOSVM_Int37Handler,
67 /* 38 */ DOSVM_Int38Handler, DOSVM_Int39Handler, DOSVM_Int3aHandler, DOSVM_Int3bHandler,
68 /* 3C */ DOSVM_Int3cHandler, DOSVM_Int3dHandler, DOSVM_Int3eHandler, 0,
69 /* 40 */ 0, DOSVM_Int41Handler, 0, 0,
70 /* 44 */ 0, 0, 0, 0,
71 /* 48 */ 0, 0, 0, DOSVM_Int4bHandler,
72 /* 4C */ 0, 0, 0, 0,
73 /* 50 */ 0, 0, 0, 0,
74 /* 54 */ 0, 0, 0, 0,
75 /* 58 */ 0, 0, 0, 0,
76 /* 5C */ DOSVM_Int5cHandler, 0, 0, 0,
77 /* 60 */ 0, 0, 0, 0,
78 /* 64 */ 0, 0, 0, DOSVM_Int67Handler,
79 /* 68 */ DOSVM_DefaultHandler
84 * Sizes of real mode and protected mode interrupt stubs.
86 #define DOSVM_STUB_RM 4
87 #define DOSVM_STUB_PM16 5
88 #define DOSVM_STUB_PM48 6
91 /**********************************************************************
92 * DOSVM_GetRMVector
94 * Return pointer to real mode interrupt vector. These are not at fixed
95 * location because those Win16 programs that do not use any real mode
96 * code have protected NULL pointer catching block at low linear memory
97 * and interrupt vectors have been moved to another location.
99 static FARPROC16* DOSVM_GetRMVector( BYTE intnum )
101 LDT_ENTRY entry;
102 FARPROC16 proc;
104 proc = GetProcAddress16( GetModuleHandle16( "KERNEL" ),
105 (LPCSTR)(ULONG_PTR)183 );
106 wine_ldt_get_entry( LOWORD(proc), &entry );
108 return (FARPROC16*)wine_ldt_get_base( &entry ) + intnum;
112 /**********************************************************************
113 * DOSVM_IsIRQ
115 * Return TRUE if interrupt is an IRQ.
117 static BOOL DOSVM_IsIRQ( BYTE intnum )
119 if (intnum >= 0x08 && intnum <= 0x0f)
120 return TRUE;
122 if (intnum >= 0x70 && intnum <= 0x77)
123 return TRUE;
125 return FALSE;
129 /**********************************************************************
130 * DOSVM_DefaultHandler
132 * Default interrupt handler. This will be used to emulate all
133 * interrupts that don't have their own interrupt handler.
135 static void WINAPI DOSVM_DefaultHandler( CONTEXT86 *context )
140 /**********************************************************************
141 * DOSVM_GetBuiltinHandler
143 * Return Wine interrupt handler procedure for a given interrupt.
145 static INTPROC DOSVM_GetBuiltinHandler( BYTE intnum )
147 if (intnum < sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) {
148 INTPROC proc = DOSVM_VectorsBuiltin[intnum];
149 if (proc)
150 return proc;
153 WARN("int%x not implemented, returning dummy handler\n", intnum );
155 if (DOSVM_IsIRQ(intnum))
156 return DOSVM_AcknowledgeIRQ;
158 return DOSVM_DefaultHandler;
162 /**********************************************************************
163 * DOSVM_IntProcRelay
165 * Simple DOSRELAY that interprets its argument as INTPROC and calls it.
167 static void DOSVM_IntProcRelay( CONTEXT86 *context, LPVOID data )
169 INTPROC proc = (INTPROC)data;
170 proc(context);
174 /**********************************************************************
175 * DOSVM_PrepareIRQ
178 static void DOSVM_PrepareIRQ( CONTEXT86 *context, BOOL isbuiltin )
180 /* Disable virtual interrupts. */
181 get_vm86_teb_info()->dpmi_vif = 0;
183 if (!isbuiltin)
185 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
186 context->SegSs,
187 context->Esp);
189 /* Push return address to stack. */
190 *(--stack) = context->SegCs;
191 *(--stack) = context->Eip;
192 context->Esp += -8;
194 /* Jump to enable interrupts stub. */
195 context->SegCs = DOSVM_dpmi_segments->relay_code_sel;
196 context->Eip = 5;
201 /**********************************************************************
202 * DOSVM_PushFlags
204 * This routine is used to make default int25 and int26 handlers leave the
205 * original eflags into stack. In order to do this, stack is manipulated
206 * so that it actually contains two copies of eflags, one of which is
207 * popped during return from interrupt handler.
209 static void DOSVM_PushFlags( CONTEXT86 *context, BOOL islong, BOOL isstub )
211 if (islong)
213 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
214 context->SegSs,
215 context->Esp);
216 context->Esp += -4; /* One item will be added to stack. */
218 if (isstub)
220 DWORD ip = stack[0];
221 DWORD cs = stack[1];
222 stack += 2; /* Pop ip and cs. */
223 *(--stack) = context->EFlags;
224 *(--stack) = cs;
225 *(--stack) = ip;
227 else
228 *(--stack) = context->EFlags;
230 else
232 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
233 context->SegSs,
234 context->Esp);
235 ADD_LOWORD( context->Esp, -2 ); /* One item will be added to stack. */
237 if (isstub)
239 WORD ip = stack[0];
240 WORD cs = stack[1];
241 stack += 2; /* Pop ip and cs. */
242 *(--stack) = LOWORD(context->EFlags);
243 *(--stack) = cs;
244 *(--stack) = ip;
246 else
247 *(--stack) = LOWORD(context->EFlags);
252 /**********************************************************************
253 * DOSVM_EmulateInterruptPM
255 * Emulate software interrupt in 16-bit or 32-bit protected mode.
256 * Called from signal handler when intXX opcode is executed.
258 * Pushes interrupt frame to stack and changes instruction
259 * pointer to interrupt handler.
261 BOOL WINAPI DOSVM_EmulateInterruptPM( CONTEXT86 *context, BYTE intnum )
263 TRACE_(relay)("Call DOS int 0x%02x ret=%04x:%08x\n"
264 " eax=%08x ebx=%08x ecx=%08x edx=%08x\n"
265 " esi=%08x edi=%08x ebp=%08x esp=%08x\n"
266 " ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n",
267 intnum, context->SegCs, context->Eip,
268 context->Eax, context->Ebx, context->Ecx, context->Edx,
269 context->Esi, context->Edi, context->Ebp, context->Esp,
270 context->SegDs, context->SegEs, context->SegFs, context->SegGs,
271 context->SegSs, context->EFlags );
273 if (context->SegCs == DOSVM_dpmi_segments->dpmi_sel)
275 DOSVM_BuildCallFrame( context,
276 DOSVM_IntProcRelay,
277 DOSVM_RawModeSwitchHandler );
279 else if (context->SegCs == DOSVM_dpmi_segments->relay_code_sel)
282 * This must not be called using DOSVM_BuildCallFrame.
284 DOSVM_RelayHandler( context );
286 else if (context->SegCs == DOSVM_dpmi_segments->int48_sel)
288 /* Restore original flags stored into the stack by the caller. */
289 DWORD *stack = CTX_SEG_OFF_TO_LIN(context,
290 context->SegSs, context->Esp);
291 context->EFlags = stack[2];
293 if (intnum != context->Eip / DOSVM_STUB_PM48)
294 WARN( "interrupt stub has been modified "
295 "(interrupt is %02x, interrupt stub is %02x)\n",
296 intnum, context->Eip/DOSVM_STUB_PM48 );
298 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
300 if (intnum == 0x25 || intnum == 0x26)
301 DOSVM_PushFlags( context, TRUE, TRUE );
303 DOSVM_BuildCallFrame( context,
304 DOSVM_IntProcRelay,
305 DOSVM_GetBuiltinHandler(intnum) );
307 else if (context->SegCs == DOSVM_dpmi_segments->int16_sel)
309 /* Restore original flags stored into the stack by the caller. */
310 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
311 context->SegSs, context->Esp);
312 context->EFlags = (DWORD)MAKELONG( stack[2], HIWORD(context->EFlags) );
314 if (intnum != context->Eip / DOSVM_STUB_PM16)
315 WARN( "interrupt stub has been modified "
316 "(interrupt is %02x, interrupt stub is %02x)\n",
317 intnum, context->Eip/DOSVM_STUB_PM16 );
319 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
321 if (intnum == 0x25 || intnum == 0x26)
322 DOSVM_PushFlags( context, FALSE, TRUE );
324 DOSVM_BuildCallFrame( context,
325 DOSVM_IntProcRelay,
326 DOSVM_GetBuiltinHandler(intnum) );
328 else if (wine_ldt_is_system(context->SegCs))
330 INTPROC proc;
331 if (intnum >= sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) return FALSE;
332 if (!(proc = DOSVM_VectorsBuiltin[intnum])) return FALSE;
333 proc( context );
335 else
337 DOSVM_HardwareInterruptPM( context, intnum );
339 return TRUE;
343 /**********************************************************************
344 * DOSVM_HardwareInterruptPM
346 * Emulate call to interrupt handler in 16-bit or 32-bit protected mode.
348 * Pushes interrupt frame to stack and changes instruction
349 * pointer to interrupt handler.
351 void DOSVM_HardwareInterruptPM( CONTEXT86 *context, BYTE intnum )
353 if(DOSVM_IsDos32())
355 FARPROC48 addr = DOSVM_GetPMHandler48( intnum );
357 if (addr.selector == DOSVM_dpmi_segments->int48_sel)
359 TRACE( "builtin interrupt %02x has been invoked "
360 "(through vector %02x)\n",
361 addr.offset / DOSVM_STUB_PM48, intnum );
363 if (intnum == 0x25 || intnum == 0x26)
364 DOSVM_PushFlags( context, TRUE, FALSE );
365 else if (DOSVM_IsIRQ(intnum))
366 DOSVM_PrepareIRQ( context, TRUE );
368 DOSVM_BuildCallFrame( context,
369 DOSVM_IntProcRelay,
370 DOSVM_GetBuiltinHandler(
371 addr.offset/DOSVM_STUB_PM48 ) );
373 else
375 DWORD *stack;
377 TRACE( "invoking hooked interrupt %02x at %04x:%08x\n",
378 intnum, addr.selector, addr.offset );
380 if (DOSVM_IsIRQ(intnum))
381 DOSVM_PrepareIRQ( context, FALSE );
383 /* Push the flags and return address on the stack */
384 stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
385 *(--stack) = context->EFlags;
386 *(--stack) = context->SegCs;
387 *(--stack) = context->Eip;
388 context->Esp += -12;
390 /* Jump to the interrupt handler */
391 context->SegCs = addr.selector;
392 context->Eip = addr.offset;
395 else
397 FARPROC16 addr = DOSVM_GetPMHandler16( intnum );
399 if (SELECTOROF(addr) == DOSVM_dpmi_segments->int16_sel)
401 TRACE( "builtin interrupt %02x has been invoked "
402 "(through vector %02x)\n",
403 OFFSETOF(addr)/DOSVM_STUB_PM16, intnum );
405 if (intnum == 0x25 || intnum == 0x26)
406 DOSVM_PushFlags( context, FALSE, FALSE );
407 else if (DOSVM_IsIRQ(intnum))
408 DOSVM_PrepareIRQ( context, TRUE );
410 DOSVM_BuildCallFrame( context,
411 DOSVM_IntProcRelay,
412 DOSVM_GetBuiltinHandler(
413 OFFSETOF(addr)/DOSVM_STUB_PM16 ) );
415 else
417 TRACE( "invoking hooked interrupt %02x at %04x:%04x\n",
418 intnum, SELECTOROF(addr), OFFSETOF(addr) );
420 if (DOSVM_IsIRQ(intnum))
421 DOSVM_PrepareIRQ( context, FALSE );
423 /* Push the flags and return address on the stack */
424 PUSH_WORD16( context, LOWORD(context->EFlags) );
425 PUSH_WORD16( context, context->SegCs );
426 PUSH_WORD16( context, LOWORD(context->Eip) );
428 /* Jump to the interrupt handler */
429 context->SegCs = HIWORD(addr);
430 context->Eip = LOWORD(addr);
436 /**********************************************************************
437 * DOSVM_EmulateInterruptRM
439 * Emulate software interrupt in real mode.
440 * Called from VM86 emulation when intXX opcode is executed.
442 * Either calls directly builtin handler or pushes interrupt frame to
443 * stack and changes instruction pointer to interrupt handler.
445 * Returns FALSE if this interrupt was caused by return
446 * from real mode wrapper.
448 BOOL WINAPI DOSVM_EmulateInterruptRM( CONTEXT86 *context, BYTE intnum )
450 TRACE_(relay)("Call DOS int 0x%02x ret=%04x:%08x\n"
451 " eax=%08x ebx=%08x ecx=%08x edx=%08x\n"
452 " esi=%08x edi=%08x ebp=%08x esp=%08x\n"
453 " ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n",
454 intnum, context->SegCs, context->Eip,
455 context->Eax, context->Ebx, context->Ecx, context->Edx,
456 context->Esi, context->Edi, context->Ebp, context->Esp,
457 context->SegDs, context->SegEs, context->SegFs, context->SegGs,
458 context->SegSs, context->EFlags );
460 /* check for our real-mode hooks */
461 if (intnum == 0x31)
463 /* is this exit from real-mode wrapper */
464 if (context->SegCs == DOSVM_dpmi_segments->wrap_seg)
465 return FALSE;
467 if (DOSVM_CheckWrappers( context ))
468 return TRUE;
471 /* check if the call is from our fake BIOS interrupt stubs */
472 if (context->SegCs==0xf000)
474 /* Restore original flags stored into the stack by the caller. */
475 WORD *stack = CTX_SEG_OFF_TO_LIN(context,
476 context->SegSs, context->Esp);
477 context->EFlags = (DWORD)MAKELONG( stack[2], HIWORD(context->EFlags) );
479 if (intnum != context->Eip / DOSVM_STUB_RM)
480 WARN( "interrupt stub has been modified "
481 "(interrupt is %02x, interrupt stub is %02x)\n",
482 intnum, context->Eip/DOSVM_STUB_RM );
484 TRACE( "builtin interrupt %02x has been branched to\n", intnum );
486 DOSVM_CallBuiltinHandler( context, intnum );
488 /* Real mode stubs use IRET so we must put flags back into stack. */
489 stack[2] = LOWORD(context->EFlags);
491 else
493 DOSVM_HardwareInterruptRM( context, intnum );
496 return TRUE;
500 /**********************************************************************
501 * DOSVM_HardwareInterruptRM
503 * Emulate call to interrupt handler in real mode.
505 * Either calls directly builtin handler or pushes interrupt frame to
506 * stack and changes instruction pointer to interrupt handler.
508 void DOSVM_HardwareInterruptRM( CONTEXT86 *context, BYTE intnum )
510 FARPROC16 handler = DOSVM_GetRMHandler( intnum );
512 /* check if the call goes to an unhooked interrupt */
513 if (SELECTOROF(handler) == 0xf000)
515 /* if so, call it directly */
516 TRACE( "builtin interrupt %02x has been invoked "
517 "(through vector %02x)\n",
518 OFFSETOF(handler)/DOSVM_STUB_RM, intnum );
519 DOSVM_CallBuiltinHandler( context, OFFSETOF(handler)/DOSVM_STUB_RM );
521 else
523 /* the interrupt is hooked, simulate interrupt in DOS space */
524 WORD flag = LOWORD( context->EFlags );
526 TRACE( "invoking hooked interrupt %02x at %04x:%04x\n",
527 intnum, SELECTOROF(handler), OFFSETOF(handler) );
529 /* Copy virtual interrupt flag to pushed interrupt flag. */
530 if (context->EFlags & VIF_MASK)
531 flag |= IF_MASK;
532 else
533 flag &= ~IF_MASK;
535 PUSH_WORD16( context, flag );
536 PUSH_WORD16( context, context->SegCs );
537 PUSH_WORD16( context, LOWORD( context->Eip ));
539 context->SegCs = SELECTOROF( handler );
540 context->Eip = OFFSETOF( handler );
542 /* Clear virtual interrupt flag and trap flag. */
543 context->EFlags &= ~(VIF_MASK | TF_MASK);
548 /**********************************************************************
549 * DOSVM_GetRMHandler
551 * Return the real mode interrupt vector for a given interrupt.
553 FARPROC16 DOSVM_GetRMHandler( BYTE intnum )
555 return *DOSVM_GetRMVector( intnum );
559 /**********************************************************************
560 * DOSVM_SetRMHandler
562 * Set the real mode interrupt handler for a given interrupt.
564 void DOSVM_SetRMHandler( BYTE intnum, FARPROC16 handler )
566 TRACE("Set real mode interrupt vector %02x <- %04x:%04x\n",
567 intnum, HIWORD(handler), LOWORD(handler) );
568 *DOSVM_GetRMVector( intnum ) = handler;
572 /**********************************************************************
573 * DOSVM_GetPMHandler16
575 * Return the protected mode interrupt vector for a given interrupt.
577 FARPROC16 DOSVM_GetPMHandler16( BYTE intnum )
579 TDB *pTask;
580 FARPROC16 proc = 0;
582 pTask = GlobalLock16(GetCurrentTask());
583 if (pTask)
585 switch( intnum )
587 case 0x00:
588 proc = pTask->int0;
589 break;
590 case 0x02:
591 proc = pTask->int2;
592 break;
593 case 0x04:
594 proc = pTask->int4;
595 break;
596 case 0x06:
597 proc = pTask->int6;
598 break;
599 case 0x07:
600 proc = pTask->int7;
601 break;
602 case 0x3e:
603 proc = pTask->int3e;
604 break;
605 case 0x75:
606 proc = pTask->int75;
607 break;
609 if( proc )
610 return proc;
612 if (!DOSVM_Vectors16[intnum])
614 proc = (FARPROC16)MAKESEGPTR( DOSVM_dpmi_segments->int16_sel,
615 DOSVM_STUB_PM16 * intnum );
616 DOSVM_Vectors16[intnum] = proc;
618 return DOSVM_Vectors16[intnum];
622 /**********************************************************************
623 * DOSVM_SetPMHandler16
625 * Set the protected mode interrupt handler for a given interrupt.
627 void DOSVM_SetPMHandler16( BYTE intnum, FARPROC16 handler )
629 TDB *pTask;
631 TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
632 intnum, HIWORD(handler), LOWORD(handler) );
634 pTask = GlobalLock16(GetCurrentTask());
635 if (!pTask)
636 return;
637 switch( intnum )
639 case 0x00:
640 pTask->int0 = handler;
641 break;
642 case 0x02:
643 pTask->int2 = handler;
644 break;
645 case 0x04:
646 pTask->int4 = handler;
647 break;
648 case 0x06:
649 pTask->int6 = handler;
650 break;
651 case 0x07:
652 pTask->int7 = handler;
653 break;
654 case 0x3e:
655 pTask->int3e = handler;
656 break;
657 case 0x75:
658 pTask->int75 = handler;
659 break;
660 default:
661 DOSVM_Vectors16[intnum] = handler;
662 break;
667 /**********************************************************************
668 * DOSVM_GetPMHandler48
670 * Return the protected mode interrupt vector for a given interrupt.
671 * Used to get 48-bit pointer for 32-bit interrupt handlers in DPMI32.
673 FARPROC48 DOSVM_GetPMHandler48( BYTE intnum )
675 if (!DOSVM_Vectors48[intnum].selector)
677 DOSVM_Vectors48[intnum].selector = DOSVM_dpmi_segments->int48_sel;
678 DOSVM_Vectors48[intnum].offset = DOSVM_STUB_PM48 * intnum;
680 return DOSVM_Vectors48[intnum];
684 /**********************************************************************
685 * DOSVM_SetPMHandler48
687 * Set the protected mode interrupt handler for a given interrupt.
688 * Used to set 48-bit pointer for 32-bit interrupt handlers in DPMI32.
690 void DOSVM_SetPMHandler48( BYTE intnum, FARPROC48 handler )
692 TRACE("Set 32-bit protected mode interrupt vector %02x <- %04x:%08x\n",
693 intnum, handler.selector, handler.offset );
694 DOSVM_Vectors48[intnum] = handler;
698 /**********************************************************************
699 * DOSVM_CallBuiltinHandler
701 * Execute Wine interrupt handler procedure.
703 void WINAPI DOSVM_CallBuiltinHandler( CONTEXT86 *context, BYTE intnum )
706 * FIXME: Make all builtin interrupt calls go via this routine.
707 * FIXME: Check for PM->RM interrupt reflection.
708 * FIXME: Check for RM->PM interrupt reflection.
711 INTPROC proc = DOSVM_GetBuiltinHandler( intnum );
712 proc( context );
716 /**********************************************************************
717 * DOSVM_Int11Handler
719 * Handler for int 11h (get equipment list).
722 * Borrowed from Ralph Brown's interrupt lists:
724 * bits 15-14: number of parallel devices
725 * bit 13: [Conv] Internal modem
726 * bit 12: reserved
727 * bits 11- 9: number of serial devices
728 * bit 8: reserved
729 * bits 7- 6: number of diskette drives minus one
730 * bits 5- 4: Initial video mode:
731 * 00b = EGA,VGA,PGA
732 * 01b = 40 x 25 color
733 * 10b = 80 x 25 color
734 * 11b = 80 x 25 mono
735 * bit 3: reserved
736 * bit 2: [PS] =1 if pointing device
737 * [non-PS] reserved
738 * bit 1: =1 if math co-processor
739 * bit 0: =1 if diskette available for boot
742 * Currently the only of these bits correctly set are:
744 * bits 15-14 } Added by William Owen Smith,
745 * bits 11-9 } wos@dcs.warwick.ac.uk
746 * bits 7-6
747 * bit 2 (always set) ( bit 2 = 4 )
748 * bit 1 } Robert 'Admiral' Coeyman
749 * All *nix systems either have a math processor or
750 * emulate one.
752 static void WINAPI DOSVM_Int11Handler( CONTEXT86 *context )
754 int diskdrives = 0;
755 int parallelports = 0;
756 int serialports = 0;
757 int x;
759 if (GetDriveTypeA("A:\\") == DRIVE_REMOVABLE) diskdrives++;
760 if (GetDriveTypeA("B:\\") == DRIVE_REMOVABLE) diskdrives++;
761 if (diskdrives) diskdrives--;
763 for (x=0; x < 9; x++)
765 HANDLE handle;
766 char file[10];
768 /* serial port name */
769 sprintf( file, "\\\\.\\COM%d", x+1 );
770 handle = CreateFileA( file, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
771 if (handle != INVALID_HANDLE_VALUE)
773 CloseHandle( handle );
774 serialports++;
777 sprintf( file, "\\\\.\\LPT%d", x+1 );
778 handle = CreateFileA( file, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
779 if (handle != INVALID_HANDLE_VALUE)
781 CloseHandle( handle );
782 parallelports++;
786 if (serialports > 7) /* 3 bits -- maximum value = 7 */
787 serialports = 7;
789 if (parallelports > 3) /* 2 bits -- maximum value = 3 */
790 parallelports = 3;
792 SET_AX( context,
793 (diskdrives << 6) | (serialports << 9) | (parallelports << 14) | 0x06 );
797 /**********************************************************************
798 * DOSVM_Int12Handler
800 * Handler for int 12h (get memory size).
802 static void WINAPI DOSVM_Int12Handler( CONTEXT86 *context )
804 SET_AX( context, 640 );
808 /**********************************************************************
809 * DOSVM_Int17Handler
811 * Handler for int 17h (printer - output character).
813 static void WINAPI DOSVM_Int17Handler( CONTEXT86 *context )
815 switch( AH_reg(context) )
817 case 0x00:/* Send character*/
818 FIXME("Send character not supported yet\n");
819 SET_AH( context, 0x00 );/*Timeout*/
820 break;
821 case 0x01: /* PRINTER - INITIALIZE */
822 FIXME("Initialize Printer - Not Supported\n");
823 SET_AH( context, 0x30 ); /* selected | out of paper */
824 break;
825 case 0x02: /* PRINTER - GET STATUS */
826 FIXME("Get Printer Status - Not Supported\n");
827 break;
828 default:
829 SET_AH( context, 0 ); /* time out */
830 INT_BARF( context, 0x17 );
835 /**********************************************************************
836 * DOSVM_Int19Handler
838 * Handler for int 19h (Reboot).
840 static void WINAPI DOSVM_Int19Handler( CONTEXT86 *context )
842 TRACE( "Attempted Reboot\n" );
843 ExitProcess(0);
847 /**********************************************************************
848 * DOSVM_Int1aHandler
850 * Handler for int 1ah.
852 static void WINAPI DOSVM_Int1aHandler( CONTEXT86 *context )
854 switch(AH_reg(context))
856 case 0x00: /* GET SYSTEM TIME */
858 BIOSDATA *data = DOSVM_BiosData();
859 SET_CX( context, HIWORD(data->Ticks) );
860 SET_DX( context, LOWORD(data->Ticks) );
861 SET_AL( context, 0 ); /* FIXME: midnight flag is unsupported */
862 TRACE( "GET SYSTEM TIME - ticks=%d\n", data->Ticks );
864 break;
866 case 0x01: /* SET SYSTEM TIME */
867 FIXME( "SET SYSTEM TIME - not allowed\n" );
868 break;
870 case 0x02: /* GET REAL-TIME CLOCK TIME */
871 TRACE( "GET REAL-TIME CLOCK TIME\n" );
873 SYSTEMTIME systime;
874 GetLocalTime( &systime );
875 SET_CH( context, BIN_TO_BCD(systime.wHour) );
876 SET_CL( context, BIN_TO_BCD(systime.wMinute) );
877 SET_DH( context, BIN_TO_BCD(systime.wSecond) );
878 SET_DL( context, 0 ); /* FIXME: assume no daylight saving */
879 RESET_CFLAG(context);
881 break;
883 case 0x03: /* SET REAL-TIME CLOCK TIME */
884 FIXME( "SET REAL-TIME CLOCK TIME - not allowed\n" );
885 break;
887 case 0x04: /* GET REAL-TIME CLOCK DATE */
888 TRACE( "GET REAL-TIME CLOCK DATE\n" );
890 SYSTEMTIME systime;
891 GetLocalTime( &systime );
892 SET_CH( context, BIN_TO_BCD(systime.wYear / 100) );
893 SET_CL( context, BIN_TO_BCD(systime.wYear % 100) );
894 SET_DH( context, BIN_TO_BCD(systime.wMonth) );
895 SET_DL( context, BIN_TO_BCD(systime.wDay) );
896 RESET_CFLAG(context);
898 break;
900 case 0x05: /* SET REAL-TIME CLOCK DATE */
901 FIXME( "SET REAL-TIME CLOCK DATE - not allowed\n" );
902 break;
904 case 0x06: /* SET ALARM */
905 FIXME( "SET ALARM - unimplemented\n" );
906 break;
908 case 0x07: /* CANCEL ALARM */
909 FIXME( "CANCEL ALARM - unimplemented\n" );
910 break;
912 case 0x08: /* SET RTC ACTIVATED POWER ON MODE */
913 case 0x09: /* READ RTC ALARM TIME AND STATUS */
914 case 0x0a: /* READ SYSTEM-TIMER DAY COUNTER */
915 case 0x0b: /* SET SYSTEM-TIMER DAY COUNTER */
916 case 0x0c: /* SET RTC DATE/TIME ACTIVATED POWER-ON MODE */
917 case 0x0d: /* RESET RTC DATE/TIME ACTIVATED POWER-ON MODE */
918 case 0x0e: /* GET RTC DATE/TIME ALARM AND STATUS */
919 case 0x0f: /* INITIALIZE REAL-TIME CLOCK */
920 INT_BARF( context, 0x1a );
921 break;
923 case 0xb0:
924 if (CX_reg(context) == 0x4d52 &&
925 DX_reg(context) == 0x4349 &&
926 AL_reg(context) == 0x01)
929 * Microsoft Real-Time Compression Interface (MRCI).
930 * Ignoring this call indicates MRCI is not supported.
932 TRACE( "Microsoft Real-Time Compression Interface - not supported\n" );
934 else
936 INT_BARF(context, 0x1a);
938 break;
940 default:
941 INT_BARF( context, 0x1a );
946 /**********************************************************************
947 * DOSVM_Int20Handler
949 * Handler for int 20h.
951 static void WINAPI DOSVM_Int20Handler( CONTEXT86 *context )
953 if (DOSVM_IsWin16())
954 DOSVM_Exit( 0 );
955 else if(ISV86(context))
956 MZ_Exit( context, TRUE, 0 );
957 else
958 ERR( "Called from DOS protected mode\n" );
962 /**********************************************************************
963 * DOSVM_Int29Handler
965 * Handler for int 29h (fast console output)
967 static void WINAPI DOSVM_Int29Handler( CONTEXT86 *context )
969 /* Yes, it seems that this is really all this interrupt does. */
970 DOSVM_PutChar(AL_reg(context));
974 /**********************************************************************
975 * DOSVM_Int2aHandler
977 * Handler for int 2ah (network).
979 static void WINAPI DOSVM_Int2aHandler( CONTEXT86 *context )
981 switch(AH_reg(context))
983 case 0x00: /* NETWORK INSTALLATION CHECK */
984 break;
986 default:
987 INT_BARF( context, 0x2a );
992 /***********************************************************************
993 * DOSVM_Int41Handler
995 static void WINAPI DOSVM_Int41Handler( CONTEXT86 *context )
997 if ( ISV86(context) )
999 /* Real-mode debugger services */
1000 switch ( AX_reg(context) )
1002 default:
1003 INT_BARF( context, 0x41 );
1004 break;
1007 else
1009 /* Protected-mode debugger services */
1010 switch ( AX_reg(context) )
1012 case 0x4f:
1013 case 0x50:
1014 case 0x150:
1015 case 0x51:
1016 case 0x52:
1017 case 0x152:
1018 case 0x59:
1019 case 0x5a:
1020 case 0x5b:
1021 case 0x5c:
1022 case 0x5d:
1023 /* Notifies the debugger of a lot of stuff. We simply ignore it
1024 for now, but some of the info might actually be useful ... */
1025 break;
1027 default:
1028 INT_BARF( context, 0x41 );
1029 break;
1035 /***********************************************************************
1036 * DOSVM_Int4bHandler
1039 static void WINAPI DOSVM_Int4bHandler( CONTEXT86 *context )
1041 switch(AH_reg(context))
1043 case 0x81: /* Virtual DMA Spec (IBM SCSI interface) */
1044 if(AL_reg(context) != 0x02) /* if not install check */
1046 SET_CFLAG(context);
1047 SET_AL( context, 0x0f ); /* function is not implemented */
1049 break;
1050 default:
1051 INT_BARF(context, 0x4b);
1056 /***********************************************************************
1057 * DOSVM_Int5cHandler
1059 * Called from NetBIOSCall16.
1061 static void WINAPI DOSVM_Int5cHandler( CONTEXT86 *context )
1063 BYTE* ptr;
1064 ptr = MapSL( MAKESEGPTR(context->SegEs,BX_reg(context)) );
1065 FIXME("(%p): command code %02x (ignored)\n",context, *ptr);
1066 *(ptr+0x01) = 0xFB; /* NetBIOS emulator not found */
1067 SET_AL( context, 0xFB );