Store %gs in the TEB on every call to 16-bit code, and don't restore
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "dosexe.h"
22 #include "wine/debug.h"
23 #include "wine/winbase16.h"
24
25 #ifdef HAVE_SYS_VM86_H
26 # include <sys/vm86.h>
27 #endif
28
29 #ifndef IF_MASK
30 #define IF_MASK 0x00000200
31 #endif
32
33 #ifndef VIF_MASK
34 #define VIF_MASK 0x00080000
35 #endif
36
37
38 WINE_DEFAULT_DEBUG_CHANNEL(int);
39
40 static FARPROC16     DOSVM_Vectors16[256];
41 static FARPROC48     DOSVM_Vectors48[256];
42 static const INTPROC DOSVM_VectorsBuiltin[] =
43 {
44   /* 00 */ 0,                  0,                  0,                  0,
45   /* 04 */ 0,                  0,                  0,                  0,
46   /* 08 */ 0,                  DOSVM_Int09Handler, 0,                  0,
47   /* 0C */ 0,                  0,                  0,                  0,
48   /* 10 */ DOSVM_Int10Handler, DOSVM_Int11Handler, DOSVM_Int12Handler, DOSVM_Int13Handler,
49   /* 14 */ 0,                  DOSVM_Int15Handler, DOSVM_Int16Handler, DOSVM_Int17Handler,
50   /* 18 */ 0,                  0,                  DOSVM_Int1aHandler, 0,
51   /* 1C */ 0,                  0,                  0,                  0,
52   /* 20 */ DOSVM_Int20Handler, DOSVM_Int21Handler, 0,                  0,
53   /* 24 */ 0,                  DOSVM_Int25Handler, DOSVM_Int26Handler, 0,
54   /* 28 */ 0,                  DOSVM_Int29Handler, DOSVM_Int2aHandler, 0,
55   /* 2C */ 0,                  0,                  0,                  DOSVM_Int2fHandler,
56   /* 30 */ 0,                  DOSVM_Int31Handler, 0,                  DOSVM_Int33Handler,
57   /* 34 */ DOSVM_Int34Handler, DOSVM_Int35Handler, DOSVM_Int36Handler, DOSVM_Int37Handler,
58   /* 38 */ DOSVM_Int38Handler, DOSVM_Int39Handler, DOSVM_Int3aHandler, DOSVM_Int3bHandler,
59   /* 3C */ DOSVM_Int3cHandler, DOSVM_Int3dHandler, DOSVM_Int3eHandler, 0,
60   /* 40 */ 0,                  DOSVM_Int41Handler, 0,                  0,
61   /* 44 */ 0,                  0,                  0,                  0,
62   /* 48 */ 0,                  0,                  0,                  DOSVM_Int4bHandler,
63   /* 4C */ 0,                  0,                  0,                  0,
64   /* 50 */ 0,                  0,                  0,                  0,
65   /* 54 */ 0,                  0,                  0,                  0,
66   /* 58 */ 0,                  0,                  0,                  0,
67   /* 5C */ DOSVM_Int5cHandler, 0,                  0,                  0,
68   /* 60 */ 0,                  0,                  0,                  0,
69   /* 64 */ 0,                  0,                  0,                  DOSVM_Int67Handler
70 };
71
72
73 /**********************************************************************
74  *         DOSVM_DefaultHandler
75  *
76  * Default interrupt handler. This will be used to emulate all
77  * interrupts that don't have their own interrupt handler.
78  */
79 void WINAPI DOSVM_DefaultHandler( CONTEXT86 *context )
80 {
81 }
82
83
84 /**********************************************************************
85  *         DOSVM_GetBuiltinHandler
86  *
87  * Return Wine interrupt handler procedure for a given interrupt.
88  */
89 static INTPROC DOSVM_GetBuiltinHandler( BYTE intnum )
90 {
91     if (intnum < sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) {
92         INTPROC proc = DOSVM_VectorsBuiltin[intnum];
93         if (proc)
94             return proc;
95     }
96
97     WARN("int%x not implemented, returning dummy handler\n", intnum );
98     return DOSVM_DefaultHandler;
99 }
100
101
102 /**********************************************************************
103  *          DOSVM_IntProcRelay
104  *
105  * Simple DOSRELAY that interprets its argument as INTPROC and calls it.
106  */
107 static void DOSVM_IntProcRelay( CONTEXT86 *context, LPVOID data )
108 {
109     INTPROC proc = (INTPROC)data;
110     proc(context);
111 }
112
113
114 /**********************************************************************
115  *          DOSVM_PushFlags
116  *
117  * This routine is used to make default int25 and int26 handlers leave the 
118  * original eflags into stack. In order to do this, stack is manipulated
119  * so that it actually contains two copies of eflags, one of which is
120  * popped during return from interrupt handler.
121  */
122 static void DOSVM_PushFlags( CONTEXT86 *context, BOOL islong, BOOL isstub )
123 {
124     if (islong)
125     {
126         DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
127                                           context->SegSs, 
128                                           context->Esp);
129         context->Esp += -4; /* One item will be added to stack. */
130
131         if (isstub)
132         {
133             DWORD ip = stack[0];
134             DWORD cs = stack[1];
135             stack += 2; /* Pop ip and cs. */
136             *(--stack) = context->EFlags;
137             *(--stack) = cs;
138             *(--stack) = ip;
139         }
140         else
141             *(--stack) = context->EFlags;            
142     }
143     else
144     {
145         WORD *stack = CTX_SEG_OFF_TO_LIN(context, 
146                                          context->SegSs, 
147                                          context->Esp);
148         ADD_LOWORD( context->Esp, -2 ); /* One item will be added to stack. */
149
150         if (isstub)
151         {
152             WORD ip = stack[0];
153             WORD cs = stack[1];
154             stack += 2; /* Pop ip and cs. */
155             *(--stack) = LOWORD(context->EFlags);
156             *(--stack) = cs;
157             *(--stack) = ip;
158         }
159         else
160             *(--stack) = LOWORD(context->EFlags);
161     }
162 }
163
164
165 /**********************************************************************
166  *         DOSVM_EmulateInterruptPM
167  *
168  * Emulate software interrupt in 16-bit or 32-bit protected mode.
169  * Called from signal handler when intXX opcode is executed. 
170  *
171  * Pushes interrupt frame to stack and changes instruction 
172  * pointer to interrupt handler.
173  */
174 void WINAPI DOSVM_EmulateInterruptPM( CONTEXT86 *context, BYTE intnum ) 
175 {
176     if (context->SegCs == DOSVM_dpmi_segments->dpmi_sel)
177     {
178         DOSVM_BuildCallFrame( context, 
179                               DOSVM_IntProcRelay,
180                               DOSVM_RawModeSwitchHandler );
181     }
182     else if (context->SegCs == DOSVM_dpmi_segments->relay_code_sel)
183     {
184         /*
185          * This must not be called using DOSVM_BuildCallFrame.
186          */
187         DOSVM_RelayHandler( context );
188     }
189     else if (context->SegCs == DOSVM_dpmi_segments->int48_sel)
190     {
191         if (intnum == 0x25 || intnum == 0x26)
192             DOSVM_PushFlags( context, TRUE, TRUE );
193
194         DOSVM_BuildCallFrame( context, 
195                               DOSVM_IntProcRelay,
196                               DOSVM_GetBuiltinHandler(intnum) );
197     }
198     else if (context->SegCs == DOSVM_dpmi_segments->int16_sel)
199     {
200         if (intnum == 0x25 || intnum == 0x26)
201             DOSVM_PushFlags( context, FALSE, TRUE );
202
203         DOSVM_BuildCallFrame( context, 
204                               DOSVM_IntProcRelay, 
205                               DOSVM_GetBuiltinHandler(intnum) );
206     }
207     else
208     {
209         DOSVM_HardwareInterruptPM( context, intnum );
210     }
211 }
212
213
214 /**********************************************************************
215  *         DOSVM_HardwareInterruptPM
216  *
217  * Emulate call to interrupt handler in 16-bit or 32-bit protected mode.
218  *
219  * Pushes interrupt frame to stack and changes instruction 
220  * pointer to interrupt handler.
221  */
222 void DOSVM_HardwareInterruptPM( CONTEXT86 *context, BYTE intnum ) 
223 {
224     if(DOSVM_IsDos32())
225     {
226         FARPROC48 addr = DOSVM_GetPMHandler48( intnum );
227         
228         if (addr.selector == DOSVM_dpmi_segments->int48_sel)
229         {
230             if (intnum == 0x25 || intnum == 0x26)
231                 DOSVM_PushFlags( context, TRUE, FALSE );
232
233              DOSVM_BuildCallFrame( context, 
234                                    DOSVM_IntProcRelay,
235                                    DOSVM_GetBuiltinHandler(intnum) );
236         }
237         else
238         {
239             DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
240                                               context->SegSs, 
241                                               context->Esp);
242             
243             /* Push the flags and return address on the stack */
244             *(--stack) = context->EFlags;
245             *(--stack) = context->SegCs;
246             *(--stack) = context->Eip;
247             context->Esp += -12;
248
249             /* Jump to the interrupt handler */
250             context->SegCs  = addr.selector;
251             context->Eip = addr.offset;
252         }
253     }
254     else
255     {
256         FARPROC16 addr = DOSVM_GetPMHandler16( intnum );
257
258         if (SELECTOROF(addr) == DOSVM_dpmi_segments->int16_sel)
259         {
260             if (intnum == 0x25 || intnum == 0x26)
261                 DOSVM_PushFlags( context, FALSE, FALSE );
262
263             DOSVM_BuildCallFrame( context, 
264                                   DOSVM_IntProcRelay,
265                                   DOSVM_GetBuiltinHandler(intnum) );
266         }
267         else
268         {
269             WORD *stack = CTX_SEG_OFF_TO_LIN(context, 
270                                              context->SegSs, 
271                                              context->Esp);
272             
273             /* Push the flags and return address on the stack */
274             *(--stack) = LOWORD(context->EFlags);
275             *(--stack) = context->SegCs;
276             *(--stack) = LOWORD(context->Eip);
277             ADD_LOWORD( context->Esp, -6 );
278
279             /* Jump to the interrupt handler */
280             context->SegCs  = HIWORD(addr);
281             context->Eip = LOWORD(addr);
282         }
283     }
284 }
285
286
287 /**********************************************************************
288  *         DOSVM_EmulateInterruptRM
289  *
290  * Emulate software interrupt in real mode.
291  * Called from VM86 emulation when intXX opcode is executed. 
292  *
293  * Either calls directly builtin handler or pushes interrupt frame to 
294  * stack and changes instruction pointer to interrupt handler.
295  *
296  * Returns FALSE if this interrupt was caused by return 
297  * from real mode wrapper.
298  */
299 BOOL WINAPI DOSVM_EmulateInterruptRM( CONTEXT86 *context, BYTE intnum ) 
300 {
301     /* check for our real-mode hooks */
302     if (intnum == 0x31)
303     {
304         /* is this exit from real-mode wrapper */
305         if (context->SegCs == DOSVM_dpmi_segments->wrap_seg)
306             return FALSE;
307
308         if (DOSVM_CheckWrappers( context ))
309             return TRUE;
310     }
311
312     /* check if the call is from our fake BIOS interrupt stubs */
313     if (context->SegCs==0xf000)
314     {
315         if (intnum != (context->Eip/4))
316             TRACE( "something fishy going on here (interrupt stub is %02lx)\n", 
317                    context->Eip/4 );
318
319         TRACE( "builtin interrupt %02x has been branched to\n", intnum );
320         
321         DOSVM_CallBuiltinHandler( context, intnum );
322     }
323     else
324     {
325         DOSVM_HardwareInterruptRM( context, intnum );
326     }
327
328     return TRUE;
329 }
330
331
332 /**********************************************************************
333  *         DOSVM_HardwareInterruptRM
334  *
335  * Emulate call to interrupt handler in real mode.
336  *
337  * Either calls directly builtin handler or pushes interrupt frame to 
338  * stack and changes instruction pointer to interrupt handler.
339  */
340 void DOSVM_HardwareInterruptRM( CONTEXT86 *context, BYTE intnum ) 
341 {
342      FARPROC16 handler = DOSVM_GetRMHandler( intnum );
343
344      /* check if the call goes to an unhooked interrupt */
345      if (SELECTOROF(handler) == 0xf000) 
346      {
347          /* if so, call it directly */
348          TRACE( "builtin interrupt %02x has been invoked (through vector %02x)\n", 
349                 OFFSETOF(handler)/4, intnum );
350          DOSVM_CallBuiltinHandler( context, OFFSETOF(handler)/4 );
351      }
352      else 
353      {
354          /* the interrupt is hooked, simulate interrupt in DOS space */ 
355          WORD* stack = PTR_REAL_TO_LIN( context->SegSs, context->Esp );
356          WORD  flag  = LOWORD( context->EFlags );
357
358          TRACE_(int)( "invoking hooked interrupt %02x at %04x:%04x\n", 
359                       intnum, SELECTOROF(handler), OFFSETOF(handler) );
360
361          /* Copy virtual interrupt flag to pushed interrupt flag. */
362          if (context->EFlags & VIF_MASK)
363              flag |= IF_MASK;
364          else 
365              flag &= ~IF_MASK;
366
367          *(--stack) = flag;
368          *(--stack) = context->SegCs;
369          *(--stack) = LOWORD( context->Eip );
370          context->Esp -= 6;
371          context->SegCs = SELECTOROF( handler );
372          context->Eip   = OFFSETOF( handler );
373
374          /* Clear virtual interrupt flag. */
375          context->EFlags &= ~VIF_MASK;
376      }
377 }
378
379
380 /**********************************************************************
381  *          DOSVM_GetRMHandler
382  *
383  * Return the real mode interrupt vector for a given interrupt.
384  */
385 FARPROC16 DOSVM_GetRMHandler( BYTE intnum )
386 {
387   return ((FARPROC16*)0)[intnum];
388 }
389
390 /**********************************************************************
391  *          DOSVM_SetRMHandler
392  *
393  * Set the real mode interrupt handler for a given interrupt.
394  */
395 void DOSVM_SetRMHandler( BYTE intnum, FARPROC16 handler )
396 {
397   TRACE("Set real mode interrupt vector %02x <- %04x:%04x\n",
398        intnum, HIWORD(handler), LOWORD(handler) );
399   ((FARPROC16*)0)[intnum] = handler;
400 }
401
402
403 /**********************************************************************
404  *          DOSVM_GetPMHandler16
405  *
406  * Return the protected mode interrupt vector for a given interrupt.
407  */
408 FARPROC16 DOSVM_GetPMHandler16( BYTE intnum )
409 {
410     if (!DOSVM_Vectors16[intnum])
411     {
412         FARPROC16 proc = (FARPROC16)MAKESEGPTR( DOSVM_dpmi_segments->int16_sel,
413                                                 5 * intnum );
414         DOSVM_Vectors16[intnum] = proc;
415     }
416     return DOSVM_Vectors16[intnum];
417 }
418
419
420 /**********************************************************************
421  *          DOSVM_SetPMHandler16
422  *
423  * Set the protected mode interrupt handler for a given interrupt.
424  */
425 void DOSVM_SetPMHandler16( BYTE intnum, FARPROC16 handler )
426 {
427   TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
428        intnum, HIWORD(handler), LOWORD(handler) );
429   DOSVM_Vectors16[intnum] = handler;
430 }
431
432 /**********************************************************************
433  *         DOSVM_GetPMHandler48
434  *
435  * Return the protected mode interrupt vector for a given interrupt.
436  * Used to get 48-bit pointer for 32-bit interrupt handlers in DPMI32.
437  */
438 FARPROC48 DOSVM_GetPMHandler48( BYTE intnum )
439 {
440   if (!DOSVM_Vectors48[intnum].selector)
441   {
442     DOSVM_Vectors48[intnum].selector = DOSVM_dpmi_segments->int48_sel;
443     DOSVM_Vectors48[intnum].offset = 6 * intnum;
444   }
445   return DOSVM_Vectors48[intnum];
446 }
447
448 /**********************************************************************
449  *         DOSVM_SetPMHandler48
450  *
451  * Set the protected mode interrupt handler for a given interrupt.
452  * Used to set 48-bit pointer for 32-bit interrupt handlers in DPMI32.
453  */
454 void DOSVM_SetPMHandler48( BYTE intnum, FARPROC48 handler )
455 {
456   TRACE("Set 32-bit protected mode interrupt vector %02x <- %04x:%08lx\n",
457        intnum, handler.selector, handler.offset );
458   DOSVM_Vectors48[intnum] = handler;
459 }
460
461
462 /**********************************************************************
463  *         DOSVM_CallBuiltinHandler
464  *
465  * Execute Wine interrupt handler procedure.
466  */
467 void WINAPI DOSVM_CallBuiltinHandler( CONTEXT86 *context, BYTE intnum ) 
468 {
469   INTPROC proc = DOSVM_GetBuiltinHandler( intnum );
470   proc( context );
471 }