d3dx8: Implement D3DXMatrixRotationY.
[wine] / dlls / winedos / interrupts.c
1 /*
2  * Interrupt emulation
3  *
4  * Copyright 2002 Jukka Heinonen
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24
25 #include "dosexe.h"
26 #include "wine/debug.h"
27 #include "wine/winbase16.h"
28
29 #include "thread.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(int);
32 WINE_DECLARE_DEBUG_CHANNEL(relay);
33
34 #define BCD_TO_BIN(x) ((x&15) + (x>>4)*10)
35 #define BIN_TO_BCD(x) ((x%10) + ((x/10)<<4))
36
37 static void WINAPI DOSVM_Int11Handler(CONTEXT86*);
38 static void WINAPI DOSVM_Int12Handler(CONTEXT86*);
39 static void WINAPI DOSVM_Int17Handler(CONTEXT86*);
40 static void WINAPI DOSVM_Int19Handler(CONTEXT86*);
41 static void WINAPI DOSVM_Int1aHandler(CONTEXT86*);
42 static void WINAPI DOSVM_Int20Handler(CONTEXT86*);
43 static void WINAPI DOSVM_Int29Handler(CONTEXT86*);
44 static void WINAPI DOSVM_Int2aHandler(CONTEXT86*);
45 static void WINAPI DOSVM_Int41Handler(CONTEXT86*);
46 static void WINAPI DOSVM_Int4bHandler(CONTEXT86*);
47 static void WINAPI DOSVM_Int5cHandler(CONTEXT86*);
48
49 static FARPROC16     DOSVM_Vectors16[256];
50 static FARPROC48     DOSVM_Vectors48[256];
51 static const INTPROC DOSVM_VectorsBuiltin[] =
52 {
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 };
80
81
82 /*
83  * Sizes of real mode and protected mode interrupt stubs.
84  */
85 #define DOSVM_STUB_RM   4
86 #define DOSVM_STUB_PM16 5
87 #define DOSVM_STUB_PM48 6
88
89
90 /**********************************************************************
91  *         DOSVM_GetRMVector
92  *
93  * Return pointer to real mode interrupt vector. These are not at fixed 
94  * location because those Win16 programs that do not use any real mode 
95  * code have protected NULL pointer catching block at low linear memory 
96  * and interrupt vectors have been moved to another location.
97  */
98 static FARPROC16* DOSVM_GetRMVector( BYTE intnum )
99 {
100     LDT_ENTRY entry;
101     FARPROC16 proc;
102
103     proc = GetProcAddress16( GetModuleHandle16( "KERNEL" ), 
104                              (LPCSTR)(ULONG_PTR)183 );
105     wine_ldt_get_entry( LOWORD(proc), &entry );
106
107     return (FARPROC16*)wine_ldt_get_base( &entry ) + intnum;
108 }
109
110
111 /**********************************************************************
112  *         DOSVM_IsIRQ
113  *
114  * Return TRUE if interrupt is an IRQ.
115  */
116 static BOOL DOSVM_IsIRQ( BYTE intnum )
117 {
118     if (intnum >= 0x08 && intnum <= 0x0f)
119         return TRUE;
120
121     if (intnum >= 0x70 && intnum <= 0x77)
122         return TRUE;
123
124     return FALSE;
125 }
126
127
128 /**********************************************************************
129  *         DOSVM_DefaultHandler
130  *
131  * Default interrupt handler. This will be used to emulate all
132  * interrupts that don't have their own interrupt handler.
133  */
134 static void WINAPI DOSVM_DefaultHandler( CONTEXT86 *context )
135 {
136 }
137
138
139 /**********************************************************************
140  *         DOSVM_GetBuiltinHandler
141  *
142  * Return Wine interrupt handler procedure for a given interrupt.
143  */
144 static INTPROC DOSVM_GetBuiltinHandler( BYTE intnum )
145 {
146     if (intnum < sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) {
147         INTPROC proc = DOSVM_VectorsBuiltin[intnum];
148         if (proc)
149             return proc;
150     }
151
152     WARN("int%x not implemented, returning dummy handler\n", intnum );
153
154     if (DOSVM_IsIRQ(intnum))
155         return DOSVM_AcknowledgeIRQ;
156
157     return DOSVM_DefaultHandler;
158 }
159
160
161 /**********************************************************************
162  *          DOSVM_IntProcRelay
163  *
164  * Simple DOSRELAY that interprets its argument as INTPROC and calls it.
165  */
166 static void DOSVM_IntProcRelay( CONTEXT86 *context, LPVOID data )
167 {
168     INTPROC proc = (INTPROC)data;
169     proc(context);
170 }
171
172
173 /**********************************************************************
174  *          DOSVM_PrepareIRQ
175  *
176  */
177 static void DOSVM_PrepareIRQ( CONTEXT86 *context, BOOL isbuiltin )
178 {
179     /* Disable virtual interrupts. */
180     NtCurrentTeb()->dpmi_vif = 0;
181
182     if (!isbuiltin)
183     {
184         DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
185                                           context->SegSs,
186                                           context->Esp);
187
188         /* Push return address to stack. */
189         *(--stack) = context->SegCs;
190         *(--stack) = context->Eip;
191         context->Esp += -8;
192
193         /* Jump to enable interrupts stub. */
194         context->SegCs = DOSVM_dpmi_segments->relay_code_sel;
195         context->Eip   = 5;
196     }
197 }
198
199
200 /**********************************************************************
201  *          DOSVM_PushFlags
202  *
203  * This routine is used to make default int25 and int26 handlers leave the 
204  * original eflags into stack. In order to do this, stack is manipulated
205  * so that it actually contains two copies of eflags, one of which is
206  * popped during return from interrupt handler.
207  */
208 static void DOSVM_PushFlags( CONTEXT86 *context, BOOL islong, BOOL isstub )
209 {
210     if (islong)
211     {
212         DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
213                                           context->SegSs, 
214                                           context->Esp);
215         context->Esp += -4; /* One item will be added to stack. */
216
217         if (isstub)
218         {
219             DWORD ip = stack[0];
220             DWORD cs = stack[1];
221             stack += 2; /* Pop ip and cs. */
222             *(--stack) = context->EFlags;
223             *(--stack) = cs;
224             *(--stack) = ip;
225         }
226         else
227             *(--stack) = context->EFlags;            
228     }
229     else
230     {
231         WORD *stack = CTX_SEG_OFF_TO_LIN(context, 
232                                          context->SegSs, 
233                                          context->Esp);
234         ADD_LOWORD( context->Esp, -2 ); /* One item will be added to stack. */
235
236         if (isstub)
237         {
238             WORD ip = stack[0];
239             WORD cs = stack[1];
240             stack += 2; /* Pop ip and cs. */
241             *(--stack) = LOWORD(context->EFlags);
242             *(--stack) = cs;
243             *(--stack) = ip;
244         }
245         else
246             *(--stack) = LOWORD(context->EFlags);
247     }
248 }
249
250
251 /**********************************************************************
252  *         DOSVM_EmulateInterruptPM
253  *
254  * Emulate software interrupt in 16-bit or 32-bit protected mode.
255  * Called from signal handler when intXX opcode is executed. 
256  *
257  * Pushes interrupt frame to stack and changes instruction 
258  * pointer to interrupt handler.
259  */
260 void WINAPI DOSVM_EmulateInterruptPM( CONTEXT86 *context, BYTE intnum ) 
261 {
262     TRACE_(relay)("Call DOS int 0x%02x ret=%04x:%08x\n"
263                   "  eax=%08x ebx=%08x ecx=%08x edx=%08x\n"
264                   "  esi=%08x edi=%08x ebp=%08x esp=%08x \n"
265                   "  ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n",
266                   intnum, context->SegCs, context->Eip,
267                   context->Eax, context->Ebx, context->Ecx, context->Edx,
268                   context->Esi, context->Edi, context->Ebp, context->Esp,
269                   context->SegDs, context->SegEs, context->SegFs, context->SegGs,
270                   context->SegSs, context->EFlags );
271
272     if (context->SegCs == DOSVM_dpmi_segments->dpmi_sel)
273     {
274         DOSVM_BuildCallFrame( context, 
275                               DOSVM_IntProcRelay,
276                               DOSVM_RawModeSwitchHandler );
277     }
278     else if (context->SegCs == DOSVM_dpmi_segments->relay_code_sel)
279     {
280         /*
281          * This must not be called using DOSVM_BuildCallFrame.
282          */
283         DOSVM_RelayHandler( context );
284     }
285     else if (context->SegCs == DOSVM_dpmi_segments->int48_sel)
286     {
287         /* Restore original flags stored into the stack by the caller. */
288         DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
289                                           context->SegSs, context->Esp);
290         context->EFlags = stack[2];
291
292         if (intnum != context->Eip / DOSVM_STUB_PM48)
293             WARN( "interrupt stub has been modified "
294                   "(interrupt is %02x, interrupt stub is %02x)\n",
295                   intnum, context->Eip/DOSVM_STUB_PM48 );
296
297         TRACE( "builtin interrupt %02x has been branched to\n", intnum );
298
299         if (intnum == 0x25 || intnum == 0x26)
300             DOSVM_PushFlags( context, TRUE, TRUE );
301
302         DOSVM_BuildCallFrame( context, 
303                               DOSVM_IntProcRelay,
304                               DOSVM_GetBuiltinHandler(intnum) );
305     }
306     else if (context->SegCs == DOSVM_dpmi_segments->int16_sel)
307     {
308         /* Restore original flags stored into the stack by the caller. */
309         WORD *stack = CTX_SEG_OFF_TO_LIN(context, 
310                                          context->SegSs, context->Esp);
311         context->EFlags = (DWORD)MAKELONG( stack[2], HIWORD(context->EFlags) );
312
313         if (intnum != context->Eip / DOSVM_STUB_PM16)
314             WARN( "interrupt stub has been modified "
315                   "(interrupt is %02x, interrupt stub is %02x)\n",
316                   intnum, context->Eip/DOSVM_STUB_PM16 );
317
318         TRACE( "builtin interrupt %02x has been branched to\n", intnum );
319
320         if (intnum == 0x25 || intnum == 0x26)
321             DOSVM_PushFlags( context, FALSE, TRUE );
322
323         DOSVM_BuildCallFrame( context, 
324                               DOSVM_IntProcRelay, 
325                               DOSVM_GetBuiltinHandler(intnum) );
326     }
327     else
328     {
329         DOSVM_HardwareInterruptPM( context, intnum );
330     }
331 }
332
333
334 /**********************************************************************
335  *         DOSVM_HardwareInterruptPM
336  *
337  * Emulate call to interrupt handler in 16-bit or 32-bit protected mode.
338  *
339  * Pushes interrupt frame to stack and changes instruction 
340  * pointer to interrupt handler.
341  */
342 void DOSVM_HardwareInterruptPM( CONTEXT86 *context, BYTE intnum ) 
343 {
344     if(DOSVM_IsDos32())
345     {
346         FARPROC48 addr = DOSVM_GetPMHandler48( intnum );
347         
348         if (addr.selector == DOSVM_dpmi_segments->int48_sel)
349         {
350             TRACE( "builtin interrupt %02x has been invoked "
351                    "(through vector %02x)\n",
352                    addr.offset / DOSVM_STUB_PM48, intnum );
353
354             if (intnum == 0x25 || intnum == 0x26)
355                 DOSVM_PushFlags( context, TRUE, FALSE );
356             else if (DOSVM_IsIRQ(intnum))
357                 DOSVM_PrepareIRQ( context, TRUE );
358
359             DOSVM_BuildCallFrame( context,
360                                   DOSVM_IntProcRelay,
361                                   DOSVM_GetBuiltinHandler(
362                                       addr.offset/DOSVM_STUB_PM48 ) );
363         }
364         else
365         {
366             DWORD *stack;
367             
368             TRACE( "invoking hooked interrupt %02x at %04x:%08x\n",
369                    intnum, addr.selector, addr.offset );
370             
371             if (DOSVM_IsIRQ(intnum))
372                 DOSVM_PrepareIRQ( context, FALSE );
373
374             /* Push the flags and return address on the stack */
375             stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
376             *(--stack) = context->EFlags;
377             *(--stack) = context->SegCs;
378             *(--stack) = context->Eip;
379             context->Esp += -12;
380
381             /* Jump to the interrupt handler */
382             context->SegCs  = addr.selector;
383             context->Eip = addr.offset;
384         }
385     }
386     else
387     {
388         FARPROC16 addr = DOSVM_GetPMHandler16( intnum );
389
390         if (SELECTOROF(addr) == DOSVM_dpmi_segments->int16_sel)
391         {
392             TRACE( "builtin interrupt %02x has been invoked "
393                    "(through vector %02x)\n", 
394                    OFFSETOF(addr)/DOSVM_STUB_PM16, intnum );
395
396             if (intnum == 0x25 || intnum == 0x26)
397                 DOSVM_PushFlags( context, FALSE, FALSE );
398             else if (DOSVM_IsIRQ(intnum))
399                 DOSVM_PrepareIRQ( context, TRUE );
400
401             DOSVM_BuildCallFrame( context, 
402                                   DOSVM_IntProcRelay,
403                                   DOSVM_GetBuiltinHandler(
404                                       OFFSETOF(addr)/DOSVM_STUB_PM16 ) );
405         }
406         else
407         {
408             TRACE( "invoking hooked interrupt %02x at %04x:%04x\n", 
409                    intnum, SELECTOROF(addr), OFFSETOF(addr) );
410
411             if (DOSVM_IsIRQ(intnum))
412                 DOSVM_PrepareIRQ( context, FALSE );
413
414             /* Push the flags and return address on the stack */
415             PUSH_WORD16( context, LOWORD(context->EFlags) );
416             PUSH_WORD16( context, context->SegCs );
417             PUSH_WORD16( context, LOWORD(context->Eip) );
418
419             /* Jump to the interrupt handler */
420             context->SegCs =  HIWORD(addr);
421             context->Eip = LOWORD(addr);
422         }
423     }
424 }
425
426
427 /**********************************************************************
428  *         DOSVM_EmulateInterruptRM
429  *
430  * Emulate software interrupt in real mode.
431  * Called from VM86 emulation when intXX opcode is executed. 
432  *
433  * Either calls directly builtin handler or pushes interrupt frame to 
434  * stack and changes instruction pointer to interrupt handler.
435  *
436  * Returns FALSE if this interrupt was caused by return 
437  * from real mode wrapper.
438  */
439 BOOL WINAPI DOSVM_EmulateInterruptRM( CONTEXT86 *context, BYTE intnum ) 
440 {
441     TRACE_(relay)("Call DOS int 0x%02x ret=%04x:%08x\n"
442                   "  eax=%08x ebx=%08x ecx=%08x edx=%08x\n"
443                   "  esi=%08x edi=%08x ebp=%08x esp=%08x \n"
444                   "  ds=%04x es=%04x fs=%04x gs=%04x ss=%04x flags=%08x\n",
445                   intnum, context->SegCs, context->Eip,
446                   context->Eax, context->Ebx, context->Ecx, context->Edx,
447                   context->Esi, context->Edi, context->Ebp, context->Esp,
448                   context->SegDs, context->SegEs, context->SegFs, context->SegGs,
449                   context->SegSs, context->EFlags );
450
451     /* check for our real-mode hooks */
452     if (intnum == 0x31)
453     {
454         /* is this exit from real-mode wrapper */
455         if (context->SegCs == DOSVM_dpmi_segments->wrap_seg)
456             return FALSE;
457
458         if (DOSVM_CheckWrappers( context ))
459             return TRUE;
460     }
461
462     /* check if the call is from our fake BIOS interrupt stubs */
463     if (context->SegCs==0xf000)
464     {
465         /* Restore original flags stored into the stack by the caller. */
466         WORD *stack = CTX_SEG_OFF_TO_LIN(context, 
467                                          context->SegSs, context->Esp);
468         context->EFlags = (DWORD)MAKELONG( stack[2], HIWORD(context->EFlags) );
469
470         if (intnum != context->Eip / DOSVM_STUB_RM)
471             WARN( "interrupt stub has been modified "
472                   "(interrupt is %02x, interrupt stub is %02x)\n",
473                   intnum, context->Eip/DOSVM_STUB_RM );
474
475         TRACE( "builtin interrupt %02x has been branched to\n", intnum );
476         
477         DOSVM_CallBuiltinHandler( context, intnum );
478
479         /* Real mode stubs use IRET so we must put flags back into stack. */
480         stack[2] = LOWORD(context->EFlags);
481     }
482     else
483     {
484         DOSVM_HardwareInterruptRM( context, intnum );
485     }
486
487     return TRUE;
488 }
489
490
491 /**********************************************************************
492  *         DOSVM_HardwareInterruptRM
493  *
494  * Emulate call to interrupt handler in real mode.
495  *
496  * Either calls directly builtin handler or pushes interrupt frame to 
497  * stack and changes instruction pointer to interrupt handler.
498  */
499 void DOSVM_HardwareInterruptRM( CONTEXT86 *context, BYTE intnum ) 
500 {
501      FARPROC16 handler = DOSVM_GetRMHandler( intnum );
502
503      /* check if the call goes to an unhooked interrupt */
504      if (SELECTOROF(handler) == 0xf000) 
505      {
506          /* if so, call it directly */
507          TRACE( "builtin interrupt %02x has been invoked "
508                 "(through vector %02x)\n", 
509                 OFFSETOF(handler)/DOSVM_STUB_RM, intnum );
510          DOSVM_CallBuiltinHandler( context, OFFSETOF(handler)/DOSVM_STUB_RM );
511      }
512      else 
513      {
514          /* the interrupt is hooked, simulate interrupt in DOS space */ 
515          WORD  flag  = LOWORD( context->EFlags );
516
517          TRACE( "invoking hooked interrupt %02x at %04x:%04x\n", 
518                 intnum, SELECTOROF(handler), OFFSETOF(handler) );
519
520          /* Copy virtual interrupt flag to pushed interrupt flag. */
521          if (context->EFlags & VIF_MASK)
522              flag |= IF_MASK;
523          else 
524              flag &= ~IF_MASK;
525
526          PUSH_WORD16( context, flag );
527          PUSH_WORD16( context, context->SegCs );
528          PUSH_WORD16( context, LOWORD( context->Eip ));
529          
530          context->SegCs = SELECTOROF( handler );
531          context->Eip   = OFFSETOF( handler );
532
533          /* Clear virtual interrupt flag and trap flag. */
534          context->EFlags &= ~(VIF_MASK | TF_MASK);
535      }
536 }
537
538
539 /**********************************************************************
540  *          DOSVM_GetRMHandler
541  *
542  * Return the real mode interrupt vector for a given interrupt.
543  */
544 FARPROC16 DOSVM_GetRMHandler( BYTE intnum )
545 {
546   return *DOSVM_GetRMVector( intnum );
547 }
548
549
550 /**********************************************************************
551  *          DOSVM_SetRMHandler
552  *
553  * Set the real mode interrupt handler for a given interrupt.
554  */
555 void DOSVM_SetRMHandler( BYTE intnum, FARPROC16 handler )
556 {
557   TRACE("Set real mode interrupt vector %02x <- %04x:%04x\n",
558        intnum, HIWORD(handler), LOWORD(handler) );
559   *DOSVM_GetRMVector( intnum ) = handler;
560 }
561
562
563 /**********************************************************************
564  *          DOSVM_GetPMHandler16
565  *
566  * Return the protected mode interrupt vector for a given interrupt.
567  */
568 FARPROC16 DOSVM_GetPMHandler16( BYTE intnum )
569 {
570     TDB *pTask;
571     FARPROC16 proc = 0;
572
573     pTask = GlobalLock16(GetCurrentTask());
574     if (pTask)
575     {
576         switch( intnum )
577         {
578         case 0x00:
579             proc = pTask->int0;
580             break;
581         case 0x02:
582             proc = pTask->int2;
583             break;
584         case 0x04:
585             proc = pTask->int4;
586             break;
587         case 0x06:
588             proc = pTask->int6;
589             break;
590         case 0x07:
591             proc = pTask->int7;
592             break;
593         case 0x3e:
594             proc = pTask->int3e;
595             break;
596         case 0x75:
597             proc = pTask->int75;
598             break;
599         }
600         if( proc )
601             return proc;
602     }
603     if (!DOSVM_Vectors16[intnum])
604     {
605         proc = (FARPROC16)MAKESEGPTR( DOSVM_dpmi_segments->int16_sel,
606                                                 DOSVM_STUB_PM16 * intnum );
607         DOSVM_Vectors16[intnum] = proc;
608     }
609     return DOSVM_Vectors16[intnum];
610 }
611
612
613 /**********************************************************************
614  *          DOSVM_SetPMHandler16
615  *
616  * Set the protected mode interrupt handler for a given interrupt.
617  */
618 void DOSVM_SetPMHandler16( BYTE intnum, FARPROC16 handler )
619 {
620   TDB *pTask;
621
622   TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
623        intnum, HIWORD(handler), LOWORD(handler) );
624
625   pTask = GlobalLock16(GetCurrentTask());
626   if (!pTask)
627     return;
628   switch( intnum )
629   {
630   case 0x00:
631     pTask->int0 = handler;
632     break;
633   case 0x02:
634     pTask->int2 = handler;
635     break;
636   case 0x04:
637     pTask->int4 = handler;
638     break;
639   case 0x06:
640     pTask->int6 = handler;
641     break;
642   case 0x07:
643     pTask->int7 = handler;
644     break;
645   case 0x3e:
646     pTask->int3e = handler;
647     break;
648   case 0x75:
649     pTask->int75 = handler;
650     break;
651   default:
652     DOSVM_Vectors16[intnum] = handler;
653     break;
654   }
655 }
656
657
658 /**********************************************************************
659  *         DOSVM_GetPMHandler48
660  *
661  * Return the protected mode interrupt vector for a given interrupt.
662  * Used to get 48-bit pointer for 32-bit interrupt handlers in DPMI32.
663  */
664 FARPROC48 DOSVM_GetPMHandler48( BYTE intnum )
665 {
666   if (!DOSVM_Vectors48[intnum].selector)
667   {
668     DOSVM_Vectors48[intnum].selector = DOSVM_dpmi_segments->int48_sel;
669     DOSVM_Vectors48[intnum].offset = DOSVM_STUB_PM48 * intnum;
670   }
671   return DOSVM_Vectors48[intnum];
672 }
673
674
675 /**********************************************************************
676  *         DOSVM_SetPMHandler48
677  *
678  * Set the protected mode interrupt handler for a given interrupt.
679  * Used to set 48-bit pointer for 32-bit interrupt handlers in DPMI32.
680  */
681 void DOSVM_SetPMHandler48( BYTE intnum, FARPROC48 handler )
682 {
683   TRACE("Set 32-bit protected mode interrupt vector %02x <- %04x:%08x\n",
684        intnum, handler.selector, handler.offset );
685   DOSVM_Vectors48[intnum] = handler;
686 }
687
688
689 /**********************************************************************
690  *         DOSVM_CallBuiltinHandler
691  *
692  * Execute Wine interrupt handler procedure.
693  */
694 void WINAPI DOSVM_CallBuiltinHandler( CONTEXT86 *context, BYTE intnum ) 
695 {
696     /*
697      * FIXME: Make all builtin interrupt calls go via this routine.
698      * FIXME: Check for PM->RM interrupt reflection.
699      * FIXME: Check for RM->PM interrupt reflection.
700      */
701
702   INTPROC proc = DOSVM_GetBuiltinHandler( intnum );
703   proc( context );
704 }
705
706
707 /**********************************************************************
708  *          DOSVM_Int11Handler
709  *
710  * Handler for int 11h (get equipment list).
711  *
712  *
713  * Borrowed from Ralph Brown's interrupt lists:
714  *
715  *   bits 15-14: number of parallel devices
716  *   bit     13: [Conv] Internal modem
717  *   bit     12: reserved
718  *   bits 11- 9: number of serial devices
719  *   bit      8: reserved
720  *   bits  7- 6: number of diskette drives minus one
721  *   bits  5- 4: Initial video mode:
722  *                 00b = EGA,VGA,PGA
723  *                 01b = 40 x 25 color
724  *                 10b = 80 x 25 color
725  *                 11b = 80 x 25 mono
726  *   bit      3: reserved
727  *   bit      2: [PS] =1 if pointing device
728  *               [non-PS] reserved
729  *   bit      1: =1 if math co-processor
730  *   bit      0: =1 if diskette available for boot
731  *
732  *
733  * Currently the only of these bits correctly set are:
734  *
735  *   bits 15-14   } Added by William Owen Smith,
736  *   bits 11-9    } wos@dcs.warwick.ac.uk
737  *   bits 7-6
738  *   bit  2       (always set)  ( bit 2 = 4 )
739  *   bit  1       } Robert 'Admiral' Coeyman
740  *                  All *nix systems either have a math processor or
741  *                   emulate one.
742  */
743 static void WINAPI DOSVM_Int11Handler( CONTEXT86 *context )
744 {
745     int diskdrives = 0;
746     int parallelports = 0;
747     int serialports = 0;
748     int x;
749
750     if (GetDriveTypeA("A:\\") == DRIVE_REMOVABLE) diskdrives++;
751     if (GetDriveTypeA("B:\\") == DRIVE_REMOVABLE) diskdrives++;
752     if (diskdrives) diskdrives--;
753
754     for (x=0; x < 9; x++)
755     {
756         HANDLE handle;
757         char file[10];
758
759         /* serial port name */
760         sprintf( file, "\\\\.\\COM%d", x+1 );
761         handle = CreateFileA( file, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
762         if (handle != INVALID_HANDLE_VALUE)
763         {
764             CloseHandle( handle );
765             serialports++;
766         }
767
768         sprintf( file, "\\\\.\\LPT%d", x+1 );
769         handle = CreateFileA( file, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
770         if (handle != INVALID_HANDLE_VALUE)
771         {
772             CloseHandle( handle );
773             parallelports++;
774         }
775     }
776
777     if (serialports > 7) /* 3 bits -- maximum value = 7 */
778         serialports = 7;
779
780     if (parallelports > 3) /* 2 bits -- maximum value = 3 */
781         parallelports = 3;
782
783     SET_AX( context,
784             (diskdrives << 6) | (serialports << 9) | (parallelports << 14) | 0x06 );
785 }
786
787
788 /**********************************************************************
789  *         DOSVM_Int12Handler
790  *
791  * Handler for int 12h (get memory size).
792  */
793 static void WINAPI DOSVM_Int12Handler( CONTEXT86 *context )
794 {
795     SET_AX( context, 640 );
796 }
797
798
799 /**********************************************************************
800  *          DOSVM_Int17Handler
801  *
802  * Handler for int 17h (printer - output character).
803  */
804 static void WINAPI DOSVM_Int17Handler( CONTEXT86 *context )
805 {
806     switch( AH_reg(context) )
807     {
808        case 0x00:/* Send character*/
809             FIXME("Send character not supported yet\n");
810             SET_AH( context, 0x00 );/*Timeout*/
811             break;
812         case 0x01:              /* PRINTER - INITIALIZE */
813             FIXME("Initialize Printer - Not Supported\n");
814             SET_AH( context, 0x30 ); /* selected | out of paper */
815             break;
816         case 0x02:              /* PRINTER - GET STATUS */
817             FIXME("Get Printer Status - Not Supported\n");
818             break;
819         default:
820             SET_AH( context, 0 ); /* time out */
821             INT_BARF( context, 0x17 );
822     }
823 }
824
825
826 /**********************************************************************
827  *          DOSVM_Int19Handler
828  *
829  * Handler for int 19h (Reboot).
830  */
831 static void WINAPI DOSVM_Int19Handler( CONTEXT86 *context )
832 {
833     TRACE( "Attempted Reboot\n" );
834     ExitProcess(0);
835 }
836
837
838 /**********************************************************************
839  *         DOSVM_Int1aHandler
840  *
841  * Handler for int 1ah.
842  */
843 static void WINAPI DOSVM_Int1aHandler( CONTEXT86 *context )
844 {
845     switch(AH_reg(context))
846     {
847     case 0x00: /* GET SYSTEM TIME */
848         {
849             BIOSDATA *data = DOSVM_BiosData();
850             SET_CX( context, HIWORD(data->Ticks) );
851             SET_DX( context, LOWORD(data->Ticks) );
852             SET_AL( context, 0 ); /* FIXME: midnight flag is unsupported */
853             TRACE( "GET SYSTEM TIME - ticks=%d\n", data->Ticks );
854         }
855         break;
856
857     case 0x01: /* SET SYSTEM TIME */
858         FIXME( "SET SYSTEM TIME - not allowed\n" );
859         break;
860
861     case 0x02: /* GET REAL-TIME CLOCK TIME */
862         TRACE( "GET REAL-TIME CLOCK TIME\n" );
863         {
864             SYSTEMTIME systime;
865             GetLocalTime( &systime );
866             SET_CH( context, BIN_TO_BCD(systime.wHour) );
867             SET_CL( context, BIN_TO_BCD(systime.wMinute) );
868             SET_DH( context, BIN_TO_BCD(systime.wSecond) );
869             SET_DL( context, 0 ); /* FIXME: assume no daylight saving */
870             RESET_CFLAG(context);
871         }
872         break;
873
874     case 0x03: /* SET REAL-TIME CLOCK TIME */
875         FIXME( "SET REAL-TIME CLOCK TIME - not allowed\n" );
876         break;
877
878     case 0x04: /* GET REAL-TIME CLOCK DATE */
879         TRACE( "GET REAL-TIME CLOCK DATE\n" );
880         {
881             SYSTEMTIME systime;
882             GetLocalTime( &systime );
883             SET_CH( context, BIN_TO_BCD(systime.wYear / 100) );
884             SET_CL( context, BIN_TO_BCD(systime.wYear % 100) );
885             SET_DH( context, BIN_TO_BCD(systime.wMonth) );
886             SET_DL( context, BIN_TO_BCD(systime.wDay) );
887             RESET_CFLAG(context);
888         }
889         break;
890
891     case 0x05: /* SET REAL-TIME CLOCK DATE */
892         FIXME( "SET REAL-TIME CLOCK DATE - not allowed\n" );
893         break;
894
895     case 0x06: /* SET ALARM */
896         FIXME( "SET ALARM - unimplemented\n" );
897         break;
898
899     case 0x07: /* CANCEL ALARM */
900         FIXME( "CANCEL ALARM - unimplemented\n" );
901         break;
902
903     case 0x08: /* SET RTC ACTIVATED POWER ON MODE */
904     case 0x09: /* READ RTC ALARM TIME AND STATUS */
905     case 0x0a: /* READ SYSTEM-TIMER DAY COUNTER */
906     case 0x0b: /* SET SYSTEM-TIMER DAY COUNTER */
907     case 0x0c: /* SET RTC DATE/TIME ACTIVATED POWER-ON MODE */
908     case 0x0d: /* RESET RTC DATE/TIME ACTIVATED POWER-ON MODE */
909     case 0x0e: /* GET RTC DATE/TIME ALARM AND STATUS */
910     case 0x0f: /* INITIALIZE REAL-TIME CLOCK */
911         INT_BARF( context, 0x1a );
912         break;
913
914     case 0xb0:
915         if (CX_reg(context) == 0x4d52 &&
916             DX_reg(context) == 0x4349 &&
917             AL_reg(context) == 0x01)
918         {
919             /*
920              * Microsoft Real-Time Compression Interface (MRCI).
921              * Ignoring this call indicates MRCI is not supported.
922              */
923             TRACE( "Microsoft Real-Time Compression Interface - not supported\n" );
924         }
925         else
926         {
927             INT_BARF(context, 0x1a);
928         }
929         break;
930
931     default:
932         INT_BARF( context, 0x1a );
933     }
934 }
935
936
937 /**********************************************************************
938  *          DOSVM_Int20Handler
939  *
940  * Handler for int 20h.
941  */
942 static void WINAPI DOSVM_Int20Handler( CONTEXT86 *context )
943 {
944     if (DOSVM_IsWin16())
945         ExitThread( 0 );
946     else if(ISV86(context))
947         MZ_Exit( context, TRUE, 0 );
948     else
949         ERR( "Called from DOS protected mode\n" );
950 }
951
952
953 /**********************************************************************
954  *          DOSVM_Int29Handler
955  *
956  * Handler for int 29h (fast console output)
957  */
958 static void WINAPI DOSVM_Int29Handler( CONTEXT86 *context )
959 {
960    /* Yes, it seems that this is really all this interrupt does. */
961    DOSVM_PutChar(AL_reg(context));
962 }
963
964
965 /**********************************************************************
966  *         DOSVM_Int2aHandler
967  *
968  * Handler for int 2ah (network).
969  */
970 static void WINAPI DOSVM_Int2aHandler( CONTEXT86 *context )
971 {
972     switch(AH_reg(context))
973     {
974     case 0x00:                             /* NETWORK INSTALLATION CHECK */
975         break;
976
977     default:
978        INT_BARF( context, 0x2a );
979     }
980 }
981
982
983 /***********************************************************************
984  *           DOSVM_Int41Handler
985  */
986 static void WINAPI DOSVM_Int41Handler( CONTEXT86 *context )
987 {
988     if ( ISV86(context) )
989     {
990         /* Real-mode debugger services */
991         switch ( AX_reg(context) )
992         {
993         default:
994             INT_BARF( context, 0x41 );
995             break;
996         }
997     }
998     else
999     {
1000         /* Protected-mode debugger services */
1001         switch ( AX_reg(context) )
1002         {
1003         case 0x4f:
1004         case 0x50:
1005         case 0x150:
1006         case 0x51:
1007         case 0x52:
1008         case 0x152:
1009         case 0x59:
1010         case 0x5a:
1011         case 0x5b:
1012         case 0x5c:
1013         case 0x5d:
1014             /* Notifies the debugger of a lot of stuff. We simply ignore it
1015                for now, but some of the info might actually be useful ... */
1016             break;
1017
1018         default:
1019             INT_BARF( context, 0x41 );
1020             break;
1021         }
1022     }
1023 }
1024
1025
1026 /***********************************************************************
1027  *           DOSVM_Int4bHandler
1028  *
1029  */
1030 static void WINAPI DOSVM_Int4bHandler( CONTEXT86 *context )
1031 {
1032     switch(AH_reg(context))
1033     {
1034     case 0x81: /* Virtual DMA Spec (IBM SCSI interface) */
1035         if(AL_reg(context) != 0x02) /* if not install check */
1036         {
1037             SET_CFLAG(context);
1038             SET_AL( context, 0x0f ); /* function is not implemented */
1039         }
1040         break;
1041     default:
1042         INT_BARF(context, 0x4b);
1043     }
1044 }
1045
1046
1047 /***********************************************************************
1048  *           DOSVM_Int5cHandler
1049  *
1050  * Called from NetBIOSCall16.
1051  */
1052 static void WINAPI DOSVM_Int5cHandler( CONTEXT86 *context )
1053 {
1054     BYTE* ptr;
1055     ptr = MapSL( MAKESEGPTR(context->SegEs,BX_reg(context)) );
1056     FIXME("(%p): command code %02x (ignored)\n",context, *ptr);
1057     *(ptr+0x01) = 0xFB; /* NetBIOS emulator not found */
1058     SET_AL( context, 0xFB );
1059 }