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