4 * Copyright 1995 Alexandre Julliard
5 * Copyright 1996 Marcus Meissner
12 #include "wine/winbase16.h"
18 #include "debugtools.h"
20 DEFAULT_DEBUG_CHANNEL(dosmem)
21 DECLARE_DEBUG_CHANNEL(selector)
23 HANDLE16 DOSMEM_BiosDataSeg; /* BIOS data segment at 0x40:0 */
24 HANDLE16 DOSMEM_BiosSysSeg; /* BIOS ROM segment at 0xf000:0 */
26 static char *DOSMEM_dosmem;
28 DWORD DOSMEM_CollateTable;
30 DWORD DOSMEM_ErrorCall;
31 DWORD DOSMEM_ErrorBuffer;
33 /* use 2 low bits of 'size' for the housekeeping */
35 #define DM_BLOCK_DEBUG 0xABE00000
36 #define DM_BLOCK_TERMINAL 0x00000001
37 #define DM_BLOCK_FREE 0x00000002
38 #define DM_BLOCK_MASK 0x001FFFFC
41 #define __DOSMEM_DEBUG__
53 #define NEXT_BLOCK(block) \
54 (dosmem_entry*)(((char*)(block)) + \
55 sizeof(dosmem_entry) + ((block)->size & DM_BLOCK_MASK))
57 #define VM_STUB(x) (0x90CF00CD|(x<<8)) /* INT x; IRET; NOP */
58 #define VM_STUB_SEGMENT 0xf000 /* BIOS segment */
60 /***********************************************************************
63 * Gets the DOS memory base.
65 char *DOSMEM_MemoryBase(void)
67 LPDOSTASK lpDosTask = MZ_Current();
69 if (lpDosTask && lpDosTask->img)
70 return lpDosTask->img;
75 /***********************************************************************
78 * Gets the DOS memory top.
80 static char *DOSMEM_MemoryTop(void)
82 return DOSMEM_MemoryBase()+0x9FFFC; /* 640K */
85 /***********************************************************************
88 * Gets the DOS memory info block.
90 static dosmem_info *DOSMEM_InfoBlock(void)
92 return (dosmem_info*)(DOSMEM_MemoryBase()+0x10000); /* 64K */
95 /***********************************************************************
98 * Gets the DOS memory root block.
100 static dosmem_entry *DOSMEM_RootBlock(void)
102 /* first block has to be paragraph-aligned */
103 return (dosmem_entry*)(((char*)DOSMEM_InfoBlock()) +
104 ((((sizeof(dosmem_info) + 0xf) & ~0xf) - sizeof(dosmem_entry))));
107 /***********************************************************************
108 * DOSMEM_FillIsrTable
110 * Fill the interrupt table with fake BIOS calls to BIOSSEG (0xf000).
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.
119 static void DOSMEM_FillIsrTable(void)
121 SEGPTR *isr = (SEGPTR*)DOSMEM_MemoryBase();
122 DWORD *stub = (DWORD*)((char*)isr + (VM_STUB_SEGMENT << 4));
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);
129 /***********************************************************************
132 * Allocate the global DPMI RMCB wrapper.
134 static void DOSMEM_InitDPMI(void)
136 extern UINT16 DPMI_wrap_seg;
137 static char wrap_code[]={
138 0xCD,0x31, /* int $0x31 */
141 LPSTR wrapper = (LPSTR)DOSMEM_GetBlock(sizeof(wrap_code), &DPMI_wrap_seg);
143 memcpy(wrapper, wrap_code, sizeof(wrap_code));
146 BIOSDATA * DOSMEM_BiosData()
148 return (BIOSDATA *)(DOSMEM_MemoryBase()+0x400);
151 BYTE * DOSMEM_BiosSys()
153 return DOSMEM_MemoryBase()+0xf0000;
156 struct _DOS_LISTOFLISTS * DOSMEM_LOL()
158 return (struct _DOS_LISTOFLISTS *)DOSMEM_MapRealToLinear
159 (PTR_SEG_OFF_TO_SEGPTR(HIWORD(DOS_LOLSeg),0));
162 /***********************************************************************
163 * DOSMEM_FillBiosSegments
165 * Fill the BIOS data segment with dummy values.
167 static void DOSMEM_FillBiosSegments(void)
169 BYTE *pBiosSys = DOSMEM_BiosSys();
170 BYTE *pBiosROMTable = pBiosSys+0xe6f5;
171 BIOSDATA *pBiosData = DOSMEM_BiosData();
173 /* bogus 0xe0xx addresses !! Adapt int 0x10/0x1b if change needed */
174 VIDEOFUNCTIONALITY *pVidFunc = (VIDEOFUNCTIONALITY *)(pBiosSys+0xe000);
175 VIDEOSTATE *pVidState = (VIDEOSTATE *)(pBiosSys+0xe010);
178 /* Clear all unused values */
179 memset( pBiosData, 0, sizeof(*pBiosData) );
180 memset( pVidFunc, 0, sizeof(*pVidFunc ) );
181 memset( pVidState, 0, sizeof(*pVidState) );
183 /* FIXME: should check the number of configured drives and ports */
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;
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 */
223 for (i = 0; i < 7; i++)
224 pVidFunc->ModeSupport[i] = 0xff;
226 pVidFunc->ScanlineSupport = 7;
227 pVidFunc->NumberCharBlocks = 0;
228 pVidFunc->ActiveCharBlocks = 0;
229 pVidFunc->MiscFlags = 0x8ff;
230 pVidFunc->SavePointerFlags = 0x3f;
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;
238 for (i = 0; i < 8; i++)
239 pVidState->CursorPos[i] = 0;
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;
263 /* BIOS date string */
264 strcpy((char *)pBiosSys+0xfff5, "13/01/99");
267 *(pBiosSys+0xfffe) = 0xfc;
270 /***********************************************************************
271 * DOSMEM_InitCollateTable
273 * Initialises the collate table (character sorting, language dependent)
275 static void DOSMEM_InitCollateTable()
281 x = GlobalDOSAlloc16(258);
282 DOSMEM_CollateTable = MAKELONG(0,(x>>16));
283 tbl = DOSMEM_MapRealToLinear(DOSMEM_CollateTable);
286 for ( i = 0; i < 0x100; i++) *tbl++ = i;
289 /***********************************************************************
290 * DOSMEM_InitErrorTable
292 * Initialises the error tables (DOS 5+)
294 static void DOSMEM_InitErrorTable()
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... */
303 /* FIXME - There is still something wrong... */
305 /* FIXME - Find hex values for opcodes...
307 (On call, AX contains message number
308 DI contains 'offset' (??)
309 Resturn, ES:DI points to counted string )
313 MOV AX, (arbitrary subfunction number)
314 INT (WINE-only interrupt)
321 const int buffer = 80;
322 const int SIZE_TO_ALLOCATE = code + buffer;
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. */
331 x = GlobalDOSAlloc16(SIZE_TO_ALLOCATE);
333 DOSMEM_ErrorCall = MAKELONG(0,(x>>16));
334 DOSMEM_ErrorBuffer = DOSMEM_ErrorCall + code;
336 call = DOSMEM_MapRealToLinear(DOSMEM_ErrorCall);
338 memset(call, 0, SIZE_TO_ALLOCATE);
340 /* Fixme - Copy assembly into buffer here */
343 /***********************************************************************
346 * Initialises the DOS memory structures.
348 static void DOSMEM_InitMemory(void)
350 /* Low 64Kb are reserved for DOS/BIOS so the useable area starts at
351 * 1000:0000 and ends at 9FFF:FFEF. */
353 dosmem_info* info_block = DOSMEM_InfoBlock();
354 dosmem_entry* root_block = DOSMEM_RootBlock();
357 root_block->size = DOSMEM_MemoryTop() - (((char*)root_block) + sizeof(dosmem_entry));
359 info_block->blocks = 0;
360 info_block->free = root_block->size;
362 dm = NEXT_BLOCK(root_block);
363 dm->size = DM_BLOCK_TERMINAL;
364 root_block->size |= DM_BLOCK_FREE
365 #ifdef __DOSMEM_DEBUG__
371 /***********************************************************************
372 * DOSMEM_MovePointers
374 * Relocates any pointers into DOS memory to a new address space.
376 static void DOSMEM_MovePointers(LPVOID dest, LPVOID src, DWORD size)
378 unsigned long delta = (char *) dest - (char *) src;
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))) {
388 LDT_SetEntry(cnt, &ent);
393 /***********************************************************************
396 * Create the dos memory segments, and store them into the KERNEL
399 BOOL DOSMEM_Init(BOOL dos_init)
401 LPVOID base = DOSMEM_MemoryBase();
402 BOOL do_init = dos_init && !DOSMEM_dosmem;
406 /* Allocate 1 MB dosmemory
408 DOSMEM_dosmem = VirtualAlloc( NULL, 0x100000, MEM_COMMIT,
409 PAGE_EXECUTE_READWRITE );
412 WARN("Could not allocate DOS memory.\n" );
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;
424 DOSMEM_FillIsrTable();
425 DOSMEM_FillBiosSegments();
427 DOSMEM_InitCollateTable();
428 DOSMEM_InitErrorTable();
430 DOSDEV_InstallDOSDevices();
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);
443 /***********************************************************************
446 * Increment the BIOS tick counter. Called by timer signal handler.
448 void DOSMEM_Tick( WORD timer )
450 BIOSDATA *pBiosData = DOSMEM_BiosData();
451 if (pBiosData) pBiosData->Ticks++;
454 /***********************************************************************
457 * Carve a chunk of the DOS memory block (without selector).
459 LPVOID DOSMEM_GetBlock(UINT size, UINT16* pseg)
463 dosmem_info *info_block = DOSMEM_InfoBlock();
465 #ifdef __DOSMEM_DEBUG_
466 dosmem_entry *prev = NULL;
469 if( size > info_block->free ) return NULL;
470 dm = DOSMEM_RootBlock();
472 while (dm && dm->size != DM_BLOCK_TERMINAL)
474 #ifdef __DOSMEM_DEBUG__
475 if( (dm->size & DM_BLOCK_DEBUG) != DM_BLOCK_DEBUG )
477 WARN("MCB overrun! [prev = 0x%08x]\n", 4 + (UINT)prev);
482 if( dm->size & DM_BLOCK_FREE )
484 dosmem_entry *next = NEXT_BLOCK(dm);
486 while( next->size & DM_BLOCK_FREE ) /* collapse free blocks */
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);
493 blocksize = dm->size & DM_BLOCK_MASK;
494 if( blocksize >= size )
496 block = ((char*)dm) + sizeof(dosmem_entry);
497 if( blocksize - size > 0x20 )
499 /* split dm so that the next one stays
500 * paragraph-aligned (and dm loses free bit) */
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__
512 } else dm->size &= DM_BLOCK_MASK;
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;
524 else dm = NEXT_BLOCK(dm);
526 return (LPVOID)block;
529 /***********************************************************************
532 BOOL DOSMEM_FreeBlock(void* ptr)
534 dosmem_info *info_block = DOSMEM_InfoBlock();
536 if( ptr >= (void*)(((char*)DOSMEM_RootBlock()) + sizeof(dosmem_entry)) &&
537 ptr < (void*)DOSMEM_MemoryTop() && !((((char*)ptr)
538 - DOSMEM_MemoryBase()) & 0xf) )
540 dosmem_entry *dm = (dosmem_entry*)(((char*)ptr) - sizeof(dosmem_entry));
542 if( !(dm->size & (DM_BLOCK_FREE | DM_BLOCK_TERMINAL))
543 #ifdef __DOSMEM_DEBUG__
544 && ((dm->size & DM_BLOCK_DEBUG) == DM_BLOCK_DEBUG )
548 info_block->blocks--;
549 info_block->free += dm->size;
551 dm->size |= DM_BLOCK_FREE;
558 /***********************************************************************
561 LPVOID DOSMEM_ResizeBlock(void* ptr, UINT size, UINT16* pseg)
564 dosmem_info *info_block = DOSMEM_InfoBlock();
566 if( ptr >= (void*)(((char*)DOSMEM_RootBlock()) + sizeof(dosmem_entry)) &&
567 ptr < (void*)DOSMEM_MemoryTop() && !((((char*)ptr)
568 - DOSMEM_MemoryBase()) & 0xf) )
570 dosmem_entry *dm = (dosmem_entry*)(((char*)ptr) - sizeof(dosmem_entry));
572 if( pseg ) *pseg = ((char*)ptr - DOSMEM_MemoryBase()) >> 4;
574 if( !(dm->size & (DM_BLOCK_FREE | DM_BLOCK_TERMINAL))
577 dosmem_entry *next = NEXT_BLOCK(dm);
578 UINT blocksize, orgsize = dm->size & DM_BLOCK_MASK;
580 while( next->size & DM_BLOCK_FREE ) /* collapse free blocks */
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);
587 blocksize = dm->size & DM_BLOCK_MASK;
588 if (blocksize >= size)
590 block = ((char*)dm) + sizeof(dosmem_entry);
591 if( blocksize - size > 0x20 )
593 /* split dm so that the next one stays
594 * paragraph-aligned (and next gains free bit) */
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
603 } else dm->size &= DM_BLOCK_MASK;
605 info_block->free += orgsize - dm->size;
607 /* the collapse didn't help, try getting a new block */
608 block = DOSMEM_GetBlock(size, pseg);
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);
614 info_block->blocks--;
615 info_block->free += dm->size;
617 dm->size |= DM_BLOCK_FREE;
619 /* and Bill Gates said 640K should be enough for everyone... */
621 /* need to split original and collapsed blocks apart again,
622 * and free the collapsed blocks again, before exiting */
623 if( blocksize - orgsize > 0x20 )
625 /* split dm so that the next one stays
626 * paragraph-aligned (and next gains free bit) */
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
635 } else dm->size &= DM_BLOCK_MASK;
640 return (LPVOID)block;
644 /***********************************************************************
647 UINT DOSMEM_Available(void)
649 UINT blocksize, available = 0;
652 dm = DOSMEM_RootBlock();
654 while (dm && dm->size != DM_BLOCK_TERMINAL)
656 #ifdef __DOSMEM_DEBUG__
657 if( (dm->size & DM_BLOCK_DEBUG) != DM_BLOCK_DEBUG )
659 WARN("MCB overrun! [prev = 0x%08x]\n", 4 + (UINT)prev);
664 if( dm->size & DM_BLOCK_FREE )
666 dosmem_entry *next = NEXT_BLOCK(dm);
668 while( next->size & DM_BLOCK_FREE ) /* collapse free blocks */
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);
675 blocksize = dm->size & DM_BLOCK_MASK;
676 if ( blocksize > available ) available = blocksize;
679 else dm = NEXT_BLOCK(dm);
685 /***********************************************************************
686 * DOSMEM_MapLinearToDos
688 * Linear address to the DOS address space.
690 UINT DOSMEM_MapLinearToDos(LPVOID ptr)
692 if (((char*)ptr >= DOSMEM_MemoryBase()) &&
693 ((char*)ptr < DOSMEM_MemoryBase() + 0x100000))
694 return (UINT)ptr - (UINT)DOSMEM_MemoryBase();
699 /***********************************************************************
700 * DOSMEM_MapDosToLinear
702 * DOS linear address to the linear address space.
704 LPVOID DOSMEM_MapDosToLinear(UINT ptr)
706 if (ptr < 0x100000) return (LPVOID)(ptr + (UINT)DOSMEM_MemoryBase());
711 /***********************************************************************
712 * DOSMEM_MapRealToLinear
714 * Real mode DOS address into a linear pointer
716 LPVOID DOSMEM_MapRealToLinear(DWORD x)
720 lin=DOSMEM_MemoryBase()+(x&0xffff)+(((x&0xffff0000)>>16)*16);
721 TRACE_(selector)("(0x%08lx) returns 0x%p.\n", x, lin );
725 /***********************************************************************
726 * DOSMEM_AllocSelector
728 * Allocates a protected mode selector for a realmode segment.
730 WORD DOSMEM_AllocSelector(WORD realsel)
732 HMODULE16 hModule = GetModuleHandle16("KERNEL");
735 sel=GLOBAL_CreateBlock(
736 GMEM_FIXED,DOSMEM_dosmem+realsel*16,0x10000,
737 hModule,FALSE,FALSE,FALSE,NULL
739 TRACE_(selector)("(0x%04x) returns 0x%04x.\n", realsel,sel);