fdopen: don't rewind the file after creating the FILE* handle. Added
[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 WINE_DEFAULT_DEBUG_CHANNEL(int);
26
27 static FARPROC16     DOSVM_Vectors16[256];
28 static FARPROC48     DOSVM_Vectors48[256];
29 static const INTPROC DOSVM_VectorsBuiltin[] =
30 {
31   /* 00 */ 0,                  0,                  0,                  0,
32   /* 04 */ 0,                  0,                  0,                  0,
33   /* 08 */ 0,                  DOSVM_Int09Handler, 0,                  0,
34   /* 0C */ 0,                  0,                  0,                  0,
35   /* 10 */ DOSVM_Int10Handler, DOSVM_Int11Handler, DOSVM_Int12Handler, DOSVM_Int13Handler,
36   /* 14 */ 0,                  DOSVM_Int15Handler, DOSVM_Int16Handler, DOSVM_Int17Handler,
37   /* 18 */ 0,                  0,                  DOSVM_Int1aHandler, 0,
38   /* 1C */ 0,                  0,                  0,                  0,
39   /* 20 */ DOSVM_Int20Handler, DOSVM_Int21Handler, 0,                  0,
40   /* 24 */ 0,                  DOSVM_Int25Handler, DOSVM_Int26Handler, 0,
41   /* 28 */ 0,                  DOSVM_Int29Handler, DOSVM_Int2aHandler, 0,
42   /* 2C */ 0,                  0,                  0,                  DOSVM_Int2fHandler,
43   /* 30 */ 0,                  DOSVM_Int31Handler, 0,                  DOSVM_Int33Handler,
44   /* 34 */ DOSVM_Int34Handler, DOSVM_Int35Handler, DOSVM_Int36Handler, DOSVM_Int37Handler,
45   /* 38 */ DOSVM_Int38Handler, DOSVM_Int39Handler, DOSVM_Int3aHandler, DOSVM_Int3bHandler,
46   /* 3C */ DOSVM_Int3cHandler, DOSVM_Int3dHandler, DOSVM_Int3eHandler, 0,
47   /* 40 */ 0,                  DOSVM_Int41Handler, 0,                  0,
48   /* 44 */ 0,                  0,                  0,                  0,
49   /* 48 */ 0,                  0,                  0,                  DOSVM_Int4bHandler,
50   /* 4C */ 0,                  0,                  0,                  0,
51   /* 50 */ 0,                  0,                  0,                  0,
52   /* 54 */ 0,                  0,                  0,                  0,
53   /* 58 */ 0,                  0,                  0,                  0,
54   /* 5C */ DOSVM_Int5cHandler, 0,                  0,                  0,
55   /* 60 */ 0,                  0,                  0,                  0,
56   /* 64 */ 0,                  0,                  0,                  DOSVM_Int67Handler
57 };
58
59
60 /**********************************************************************
61  *         DOSVM_DefaultHandler
62  *
63  * Default interrupt handler. This will be used to emulate all
64  * interrupts that don't have their own interrupt handler.
65  */
66 void WINAPI DOSVM_DefaultHandler( CONTEXT86 *context )
67 {
68 }
69
70
71 /**********************************************************************
72  *          DOSVM_IntProcRelay
73  *
74  * Simple DOSRELAY that interprets its argument as INTPROC and calls it.
75  */
76 static void DOSVM_IntProcRelay( CONTEXT86 *context, LPVOID data )
77 {
78     INTPROC proc = (INTPROC)data;
79     proc(context);
80 }
81
82
83 /**********************************************************************
84  *          DOSVM_PushFlags
85  *
86  * This routine is used to make default int25 and int26 handlers leave the 
87  * original eflags into stack. In order to do this, stack is manipulated
88  * so that it actually contains two copies of eflags, one of which is
89  * popped during return from interrupt handler.
90  */
91 static void DOSVM_PushFlags( CONTEXT86 *context, BOOL islong, BOOL isstub )
92 {
93     if (islong)
94     {
95         DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
96                                           context->SegSs, 
97                                           context->Esp);
98         context->Esp += -4; /* One item will be added to stack. */
99
100         if (isstub)
101         {
102             DWORD ip = stack[0];
103             DWORD cs = stack[1];
104             stack += 2; /* Pop ip and cs. */
105             *(--stack) = context->EFlags;
106             *(--stack) = cs;
107             *(--stack) = ip;
108         }
109         else
110             *(--stack) = context->EFlags;            
111     }
112     else
113     {
114         WORD *stack = CTX_SEG_OFF_TO_LIN(context, 
115                                          context->SegSs, 
116                                          context->Esp);
117         ADD_LOWORD( context->Esp, -2 ); /* One item will be added to stack. */
118
119         if (isstub)
120         {
121             WORD ip = stack[0];
122             WORD cs = stack[1];
123             stack += 2; /* Pop ip and cs. */
124             *(--stack) = LOWORD(context->EFlags);
125             *(--stack) = cs;
126             *(--stack) = ip;
127         }
128         else
129             *(--stack) = LOWORD(context->EFlags);
130     }
131 }
132
133
134 /**********************************************************************
135  *         DOSVM_EmulateInterruptPM
136  *
137  * Emulate software interrupt in 16-bit or 32-bit protected mode.
138  * Called from signal handler when intXX opcode is executed. 
139  *
140  * Pushes interrupt frame to stack and changes instruction 
141  * pointer to interrupt handler.
142  */
143 void WINAPI DOSVM_EmulateInterruptPM( CONTEXT86 *context, BYTE intnum ) 
144 {
145     if (context->SegCs == DOSVM_dpmi_segments->dpmi_sel)
146     {
147         DOSVM_BuildCallFrame( context, 
148                               DOSVM_IntProcRelay,
149                               DOSVM_RawModeSwitchHandler );
150     }
151     else if (context->SegCs == DOSVM_dpmi_segments->relay_code_sel)
152     {
153         /*
154          * This must not be called using DOSVM_BuildCallFrame.
155          */
156         DOSVM_RelayHandler( context );
157     }
158     else if (context->SegCs == DOSVM_dpmi_segments->int48_sel)
159     {
160         if (intnum == 0x25 || intnum == 0x26)
161             DOSVM_PushFlags( context, TRUE, TRUE );
162
163         DOSVM_BuildCallFrame( context, 
164                               DOSVM_IntProcRelay,
165                               DOSVM_GetBuiltinHandler(intnum) );
166     }
167     else if (context->SegCs == DOSVM_dpmi_segments->int16_sel)
168     {
169         if (intnum == 0x25 || intnum == 0x26)
170             DOSVM_PushFlags( context, FALSE, TRUE );
171
172         DOSVM_BuildCallFrame( context, 
173                               DOSVM_IntProcRelay, 
174                               DOSVM_GetBuiltinHandler(intnum) );
175     }
176     else if(DOSVM_IsDos32())
177     {
178         FARPROC48 addr = DOSVM_GetPMHandler48( intnum );
179         
180         if (addr.selector == DOSVM_dpmi_segments->int48_sel)
181         {
182             if (intnum == 0x25 || intnum == 0x26)
183                 DOSVM_PushFlags( context, TRUE, FALSE );
184
185              DOSVM_BuildCallFrame( context, 
186                                    DOSVM_IntProcRelay,
187                                    DOSVM_GetBuiltinHandler(intnum) );
188         }
189         else
190         {
191             DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
192                                               context->SegSs, 
193                                               context->Esp);
194             
195             /* Push the flags and return address on the stack */
196             *(--stack) = context->EFlags;
197             *(--stack) = context->SegCs;
198             *(--stack) = context->Eip;
199             context->Esp += -12;
200
201             /* Jump to the interrupt handler */
202             context->SegCs  = addr.selector;
203             context->Eip = addr.offset;
204         }
205     }
206     else
207     {
208         FARPROC16 addr = DOSVM_GetPMHandler16( intnum );
209
210         if (SELECTOROF(addr) == DOSVM_dpmi_segments->int16_sel)
211         {
212             if (intnum == 0x25 || intnum == 0x26)
213                 DOSVM_PushFlags( context, FALSE, FALSE );
214
215             DOSVM_BuildCallFrame( context, 
216                                   DOSVM_IntProcRelay,
217                                   DOSVM_GetBuiltinHandler(intnum) );
218         }
219         else
220         {
221             WORD *stack = CTX_SEG_OFF_TO_LIN(context, 
222                                              context->SegSs, 
223                                              context->Esp);
224             
225             /* Push the flags and return address on the stack */
226             *(--stack) = LOWORD(context->EFlags);
227             *(--stack) = context->SegCs;
228             *(--stack) = LOWORD(context->Eip);
229             ADD_LOWORD( context->Esp, -6 );
230
231             /* Jump to the interrupt handler */
232             context->SegCs  = HIWORD(addr);
233             context->Eip = LOWORD(addr);
234         }
235     }
236 }
237
238 /**********************************************************************
239  *          DOSVM_GetRMHandler
240  *
241  * Return the real mode interrupt vector for a given interrupt.
242  */
243 FARPROC16 DOSVM_GetRMHandler( BYTE intnum )
244 {
245   return ((FARPROC16*)0)[intnum];
246 }
247
248 /**********************************************************************
249  *          DOSVM_SetRMHandler
250  *
251  * Set the real mode interrupt handler for a given interrupt.
252  */
253 void DOSVM_SetRMHandler( BYTE intnum, FARPROC16 handler )
254 {
255   TRACE("Set real mode interrupt vector %02x <- %04x:%04x\n",
256        intnum, HIWORD(handler), LOWORD(handler) );
257   ((FARPROC16*)0)[intnum] = handler;
258 }
259
260
261 /**********************************************************************
262  *          DOSVM_GetPMHandler16
263  *
264  * Return the protected mode interrupt vector for a given interrupt.
265  */
266 FARPROC16 DOSVM_GetPMHandler16( BYTE intnum )
267 {
268     if (!DOSVM_Vectors16[intnum])
269     {
270         FARPROC16 proc = (FARPROC16)MAKESEGPTR( DOSVM_dpmi_segments->int16_sel,
271                                                 5 * intnum );
272         DOSVM_Vectors16[intnum] = proc;
273     }
274     return DOSVM_Vectors16[intnum];
275 }
276
277
278 /**********************************************************************
279  *          DOSVM_SetPMHandler16
280  *
281  * Set the protected mode interrupt handler for a given interrupt.
282  */
283 void DOSVM_SetPMHandler16( BYTE intnum, FARPROC16 handler )
284 {
285   TRACE("Set protected mode interrupt vector %02x <- %04x:%04x\n",
286        intnum, HIWORD(handler), LOWORD(handler) );
287   DOSVM_Vectors16[intnum] = handler;
288 }
289
290 /**********************************************************************
291  *         DOSVM_GetPMHandler48
292  *
293  * Return the protected mode interrupt vector for a given interrupt.
294  * Used to get 48-bit pointer for 32-bit interrupt handlers in DPMI32.
295  */
296 FARPROC48 DOSVM_GetPMHandler48( BYTE intnum )
297 {
298   if (!DOSVM_Vectors48[intnum].selector)
299   {
300     DOSVM_Vectors48[intnum].selector = DOSVM_dpmi_segments->int48_sel;
301     DOSVM_Vectors48[intnum].offset = 6 * intnum;
302   }
303   return DOSVM_Vectors48[intnum];
304 }
305
306 /**********************************************************************
307  *         DOSVM_SetPMHandler48
308  *
309  * Set the protected mode interrupt handler for a given interrupt.
310  * Used to set 48-bit pointer for 32-bit interrupt handlers in DPMI32.
311  */
312 void DOSVM_SetPMHandler48( BYTE intnum, FARPROC48 handler )
313 {
314   TRACE("Set 32-bit protected mode interrupt vector %02x <- %04x:%08lx\n",
315        intnum, handler.selector, handler.offset );
316   DOSVM_Vectors48[intnum] = handler;
317 }
318
319 /**********************************************************************
320  *         DOSVM_GetBuiltinHandler
321  *
322  * Return Wine interrupt handler procedure for a given interrupt.
323  */
324 INTPROC DOSVM_GetBuiltinHandler( BYTE intnum )
325 {
326   if (intnum < sizeof(DOSVM_VectorsBuiltin)/sizeof(INTPROC)) {
327     INTPROC proc = DOSVM_VectorsBuiltin[intnum];
328     if(proc)
329       return proc;
330   }
331
332   WARN("int%x not implemented, returning dummy handler\n", intnum );
333   return DOSVM_DefaultHandler;
334 }
335
336 /**********************************************************************
337  *         DOSVM_CallBuiltinHandler
338  *
339  * Execute Wine interrupt handler procedure.
340  */
341 void WINAPI DOSVM_CallBuiltinHandler( CONTEXT86 *context, BYTE intnum ) 
342 {
343   INTPROC proc = DOSVM_GetBuiltinHandler( intnum );
344   proc( context );
345 }