zlib: Import upstream release 1.2.12.
[wine.git] / tools / winebuild / relay.c
blobb86a88aae242f9868d53f2bba90bc380841eda9a
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 static void function_header( const char *name )
41 output( "\n\t.align %d\n", get_alignment(4) );
42 output( "\t%s\n", func_declaration(name) );
43 output( "%s\n", asm_globl(name) );
47 /*******************************************************************
48 * BuildCallFrom16Core
50 * This routine builds the core routines used in 16->32 thunks:
51 * CallFrom16Word, CallFrom16Long, CallFrom16Register, and CallFrom16Thunk.
53 * These routines are intended to be called via a far call (with 32-bit
54 * operand size) from 16-bit code. The 16-bit code stub must push %bp,
55 * the 32-bit entry point to be called, and the argument conversion
56 * routine to be used (see stack layout below).
58 * The core routine completes the STACK16FRAME on the 16-bit stack and
59 * switches to the 32-bit stack. Then, the argument conversion routine
60 * is called; it gets passed the 32-bit entry point and a pointer to the
61 * 16-bit arguments (on the 16-bit stack) as parameters. (You can either
62 * use conversion routines automatically generated by BuildCallFrom16,
63 * or write your own for special purposes.)
65 * The conversion routine must call the 32-bit entry point, passing it
66 * the converted arguments, and return its return value to the core.
67 * After the conversion routine has returned, the core switches back
68 * to the 16-bit stack, converts the return value to the DX:AX format
69 * (CallFrom16Long), and returns to the 16-bit call stub. All parameters,
70 * including %bp, are popped off the stack.
72 * The 16-bit call stub now returns to the caller, popping the 16-bit
73 * arguments if necessary (pascal calling convention).
75 * In the case of a 'register' function, CallFrom16Register fills a
76 * CONTEXT86 structure with the values all registers had at the point
77 * the first instruction of the 16-bit call stub was about to be
78 * executed. A pointer to this CONTEXT86 is passed as third parameter
79 * to the argument conversion routine, which typically passes it on
80 * to the called 32-bit entry point.
82 * CallFrom16Thunk is a special variant used by the implementation of
83 * the Win95 16->32 thunk functions C16ThkSL and C16ThkSL01 and is
84 * implemented as follows:
85 * On entry, the EBX register is set up to contain a flat pointer to the
86 * 16-bit stack such that EBX+22 points to the first argument.
87 * Then, the entry point is called, while EBP is set up to point
88 * to the return address (on the 32-bit stack).
89 * The called function returns with CX set to the number of bytes
90 * to be popped of the caller's stack.
92 * Stack layout upon entry to the core routine (STACK16FRAME):
93 * ... ...
94 * (sp+24) word first 16-bit arg
95 * (sp+22) word cs
96 * (sp+20) word ip
97 * (sp+18) word bp
98 * (sp+14) long 32-bit entry point (reused for Win16 mutex recursion count)
99 * (sp+12) word ip of actual entry point (necessary for relay debugging)
100 * (sp+8) long relay (argument conversion) function entry point
101 * (sp+4) long cs of 16-bit entry point
102 * (sp) long ip of 16-bit entry point
104 * Added on the stack:
105 * (sp-2) word saved gs
106 * (sp-4) word saved fs
107 * (sp-6) word saved es
108 * (sp-8) word saved ds
109 * (sp-12) long saved ebp
110 * (sp-16) long saved ecx
111 * (sp-20) long saved edx
112 * (sp-24) long saved previous stack
114 static void BuildCallFrom16Core( int reg_func, int thunk )
116 /* Function header */
117 if (thunk) function_header( "__wine_call_from_16_thunk" );
118 else if (reg_func) function_header( "__wine_call_from_16_regs" );
119 else function_header( "__wine_call_from_16" );
121 /* Create STACK16FRAME (except STACK32FRAME link) */
122 output( "\tpushw %%gs\n" );
123 output( "\tpushw %%fs\n" );
124 output( "\tpushw %%es\n" );
125 output( "\tpushw %%ds\n" );
126 output( "\tpushl %%ebp\n" );
127 output( "\tpushl %%ecx\n" );
128 output( "\tpushl %%edx\n" );
130 /* Save original EFlags register */
131 if (reg_func) output( "\tpushfl\n" );
133 if ( UsePIC )
135 output( "\tcall 1f\n" );
136 output( "1:\tpopl %%ecx\n" );
137 output( "\t.byte 0x2e\n\tmovl %s-1b(%%ecx),%%edx\n", asm_name("CallTo16_DataSelector") );
139 else
140 output( "\t.byte 0x2e\n\tmovl %s,%%edx\n", asm_name("CallTo16_DataSelector") );
142 /* Load 32-bit segment registers */
143 output( "\tmovw %%dx, %%ds\n" );
144 output( "\tmovw %%dx, %%es\n" );
146 if ( UsePIC )
147 output( "\tmovw %s-1b(%%ecx), %%fs\n", asm_name("CallTo16_TebSelector") );
148 else
149 output( "\tmovw %s, %%fs\n", asm_name("CallTo16_TebSelector") );
151 output( "\t.byte 0x64\n\tmov (%d),%%gs\n", GS_OFFSET );
153 /* Translate STACK16FRAME base to flat offset in %edx */
154 output( "\tmovw %%ss, %%dx\n" );
155 output( "\tandl $0xfff8, %%edx\n" );
156 output( "\tshrl $1, %%edx\n" );
157 if (UsePIC)
158 output( "\taddl .Lwine_ldt_copy_ptr-1b(%%ecx),%%edx\n" );
159 else
160 output( "\taddl .Lwine_ldt_copy_ptr,%%edx\n" );
161 output( "\tmovl (%%edx), %%edx\n" );
162 output( "\tmovzwl %%sp, %%ebp\n" );
163 output( "\tleal %d(%%ebp,%%edx), %%edx\n", reg_func ? 0 : -4 );
165 /* Get saved flags into %ecx */
166 if (reg_func) output( "\tpopl %%ecx\n" );
168 /* Get the 32-bit stack pointer from the TEB and complete STACK16FRAME */
169 output( "\t.byte 0x64\n\tmovl (%d), %%ebp\n", STACKOFFSET );
170 output( "\tpushl %%ebp\n" );
172 /* Switch stacks */
173 output( "\t.byte 0x64\n\tmovw %%ss, (%d)\n", STACKOFFSET + 2 );
174 output( "\t.byte 0x64\n\tmovw %%sp, (%d)\n", STACKOFFSET );
175 output( "\tpushl %%ds\n" );
176 output( "\tpopl %%ss\n" );
177 output( "\tmovl %%ebp, %%esp\n" );
178 output( "\taddl $0x20,%%ebp\n"); /* FIELD_OFFSET(STACK32FRAME,ebp) */
181 /* At this point:
182 STACK16FRAME is completely set up
183 DS, ES, SS: flat data segment
184 FS: current TEB
185 ESP: points to last STACK32FRAME
186 EBP: points to ebp member of last STACK32FRAME
187 EDX: points to current STACK16FRAME
188 ECX: contains saved flags
189 all other registers: unchanged */
191 /* Special case: C16ThkSL stub */
192 if ( thunk )
194 /* Set up registers as expected and call thunk */
195 output( "\tleal 0x1a(%%edx),%%ebx\n" ); /* sizeof(STACK16FRAME)-22 */
196 output( "\tleal -4(%%esp), %%ebp\n" );
198 output( "\tcall *0x26(%%edx)\n"); /* FIELD_OFFSET(STACK16FRAME,entry_point) */
200 /* Switch stack back */
201 output( "\t.byte 0x64\n\tmovw (%d), %%ss\n", STACKOFFSET+2 );
202 output( "\t.byte 0x64\n\tmovzwl (%d), %%esp\n", STACKOFFSET );
203 output( "\t.byte 0x64\n\tpopl (%d)\n", STACKOFFSET );
205 /* Restore registers and return directly to caller */
206 output( "\taddl $8, %%esp\n" );
207 output( "\tpopl %%ebp\n" );
208 output( "\tpopw %%ds\n" );
209 output( "\tpopw %%es\n" );
210 output( "\tpopw %%fs\n" );
211 output( "\tpopw %%gs\n" );
212 output( "\taddl $20, %%esp\n" );
214 output( "\txorb %%ch, %%ch\n" );
215 output( "\tpopl %%ebx\n" );
216 output( "\taddw %%cx, %%sp\n" );
217 output( "\tpush %%ebx\n" );
219 output( "\t.byte 0x66\n" );
220 output( "\tlret\n" );
222 output_function_size( "__wine_call_from_16_thunk" );
223 return;
227 /* Build register CONTEXT */
228 if ( reg_func )
230 output( "\tsubl $0x2cc,%%esp\n" ); /* sizeof(CONTEXT86) */
232 output( "\tmovl %%ecx,0xc0(%%esp)\n" ); /* EFlags */
234 output( "\tmovl %%eax,0xb0(%%esp)\n" ); /* Eax */
235 output( "\tmovl %%ebx,0xa4(%%esp)\n" ); /* Ebx */
236 output( "\tmovl %%esi,0xa0(%%esp)\n" ); /* Esi */
237 output( "\tmovl %%edi,0x9c(%%esp)\n" ); /* Edi */
239 output( "\tmovl 0x0c(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,ebp) */
240 output( "\tmovl %%eax,0xb4(%%esp)\n" ); /* Ebp */
241 output( "\tmovl 0x08(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,ecx) */
242 output( "\tmovl %%eax,0xac(%%esp)\n" ); /* Ecx */
243 output( "\tmovl 0x04(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,edx) */
244 output( "\tmovl %%eax,0xa8(%%esp)\n" ); /* Edx */
246 output( "\tmovzwl 0x10(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,ds) */
247 output( "\tmovl %%eax,0x98(%%esp)\n" ); /* SegDs */
248 output( "\tmovzwl 0x12(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,es) */
249 output( "\tmovl %%eax,0x94(%%esp)\n" ); /* SegEs */
250 output( "\tmovzwl 0x14(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,fs) */
251 output( "\tmovl %%eax,0x90(%%esp)\n" ); /* SegFs */
252 output( "\tmovzwl 0x16(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,gs) */
253 output( "\tmovl %%eax,0x8c(%%esp)\n" ); /* SegGs */
255 output( "\tmovzwl 0x2e(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,cs) */
256 output( "\tmovl %%eax,0xbc(%%esp)\n" ); /* SegCs */
257 output( "\tmovzwl 0x2c(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,ip) */
258 output( "\tmovl %%eax,0xb8(%%esp)\n" ); /* Eip */
260 output( "\t.byte 0x64\n\tmovzwl (%d), %%eax\n", STACKOFFSET+2 );
261 output( "\tmovl %%eax,0xc8(%%esp)\n" ); /* SegSs */
262 output( "\t.byte 0x64\n\tmovzwl (%d), %%eax\n", STACKOFFSET );
263 output( "\taddl $0x2c,%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,ip) */
264 output( "\tmovl %%eax,0xc4(%%esp)\n" ); /* Esp */
265 #if 0
266 output( "\tfsave 0x1c(%%esp)\n" ); /* FloatSave */
267 #endif
269 /* Push address of CONTEXT86 structure -- popped by the relay routine */
270 output( "\tmovl %%esp,%%eax\n" );
271 output( "\tandl $~15,%%esp\n" );
272 output( "\tsubl $4,%%esp\n" );
273 output( "\tpushl %%eax\n" );
275 else
277 output( "\tsubl $8,%%esp\n" );
278 output( "\tandl $~15,%%esp\n" );
279 output( "\taddl $8,%%esp\n" );
282 /* Call relay routine (which will call the API entry point) */
283 output( "\tleal 0x30(%%edx),%%eax\n" ); /* sizeof(STACK16FRAME) */
284 output( "\tpushl %%eax\n" );
285 output( "\tpushl 0x26(%%edx)\n"); /* FIELD_OFFSET(STACK16FRAME,entry_point) */
286 output( "\tcall *0x20(%%edx)\n"); /* FIELD_OFFSET(STACK16FRAME,relay) */
288 if ( reg_func )
290 output( "\tleal -748(%%ebp),%%ebx\n" ); /* sizeof(CONTEXT) + FIELD_OFFSET(STACK32FRAME,ebp) */
292 /* Switch stack back */
293 output( "\t.byte 0x64\n\tmovw (%d), %%ss\n", STACKOFFSET+2 );
294 output( "\t.byte 0x64\n\tmovzwl (%d), %%esp\n", STACKOFFSET );
295 output( "\t.byte 0x64\n\tpopl (%d)\n", STACKOFFSET );
297 /* Get return address to CallFrom16 stub */
298 output( "\taddw $0x14,%%sp\n" ); /* FIELD_OFFSET(STACK16FRAME,callfrom_ip)-4 */
299 output( "\tpopl %%eax\n" );
300 output( "\tpopl %%edx\n" );
302 /* Restore all registers from CONTEXT */
303 output( "\tmovw 0xc8(%%ebx),%%ss\n"); /* SegSs */
304 output( "\tmovl 0xc4(%%ebx),%%esp\n"); /* Esp */
305 output( "\taddl $4, %%esp\n" ); /* room for final return address */
307 output( "\tpushw 0xbc(%%ebx)\n"); /* SegCs */
308 output( "\tpushw 0xb8(%%ebx)\n"); /* Eip */
309 output( "\tpushl %%edx\n" );
310 output( "\tpushl %%eax\n" );
311 output( "\tpushl 0xc0(%%ebx)\n"); /* EFlags */
312 output( "\tpushl 0x98(%%ebx)\n"); /* SegDs */
314 output( "\tpushl 0x94(%%ebx)\n"); /* SegEs */
315 output( "\tpopl %%es\n" );
316 output( "\tpushl 0x90(%%ebx)\n"); /* SegFs */
317 output( "\tpopl %%fs\n" );
318 output( "\tpushl 0x8c(%%ebx)\n"); /* SegGs */
319 output( "\tpopl %%gs\n" );
321 output( "\tmovl 0xb4(%%ebx),%%ebp\n"); /* Ebp */
322 output( "\tmovl 0xa0(%%ebx),%%esi\n"); /* Esi */
323 output( "\tmovl 0x9c(%%ebx),%%edi\n"); /* Edi */
324 output( "\tmovl 0xb0(%%ebx),%%eax\n"); /* Eax */
325 output( "\tmovl 0xa8(%%ebx),%%edx\n"); /* Edx */
326 output( "\tmovl 0xac(%%ebx),%%ecx\n"); /* Ecx */
327 output( "\tmovl 0xa4(%%ebx),%%ebx\n"); /* Ebx */
329 output( "\tpopl %%ds\n" );
330 output( "\tpopfl\n" );
331 output( "\tlret\n" );
333 output_function_size( "__wine_call_from_16_regs" );
335 else
337 /* Switch stack back */
338 output( "\t.byte 0x64\n\tmovw (%d), %%ss\n", STACKOFFSET+2 );
339 output( "\t.byte 0x64\n\tmovzwl (%d), %%esp\n", STACKOFFSET );
340 output( "\t.byte 0x64\n\tpopl (%d)\n", STACKOFFSET );
342 /* Restore registers */
343 output( "\tpopl %%edx\n" );
344 output( "\tpopl %%ecx\n" );
345 output( "\tpopl %%ebp\n" );
346 output( "\tpopw %%ds\n" );
347 output( "\tpopw %%es\n" );
348 output( "\tpopw %%fs\n" );
349 output( "\tpopw %%gs\n" );
351 /* Return to return stub which will return to caller */
352 output( "\tlret $12\n" );
354 output_function_size( "__wine_call_from_16" );
359 /*******************************************************************
360 * BuildCallTo16Core
362 * This routine builds the core routines used in 32->16 thunks:
364 * extern DWORD WINAPI wine_call_to_16( FARPROC16 target, DWORD cbArgs, PEXCEPTION_HANDLER handler );
365 * extern void WINAPI wine_call_to_16_regs( CONTEXT86 *context, DWORD cbArgs, PEXCEPTION_HANDLER handler );
367 * These routines can be called directly from 32-bit code.
369 * All routines expect that the 16-bit stack contents (arguments) and the
370 * return address (segptr to CallTo16_Ret) were already set up by the
371 * caller; nb_args must contain the number of bytes to be conserved. The
372 * 16-bit SS:SP will be set accordingly.
374 * All other registers are either taken from the CONTEXT86 structure
375 * or else set to default values. The target routine address is either
376 * given directly or taken from the CONTEXT86.
378 static void BuildCallTo16Core( int reg_func )
380 const char *name = reg_func ? "wine_call_to_16_regs" : "wine_call_to_16";
381 const char *func_name = is_pe() ? strmake( "%s@12", name ) : name;
383 /* Function header */
384 function_header( func_name );
386 /* Function entry sequence */
387 output_cfi( ".cfi_startproc" );
388 output( "\tpushl %%ebp\n" );
389 output_cfi( ".cfi_adjust_cfa_offset 4" );
390 output_cfi( ".cfi_rel_offset %%ebp,0" );
391 output( "\tmovl %%esp, %%ebp\n" );
392 output_cfi( ".cfi_def_cfa_register %%ebp" );
394 /* Save the 32-bit registers */
395 output( "\tpushl %%ebx\n" );
396 output_cfi( ".cfi_rel_offset %%ebx,-4" );
397 output( "\tpushl %%esi\n" );
398 output_cfi( ".cfi_rel_offset %%esi,-8" );
399 output( "\tpushl %%edi\n" );
400 output_cfi( ".cfi_rel_offset %%edi,-12" );
401 output( "\t.byte 0x64\n\tmov %%gs,(%d)\n", GS_OFFSET );
403 /* Setup exception frame */
404 output( "\t.byte 0x64\n\tpushl (%d)\n", STACKOFFSET );
405 output( "\tpushl 16(%%ebp)\n" ); /* handler */
406 output( "\t.byte 0x64\n\tpushl (0)\n" );
407 output( "\t.byte 0x64\n\tmovl %%esp,(0)\n" );
409 /* Call the actual CallTo16 routine (simulate a lcall) */
410 output( "\tpushl %%cs\n" );
411 output( "\tcall .L%s\n", name );
413 /* Remove exception frame */
414 output( "\t.byte 0x64\n\tpopl (0)\n" );
415 output( "\taddl $4, %%esp\n" );
416 output( "\t.byte 0x64\n\tpopl (%d)\n", STACKOFFSET );
418 if ( !reg_func )
420 /* Convert return value */
421 output( "\tandl $0xffff,%%eax\n" );
422 output( "\tshll $16,%%edx\n" );
423 output( "\torl %%edx,%%eax\n" );
425 else
428 * Modify CONTEXT86 structure to contain new values
430 * NOTE: We restore only EAX, EBX, ECX, EDX, EBP, and ESP.
431 * The segment registers as well as ESI and EDI should
432 * not be modified by a well-behaved 16-bit routine in
433 * any case. [If necessary, we could restore them as well,
434 * at the cost of a somewhat less efficient return path.]
437 output( "\tmovl 0x14(%%esp),%%edi\n" ); /* FIELD_OFFSET(STACK32FRAME,target) - FIELD_OFFSET(STACK32FRAME,edi) */
438 /* everything above edi has been popped already */
440 output( "\tmovl %%eax,0xb0(%%edi)\n"); /* Eax */
441 output( "\tmovl %%ebx,0xa4(%%edi)\n"); /* Ebx */
442 output( "\tmovl %%ecx,0xac(%%edi)\n"); /* Ecx */
443 output( "\tmovl %%edx,0xa8(%%edi)\n"); /* Edx */
444 output( "\tmovl %%ebp,0xb4(%%edi)\n"); /* Ebp */
445 output( "\tmovl %%esi,0xc4(%%edi)\n"); /* Esp */
446 /* The return glue code saved %esp into %esi */
449 /* Restore the 32-bit registers */
450 output( "\tpopl %%edi\n" );
451 output_cfi( ".cfi_same_value %%edi" );
452 output( "\tpopl %%esi\n" );
453 output_cfi( ".cfi_same_value %%esi" );
454 output( "\tpopl %%ebx\n" );
455 output_cfi( ".cfi_same_value %%ebx" );
457 /* Function exit sequence */
458 output( "\tpopl %%ebp\n" );
459 output_cfi( ".cfi_def_cfa %%esp,4" );
460 output_cfi( ".cfi_same_value %%ebp" );
461 output( "\tret $12\n" );
462 output_cfi( ".cfi_endproc" );
465 /* Start of the actual CallTo16 routine */
467 output( ".L%s:\n", name );
469 /* Switch to the 16-bit stack */
470 output( "\tmovl %%esp,%%edx\n" );
471 output( "\t.byte 0x64\n\tmovw (%d),%%ss\n", STACKOFFSET + 2);
472 output( "\t.byte 0x64\n\tmovw (%d),%%sp\n", STACKOFFSET );
473 output( "\t.byte 0x64\n\tmovl %%edx,(%d)\n", STACKOFFSET );
475 /* Make %bp point to the previous stackframe (built by CallFrom16) */
476 output( "\tmovzwl %%sp,%%ebp\n" );
477 output( "\tleal 0x2a(%%ebp),%%ebp\n"); /* FIELD_OFFSET(STACK16FRAME,bp) */
479 /* Add the specified offset to the new sp */
480 output( "\tsubw 0x2c(%%edx), %%sp\n"); /* FIELD_OFFSET(STACK32FRAME,nb_args) */
482 if (reg_func)
484 /* Push the called routine address */
485 output( "\tmovl 0x28(%%edx),%%edx\n"); /* FIELD_OFFSET(STACK32FRAME,target) */
486 output( "\tpushw 0xbc(%%edx)\n"); /* SegCs */
487 output( "\tpushw 0xb8(%%edx)\n"); /* Eip */
489 /* Get the registers */
490 output( "\tpushw 0x98(%%edx)\n"); /* SegDs */
491 output( "\tpushl 0x94(%%edx)\n"); /* SegEs */
492 output( "\tpopl %%es\n" );
493 output( "\tmovl 0xb4(%%edx),%%ebp\n"); /* Ebp */
494 output( "\tmovl 0xa0(%%edx),%%esi\n"); /* Esi */
495 output( "\tmovl 0x9c(%%edx),%%edi\n"); /* Edi */
496 output( "\tmovl 0xb0(%%edx),%%eax\n"); /* Eax */
497 output( "\tmovl 0xa4(%%edx),%%ebx\n"); /* Ebx */
498 output( "\tmovl 0xac(%%edx),%%ecx\n"); /* Ecx */
499 output( "\tmovl 0xa8(%%edx),%%edx\n"); /* Edx */
501 /* Get the 16-bit ds */
502 output( "\tpopw %%ds\n" );
504 else /* not a register function */
506 /* Push the called routine address */
507 output( "\tpushl 0x28(%%edx)\n"); /* FIELD_OFFSET(STACK32FRAME,target) */
509 /* Set %fs and %gs to the value saved by the last CallFrom16 */
510 output( "\tpushw -22(%%ebp)\n" ); /* FIELD_OFFSET(STACK16FRAME,fs)-FIELD_OFFSET(STACK16FRAME,bp) */
511 output( "\tpopw %%fs\n" );
512 output( "\tpushw -20(%%ebp)\n" ); /* FIELD_OFFSET(STACK16FRAME,gs)-FIELD_OFFSET(STACK16FRAME,bp) */
513 output( "\tpopw %%gs\n" );
515 /* Set %ds and %es (and %ax just in case) equal to %ss */
516 output( "\tmovw %%ss,%%ax\n" );
517 output( "\tmovw %%ax,%%ds\n" );
518 output( "\tmovw %%ax,%%es\n" );
521 /* Jump to the called routine */
522 output( "\t.byte 0x66\n" );
523 output( "\tlret\n" );
525 /* Function footer */
526 output_function_size( func_name );
530 /*******************************************************************
531 * BuildRet16Func
533 * Build the return code for 16-bit callbacks
535 static void BuildRet16Func(void)
537 function_header( "__wine_call_to_16_ret" );
539 /* Save %esp into %esi */
540 output( "\tmovl %%esp,%%esi\n" );
542 /* Restore 32-bit segment registers */
544 output( "\t.byte 0x2e\n\tmovl %s", asm_name("CallTo16_DataSelector") );
545 output( "-%s,%%edi\n", asm_name("__wine_call16_start") );
546 output( "\tmovw %%di,%%ds\n" );
547 output( "\tmovw %%di,%%es\n" );
549 output( "\t.byte 0x2e\n\tmov %s", asm_name("CallTo16_TebSelector") );
550 output( "-%s,%%fs\n", asm_name("__wine_call16_start") );
552 output( "\t.byte 0x64\n\tmov (%d),%%gs\n", GS_OFFSET );
554 /* Restore the 32-bit stack */
556 output( "\tmovw %%di,%%ss\n" );
557 output( "\t.byte 0x64\n\tmovl (%d),%%esp\n", STACKOFFSET );
559 /* Return to caller */
561 output( "\tlret\n" );
562 output_function_size( "__wine_call_to_16_ret" );
566 /*******************************************************************
567 * output_asm_relays16
569 * Build all the 16-bit relay callbacks
571 void output_asm_relays16(void)
573 /* File header */
575 output( "\t.text\n" );
576 output( "%s:\n\n", asm_name("__wine_spec_thunk_text_16") );
578 output( "%s\n", asm_globl("__wine_call16_start") );
580 /* Standard CallFrom16 routine */
581 BuildCallFrom16Core( 0, 0 );
583 /* Register CallFrom16 routine */
584 BuildCallFrom16Core( 1, 0 );
586 /* C16ThkSL CallFrom16 routine */
587 BuildCallFrom16Core( 0, 1 );
589 /* Standard CallTo16 routine */
590 BuildCallTo16Core( 0 );
592 /* Register CallTo16 routine */
593 BuildCallTo16Core( 1 );
595 /* Standard CallTo16 return stub */
596 BuildRet16Func();
598 output( "%s\n", asm_globl("__wine_call16_end") );
599 output_function_size( "__wine_spec_thunk_text_16" );
601 /* Declare the return address and data selector variables */
602 output( "\n\t.data\n\t.align %d\n", get_alignment(4) );
603 output( "%s\n\t.long 0\n", asm_globl("CallTo16_DataSelector") );
604 output( "%s\n\t.long 0\n", asm_globl("CallTo16_TebSelector") );