winedd: Move shader_*_add_instruction_modifiers into the shader backend.
[wine] / dlls / kernel32 / selector.c
1 /*
2  * Selector manipulation functions
3  *
4  * Copyright 1995 Alexandre Julliard
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <string.h>
25
26 #include "wine/winbase16.h"
27 #include "wine/server.h"
28 #include "wine/debug.h"
29 #include "kernel_private.h"
30 #include "toolhelp.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(selector);
33
34 #define LDT_SIZE 8192
35
36 /* get the number of selectors needed to cover up to the selector limit */
37 static inline WORD get_sel_count( WORD sel )
38 {
39     return (wine_ldt_copy.limit[sel >> __AHSHIFT] >> 16) + 1;
40 }
41
42
43 /***********************************************************************
44  *           AllocSelectorArray   (KERNEL.206)
45  */
46 WORD WINAPI AllocSelectorArray16( WORD count )
47 {
48     WORD i, sel = wine_ldt_alloc_entries( count );
49
50     if (sel)
51     {
52         LDT_ENTRY entry;
53         wine_ldt_set_base( &entry, 0 );
54         wine_ldt_set_limit( &entry, 1 ); /* avoid 0 base and limit */
55         wine_ldt_set_flags( &entry, WINE_LDT_FLAGS_DATA );
56         for (i = 0; i < count; i++) wine_ldt_set_entry( sel + (i << __AHSHIFT), &entry );
57     }
58     return sel;
59 }
60
61
62 /***********************************************************************
63  *           AllocSelector   (KERNEL.175)
64  */
65 WORD WINAPI AllocSelector16( WORD sel )
66 {
67     WORD newsel, count, i;
68
69     count = sel ? get_sel_count(sel) : 1;
70     newsel = wine_ldt_alloc_entries( count );
71     TRACE("(%04x): returning %04x\n", sel, newsel );
72     if (!newsel) return 0;
73     if (!sel) return newsel;  /* nothing to copy */
74     for (i = 0; i < count; i++)
75     {
76         LDT_ENTRY entry;
77         wine_ldt_get_entry( sel + (i << __AHSHIFT), &entry );
78         wine_ldt_set_entry( newsel + (i << __AHSHIFT), &entry );
79     }
80     return newsel;
81 }
82
83
84 /***********************************************************************
85  *           FreeSelector   (KERNEL.176)
86  */
87 WORD WINAPI FreeSelector16( WORD sel )
88 {
89     LDT_ENTRY entry;
90
91     wine_ldt_get_entry( sel, &entry );
92     if (wine_ldt_is_empty( &entry )) return sel;  /* error */
93 #ifdef __i386__
94     /* Check if we are freeing current %fs selector */
95     if (!((wine_get_fs() ^ sel) & ~3))
96         WARN("Freeing %%fs selector (%04x), not good.\n", wine_get_fs() );
97 #endif  /* __i386__ */
98     wine_ldt_free_entries( sel, 1 );
99     return 0;
100 }
101
102
103 /***********************************************************************
104  *           SELECTOR_SetEntries
105  *
106  * Set the LDT entries for an array of selectors.
107  */
108 static void SELECTOR_SetEntries( WORD sel, const void *base, DWORD size, unsigned char flags )
109 {
110     LDT_ENTRY entry;
111     WORD i, count;
112
113     wine_ldt_set_base( &entry, base );
114     wine_ldt_set_limit( &entry, size - 1 );
115     wine_ldt_set_flags( &entry, flags );
116     count = (size + 0xffff) / 0x10000;
117     for (i = 0; i < count; i++)
118     {
119         wine_ldt_set_entry( sel + (i << __AHSHIFT), &entry );
120         wine_ldt_set_base( &entry, (char*)wine_ldt_get_base(&entry) + 0x10000);
121         /* yep, Windows sets limit like that, not 64K sel units */
122         wine_ldt_set_limit( &entry, wine_ldt_get_limit(&entry) - 0x10000 );
123     }
124 }
125
126
127 /***********************************************************************
128  *           SELECTOR_AllocBlock
129  *
130  * Allocate selectors for a block of linear memory.
131  */
132 WORD SELECTOR_AllocBlock( const void *base, DWORD size, unsigned char flags )
133 {
134     WORD sel, count;
135
136     if (!size) return 0;
137     count = (size + 0xffff) / 0x10000;
138     sel = wine_ldt_alloc_entries( count );
139     if (sel) SELECTOR_SetEntries( sel, base, size, flags );
140     return sel;
141 }
142
143
144 /***********************************************************************
145  *           SELECTOR_FreeBlock
146  *
147  * Free a block of selectors.
148  */
149 void SELECTOR_FreeBlock( WORD sel )
150 {
151     WORD i, count = get_sel_count( sel );
152
153     TRACE("(%04x,%d)\n", sel, count );
154     for (i = 0; i < count; i++) FreeSelector16( sel + (i << __AHSHIFT) );
155 }
156
157
158 /***********************************************************************
159  *           SELECTOR_ReallocBlock
160  *
161  * Change the size of a block of selectors.
162  */
163 WORD SELECTOR_ReallocBlock( WORD sel, const void *base, DWORD size )
164 {
165     LDT_ENTRY entry;
166     int oldcount, newcount;
167
168     if (!size) size = 1;
169     wine_ldt_get_entry( sel, &entry );
170     oldcount = (wine_ldt_get_limit(&entry) >> 16) + 1;
171     newcount = (size + 0xffff) >> 16;
172
173     sel = wine_ldt_realloc_entries( sel, oldcount, newcount );
174     if (sel) SELECTOR_SetEntries( sel, base, size, wine_ldt_get_flags(&entry) );
175     return sel;
176 }
177
178
179 /***********************************************************************
180  *           PrestoChangoSelector   (KERNEL.177)
181  */
182 WORD WINAPI PrestoChangoSelector16( WORD selSrc, WORD selDst )
183 {
184     LDT_ENTRY entry;
185     wine_ldt_get_entry( selSrc, &entry );
186     /* toggle the executable bit */
187     entry.HighWord.Bits.Type ^= (WINE_LDT_FLAGS_CODE ^ WINE_LDT_FLAGS_DATA);
188     wine_ldt_set_entry( selDst, &entry );
189     return selDst;
190 }
191
192
193 /***********************************************************************
194  *           AllocCStoDSAlias   (KERNEL.170)
195  *           AllocAlias         (KERNEL.172)
196  */
197 WORD WINAPI AllocCStoDSAlias16( WORD sel )
198 {
199     WORD newsel;
200     LDT_ENTRY entry;
201
202     newsel = wine_ldt_alloc_entries( 1 );
203     TRACE("(%04x): returning %04x\n",
204                       sel, newsel );
205     if (!newsel) return 0;
206     wine_ldt_get_entry( sel, &entry );
207     entry.HighWord.Bits.Type = WINE_LDT_FLAGS_DATA;
208     wine_ldt_set_entry( newsel, &entry );
209     return newsel;
210 }
211
212
213 /***********************************************************************
214  *           AllocDStoCSAlias   (KERNEL.171)
215  */
216 WORD WINAPI AllocDStoCSAlias16( WORD sel )
217 {
218     WORD newsel;
219     LDT_ENTRY entry;
220
221     newsel = wine_ldt_alloc_entries( 1 );
222     TRACE("(%04x): returning %04x\n",
223                       sel, newsel );
224     if (!newsel) return 0;
225     wine_ldt_get_entry( sel, &entry );
226     entry.HighWord.Bits.Type = WINE_LDT_FLAGS_CODE;
227     wine_ldt_set_entry( newsel, &entry );
228     return newsel;
229 }
230
231
232 /***********************************************************************
233  *           LongPtrAdd   (KERNEL.180)
234  */
235 void WINAPI LongPtrAdd16( DWORD ptr, DWORD add )
236 {
237     LDT_ENTRY entry;
238     wine_ldt_get_entry( SELECTOROF(ptr), &entry );
239     wine_ldt_set_base( &entry, (char *)wine_ldt_get_base(&entry) + add );
240     wine_ldt_set_entry( SELECTOROF(ptr), &entry );
241 }
242
243
244 /***********************************************************************
245  *             GetSelectorBase   (KERNEL.186)
246  */
247 DWORD WINAPI GetSelectorBase( WORD sel )
248 {
249     void *base = wine_ldt_copy.base[sel >> __AHSHIFT];
250
251     /* if base points into DOSMEM, assume we have to
252      * return pointer into physical lower 1MB */
253
254     return DOSMEM_MapLinearToDos( base );
255 }
256
257
258 /***********************************************************************
259  *             SetSelectorBase   (KERNEL.187)
260  */
261 WORD WINAPI SetSelectorBase( WORD sel, DWORD base )
262 {
263     LDT_ENTRY entry;
264     wine_ldt_get_entry( sel, &entry );
265     wine_ldt_set_base( &entry, DOSMEM_MapDosToLinear(base) );
266     wine_ldt_set_entry( sel, &entry );
267     return sel;
268 }
269
270
271 /***********************************************************************
272  *           GetSelectorLimit   (KERNEL.188)
273  */
274 DWORD WINAPI GetSelectorLimit16( WORD sel )
275 {
276     return wine_ldt_copy.limit[sel >> __AHSHIFT];
277 }
278
279
280 /***********************************************************************
281  *           SetSelectorLimit   (KERNEL.189)
282  */
283 WORD WINAPI SetSelectorLimit16( WORD sel, DWORD limit )
284 {
285     LDT_ENTRY entry;
286     wine_ldt_get_entry( sel, &entry );
287     wine_ldt_set_limit( &entry, limit );
288     wine_ldt_set_entry( sel, &entry );
289     return sel;
290 }
291
292
293 /***********************************************************************
294  *           SelectorAccessRights   (KERNEL.196)
295  */
296 WORD WINAPI SelectorAccessRights16( WORD sel, WORD op, WORD val )
297 {
298     LDT_ENTRY entry;
299     wine_ldt_get_entry( sel, &entry );
300
301     if (op == 0)  /* get */
302     {
303         return entry.HighWord.Bytes.Flags1 | ((entry.HighWord.Bytes.Flags2 << 8) & 0xf0);
304     }
305     else  /* set */
306     {
307         entry.HighWord.Bytes.Flags1 = LOBYTE(val) | 0xf0;
308         entry.HighWord.Bytes.Flags2 = (entry.HighWord.Bytes.Flags2 & 0x0f) | (HIBYTE(val) & 0xf0);
309         wine_ldt_set_entry( sel, &entry );
310         return 0;
311     }
312 }
313
314
315 /***********************************************************************
316  *           IsBadCodePtr   (KERNEL.336)
317  */
318 BOOL16 WINAPI IsBadCodePtr16( SEGPTR lpfn )
319 {
320     WORD sel;
321     LDT_ENTRY entry;
322
323     sel = SELECTOROF(lpfn);
324     if (!sel) return TRUE;
325     wine_ldt_get_entry( sel, &entry );
326     if (wine_ldt_is_empty( &entry )) return TRUE;
327     /* check for code segment, ignoring conforming, read-only and accessed bits */
328     if ((entry.HighWord.Bits.Type ^ WINE_LDT_FLAGS_CODE) & 0x18) return TRUE;
329     if (OFFSETOF(lpfn) > wine_ldt_get_limit(&entry)) return TRUE;
330     return FALSE;
331 }
332
333
334 /***********************************************************************
335  *           IsBadStringPtr   (KERNEL.337)
336  */
337 BOOL16 WINAPI IsBadStringPtr16( SEGPTR ptr, UINT16 size )
338 {
339     WORD sel;
340     LDT_ENTRY entry;
341
342     sel = SELECTOROF(ptr);
343     if (!sel) return TRUE;
344     wine_ldt_get_entry( sel, &entry );
345     if (wine_ldt_is_empty( &entry )) return TRUE;
346     /* check for data or readable code segment */
347     if (!(entry.HighWord.Bits.Type & 0x10)) return TRUE;  /* system descriptor */
348     if ((entry.HighWord.Bits.Type & 0x0a) == 0x08) return TRUE;  /* non-readable code segment */
349     if (strlen(MapSL(ptr)) < size) size = strlen(MapSL(ptr)) + 1;
350     if (size && (OFFSETOF(ptr) + size - 1 > wine_ldt_get_limit(&entry))) return TRUE;
351     return FALSE;
352 }
353
354
355 /***********************************************************************
356  *           IsBadHugeReadPtr   (KERNEL.346)
357  */
358 BOOL16 WINAPI IsBadHugeReadPtr16( SEGPTR ptr, DWORD size )
359 {
360     WORD sel;
361     LDT_ENTRY entry;
362
363     sel = SELECTOROF(ptr);
364     if (!sel) return TRUE;
365     wine_ldt_get_entry( sel, &entry );
366     if (wine_ldt_is_empty( &entry )) return TRUE;
367     /* check for data or readable code segment */
368     if (!(entry.HighWord.Bits.Type & 0x10)) return TRUE;  /* system descriptor */
369     if ((entry.HighWord.Bits.Type & 0x0a) == 0x08) return TRUE;  /* non-readable code segment */
370     if (size && (OFFSETOF(ptr) + size - 1 > wine_ldt_get_limit( &entry ))) return TRUE;
371     return FALSE;
372 }
373
374
375 /***********************************************************************
376  *           IsBadHugeWritePtr   (KERNEL.347)
377  */
378 BOOL16 WINAPI IsBadHugeWritePtr16( SEGPTR ptr, DWORD size )
379 {
380     WORD sel;
381     LDT_ENTRY entry;
382
383     sel = SELECTOROF(ptr);
384     if (!sel) return TRUE;
385     wine_ldt_get_entry( sel, &entry );
386     if (wine_ldt_is_empty( &entry )) return TRUE;
387     /* check for writable data segment, ignoring expand-down and accessed flags */
388     if ((entry.HighWord.Bits.Type ^ WINE_LDT_FLAGS_DATA) & ~5) return TRUE;
389     if (size && (OFFSETOF(ptr) + size - 1 > wine_ldt_get_limit( &entry ))) return TRUE;
390     return FALSE;
391 }
392
393 /***********************************************************************
394  *           IsBadReadPtr   (KERNEL.334)
395  */
396 BOOL16 WINAPI IsBadReadPtr16( SEGPTR ptr, UINT16 size )
397 {
398     return IsBadHugeReadPtr16( ptr, size );
399 }
400
401
402 /***********************************************************************
403  *           IsBadWritePtr   (KERNEL.335)
404  */
405 BOOL16 WINAPI IsBadWritePtr16( SEGPTR ptr, UINT16 size )
406 {
407     return IsBadHugeWritePtr16( ptr, size );
408 }
409
410
411 /***********************************************************************
412  *           IsBadFlatReadWritePtr   (KERNEL.627)
413  */
414 BOOL16 WINAPI IsBadFlatReadWritePtr16( SEGPTR ptr, DWORD size, BOOL16 bWrite )
415 {
416     return bWrite? IsBadHugeWritePtr16( ptr, size )
417                  : IsBadHugeReadPtr16( ptr, size );
418 }
419
420
421 /***********************************************************************
422  *           MemoryRead   (TOOLHELP.78)
423  */
424 DWORD WINAPI MemoryRead16( WORD sel, DWORD offset, void *buffer, DWORD count )
425 {
426     LDT_ENTRY entry;
427     DWORD limit;
428
429     wine_ldt_get_entry( sel, &entry );
430     if (wine_ldt_is_empty( &entry )) return 0;
431     limit = wine_ldt_get_limit( &entry );
432     if (offset > limit) return 0;
433     if (offset + count > limit + 1) count = limit + 1 - offset;
434     memcpy( buffer, (char *)wine_ldt_get_base(&entry) + offset, count );
435     return count;
436 }
437
438
439 /***********************************************************************
440  *           MemoryWrite   (TOOLHELP.79)
441  */
442 DWORD WINAPI MemoryWrite16( WORD sel, DWORD offset, void *buffer, DWORD count )
443 {
444     LDT_ENTRY entry;
445     DWORD limit;
446
447     wine_ldt_get_entry( sel, &entry );
448     if (wine_ldt_is_empty( &entry )) return 0;
449     limit = wine_ldt_get_limit( &entry );
450     if (offset > limit) return 0;
451     if (offset + count > limit) count = limit + 1 - offset;
452     memcpy( (char *)wine_ldt_get_base(&entry) + offset, buffer, count );
453     return count;
454 }
455
456 /************************************* Win95 pointer mapping functions *
457  *
458  */
459
460 struct mapls_entry
461 {
462     struct mapls_entry *next;
463     void               *addr;   /* linear address */
464     int                 count;  /* ref count */
465     WORD                sel;    /* selector */
466 };
467
468 static struct mapls_entry *first_entry;
469
470
471 /***********************************************************************
472  *           MapLS   (KERNEL32.@)
473  *           MapLS   (KERNEL.358)
474  *
475  * Maps linear pointer to segmented.
476  */
477 SEGPTR WINAPI MapLS( LPCVOID ptr )
478 {
479     struct mapls_entry *entry, *free = NULL;
480     const void *base;
481     SEGPTR ret = 0;
482
483     if (!HIWORD(ptr)) return (SEGPTR)LOWORD(ptr);
484
485     base = (const char *)ptr - ((ULONG_PTR)ptr & 0x7fff);
486     HeapLock( GetProcessHeap() );
487     for (entry = first_entry; entry; entry = entry->next)
488     {
489         if (entry->addr == base) break;
490         if (!entry->count) free = entry;
491     }
492
493     if (!entry)
494     {
495         if (!free)  /* no free entry found, create a new one */
496         {
497             if (!(free = HeapAlloc( GetProcessHeap(), 0, sizeof(*free) ))) goto done;
498             if (!(free->sel = SELECTOR_AllocBlock( base, 0x10000, WINE_LDT_FLAGS_DATA )))
499             {
500                 HeapFree( GetProcessHeap(), 0, free );
501                 goto done;
502             }
503             free->count = 0;
504             free->next = first_entry;
505             first_entry = free;
506         }
507         SetSelectorBase( free->sel, (DWORD)base );
508         free->addr = (void*)base;
509         entry = free;
510     }
511     entry->count++;
512     ret = MAKESEGPTR( entry->sel, (const char *)ptr - (char *)entry->addr );
513  done:
514     HeapUnlock( GetProcessHeap() );
515     return ret;
516 }
517
518 /***********************************************************************
519  *           UnMapLS   (KERNEL32.@)
520  *           UnMapLS   (KERNEL.359)
521  *
522  * Free mapped selector.
523  */
524 void WINAPI UnMapLS( SEGPTR sptr )
525 {
526     struct mapls_entry *entry;
527     WORD sel = SELECTOROF(sptr);
528
529     if (sel)
530     {
531         HeapLock( GetProcessHeap() );
532         for (entry = first_entry; entry; entry = entry->next) if (entry->sel == sel) break;
533         if (entry && entry->count > 0) entry->count--;
534         HeapUnlock( GetProcessHeap() );
535     }
536 }
537
538 /***********************************************************************
539  *           MapSL   (KERNEL32.@)
540  *           MapSL   (KERNEL.357)
541  *
542  * Maps fixed segmented pointer to linear.
543  */
544 LPVOID WINAPI MapSL( SEGPTR sptr )
545 {
546     return (char *)wine_ldt_copy.base[SELECTOROF(sptr) >> __AHSHIFT] + OFFSETOF(sptr);
547 }
548
549 /***********************************************************************
550  *           MapSLFix   (KERNEL32.@)
551  *
552  * FIXME: MapSLFix and UnMapSLFixArray should probably prevent
553  * unexpected linear address change when GlobalCompact() shuffles
554  * moveable blocks.
555  */
556
557 LPVOID WINAPI MapSLFix( SEGPTR sptr )
558 {
559     return MapSL(sptr);
560 }
561
562 /***********************************************************************
563  *           UnMapSLFixArray   (KERNEL32.@)
564  *
565  * Must not change EAX, hence defined as asm function.
566  */
567 #ifdef __i386__
568 __ASM_GLOBAL_FUNC( UnMapSLFixArray, "ret $8" )
569 #endif
570
571
572 /***********************************************************************
573  *           GetThreadSelectorEntry   (KERNEL32.@)
574  */
575 BOOL WINAPI GetThreadSelectorEntry( HANDLE hthread, DWORD sel, LPLDT_ENTRY ldtent )
576 {
577     THREAD_DESCRIPTOR_INFORMATION       tdi;
578     NTSTATUS                            status;
579
580     tdi.Selector = sel;
581     status = NtQueryInformationThread( hthread, ThreadDescriptorTableEntry,
582                                        &tdi, sizeof(tdi), NULL);
583     if (status)
584     {
585         SetLastError( RtlNtStatusToDosError(status) );
586         return FALSE;
587     }
588     *ldtent = tdi.Entry;
589     return TRUE;
590 }
591
592
593 #ifdef __i386__
594
595 /***********************************************************************
596  *              SMapLS (KERNEL32.@)
597  */
598 __ASM_GLOBAL_FUNC( SMapLS,
599                    "xor %edx,%edx\n\t"
600                    "testl $0xffff0000,%eax\n\t"
601                    "jz 1f\n\t"
602                    "pushl %eax\n\t"
603                    "call " __ASM_NAME("MapLS") "\n\t"
604                    "movl %eax,%edx\n"
605                    "1:\tret" )
606
607 /***********************************************************************
608  *              SUnMapLS (KERNEL32.@)
609  */
610 __ASM_GLOBAL_FUNC( SUnMapLS,
611                    "pushl %eax\n\t"  /* preserve eax */
612                    "pushl %eax\n\t"
613                    "call " __ASM_NAME("UnMapLS") "\n\t"
614                    "popl %eax\n\t"
615                    "ret" )
616
617 /***********************************************************************
618  *              SMapLS_IP_EBP_8 (KERNEL32.@)
619  *              SMapLS_IP_EBP_12 (KERNEL32.@)
620  *              SMapLS_IP_EBP_16 (KERNEL32.@)
621  *              SMapLS_IP_EBP_20 (KERNEL32.@)
622  *              SMapLS_IP_EBP_24 (KERNEL32.@)
623  *              SMapLS_IP_EBP_28 (KERNEL32.@)
624  *              SMapLS_IP_EBP_32 (KERNEL32.@)
625  *              SMapLS_IP_EBP_36 (KERNEL32.@)
626  *              SMapLS_IP_EBP_40 (KERNEL32.@)
627  *
628  * These functions map linear pointers at [EBP+xxx] to segmented pointers
629  * and return them.
630  * Win95 uses some kind of alias structs, which it stores in [EBP+x] to
631  * unravel them at SUnMapLS. We just store the segmented pointer there.
632  */
633 #define DEFINE_SMapLS(n) \
634   __ASM_GLOBAL_FUNC( SMapLS_IP_EBP_ ## n, \
635                      "movl " #n "(%ebp),%eax\n\t" \
636                      "call " __ASM_NAME("SMapLS") "\n\t" \
637                      "movl %edx," #n "(%ebp)\n\t" \
638                      "ret" )
639
640 DEFINE_SMapLS(8)
641 DEFINE_SMapLS(12)
642 DEFINE_SMapLS(16)
643 DEFINE_SMapLS(20)
644 DEFINE_SMapLS(24)
645 DEFINE_SMapLS(28)
646 DEFINE_SMapLS(32)
647 DEFINE_SMapLS(36)
648 DEFINE_SMapLS(40)
649
650
651 /***********************************************************************
652  *              SUnMapLS_IP_EBP_8 (KERNEL32.@)
653  *              SUnMapLS_IP_EBP_12 (KERNEL32.@)
654  *              SUnMapLS_IP_EBP_16 (KERNEL32.@)
655  *              SUnMapLS_IP_EBP_20 (KERNEL32.@)
656  *              SUnMapLS_IP_EBP_24 (KERNEL32.@)
657  *              SUnMapLS_IP_EBP_28 (KERNEL32.@)
658  *              SUnMapLS_IP_EBP_32 (KERNEL32.@)
659  *              SUnMapLS_IP_EBP_36 (KERNEL32.@)
660  *              SUnMapLS_IP_EBP_40 (KERNEL32.@)
661  */
662
663 #define DEFINE_SUnMapLS(n) \
664   __ASM_GLOBAL_FUNC( SUnMapLS_IP_EBP_ ## n, \
665                      "pushl %eax\n\t"  /* preserve eax */ \
666                      "pushl " #n "(%ebp)\n\t" \
667                      "call " __ASM_NAME("UnMapLS") "\n\t" \
668                      "movl $0," #n "(%ebp)\n\t" \
669                      "popl %eax\n\t" \
670                      "ret" )
671
672 DEFINE_SUnMapLS(8)
673 DEFINE_SUnMapLS(12)
674 DEFINE_SUnMapLS(16)
675 DEFINE_SUnMapLS(20)
676 DEFINE_SUnMapLS(24)
677 DEFINE_SUnMapLS(28)
678 DEFINE_SUnMapLS(32)
679 DEFINE_SUnMapLS(36)
680 DEFINE_SUnMapLS(40)
681
682 #endif  /* __i386__ */