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