Added a default program option in wine.conf in section [programs] key
[wine] / msdos / dosmem.c
1 /*
2  * DOS memory emulation
3  *
4  * Copyright 1995 Alexandre Julliard
5  * Copyright 1996 Marcus Meissner
6  */
7
8 #include <signal.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include "windows.h"
12 #include "winbase.h"
13 #include "global.h"
14 #include "ldt.h"
15 #include "miscemu.h"
16 #include "module.h"
17 #include "task.h"
18 #include "debug.h"
19
20 HANDLE16 DOSMEM_BiosSeg;  /* BIOS data segment at 0x40:0 */
21
22 #pragma pack(1)
23
24 typedef struct
25 {
26     WORD  Com1Addr;                  /* 00: COM1 I/O address */
27     WORD  Com2Addr;                  /* 02: COM2 I/O address */
28     WORD  Com3Addr;                  /* 04: COM3 I/O address */
29     WORD  Com4Addr;                  /* 06: COM4 I/O address */
30     WORD  Lpt1Addr;                  /* 08: LPT1 I/O address */
31     WORD  Lpt2Addr;                  /* 0a: LPT2 I/O address */
32     WORD  Lpt3Addr;                  /* 0c: LPT3 I/O address */
33     WORD  Lpt4Addr;                  /* 0e: LPT4 I/O address */
34     WORD  InstalledHardware;         /* 10: Installed hardware flags */
35     BYTE  POSTstatus;                /* 12: Power-On Self Test status */
36     WORD  MemSize WINE_PACKED;       /* 13: Base memory size in Kb */
37     WORD  unused1 WINE_PACKED;       /* 15: Manufacturing test scratch pad */
38     BYTE  KbdFlags1;                 /* 17: Keyboard flags 1 */
39     BYTE  KbdFlags2;                 /* 18: Keyboard flags 2 */
40     BYTE  unused2;                   /* 19: Keyboard driver workspace */
41     WORD  NextKbdCharPtr;            /* 1a: Next character in kbd buffer */
42     WORD  FirstKbdCharPtr;           /* 1c: First character in kbd buffer */
43     WORD  KbdBuffer[16];             /* 1e: Keyboard buffer */
44     BYTE  DisketteStatus1;           /* 3e: Diskette recalibrate status */
45     BYTE  DisketteStatus2;           /* 3f: Diskette motor status */
46     BYTE  DisketteStatus3;           /* 40: Diskette motor timeout */
47     BYTE  DisketteStatus4;           /* 41: Diskette last operation status */
48     BYTE  DiskStatus[7];             /* 42: Disk status/command bytes */
49     BYTE  VideoMode;                 /* 49: Video mode */
50     WORD  VideoColumns;              /* 4a: Number of columns */
51     WORD  VideoPageSize;             /* 4c: Video page size in bytes */
52     WORD  VideoPageStartAddr;        /* 4e: Video page start address */
53     BYTE  VideoCursorPos[16];        /* 50: Cursor position for 8 pages */
54     WORD  VideoCursorType;           /* 60: Video cursor type */
55     BYTE  VideoCurPage;              /* 62: Video current page */
56     WORD  VideoCtrlAddr WINE_PACKED; /* 63: Video controller address */
57     BYTE  VideoReg1;                 /* 65: Video mode select register */
58     BYTE  VideoReg2;                 /* 66: Video CGA palette register */
59     DWORD ResetEntry WINE_PACKED;    /* 67: Warm reset entry point */
60     BYTE  LastIRQ;                   /* 6b: Last unexpected interrupt */
61     DWORD Ticks;                     /* 6c: Ticks since midnight */
62     BYTE  TicksOverflow;             /* 70: Timer overflow if past midnight */
63     BYTE  CtrlBreakFlag;             /* 71: Ctrl-Break flag */
64     WORD  ResetFlag;                 /* 72: POST Reset flag */
65     BYTE  DiskOpStatus;              /* 74: Last hard-disk operation status */
66     BYTE  NbHardDisks;               /* 75: Number of hard disks */
67     BYTE  DiskCtrlByte;              /* 76: Disk control byte */
68     BYTE  DiskIOPort;                /* 77: Disk I/O port offset */
69     BYTE  LptTimeout[4];             /* 78: Timeouts for parallel ports */
70     BYTE  ComTimeout[4];             /* 7c: Timeouts for serial ports */
71     WORD  KbdBufferStart;            /* 80: Keyboard buffer start */
72     WORD  KbdBufferEnd;              /* 82: Keyboard buffer end */
73 } BIOSDATA;
74
75 #pragma pack(4)
76
77 static BIOSDATA *pBiosData = NULL;
78 static char     *DOSMEM_dosmem;
79
80        DWORD     DOSMEM_CollateTable;
81
82        DWORD     DOSMEM_ErrorCall;
83        DWORD     DOSMEM_ErrorBuffer;
84
85 /* use 2 low bits of 'size' for the housekeeping */
86
87 #define DM_BLOCK_DEBUG          0xABE00000
88 #define DM_BLOCK_TERMINAL       0x00000001
89 #define DM_BLOCK_FREE           0x00000002
90 #define DM_BLOCK_MASK           0x001FFFFC
91
92 /*
93 #define __DOSMEM_DEBUG__
94  */
95
96 typedef struct {
97    unsigned     size;
98 } dosmem_entry;
99
100 typedef struct {
101   unsigned      blocks;
102   unsigned      free;
103 } dosmem_info;
104
105 #define NEXT_BLOCK(block) \
106         (dosmem_entry*)(((char*)(block)) + \
107          sizeof(dosmem_entry) + ((block)->size & DM_BLOCK_MASK))
108
109 #define VM_STUB(x) (0x90CF00CD|(x<<8)) /* INT x; IRET; NOP */
110 #define VM_STUB_SEGMENT 0xf000         /* BIOS segment */
111
112 /***********************************************************************
113  *           DOSMEM_MemoryBase
114  *
115  * Gets the DOS memory base.
116  */
117 char *DOSMEM_MemoryBase(HMODULE16 hModule)
118 {
119     TDB *pTask = hModule ? NULL : (TDB *)GlobalLock16( GetCurrentTask() );
120     NE_MODULE *pModule = (hModule || pTask) ? NE_GetPtr( hModule ? hModule : pTask->hModule ) : NULL;
121
122     GlobalUnlock16( GetCurrentTask() );
123     if (pModule && pModule->dos_image)
124         return pModule->dos_image;
125     else
126         return DOSMEM_dosmem;
127 }
128
129 /***********************************************************************
130  *           DOSMEM_MemoryTop
131  *
132  * Gets the DOS memory top.
133  */
134 static char *DOSMEM_MemoryTop(HMODULE16 hModule)
135 {
136     return DOSMEM_MemoryBase(hModule)+0x9FFFC; /* 640K */
137 }
138
139 /***********************************************************************
140  *           DOSMEM_InfoBlock
141  *
142  * Gets the DOS memory info block.
143  */
144 static dosmem_info *DOSMEM_InfoBlock(HMODULE16 hModule)
145 {
146     return (dosmem_info*)(DOSMEM_MemoryBase(hModule)+0x10000); /* 64K */
147 }
148
149 /***********************************************************************
150  *           DOSMEM_RootBlock
151  *
152  * Gets the DOS memory root block.
153  */
154 static dosmem_entry *DOSMEM_RootBlock(HMODULE16 hModule)
155 {
156     /* first block has to be paragraph-aligned */
157     return (dosmem_entry*)(((char*)DOSMEM_InfoBlock(hModule)) +
158                            ((((sizeof(dosmem_info) + 0xf) & ~0xf) - sizeof(dosmem_entry))));
159 }
160
161 /***********************************************************************
162  *           DOSMEM_FillIsrTable
163  *
164  * Fill the interrupt table with fake BIOS calls to BIOSSEG (0xf000).
165  *
166  * NOTES:
167  * Linux normally only traps INTs performed from or destined to BIOSSEG
168  * for us to handle, if the int_revectored table is empty. Filling the
169  * interrupt table with calls to INT stubs in BIOSSEG allows DOS programs
170  * to hook interrupts, as well as use their familiar retf tricks to call
171  * them, AND let Wine handle any unhooked interrupts transparently.
172  */
173 static void DOSMEM_FillIsrTable(HMODULE16 hModule)
174 {
175     SEGPTR *isr = (SEGPTR*)DOSMEM_MemoryBase(hModule);
176     DWORD *stub = (DWORD*)((char*)isr + (VM_STUB_SEGMENT << 4));
177     int x;
178  
179     for (x=0; x<256; x++) isr[x]=PTR_SEG_OFF_TO_SEGPTR(VM_STUB_SEGMENT,x*4);
180     for (x=0; x<256; x++) stub[x]=VM_STUB(x);
181
182
183 /***********************************************************************
184  *           DOSMEM_FillBiosSegment
185  *
186  * Fill the BIOS data segment with dummy values.
187  */
188 static void DOSMEM_FillBiosSegment(void)
189 {
190     pBiosData = (BIOSDATA *)GlobalLock16( DOSMEM_BiosSeg );
191
192       /* Clear all unused values */
193     memset( pBiosData, 0, sizeof(*pBiosData) );
194
195     /* FIXME: should check the number of configured drives and ports */
196
197     pBiosData->Com1Addr             = 0x3e8;
198     pBiosData->Com2Addr             = 0x2e8;
199     pBiosData->Lpt1Addr             = 0x378;
200     pBiosData->Lpt2Addr             = 0x278;
201     pBiosData->InstalledHardware    = 0x8443;
202     pBiosData->MemSize              = 640;
203     pBiosData->NextKbdCharPtr       = 0x1e;
204     pBiosData->FirstKbdCharPtr      = 0x1e;
205     pBiosData->VideoMode            = 0;
206     pBiosData->VideoColumns         = 80;
207     pBiosData->VideoPageSize        = 80 * 25 * 2;
208     pBiosData->VideoPageStartAddr   = 0xb800;
209     pBiosData->VideoCtrlAddr        = 0x3d4;
210     pBiosData->Ticks                = INT1A_GetTicksSinceMidnight();
211     pBiosData->NbHardDisks          = 2;
212     pBiosData->KbdBufferStart       = 0x1e;
213     pBiosData->KbdBufferEnd         = 0x3e;
214 }
215
216 /***********************************************************************
217  *           DOSMEM_InitCollateTable
218  *
219  * Initialises the collate table (character sorting, language dependent)
220  */
221 static void DOSMEM_InitCollateTable()
222 {
223         DWORD           x;
224         unsigned char   *tbl;
225         int             i;
226
227         x = GlobalDOSAlloc(258);
228         DOSMEM_CollateTable = MAKELONG(0,(x>>16));
229         tbl = DOSMEM_MapRealToLinear(DOSMEM_CollateTable);
230         *(WORD*)tbl     = 0x100;
231         tbl += 2;
232         for ( i = 0; i < 0x100; i++) *tbl++ = i;
233 }
234
235 /***********************************************************************
236  *           DOSMEM_InitErrorTable
237  *
238  * Initialises the error tables (DOS 5+)
239  */
240 static void DOSMEM_InitErrorTable()
241 {
242         DWORD           x;
243         char            *call;
244
245         /* We will use a snippet of real mode code that calls */
246         /* a WINE-only interrupt to handle moving the requested */
247         /* message into the buffer... */
248
249         /* FIXME - There is still something wrong... */
250
251         /* FIXME - Find hex values for opcodes...
252            
253            (On call, AX contains message number
254                      DI contains 'offset' (??)
255             Resturn, ES:DI points to counted string )
256
257            PUSH BX
258            MOV BX, AX
259            MOV AX, (arbitrary subfunction number)
260            INT (WINE-only interrupt)
261            POP BX
262            RET
263
264         */
265            
266         const int       code = 4;       
267         const int       buffer = 80; 
268         const int       SIZE_TO_ALLOCATE = code + buffer;
269
270         /* FIXME - Complete rewrite of the table system to save */
271         /* precious DOS space. Now, we return the 0001:???? as */
272         /* DOS 4+ (??, it seems to be the case in MS 7.10) treats that */
273         /* as a special case and programs will use the alternate */
274         /* interface (a farcall returned with INT 24 (AX = 0x122e, DL = */
275         /* 0x08) which lets us have a smaller memory footprint anyway. */
276  
277         x = GlobalDOSAlloc(SIZE_TO_ALLOCATE);  
278
279         DOSMEM_ErrorCall = MAKELONG(0,(x>>16));
280         DOSMEM_ErrorBuffer = DOSMEM_ErrorCall + code;
281
282         call = DOSMEM_MapRealToLinear(DOSMEM_ErrorCall);
283
284         memset(call, 0, SIZE_TO_ALLOCATE);
285
286         /* Fixme - Copy assembly into buffer here */        
287 }
288
289 /***********************************************************************
290  *           DOSMEM_InitMemory
291  *
292  * Initialises the DOS memory structures.
293  */
294 static void DOSMEM_InitMemory(HMODULE16 hModule)
295 {
296    /* Low 64Kb are reserved for DOS/BIOS so the useable area starts at
297     * 1000:0000 and ends at 9FFF:FFEF. */
298
299     dosmem_info*        info_block = DOSMEM_InfoBlock(hModule);
300     dosmem_entry*       root_block = DOSMEM_RootBlock(hModule);
301     dosmem_entry*       dm;
302
303     root_block->size = DOSMEM_MemoryTop(hModule) - (((char*)root_block) + sizeof(dosmem_entry));
304
305     info_block->blocks = 0;
306     info_block->free = root_block->size;
307
308     dm = NEXT_BLOCK(root_block);
309     dm->size = DM_BLOCK_TERMINAL;
310     root_block->size |= DM_BLOCK_FREE 
311 #ifdef __DOSMEM_DEBUG__
312                      | DM_BLOCK_DEBUG;
313 #endif
314                      ;
315 }
316
317 /***********************************************************************
318  *           DOSMEM_Init
319  *
320  * Create the dos memory segments, and store them into the KERNEL
321  * exported values.
322  */
323 BOOL32 DOSMEM_Init(HMODULE16 hModule)
324 {
325     if (!hModule)
326     {
327         /* Allocate 1 MB dosmemory 
328          * - it is mostly wasted but we use can some of it to 
329          *   store internal translation tables, etc...
330          */
331         DOSMEM_dosmem = VirtualAlloc( NULL, 0x100000, MEM_COMMIT,
332                                       PAGE_EXECUTE_READWRITE );
333         if (!DOSMEM_dosmem)
334         {
335             WARN(dosmem, "Could not allocate DOS memory.\n" );
336             return FALSE;
337         }
338         DOSMEM_BiosSeg = GLOBAL_CreateBlock(GMEM_FIXED,DOSMEM_dosmem+0x400,0x100,
339                                         0, FALSE, FALSE, FALSE, NULL );
340         DOSMEM_FillIsrTable(0);
341         DOSMEM_FillBiosSegment();
342         DOSMEM_InitMemory(0);
343         DOSMEM_InitCollateTable();
344         DOSMEM_InitErrorTable();
345     }
346     else
347     {
348 #if 0
349         DOSMEM_FillIsrTable(hModule);
350         DOSMEM_InitMemory(hModule);
351 #else
352         /* bootstrap the new V86 task with a copy of the "system" memory */
353         memcpy(DOSMEM_MemoryBase(hModule), DOSMEM_dosmem, 0x100000);
354 #endif
355     }
356     return TRUE;
357 }
358
359
360 /***********************************************************************
361  *           DOSMEM_Tick
362  *
363  * Increment the BIOS tick counter. Called by timer signal handler.
364  */
365 void DOSMEM_Tick(void)
366 {
367     if (pBiosData) pBiosData->Ticks++;
368 }
369
370 /***********************************************************************
371  *           DOSMEM_GetBlock
372  *
373  * Carve a chunk of the DOS memory block (without selector).
374  */
375 LPVOID DOSMEM_GetBlock(HMODULE16 hModule, UINT32 size, UINT16* pseg)
376 {
377    UINT32        blocksize;
378    char         *block = NULL;
379    dosmem_info  *info_block = DOSMEM_InfoBlock(hModule);
380    dosmem_entry *dm;
381 #ifdef __DOSMEM_DEBUG_
382    dosmem_entry *prev = NULL;
383 #endif
384  
385    if( size > info_block->free ) return NULL;
386    dm = DOSMEM_RootBlock(hModule);
387
388    while (dm && dm->size != DM_BLOCK_TERMINAL)
389    {
390 #ifdef __DOSMEM_DEBUG__
391        if( (dm->size & DM_BLOCK_DEBUG) != DM_BLOCK_DEBUG )
392        {
393             WARN(dosmem,"MCB overrun! [prev = 0x%08x]\n", 4 + (UINT32)prev);
394             return NULL;
395        }
396        prev = dm;
397 #endif
398        if( dm->size & DM_BLOCK_FREE )
399        {
400            dosmem_entry  *next = NEXT_BLOCK(dm);
401
402            while( next->size & DM_BLOCK_FREE ) /* collapse free blocks */
403            {
404                dm->size += sizeof(dosmem_entry) + (next->size & DM_BLOCK_MASK);
405                next->size = (DM_BLOCK_FREE | DM_BLOCK_TERMINAL);
406                next = NEXT_BLOCK(dm);
407            }
408
409            blocksize = dm->size & DM_BLOCK_MASK;
410            if( blocksize >= size )
411            {
412                block = ((char*)dm) + sizeof(dosmem_entry);
413                if( blocksize - size > 0x20 )
414                {
415                    /* split dm so that the next one stays
416                     * paragraph-aligned (and dm loses free bit) */
417
418                    dm->size = (((size + 0xf + sizeof(dosmem_entry)) & ~0xf) -
419                                               sizeof(dosmem_entry));
420                    next = (dosmem_entry*)(((char*)dm) + 
421                            sizeof(dosmem_entry) + dm->size);
422                    next->size = (blocksize - (dm->size + 
423                            sizeof(dosmem_entry))) | DM_BLOCK_FREE 
424 #ifdef __DOSMEM_DEBUG__
425                                                   | DM_BLOCK_DEBUG
426 #endif
427                                                   ;
428                } else dm->size &= DM_BLOCK_MASK;
429
430                info_block->blocks++;
431                info_block->free -= dm->size;
432                if( pseg ) *pseg = (block - DOSMEM_MemoryBase(hModule)) >> 4;
433 #ifdef __DOSMEM_DEBUG__
434                dm->size |= DM_BLOCK_DEBUG;
435 #endif
436                break;
437            }
438            dm = next;
439        }
440        else dm = NEXT_BLOCK(dm);
441    }
442    return (LPVOID)block;
443 }
444
445 /***********************************************************************
446  *           DOSMEM_FreeBlock
447  */
448 BOOL32 DOSMEM_FreeBlock(HMODULE16 hModule, void* ptr)
449 {
450    dosmem_info  *info_block = DOSMEM_InfoBlock(hModule);
451
452    if( ptr >= (void*)(((char*)DOSMEM_RootBlock(hModule)) + sizeof(dosmem_entry)) &&
453        ptr < (void*)DOSMEM_MemoryTop(hModule) && !((((char*)ptr)
454                   - DOSMEM_MemoryBase(hModule)) & 0xf) )
455    {
456        dosmem_entry  *dm = (dosmem_entry*)(((char*)ptr) - sizeof(dosmem_entry));
457
458        if( !(dm->size & (DM_BLOCK_FREE | DM_BLOCK_TERMINAL))
459 #ifdef __DOSMEM_DEBUG__
460          && ((dm->size & DM_BLOCK_DEBUG) == DM_BLOCK_DEBUG )
461 #endif
462          )
463        {
464              info_block->blocks--;
465              info_block->free += dm->size;
466
467              dm->size |= DM_BLOCK_FREE;
468              return TRUE;
469        }
470    }
471    return FALSE;
472 }
473
474 /***********************************************************************
475  *           DOSMEM_ResizeBlock
476  */
477 LPVOID DOSMEM_ResizeBlock(HMODULE16 hModule, void* ptr, UINT32 size, UINT16* pseg)
478 {
479    char         *block = NULL;
480    dosmem_info  *info_block = DOSMEM_InfoBlock(hModule);
481
482    if( ptr >= (void*)(((char*)DOSMEM_RootBlock(hModule)) + sizeof(dosmem_entry)) &&
483        ptr < (void*)DOSMEM_MemoryTop(hModule) && !((((char*)ptr)
484                   - DOSMEM_MemoryBase(hModule)) & 0xf) )
485    {
486        dosmem_entry  *dm = (dosmem_entry*)(((char*)ptr) - sizeof(dosmem_entry));
487
488        if( pseg ) *pseg = ((char*)ptr - DOSMEM_MemoryBase(hModule)) >> 4;
489
490        if( !(dm->size & (DM_BLOCK_FREE | DM_BLOCK_TERMINAL))
491          )
492        {
493              dosmem_entry  *next = NEXT_BLOCK(dm);
494              UINT32 blocksize, orgsize = dm->size & DM_BLOCK_MASK;
495
496              while( next->size & DM_BLOCK_FREE ) /* collapse free blocks */
497              {
498                  dm->size += sizeof(dosmem_entry) + (next->size & DM_BLOCK_MASK);
499                  next->size = (DM_BLOCK_FREE | DM_BLOCK_TERMINAL);
500                  next = NEXT_BLOCK(dm);
501              }
502
503              blocksize = dm->size & DM_BLOCK_MASK;
504              if (blocksize >= size)
505              {
506                  block = ((char*)dm) + sizeof(dosmem_entry);
507                  if( blocksize - size > 0x20 )
508                  {
509                      /* split dm so that the next one stays
510                       * paragraph-aligned (and next gains free bit) */
511
512                      dm->size = (((size + 0xf + sizeof(dosmem_entry)) & ~0xf) -
513                                                 sizeof(dosmem_entry));
514                      next = (dosmem_entry*)(((char*)dm) + 
515                              sizeof(dosmem_entry) + dm->size);
516                      next->size = (blocksize - (dm->size + 
517                              sizeof(dosmem_entry))) | DM_BLOCK_FREE 
518                                                     ;
519                  } else dm->size &= DM_BLOCK_MASK;
520
521                  info_block->free += orgsize - dm->size;
522              } else {
523                  block = DOSMEM_GetBlock(hModule, size, pseg);
524                  if (block) {
525                      info_block->blocks--;
526                      info_block->free += dm->size;
527
528                      dm->size |= DM_BLOCK_FREE;
529                  }
530              }
531        }
532    }
533    return (LPVOID)block;
534 }
535
536
537 /***********************************************************************
538  *           DOSMEM_Available
539  */
540 UINT32 DOSMEM_Available(HMODULE16 hModule)
541 {
542    UINT32        blocksize, available = 0;
543    dosmem_entry *dm;
544    
545    dm = DOSMEM_RootBlock(hModule);
546
547    while (dm && dm->size != DM_BLOCK_TERMINAL)
548    {
549 #ifdef __DOSMEM_DEBUG__
550        if( (dm->size & DM_BLOCK_DEBUG) != DM_BLOCK_DEBUG )
551        {
552             WARN(dosmem,"MCB overrun! [prev = 0x%08x]\n", 4 + (UINT32)prev);
553             return NULL;
554        }
555        prev = dm;
556 #endif
557        if( dm->size & DM_BLOCK_FREE )
558        {
559            dosmem_entry  *next = NEXT_BLOCK(dm);
560
561            while( next->size & DM_BLOCK_FREE ) /* collapse free blocks */
562            {
563                dm->size += sizeof(dosmem_entry) + (next->size & DM_BLOCK_MASK);
564                next->size = (DM_BLOCK_FREE | DM_BLOCK_TERMINAL);
565                next = NEXT_BLOCK(dm);
566            }
567
568            blocksize = dm->size & DM_BLOCK_MASK;
569            if ( blocksize > available ) available = blocksize;
570            dm = next;
571        }
572        else dm = NEXT_BLOCK(dm);
573    }
574    return available;
575 }
576
577
578 /***********************************************************************
579  *           DOSMEM_MapLinearToDos
580  *
581  * Linear address to the DOS address space.
582  */
583 UINT32 DOSMEM_MapLinearToDos(LPVOID ptr)
584 {
585     if (((char*)ptr >= DOSMEM_MemoryBase(0)) &&
586         ((char*)ptr < DOSMEM_MemoryBase(0) + 0x100000))
587           return (UINT32)ptr - (UINT32)DOSMEM_MemoryBase(0);
588     return (UINT32)ptr;
589 }
590
591
592 /***********************************************************************
593  *           DOSMEM_MapDosToLinear
594  *
595  * DOS linear address to the linear address space.
596  */
597 LPVOID DOSMEM_MapDosToLinear(UINT32 ptr)
598 {
599     if (ptr < 0x100000) return (LPVOID)(ptr + (UINT32)DOSMEM_MemoryBase(0));
600     return (LPVOID)ptr;
601 }
602
603
604 /***********************************************************************
605  *           DOSMEM_MapRealToLinear
606  *
607  * Real mode DOS address into a linear pointer
608  */
609 LPVOID DOSMEM_MapRealToLinear(DWORD x)
610 {
611    LPVOID       lin;
612
613    lin=DOSMEM_MemoryBase(0)+(x&0xffff)+(((x&0xffff0000)>>16)*16);
614    TRACE(selector,"(0x%08lx) returns 0x%p.\n",
615                     x,lin );
616    return lin;
617 }
618
619 /***********************************************************************
620  *           DOSMEM_AllocSelector
621  *
622  * Allocates a protected mode selector for a realmode segment.
623  */
624 WORD DOSMEM_AllocSelector(WORD realsel)
625 {
626         HMODULE16 hModule = GetModuleHandle16("KERNEL");
627         WORD    sel;
628
629         sel=GLOBAL_CreateBlock(
630                 GMEM_FIXED,DOSMEM_dosmem+realsel*16,0x10000,
631                 hModule,FALSE,FALSE,FALSE,NULL
632         );
633         TRACE(selector,"(0x%04x) returns 0x%04x.\n",
634                 realsel,sel
635         );
636         return sel;
637 }
638