2 * Selector manipulation functions
4 * Copyright 1995 Alexandre Julliard
8 #include "wine/winbase16.h"
11 #include "selectors.h"
12 #include "stackframe.h"
14 #include "debugtools.h"
17 DEFAULT_DEBUG_CHANNEL(selector)
20 /***********************************************************************
21 * AllocSelectorArray (KERNEL.206)
23 WORD WINAPI AllocSelectorArray16( WORD count )
25 WORD i, sel, size = 0;
29 for (i = FIRST_LDT_ENTRY_TO_ALLOC; i < LDT_SIZE; i++)
31 if (!IS_LDT_ENTRY_FREE(i)) size = 0;
32 else if (++size >= count) break;
34 if (i == LDT_SIZE) return 0;
38 entry.type = SEGMENT_DATA;
39 entry.seg_32bit = FALSE;
40 entry.read_only = FALSE;
41 entry.limit_in_pages = FALSE;
42 entry.limit = 1; /* avoid 0 base and limit */
44 for (i = 0; i < count; i++)
46 /* Mark selector as allocated */
47 ldt_flags_copy[sel + i] |= LDT_FLAGS_ALLOCATED;
48 LDT_SetEntry( sel + i, &entry );
50 return ENTRY_TO_SELECTOR( sel );
54 /***********************************************************************
55 * AllocSelector (KERNEL.175)
57 WORD WINAPI AllocSelector16( WORD sel )
59 WORD newsel, count, i;
61 count = sel ? ((GET_SEL_LIMIT(sel) >> 16) + 1) : 1;
62 newsel = AllocSelectorArray16( count );
63 TRACE("(%04x): returning %04x\n",
65 if (!newsel) return 0;
66 if (!sel) return newsel; /* nothing to copy */
67 for (i = 0; i < count; i++)
70 LDT_GetEntry( SELECTOR_TO_ENTRY(sel) + i, &entry );
71 LDT_SetEntry( SELECTOR_TO_ENTRY(newsel) + i, &entry );
77 /***********************************************************************
78 * FreeSelector (KERNEL.176)
80 WORD WINAPI FreeSelector16( WORD sel )
82 if (IS_SELECTOR_FREE(sel)) return sel; /* error */
83 SELECTOR_FreeBlock( sel, 1 );
88 /***********************************************************************
91 * Set the LDT entries for an array of selectors.
93 static void SELECTOR_SetEntries( WORD sel, const void *base, DWORD size,
94 enum seg_type type, BOOL is32bit,
100 /* The limit for the first selector is the whole */
101 /* block. The next selectors get a 64k limit. */
102 entry.base = (unsigned long)base;
104 entry.seg_32bit = is32bit;
105 entry.read_only = readonly;
106 entry.limit_in_pages = (size > 0x100000);
107 if (entry.limit_in_pages) entry.limit = ((size + 0xfff) >> 12) - 1;
108 else entry.limit = size - 1;
109 /* Make sure base and limit are not 0 together if the size is not 0 */
110 if (!base && !entry.limit && size) entry.limit = 1;
111 count = (size + 0xffff) / 0x10000;
112 for (i = 0; i < count; i++)
114 LDT_SetEntry( SELECTOR_TO_ENTRY(sel) + i, &entry );
115 entry.base += 0x10000;
116 /* Apparently the next selectors should *not* get a 64k limit. */
117 /* Can't remember where I read they should... --AJ */
118 entry.limit -= entry.limit_in_pages ? 0x10 : 0x10000;
123 /***********************************************************************
124 * SELECTOR_AllocBlock
126 * Allocate selectors for a block of linear memory.
128 WORD SELECTOR_AllocBlock( const void *base, DWORD size, enum seg_type type,
129 BOOL is32bit, BOOL readonly )
134 count = (size + 0xffff) / 0x10000;
135 sel = AllocSelectorArray16( count );
136 if (sel) SELECTOR_SetEntries( sel, base, size, type, is32bit, readonly );
141 /***********************************************************************
144 * Move a block of selectors in linear memory.
146 void SELECTOR_MoveBlock( WORD sel, const void *new_base )
148 WORD i, count = (GET_SEL_LIMIT(sel) >> 16) + 1;
150 for (i = 0; i < count; i++)
153 LDT_GetEntry( SELECTOR_TO_ENTRY(sel) + i, &entry );
154 entry.base = (unsigned long)new_base;
155 LDT_SetEntry( SELECTOR_TO_ENTRY(sel) + i, &entry );
160 /***********************************************************************
163 * Free a block of selectors.
165 void SELECTOR_FreeBlock( WORD sel, WORD count )
170 TRACE("(%04x,%d)\n", sel, count );
171 sel &= ~(__AHINCR - 1); /* clear bottom bits of selector */
172 nextsel = sel + (count << __AHSHIFT);
176 /* Check if we are freeing current %fs or %gs selector */
180 if ((fs >= sel) && (fs < nextsel))
182 WARN("Freeing %%fs selector (%04x), not good.\n", fs );
186 if ((gs >= sel) && (gs < nextsel)) SET_GS( 0 );
188 #endif /* __i386__ */
190 memset( &entry, 0, sizeof(entry) ); /* clear the LDT entries */
191 for (i = SELECTOR_TO_ENTRY(sel); count; i++, count--)
193 LDT_SetEntry( i, &entry );
194 ldt_flags_copy[i] &= ~LDT_FLAGS_ALLOCATED;
199 /***********************************************************************
200 * SELECTOR_ReallocBlock
202 * Change the size of a block of selectors.
204 WORD SELECTOR_ReallocBlock( WORD sel, const void *base, DWORD size )
207 WORD i, oldcount, newcount;
210 oldcount = (GET_SEL_LIMIT(sel) >> 16) + 1;
211 newcount = (size + 0xffff) >> 16;
212 LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
214 if (oldcount < newcount) /* We need to add selectors */
216 /* Check if the next selectors are free */
217 if (SELECTOR_TO_ENTRY(sel) + newcount > LDT_SIZE) i = oldcount;
219 for (i = oldcount; i < newcount; i++)
220 if (!IS_LDT_ENTRY_FREE(SELECTOR_TO_ENTRY(sel)+i)) break;
222 if (i < newcount) /* they are not free */
224 SELECTOR_FreeBlock( sel, oldcount );
225 sel = AllocSelectorArray16( newcount );
227 else /* mark the selectors as allocated */
229 for (i = oldcount; i < newcount; i++)
230 ldt_flags_copy[SELECTOR_TO_ENTRY(sel)+i] |=LDT_FLAGS_ALLOCATED;
233 else if (oldcount > newcount) /* We need to remove selectors */
235 SELECTOR_FreeBlock( ENTRY_TO_SELECTOR(SELECTOR_TO_ENTRY(sel)+newcount),
236 oldcount - newcount );
238 if (sel) SELECTOR_SetEntries( sel, base, size, entry.type,
239 entry.seg_32bit, entry.read_only );
244 /***********************************************************************
245 * PrestoChangoSelector (KERNEL.177)
247 WORD WINAPI PrestoChangoSelector16( WORD selSrc, WORD selDst )
250 LDT_GetEntry( SELECTOR_TO_ENTRY( selSrc ), &entry );
251 entry.type ^= SEGMENT_CODE; /* toggle the executable bit */
252 LDT_SetEntry( SELECTOR_TO_ENTRY( selDst ), &entry );
257 /***********************************************************************
258 * AllocCStoDSAlias (KERNEL.170)
260 WORD WINAPI AllocCStoDSAlias16( WORD sel )
265 newsel = AllocSelectorArray16( 1 );
266 TRACE("(%04x): returning %04x\n",
268 if (!newsel) return 0;
269 LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
270 entry.type = SEGMENT_DATA;
271 LDT_SetEntry( SELECTOR_TO_ENTRY(newsel), &entry );
276 /***********************************************************************
277 * AllocDStoCSAlias (KERNEL.171)
279 WORD WINAPI AllocDStoCSAlias16( WORD sel )
284 newsel = AllocSelectorArray16( 1 );
285 TRACE("(%04x): returning %04x\n",
287 if (!newsel) return 0;
288 LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
289 entry.type = SEGMENT_CODE;
290 LDT_SetEntry( SELECTOR_TO_ENTRY(newsel), &entry );
295 /***********************************************************************
296 * LongPtrAdd (KERNEL.180)
298 void WINAPI LongPtrAdd16( DWORD ptr, DWORD add )
301 LDT_GetEntry( SELECTOR_TO_ENTRY(SELECTOROF(ptr)), &entry );
303 LDT_SetEntry( SELECTOR_TO_ENTRY(SELECTOROF(ptr)), &entry );
307 /***********************************************************************
308 * GetSelectorBase (KERNEL.186)
310 DWORD WINAPI WIN16_GetSelectorBase( WORD sel )
313 * Note: For Win32s processes, the whole linear address space is
314 * shifted by 0x10000 relative to the OS linear address space.
315 * See the comment in msdos/vxd.c.
318 DWORD base = GetSelectorBase( sel );
319 return W32S_WINE2APP( base, W32S_APPLICATION() ? W32S_OFFSET : 0 );
321 DWORD WINAPI GetSelectorBase( WORD sel )
323 DWORD base = GET_SEL_BASE(sel);
325 /* if base points into DOSMEM, assume we have to
326 * return pointer into physical lower 1MB */
328 return DOSMEM_MapLinearToDos( (LPVOID)base );
332 /***********************************************************************
333 * SetSelectorBase (KERNEL.187)
335 DWORD WINAPI WIN16_SetSelectorBase( WORD sel, DWORD base )
338 * Note: For Win32s processes, the whole linear address space is
339 * shifted by 0x10000 relative to the OS linear address space.
340 * See the comment in msdos/vxd.c.
343 SetSelectorBase( sel,
344 W32S_APP2WINE( base, W32S_APPLICATION() ? W32S_OFFSET : 0 ) );
347 WORD WINAPI SetSelectorBase( WORD sel, DWORD base )
351 LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
353 entry.base = (DWORD)DOSMEM_MapDosToLinear(base);
355 LDT_SetEntry( SELECTOR_TO_ENTRY(sel), &entry );
360 /***********************************************************************
361 * GetSelectorLimit (KERNEL.188)
363 DWORD WINAPI GetSelectorLimit16( WORD sel )
365 return GET_SEL_LIMIT(sel);
369 /***********************************************************************
370 * SetSelectorLimit (KERNEL.189)
372 WORD WINAPI SetSelectorLimit16( WORD sel, DWORD limit )
375 LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
376 entry.limit_in_pages = (limit >= 0x100000);
377 if (entry.limit_in_pages) entry.limit = limit >> 12;
378 else entry.limit = limit;
379 LDT_SetEntry( SELECTOR_TO_ENTRY(sel), &entry );
384 /***********************************************************************
385 * SelectorAccessRights (KERNEL.196)
387 WORD WINAPI SelectorAccessRights16( WORD sel, WORD op, WORD val )
390 LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
391 if (op == 0) /* get */
393 return 0x01 | /* accessed */
394 0x10 | /* not system */
397 ((entry.read_only == 0) << 1) |
399 (entry.seg_32bit << 14) |
400 (entry.limit_in_pages << 15);
404 entry.read_only = ((val & 2) == 0);
405 entry.type = (val >> 2) & 3;
406 entry.seg_32bit = val & 0x4000;
407 entry.limit_in_pages = val & 0x8000;
408 LDT_SetEntry( SELECTOR_TO_ENTRY(sel), &entry );
414 /***********************************************************************
415 * IsBadCodePtr16 (KERNEL.336)
417 BOOL16 WINAPI IsBadCodePtr16( SEGPTR lpfn )
422 sel = SELECTOROF(lpfn);
423 if (!sel) return TRUE;
424 if (IS_SELECTOR_FREE(sel)) return TRUE;
425 LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
426 if (entry.type != SEGMENT_CODE) return TRUE;
427 if (OFFSETOF(lpfn) > GET_SEL_LIMIT(sel)) return TRUE;
432 /***********************************************************************
433 * IsBadStringPtr16 (KERNEL.337)
435 BOOL16 WINAPI IsBadStringPtr16( SEGPTR ptr, UINT16 size )
440 sel = SELECTOROF(ptr);
441 if (!sel) return TRUE;
442 if (IS_SELECTOR_FREE(sel)) return TRUE;
443 LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
444 if ((entry.type == SEGMENT_CODE) && entry.read_only) return TRUE;
445 if (strlen(PTR_SEG_TO_LIN(ptr)) < size) size = strlen(PTR_SEG_TO_LIN(ptr)) + 1;
446 if (size && (OFFSETOF(ptr) + size - 1 > GET_SEL_LIMIT(sel))) return TRUE;
451 /***********************************************************************
452 * IsBadHugeReadPtr16 (KERNEL.346)
454 BOOL16 WINAPI IsBadHugeReadPtr16( SEGPTR ptr, DWORD size )
459 sel = SELECTOROF(ptr);
460 if (!sel) return TRUE;
461 if (IS_SELECTOR_FREE(sel)) return TRUE;
462 LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
463 if ((entry.type == SEGMENT_CODE) && entry.read_only) return TRUE;
464 if (size && (OFFSETOF(ptr) + size - 1 > GET_SEL_LIMIT(sel))) return TRUE;
469 /***********************************************************************
470 * IsBadHugeWritePtr16 (KERNEL.347)
472 BOOL16 WINAPI IsBadHugeWritePtr16( SEGPTR ptr, DWORD size )
477 sel = SELECTOROF(ptr);
478 if (!sel) return TRUE;
479 if (IS_SELECTOR_FREE(sel)) return TRUE;
480 LDT_GetEntry( SELECTOR_TO_ENTRY(sel), &entry );
481 if ((entry.type == SEGMENT_CODE) || entry.read_only) return TRUE;
482 if (size && (OFFSETOF(ptr) + size - 1 > GET_SEL_LIMIT(sel))) return TRUE;
486 /***********************************************************************
487 * IsBadReadPtr16 (KERNEL.334)
489 BOOL16 WINAPI IsBadReadPtr16( SEGPTR ptr, UINT16 size )
491 return IsBadHugeReadPtr16( ptr, size );
495 /***********************************************************************
496 * IsBadWritePtr16 (KERNEL.335)
498 BOOL16 WINAPI IsBadWritePtr16( SEGPTR ptr, UINT16 size )
500 return IsBadHugeWritePtr16( ptr, size );
504 /***********************************************************************
505 * IsBadFlatReadWritePtr16 (KERNEL.627)
507 BOOL16 WINAPI IsBadFlatReadWritePtr16( SEGPTR ptr, DWORD size, BOOL16 bWrite )
509 return bWrite? IsBadHugeWritePtr16( ptr, size )
510 : IsBadHugeReadPtr16( ptr, size );
514 /***********************************************************************
515 * MemoryRead (TOOLHELP.78)
517 DWORD WINAPI MemoryRead16( WORD sel, DWORD offset, void *buffer, DWORD count )
519 if (IS_SELECTOR_FREE(sel)) return 0;
520 if (offset > GET_SEL_LIMIT(sel)) return 0;
521 if (offset + count > GET_SEL_LIMIT(sel) + 1)
522 count = GET_SEL_LIMIT(sel) + 1 - offset;
523 memcpy( buffer, ((char *)GET_SEL_BASE(sel)) + offset, count );
528 /***********************************************************************
529 * MemoryWrite (TOOLHELP.79)
531 DWORD WINAPI MemoryWrite16( WORD sel, DWORD offset, void *buffer, DWORD count )
533 if (IS_SELECTOR_FREE(sel)) return 0;
534 if (offset > GET_SEL_LIMIT(sel)) return 0;
535 if (offset + count > GET_SEL_LIMIT(sel) + 1)
536 count = GET_SEL_LIMIT(sel) + 1 - offset;
537 memcpy( ((char *)GET_SEL_BASE(sel)) + offset, buffer, count );
541 /************************************* Win95 pointer mapping functions *
545 /***********************************************************************
546 * MapSL (KERNEL32.523)
548 * Maps fixed segmented pointer to linear.
550 LPVOID WINAPI MapSL( SEGPTR sptr )
552 return (LPVOID)PTR_SEG_TO_LIN(sptr);
555 /***********************************************************************
556 * MapSLFix (KERNEL32.524)
558 * FIXME: MapSLFix and UnMapSLFixArray should probably prevent
559 * unexpected linear address change when GlobalCompact() shuffles
563 LPVOID WINAPI MapSLFix( SEGPTR sptr )
565 return (LPVOID)PTR_SEG_TO_LIN(sptr);
568 /***********************************************************************
569 * UnMapSLFixArray (KERNEL32.701)
572 void WINAPI UnMapSLFixArray( SEGPTR sptr[], INT length, CONTEXT86 *context )
574 /* Must not change EAX, hence defined as 'register' function */
577 /***********************************************************************
578 * MapLS (KERNEL32.522)
580 * Maps linear pointer to segmented.
582 SEGPTR WINAPI MapLS( LPVOID ptr )
588 WORD sel = SELECTOR_AllocBlock( ptr, 0x10000, SEGMENT_DATA, FALSE, FALSE );
589 return PTR_SEG_OFF_TO_SEGPTR( sel, 0 );
594 /***********************************************************************
595 * UnMapLS (KERNEL32.700)
597 * Free mapped selector.
599 void WINAPI UnMapLS( SEGPTR sptr )
601 if (SELECTOROF(sptr))
602 SELECTOR_FreeBlock( SELECTOROF(sptr), 1 );
605 /***********************************************************************
606 * GetThreadSelectorEntry (KERNEL32)
607 * FIXME: add #ifdef i386 for non x86
609 BOOL WINAPI GetThreadSelectorEntry( HANDLE hthread, DWORD sel,
614 LDT_GetEntry(SELECTOR_TO_ENTRY(sel),&ldtentry);
615 ldtent->BaseLow = ldtentry.base & 0x0000ffff;
616 ldtent->HighWord.Bits.BaseMid = (ldtentry.base & 0x00ff0000) >> 16;
617 ldtent->HighWord.Bits.BaseHi = (ldtentry.base & 0xff000000) >> 24;
618 ldtent->LimitLow = ldtentry.limit & 0x0000ffff;
619 ldtent->HighWord.Bits.LimitHi = (ldtentry.limit & 0x00ff0000) >> 16;
620 ldtent->HighWord.Bits.Dpl = 3;
621 ldtent->HighWord.Bits.Sys = 0;
622 ldtent->HighWord.Bits.Pres = 1;
623 ldtent->HighWord.Bits.Type = 0x10|(ldtentry.type << 2);
624 if (!ldtentry.read_only)
625 ldtent->HighWord.Bits.Type|=0x2;
626 ldtent->HighWord.Bits.Granularity = ldtentry.limit_in_pages;
627 ldtent->HighWord.Bits.Default_Big = ldtentry.seg_32bit;
632 /**********************************************************************
634 * These functions map linear pointers at [EBP+xxx] to segmented pointers
636 * Win95 uses some kind of alias structs, which it stores in [EBP+x] to
637 * unravel them at SUnMapLS. We just store the segmented pointer there.
640 x_SMapLS_IP_EBP_x(CONTEXT86 *context,int argoff) {
643 val =*(DWORD*)(EBP_reg(context)+argoff);
646 *(DWORD*)(EBP_reg(context)+argoff) = 0;
648 ptr = MapLS((LPVOID)val);
649 *(DWORD*)(EBP_reg(context)+argoff) = ptr;
651 EAX_reg(context) = ptr;
654 void WINAPI SMapLS_IP_EBP_8 (CONTEXT86 *context) {x_SMapLS_IP_EBP_x(context, 8);}
655 void WINAPI SMapLS_IP_EBP_12(CONTEXT86 *context) {x_SMapLS_IP_EBP_x(context,12);}
656 void WINAPI SMapLS_IP_EBP_16(CONTEXT86 *context) {x_SMapLS_IP_EBP_x(context,16);}
657 void WINAPI SMapLS_IP_EBP_20(CONTEXT86 *context) {x_SMapLS_IP_EBP_x(context,20);}
658 void WINAPI SMapLS_IP_EBP_24(CONTEXT86 *context) {x_SMapLS_IP_EBP_x(context,24);}
659 void WINAPI SMapLS_IP_EBP_28(CONTEXT86 *context) {x_SMapLS_IP_EBP_x(context,28);}
660 void WINAPI SMapLS_IP_EBP_32(CONTEXT86 *context) {x_SMapLS_IP_EBP_x(context,32);}
661 void WINAPI SMapLS_IP_EBP_36(CONTEXT86 *context) {x_SMapLS_IP_EBP_x(context,36);}
662 void WINAPI SMapLS_IP_EBP_40(CONTEXT86 *context) {x_SMapLS_IP_EBP_x(context,40);}
664 void WINAPI SMapLS( CONTEXT86 *context )
666 if (EAX_reg(context)>=0x10000) {
667 EAX_reg(context) = MapLS((LPVOID)EAX_reg(context));
668 EDX_reg(context) = EAX_reg(context);
670 EDX_reg(context) = 0;
674 void WINAPI SUnMapLS( CONTEXT86 *context )
676 if (EAX_reg(context)>=0x10000)
677 UnMapLS((SEGPTR)EAX_reg(context));
681 x_SUnMapLS_IP_EBP_x(CONTEXT86 *context,int argoff) {
682 if (*(DWORD*)(EBP_reg(context)+argoff))
683 UnMapLS(*(DWORD*)(EBP_reg(context)+argoff));
684 *(DWORD*)(EBP_reg(context)+argoff)=0;
686 void WINAPI SUnMapLS_IP_EBP_8 (CONTEXT86 *context) { x_SUnMapLS_IP_EBP_x(context, 8); }
687 void WINAPI SUnMapLS_IP_EBP_12(CONTEXT86 *context) { x_SUnMapLS_IP_EBP_x(context,12); }
688 void WINAPI SUnMapLS_IP_EBP_16(CONTEXT86 *context) { x_SUnMapLS_IP_EBP_x(context,16); }
689 void WINAPI SUnMapLS_IP_EBP_20(CONTEXT86 *context) { x_SUnMapLS_IP_EBP_x(context,20); }
690 void WINAPI SUnMapLS_IP_EBP_24(CONTEXT86 *context) { x_SUnMapLS_IP_EBP_x(context,24); }
691 void WINAPI SUnMapLS_IP_EBP_28(CONTEXT86 *context) { x_SUnMapLS_IP_EBP_x(context,28); }
692 void WINAPI SUnMapLS_IP_EBP_32(CONTEXT86 *context) { x_SUnMapLS_IP_EBP_x(context,32); }
693 void WINAPI SUnMapLS_IP_EBP_36(CONTEXT86 *context) { x_SUnMapLS_IP_EBP_x(context,36); }
694 void WINAPI SUnMapLS_IP_EBP_40(CONTEXT86 *context) { x_SUnMapLS_IP_EBP_x(context,40); }
696 /**********************************************************************
697 * AllocMappedBuffer (KERNEL32.38)
699 * This is a undocumented KERNEL32 function that
700 * SMapLS's a GlobalAlloc'ed buffer.
702 * Input: EDI register: size of buffer to allocate
703 * Output: EDI register: pointer to buffer
705 * Note: The buffer is preceeded by 8 bytes:
708 * edi-4 SEGPTR to buffer
709 * edi-8 some magic Win95 needs for SUnMapLS
710 * (we use it for the memory handle)
712 * The SEGPTR is used by the caller!
715 void WINAPI AllocMappedBuffer( CONTEXT86 *context )
717 HGLOBAL handle = GlobalAlloc(0, EDI_reg(context) + 8);
718 DWORD *buffer = (DWORD *)GlobalLock(handle);
722 if (!(ptr = MapLS(buffer + 2)))
724 GlobalUnlock(handle);
729 EAX_reg(context) = EDI_reg(context) = 0;
735 EAX_reg(context) = (DWORD) ptr;
736 EDI_reg(context) = (DWORD)(buffer + 2);
740 /**********************************************************************
741 * FreeMappedBuffer (KERNEL32.39)
743 * Free a buffer allocated by AllocMappedBuffer
745 * Input: EDI register: pointer to buffer
748 void WINAPI FreeMappedBuffer( CONTEXT86 *context )
750 if (EDI_reg(context))
752 DWORD *buffer = (DWORD *)EDI_reg(context) - 2;
756 GlobalUnlock(buffer[0]);
757 GlobalFree(buffer[0]);
762 /***********************************************************************
763 * UTSelectorOffsetToLinear (WIN32S16.48)
765 * rough guesswork, but seems to work (I had no "reasonable" docu)
767 LPVOID WINAPI UTSelectorOffsetToLinear16(SEGPTR sptr)
769 return PTR_SEG_TO_LIN(sptr);
772 /***********************************************************************
773 * UTLinearToSelectorOffset (WIN32S16.49)
775 * FIXME: I don't know if that's the right way to do linear -> segmented
777 SEGPTR WINAPI UTLinearToSelectorOffset16(LPVOID lptr)