vkd3d: Disable printf format checks.
[wine.git] / tools / winebuild / relay.c
blob35a34d70fbf5424d4c6d6f5093a12edd1e5048ff
1 /*
2 * Relay calls helper routines
4 * Copyright 1993 Robert J. Amstadt
5 * Copyright 1995 Martin von Loewis
6 * Copyright 1995, 1996, 1997 Alexandre Julliard
7 * Copyright 1997 Eric Youngdale
8 * Copyright 1999 Ulrich Weigand
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "config.h"
27 #include <ctype.h>
28 #include <stdarg.h>
30 #include "build.h"
32 /* offset of the stack pointer relative to %fs:(0) */
33 #define STACKOFFSET 0x10c /* FIELD_OFFSET(TEB,SystemReserved1) */
35 /* fix this if the x86_thread_data structure is changed */
36 #define GS_OFFSET 0x1d8 /* FIELD_OFFSET(TEB,SystemReserved2) + FIELD_OFFSET(struct x86_thread_data,gs) */
39 /*******************************************************************
40 * BuildCallFrom16Core
42 * This routine builds the core routines used in 16->32 thunks:
43 * CallFrom16Word, CallFrom16Long, CallFrom16Register, and CallFrom16Thunk.
45 * These routines are intended to be called via a far call (with 32-bit
46 * operand size) from 16-bit code. The 16-bit code stub must push %bp,
47 * the 32-bit entry point to be called, and the argument conversion
48 * routine to be used (see stack layout below).
50 * The core routine completes the STACK16FRAME on the 16-bit stack and
51 * switches to the 32-bit stack. Then, the argument conversion routine
52 * is called; it gets passed the 32-bit entry point and a pointer to the
53 * 16-bit arguments (on the 16-bit stack) as parameters. (You can either
54 * use conversion routines automatically generated by BuildCallFrom16,
55 * or write your own for special purposes.)
57 * The conversion routine must call the 32-bit entry point, passing it
58 * the converted arguments, and return its return value to the core.
59 * After the conversion routine has returned, the core switches back
60 * to the 16-bit stack, converts the return value to the DX:AX format
61 * (CallFrom16Long), and returns to the 16-bit call stub. All parameters,
62 * including %bp, are popped off the stack.
64 * The 16-bit call stub now returns to the caller, popping the 16-bit
65 * arguments if necessary (pascal calling convention).
67 * In the case of a 'register' function, CallFrom16Register fills a
68 * CONTEXT86 structure with the values all registers had at the point
69 * the first instruction of the 16-bit call stub was about to be
70 * executed. A pointer to this CONTEXT86 is passed as third parameter
71 * to the argument conversion routine, which typically passes it on
72 * to the called 32-bit entry point.
74 * CallFrom16Thunk is a special variant used by the implementation of
75 * the Win95 16->32 thunk functions C16ThkSL and C16ThkSL01 and is
76 * implemented as follows:
77 * On entry, the EBX register is set up to contain a flat pointer to the
78 * 16-bit stack such that EBX+22 points to the first argument.
79 * Then, the entry point is called, while EBP is set up to point
80 * to the return address (on the 32-bit stack).
81 * The called function returns with CX set to the number of bytes
82 * to be popped of the caller's stack.
84 * Stack layout upon entry to the core routine (STACK16FRAME):
85 * ... ...
86 * (sp+24) word first 16-bit arg
87 * (sp+22) word cs
88 * (sp+20) word ip
89 * (sp+18) word bp
90 * (sp+14) long 32-bit entry point (reused for Win16 mutex recursion count)
91 * (sp+12) word ip of actual entry point (necessary for relay debugging)
92 * (sp+8) long relay (argument conversion) function entry point
93 * (sp+4) long cs of 16-bit entry point
94 * (sp) long ip of 16-bit entry point
96 * Added on the stack:
97 * (sp-2) word saved gs
98 * (sp-4) word saved fs
99 * (sp-6) word saved es
100 * (sp-8) word saved ds
101 * (sp-12) long saved ebp
102 * (sp-16) long saved ecx
103 * (sp-20) long saved edx
104 * (sp-24) long saved previous stack
106 static void BuildCallFrom16Core( int reg_func, int thunk )
108 /* Function header */
109 if (thunk) output_function_header( "__wine_call_from_16_thunk", 1 );
110 else if (reg_func) output_function_header( "__wine_call_from_16_regs", 1 );
111 else output_function_header( "__wine_call_from_16", 1 );
113 /* Create STACK16FRAME (except STACK32FRAME link) */
114 output( "\tpushw %%gs\n" );
115 output( "\tpushw %%fs\n" );
116 output( "\tpushw %%es\n" );
117 output( "\tpushw %%ds\n" );
118 output( "\tpushl %%ebp\n" );
119 output( "\tpushl %%ecx\n" );
120 output( "\tpushl %%edx\n" );
122 /* Save original EFlags register */
123 if (reg_func) output( "\tpushfl\n" );
125 if ( UsePIC )
127 output( "\tcall 1f\n" );
128 output( "1:\tpopl %%ecx\n" );
129 output( "\tmovl %%cs:%s-1b(%%ecx),%%edx\n", asm_name("CallTo16_DataSelector") );
131 else
132 output( "\tmovl %%cs:%s,%%edx\n", asm_name("CallTo16_DataSelector") );
134 /* Load 32-bit segment registers */
135 output( "\tmovw %%dx, %%ds\n" );
136 output( "\tmovw %%dx, %%es\n" );
138 if ( UsePIC )
139 output( "\tmovw %s-1b(%%ecx), %%fs\n", asm_name("CallTo16_TebSelector") );
140 else
141 output( "\tmovw %s, %%fs\n", asm_name("CallTo16_TebSelector") );
143 output( "\tmov %%fs:(%d),%%gs\n", GS_OFFSET );
145 /* Translate STACK16FRAME base to flat offset in %edx */
146 output( "\tmovw %%ss, %%dx\n" );
147 output( "\tandl $0xfff8, %%edx\n" );
148 output( "\tshrl $1, %%edx\n" );
149 if (UsePIC)
150 output( "\taddl .Lwine_ldt_copy_ptr-1b(%%ecx),%%edx\n" );
151 else
152 output( "\taddl .Lwine_ldt_copy_ptr,%%edx\n" );
153 output( "\tmovl (%%edx), %%edx\n" );
154 output( "\tmovzwl %%sp, %%ebp\n" );
155 output( "\tleal %d(%%ebp,%%edx), %%edx\n", reg_func ? 0 : -4 );
157 /* Get saved flags into %ecx */
158 if (reg_func) output( "\tpopl %%ecx\n" );
160 /* Get the 32-bit stack pointer from the TEB and complete STACK16FRAME */
161 output( "\tmovl %%fs:(%d), %%ebp\n", STACKOFFSET );
162 output( "\tpushl %%ebp\n" );
164 /* Switch stacks */
165 output( "\tmovw %%ss, %%fs:(%d)\n", STACKOFFSET + 2 );
166 output( "\tmovw %%sp, %%fs:(%d)\n", STACKOFFSET );
167 output( "\tpushl %%ds\n" );
168 output( "\tpopl %%ss\n" );
169 output( "\tmovl %%ebp, %%esp\n" );
170 output( "\taddl $0x20,%%ebp\n"); /* FIELD_OFFSET(STACK32FRAME,ebp) */
173 /* At this point:
174 STACK16FRAME is completely set up
175 DS, ES, SS: flat data segment
176 FS: current TEB
177 ESP: points to last STACK32FRAME
178 EBP: points to ebp member of last STACK32FRAME
179 EDX: points to current STACK16FRAME
180 ECX: contains saved flags
181 all other registers: unchanged */
183 /* Special case: C16ThkSL stub */
184 if ( thunk )
186 /* Set up registers as expected and call thunk */
187 output( "\tleal 0x1a(%%edx),%%ebx\n" ); /* sizeof(STACK16FRAME)-22 */
188 output( "\tleal -4(%%esp), %%ebp\n" );
190 output( "\tcall *0x26(%%edx)\n"); /* FIELD_OFFSET(STACK16FRAME,entry_point) */
192 /* Switch stack back */
193 output( "\tmovw %%fs:(%d), %%ss\n", STACKOFFSET+2 );
194 output( "\tmovzwl %%fs:(%d), %%esp\n", STACKOFFSET );
195 output( "\tpopl %%fs:(%d)\n", STACKOFFSET );
197 /* Restore registers and return directly to caller */
198 output( "\taddl $8, %%esp\n" );
199 output( "\tpopl %%ebp\n" );
200 output( "\tpopw %%ds\n" );
201 output( "\tpopw %%es\n" );
202 output( "\tpopw %%fs\n" );
203 output( "\tpopw %%gs\n" );
204 output( "\taddl $20, %%esp\n" );
206 output( "\txorb %%ch, %%ch\n" );
207 output( "\tpopl %%ebx\n" );
208 output( "\taddw %%cx, %%sp\n" );
209 output( "\tpush %%ebx\n" );
211 output( "\t.byte 0x66\n" );
212 output( "\tlret\n" );
214 output_function_size( "__wine_call_from_16_thunk" );
215 return;
219 /* Build register CONTEXT */
220 if ( reg_func )
222 output( "\tsubl $0x2cc,%%esp\n" ); /* sizeof(CONTEXT86) */
224 output( "\tmovl %%ecx,0xc0(%%esp)\n" ); /* EFlags */
226 output( "\tmovl %%eax,0xb0(%%esp)\n" ); /* Eax */
227 output( "\tmovl %%ebx,0xa4(%%esp)\n" ); /* Ebx */
228 output( "\tmovl %%esi,0xa0(%%esp)\n" ); /* Esi */
229 output( "\tmovl %%edi,0x9c(%%esp)\n" ); /* Edi */
231 output( "\tmovl 0x0c(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,ebp) */
232 output( "\tmovl %%eax,0xb4(%%esp)\n" ); /* Ebp */
233 output( "\tmovl 0x08(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,ecx) */
234 output( "\tmovl %%eax,0xac(%%esp)\n" ); /* Ecx */
235 output( "\tmovl 0x04(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,edx) */
236 output( "\tmovl %%eax,0xa8(%%esp)\n" ); /* Edx */
238 output( "\tmovzwl 0x10(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,ds) */
239 output( "\tmovl %%eax,0x98(%%esp)\n" ); /* SegDs */
240 output( "\tmovzwl 0x12(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,es) */
241 output( "\tmovl %%eax,0x94(%%esp)\n" ); /* SegEs */
242 output( "\tmovzwl 0x14(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,fs) */
243 output( "\tmovl %%eax,0x90(%%esp)\n" ); /* SegFs */
244 output( "\tmovzwl 0x16(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,gs) */
245 output( "\tmovl %%eax,0x8c(%%esp)\n" ); /* SegGs */
247 output( "\tmovzwl 0x2e(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,cs) */
248 output( "\tmovl %%eax,0xbc(%%esp)\n" ); /* SegCs */
249 output( "\tmovzwl 0x2c(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,ip) */
250 output( "\tmovl %%eax,0xb8(%%esp)\n" ); /* Eip */
252 output( "\tmovzwl %%fs:(%d), %%eax\n", STACKOFFSET+2 );
253 output( "\tmovl %%eax,0xc8(%%esp)\n" ); /* SegSs */
254 output( "\tmovzwl %%fs:(%d), %%eax\n", STACKOFFSET );
255 output( "\taddl $0x2c,%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,ip) */
256 output( "\tmovl %%eax,0xc4(%%esp)\n" ); /* Esp */
257 #if 0
258 output( "\tfsave 0x1c(%%esp)\n" ); /* FloatSave */
259 #endif
261 /* Push address of CONTEXT86 structure -- popped by the relay routine */
262 output( "\tmovl %%esp,%%eax\n" );
263 output( "\tandl $~15,%%esp\n" );
264 output( "\tsubl $4,%%esp\n" );
265 output( "\tpushl %%eax\n" );
267 else
269 output( "\tsubl $8,%%esp\n" );
270 output( "\tandl $~15,%%esp\n" );
271 output( "\taddl $8,%%esp\n" );
274 /* Call relay routine (which will call the API entry point) */
275 output( "\tleal 0x30(%%edx),%%eax\n" ); /* sizeof(STACK16FRAME) */
276 output( "\tpushl %%eax\n" );
277 output( "\tpushl 0x26(%%edx)\n"); /* FIELD_OFFSET(STACK16FRAME,entry_point) */
278 output( "\tcall *0x20(%%edx)\n"); /* FIELD_OFFSET(STACK16FRAME,relay) */
280 if ( reg_func )
282 output( "\tleal -748(%%ebp),%%ebx\n" ); /* sizeof(CONTEXT) + FIELD_OFFSET(STACK32FRAME,ebp) */
284 /* Switch stack back */
285 output( "\tmovw %%fs:(%d), %%ss\n", STACKOFFSET+2 );
286 output( "\tmovzwl %%fs:(%d), %%esp\n", STACKOFFSET );
287 output( "\tpopl %%fs:(%d)\n", STACKOFFSET );
289 /* Get return address to CallFrom16 stub */
290 output( "\taddw $0x14,%%sp\n" ); /* FIELD_OFFSET(STACK16FRAME,callfrom_ip)-4 */
291 output( "\tpopl %%eax\n" );
292 output( "\tpopl %%edx\n" );
294 /* Restore all registers from CONTEXT */
295 output( "\tmovw 0xc8(%%ebx),%%ss\n"); /* SegSs */
296 output( "\tmovl 0xc4(%%ebx),%%esp\n"); /* Esp */
297 output( "\taddl $4, %%esp\n" ); /* room for final return address */
299 output( "\tpushw 0xbc(%%ebx)\n"); /* SegCs */
300 output( "\tpushw 0xb8(%%ebx)\n"); /* Eip */
301 output( "\tpushl %%edx\n" );
302 output( "\tpushl %%eax\n" );
303 output( "\tpushl 0xc0(%%ebx)\n"); /* EFlags */
304 output( "\tpushl 0x98(%%ebx)\n"); /* SegDs */
306 output( "\tpushl 0x94(%%ebx)\n"); /* SegEs */
307 output( "\tpopl %%es\n" );
308 output( "\tpushl 0x90(%%ebx)\n"); /* SegFs */
309 output( "\tpopl %%fs\n" );
310 output( "\tpushl 0x8c(%%ebx)\n"); /* SegGs */
311 output( "\tpopl %%gs\n" );
313 output( "\tmovl 0xb4(%%ebx),%%ebp\n"); /* Ebp */
314 output( "\tmovl 0xa0(%%ebx),%%esi\n"); /* Esi */
315 output( "\tmovl 0x9c(%%ebx),%%edi\n"); /* Edi */
316 output( "\tmovl 0xb0(%%ebx),%%eax\n"); /* Eax */
317 output( "\tmovl 0xa8(%%ebx),%%edx\n"); /* Edx */
318 output( "\tmovl 0xac(%%ebx),%%ecx\n"); /* Ecx */
319 output( "\tmovl 0xa4(%%ebx),%%ebx\n"); /* Ebx */
321 output( "\tpopl %%ds\n" );
322 output( "\tpopfl\n" );
323 output( "\tlret\n" );
325 output_function_size( "__wine_call_from_16_regs" );
327 else
329 /* Switch stack back */
330 output( "\tmovw %%fs:(%d), %%ss\n", STACKOFFSET+2 );
331 output( "\tmovzwl %%fs:(%d), %%esp\n", STACKOFFSET );
332 output( "\tpopl %%fs:(%d)\n", STACKOFFSET );
334 /* Restore registers */
335 output( "\tpopl %%edx\n" );
336 output( "\tpopl %%ecx\n" );
337 output( "\tpopl %%ebp\n" );
338 output( "\tpopw %%ds\n" );
339 output( "\tpopw %%es\n" );
340 output( "\tpopw %%fs\n" );
341 output( "\tpopw %%gs\n" );
343 /* Return to return stub which will return to caller */
344 output( "\tlret $12\n" );
346 output_function_size( "__wine_call_from_16" );
351 /*******************************************************************
352 * BuildCallTo16Core
354 * This routine builds the core routines used in 32->16 thunks:
356 * extern DWORD WINAPI wine_call_to_16( FARPROC16 target, DWORD cbArgs, PEXCEPTION_HANDLER handler );
357 * extern void WINAPI wine_call_to_16_regs( CONTEXT86 *context, DWORD cbArgs, PEXCEPTION_HANDLER handler );
359 * These routines can be called directly from 32-bit code.
361 * All routines expect that the 16-bit stack contents (arguments) and the
362 * return address (segptr to CallTo16_Ret) were already set up by the
363 * caller; nb_args must contain the number of bytes to be conserved. The
364 * 16-bit SS:SP will be set accordingly.
366 * All other registers are either taken from the CONTEXT86 structure
367 * or else set to default values. The target routine address is either
368 * given directly or taken from the CONTEXT86.
370 static void BuildCallTo16Core( int reg_func )
372 const char *name = reg_func ? "wine_call_to_16_regs" : "wine_call_to_16";
373 const char *func_name = is_pe() ? strmake( "%s@12", name ) : name;
375 /* Function header */
376 output_function_header( func_name, 1 );
378 /* Function entry sequence */
379 output_cfi( ".cfi_startproc" );
380 output( "\tpushl %%ebp\n" );
381 output_cfi( ".cfi_adjust_cfa_offset 4" );
382 output_cfi( ".cfi_rel_offset %%ebp,0" );
383 output( "\tmovl %%esp, %%ebp\n" );
384 output_cfi( ".cfi_def_cfa_register %%ebp" );
386 /* Save the 32-bit registers */
387 output( "\tpushl %%ebx\n" );
388 output_cfi( ".cfi_rel_offset %%ebx,-4" );
389 output( "\tpushl %%esi\n" );
390 output_cfi( ".cfi_rel_offset %%esi,-8" );
391 output( "\tpushl %%edi\n" );
392 output_cfi( ".cfi_rel_offset %%edi,-12" );
393 output( "\tmov %%gs,%%fs:(%d)\n", GS_OFFSET );
395 /* Setup exception frame */
396 output( "\tpushl %%fs:(%d)\n", STACKOFFSET );
397 output( "\tpushl 16(%%ebp)\n" ); /* handler */
398 output( "\tpushl %%fs:(0)\n" );
399 output( "\tmovl %%esp,%%fs:(0)\n" );
401 /* Call the actual CallTo16 routine (simulate a lcall) */
402 output( "\tpushl %%cs\n" );
403 output( "\tcall .L%s\n", name );
405 /* Remove exception frame */
406 output( "\tpopl %%fs:(0)\n" );
407 output( "\taddl $4, %%esp\n" );
408 output( "\tpopl %%fs:(%d)\n", STACKOFFSET );
410 if ( !reg_func )
412 /* Convert return value */
413 output( "\tandl $0xffff,%%eax\n" );
414 output( "\tshll $16,%%edx\n" );
415 output( "\torl %%edx,%%eax\n" );
417 else
420 * Modify CONTEXT86 structure to contain new values
422 * NOTE: We restore only EAX, EBX, ECX, EDX, EBP, and ESP.
423 * The segment registers as well as ESI and EDI should
424 * not be modified by a well-behaved 16-bit routine in
425 * any case. [If necessary, we could restore them as well,
426 * at the cost of a somewhat less efficient return path.]
429 output( "\tmovl 0x14(%%esp),%%edi\n" ); /* FIELD_OFFSET(STACK32FRAME,target) - FIELD_OFFSET(STACK32FRAME,edi) */
430 /* everything above edi has been popped already */
432 output( "\tmovl %%eax,0xb0(%%edi)\n"); /* Eax */
433 output( "\tmovl %%ebx,0xa4(%%edi)\n"); /* Ebx */
434 output( "\tmovl %%ecx,0xac(%%edi)\n"); /* Ecx */
435 output( "\tmovl %%edx,0xa8(%%edi)\n"); /* Edx */
436 output( "\tmovl %%ebp,0xb4(%%edi)\n"); /* Ebp */
437 output( "\tmovl %%esi,0xc4(%%edi)\n"); /* Esp */
438 /* The return glue code saved %esp into %esi */
441 /* Restore the 32-bit registers */
442 output( "\tpopl %%edi\n" );
443 output_cfi( ".cfi_same_value %%edi" );
444 output( "\tpopl %%esi\n" );
445 output_cfi( ".cfi_same_value %%esi" );
446 output( "\tpopl %%ebx\n" );
447 output_cfi( ".cfi_same_value %%ebx" );
449 /* Function exit sequence */
450 output( "\tpopl %%ebp\n" );
451 output_cfi( ".cfi_def_cfa %%esp,4" );
452 output_cfi( ".cfi_same_value %%ebp" );
453 output( "\tret $12\n" );
454 output_cfi( ".cfi_endproc" );
457 /* Start of the actual CallTo16 routine */
459 output( ".L%s:\n", name );
461 /* Switch to the 16-bit stack */
462 output( "\tmovl %%esp,%%edx\n" );
463 output( "\tmovw %%fs:(%d),%%ss\n", STACKOFFSET + 2);
464 output( "\tmovw %%fs:(%d),%%sp\n", STACKOFFSET );
465 output( "\tmovl %%edx,%%fs:(%d)\n", STACKOFFSET );
467 /* Make %bp point to the previous stackframe (built by CallFrom16) */
468 output( "\tmovzwl %%sp,%%ebp\n" );
469 output( "\tleal 0x2a(%%ebp),%%ebp\n"); /* FIELD_OFFSET(STACK16FRAME,bp) */
471 /* Add the specified offset to the new sp */
472 output( "\tsubw 0x2c(%%edx), %%sp\n"); /* FIELD_OFFSET(STACK32FRAME,nb_args) */
474 if (reg_func)
476 /* Push the called routine address */
477 output( "\tmovl 0x28(%%edx),%%edx\n"); /* FIELD_OFFSET(STACK32FRAME,target) */
478 output( "\tpushw 0xbc(%%edx)\n"); /* SegCs */
479 output( "\tpushw 0xb8(%%edx)\n"); /* Eip */
481 /* Get the registers */
482 output( "\tpushw 0x98(%%edx)\n"); /* SegDs */
483 output( "\tpushl 0x94(%%edx)\n"); /* SegEs */
484 output( "\tpopl %%es\n" );
485 output( "\tmovl 0xb4(%%edx),%%ebp\n"); /* Ebp */
486 output( "\tmovl 0xa0(%%edx),%%esi\n"); /* Esi */
487 output( "\tmovl 0x9c(%%edx),%%edi\n"); /* Edi */
488 output( "\tmovl 0xb0(%%edx),%%eax\n"); /* Eax */
489 output( "\tmovl 0xa4(%%edx),%%ebx\n"); /* Ebx */
490 output( "\tmovl 0xac(%%edx),%%ecx\n"); /* Ecx */
491 output( "\tmovl 0xa8(%%edx),%%edx\n"); /* Edx */
493 /* Get the 16-bit ds */
494 output( "\tpopw %%ds\n" );
496 else /* not a register function */
498 /* Push the called routine address */
499 output( "\tpushl 0x28(%%edx)\n"); /* FIELD_OFFSET(STACK32FRAME,target) */
501 /* Set %fs and %gs to the value saved by the last CallFrom16 */
502 output( "\tpushw -22(%%ebp)\n" ); /* FIELD_OFFSET(STACK16FRAME,fs)-FIELD_OFFSET(STACK16FRAME,bp) */
503 output( "\tpopw %%fs\n" );
504 output( "\tpushw -20(%%ebp)\n" ); /* FIELD_OFFSET(STACK16FRAME,gs)-FIELD_OFFSET(STACK16FRAME,bp) */
505 output( "\tpopw %%gs\n" );
507 /* Set %ds and %es (and %ax just in case) equal to %ss */
508 output( "\tmovw %%ss,%%ax\n" );
509 output( "\tmovw %%ax,%%ds\n" );
510 output( "\tmovw %%ax,%%es\n" );
513 /* Jump to the called routine */
514 output( "\tlretw\n" );
516 /* Function footer */
517 output_function_size( func_name );
521 /*******************************************************************
522 * BuildRet16Func
524 * Build the return code for 16-bit callbacks
526 static void BuildRet16Func(void)
528 output_function_header( "__wine_call_to_16_ret", 1 );
530 /* Save %esp into %esi */
531 output( "\tmovl %%esp,%%esi\n" );
533 /* Restore 32-bit segment registers */
535 output( "\tmovl %%cs:%s", asm_name("CallTo16_DataSelector") );
536 output( "-%s,%%edi\n", asm_name("__wine_call16_start") );
537 output( "\tmovw %%di,%%ds\n" );
538 output( "\tmovw %%di,%%es\n" );
540 output( "\tmov %%cs:%s", asm_name("CallTo16_TebSelector") );
541 output( "-%s,%%fs\n", asm_name("__wine_call16_start") );
543 output( "\tmov %%fs:(%d),%%gs\n", GS_OFFSET );
545 /* Restore the 32-bit stack */
547 output( "\tmovw %%di,%%ss\n" );
548 output( "\tmovl %%fs:(%d),%%esp\n", STACKOFFSET );
550 /* Return to caller */
552 output( "\tlret\n" );
553 output_function_size( "__wine_call_to_16_ret" );
557 /*******************************************************************
558 * output_asm_relays16
560 * Build all the 16-bit relay callbacks
562 void output_asm_relays16(void)
564 /* File header */
566 output( "\t.text\n" );
567 output( "%s:\n\n", asm_name("__wine_spec_thunk_text_16") );
569 output( "%s\n", asm_globl("__wine_call16_start") );
571 /* Standard CallFrom16 routine */
572 BuildCallFrom16Core( 0, 0 );
574 /* Register CallFrom16 routine */
575 BuildCallFrom16Core( 1, 0 );
577 /* C16ThkSL CallFrom16 routine */
578 BuildCallFrom16Core( 0, 1 );
580 /* Standard CallTo16 routine */
581 BuildCallTo16Core( 0 );
583 /* Register CallTo16 routine */
584 BuildCallTo16Core( 1 );
586 /* Standard CallTo16 return stub */
587 BuildRet16Func();
589 output( "%s\n", asm_globl("__wine_call16_end") );
590 output_function_size( "__wine_spec_thunk_text_16" );
592 /* Declare the return address and data selector variables */
593 output( "\n\t.data\n\t.balign 4\n" );
594 output( "%s\n\t.long 0\n", asm_globl("CallTo16_DataSelector") );
595 output( "%s\n\t.long 0\n", asm_globl("CallTo16_TebSelector") );