kernel32: Update version to Win 10.
[wine.git] / dlls / krnl386.exe16 / relay.c
blob6eab9970a909e8049b2ee5c518ad861c53960223
1 /*
2 * Copyright 1993 Robert J. Amstadt
3 * Copyright 1995 Alexandre Julliard
4 * Copyright 2002 Jukka Heinonen
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.
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.
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
21 #include <assert.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdarg.h>
25 #include <stdio.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wine/winbase16.h"
30 #include "winternl.h"
31 #include "kernel16_private.h"
32 #include "dosexe.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(relay);
38 * Magic DWORD used to check stack integrity.
40 #define RELAY_MAGIC 0xabcdef00
43 * Memory block for temporary 16-bit stacks used with relay calls.
45 typedef struct {
46 DWORD inuse; /* non-zero if stack block is in use */
47 DWORD eip; /* saved ip */
48 DWORD seg_cs; /* saved cs */
49 DWORD esp; /* saved sp */
50 DWORD seg_ss; /* saved ss */
51 DWORD stack_bottom; /* guard dword */
52 BYTE stack[256-7*4]; /* 16-bit stack */
53 DWORD stack_top; /* guard dword */
54 } RELAY_Stack16;
57 static const char **debug_relay_excludelist;
58 static const char **debug_relay_includelist;
59 static const char **debug_snoop_excludelist;
60 static const char **debug_snoop_includelist;
63 /***********************************************************************
64 * build_list
66 * Build a function list from a ';'-separated string.
68 static const char **build_list( const WCHAR *buffer )
70 int count = 1;
71 const WCHAR *p = buffer;
72 const char **ret;
74 while ((p = wcschr( p, ';' )))
76 count++;
77 p++;
79 /* allocate count+1 pointers, plus the space for a copy of the string */
80 if ((ret = RtlAllocateHeap( GetProcessHeap(), 0,
81 (count + 1) * sizeof(char *) + (lstrlenW(buffer) + 1) )))
83 char *str = (char *)(ret + count + 1);
84 char *p = str;
86 while ((*str++ = *buffer++));
87 count = 0;
88 for (;;)
90 ret[count++] = p;
91 if (!(p = strchr( p, ';' ))) break;
92 *p++ = 0;
94 ret[count++] = NULL;
96 return ret;
100 /***********************************************************************
101 * RELAY16_InitDebugLists
103 * Build the relay include/exclude function lists.
105 void RELAY16_InitDebugLists(void)
107 OBJECT_ATTRIBUTES attr;
108 UNICODE_STRING name;
109 char buffer[1024];
110 HANDLE root, hkey;
111 DWORD count;
112 WCHAR *str;
113 static const WCHAR configW[] = {'S','o','f','t','w','a','r','e','\\',
114 'W','i','n','e','\\',
115 'D','e','b','u','g',0};
116 static const WCHAR RelayIncludeW[] = {'R','e','l','a','y','I','n','c','l','u','d','e',0};
117 static const WCHAR RelayExcludeW[] = {'R','e','l','a','y','E','x','c','l','u','d','e',0};
118 static const WCHAR SnoopIncludeW[] = {'S','n','o','o','p','I','n','c','l','u','d','e',0};
119 static const WCHAR SnoopExcludeW[] = {'S','n','o','o','p','E','x','c','l','u','d','e',0};
121 RtlOpenCurrentUser( KEY_READ, &root );
122 attr.Length = sizeof(attr);
123 attr.RootDirectory = root;
124 attr.ObjectName = &name;
125 attr.Attributes = 0;
126 attr.SecurityDescriptor = NULL;
127 attr.SecurityQualityOfService = NULL;
128 RtlInitUnicodeString( &name, configW );
130 /* @@ Wine registry key: HKCU\Software\Wine\Debug */
131 if (NtOpenKey( &hkey, KEY_READ, &attr )) hkey = 0;
132 NtClose( root );
133 if (!hkey) return;
135 str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)buffer)->Data;
136 RtlInitUnicodeString( &name, RelayIncludeW );
137 if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
139 debug_relay_includelist = build_list( str );
142 RtlInitUnicodeString( &name, RelayExcludeW );
143 if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
145 debug_relay_excludelist = build_list( str );
148 RtlInitUnicodeString( &name, SnoopIncludeW );
149 if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
151 debug_snoop_includelist = build_list( str );
154 RtlInitUnicodeString( &name, SnoopExcludeW );
155 if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
157 debug_snoop_excludelist = build_list( str );
159 NtClose( hkey );
163 /***********************************************************************
164 * check_list
166 * Check if a given module and function is in the list.
168 static BOOL check_list( const char *module, int ordinal, const char *func, const char **list )
170 char ord_str[10];
172 sprintf( ord_str, "%d", ordinal );
173 for(; *list; list++)
175 const char *p = strrchr( *list, '.' );
176 if (p && p > *list) /* check module and function */
178 int len = p - *list;
179 if (_strnicmp( module, *list, len-1 ) || module[len]) continue;
180 if (p[1] == '*' && !p[2]) return TRUE;
181 if (!strcmp( ord_str, p + 1 )) return TRUE;
182 if (func && !stricmp( func, p + 1 )) return TRUE;
184 else /* function only */
186 if (func && !stricmp( func, *list )) return TRUE;
189 return FALSE;
193 /***********************************************************************
194 * RELAY_ShowDebugmsgRelay
196 * Simple function to decide if a particular debugging message is
197 * wanted.
199 static BOOL RELAY_ShowDebugmsgRelay(const char *module, int ordinal, const char *func)
201 if (debug_relay_excludelist && check_list( module, ordinal, func, debug_relay_excludelist ))
202 return FALSE;
203 if (debug_relay_includelist && !check_list( module, ordinal, func, debug_relay_includelist ))
204 return FALSE;
205 return TRUE;
209 /***********************************************************************
210 * SNOOP16_ShowDebugmsgSnoop
212 * Simple function to decide if a particular debugging message is
213 * wanted.
215 BOOL SNOOP16_ShowDebugmsgSnoop(const char *module, int ordinal, const char *func)
217 if (debug_snoop_excludelist && check_list( module, ordinal, func, debug_snoop_excludelist ))
218 return FALSE;
219 if (debug_snoop_includelist && !check_list( module, ordinal, func, debug_snoop_includelist ))
220 return FALSE;
221 return TRUE;
225 /***********************************************************************
226 * get_entry_point
228 * Return the ordinal, name, and type info corresponding to a CS:IP address.
230 static const CALLFROM16 *get_entry_point( STACK16FRAME *frame, LPSTR module, LPSTR func, WORD *pOrd )
232 WORD i, max_offset;
233 register BYTE *p;
234 NE_MODULE *pModule;
235 ET_BUNDLE *bundle;
236 ET_ENTRY *entry;
238 *pOrd = 0;
239 if (!(pModule = NE_GetPtr( FarGetOwner16( GlobalHandle16( frame->module_cs ) ))))
240 return NULL;
242 max_offset = 0;
243 bundle = (ET_BUNDLE *)((BYTE *)pModule + pModule->ne_enttab);
246 entry = (ET_ENTRY *)((BYTE *)bundle+6);
247 for (i = bundle->first + 1; i <= bundle->last; i++)
249 if ((entry->offs < frame->entry_ip)
250 && (entry->segnum == 1) /* code segment ? */
251 && (entry->offs >= max_offset))
253 max_offset = entry->offs;
254 *pOrd = i;
256 entry++;
258 } while ( (bundle->next)
259 && (bundle = (ET_BUNDLE *)((BYTE *)pModule+bundle->next)));
261 /* Search for the name in the resident names table */
262 /* (built-in modules have no non-resident table) */
264 p = (BYTE *)pModule + pModule->ne_restab;
265 memcpy( module, p + 1, *p );
266 module[*p] = 0;
268 while (*p)
270 p += *p + 1 + sizeof(WORD);
271 if (*(WORD *)(p + *p + 1) == *pOrd) break;
273 memcpy( func, p + 1, *p );
274 func[*p] = 0;
276 /* Retrieve entry point call structure */
277 p = MapSL( MAKESEGPTR( frame->module_cs, frame->callfrom_ip ) );
278 /* p now points to lret, get the start of CALLFROM16 structure */
279 return (CALLFROM16 *)(p - FIELD_OFFSET( CALLFROM16, ret ));
283 extern int call_entry_point( void *func, int nb_args, const int *args );
284 __ASM_GLOBAL_FUNC( call_entry_point,
285 "pushl %ebp\n\t"
286 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
287 __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
288 "movl %esp,%ebp\n\t"
289 __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
290 "pushl %esi\n\t"
291 __ASM_CFI(".cfi_rel_offset %esi,-4\n\t")
292 "pushl %edi\n\t"
293 __ASM_CFI(".cfi_rel_offset %edi,-8\n\t")
294 "movl 12(%ebp),%edx\n\t"
295 "shll $2,%edx\n\t"
296 "jz 1f\n\t"
297 "subl %edx,%esp\n\t"
298 "andl $~15,%esp\n\t"
299 "movl 12(%ebp),%ecx\n\t"
300 "movl 16(%ebp),%esi\n\t"
301 "movl %esp,%edi\n\t"
302 "cld\n\t"
303 "rep; movsl\n"
304 "1:\tcall *8(%ebp)\n\t"
305 "leal -8(%ebp),%esp\n\t"
306 "popl %edi\n\t"
307 __ASM_CFI(".cfi_same_value %edi\n\t")
308 "popl %esi\n\t"
309 __ASM_CFI(".cfi_same_value %esi\n\t")
310 "popl %ebp\n\t"
311 __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
312 __ASM_CFI(".cfi_same_value %ebp\n\t")
313 "ret" )
316 /***********************************************************************
317 * relay_call_from_16_no_debug
319 * Same as relay_call_from_16 but doesn't print any debug information.
321 static int relay_call_from_16_no_debug( void *entry_point, unsigned char *args16, CONTEXT *context,
322 const CALLFROM16 *call )
324 unsigned int i, j, nb_args = 0;
325 int args32[20];
327 /* look for the ret instruction */
328 for (j = 0; j < ARRAY_SIZE(call->ret); j++)
329 if (call->ret[j] == 0xca66 || call->ret[j] == 0xcb66) break;
331 if (call->ret[j] == 0xcb66) /* cdecl */
333 for (i = 0; i < 20; i++, nb_args++)
335 int type = (call->arg_types[i / 10] >> (3 * (i % 10))) & 7;
337 if (type == ARG_NONE) break;
338 switch(type)
340 case ARG_WORD:
341 args32[nb_args] = *(WORD *)args16;
342 args16 += sizeof(WORD);
343 break;
344 case ARG_SWORD:
345 args32[nb_args] = *(short *)args16;
346 args16 += sizeof(WORD);
347 break;
348 case ARG_LONG:
349 case ARG_SEGSTR:
350 args32[nb_args] = *(int *)args16;
351 args16 += sizeof(int);
352 break;
353 case ARG_PTR:
354 case ARG_STR:
355 args32[nb_args] = (int)MapSL( *(SEGPTR *)args16 );
356 args16 += sizeof(SEGPTR);
357 break;
358 case ARG_VARARG:
359 args32[nb_args] = (int)args16;
360 break;
361 default:
362 break;
366 else /* not cdecl */
368 /* Start with the last arg */
369 args16 += call->ret[j + 1];
370 for (i = 0; i < 20; i++, nb_args++)
372 int type = (call->arg_types[i / 10] >> (3 * (i % 10))) & 7;
374 if (type == ARG_NONE) break;
375 switch(type)
377 case ARG_WORD:
378 args16 -= sizeof(WORD);
379 args32[nb_args] = *(WORD *)args16;
380 break;
381 case ARG_SWORD:
382 args16 -= sizeof(WORD);
383 args32[nb_args] = *(short *)args16;
384 break;
385 case ARG_LONG:
386 case ARG_SEGSTR:
387 args16 -= sizeof(int);
388 args32[nb_args] = *(int *)args16;
389 break;
390 case ARG_PTR:
391 case ARG_STR:
392 args16 -= sizeof(SEGPTR);
393 args32[nb_args] = (int)MapSL( *(SEGPTR *)args16 );
394 break;
395 default:
396 break;
401 if (!j) /* register function */
402 args32[nb_args++] = (int)context;
404 SYSLEVEL_CheckNotLevel( 2 );
406 return call_entry_point( entry_point, nb_args, args32 );
410 /***********************************************************************
411 * relay_call_from_16
413 * Replacement for the 16-bit relay functions when relay debugging is on.
415 int relay_call_from_16( void *entry_point, unsigned char *args16, CONTEXT *context )
417 STACK16FRAME *frame;
418 WORD ordinal;
419 unsigned int i, j, nb_args = 0;
420 int ret_val, args32[20];
421 char module[10], func[64];
422 const CALLFROM16 *call;
424 frame = CURRENT_STACK16;
425 call = get_entry_point( frame, module, func, &ordinal );
426 if (!TRACE_ON(relay) || !RELAY_ShowDebugmsgRelay( module, ordinal, func ))
427 return relay_call_from_16_no_debug( entry_point, args16, context, call );
429 TRACE( "\1Call %s.%d: %s(", module, ordinal, func );
431 /* look for the ret instruction */
432 for (j = 0; j < ARRAY_SIZE(call->ret); j++)
433 if (call->ret[j] == 0xca66 || call->ret[j] == 0xcb66) break;
435 if (call->ret[j] == 0xcb66) /* cdecl */
437 for (i = 0; i < 20; i++, nb_args++)
439 int type = (call->arg_types[i / 10] >> (3 * (i % 10))) & 7;
441 if (type == ARG_NONE) break;
442 if (i) TRACE( "," );
443 switch(type)
445 case ARG_WORD:
446 TRACE( "%04x", *(WORD *)args16 );
447 args32[nb_args] = *(WORD *)args16;
448 args16 += sizeof(WORD);
449 break;
450 case ARG_SWORD:
451 TRACE( "%04x", *(WORD *)args16 );
452 args32[nb_args] = *(short *)args16;
453 args16 += sizeof(WORD);
454 break;
455 case ARG_LONG:
456 TRACE( "%08x", *(int *)args16 );
457 args32[nb_args] = *(int *)args16;
458 args16 += sizeof(int);
459 break;
460 case ARG_PTR:
461 TRACE( "%04x:%04x", *(WORD *)(args16+2), *(WORD *)args16 );
462 args32[nb_args] = (int)MapSL( *(SEGPTR *)args16 );
463 args16 += sizeof(SEGPTR);
464 break;
465 case ARG_STR:
466 TRACE( "%08x %s", *(int *)args16, debugstr_a( MapSL(*(SEGPTR *)args16 )));
467 args32[nb_args] = (int)MapSL( *(SEGPTR *)args16 );
468 args16 += sizeof(int);
469 break;
470 case ARG_SEGSTR:
471 TRACE( "%04x:%04x %s", *(WORD *)(args16+2), *(WORD *)args16,
472 debugstr_a( MapSL(*(SEGPTR *)args16 )) );
473 args32[nb_args] = *(SEGPTR *)args16;
474 args16 += sizeof(SEGPTR);
475 break;
476 case ARG_VARARG:
477 TRACE( "..." );
478 args32[nb_args] = (int)args16;
479 break;
480 default:
481 break;
485 else /* not cdecl */
487 /* Start with the last arg */
488 args16 += call->ret[j + 1];
489 for (i = 0; i < 20; i++, nb_args++)
491 int type = (call->arg_types[i / 10] >> (3 * (i % 10))) & 7;
493 if (type == ARG_NONE) break;
494 if (i) TRACE( "," );
495 switch(type)
497 case ARG_WORD:
498 args16 -= sizeof(WORD);
499 args32[nb_args] = *(WORD *)args16;
500 TRACE( "%04x", *(WORD *)args16 );
501 break;
502 case ARG_SWORD:
503 args16 -= sizeof(WORD);
504 args32[nb_args] = *(short *)args16;
505 TRACE( "%04x", *(WORD *)args16 );
506 break;
507 case ARG_LONG:
508 args16 -= sizeof(int);
509 args32[nb_args] = *(int *)args16;
510 TRACE( "%08x", *(int *)args16 );
511 break;
512 case ARG_PTR:
513 args16 -= sizeof(SEGPTR);
514 args32[nb_args] = (int)MapSL( *(SEGPTR *)args16 );
515 TRACE( "%04x:%04x", *(WORD *)(args16+2), *(WORD *)args16 );
516 break;
517 case ARG_STR:
518 args16 -= sizeof(int);
519 args32[nb_args] = (int)MapSL( *(SEGPTR *)args16 );
520 TRACE( "%08x %s", *(int *)args16, debugstr_a( MapSL(*(SEGPTR *)args16 )));
521 break;
522 case ARG_SEGSTR:
523 args16 -= sizeof(SEGPTR);
524 args32[nb_args] = *(SEGPTR *)args16;
525 TRACE( "%04x:%04x %s", *(WORD *)(args16+2), *(WORD *)args16,
526 debugstr_a( MapSL(*(SEGPTR *)args16 )) );
527 break;
528 case ARG_VARARG:
529 TRACE( "..." );
530 args32[nb_args] = (int)args16;
531 break;
532 default:
533 break;
538 if (!j) /* register function */
540 args32[nb_args++] = (int)context;
541 TRACE( ") ret=%04x:%04x ax=%04x bx=%04x cx=%04x dx=%04x si=%04x di=%04x ds=%04x es=%04x efl=%08x\n",
542 frame->cs, frame->ip, (WORD)context->Eax, (WORD)context->Ebx, (WORD)context->Ecx,
543 (WORD)context->Edx, (WORD)context->Esi, (WORD)context->Edi, (WORD)context->SegDs,
544 (WORD)context->SegEs, context->EFlags );
546 else TRACE( ") ret=%04x:%04x ds=%04x\n", frame->cs, frame->ip, frame->ds );
548 SYSLEVEL_CheckNotLevel( 2 );
550 ret_val = call_entry_point( entry_point, nb_args, args32 );
552 SYSLEVEL_CheckNotLevel( 2 );
554 TRACE( "\1Ret %s.%d: %s() ", module, ordinal, func );
555 if (!j) /* register function */
557 TRACE( "retval=none ret=%04x:%04x ax=%04x bx=%04x cx=%04x dx=%04x si=%04x di=%04x ds=%04x es=%04x efl=%08x\n",
558 (WORD)context->SegCs, LOWORD(context->Eip), (WORD)context->Eax, (WORD)context->Ebx,
559 (WORD)context->Ecx, (WORD)context->Edx, (WORD)context->Esi, (WORD)context->Edi,
560 (WORD)context->SegDs, (WORD)context->SegEs, context->EFlags );
562 else
564 frame = CURRENT_STACK16; /* might have be changed by the entry point */
565 if (j == 1) /* 16-bit return sequence */
566 TRACE( "retval=%04x ret=%04x:%04x ds=%04x\n",
567 ret_val & 0xffff, frame->cs, frame->ip, frame->ds );
568 else
569 TRACE( "retval=%08x ret=%04x:%04x ds=%04x\n",
570 ret_val, frame->cs, frame->ip, frame->ds );
572 return ret_val;
575 /**********************************************************************
576 * RELAY_GetPointer
578 * Get pointer to stack block when given esp pointing to 16-bit stack
579 * inside relay data segment.
581 static RELAY_Stack16 *RELAY_GetPointer( DWORD offset )
583 offset = offset / sizeof(RELAY_Stack16) * sizeof(RELAY_Stack16);
584 return MapSL(MAKESEGPTR(relay_data_sel, offset));
588 /**********************************************************************
589 * RELAY_MakeShortContext
591 * Allocate separate 16-bit stack, make stack pointer point to this
592 * stack and make code pointer point to stub that restores everything.
593 * So, after this routine, SS and CS are guaranteed to be 16-bit.
595 * Note: This might be called from signal handler, so the stack
596 * allocation algorithm must be signal safe.
598 static void RELAY_MakeShortContext( CONTEXT *context )
600 DWORD offset = offsetof(RELAY_Stack16, stack_top);
601 RELAY_Stack16 *stack = RELAY_GetPointer( 0 );
603 while (stack->inuse && offset < DOSVM_RELAY_DATA_SIZE) {
604 stack++;
605 offset += sizeof(RELAY_Stack16);
608 if (offset >= DOSVM_RELAY_DATA_SIZE)
609 ERR( "Too many nested interrupts!\n" );
611 stack->inuse = 1;
612 stack->eip = context->Eip;
613 stack->seg_cs = context->SegCs;
614 stack->esp = context->Esp;
615 stack->seg_ss = context->SegSs;
617 stack->stack_bottom = RELAY_MAGIC;
618 stack->stack_top = RELAY_MAGIC;
620 context->SegSs = relay_data_sel;
621 context->Esp = offset;
622 context->SegCs = relay_code_sel;
623 context->Eip = 3;
627 /**********************************************************************
628 * RELAY_RelayStub
630 * This stub is called by __wine_call_from_16_regs in order to marshall
631 * relay parameters.
633 static void __stdcall RELAY_RelayStub( DOSRELAY proc, unsigned char *args, CONTEXT *context )
635 if (proc)
637 RELAY_Stack16 *stack = RELAY_GetPointer( context->Esp );
639 DWORD old_seg_cs = context->SegCs;
640 DWORD old_eip = context->Eip;
641 DWORD old_seg_ss = context->SegSs;
642 DWORD old_esp = context->Esp;
644 context->SegCs = stack->seg_cs;
645 context->Eip = stack->eip;
646 context->SegSs = stack->seg_ss;
647 context->Esp = stack->esp;
649 proc( context, *(LPVOID *)args );
651 stack->seg_cs = context->SegCs;
652 stack->eip = context->Eip;
653 stack->seg_ss = context->SegSs;
654 stack->esp = context->Esp;
656 context->SegCs = old_seg_cs;
657 context->Eip = old_eip;
658 context->SegSs = old_seg_ss;
659 context->Esp = old_esp;
664 /**********************************************************************
665 * DOSVM_RelayHandler
667 * Restore saved code and stack pointers and release stack block.
669 void DOSVM_RelayHandler( CONTEXT *context )
671 RELAY_Stack16 *stack = RELAY_GetPointer( context->Esp );
673 context->SegSs = stack->seg_ss;
674 context->Esp = stack->esp;
675 context->SegCs = stack->seg_cs;
676 context->Eip = stack->eip;
678 if (!stack->inuse ||
679 stack->stack_bottom != RELAY_MAGIC ||
680 stack->stack_top != RELAY_MAGIC)
681 ERR( "Stack corrupted!\n" );
683 stack->inuse = 0;
687 /**********************************************************************
688 * DOSVM_BuildCallFrame
690 * Modifies the context so that return to context calls DOSRELAY and
691 * only after return from DOSRELAY the original context will be returned to.
693 void DOSVM_BuildCallFrame( CONTEXT *context, DOSRELAY relay, LPVOID data )
695 WORD code_sel = relay_code_sel;
698 * Allocate separate stack for relay call.
700 RELAY_MakeShortContext( context );
703 * Build call frame.
705 PUSH_WORD16( context, HIWORD(data) ); /* argument.hiword */
706 PUSH_WORD16( context, LOWORD(data) ); /* argument.loword */
707 PUSH_WORD16( context, context->SegCs ); /* STACK16FRAME.cs */
708 PUSH_WORD16( context, LOWORD(context->Eip) ); /* STACK16FRAME.ip */
709 PUSH_WORD16( context, LOWORD(context->Ebp) ); /* STACK16FRAME.bp */
710 PUSH_WORD16( context, HIWORD(relay) ); /* STACK16FRAME.entry_point.hiword */
711 PUSH_WORD16( context, LOWORD(relay) ); /* STACK16FRAME.entry_point.loword */
712 PUSH_WORD16( context, 0 ); /* STACK16FRAME.entry_ip */
713 PUSH_WORD16( context, HIWORD(RELAY_RelayStub) ); /* STACK16FRAME.relay.hiword */
714 PUSH_WORD16( context, LOWORD(RELAY_RelayStub) ); /* STACK16FRAME.relay.loword */
715 PUSH_WORD16( context, 0 ); /* STACK16FRAME.module_cs.hiword */
716 PUSH_WORD16( context, code_sel ); /* STACK16FRAME.module_cs.loword */
717 PUSH_WORD16( context, 0 ); /* STACK16FRAME.callfrom_ip.hiword */
718 PUSH_WORD16( context, 0 ); /* STACK16FRAME.callfrom_ip.loword */
721 * Adjust code pointer.
723 context->SegCs = get_cs();
724 context->Eip = (DWORD)__wine_call_from_16_regs;