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