Fixed a couple of dosmem block resize bugs.
[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 "winbase.h"
12 #include "wine/winbase16.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     BYTE  RowsOnScreenMinus1;        /* 84: EGA only */
76     WORD  BytesPerChar;              /* 85: EGA only */
77     BYTE  ModeOptions;               /* 87: EGA only */
78     BYTE  FeatureBitsSwitches;       /* 88: EGA only */
79     BYTE  unknown;                   /* 89: ??? */
80     BYTE  DisplayCombination;        /* 8A: VGA display combinations */
81     BYTE  DiskDataRate;              /* 8B: Last disk data rate selected */
82 } BIOSDATA;
83
84 #pragma pack(4)
85
86 static BIOSDATA *pBiosData = NULL;
87 static char     *DOSMEM_dosmem;
88
89        DWORD     DOSMEM_CollateTable;
90
91        DWORD     DOSMEM_ErrorCall;
92        DWORD     DOSMEM_ErrorBuffer;
93
94 /* use 2 low bits of 'size' for the housekeeping */
95
96 #define DM_BLOCK_DEBUG          0xABE00000
97 #define DM_BLOCK_TERMINAL       0x00000001
98 #define DM_BLOCK_FREE           0x00000002
99 #define DM_BLOCK_MASK           0x001FFFFC
100
101 /*
102 #define __DOSMEM_DEBUG__
103  */
104
105 typedef struct {
106    unsigned     size;
107 } dosmem_entry;
108
109 typedef struct {
110   unsigned      blocks;
111   unsigned      free;
112 } dosmem_info;
113
114 #define NEXT_BLOCK(block) \
115         (dosmem_entry*)(((char*)(block)) + \
116          sizeof(dosmem_entry) + ((block)->size & DM_BLOCK_MASK))
117
118 #define VM_STUB(x) (0x90CF00CD|(x<<8)) /* INT x; IRET; NOP */
119 #define VM_STUB_SEGMENT 0xf000         /* BIOS segment */
120
121 /***********************************************************************
122  *           DOSMEM_MemoryBase
123  *
124  * Gets the DOS memory base.
125  */
126 char *DOSMEM_MemoryBase(HMODULE16 hModule)
127 {
128     TDB *pTask = hModule ? NULL : (TDB *)GlobalLock16( GetCurrentTask() );
129     NE_MODULE *pModule = (hModule || pTask) ? NE_GetPtr( hModule ? hModule : pTask->hModule ) : NULL;
130
131     GlobalUnlock16( GetCurrentTask() );
132     if (pModule && pModule->dos_image)
133         return pModule->dos_image;
134     else
135         return DOSMEM_dosmem;
136 }
137
138 /***********************************************************************
139  *           DOSMEM_MemoryTop
140  *
141  * Gets the DOS memory top.
142  */
143 static char *DOSMEM_MemoryTop(HMODULE16 hModule)
144 {
145     return DOSMEM_MemoryBase(hModule)+0x9FFFC; /* 640K */
146 }
147
148 /***********************************************************************
149  *           DOSMEM_InfoBlock
150  *
151  * Gets the DOS memory info block.
152  */
153 static dosmem_info *DOSMEM_InfoBlock(HMODULE16 hModule)
154 {
155     return (dosmem_info*)(DOSMEM_MemoryBase(hModule)+0x10000); /* 64K */
156 }
157
158 /***********************************************************************
159  *           DOSMEM_RootBlock
160  *
161  * Gets the DOS memory root block.
162  */
163 static dosmem_entry *DOSMEM_RootBlock(HMODULE16 hModule)
164 {
165     /* first block has to be paragraph-aligned */
166     return (dosmem_entry*)(((char*)DOSMEM_InfoBlock(hModule)) +
167                            ((((sizeof(dosmem_info) + 0xf) & ~0xf) - sizeof(dosmem_entry))));
168 }
169
170 /***********************************************************************
171  *           DOSMEM_FillIsrTable
172  *
173  * Fill the interrupt table with fake BIOS calls to BIOSSEG (0xf000).
174  *
175  * NOTES:
176  * Linux normally only traps INTs performed from or destined to BIOSSEG
177  * for us to handle, if the int_revectored table is empty. Filling the
178  * interrupt table with calls to INT stubs in BIOSSEG allows DOS programs
179  * to hook interrupts, as well as use their familiar retf tricks to call
180  * them, AND let Wine handle any unhooked interrupts transparently.
181  */
182 static void DOSMEM_FillIsrTable(HMODULE16 hModule)
183 {
184     SEGPTR *isr = (SEGPTR*)DOSMEM_MemoryBase(hModule);
185     DWORD *stub = (DWORD*)((char*)isr + (VM_STUB_SEGMENT << 4));
186     int x;
187  
188     for (x=0; x<256; x++) isr[x]=PTR_SEG_OFF_TO_SEGPTR(VM_STUB_SEGMENT,x*4);
189     for (x=0; x<256; x++) stub[x]=VM_STUB(x);
190
191
192 /***********************************************************************
193  *           DOSMEM_InitDPMI
194  *
195  * Allocate the global DPMI RMCB wrapper.
196  */
197 static void DOSMEM_InitDPMI(void)
198 {
199     extern UINT16 DPMI_wrap_seg;
200     static char wrap_code[]={
201      0xCD,0x31, /* int $0x31 */
202      0xCB       /* lret */
203     };
204     LPSTR wrapper = (LPSTR)DOSMEM_GetBlock(0, sizeof(wrap_code), &DPMI_wrap_seg);
205
206     memcpy(wrapper, wrap_code, sizeof(wrap_code));
207 }
208
209 /***********************************************************************
210  *           DOSMEM_FillBiosSegments
211  *
212  * Fill the BIOS data segment with dummy values.
213  */
214 static void DOSMEM_FillBiosSegments(void)
215 {
216     BYTE *pBiosSys = (BYTE *)GlobalLock16( DOSMEM_BiosSysSeg );
217     BYTE *pBiosROMTable = pBiosSys+0xe6f5;
218
219     pBiosData = (BIOSDATA *)GlobalLock16( DOSMEM_BiosDataSeg );
220
221       /* Clear all unused values */
222     memset( pBiosData, 0, sizeof(*pBiosData) );
223
224     /* FIXME: should check the number of configured drives and ports */
225
226     pBiosData->Com1Addr             = 0x3f8;
227     pBiosData->Com2Addr             = 0x2f8;
228     pBiosData->Lpt1Addr             = 0x378;
229     pBiosData->Lpt2Addr             = 0x278;
230     pBiosData->InstalledHardware    = 0x5463;
231     pBiosData->MemSize              = 640;
232     pBiosData->NextKbdCharPtr       = 0x1e;
233     pBiosData->FirstKbdCharPtr      = 0x1e;
234     pBiosData->VideoMode            = 3;
235     pBiosData->VideoColumns         = 80;
236     pBiosData->VideoPageSize        = 80 * 25 * 2;
237     pBiosData->VideoPageStartAddr   = 0xb800;
238     pBiosData->VideoCtrlAddr        = 0x3d4;
239     pBiosData->Ticks                = INT1A_GetTicksSinceMidnight();
240     pBiosData->NbHardDisks          = 2;
241     pBiosData->KbdBufferStart       = 0x1e;
242     pBiosData->KbdBufferEnd         = 0x3e;
243     pBiosData->RowsOnScreenMinus1   = 23;
244     pBiosData->BytesPerChar         = 0x10;
245     pBiosData->ModeOptions          = 0x64;
246     pBiosData->FeatureBitsSwitches  = 0xf9;
247     pBiosData->unknown              = 0x51;
248     pBiosData->DisplayCombination   = 0x08;
249     pBiosData->DiskDataRate         = 0;
250
251     /* fill ROM configuration table (values from Award) */
252     *(WORD *)(pBiosROMTable)= 0x08; /* number of bytes following */
253     *(pBiosROMTable+0x2)        = 0xfc; /* model */
254     *(pBiosROMTable+0x3)        = 0x01; /* submodel */
255     *(pBiosROMTable+0x4)        = 0x00; /* BIOS revision */
256     *(pBiosROMTable+0x5)        = 0x74; /* feature byte 1 */
257     *(pBiosROMTable+0x6)        = 0x00; /* feature byte 2 */
258     *(pBiosROMTable+0x7)        = 0x00; /* feature byte 3 */
259     *(pBiosROMTable+0x8)        = 0x00; /* feature byte 4 */
260     *(pBiosROMTable+0x9)        = 0x00; /* feature byte 5 */
261
262     /* BIOS date string */
263     strcpy((char *)pBiosSys+0xfff5, "13/01/99");
264
265     /* BIOS ID */
266     *(pBiosSys+0xfffe) = 0xfc;
267 }
268
269 /***********************************************************************
270  *           DOSMEM_InitCollateTable
271  *
272  * Initialises the collate table (character sorting, language dependent)
273  */
274 static void DOSMEM_InitCollateTable()
275 {
276         DWORD           x;
277         unsigned char   *tbl;
278         int             i;
279
280         x = GlobalDOSAlloc16(258);
281         DOSMEM_CollateTable = MAKELONG(0,(x>>16));
282         tbl = DOSMEM_MapRealToLinear(DOSMEM_CollateTable);
283         *(WORD*)tbl     = 0x100;
284         tbl += 2;
285         for ( i = 0; i < 0x100; i++) *tbl++ = i;
286 }
287
288 /***********************************************************************
289  *           DOSMEM_InitErrorTable
290  *
291  * Initialises the error tables (DOS 5+)
292  */
293 static void DOSMEM_InitErrorTable()
294 {
295         DWORD           x;
296         char            *call;
297
298         /* We will use a snippet of real mode code that calls */
299         /* a WINE-only interrupt to handle moving the requested */
300         /* message into the buffer... */
301
302         /* FIXME - There is still something wrong... */
303
304         /* FIXME - Find hex values for opcodes...
305            
306            (On call, AX contains message number
307                      DI contains 'offset' (??)
308             Resturn, ES:DI points to counted string )
309
310            PUSH BX
311            MOV BX, AX
312            MOV AX, (arbitrary subfunction number)
313            INT (WINE-only interrupt)
314            POP BX
315            RET
316
317         */
318            
319         const int       code = 4;       
320         const int       buffer = 80; 
321         const int       SIZE_TO_ALLOCATE = code + buffer;
322
323         /* FIXME - Complete rewrite of the table system to save */
324         /* precious DOS space. Now, we return the 0001:???? as */
325         /* DOS 4+ (??, it seems to be the case in MS 7.10) treats that */
326         /* as a special case and programs will use the alternate */
327         /* interface (a farcall returned with INT 24 (AX = 0x122e, DL = */
328         /* 0x08) which lets us have a smaller memory footprint anyway. */
329  
330         x = GlobalDOSAlloc16(SIZE_TO_ALLOCATE);  
331
332         DOSMEM_ErrorCall = MAKELONG(0,(x>>16));
333         DOSMEM_ErrorBuffer = DOSMEM_ErrorCall + code;
334
335         call = DOSMEM_MapRealToLinear(DOSMEM_ErrorCall);
336
337         memset(call, 0, SIZE_TO_ALLOCATE);
338
339         /* Fixme - Copy assembly into buffer here */        
340 }
341
342 /***********************************************************************
343  *           DOSMEM_InitMemory
344  *
345  * Initialises the DOS memory structures.
346  */
347 static void DOSMEM_InitMemory(HMODULE16 hModule)
348 {
349    /* Low 64Kb are reserved for DOS/BIOS so the useable area starts at
350     * 1000:0000 and ends at 9FFF:FFEF. */
351
352     dosmem_info*        info_block = DOSMEM_InfoBlock(hModule);
353     dosmem_entry*       root_block = DOSMEM_RootBlock(hModule);
354     dosmem_entry*       dm;
355
356     root_block->size = DOSMEM_MemoryTop(hModule) - (((char*)root_block) + sizeof(dosmem_entry));
357
358     info_block->blocks = 0;
359     info_block->free = root_block->size;
360
361     dm = NEXT_BLOCK(root_block);
362     dm->size = DM_BLOCK_TERMINAL;
363     root_block->size |= DM_BLOCK_FREE 
364 #ifdef __DOSMEM_DEBUG__
365                      | DM_BLOCK_DEBUG;
366 #endif
367                      ;
368 }
369
370 /***********************************************************************
371  *           DOSMEM_Init
372  *
373  * Create the dos memory segments, and store them into the KERNEL
374  * exported values.
375  */
376 BOOL DOSMEM_Init(HMODULE16 hModule)
377 {
378     if (!hModule)
379     {
380         /* Allocate 1 MB dosmemory 
381          * - it is mostly wasted but we can use some of it to 
382          *   store internal translation tables, etc...
383          */
384         DOSMEM_dosmem = VirtualAlloc( NULL, 0x100000, MEM_COMMIT,
385                                       PAGE_EXECUTE_READWRITE );
386         if (!DOSMEM_dosmem)
387         {
388             WARN(dosmem, "Could not allocate DOS memory.\n" );
389             return FALSE;
390         }
391         DOSMEM_BiosDataSeg = GLOBAL_CreateBlock(GMEM_FIXED,DOSMEM_dosmem+0x400,
392                                      0x100, 0, FALSE, FALSE, FALSE, NULL );
393         DOSMEM_BiosSysSeg = GLOBAL_CreateBlock(GMEM_FIXED,DOSMEM_dosmem+0xf0000,
394                                      0x10000, 0, FALSE, FALSE, FALSE, NULL );
395         DOSMEM_FillIsrTable(0);
396         DOSMEM_FillBiosSegments();
397         DOSMEM_InitMemory(0);
398         DOSMEM_InitCollateTable();
399         DOSMEM_InitErrorTable();
400         DOSMEM_InitDPMI();
401     }
402     else
403     {
404 #if 0
405         DOSMEM_FillIsrTable(hModule);
406         DOSMEM_InitMemory(hModule);
407 #else
408         /* bootstrap the new V86 task with a copy of the "system" memory */
409         memcpy(DOSMEM_MemoryBase(hModule), DOSMEM_dosmem, 0x100000);
410 #endif
411     }
412     return TRUE;
413 }
414
415
416 /***********************************************************************
417  *           DOSMEM_Tick
418  *
419  * Increment the BIOS tick counter. Called by timer signal handler.
420  */
421 void DOSMEM_Tick( WORD timer )
422 {
423     if (pBiosData) pBiosData->Ticks++;
424 }
425
426 /***********************************************************************
427  *           DOSMEM_GetBlock
428  *
429  * Carve a chunk of the DOS memory block (without selector).
430  */
431 LPVOID DOSMEM_GetBlock(HMODULE16 hModule, UINT size, UINT16* pseg)
432 {
433    UINT          blocksize;
434    char         *block = NULL;
435    dosmem_info  *info_block = DOSMEM_InfoBlock(hModule);
436    dosmem_entry *dm;
437 #ifdef __DOSMEM_DEBUG_
438    dosmem_entry *prev = NULL;
439 #endif
440  
441    if( size > info_block->free ) return NULL;
442    dm = DOSMEM_RootBlock(hModule);
443
444    while (dm && dm->size != DM_BLOCK_TERMINAL)
445    {
446 #ifdef __DOSMEM_DEBUG__
447        if( (dm->size & DM_BLOCK_DEBUG) != DM_BLOCK_DEBUG )
448        {
449             WARN(dosmem,"MCB overrun! [prev = 0x%08x]\n", 4 + (UINT)prev);
450             return NULL;
451        }
452        prev = dm;
453 #endif
454        if( dm->size & DM_BLOCK_FREE )
455        {
456            dosmem_entry  *next = NEXT_BLOCK(dm);
457
458            while( next->size & DM_BLOCK_FREE ) /* collapse free blocks */
459            {
460                dm->size += sizeof(dosmem_entry) + (next->size & DM_BLOCK_MASK);
461                next->size = (DM_BLOCK_FREE | DM_BLOCK_TERMINAL);
462                next = NEXT_BLOCK(dm);
463            }
464
465            blocksize = dm->size & DM_BLOCK_MASK;
466            if( blocksize >= size )
467            {
468                block = ((char*)dm) + sizeof(dosmem_entry);
469                if( blocksize - size > 0x20 )
470                {
471                    /* split dm so that the next one stays
472                     * paragraph-aligned (and dm loses free bit) */
473
474                    dm->size = (((size + 0xf + sizeof(dosmem_entry)) & ~0xf) -
475                                               sizeof(dosmem_entry));
476                    next = (dosmem_entry*)(((char*)dm) + 
477                            sizeof(dosmem_entry) + dm->size);
478                    next->size = (blocksize - (dm->size + 
479                            sizeof(dosmem_entry))) | DM_BLOCK_FREE 
480 #ifdef __DOSMEM_DEBUG__
481                                                   | DM_BLOCK_DEBUG
482 #endif
483                                                   ;
484                } else dm->size &= DM_BLOCK_MASK;
485
486                info_block->blocks++;
487                info_block->free -= dm->size;
488                if( pseg ) *pseg = (block - DOSMEM_MemoryBase(hModule)) >> 4;
489 #ifdef __DOSMEM_DEBUG__
490                dm->size |= DM_BLOCK_DEBUG;
491 #endif
492                break;
493            }
494            dm = next;
495        }
496        else dm = NEXT_BLOCK(dm);
497    }
498    return (LPVOID)block;
499 }
500
501 /***********************************************************************
502  *           DOSMEM_FreeBlock
503  */
504 BOOL DOSMEM_FreeBlock(HMODULE16 hModule, void* ptr)
505 {
506    dosmem_info  *info_block = DOSMEM_InfoBlock(hModule);
507
508    if( ptr >= (void*)(((char*)DOSMEM_RootBlock(hModule)) + sizeof(dosmem_entry)) &&
509        ptr < (void*)DOSMEM_MemoryTop(hModule) && !((((char*)ptr)
510                   - DOSMEM_MemoryBase(hModule)) & 0xf) )
511    {
512        dosmem_entry  *dm = (dosmem_entry*)(((char*)ptr) - sizeof(dosmem_entry));
513
514        if( !(dm->size & (DM_BLOCK_FREE | DM_BLOCK_TERMINAL))
515 #ifdef __DOSMEM_DEBUG__
516          && ((dm->size & DM_BLOCK_DEBUG) == DM_BLOCK_DEBUG )
517 #endif
518          )
519        {
520              info_block->blocks--;
521              info_block->free += dm->size;
522
523              dm->size |= DM_BLOCK_FREE;
524              return TRUE;
525        }
526    }
527    return FALSE;
528 }
529
530 /***********************************************************************
531  *           DOSMEM_ResizeBlock
532  */
533 LPVOID DOSMEM_ResizeBlock(HMODULE16 hModule, void* ptr, UINT size, UINT16* pseg)
534 {
535    char         *block = NULL;
536    dosmem_info  *info_block = DOSMEM_InfoBlock(hModule);
537
538    if( ptr >= (void*)(((char*)DOSMEM_RootBlock(hModule)) + sizeof(dosmem_entry)) &&
539        ptr < (void*)DOSMEM_MemoryTop(hModule) && !((((char*)ptr)
540                   - DOSMEM_MemoryBase(hModule)) & 0xf) )
541    {
542        dosmem_entry  *dm = (dosmem_entry*)(((char*)ptr) - sizeof(dosmem_entry));
543
544        if( pseg ) *pseg = ((char*)ptr - DOSMEM_MemoryBase(hModule)) >> 4;
545
546        if( !(dm->size & (DM_BLOCK_FREE | DM_BLOCK_TERMINAL))
547          )
548        {
549              dosmem_entry  *next = NEXT_BLOCK(dm);
550              UINT blocksize, orgsize = dm->size & DM_BLOCK_MASK;
551
552              while( next->size & DM_BLOCK_FREE ) /* collapse free blocks */
553              {
554                  dm->size += sizeof(dosmem_entry) + (next->size & DM_BLOCK_MASK);
555                  next->size = (DM_BLOCK_FREE | DM_BLOCK_TERMINAL);
556                  next = NEXT_BLOCK(dm);
557              }
558
559              blocksize = dm->size & DM_BLOCK_MASK;
560              if (blocksize >= size)
561              {
562                  block = ((char*)dm) + sizeof(dosmem_entry);
563                  if( blocksize - size > 0x20 )
564                  {
565                      /* split dm so that the next one stays
566                       * paragraph-aligned (and next gains free bit) */
567
568                      dm->size = (((size + 0xf + sizeof(dosmem_entry)) & ~0xf) -
569                                                 sizeof(dosmem_entry));
570                      next = (dosmem_entry*)(((char*)dm) + 
571                              sizeof(dosmem_entry) + dm->size);
572                      next->size = (blocksize - (dm->size + 
573                              sizeof(dosmem_entry))) | DM_BLOCK_FREE 
574                                                     ;
575                  } else dm->size &= DM_BLOCK_MASK;
576
577                  info_block->free += orgsize - dm->size;
578              } else {
579                  /* the collapse didn't help, try getting a new block */
580                  block = DOSMEM_GetBlock(hModule, size, pseg);
581                  if (block) {
582                      /* we got one, copy the old data there (we do need to, right?) */
583                      memcpy(block, ((char*)dm) + sizeof(dosmem_entry),
584                                    (size<orgsize) ? size : orgsize);
585                      /* free old block */
586                      info_block->blocks--;
587                      info_block->free += dm->size;
588
589                      dm->size |= DM_BLOCK_FREE;
590                  } else {
591                      /* and Bill Gates said 640K should be enough for everyone... */
592
593                      /* need to split original and collapsed blocks apart again,
594                       * and free the collapsed blocks again, before exiting */
595                      if( blocksize - orgsize > 0x20 )
596                      {
597                          /* split dm so that the next one stays
598                           * paragraph-aligned (and next gains free bit) */
599
600                          dm->size = (((orgsize + 0xf + sizeof(dosmem_entry)) & ~0xf) -
601                                                        sizeof(dosmem_entry));
602                          next = (dosmem_entry*)(((char*)dm) + 
603                                  sizeof(dosmem_entry) + dm->size);
604                          next->size = (blocksize - (dm->size + 
605                                  sizeof(dosmem_entry))) | DM_BLOCK_FREE 
606                                                         ;
607                      } else dm->size &= DM_BLOCK_MASK;
608                  }
609              }
610        }
611    }
612    return (LPVOID)block;
613 }
614
615
616 /***********************************************************************
617  *           DOSMEM_Available
618  */
619 UINT DOSMEM_Available(HMODULE16 hModule)
620 {
621    UINT          blocksize, available = 0;
622    dosmem_entry *dm;
623    
624    dm = DOSMEM_RootBlock(hModule);
625
626    while (dm && dm->size != DM_BLOCK_TERMINAL)
627    {
628 #ifdef __DOSMEM_DEBUG__
629        if( (dm->size & DM_BLOCK_DEBUG) != DM_BLOCK_DEBUG )
630        {
631             WARN(dosmem,"MCB overrun! [prev = 0x%08x]\n", 4 + (UINT)prev);
632             return NULL;
633        }
634        prev = dm;
635 #endif
636        if( dm->size & DM_BLOCK_FREE )
637        {
638            dosmem_entry  *next = NEXT_BLOCK(dm);
639
640            while( next->size & DM_BLOCK_FREE ) /* collapse free blocks */
641            {
642                dm->size += sizeof(dosmem_entry) + (next->size & DM_BLOCK_MASK);
643                next->size = (DM_BLOCK_FREE | DM_BLOCK_TERMINAL);
644                next = NEXT_BLOCK(dm);
645            }
646
647            blocksize = dm->size & DM_BLOCK_MASK;
648            if ( blocksize > available ) available = blocksize;
649            dm = next;
650        }
651        else dm = NEXT_BLOCK(dm);
652    }
653    return available;
654 }
655
656
657 /***********************************************************************
658  *           DOSMEM_MapLinearToDos
659  *
660  * Linear address to the DOS address space.
661  */
662 UINT DOSMEM_MapLinearToDos(LPVOID ptr)
663 {
664     if (((char*)ptr >= DOSMEM_MemoryBase(0)) &&
665         ((char*)ptr < DOSMEM_MemoryBase(0) + 0x100000))
666           return (UINT)ptr - (UINT)DOSMEM_MemoryBase(0);
667     return (UINT)ptr;
668 }
669
670
671 /***********************************************************************
672  *           DOSMEM_MapDosToLinear
673  *
674  * DOS linear address to the linear address space.
675  */
676 LPVOID DOSMEM_MapDosToLinear(UINT ptr)
677 {
678     if (ptr < 0x100000) return (LPVOID)(ptr + (UINT)DOSMEM_MemoryBase(0));
679     return (LPVOID)ptr;
680 }
681
682
683 /***********************************************************************
684  *           DOSMEM_MapRealToLinear
685  *
686  * Real mode DOS address into a linear pointer
687  */
688 LPVOID DOSMEM_MapRealToLinear(DWORD x)
689 {
690    LPVOID       lin;
691
692    lin=DOSMEM_MemoryBase(0)+(x&0xffff)+(((x&0xffff0000)>>16)*16);
693    TRACE(selector,"(0x%08lx) returns 0x%p.\n",
694                     x,lin );
695    return lin;
696 }
697
698 /***********************************************************************
699  *           DOSMEM_AllocSelector
700  *
701  * Allocates a protected mode selector for a realmode segment.
702  */
703 WORD DOSMEM_AllocSelector(WORD realsel)
704 {
705         HMODULE16 hModule = GetModuleHandle16("KERNEL");
706         WORD    sel;
707
708         sel=GLOBAL_CreateBlock(
709                 GMEM_FIXED,DOSMEM_dosmem+realsel*16,0x10000,
710                 hModule,FALSE,FALSE,FALSE,NULL
711         );
712         TRACE(selector,"(0x%04x) returns 0x%04x.\n",
713                 realsel,sel
714         );
715         return sel;
716 }
717