Fix non-RTDSC case for QueryPerformanceFrequency.
[wine] / dlls / kernel / wowthunk.c
1 /*
2  * Win32 WOW Generic Thunk API
3  *
4  * Copyright 1999 Ulrich Weigand
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 "config.h"
22 #include "wine/port.h"
23
24 #include <assert.h>
25 #include <stdarg.h>
26
27 #include "wine/winbase16.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winerror.h"
31 #include "wownt32.h"
32 #include "excpt.h"
33 #include "winreg.h"
34 #include "winternl.h"
35 #include "miscemu.h"
36 #include "module.h"
37 #include "stackframe.h"
38 #include "kernel_private.h"
39 #include "wine/exception.h"
40 #include "wine/debug.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(thunk);
43 WINE_DECLARE_DEBUG_CHANNEL(relay);
44 WINE_DECLARE_DEBUG_CHANNEL(snoop);
45
46 /*
47  * These are the 16-bit side WOW routines.  They reside in wownt16.h
48  * in the SDK; since we don't support Win16 source code anyway, I've
49  * placed them here for compilation with Wine ...
50  */
51
52 DWORD WINAPI GetVDMPointer32W16(SEGPTR,UINT16);
53
54 DWORD WINAPI LoadLibraryEx32W16(LPCSTR,DWORD,DWORD);
55 DWORD WINAPI GetProcAddress32W16(DWORD,LPCSTR);
56 DWORD WINAPI FreeLibrary32W16(DWORD);
57
58 #define CPEX_DEST_STDCALL   0x00000000L
59 #define CPEX_DEST_CDECL     0x80000000L
60
61 /* thunk for 16-bit CreateThread */
62 struct thread_args
63 {
64     FARPROC16 proc;
65     DWORD     param;
66 };
67
68 static DWORD CALLBACK start_thread16( LPVOID threadArgs )
69 {
70     struct thread_args args = *(struct thread_args *)threadArgs;
71     HeapFree( GetProcessHeap(), 0, threadArgs );
72     return K32WOWCallback16( (DWORD)args.proc, args.param );
73 }
74
75
76 #ifdef __i386__
77
78 /* symbols exported from relay16.s */
79 extern DWORD WINAPI wine_call_to_16( FARPROC16 target, DWORD cbArgs, PEXCEPTION_HANDLER handler );
80 extern void WINAPI wine_call_to_16_regs( CONTEXT86 *context, DWORD cbArgs, PEXCEPTION_HANDLER handler );
81 extern void Call16_Ret_Start();
82 extern void Call16_Ret_End();
83 extern void CallTo16_Ret();
84 extern void CALL32_CBClient_Ret();
85 extern void CALL32_CBClientEx_Ret();
86 extern void DPMI_PendingEventCheck();
87 extern void DPMI_PendingEventCheck_Cleanup();
88 extern void DPMI_PendingEventCheck_Return();
89 extern DWORD CallTo16_DataSelector;
90 extern SEGPTR CALL32_CBClient_RetAddr;
91 extern SEGPTR CALL32_CBClientEx_RetAddr;
92 extern BYTE Call16_Start;
93 extern BYTE Call16_End;
94
95 extern void RELAY16_InitDebugLists(void);
96
97 static LONG CALLBACK vectored_handler( EXCEPTION_POINTERS *ptrs );
98 static SEGPTR call16_ret_addr;  /* segptr to CallTo16_Ret routine */
99
100 static WORD  dpmi_checker_selector;
101 static DWORD dpmi_checker_offset_call;
102 static DWORD dpmi_checker_offset_cleanup;
103 static DWORD dpmi_checker_offset_return;
104
105 /***********************************************************************
106  *           WOWTHUNK_Init
107  */
108 BOOL WOWTHUNK_Init(void)
109 {
110     /* allocate the code selector for CallTo16 routines */
111     WORD codesel = SELECTOR_AllocBlock( (void *)Call16_Ret_Start,
112                                         (char *)Call16_Ret_End - (char *)Call16_Ret_Start,
113                                         WINE_LDT_FLAGS_CODE | WINE_LDT_FLAGS_32BIT );
114     if (!codesel) return FALSE;
115
116       /* Patch the return addresses for CallTo16 routines */
117
118     CallTo16_DataSelector = wine_get_ds();
119     call16_ret_addr = MAKESEGPTR( codesel, (char*)CallTo16_Ret - (char*)Call16_Ret_Start );
120     CALL32_CBClient_RetAddr =
121         MAKESEGPTR( codesel, (char*)CALL32_CBClient_Ret - (char*)Call16_Ret_Start );
122     CALL32_CBClientEx_RetAddr =
123         MAKESEGPTR( codesel, (char*)CALL32_CBClientEx_Ret - (char*)Call16_Ret_Start );
124
125     /* Prepare selector and offsets for DPMI event checking. */
126     dpmi_checker_selector = codesel;
127     dpmi_checker_offset_call = 
128         (char*)DPMI_PendingEventCheck - (char*)Call16_Ret_Start;
129     dpmi_checker_offset_cleanup = 
130         (char*)DPMI_PendingEventCheck_Cleanup - (char*)Call16_Ret_Start;
131     dpmi_checker_offset_return = 
132         (char*)DPMI_PendingEventCheck_Return - (char*)Call16_Ret_Start;
133
134     if (TRACE_ON(relay) || TRACE_ON(snoop)) RELAY16_InitDebugLists();
135
136     /* setup emulation of protected instructions from 32-bit code (only for Win9x versions) */
137     if (GetVersion() & 0x80000000) RtlAddVectoredExceptionHandler( TRUE, vectored_handler );
138     return TRUE;
139 }
140
141
142 /*************************************************************
143  *            fix_selector
144  *
145  * Fix a selector load that caused an exception if it's in the
146  * 16-bit relay code.
147  */
148 static BOOL fix_selector( CONTEXT *context )
149 {
150     WORD *stack;
151     BYTE *instr = (BYTE *)context->Eip;
152
153     if (instr < &Call16_Start || instr >= &Call16_End) return FALSE;
154
155     /* skip prefixes */
156     while (*instr == 0x66 || *instr == 0x67) instr++;
157
158     switch(instr[0])
159     {
160     case 0x07: /* pop es */
161     case 0x17: /* pop ss */
162     case 0x1f: /* pop ds */
163         break;
164     case 0x0f: /* extended instruction */
165         switch(instr[1])
166         {
167         case 0xa1: /* pop fs */
168         case 0xa9: /* pop gs */
169             break;
170         default:
171             return FALSE;
172         }
173         break;
174     default:
175         return FALSE;
176     }
177     stack = wine_ldt_get_ptr( context->SegSs, context->Esp );
178     TRACE( "fixing up selector %x for pop instruction\n", *stack );
179     *stack = 0;
180     return TRUE;
181 }
182
183
184 /*************************************************************
185  *            insert_event_check
186  *
187  * Make resuming the context check for pending DPMI events
188  * before the original context is restored. This is required
189  * because DPMI events are asynchronous, they are blocked while 
190  * Wine 32-bit code is being executed and we want to prevent 
191  * a race when returning back to 16-bit or 32-bit DPMI context.
192  */
193 static void insert_event_check( CONTEXT *context )
194 {
195     char *stack = wine_ldt_get_ptr( context->SegSs, context->Esp );
196
197     /* don't do event check while in system code */
198     if (wine_ldt_is_system(context->SegCs))
199         return;
200
201     if(context->SegCs == dpmi_checker_selector &&
202        context->Eip   >= dpmi_checker_offset_call && 
203        context->Eip   <= dpmi_checker_offset_cleanup)
204     {
205         /*
206          * Nested call. Stack will be preserved. 
207          */
208     }
209     else if(context->SegCs == dpmi_checker_selector &&
210             context->Eip   == dpmi_checker_offset_return)
211     {
212         /*
213          * Nested call. We have just finished popping the fs
214          * register, lets put it back into stack.
215          */
216
217         stack -= sizeof(WORD);
218         *(WORD*)stack = context->SegFs;
219
220         context->Esp -= 2;
221     }
222     else
223     {
224         /*
225          * Call is not nested.
226          * Push modified registers into stack.
227          * These will be popped by the assembler stub.
228          */
229
230         stack -= sizeof(DWORD);
231         *(DWORD*)stack = context->EFlags;
232    
233         stack -= sizeof(DWORD);
234         *(DWORD*)stack = context->SegCs;
235
236         stack -= sizeof(DWORD);
237         *(DWORD*)stack = context->Eip;
238
239         stack -= sizeof(WORD);
240         *(WORD*)stack = context->SegFs;
241
242         context->Esp  -= 14;
243     }
244
245     /*
246      * Modify the context so that we jump into assembler stub.
247      * TEB access is made easier by providing the stub
248      * with the correct fs register value.
249      */
250
251     context->SegCs = dpmi_checker_selector;
252     context->Eip   = dpmi_checker_offset_call;
253     context->SegFs = wine_get_fs();
254 }
255
256
257 /*************************************************************
258  *            call16_handler
259  *
260  * Handler for exceptions occurring in 16-bit code.
261  */
262 static DWORD call16_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
263                              CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **pdispatcher )
264 {
265     if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
266     {
267         /* unwinding: restore the stack pointer in the TEB, and leave the Win16 mutex */
268         STACK32FRAME *frame32 = (STACK32FRAME *)((char *)frame - offsetof(STACK32FRAME,frame));
269         NtCurrentTeb()->cur_stack = frame32->frame16;
270         _LeaveWin16Lock();
271     }
272     else if (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
273              record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION)
274     {
275         if (wine_ldt_is_system(context->SegCs))
276         {
277             if (fix_selector( context )) return ExceptionContinueExecution;
278         }
279         else
280         {
281             SEGPTR gpHandler;
282             DWORD ret = INSTR_EmulateInstruction( record, context );
283
284             /*
285              * Insert check for pending DPMI events. Note that this 
286              * check must be inserted after instructions have been 
287              * emulated because the instruction emulation requires
288              * original CS:IP and the emulation may change TEB.dpmi_vif.
289              */
290             if(NtCurrentTeb()->dpmi_vif)
291                 insert_event_check( context );
292
293             if (ret != ExceptionContinueSearch) return ret;
294
295             /* check for Win16 __GP handler */
296             if ((gpHandler = HasGPHandler16( MAKESEGPTR( context->SegCs, context->Eip ) )))
297             {
298                 WORD *stack = wine_ldt_get_ptr( context->SegSs, context->Esp );
299                 *--stack = context->SegCs;
300                 *--stack = context->Eip;
301
302                 if (!IS_SELECTOR_32BIT(context->SegSs))
303                     context->Esp = MAKELONG( LOWORD(context->Esp - 2*sizeof(WORD)),
304                                              HIWORD(context->Esp) );
305                 else
306                     context->Esp -= 2*sizeof(WORD);
307
308                 context->SegCs = SELECTOROF( gpHandler );
309                 context->Eip   = OFFSETOF( gpHandler );
310                 return ExceptionContinueExecution;
311             }
312         }
313     }
314     else if (record->ExceptionCode == EXCEPTION_VM86_STI)
315     {
316         insert_event_check( context );
317     }
318     return ExceptionContinueSearch;
319 }
320
321
322 /*************************************************************
323  *            vm86_handler
324  *
325  * Handler for exceptions occurring in vm86 code.
326  */
327 static DWORD vm86_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
328                            CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **pdispatcher )
329 {
330     if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
331         return ExceptionContinueSearch;
332
333     if (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
334         record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION)
335     {
336         return INSTR_EmulateInstruction( record, context );
337     }
338
339     return ExceptionContinueSearch;
340 }
341
342
343 /***********************************************************************
344  *           vectored_handler
345  *
346  * Vectored exception handler used to emulate protected instructions
347  * from 32-bit code.
348  */
349 static LONG CALLBACK vectored_handler( EXCEPTION_POINTERS *ptrs )
350 {
351     EXCEPTION_RECORD *record = ptrs->ExceptionRecord;
352     CONTEXT *context = ptrs->ContextRecord;
353
354     if (wine_ldt_is_system(context->SegCs) &&
355         (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
356          record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION))
357     {
358         if (INSTR_EmulateInstruction( record, context ) == ExceptionContinueExecution)
359             return EXCEPTION_CONTINUE_EXECUTION;
360     }
361     return EXCEPTION_CONTINUE_SEARCH;
362 }
363
364
365 #else  /* __i386__ */
366
367 BOOL WOWTHUNK_Init(void)
368 {
369     return TRUE;
370 }
371
372 #endif  /* __i386__ */
373
374
375 /*
376  *  32-bit WOW routines (in WOW32, but actually forwarded to KERNEL32)
377  */
378
379 /**********************************************************************
380  *           K32WOWGetDescriptor        (KERNEL32.70)
381  */
382 BOOL WINAPI K32WOWGetDescriptor( SEGPTR segptr, LPLDT_ENTRY ldtent )
383 {
384     return GetThreadSelectorEntry( GetCurrentThread(),
385                                    segptr >> 16, ldtent );
386 }
387
388 /**********************************************************************
389  *           K32WOWGetVDMPointer        (KERNEL32.56)
390  */
391 LPVOID WINAPI K32WOWGetVDMPointer( DWORD vp, DWORD dwBytes, BOOL fProtectedMode )
392 {
393     /* FIXME: add size check too */
394
395     if ( fProtectedMode )
396         return MapSL( vp );
397     else
398         return DOSMEM_MapRealToLinear( vp );
399 }
400
401 /**********************************************************************
402  *           K32WOWGetVDMPointerFix     (KERNEL32.68)
403  */
404 LPVOID WINAPI K32WOWGetVDMPointerFix( DWORD vp, DWORD dwBytes, BOOL fProtectedMode )
405 {
406     /*
407      * Hmmm. According to the docu, we should call:
408      *
409      *          GlobalFix16( SELECTOROF(vp) );
410      *
411      * But this is unnecessary under Wine, as we never move global
412      * memory segments in linear memory anyway.
413      *
414      * (I'm not so sure what we are *supposed* to do if
415      *  fProtectedMode is TRUE, anyway ...)
416      */
417
418     return K32WOWGetVDMPointer( vp, dwBytes, fProtectedMode );
419 }
420
421 /**********************************************************************
422  *           K32WOWGetVDMPointerUnfix   (KERNEL32.69)
423  */
424 VOID WINAPI K32WOWGetVDMPointerUnfix( DWORD vp )
425 {
426     /*
427      * See above why we don't call:
428      *
429      * GlobalUnfix16( SELECTOROF(vp) );
430      *
431      */
432 }
433
434 /**********************************************************************
435  *           K32WOWGlobalAlloc16        (KERNEL32.59)
436  */
437 WORD WINAPI K32WOWGlobalAlloc16( WORD wFlags, DWORD cb )
438 {
439     return (WORD)GlobalAlloc16( wFlags, cb );
440 }
441
442 /**********************************************************************
443  *           K32WOWGlobalFree16         (KERNEL32.62)
444  */
445 WORD WINAPI K32WOWGlobalFree16( WORD hMem )
446 {
447     return (WORD)GlobalFree16( (HGLOBAL16)hMem );
448 }
449
450 /**********************************************************************
451  *           K32WOWGlobalUnlock16       (KERNEL32.61)
452  */
453 BOOL WINAPI K32WOWGlobalUnlock16( WORD hMem )
454 {
455     return (BOOL)GlobalUnlock16( (HGLOBAL16)hMem );
456 }
457
458 /**********************************************************************
459  *           K32WOWGlobalAllocLock16    (KERNEL32.63)
460  */
461 DWORD WINAPI K32WOWGlobalAllocLock16( WORD wFlags, DWORD cb, WORD *phMem )
462 {
463     WORD hMem = K32WOWGlobalAlloc16( wFlags, cb );
464     if (phMem) *phMem = hMem;
465
466     return K32WOWGlobalLock16( hMem );
467 }
468
469 /**********************************************************************
470  *           K32WOWGlobalLockSize16     (KERNEL32.65)
471  */
472 DWORD WINAPI K32WOWGlobalLockSize16( WORD hMem, PDWORD pcb )
473 {
474     if ( pcb )
475         *pcb = GlobalSize16( (HGLOBAL16)hMem );
476
477     return K32WOWGlobalLock16( hMem );
478 }
479
480 /**********************************************************************
481  *           K32WOWGlobalUnlockFree16   (KERNEL32.64)
482  */
483 WORD WINAPI K32WOWGlobalUnlockFree16( DWORD vpMem )
484 {
485     if ( !K32WOWGlobalUnlock16( HIWORD(vpMem) ) )
486         return FALSE;
487
488     return K32WOWGlobalFree16( HIWORD(vpMem) );
489 }
490
491
492 /**********************************************************************
493  *           K32WOWYield16              (KERNEL32.66)
494  */
495 VOID WINAPI K32WOWYield16( void )
496 {
497     /*
498      * This does the right thing for both Win16 and Win32 tasks.
499      * More or less, at least :-/
500      */
501     Yield16();
502 }
503
504 /**********************************************************************
505  *           K32WOWDirectedYield16       (KERNEL32.67)
506  */
507 VOID WINAPI K32WOWDirectedYield16( WORD htask16 )
508 {
509     /*
510      * Argh.  Our scheduler doesn't like DirectedYield by Win32
511      * tasks at all.  So we do hope that this routine is indeed
512      * only ever called by Win16 tasks that have thunked up ...
513      */
514     DirectedYield16( (HTASK16)htask16 );
515 }
516
517
518 /***********************************************************************
519  *           K32WOWHandle32              (KERNEL32.57)
520  */
521 HANDLE WINAPI K32WOWHandle32( WORD handle, WOW_HANDLE_TYPE type )
522 {
523     switch ( type )
524     {
525     case WOW_TYPE_HWND:
526     case WOW_TYPE_HMENU:
527     case WOW_TYPE_HDWP:
528     case WOW_TYPE_HDROP:
529     case WOW_TYPE_HDC:
530     case WOW_TYPE_HFONT:
531     case WOW_TYPE_HRGN:
532     case WOW_TYPE_HBITMAP:
533     case WOW_TYPE_HBRUSH:
534     case WOW_TYPE_HPALETTE:
535     case WOW_TYPE_HPEN:
536     case WOW_TYPE_HACCEL:
537         return (HANDLE)(ULONG_PTR)handle;
538
539     case WOW_TYPE_HMETAFILE:
540         FIXME( "conversion of metafile handles not supported yet\n" );
541         return (HANDLE)(ULONG_PTR)handle;
542
543     case WOW_TYPE_HTASK:
544         return ((TDB *)GlobalLock16(handle))->teb->ClientId.UniqueThread;
545
546     case WOW_TYPE_FULLHWND:
547         FIXME( "conversion of full window handles not supported yet\n" );
548         return (HANDLE)(ULONG_PTR)handle;
549
550     default:
551         ERR( "handle 0x%04x of unknown type %d\n", handle, type );
552         return (HANDLE)(ULONG_PTR)handle;
553     }
554 }
555
556 /***********************************************************************
557  *           K32WOWHandle16              (KERNEL32.58)
558  */
559 WORD WINAPI K32WOWHandle16( HANDLE handle, WOW_HANDLE_TYPE type )
560 {
561     switch ( type )
562     {
563     case WOW_TYPE_HWND:
564     case WOW_TYPE_HMENU:
565     case WOW_TYPE_HDWP:
566     case WOW_TYPE_HDROP:
567     case WOW_TYPE_HDC:
568     case WOW_TYPE_HFONT:
569     case WOW_TYPE_HRGN:
570     case WOW_TYPE_HBITMAP:
571     case WOW_TYPE_HBRUSH:
572     case WOW_TYPE_HPALETTE:
573     case WOW_TYPE_HPEN:
574     case WOW_TYPE_HACCEL:
575     case WOW_TYPE_FULLHWND:
576         if ( HIWORD(handle ) )
577                 ERR( "handle %p of type %d has non-zero HIWORD\n", handle, type );
578         return LOWORD(handle);
579
580     case WOW_TYPE_HMETAFILE:
581         FIXME( "conversion of metafile handles not supported yet\n" );
582         return LOWORD(handle);
583
584     case WOW_TYPE_HTASK:
585         return TASK_GetTaskFromThread( (DWORD)handle );
586
587     default:
588         ERR( "handle %p of unknown type %d\n", handle, type );
589         return LOWORD(handle);
590     }
591 }
592
593 /**********************************************************************
594  *           K32WOWCallback16Ex         (KERNEL32.55)
595  */
596 BOOL WINAPI K32WOWCallback16Ex( DWORD vpfn16, DWORD dwFlags,
597                                 DWORD cbArgs, LPVOID pArgs, LPDWORD pdwRetCode )
598 {
599 #ifdef __i386__
600     /*
601      * Arguments must be prepared in the correct order by the caller
602      * (both for PASCAL and CDECL calling convention), so we simply
603      * copy them to the 16-bit stack ...
604      */
605     char *stack = (char *)CURRENT_STACK16 - cbArgs;
606
607     memcpy( stack, pArgs, cbArgs );
608
609     if (dwFlags & (WCB16_REGS|WCB16_REGS_LONG))
610     {
611         CONTEXT *context = (CONTEXT *)pdwRetCode;
612
613         if (TRACE_ON(relay))
614         {
615             DWORD count = cbArgs / sizeof(WORD);
616             WORD * wstack = (WORD *)stack;
617
618             DPRINTF("%04lx:CallTo16(func=%04lx:%04x,ds=%04lx",
619                     GetCurrentThreadId(),
620                     context->SegCs, LOWORD(context->Eip), context->SegDs );
621             while (count) DPRINTF( ",%04x", wstack[--count] );
622             DPRINTF(") ss:sp=%04x:%04x",
623                     SELECTOROF(NtCurrentTeb()->cur_stack), OFFSETOF(NtCurrentTeb()->cur_stack) );
624             DPRINTF(" ax=%04x bx=%04x cx=%04x dx=%04x si=%04x di=%04x bp=%04x es=%04x fs=%04x\n",
625                     (WORD)context->Eax, (WORD)context->Ebx, (WORD)context->Ecx,
626                     (WORD)context->Edx, (WORD)context->Esi, (WORD)context->Edi,
627                     (WORD)context->Ebp, (WORD)context->SegEs, (WORD)context->SegFs );
628             SYSLEVEL_CheckNotLevel( 2 );
629         }
630
631         if (context->EFlags & 0x00020000)  /* v86 mode */
632         {
633             EXCEPTION_REGISTRATION_RECORD frame;
634             frame.Handler = vm86_handler;
635             __wine_push_frame( &frame );
636             __wine_enter_vm86( context );
637             __wine_pop_frame( &frame );
638         }
639         else
640         {
641             /* push return address */
642             if (dwFlags & WCB16_REGS_LONG)
643             {
644                 stack -= sizeof(DWORD);
645                 *((DWORD *)stack) = HIWORD(call16_ret_addr);
646                 stack -= sizeof(DWORD);
647                 *((DWORD *)stack) = LOWORD(call16_ret_addr);
648                 cbArgs += 2 * sizeof(DWORD);
649             }
650             else
651             {
652                 stack -= sizeof(SEGPTR);
653                 *((SEGPTR *)stack) = call16_ret_addr;
654                 cbArgs += sizeof(SEGPTR);
655             }
656
657             /*
658              * Start call by checking for pending events.
659              * Note that wine_call_to_16_regs overwrites context stack
660              * pointer so we may modify it here without a problem.
661              */
662             if (NtCurrentTeb()->dpmi_vif)
663             {
664                 context->SegSs = wine_get_ds();
665                 context->Esp   = (DWORD)stack;
666                 insert_event_check( context );
667                 cbArgs += (DWORD)stack - context->Esp;
668             }
669
670             _EnterWin16Lock();
671             wine_call_to_16_regs( context, cbArgs, call16_handler );
672             _LeaveWin16Lock();
673         }
674
675         if (TRACE_ON(relay))
676         {
677             DPRINTF("%04lx:RetFrom16() ss:sp=%04x:%04x ",
678                     GetCurrentThreadId(), SELECTOROF(NtCurrentTeb()->cur_stack),
679                     OFFSETOF(NtCurrentTeb()->cur_stack));
680             DPRINTF(" ax=%04x bx=%04x cx=%04x dx=%04x bp=%04x sp=%04x\n",
681                     (WORD)context->Eax, (WORD)context->Ebx, (WORD)context->Ecx,
682                     (WORD)context->Edx, (WORD)context->Ebp, (WORD)context->Esp );
683             SYSLEVEL_CheckNotLevel( 2 );
684         }
685     }
686     else
687     {
688         DWORD ret;
689
690         if (TRACE_ON(relay))
691         {
692             DWORD count = cbArgs / sizeof(WORD);
693             WORD * wstack = (WORD *)stack;
694
695             DPRINTF("%04lx:CallTo16(func=%04x:%04x,ds=%04x",
696                     GetCurrentThreadId(), HIWORD(vpfn16), LOWORD(vpfn16),
697                     SELECTOROF(NtCurrentTeb()->cur_stack) );
698             while (count) DPRINTF( ",%04x", wstack[--count] );
699             DPRINTF(") ss:sp=%04x:%04x\n",
700                     SELECTOROF(NtCurrentTeb()->cur_stack), OFFSETOF(NtCurrentTeb()->cur_stack) );
701             SYSLEVEL_CheckNotLevel( 2 );
702         }
703
704         /* push return address */
705         stack -= sizeof(SEGPTR);
706         *((SEGPTR *)stack) = call16_ret_addr;
707         cbArgs += sizeof(SEGPTR);
708
709         /*
710          * Actually, we should take care whether the called routine cleans up
711          * its stack or not.  Fortunately, our wine_call_to_16 core doesn't rely on
712          * the callee to do so; after the routine has returned, the 16-bit
713          * stack pointer is always reset to the position it had before.
714          */
715         _EnterWin16Lock();
716         ret = wine_call_to_16( (FARPROC16)vpfn16, cbArgs, call16_handler );
717         if (pdwRetCode) *pdwRetCode = ret;
718         _LeaveWin16Lock();
719
720         if (TRACE_ON(relay))
721         {
722             DPRINTF("%04lx:RetFrom16() ss:sp=%04x:%04x retval=%08lx\n",
723                     GetCurrentThreadId(), SELECTOROF(NtCurrentTeb()->cur_stack),
724                     OFFSETOF(NtCurrentTeb()->cur_stack), ret);
725             SYSLEVEL_CheckNotLevel( 2 );
726         }
727     }
728 #else
729     assert(0);  /* cannot call to 16-bit on non-Intel architectures */
730 #endif  /* __i386__ */
731
732     return TRUE;  /* success */
733 }
734
735 /**********************************************************************
736  *           K32WOWCallback16            (KERNEL32.54)
737  */
738 DWORD WINAPI K32WOWCallback16( DWORD vpfn16, DWORD dwParam )
739 {
740     DWORD ret;
741
742     if ( !K32WOWCallback16Ex( vpfn16, WCB16_PASCAL,
743                            sizeof(DWORD), &dwParam, &ret ) )
744         ret = 0L;
745
746     return ret;
747 }
748
749
750 /*
751  *  16-bit WOW routines (in KERNEL)
752  */
753
754 /**********************************************************************
755  *           GetVDMPointer32W      (KERNEL.516)
756  */
757 DWORD WINAPI GetVDMPointer32W16( SEGPTR vp, UINT16 fMode )
758 {
759     GlobalPageLock16(GlobalHandle16(SELECTOROF(vp)));
760     return (DWORD)K32WOWGetVDMPointer( vp, 0, (DWORD)fMode );
761 }
762
763 /***********************************************************************
764  *           LoadLibraryEx32W      (KERNEL.513)
765  */
766 DWORD WINAPI LoadLibraryEx32W16( LPCSTR lpszLibFile, DWORD hFile, DWORD dwFlags )
767 {
768     HMODULE hModule;
769     DWORD mutex_count;
770     OFSTRUCT ofs;
771     const char *p;
772
773     if (!lpszLibFile)
774     {
775         SetLastError(ERROR_INVALID_PARAMETER);
776         return 0;
777     }
778
779     /* if the file can not be found, call LoadLibraryExA anyway, since it might be
780        a builtin module. This case is handled in MODULE_LoadLibraryExA */
781
782     if ((p = strrchr( lpszLibFile, '.' )) && !strchr( p, '\\' ))  /* got an extension */
783     {
784         if (OpenFile16( lpszLibFile, &ofs, OF_EXIST ) != HFILE_ERROR16)
785             lpszLibFile = ofs.szPathName;
786     }
787     else
788     {
789         char buffer[MAX_PATH+4];
790         strcpy( buffer, lpszLibFile );
791         strcat( buffer, ".dll" );
792         if (OpenFile16( buffer, &ofs, OF_EXIST ) != HFILE_ERROR16)
793             lpszLibFile = ofs.szPathName;
794     }
795
796     ReleaseThunkLock( &mutex_count );
797     hModule = LoadLibraryExA( lpszLibFile, (HANDLE)hFile, dwFlags );
798     RestoreThunkLock( mutex_count );
799
800     return (DWORD)hModule;
801 }
802
803 /***********************************************************************
804  *           GetProcAddress32W     (KERNEL.515)
805  */
806 DWORD WINAPI GetProcAddress32W16( DWORD hModule, LPCSTR lpszProc )
807 {
808     return (DWORD)GetProcAddress( (HMODULE)hModule, lpszProc );
809 }
810
811 /***********************************************************************
812  *           FreeLibrary32W        (KERNEL.514)
813  */
814 DWORD WINAPI FreeLibrary32W16( DWORD hLibModule )
815 {
816     BOOL retv;
817     DWORD mutex_count;
818
819     ReleaseThunkLock( &mutex_count );
820     retv = FreeLibrary( (HMODULE)hLibModule );
821     RestoreThunkLock( mutex_count );
822     return (DWORD)retv;
823 }
824
825
826 /**********************************************************************
827  *           WOW_CallProc32W
828  */
829 static DWORD WOW_CallProc32W16( FARPROC proc32, DWORD nrofargs, DWORD *args )
830 {
831     DWORD ret;
832     DWORD mutex_count;
833
834     ReleaseThunkLock( &mutex_count );
835
836     /*
837      * FIXME:  If ( nrofargs & CPEX_DEST_CDECL ) != 0, we should call a
838      *         32-bit CDECL routine ...
839      */
840
841     if (!proc32) ret = 0;
842     else switch (nrofargs)
843     {
844     case 0: ret = proc32();
845             break;
846     case 1: ret = proc32(args[0]);
847             break;
848     case 2: ret = proc32(args[0],args[1]);
849             break;
850     case 3: ret = proc32(args[0],args[1],args[2]);
851             break;
852     case 4: ret = proc32(args[0],args[1],args[2],args[3]);
853             break;
854     case 5: ret = proc32(args[0],args[1],args[2],args[3],args[4]);
855             break;
856     case 6: ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5]);
857             break;
858     case 7: ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6]);
859             break;
860     case 8: ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7]);
861             break;
862     case 9: ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8]);
863             break;
864     case 10:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9]);
865             break;
866     case 11:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10]);
867             break;
868     case 12:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11]);
869             break;
870     case 13:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11],args[12]);
871             break;
872     case 14:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11],args[12],args[13]);
873             break;
874     case 15:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11],args[12],args[13],args[14]);
875             break;
876     default:
877             /* FIXME: should go up to 32  arguments */
878             ERR("Unsupported number of arguments %ld, please report.\n",nrofargs);
879             ret = 0;
880             break;
881     }
882
883     RestoreThunkLock( mutex_count );
884
885     TRACE("returns %08lx\n",ret);
886     return ret;
887 }
888
889 /**********************************************************************
890  *           CallProc32W           (KERNEL.517)
891  */
892 DWORD WINAPIV CallProc32W16( DWORD nrofargs, DWORD argconvmask, FARPROC proc32, VA_LIST16 valist )
893 {
894     DWORD args[32];
895     unsigned int i;
896
897     TRACE("(%ld,%ld,%p args[",nrofargs,argconvmask,proc32);
898
899     for (i=0;i<nrofargs;i++)
900     {
901         if (argconvmask & (1<<i))
902         {
903             SEGPTR ptr = VA_ARG16( valist, SEGPTR );
904             /* pascal convention, have to reverse the arguments order */
905             args[nrofargs - i - 1] = (DWORD)MapSL(ptr);
906             TRACE("%08lx(%p),",ptr,MapSL(ptr));
907         }
908         else
909         {
910             DWORD arg = VA_ARG16( valist, DWORD );
911             /* pascal convention, have to reverse the arguments order */
912             args[nrofargs - i - 1] = arg;
913             TRACE("%ld,", arg);
914         }
915     }
916     TRACE("])\n");
917
918     /* POP nrofargs DWORD arguments and 3 DWORD parameters */
919     stack16_pop( (3 + nrofargs) * sizeof(DWORD) );
920
921     return WOW_CallProc32W16( proc32, nrofargs, args );
922 }
923
924 /**********************************************************************
925  *           _CallProcEx32W         (KERNEL.518)
926  */
927 DWORD WINAPIV CallProcEx32W16( DWORD nrofargs, DWORD argconvmask, FARPROC proc32, VA_LIST16 valist )
928 {
929     DWORD args[32];
930     unsigned int i;
931
932     TRACE("(%ld,%ld,%p args[",nrofargs,argconvmask,proc32);
933
934     for (i=0;i<nrofargs;i++)
935     {
936         if (argconvmask & (1<<i))
937         {
938             SEGPTR ptr = VA_ARG16( valist, SEGPTR );
939             args[i] = (DWORD)MapSL(ptr);
940             TRACE("%08lx(%p),",ptr,MapSL(ptr));
941         }
942         else
943         {
944             DWORD arg = VA_ARG16( valist, DWORD );
945             args[i] = arg;
946             TRACE("%ld,", arg);
947         }
948     }
949     TRACE("])\n");
950     return WOW_CallProc32W16( proc32, nrofargs, args );
951 }
952
953
954 /**********************************************************************
955  *           WOW16Call               (KERNEL.500)
956  *
957  * FIXME!!!
958  *
959  */
960 DWORD WINAPIV WOW16Call(WORD x, WORD y, WORD z, VA_LIST16 args)
961 {
962         int     i;
963         DWORD   calladdr;
964         FIXME("(0x%04x,0x%04x,%d),calling (",x,y,z);
965
966         for (i=0;i<x/2;i++) {
967                 WORD    a = VA_ARG16(args,WORD);
968                 DPRINTF("%04x ",a);
969         }
970         calladdr = VA_ARG16(args,DWORD);
971         stack16_pop( 3*sizeof(WORD) + x + sizeof(DWORD) );
972         DPRINTF(") calling address was 0x%08lx\n",calladdr);
973         return 0;
974 }
975
976
977 /***********************************************************************
978  *           CreateThread16   (KERNEL.441)
979  */
980 HANDLE WINAPI CreateThread16( SECURITY_ATTRIBUTES *sa, DWORD stack,
981                               FARPROC16 start, SEGPTR param,
982                               DWORD flags, LPDWORD id )
983 {
984     struct thread_args *args = HeapAlloc( GetProcessHeap(), 0, sizeof(*args) );
985     if (!args) return INVALID_HANDLE_VALUE;
986     args->proc = start;
987     args->param = param;
988     return CreateThread( sa, stack, start_thread16, args, flags, id );
989 }