makefiles: Add rules for installing fake modules.
[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 "winternl.h"
27 #include "wine/debug.h"
28 #include "wine/winbase16.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(int);
31 WINE_DECLARE_DEBUG_CHANNEL(relay);
32
33 #define BCD_TO_BIN(x) ((x&15) + (x>>4)*10)
34 #define BIN_TO_BCD(x) ((x%10) + ((x/10)<<4))
35
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*);
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   /* 68 */ DOSVM_DefaultHandler
80 };
81
82
83 /*
84  * Sizes of real mode and protected mode interrupt stubs.
85  */
86 #define DOSVM_STUB_RM   4
87 #define DOSVM_STUB_PM16 5
88 #define DOSVM_STUB_PM48 6
89
90
91 /**********************************************************************
92  *         DOSVM_GetRMVector
93  *
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.
98  */
99 static FARPROC16* DOSVM_GetRMVector( BYTE intnum )
100 {
101     LDT_ENTRY entry;
102     FARPROC16 proc;
103
104     proc = GetProcAddress16( GetModuleHandle16( "KERNEL" ), 
105                              (LPCSTR)(ULONG_PTR)183 );
106     wine_ldt_get_entry( LOWORD(proc), &entry );
107
108     return (FARPROC16*)wine_ldt_get_base( &entry ) + intnum;
109 }
110
111
112 /**********************************************************************
113  *         DOSVM_IsIRQ
114  *
115  * Return TRUE if interrupt is an IRQ.
116  */
117 static BOOL DOSVM_IsIRQ( BYTE intnum )
118 {
119     if (intnum >= 0x08 && intnum <= 0x0f)
120         return TRUE;
121
122     if (intnum >= 0x70 && intnum <= 0x77)
123         return TRUE;
124
125     return FALSE;
126 }
127
128
129 /**********************************************************************
130  *         DOSVM_DefaultHandler
131  *
132  * Default interrupt handler. This will be used to emulate all
133  * interrupts that don't have their own interrupt handler.
134  */
135 static void WINAPI DOSVM_DefaultHandler( CONTEXT86 *context )
136 {
137 }
138
139
140 /**********************************************************************
141  *         DOSVM_GetBuiltinHandler
142  *
143  * Return Wine interrupt handler procedure for a given interrupt.
144  */
145 static INTPROC DOSVM_GetBuiltinHandler( BYTE intnum )
146 {
147     if (intnum < sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) {
148         INTPROC proc = DOSVM_VectorsBuiltin[intnum];
149         if (proc)
150             return proc;
151     }
152
153     WARN("int%x not implemented, returning dummy handler\n", intnum );
154
155     if (DOSVM_IsIRQ(intnum))
156         return DOSVM_AcknowledgeIRQ;
157
158     return DOSVM_DefaultHandler;
159 }
160
161
162 /**********************************************************************
163  *          DOSVM_IntProcRelay
164  *
165  * Simple DOSRELAY that interprets its argument as INTPROC and calls it.
166  */
167 static void DOSVM_IntProcRelay( CONTEXT86 *context, LPVOID data )
168 {
169     INTPROC proc = (INTPROC)data;
170     proc(context);
171 }
172
173
174 /**********************************************************************
175  *          DOSVM_PrepareIRQ
176  *
177  */
178 static void DOSVM_PrepareIRQ( CONTEXT86 *context, BOOL isbuiltin )
179 {
180     /* Disable virtual interrupts. */
181     get_vm86_teb_info()->dpmi_vif = 0;
182
183     if (!isbuiltin)
184     {
185         DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
186                                           context->SegSs,
187                                           context->Esp);
188
189         /* Push return address to stack. */
190         *(--stack) = context->SegCs;
191         *(--stack) = context->Eip;
192         context->Esp += -8;
193
194         /* Jump to enable interrupts stub. */
195         context->SegCs = DOSVM_dpmi_segments->relay_code_sel;
196         context->Eip   = 5;
197     }
198 }
199
200
201 /**********************************************************************
202  *          DOSVM_PushFlags
203  *
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.
208  */
209 static void DOSVM_PushFlags( CONTEXT86 *context, BOOL islong, BOOL isstub )
210 {
211     if (islong)
212     {
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. */
217
218         if (isstub)
219         {
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;
226         }
227         else
228             *(--stack) = context->EFlags;            
229     }
230     else
231     {
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. */
236
237         if (isstub)
238         {
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;
245         }
246         else
247             *(--stack) = LOWORD(context->EFlags);
248     }
249 }
250
251
252 /**********************************************************************
253  *         DOSVM_EmulateInterruptPM
254  *
255  * Emulate software interrupt in 16-bit or 32-bit protected mode.
256  * Called from signal handler when intXX opcode is executed. 
257  *
258  * Pushes interrupt frame to stack and changes instruction 
259  * pointer to interrupt handler.
260  */
261 BOOL WINAPI DOSVM_EmulateInterruptPM( CONTEXT86 *context, BYTE intnum ) 
262 {
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 );
272
273     if (context->SegCs == DOSVM_dpmi_segments->dpmi_sel)
274     {
275         DOSVM_BuildCallFrame( context, 
276                               DOSVM_IntProcRelay,
277                               DOSVM_RawModeSwitchHandler );
278     }
279     else if (context->SegCs == DOSVM_dpmi_segments->relay_code_sel)
280     {
281         /*
282          * This must not be called using DOSVM_BuildCallFrame.
283          */
284         DOSVM_RelayHandler( context );
285     }
286     else if (context->SegCs == DOSVM_dpmi_segments->int48_sel)
287     {
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];
292
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 );
297
298         TRACE( "builtin interrupt %02x has been branched to\n", intnum );
299
300         if (intnum == 0x25 || intnum == 0x26)
301             DOSVM_PushFlags( context, TRUE, TRUE );
302
303         DOSVM_BuildCallFrame( context, 
304                               DOSVM_IntProcRelay,
305                               DOSVM_GetBuiltinHandler(intnum) );
306     }
307     else if (context->SegCs == DOSVM_dpmi_segments->int16_sel)
308     {
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) );
313
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 );
318
319         TRACE( "builtin interrupt %02x has been branched to\n", intnum );
320
321         if (intnum == 0x25 || intnum == 0x26)
322             DOSVM_PushFlags( context, FALSE, TRUE );
323
324         DOSVM_BuildCallFrame( context, 
325                               DOSVM_IntProcRelay, 
326                               DOSVM_GetBuiltinHandler(intnum) );
327     }
328     else if (wine_ldt_is_system(context->SegCs))
329     {
330         INTPROC proc;
331         if (intnum >= sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) return FALSE;
332         if (!(proc = DOSVM_VectorsBuiltin[intnum])) return FALSE;
333         proc( context );
334     }
335     else
336     {
337         DOSVM_HardwareInterruptPM( context, intnum );
338     }
339     return TRUE;
340 }
341
342
343 /**********************************************************************
344  *         DOSVM_HardwareInterruptPM
345  *
346  * Emulate call to interrupt handler in 16-bit or 32-bit protected mode.
347  *
348  * Pushes interrupt frame to stack and changes instruction 
349  * pointer to interrupt handler.
350  */
351 void DOSVM_HardwareInterruptPM( CONTEXT86 *context, BYTE intnum ) 
352 {
353     if(DOSVM_IsDos32())
354     {
355         FARPROC48 addr = DOSVM_GetPMHandler48( intnum );
356         
357         if (addr.selector == DOSVM_dpmi_segments->int48_sel)
358         {
359             TRACE( "builtin interrupt %02x has been invoked "
360                    "(through vector %02x)\n",
361                    addr.offset / DOSVM_STUB_PM48, intnum );
362
363             if (intnum == 0x25 || intnum == 0x26)
364                 DOSVM_PushFlags( context, TRUE, FALSE );
365             else if (DOSVM_IsIRQ(intnum))
366                 DOSVM_PrepareIRQ( context, TRUE );
367
368             DOSVM_BuildCallFrame( context,
369                                   DOSVM_IntProcRelay,
370                                   DOSVM_GetBuiltinHandler(
371                                       addr.offset/DOSVM_STUB_PM48 ) );
372         }
373         else
374         {
375             DWORD *stack;
376             
377             TRACE( "invoking hooked interrupt %02x at %04x:%08x\n",
378                    intnum, addr.selector, addr.offset );
379             
380             if (DOSVM_IsIRQ(intnum))
381                 DOSVM_PrepareIRQ( context, FALSE );
382
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;
389
390             /* Jump to the interrupt handler */
391             context->SegCs  = addr.selector;
392             context->Eip = addr.offset;
393         }
394     }
395     else
396     {
397         FARPROC16 addr = DOSVM_GetPMHandler16( intnum );
398
399         if (SELECTOROF(addr) == DOSVM_dpmi_segments->int16_sel)
400         {
401             TRACE( "builtin interrupt %02x has been invoked "
402                    "(through vector %02x)\n", 
403                    OFFSETOF(addr)/DOSVM_STUB_PM16, intnum );
404
405             if (intnum == 0x25 || intnum == 0x26)
406                 DOSVM_PushFlags( context, FALSE, FALSE );
407             else if (DOSVM_IsIRQ(intnum))
408                 DOSVM_PrepareIRQ( context, TRUE );
409
410             DOSVM_BuildCallFrame( context, 
411                                   DOSVM_IntProcRelay,
412                                   DOSVM_GetBuiltinHandler(
413                                       OFFSETOF(addr)/DOSVM_STUB_PM16 ) );
414         }
415         else
416         {
417             TRACE( "invoking hooked interrupt %02x at %04x:%04x\n", 
418                    intnum, SELECTOROF(addr), OFFSETOF(addr) );
419
420             if (DOSVM_IsIRQ(intnum))
421                 DOSVM_PrepareIRQ( context, FALSE );
422
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) );
427
428             /* Jump to the interrupt handler */
429             context->SegCs =  HIWORD(addr);
430             context->Eip = LOWORD(addr);
431         }
432     }
433 }
434
435
436 /**********************************************************************
437  *         DOSVM_EmulateInterruptRM
438  *
439  * Emulate software interrupt in real mode.
440  * Called from VM86 emulation when intXX opcode is executed. 
441  *
442  * Either calls directly builtin handler or pushes interrupt frame to 
443  * stack and changes instruction pointer to interrupt handler.
444  *
445  * Returns FALSE if this interrupt was caused by return 
446  * from real mode wrapper.
447  */
448 BOOL WINAPI DOSVM_EmulateInterruptRM( CONTEXT86 *context, BYTE intnum ) 
449 {
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 );
459
460     /* check for our real-mode hooks */
461     if (intnum == 0x31)
462     {
463         /* is this exit from real-mode wrapper */
464         if (context->SegCs == DOSVM_dpmi_segments->wrap_seg)
465             return FALSE;
466
467         if (DOSVM_CheckWrappers( context ))
468             return TRUE;
469     }
470
471     /* check if the call is from our fake BIOS interrupt stubs */
472     if (context->SegCs==0xf000)
473     {
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) );
478
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 );
483
484         TRACE( "builtin interrupt %02x has been branched to\n", intnum );
485         
486         DOSVM_CallBuiltinHandler( context, intnum );
487
488         /* Real mode stubs use IRET so we must put flags back into stack. */
489         stack[2] = LOWORD(context->EFlags);
490     }
491     else
492     {
493         DOSVM_HardwareInterruptRM( context, intnum );
494     }
495
496     return TRUE;
497 }
498
499
500 /**********************************************************************
501  *         DOSVM_HardwareInterruptRM
502  *
503  * Emulate call to interrupt handler in real mode.
504  *
505  * Either calls directly builtin handler or pushes interrupt frame to 
506  * stack and changes instruction pointer to interrupt handler.
507  */
508 void DOSVM_HardwareInterruptRM( CONTEXT86 *context, BYTE intnum ) 
509 {
510      FARPROC16 handler = DOSVM_GetRMHandler( intnum );
511
512      /* check if the call goes to an unhooked interrupt */
513      if (SELECTOROF(handler) == 0xf000) 
514      {
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 );
520      }
521      else 
522      {
523          /* the interrupt is hooked, simulate interrupt in DOS space */ 
524          WORD  flag  = LOWORD( context->EFlags );
525
526          TRACE( "invoking hooked interrupt %02x at %04x:%04x\n", 
527                 intnum, SELECTOROF(handler), OFFSETOF(handler) );
528
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;
534
535          PUSH_WORD16( context, flag );
536          PUSH_WORD16( context, context->SegCs );
537          PUSH_WORD16( context, LOWORD( context->Eip ));
538          
539          context->SegCs = SELECTOROF( handler );
540          context->Eip   = OFFSETOF( handler );
541
542          /* Clear virtual interrupt flag and trap flag. */
543          context->EFlags &= ~(VIF_MASK | TF_MASK);
544      }
545 }
546
547
548 /**********************************************************************
549  *          DOSVM_GetRMHandler
550  *
551  * Return the real mode interrupt vector for a given interrupt.
552  */
553 FARPROC16 DOSVM_GetRMHandler( BYTE intnum )
554 {
555   return *DOSVM_GetRMVector( intnum );
556 }
557
558
559 /**********************************************************************
560  *          DOSVM_SetRMHandler
561  *
562  * Set the real mode interrupt handler for a given interrupt.
563  */
564 void DOSVM_SetRMHandler( BYTE intnum, FARPROC16 handler )
565 {
566   TRACE("Set real mode interrupt vector %02x <- %04x:%04x\n",
567        intnum, HIWORD(handler), LOWORD(handler) );
568   *DOSVM_GetRMVector( intnum ) = handler;
569 }
570
571
572 /**********************************************************************
573  *          DOSVM_GetPMHandler16
574  *
575  * Return the protected mode interrupt vector for a given interrupt.
576  */
577 FARPROC16 DOSVM_GetPMHandler16( BYTE intnum )
578 {
579     TDB *pTask;
580     FARPROC16 proc = 0;
581
582     pTask = GlobalLock16(GetCurrentTask());
583     if (pTask)
584     {
585         switch( intnum )
586         {
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;
608         }
609         if( proc )
610             return proc;
611     }
612     if (!DOSVM_Vectors16[intnum])
613     {
614         proc = (FARPROC16)MAKESEGPTR( DOSVM_dpmi_segments->int16_sel,
615                                                 DOSVM_STUB_PM16 * intnum );
616         DOSVM_Vectors16[intnum] = proc;
617     }
618     return DOSVM_Vectors16[intnum];
619 }
620
621
622 /**********************************************************************
623  *          DOSVM_SetPMHandler16
624  *
625  * Set the protected mode interrupt handler for a given interrupt.
626  */
627 void DOSVM_SetPMHandler16( BYTE intnum, FARPROC16 handler )
628 {
629   TDB *pTask;
630
631   TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
632        intnum, HIWORD(handler), LOWORD(handler) );
633
634   pTask = GlobalLock16(GetCurrentTask());
635   if (!pTask)
636     return;
637   switch( intnum )
638   {
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;
663   }
664 }
665
666
667 /**********************************************************************
668  *         DOSVM_GetPMHandler48
669  *
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.
672  */
673 FARPROC48 DOSVM_GetPMHandler48( BYTE intnum )
674 {
675   if (!DOSVM_Vectors48[intnum].selector)
676   {
677     DOSVM_Vectors48[intnum].selector = DOSVM_dpmi_segments->int48_sel;
678     DOSVM_Vectors48[intnum].offset = DOSVM_STUB_PM48 * intnum;
679   }
680   return DOSVM_Vectors48[intnum];
681 }
682
683
684 /**********************************************************************
685  *         DOSVM_SetPMHandler48
686  *
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.
689  */
690 void DOSVM_SetPMHandler48( BYTE intnum, FARPROC48 handler )
691 {
692   TRACE("Set 32-bit protected mode interrupt vector %02x <- %04x:%08x\n",
693        intnum, handler.selector, handler.offset );
694   DOSVM_Vectors48[intnum] = handler;
695 }
696
697
698 /**********************************************************************
699  *         DOSVM_CallBuiltinHandler
700  *
701  * Execute Wine interrupt handler procedure.
702  */
703 void WINAPI DOSVM_CallBuiltinHandler( CONTEXT86 *context, BYTE intnum ) 
704 {
705     /*
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.
709      */
710
711   INTPROC proc = DOSVM_GetBuiltinHandler( intnum );
712   proc( context );
713 }
714
715
716 /**********************************************************************
717  *          DOSVM_Int11Handler
718  *
719  * Handler for int 11h (get equipment list).
720  *
721  *
722  * Borrowed from Ralph Brown's interrupt lists:
723  *
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
740  *
741  *
742  * Currently the only of these bits correctly set are:
743  *
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.
751  */
752 static void WINAPI DOSVM_Int11Handler( CONTEXT86 *context )
753 {
754     int diskdrives = 0;
755     int parallelports = 0;
756     int serialports = 0;
757     int x;
758
759     if (GetDriveTypeA("A:\\") == DRIVE_REMOVABLE) diskdrives++;
760     if (GetDriveTypeA("B:\\") == DRIVE_REMOVABLE) diskdrives++;
761     if (diskdrives) diskdrives--;
762
763     for (x=0; x < 9; x++)
764     {
765         HANDLE handle;
766         char file[10];
767
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)
772         {
773             CloseHandle( handle );
774             serialports++;
775         }
776
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)
780         {
781             CloseHandle( handle );
782             parallelports++;
783         }
784     }
785
786     if (serialports > 7) /* 3 bits -- maximum value = 7 */
787         serialports = 7;
788
789     if (parallelports > 3) /* 2 bits -- maximum value = 3 */
790         parallelports = 3;
791
792     SET_AX( context,
793             (diskdrives << 6) | (serialports << 9) | (parallelports << 14) | 0x06 );
794 }
795
796
797 /**********************************************************************
798  *         DOSVM_Int12Handler
799  *
800  * Handler for int 12h (get memory size).
801  */
802 static void WINAPI DOSVM_Int12Handler( CONTEXT86 *context )
803 {
804     SET_AX( context, 640 );
805 }
806
807
808 /**********************************************************************
809  *          DOSVM_Int17Handler
810  *
811  * Handler for int 17h (printer - output character).
812  */
813 static void WINAPI DOSVM_Int17Handler( CONTEXT86 *context )
814 {
815     switch( AH_reg(context) )
816     {
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 );
831     }
832 }
833
834
835 /**********************************************************************
836  *          DOSVM_Int19Handler
837  *
838  * Handler for int 19h (Reboot).
839  */
840 static void WINAPI DOSVM_Int19Handler( CONTEXT86 *context )
841 {
842     TRACE( "Attempted Reboot\n" );
843     ExitProcess(0);
844 }
845
846
847 /**********************************************************************
848  *         DOSVM_Int1aHandler
849  *
850  * Handler for int 1ah.
851  */
852 static void WINAPI DOSVM_Int1aHandler( CONTEXT86 *context )
853 {
854     switch(AH_reg(context))
855     {
856     case 0x00: /* GET SYSTEM TIME */
857         {
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 );
863         }
864         break;
865
866     case 0x01: /* SET SYSTEM TIME */
867         FIXME( "SET SYSTEM TIME - not allowed\n" );
868         break;
869
870     case 0x02: /* GET REAL-TIME CLOCK TIME */
871         TRACE( "GET REAL-TIME CLOCK TIME\n" );
872         {
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);
880         }
881         break;
882
883     case 0x03: /* SET REAL-TIME CLOCK TIME */
884         FIXME( "SET REAL-TIME CLOCK TIME - not allowed\n" );
885         break;
886
887     case 0x04: /* GET REAL-TIME CLOCK DATE */
888         TRACE( "GET REAL-TIME CLOCK DATE\n" );
889         {
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);
897         }
898         break;
899
900     case 0x05: /* SET REAL-TIME CLOCK DATE */
901         FIXME( "SET REAL-TIME CLOCK DATE - not allowed\n" );
902         break;
903
904     case 0x06: /* SET ALARM */
905         FIXME( "SET ALARM - unimplemented\n" );
906         break;
907
908     case 0x07: /* CANCEL ALARM */
909         FIXME( "CANCEL ALARM - unimplemented\n" );
910         break;
911
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;
922
923     case 0xb0:
924         if (CX_reg(context) == 0x4d52 &&
925             DX_reg(context) == 0x4349 &&
926             AL_reg(context) == 0x01)
927         {
928             /*
929              * Microsoft Real-Time Compression Interface (MRCI).
930              * Ignoring this call indicates MRCI is not supported.
931              */
932             TRACE( "Microsoft Real-Time Compression Interface - not supported\n" );
933         }
934         else
935         {
936             INT_BARF(context, 0x1a);
937         }
938         break;
939
940     default:
941         INT_BARF( context, 0x1a );
942     }
943 }
944
945
946 /**********************************************************************
947  *          DOSVM_Int20Handler
948  *
949  * Handler for int 20h.
950  */
951 static void WINAPI DOSVM_Int20Handler( CONTEXT86 *context )
952 {
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" );
959 }
960
961
962 /**********************************************************************
963  *          DOSVM_Int29Handler
964  *
965  * Handler for int 29h (fast console output)
966  */
967 static void WINAPI DOSVM_Int29Handler( CONTEXT86 *context )
968 {
969    /* Yes, it seems that this is really all this interrupt does. */
970    DOSVM_PutChar(AL_reg(context));
971 }
972
973
974 /**********************************************************************
975  *         DOSVM_Int2aHandler
976  *
977  * Handler for int 2ah (network).
978  */
979 static void WINAPI DOSVM_Int2aHandler( CONTEXT86 *context )
980 {
981     switch(AH_reg(context))
982     {
983     case 0x00:                             /* NETWORK INSTALLATION CHECK */
984         break;
985
986     default:
987        INT_BARF( context, 0x2a );
988     }
989 }
990
991
992 /***********************************************************************
993  *           DOSVM_Int41Handler
994  */
995 static void WINAPI DOSVM_Int41Handler( CONTEXT86 *context )
996 {
997     if ( ISV86(context) )
998     {
999         /* Real-mode debugger services */
1000         switch ( AX_reg(context) )
1001         {
1002         default:
1003             INT_BARF( context, 0x41 );
1004             break;
1005         }
1006     }
1007     else
1008     {
1009         /* Protected-mode debugger services */
1010         switch ( AX_reg(context) )
1011         {
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;
1026
1027         default:
1028             INT_BARF( context, 0x41 );
1029             break;
1030         }
1031     }
1032 }
1033
1034
1035 /***********************************************************************
1036  *           DOSVM_Int4bHandler
1037  *
1038  */
1039 static void WINAPI DOSVM_Int4bHandler( CONTEXT86 *context )
1040 {
1041     switch(AH_reg(context))
1042     {
1043     case 0x81: /* Virtual DMA Spec (IBM SCSI interface) */
1044         if(AL_reg(context) != 0x02) /* if not install check */
1045         {
1046             SET_CFLAG(context);
1047             SET_AL( context, 0x0f ); /* function is not implemented */
1048         }
1049         break;
1050     default:
1051         INT_BARF(context, 0x4b);
1052     }
1053 }
1054
1055
1056 /***********************************************************************
1057  *           DOSVM_Int5cHandler
1058  *
1059  * Called from NetBIOSCall16.
1060  */
1061 static void WINAPI DOSVM_Int5cHandler( CONTEXT86 *context )
1062 {
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 );
1068 }