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