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