diff --git a/dlls/kernel/dosmem.c b/dlls/kernel/dosmem.c index 7d50caa81f0..a27c1865eba 100644 --- a/dlls/kernel/dosmem.c +++ b/dlls/kernel/dosmem.c @@ -40,7 +40,6 @@ #include "kernel_private.h" #include "toolhelp.h" -#include "miscemu.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(dosmem); @@ -54,33 +53,6 @@ WORD DOSMEM_BiosSysSeg; /* BIOS ROM segment at 0xf000:0 */ #define DOSMEM_SIZE 0x110000 #define DOSMEM_64KB 0x10000 -/* use 2 low bits of 'size' for the housekeeping */ - -#define DM_BLOCK_DEBUG 0xABE00000 -#define DM_BLOCK_TERMINAL 0x00000001 -#define DM_BLOCK_FREE 0x00000002 -#define DM_BLOCK_MASK 0x001FFFFC - -/* -#define __DOSMEM_DEBUG__ - */ - -typedef struct { - unsigned size; -} dosmem_entry; - -typedef struct { - unsigned blocks; - unsigned free; -} dosmem_info; - -#define NEXT_BLOCK(block) \ - (dosmem_entry*)(((char*)(block)) + \ - sizeof(dosmem_entry) + ((block)->size & DM_BLOCK_MASK)) - -#define VM_STUB(x) (0x90CF00CD|(x<<8)) /* INT x; IRET; NOP */ -#define VM_STUB_SEGMENT 0xf000 /* BIOS segment */ - /* when looking at DOS and real mode memory, we activate in three different * modes, depending the situation. * 1/ By default (protected mode), the first MB of memory (actually 0x110000, @@ -91,242 +63,71 @@ typedef struct { * commit this memory, made of: BIOS segment, but also some system * information, usually low in memory that we map for the circumstance also * in the BIOS segment, so that we keep the low memory protected (for NULL - * pointer deref catching for example). In this case, we'res still in PM - * mode, accessing part of the "physicale" real mode memory. - * 3/ if the process enters the real mode, then we commit the full first MB of - * memory (and also initialize the DOS structures in it). + * pointer deref catching for example). In this case, we're still in PM + * mode, accessing part of the "physical" real mode memory. In fact, we don't + * map all the first meg, we keep 64k uncommitted to still catch NULL + * pointers dereference + * 3/ if the process enters the real mode, then we (also) commit the full first + * MB of memory (and also initialize the DOS structures in it). */ /* DOS memory base (linear in process address space) */ static char *DOSMEM_dosmem; -/* DOS system base (for interrupt vector table and BIOS data area) - * ...should in theory (i.e. Windows) be equal to DOSMEM_dosmem (NULL), - * but is normally set to 0xf0000 in Wine to allow trapping of NULL pointers, - * and only relocated to NULL when absolutely necessary */ -static char *DOSMEM_sysmem; /* number of bytes protected from _dosmem. 0 when DOS memory is initialized, * 64k otherwise to trap NULL pointers deref */ static DWORD DOSMEM_protect; -static void DOSMEM_InitMemory(void); +static LONG WINAPI dosmem_handler(EXCEPTION_POINTERS* except); -/*********************************************************************** - * DOSMEM_MemoryTop - * - * Gets the DOS memory top. - */ -static char *DOSMEM_MemoryTop(void) +struct winedos_exports winedos; + +void load_winedos(void) { - return DOSMEM_dosmem+0x9FFFC; /* 640K */ -} + static HANDLE hRunOnce /* = 0 */; + static HMODULE hWineDos /* = 0 */; -/*********************************************************************** - * DOSMEM_InfoBlock - * - * Gets the DOS memory info block. - */ -static dosmem_info *DOSMEM_InfoBlock(void) -{ - /* Start of DOS conventional memory */ - static char *DOSMEM_membase; - - if (!DOSMEM_membase) - { - DWORD reserve; - - /* - * Reserve either: - * - lowest 64k for NULL pointer catching (Win16) - * - lowest 1k for interrupt handlers and - * another 0.5k for BIOS, DOS and intra-application - * areas (DOS) - */ - if (DOSMEM_dosmem != DOSMEM_sysmem) - reserve = DOSMEM_64KB; - else - reserve = 0x600; /* 1.5k */ - - /* - * Round to paragraph boundary in order to make - * sure the alignment is correct. - */ - reserve = ((reserve + 15) >> 4) << 4; - - /* - * Set DOS memory base and initialize conventional memory. - */ - DOSMEM_membase = DOSMEM_dosmem + reserve; - DOSMEM_InitMemory(); - } - - return (dosmem_info*)DOSMEM_membase; -} - -/*********************************************************************** - * DOSMEM_RootBlock - * - * Gets the DOS memory root block. - */ -static dosmem_entry *DOSMEM_RootBlock(void) -{ - /* first block has to be paragraph-aligned */ - return (dosmem_entry*)(((char*)DOSMEM_InfoBlock()) + - ((((sizeof(dosmem_info) + 0xf) & ~0xf) - sizeof(dosmem_entry)))); -} - -/*********************************************************************** - * DOSMEM_FillIsrTable - * - * Fill the interrupt table with fake BIOS calls to BIOSSEG (0xf000). - * - * NOTES: - * Linux normally only traps INTs performed from or destined to BIOSSEG - * for us to handle, if the int_revectored table is empty. Filling the - * interrupt table with calls to INT stubs in BIOSSEG allows DOS programs - * to hook interrupts, as well as use their familiar retf tricks to call - * them, AND let Wine handle any unhooked interrupts transparently. - */ -static void DOSMEM_FillIsrTable(void) -{ - SEGPTR *isr = (SEGPTR*)DOSMEM_sysmem; - int x; - - for (x=0; x<256; x++) isr[x]=MAKESEGPTR(VM_STUB_SEGMENT,x*4); -} - -static void DOSMEM_MakeIsrStubs(void) -{ - DWORD *stub = (DWORD*)(DOSMEM_dosmem + (VM_STUB_SEGMENT << 4)); - int x; - - for (x=0; x<256; x++) stub[x]=VM_STUB(x); -} - -static BIOSDATA * DOSMEM_BiosData(void) -{ - return (BIOSDATA *)(DOSMEM_sysmem + 0x400); -} - -/********************************************************************** - * DOSMEM_GetTicksSinceMidnight - * - * Return number of clock ticks since midnight. - */ -static DWORD DOSMEM_GetTicksSinceMidnight(void) -{ - SYSTEMTIME time; - - /* This should give us the (approximately) correct - * 18.206 clock ticks per second since midnight. + /* FIXME: this isn't 100% thread safe, as we won't catch access to 1MB while + * loading winedos (and may return uninitialized valued) */ - - GetLocalTime( &time ); - - return (((time.wHour * 3600 + time.wMinute * 60 + - time.wSecond) * 18206) / 1000) + - (time.wMilliseconds * 1000 / 54927); -} - -/*********************************************************************** - * DOSMEM_FillBiosSegments - * - * Fill the BIOS data segment with dummy values. - */ -static void DOSMEM_FillBiosSegments(void) -{ - char *pBiosSys = DOSMEM_dosmem + 0xf0000; - BYTE *pBiosROMTable = pBiosSys+0xe6f5; - BIOSDATA *pBiosData = DOSMEM_BiosData(); - - /* Clear all unused values */ - memset( pBiosData, 0, sizeof(*pBiosData) ); - - /* FIXME: should check the number of configured drives and ports */ - pBiosData->Com1Addr = 0x3f8; - pBiosData->Com2Addr = 0x2f8; - pBiosData->Lpt1Addr = 0x378; - pBiosData->Lpt2Addr = 0x278; - pBiosData->InstalledHardware = 0x5463; - pBiosData->MemSize = 640; - pBiosData->NextKbdCharPtr = 0x1e; - pBiosData->FirstKbdCharPtr = 0x1e; - pBiosData->VideoMode = 3; - pBiosData->VideoColumns = 80; - pBiosData->VideoPageSize = 80 * 25 * 2; - pBiosData->VideoPageStartAddr = 0xb800; - pBiosData->VideoCtrlAddr = 0x3d4; - pBiosData->Ticks = DOSMEM_GetTicksSinceMidnight(); - pBiosData->NbHardDisks = 2; - pBiosData->KbdBufferStart = 0x1e; - pBiosData->KbdBufferEnd = 0x3e; - pBiosData->RowsOnScreenMinus1 = 24; - pBiosData->BytesPerChar = 0x10; - pBiosData->ModeOptions = 0x64; - pBiosData->FeatureBitsSwitches = 0xf9; - pBiosData->VGASettings = 0x51; - pBiosData->DisplayCombination = 0x08; - pBiosData->DiskDataRate = 0; - - /* fill ROM configuration table (values from Award) */ - *(pBiosROMTable+0x0) = 0x08; /* number of bytes following LO */ - *(pBiosROMTable+0x1) = 0x00; /* number of bytes following HI */ - *(pBiosROMTable+0x2) = 0xfc; /* model */ - *(pBiosROMTable+0x3) = 0x01; /* submodel */ - *(pBiosROMTable+0x4) = 0x00; /* BIOS revision */ - *(pBiosROMTable+0x5) = 0x74; /* feature byte 1 */ - *(pBiosROMTable+0x6) = 0x00; /* feature byte 2 */ - *(pBiosROMTable+0x7) = 0x00; /* feature byte 3 */ - *(pBiosROMTable+0x8) = 0x00; /* feature byte 4 */ - *(pBiosROMTable+0x9) = 0x00; /* feature byte 5 */ - - /* BIOS date string */ - strcpy(pBiosSys+0xfff5, "13/01/99"); - - /* BIOS ID */ - *(pBiosSys+0xfffe) = 0xfc; - - /* Reboot vector (f000:fff0 or ffff:0000) */ - *(DWORD*)(pBiosSys + 0xfff0) = VM_STUB(0x19); -} - -/*********************************************************************** - * DOSMEM_InitMemory - * - * Initialises the DOS memory structures. - */ -static void DOSMEM_InitMemory(void) -{ - dosmem_info* info_block = DOSMEM_InfoBlock(); - dosmem_entry* root_block = DOSMEM_RootBlock(); - dosmem_entry* dm; - - root_block->size = DOSMEM_MemoryTop() - (((char*)root_block) + sizeof(dosmem_entry)); - - info_block->blocks = 0; - info_block->free = root_block->size; - - dm = NEXT_BLOCK(root_block); - dm->size = DM_BLOCK_TERMINAL; - root_block->size |= DM_BLOCK_FREE -#ifdef __DOSMEM_DEBUG__ - | DM_BLOCK_DEBUG -#endif - ; - - TRACE( "DOS conventional memory initialized, %d bytes free.\n", - DOSMEM_Available() ); -} - -static void dosmem_bios_init(void) -{ - static int bios_created; - - if (!bios_created) + if (hWineDos) return; + if (hRunOnce == 0) { - DOSMEM_FillBiosSegments(); - DOSMEM_FillIsrTable(); - bios_created = TRUE; + HANDLE hEvent = CreateEventW( NULL, TRUE, FALSE, NULL ); + if (InterlockedCompareExchangePointer( (PVOID)&hRunOnce, hEvent, 0 ) == 0) + { + HMODULE hModule; + + /* ok, we're the winning thread */ + VirtualProtect( DOSMEM_dosmem + DOSMEM_protect, + DOSMEM_SIZE - DOSMEM_protect, + PAGE_EXECUTE_READWRITE, NULL ); + if (!(hModule = LoadLibraryA( "winedos.dll" ))) + { + ERR("Could not load winedos.dll, DOS subsystem unavailable\n"); + hWineDos = (HMODULE)1; /* not to try to load it again */ + return; + } +#define GET_ADDR(func) winedos.func = (void *)GetProcAddress( hModule, #func ); + GET_ADDR(AllocDosBlock); + GET_ADDR(FreeDosBlock); + GET_ADDR(ResizeDosBlock); + GET_ADDR(inport); + GET_ADDR(outport); + GET_ADDR(EmulateInterruptPM); + GET_ADDR(CallBuiltinHandler); + GET_ADDR(BiosTick); +#undef GET_ADDR + RtlRemoveVectoredExceptionHandler( dosmem_handler ); + hWineDos = hModule; + SetEvent( hRunOnce ); + return; + } + /* someone beat us here... */ + CloseHandle( hEvent ); } + + /* and wait for the winner to have finished */ + WaitForSingleObject( hRunOnce, INFINITE ); } /****************************************************************** @@ -339,19 +140,10 @@ static LONG WINAPI dosmem_handler(EXCEPTION_POINTERS* except) if (except->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { DWORD addr = except->ExceptionRecord->ExceptionInformation[1]; - if (addr >= (ULONG_PTR)DOSMEM_sysmem && - addr < (ULONG_PTR)DOSMEM_sysmem + DOSMEM_64KB) - { - VirtualProtect( DOSMEM_sysmem, DOSMEM_64KB, PAGE_EXECUTE_READWRITE, NULL ); - dosmem_bios_init(); - return EXCEPTION_CONTINUE_EXECUTION; - } if (addr >= (ULONG_PTR)DOSMEM_dosmem + DOSMEM_protect && addr < (ULONG_PTR)DOSMEM_dosmem + DOSMEM_SIZE) { - VirtualProtect( DOSMEM_dosmem + DOSMEM_protect, DOSMEM_SIZE - DOSMEM_protect, - PAGE_EXECUTE_READWRITE, NULL ); - dosmem_bios_init(); + load_winedos(); return EXCEPTION_CONTINUE_EXECUTION; } } @@ -363,7 +155,7 @@ static LONG WINAPI dosmem_handler(EXCEPTION_POINTERS* except) * * Setup the first megabyte for DOS memory access */ -static void setup_dos_mem(void) +static char* setup_dos_mem(void) { int sys_offset = 0; int page_size = getpagesize(); @@ -399,8 +191,8 @@ static void setup_dos_mem(void) } } DOSMEM_dosmem = addr; - DOSMEM_sysmem = (char*)addr + sys_offset; RtlAddVectoredExceptionHandler(FALSE, dosmem_handler); + return (char*)addr + sys_offset; } @@ -412,285 +204,18 @@ static void setup_dos_mem(void) */ BOOL DOSMEM_Init(void) { - setup_dos_mem(); + char* sysmem = setup_dos_mem(); - DOSMEM_0000H = GLOBAL_CreateBlock( GMEM_FIXED, DOSMEM_sysmem, + DOSMEM_0000H = GLOBAL_CreateBlock( GMEM_FIXED, sysmem, DOSMEM_64KB, 0, WINE_LDT_FLAGS_DATA ); - DOSMEM_BiosDataSeg = GLOBAL_CreateBlock(GMEM_FIXED,DOSMEM_sysmem + 0x400, - 0x100, 0, WINE_LDT_FLAGS_DATA ); - DOSMEM_BiosSysSeg = GLOBAL_CreateBlock(GMEM_FIXED,DOSMEM_dosmem + 0xf0000, - DOSMEM_64KB, 0, WINE_LDT_FLAGS_DATA ); + DOSMEM_BiosDataSeg = GLOBAL_CreateBlock( GMEM_FIXED, sysmem + 0x400, + 0x100, 0, WINE_LDT_FLAGS_DATA ); + DOSMEM_BiosSysSeg = GLOBAL_CreateBlock( GMEM_FIXED, DOSMEM_dosmem + 0xf0000, + DOSMEM_64KB, 0, WINE_LDT_FLAGS_DATA ); return TRUE; } -/****************************************************************** - * DOSMEM_InitDosMem - * - * Initialize the first MB of memory to look like a real DOS setup - */ -BOOL DOSMEM_InitDosMem(void) -{ - static int already_mapped; - - if (!already_mapped) - { - if (DOSMEM_dosmem) - { - ERR( "Needs access to the first megabyte for DOS mode\n" ); - ExitProcess(1); - } - MESSAGE( "Warning: unprotecting memory to allow real-mode calls.\n" - " NULL pointer accesses will no longer be caught.\n" ); - VirtualProtect( NULL, DOSMEM_SIZE, PAGE_EXECUTE_READWRITE, NULL ); - DOSMEM_protect = 0; - dosmem_bios_init(); - /* copy the BIOS and ISR area down */ - memcpy( DOSMEM_dosmem, DOSMEM_sysmem, 0x400 + 0x100 ); - DOSMEM_sysmem = DOSMEM_dosmem; - SetSelectorBase( DOSMEM_0000H, 0 ); - SetSelectorBase( DOSMEM_BiosDataSeg, 0x400 ); - /* we may now need the actual interrupt stubs, and since we've just moved the - * interrupt vector table away, we can fill the area with stubs instead... */ - DOSMEM_MakeIsrStubs(); - already_mapped = 1; - } - return TRUE; -} - - -/*********************************************************************** - * DOSMEM_Tick - * - * Increment the BIOS tick counter. Called by timer signal handler. - */ -void DOSMEM_Tick( WORD timer ) -{ - BIOSDATA *pBiosData = DOSMEM_BiosData(); - if (pBiosData) pBiosData->Ticks++; -} - -/*********************************************************************** - * DOSMEM_GetBlock - * - * Carve a chunk of the DOS memory block (without selector). - */ -LPVOID DOSMEM_GetBlock(UINT size, UINT16* pseg) -{ - UINT blocksize; - char *block = NULL; - dosmem_info *info_block = DOSMEM_InfoBlock(); - dosmem_entry *dm; -#ifdef __DOSMEM_DEBUG_ - dosmem_entry *prev = NULL; -#endif - - if( size > info_block->free ) return NULL; - dm = DOSMEM_RootBlock(); - - while (dm && dm->size != DM_BLOCK_TERMINAL) - { -#ifdef __DOSMEM_DEBUG__ - if( (dm->size & DM_BLOCK_DEBUG) != DM_BLOCK_DEBUG ) - { - WARN("MCB overrun! [prev = 0x%08x]\n", 4 + (UINT)prev); - return NULL; - } - prev = dm; -#endif - if( dm->size & DM_BLOCK_FREE ) - { - dosmem_entry *next = NEXT_BLOCK(dm); - - while( next->size & DM_BLOCK_FREE ) /* collapse free blocks */ - { - dm->size += sizeof(dosmem_entry) + (next->size & DM_BLOCK_MASK); - next->size = (DM_BLOCK_FREE | DM_BLOCK_TERMINAL); - next = NEXT_BLOCK(dm); - } - - blocksize = dm->size & DM_BLOCK_MASK; - if( blocksize >= size ) - { - block = ((char*)dm) + sizeof(dosmem_entry); - if( blocksize - size > 0x20 ) - { - /* split dm so that the next one stays - * paragraph-aligned (and dm loses free bit) */ - - dm->size = (((size + 0xf + sizeof(dosmem_entry)) & ~0xf) - - sizeof(dosmem_entry)); - next = (dosmem_entry*)(((char*)dm) + - sizeof(dosmem_entry) + dm->size); - next->size = (blocksize - (dm->size + - sizeof(dosmem_entry))) | DM_BLOCK_FREE -#ifdef __DOSMEM_DEBUG__ - | DM_BLOCK_DEBUG -#endif - ; - } else dm->size &= DM_BLOCK_MASK; - - info_block->blocks++; - info_block->free -= dm->size; - if( pseg ) *pseg = (block - DOSMEM_dosmem) >> 4; -#ifdef __DOSMEM_DEBUG__ - dm->size |= DM_BLOCK_DEBUG; -#endif - break; - } - dm = next; - } - else dm = NEXT_BLOCK(dm); - } - return (LPVOID)block; -} - -/*********************************************************************** - * DOSMEM_FreeBlock - */ -BOOL DOSMEM_FreeBlock(void* ptr) -{ - dosmem_info *info_block = DOSMEM_InfoBlock(); - - if( ptr >= (void*)(((char*)DOSMEM_RootBlock()) + sizeof(dosmem_entry)) && - ptr < (void*)DOSMEM_MemoryTop() && !((((char*)ptr) - - DOSMEM_dosmem) & 0xf) ) - { - dosmem_entry *dm = (dosmem_entry*)(((char*)ptr) - sizeof(dosmem_entry)); - - if( !(dm->size & (DM_BLOCK_FREE | DM_BLOCK_TERMINAL)) -#ifdef __DOSMEM_DEBUG__ - && ((dm->size & DM_BLOCK_DEBUG) == DM_BLOCK_DEBUG ) -#endif - ) - { - info_block->blocks--; - info_block->free += dm->size; - - dm->size |= DM_BLOCK_FREE; - return TRUE; - } - } - return FALSE; -} - -/*********************************************************************** - * DOSMEM_ResizeBlock - * - * Resize DOS memory block in place. Returns block size or -1 on error. - * - * If exact is TRUE, returned value is either old or requested block - * size. If exact is FALSE, block is expanded even if there is not - * enough space for full requested block size. - */ -UINT DOSMEM_ResizeBlock(void *ptr, UINT size, BOOL exact) -{ - char *block = NULL; - dosmem_info *info_block = DOSMEM_InfoBlock(); - dosmem_entry *dm; - dosmem_entry *next; - UINT blocksize; - UINT orgsize; - - if( (ptr < (void*)(sizeof(dosmem_entry) + (char*)DOSMEM_RootBlock())) || - (ptr >= (void*)DOSMEM_MemoryTop()) || - (((((char*)ptr) - DOSMEM_dosmem) & 0xf) != 0) ) - return (UINT)-1; - - dm = (dosmem_entry*)(((char*)ptr) - sizeof(dosmem_entry)); - if( dm->size & (DM_BLOCK_FREE | DM_BLOCK_TERMINAL) ) - return (UINT)-1; - - next = NEXT_BLOCK(dm); - orgsize = dm->size & DM_BLOCK_MASK; - - /* collapse free blocks */ - while( next->size & DM_BLOCK_FREE ) - { - dm->size += sizeof(dosmem_entry) + (next->size & DM_BLOCK_MASK); - next->size = (DM_BLOCK_FREE | DM_BLOCK_TERMINAL); - next = NEXT_BLOCK(dm); - } - - blocksize = dm->size & DM_BLOCK_MASK; - - /* - * If collapse didn't help we either expand block to maximum - * available size (exact == FALSE) or give collapsed blocks - * back to free storage (exact == TRUE). - */ - if (blocksize < size) - size = exact ? orgsize : blocksize; - - block = ((char*)dm) + sizeof(dosmem_entry); - if( blocksize - size > 0x20 ) - { - /* - * split dm so that the next one stays - * paragraph-aligned (and next gains free bit) - */ - - dm->size = (((size + 0xf + sizeof(dosmem_entry)) & ~0xf) - - sizeof(dosmem_entry)); - next = (dosmem_entry*)(((char*)dm) + - sizeof(dosmem_entry) + dm->size); - next->size = (blocksize - (dm->size + - sizeof(dosmem_entry))) | DM_BLOCK_FREE; - } - else - { - dm->size &= DM_BLOCK_MASK; - } - - /* - * Adjust available memory if block size changes. - */ - info_block->free += orgsize - dm->size; - - return size; -} - -/*********************************************************************** - * DOSMEM_Available - */ -UINT DOSMEM_Available(void) -{ - UINT blocksize, available = 0; - dosmem_entry *dm; - - dm = DOSMEM_RootBlock(); - - while (dm && dm->size != DM_BLOCK_TERMINAL) - { -#ifdef __DOSMEM_DEBUG__ - if( (dm->size & DM_BLOCK_DEBUG) != DM_BLOCK_DEBUG ) - { - WARN("MCB overrun! [prev = 0x%08x]\n", 4 + (UINT)prev); - return NULL; - } - prev = dm; -#endif - if( dm->size & DM_BLOCK_FREE ) - { - dosmem_entry *next = NEXT_BLOCK(dm); - - while( next->size & DM_BLOCK_FREE ) /* collapse free blocks */ - { - dm->size += sizeof(dosmem_entry) + (next->size & DM_BLOCK_MASK); - next->size = (DM_BLOCK_FREE | DM_BLOCK_TERMINAL); - next = NEXT_BLOCK(dm); - } - - blocksize = dm->size & DM_BLOCK_MASK; - if ( blocksize > available ) available = blocksize; - dm = next; - } - else dm = NEXT_BLOCK(dm); - } - return available; -} - - /*********************************************************************** * DOSMEM_MapLinearToDos * diff --git a/dlls/kernel/global16.c b/dlls/kernel/global16.c index cd7e188b705..cb79d394f88 100644 --- a/dlls/kernel/global16.c +++ b/dlls/kernel/global16.c @@ -40,7 +40,6 @@ #include "wine/winbase16.h" #include "ntstatus.h" #include "toolhelp.h" -#include "miscemu.h" #include "stackframe.h" #include "kernel_private.h" #include "wine/debug.h" @@ -77,6 +76,23 @@ static int globalArenaSize; #define VALID_HANDLE(handle) (((handle)>>__AHSHIFT)> __AHSHIFT)) +static inline void* DOSMEM_AllocBlock(UINT size, UINT16* pseg) +{ + if (!winedos.AllocDosBlock) load_winedos(); + return winedos.AllocDosBlock ? winedos.AllocDosBlock(size, pseg) : NULL; +} + +static inline BOOL DOSMEM_FreeBlock(void* ptr) +{ + if (!winedos.FreeDosBlock) load_winedos(); + return winedos.FreeDosBlock ? winedos.FreeDosBlock( ptr ) : FALSE; +} + +static inline UINT DOSMEM_ResizeBlock(void *ptr, UINT size, BOOL exact) +{ + if (!winedos.ResizeDosBlock) load_winedos(); + return winedos.ResizeDosBlock ? winedos.ResizeDosBlock(ptr, size, TRUE) : 0; +} /*********************************************************************** * GLOBAL_GetArena @@ -336,7 +352,7 @@ HGLOBAL16 WINAPI GlobalReAlloc16( newptr = 0; else { - newptr = DOSMEM_GetBlock( size, 0 ); + newptr = DOSMEM_AllocBlock( size, 0 ); if (newptr) { memcpy( newptr, ptr, oldsize ); @@ -757,7 +773,7 @@ DWORD WINAPI GlobalDOSAlloc16( DWORD size /* [in] Number of bytes to be allocated */ ) { UINT16 uParagraph; - LPVOID lpBlock = DOSMEM_GetBlock( size, &uParagraph ); + LPVOID lpBlock = DOSMEM_AllocBlock( size, &uParagraph ); if( lpBlock ) { diff --git a/dlls/kernel/instr.c b/dlls/kernel/instr.c index d2285de3b16..8a19b17a103 100644 --- a/dlls/kernel/instr.c +++ b/dlls/kernel/instr.c @@ -30,7 +30,6 @@ #include "wine/winuser16.h" #include "excpt.h" #include "module.h" -#include "miscemu.h" #include "wine/debug.h" #include "kernel_private.h" #include "thread.h" @@ -67,34 +66,6 @@ inline static void *get_stack( CONTEXT86 *context ) return wine_ldt_get_ptr( context->SegSs, context->Esp ); } - -static void (WINAPI *DOS_EmulateInterruptPM)( CONTEXT86 *context, BYTE intnum ); -static void (WINAPI *DOS_CallBuiltinHandler)( CONTEXT86 *context, BYTE intnum ); -static DWORD (WINAPI *DOS_inport)( int port, int size ); -static void (WINAPI *DOS_outport)( int port, int size, DWORD val ); - - -static void init_winedos(void) -{ - static HMODULE module; - - if (module) return; - module = LoadLibraryA( "winedos.dll" ); - if (!module) - { - ERR("could not load winedos.dll, DOS subsystem unavailable\n"); - module = (HMODULE)1; /* don't try again */ - return; - } -#define GET_ADDR(func) DOS_##func = (void *)GetProcAddress(module, #func); - GET_ADDR(inport); - GET_ADDR(outport); - GET_ADDR(EmulateInterruptPM); - GET_ADDR(CallBuiltinHandler); -#undef GET_ADDR -} - - /*********************************************************************** * INSTR_ReplaceSelector * @@ -112,7 +83,11 @@ static BOOL INSTR_ReplaceSelector( CONTEXT86 *context, WORD *sel ) { static WORD sys_timer = 0; if (!sys_timer) - sys_timer = CreateSystemTimer( 55, DOSMEM_Tick ); + { + if (!winedos.BiosTick) load_winedos(); + if (winedos.BiosTick) + sys_timer = CreateSystemTimer( 55, winedos.BiosTick ); + } *sel = DOSMEM_BiosDataSeg; return TRUE; } @@ -367,8 +342,8 @@ static DWORD INSTR_inport( WORD port, int size, CONTEXT86 *context ) { DWORD res = ~0U; - if (!DOS_inport) init_winedos(); - if (DOS_inport) res = DOS_inport( port, size ); + if (!winedos.inport) load_winedos(); + if (winedos.inport) res = winedos.inport( port, size ); if (TRACE_ON(io)) { @@ -399,8 +374,8 @@ static DWORD INSTR_inport( WORD port, int size, CONTEXT86 *context ) */ static void INSTR_outport( WORD port, int size, DWORD val, CONTEXT86 *context ) { - if (!DOS_outport) init_winedos(); - if (DOS_outport) DOS_outport( port, size, val ); + if (!winedos.outport) load_winedos(); + if (winedos.outport) winedos.outport( port, size, val ); if (TRACE_ON(io)) { @@ -733,11 +708,11 @@ DWORD INSTR_EmulateInstruction( EXCEPTION_RECORD *rec, CONTEXT86 *context ) case 0xcd: /* int */ if (wine_ldt_is_system(context->SegCs)) break; /* don't emulate it in 32-bit code */ - if (!DOS_EmulateInterruptPM) init_winedos(); - if (DOS_EmulateInterruptPM) + if (!winedos.EmulateInterruptPM) load_winedos(); + if (winedos.EmulateInterruptPM) { context->Eip += prefixlen + 2; - DOS_EmulateInterruptPM( context, instr[1] ); + winedos.EmulateInterruptPM( context, instr[1] ); return ExceptionContinueExecution; } break; /* Unable to emulate it */ @@ -839,8 +814,8 @@ DWORD INSTR_EmulateInstruction( EXCEPTION_RECORD *rec, CONTEXT86 *context ) */ void INSTR_CallBuiltinHandler( CONTEXT86 *context, BYTE intnum ) { - if (!DOS_CallBuiltinHandler) init_winedos(); - if (DOS_CallBuiltinHandler) DOS_CallBuiltinHandler( context, intnum ); + if (!winedos.CallBuiltinHandler) load_winedos(); + if (winedos.CallBuiltinHandler) winedos.CallBuiltinHandler( context, intnum ); } diff --git a/dlls/kernel/kernel32.spec b/dlls/kernel/kernel32.spec index 1da01f01d68..5a6b461b2a6 100644 --- a/dlls/kernel/kernel32.spec +++ b/dlls/kernel/kernel32.spec @@ -1167,11 +1167,6 @@ # Wine dll separation hacks, these will go away, don't use them # @ cdecl DOSMEM_AllocSelector(long) -@ cdecl DOSMEM_Available() -@ cdecl DOSMEM_FreeBlock(ptr) -@ cdecl DOSMEM_GetBlock(long ptr) -@ cdecl DOSMEM_InitDosMem(long) -@ cdecl DOSMEM_ResizeBlock(ptr long long) @ cdecl LOCAL_Alloc(long long long) @ cdecl LOCAL_Compact(long long long) @ cdecl LOCAL_CountFree(long) diff --git a/dlls/kernel/kernel_private.h b/dlls/kernel/kernel_private.h index b022f4b6d28..db82db3656c 100644 --- a/dlls/kernel/kernel_private.h +++ b/dlls/kernel/kernel_private.h @@ -90,5 +90,30 @@ typedef struct WORD stackbottom; /* Bottom of the stack */ } INSTANCEDATA; #include "poppack.h" +extern WORD DOSMEM_0000H; +extern WORD DOSMEM_BiosDataSeg; +extern WORD DOSMEM_BiosSysSeg; + +/* msdos/dosmem.c */ +extern BOOL DOSMEM_Init(void); +extern WORD DOSMEM_AllocSelector(WORD); +extern LPVOID DOSMEM_MapRealToLinear(DWORD); /* real-mode to linear */ +extern LPVOID DOSMEM_MapDosToLinear(UINT); /* linear DOS to Wine */ +extern UINT DOSMEM_MapLinearToDos(LPVOID); /* linear Wine to DOS */ +extern void load_winedos(void); + +extern struct winedos_exports +{ + /* for global16.c */ + void* (*AllocDosBlock)(UINT size, UINT16* pseg); + BOOL (*FreeDosBlock)(void* ptr); + UINT (*ResizeDosBlock)(void *ptr, UINT size, BOOL exact); + /* for instr.c */ + void (WINAPI *EmulateInterruptPM)( CONTEXT86 *context, BYTE intnum ); + void (WINAPI *CallBuiltinHandler)( CONTEXT86 *context, BYTE intnum ); + DWORD (WINAPI *inport)( int port, int size ); + void (WINAPI *outport)( int port, int size, DWORD val ); + void (* BiosTick)(WORD timer); +} winedos; #endif diff --git a/dlls/winedos/Makefile.in b/dlls/winedos/Makefile.in index bfeaff0a6c2..88612e69b3b 100644 --- a/dlls/winedos/Makefile.in +++ b/dlls/winedos/Makefile.in @@ -14,6 +14,7 @@ C_SRCS = \ dma.c \ dosaspi.c \ dosconf.c \ + dosmem.c \ dosvm.c \ fpu.c \ himem.c \ diff --git a/dlls/winedos/dosexe.h b/dlls/winedos/dosexe.h index 728f793c634..5def4d0c36f 100644 --- a/dlls/winedos/dosexe.h +++ b/dlls/winedos/dosexe.h @@ -24,11 +24,11 @@ #include #include "windef.h" +#include "wine/library.h" #include "wine/windef16.h" #include "winbase.h" #include "winnt.h" /* for PCONTEXT */ #include "wincon.h" /* for MOUSE_EVENT_RECORD */ -#include "miscemu.h" #define MAX_DOS_DRIVES 26 @@ -182,6 +182,68 @@ extern struct DPMI_segments *DOSVM_dpmi_segments; #define SET_CH(context,val) ((void)((context)->Ecx = ((context)->Ecx & ~0xff00) | (((BYTE)(val)) << 8))) #define SET_DH(context,val) ((void)((context)->Edx = ((context)->Edx & ~0xff00) | (((BYTE)(val)) << 8))) +#include + +typedef struct +{ + WORD Com1Addr; /* 00: COM1 I/O address */ + WORD Com2Addr; /* 02: COM2 I/O address */ + WORD Com3Addr; /* 04: COM3 I/O address */ + WORD Com4Addr; /* 06: COM4 I/O address */ + WORD Lpt1Addr; /* 08: LPT1 I/O address */ + WORD Lpt2Addr; /* 0a: LPT2 I/O address */ + WORD Lpt3Addr; /* 0c: LPT3 I/O address */ + WORD Lpt4Addr; /* 0e: LPT4 I/O address */ + WORD InstalledHardware; /* 10: Installed hardware flags */ + BYTE POSTstatus; /* 12: Power-On Self Test status */ + WORD MemSize; /* 13: Base memory size in Kb */ + WORD unused1; /* 15: Manufacturing test scratch pad */ + BYTE KbdFlags1; /* 17: Keyboard flags 1 */ + BYTE KbdFlags2; /* 18: Keyboard flags 2 */ + BYTE unused2; /* 19: Keyboard driver workspace */ + WORD NextKbdCharPtr; /* 1a: Next character in kbd buffer */ + WORD FirstKbdCharPtr; /* 1c: First character in kbd buffer */ + WORD KbdBuffer[16]; /* 1e: Keyboard buffer */ + BYTE DisketteStatus1; /* 3e: Diskette recalibrate status */ + BYTE DisketteStatus2; /* 3f: Diskette motor status */ + BYTE DisketteStatus3; /* 40: Diskette motor timeout */ + BYTE DisketteStatus4; /* 41: Diskette last operation status */ + BYTE DiskStatus[7]; /* 42: Disk status/command bytes */ + BYTE VideoMode; /* 49: Video mode */ + WORD VideoColumns; /* 4a: Number of columns */ + WORD VideoPageSize; /* 4c: Video page size in bytes */ + WORD VideoPageStartAddr; /* 4e: Video page start address */ + BYTE VideoCursorPos[16]; /* 50: Cursor position for 8 pages, column/row order */ + WORD VideoCursorType; /* 60: Video cursor type */ + BYTE VideoCurPage; /* 62: Video current page */ + WORD VideoCtrlAddr; /* 63: Video controller address */ + BYTE VideoReg1; /* 65: Video mode select register */ + BYTE VideoReg2; /* 66: Video CGA palette register */ + DWORD ResetEntry; /* 67: Warm reset entry point */ + BYTE LastIRQ; /* 6b: Last unexpected interrupt */ + DWORD Ticks; /* 6c: Ticks since midnight */ + BYTE TicksOverflow; /* 70: Timer overflow if past midnight */ + BYTE CtrlBreakFlag; /* 71: Ctrl-Break flag */ + WORD ResetFlag; /* 72: POST Reset flag */ + BYTE DiskOpStatus; /* 74: Last hard-disk operation status */ + BYTE NbHardDisks; /* 75: Number of hard disks */ + BYTE DiskCtrlByte; /* 76: Disk control byte */ + BYTE DiskIOPort; /* 77: Disk I/O port offset */ + BYTE LptTimeout[4]; /* 78: Timeouts for parallel ports */ + BYTE ComTimeout[4]; /* 7c: Timeouts for serial ports */ + WORD KbdBufferStart; /* 80: Keyboard buffer start */ + WORD KbdBufferEnd; /* 82: Keyboard buffer end */ + BYTE RowsOnScreenMinus1; /* 84: EGA only */ + WORD BytesPerChar; /* 85: EGA only */ + BYTE ModeOptions; /* 87: EGA only */ + BYTE FeatureBitsSwitches; /* 88: EGA only */ + BYTE VGASettings; /* 89: VGA misc settings */ + BYTE DisplayCombination; /* 8A: VGA display combinations */ + BYTE DiskDataRate; /* 8B: Last disk data rate selected */ +} BIOSDATA; + +#include + /* module.c */ extern void WINAPI MZ_LoadImage( LPCSTR filename, HANDLE hFile ); extern BOOL WINAPI MZ_Exec( CONTEXT86 *context, LPCSTR filename, BYTE func, LPVOID paramblk ); @@ -201,7 +263,6 @@ extern void WINAPI DOSVM_QueueEvent( INT irq, INT priority, DOSRELAY relay, LPVO extern void WINAPI DOSVM_PIC_ioport_out( WORD port, BYTE val ); extern void WINAPI DOSVM_SetTimer( UINT ticks ); extern UINT WINAPI DOSVM_GetTimer( void ); -extern BIOSDATA *DOSVM_BiosData( void ); /* devices.c */ extern void DOSDEV_InstallDOSDevices(void); @@ -221,10 +282,20 @@ extern void DMA_ioport_out( WORD port, BYTE val ); extern BYTE DMA_ioport_in( WORD port ); /* dosaspi.c */ -void WINAPI DOSVM_ASPIHandler(CONTEXT86*); +extern void WINAPI DOSVM_ASPIHandler(CONTEXT86*); /* dosconf.c */ -DOSCONF *DOSCONF_GetConfig( void ); +extern DOSCONF *DOSCONF_GetConfig( void ); + +/* dosmem.c */ +extern void DOSMEM_InitDosMemory(void); +extern BOOL DOSMEM_MapDosLayout(void); +extern WORD DOSMEM_AllocSelector(WORD); /* FIXME: to be removed */ +extern LPVOID DOSMEM_AllocBlock(UINT size, WORD* p); +extern BOOL DOSMEM_FreeBlock(void* ptr); +extern UINT DOSMEM_ResizeBlock(void* ptr, UINT size, BOOL exact); +extern UINT DOSMEM_Available(void); +extern BIOSDATA *DOSVM_BiosData( void ); /* fpu.c */ extern void WINAPI DOSVM_Int34Handler(CONTEXT86*); diff --git a/dlls/winedos/dosmem.c b/dlls/winedos/dosmem.c new file mode 100644 index 00000000000..f742efa6112 --- /dev/null +++ b/dlls/winedos/dosmem.c @@ -0,0 +1,576 @@ +/* + * DOS memory emulation + * + * Copyright 1995 Alexandre Julliard + * Copyright 1996 Marcus Meissner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "wine/port.h" + +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_MMAN_H +# include +#endif + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "excpt.h" +#include "winternl.h" +#include "wine/winbase16.h" + +#include "dosexe.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(dosmem); + +/* DOS memory highest address (including HMA) */ +#define DOSMEM_SIZE 0x110000 +#define DOSMEM_64KB 0x10000 + +/* see dlls/kernel/dosmem.c for the details */ +static char *DOSMEM_dosmem; +static char *DOSMEM_sysmem; + +/* use 2 low bits of 'size' for the housekeeping */ +#define DM_BLOCK_DEBUG 0xABE00000 +#define DM_BLOCK_TERMINAL 0x00000001 +#define DM_BLOCK_FREE 0x00000002 +#define DM_BLOCK_MASK 0x001FFFFC + +/* +#define __DOSMEM_DEBUG__ + */ + +typedef struct { + unsigned size; +} dosmem_entry; + +typedef struct { + unsigned blocks; + unsigned free; +} dosmem_info; + +static inline dosmem_entry* next_block(dosmem_entry* block) +{ + return (dosmem_entry*)((char*)block + + sizeof(dosmem_entry) + (block->size & DM_BLOCK_MASK)); +} + +#define VM_STUB(x) (0x90CF00CD|(x<<8)) /* INT x; IRET; NOP */ +#define VM_STUB_SEGMENT 0xf000 /* BIOS segment */ + +/* FIXME: this should be moved to the LOL, and the whole allocation strategy + * should use real MCB + */ +static dosmem_info* DOSMEM_info_block; + +/*********************************************************************** + * DOSMEM_MemoryTop + * + * Gets the DOS memory top. + */ +static char *DOSMEM_MemoryTop(void) +{ + return DOSMEM_dosmem+0x9FFFC; /* 640K */ +} + +/*********************************************************************** + * DOSMEM_RootBlock + * + * Gets the DOS memory root block. + */ +static dosmem_entry *DOSMEM_RootBlock(void) +{ + /* first block has to be paragraph-aligned */ + return (dosmem_entry*)(((char*)DOSMEM_info_block) + + ((((sizeof(dosmem_info) + 0xf) & ~0xf) - sizeof(dosmem_entry)))); +} + +/*********************************************************************** + * DOSMEM_FillIsrTable + * + * Fill the interrupt table with fake BIOS calls to BIOSSEG (0xf000). + * + * NOTES: + * Linux normally only traps INTs performed from or destined to BIOSSEG + * for us to handle, if the int_revectored table is empty. Filling the + * interrupt table with calls to INT stubs in BIOSSEG allows DOS programs + * to hook interrupts, as well as use their familiar retf tricks to call + * them, AND let Wine handle any unhooked interrupts transparently. + */ +static void DOSMEM_FillIsrTable(void) +{ + SEGPTR *isr = (SEGPTR*)DOSMEM_sysmem; + int x; + + for (x=0; x<256; x++) isr[x]=MAKESEGPTR(VM_STUB_SEGMENT,x*4); +} + +static void DOSMEM_MakeIsrStubs(void) +{ + DWORD *stub = (DWORD*)(DOSMEM_dosmem + (VM_STUB_SEGMENT << 4)); + int x; + + for (x=0; x<256; x++) stub[x]=VM_STUB(x); +} + +BIOSDATA* DOSVM_BiosData(void) +{ + return (BIOSDATA *)(DOSMEM_sysmem + 0x400); +} + +/********************************************************************** + * DOSMEM_GetTicksSinceMidnight + * + * Return number of clock ticks since midnight. + */ +static DWORD DOSMEM_GetTicksSinceMidnight(void) +{ + SYSTEMTIME time; + + /* This should give us the (approximately) correct + * 18.206 clock ticks per second since midnight. + */ + + GetLocalTime( &time ); + + return (((time.wHour * 3600 + time.wMinute * 60 + + time.wSecond) * 18206) / 1000) + + (time.wMilliseconds * 1000 / 54927); +} + +/*********************************************************************** + * DOSMEM_FillBiosSegments + * + * Fill the BIOS data segment with dummy values. + */ +static void DOSMEM_FillBiosSegments(void) +{ + char *pBiosSys = DOSMEM_dosmem + 0xf0000; + BYTE *pBiosROMTable = pBiosSys+0xe6f5; + BIOSDATA *pBiosData = DOSVM_BiosData(); + + /* Clear all unused values */ + memset( pBiosData, 0, sizeof(*pBiosData) ); + + /* FIXME: should check the number of configured drives and ports */ + pBiosData->Com1Addr = 0x3f8; + pBiosData->Com2Addr = 0x2f8; + pBiosData->Lpt1Addr = 0x378; + pBiosData->Lpt2Addr = 0x278; + pBiosData->InstalledHardware = 0x5463; + pBiosData->MemSize = 640; + pBiosData->NextKbdCharPtr = 0x1e; + pBiosData->FirstKbdCharPtr = 0x1e; + pBiosData->VideoMode = 3; + pBiosData->VideoColumns = 80; + pBiosData->VideoPageSize = 80 * 25 * 2; + pBiosData->VideoPageStartAddr = 0xb800; + pBiosData->VideoCtrlAddr = 0x3d4; + pBiosData->Ticks = DOSMEM_GetTicksSinceMidnight(); + pBiosData->NbHardDisks = 2; + pBiosData->KbdBufferStart = 0x1e; + pBiosData->KbdBufferEnd = 0x3e; + pBiosData->RowsOnScreenMinus1 = 24; + pBiosData->BytesPerChar = 0x10; + pBiosData->ModeOptions = 0x64; + pBiosData->FeatureBitsSwitches = 0xf9; + pBiosData->VGASettings = 0x51; + pBiosData->DisplayCombination = 0x08; + pBiosData->DiskDataRate = 0; + + /* fill ROM configuration table (values from Award) */ + *(pBiosROMTable+0x0) = 0x08; /* number of bytes following LO */ + *(pBiosROMTable+0x1) = 0x00; /* number of bytes following HI */ + *(pBiosROMTable+0x2) = 0xfc; /* model */ + *(pBiosROMTable+0x3) = 0x01; /* submodel */ + *(pBiosROMTable+0x4) = 0x00; /* BIOS revision */ + *(pBiosROMTable+0x5) = 0x74; /* feature byte 1 */ + *(pBiosROMTable+0x6) = 0x00; /* feature byte 2 */ + *(pBiosROMTable+0x7) = 0x00; /* feature byte 3 */ + *(pBiosROMTable+0x8) = 0x00; /* feature byte 4 */ + *(pBiosROMTable+0x9) = 0x00; /* feature byte 5 */ + + /* BIOS date string */ + strcpy(pBiosSys+0xfff5, "13/01/99"); + + /* BIOS ID */ + *(pBiosSys+0xfffe) = 0xfc; + + /* Reboot vector (f000:fff0 or ffff:0000) */ + *(DWORD*)(pBiosSys + 0xfff0) = VM_STUB(0x19); +} + +/*********************************************************************** + * BiosTick + * + * Increment the BIOS tick counter. Called by timer signal handler. + */ +void BiosTick( WORD timer ) +{ + BIOSDATA *pBiosData = DOSVM_BiosData(); + if (pBiosData) pBiosData->Ticks++; +} + +/*********************************************************************** + * DOSMEM_AllocBlock + * + * Carve a chunk of the DOS memory block (without selector). + */ +LPVOID DOSMEM_AllocBlock(UINT size, UINT16* pseg) +{ + UINT blocksize; + char *block = NULL; + dosmem_info *info_block = DOSMEM_info_block; + dosmem_entry *dm; +#ifdef __DOSMEM_DEBUG_ + dosmem_entry *prev = NULL; +#endif + + if( size > info_block->free ) return NULL; + dm = DOSMEM_RootBlock(); + + while (dm && dm->size != DM_BLOCK_TERMINAL) + { +#ifdef __DOSMEM_DEBUG__ + if( (dm->size & DM_BLOCK_DEBUG) != DM_BLOCK_DEBUG ) + { + WARN("MCB overrun! [prev = 0x%08x]\n", 4 + (UINT)prev); + return NULL; + } + prev = dm; +#endif + if( dm->size & DM_BLOCK_FREE ) + { + dosmem_entry *next = next_block(dm); + + while ( next->size & DM_BLOCK_FREE ) /* collapse free blocks */ + { + dm->size += sizeof(dosmem_entry) + (next->size & DM_BLOCK_MASK); + next->size = (DM_BLOCK_FREE | DM_BLOCK_TERMINAL); + next = next_block(dm); + } + + blocksize = dm->size & DM_BLOCK_MASK; + if( blocksize >= size ) + { + block = ((char*)dm) + sizeof(dosmem_entry); + if( blocksize - size > 0x20 ) + { + /* split dm so that the next one stays + * paragraph-aligned (and dm loses free bit) */ + + dm->size = (((size + 0xf + sizeof(dosmem_entry)) & ~0xf) - + sizeof(dosmem_entry)); + next = (dosmem_entry*)(((char*)dm) + + sizeof(dosmem_entry) + dm->size); + next->size = (blocksize - (dm->size + + sizeof(dosmem_entry))) | DM_BLOCK_FREE +#ifdef __DOSMEM_DEBUG__ + | DM_BLOCK_DEBUG +#endif + ; + } else dm->size &= DM_BLOCK_MASK; + + info_block->blocks++; + info_block->free -= dm->size; + if( pseg ) *pseg = (block - DOSMEM_dosmem) >> 4; +#ifdef __DOSMEM_DEBUG__ + dm->size |= DM_BLOCK_DEBUG; +#endif + break; + } + dm = next; + } + else dm = next_block(dm); + } + return (LPVOID)block; +} + +/*********************************************************************** + * DOSMEM_FreeBlock + */ +BOOL DOSMEM_FreeBlock(void* ptr) +{ + dosmem_info *info_block = DOSMEM_info_block; + + if( ptr >= (void*)(((char*)DOSMEM_RootBlock()) + sizeof(dosmem_entry)) && + ptr < (void*)DOSMEM_MemoryTop() && !((((char*)ptr) + - DOSMEM_dosmem) & 0xf) ) + { + dosmem_entry *dm = (dosmem_entry*)(((char*)ptr) - sizeof(dosmem_entry)); + + if( !(dm->size & (DM_BLOCK_FREE | DM_BLOCK_TERMINAL)) +#ifdef __DOSMEM_DEBUG__ + && ((dm->size & DM_BLOCK_DEBUG) == DM_BLOCK_DEBUG ) +#endif + ) + { + info_block->blocks--; + info_block->free += dm->size; + + dm->size |= DM_BLOCK_FREE; + return TRUE; + } + } + return FALSE; +} + +/*********************************************************************** + * DOSMEM_ResizeBlock + * + * Resize DOS memory block in place. Returns block size or -1 on error. + * + * If exact is TRUE, returned value is either old or requested block + * size. If exact is FALSE, block is expanded even if there is not + * enough space for full requested block size. + */ +UINT DOSMEM_ResizeBlock(void *ptr, UINT size, BOOL exact) +{ + char *block = NULL; + dosmem_info *info_block = DOSMEM_info_block; + dosmem_entry *dm; + dosmem_entry *next; + UINT blocksize; + UINT orgsize; + + if( (ptr < (void*)(sizeof(dosmem_entry) + (char*)DOSMEM_RootBlock())) || + (ptr >= (void*)DOSMEM_MemoryTop()) || + (((((char*)ptr) - DOSMEM_dosmem) & 0xf) != 0) ) + return (UINT)-1; + + dm = (dosmem_entry*)(((char*)ptr) - sizeof(dosmem_entry)); + if( dm->size & (DM_BLOCK_FREE | DM_BLOCK_TERMINAL) ) + return (UINT)-1; + + next = next_block(dm); + orgsize = dm->size & DM_BLOCK_MASK; + + /* collapse free blocks */ + while ( next->size & DM_BLOCK_FREE ) + { + dm->size += sizeof(dosmem_entry) + (next->size & DM_BLOCK_MASK); + next->size = (DM_BLOCK_FREE | DM_BLOCK_TERMINAL); + next = next_block(dm); + } + + blocksize = dm->size & DM_BLOCK_MASK; + + /* + * If collapse didn't help we either expand block to maximum + * available size (exact == FALSE) or give collapsed blocks + * back to free storage (exact == TRUE). + */ + if (blocksize < size) + size = exact ? orgsize : blocksize; + + block = ((char*)dm) + sizeof(dosmem_entry); + if( blocksize - size > 0x20 ) + { + /* + * split dm so that the next one stays + * paragraph-aligned (and next gains free bit) + */ + + dm->size = (((size + 0xf + sizeof(dosmem_entry)) & ~0xf) - + sizeof(dosmem_entry)); + next = (dosmem_entry*)(((char*)dm) + + sizeof(dosmem_entry) + dm->size); + next->size = (blocksize - (dm->size + + sizeof(dosmem_entry))) | DM_BLOCK_FREE; + } + else + { + dm->size &= DM_BLOCK_MASK; + } + + /* + * Adjust available memory if block size changes. + */ + info_block->free += orgsize - dm->size; + + return size; +} + +/*********************************************************************** + * DOSMEM_Available + */ +UINT DOSMEM_Available(void) +{ + UINT blocksize, available = 0; + dosmem_entry *dm; + + dm = DOSMEM_RootBlock(); + + while (dm && dm->size != DM_BLOCK_TERMINAL) + { +#ifdef __DOSMEM_DEBUG__ + if( (dm->size & DM_BLOCK_DEBUG) != DM_BLOCK_DEBUG ) + { + WARN("MCB overrun! [prev = 0x%08x]\n", 4 + (UINT)prev); + return NULL; + } + prev = dm; +#endif + if( dm->size & DM_BLOCK_FREE ) + { + dosmem_entry *next = next_block(dm); + + while ( next->size & DM_BLOCK_FREE ) /* collapse free blocks */ + { + dm->size += sizeof(dosmem_entry) + (next->size & DM_BLOCK_MASK); + next->size = (DM_BLOCK_FREE | DM_BLOCK_TERMINAL); + next = next_block(dm); + } + + blocksize = dm->size & DM_BLOCK_MASK; + if ( blocksize > available ) available = blocksize; + dm = next; + } + else dm = next_block(dm); + } + return available; +} + +/*********************************************************************** + * DOSMEM_InitMemory + * + * Initialises the DOS memory structures. + */ +static void DOSMEM_InitMemory(char* addr) +{ + dosmem_entry* root_block; + dosmem_entry* dm; + + DOSMEM_FillBiosSegments(); + DOSMEM_FillIsrTable(); + + DOSMEM_info_block = (dosmem_info*)addr; + root_block = DOSMEM_RootBlock(); + root_block->size = DOSMEM_MemoryTop() - (((char*)root_block) + sizeof(dosmem_entry)); + + DOSMEM_info_block->blocks = 0; + DOSMEM_info_block->free = root_block->size; + + dm = next_block(root_block); + dm->size = DM_BLOCK_TERMINAL; + root_block->size |= DM_BLOCK_FREE +#ifdef __DOSMEM_DEBUG__ + | DM_BLOCK_DEBUG +#endif + ; + + TRACE("DOS conventional memory initialized, %d bytes free.\n", + DOSMEM_Available()); +} + +/****************************************************************** + * DOSMEM_InitDosMemory + * + * When WineDOS is loaded, initializes the current DOS memory layout. + */ +void DOSMEM_InitDosMemory(void) +{ + HMODULE16 hModule; + unsigned short sel; + LDT_ENTRY entry; + DWORD reserve; + + hModule = GetModuleHandle16("KERNEL"); + /* KERNEL.194: __F000H */ + sel = LOWORD(GetProcAddress16(hModule, (LPCSTR)(ULONG_PTR)194)); + wine_ldt_get_entry(sel, &entry); + DOSMEM_dosmem = (char*)wine_ldt_get_base(&entry) - 0xF0000; + /* KERNEL.183: __0000H */ + sel = LOWORD(GetProcAddress16(hModule, (LPCSTR)(DWORD_PTR)183)); + wine_ldt_get_entry(sel, &entry); + DOSMEM_sysmem = wine_ldt_get_base(&entry); + + /* + * Reserve either: + * - lowest 64k for NULL pointer catching (Win16) + * - lowest 1k for interrupt handlers and + * another 0.5k for BIOS, DOS and intra-application + * areas (DOS) + */ + if (DOSMEM_dosmem != DOSMEM_sysmem) + reserve = 0x10000; /* 64k */ + else + reserve = 0x600; /* 1.5k */ + + /* + * Round to paragraph boundary in order to make + * sure the alignment is correct. + */ + reserve = ((reserve + 15) >> 4) << 4; + + /* + * Set DOS memory base and initialize conventional memory. + */ + DOSMEM_InitMemory(DOSMEM_dosmem + reserve); +} + +/****************************************************************** + * DOSMEM_MapDosLayout + * + * Initialize the first MB of memory to look like a real DOS setup + */ +BOOL DOSMEM_MapDosLayout(void) +{ + static int already_mapped; + + if (!already_mapped) + { + HMODULE16 hModule; + unsigned short sel; + LDT_ENTRY entry; + + if (DOSMEM_dosmem) + { + ERR( "Needs access to the first megabyte for DOS mode\n" ); + ExitProcess(1); + } + MESSAGE( "Warning: unprotecting memory to allow real-mode calls.\n" + " NULL pointer accesses will no longer be caught.\n" ); + VirtualProtect( NULL, DOSMEM_SIZE, PAGE_EXECUTE_READWRITE, NULL ); + /* copy the BIOS and ISR area down */ + memcpy( DOSMEM_dosmem, DOSMEM_sysmem, 0x400 + 0x100 ); + DOSMEM_sysmem = DOSMEM_dosmem; + hModule = GetModuleHandle16("KERNEL"); + /* selector to 0000H */ + sel = LOWORD(GetProcAddress16(hModule, (LPCSTR)(DWORD_PTR)183)); + wine_ldt_get_entry(sel, &entry); + wine_ldt_set_base(&entry, NULL); + wine_ldt_set_entry(sel, &entry); + /* selector to BiosData */ + sel = LOWORD(GetProcAddress16(hModule, (LPCSTR)(DWORD_PTR)193)); + wine_ldt_get_entry(sel, &entry); + wine_ldt_set_base(&entry, (const void*)0x400); + wine_ldt_set_entry(sel, &entry); + /* we may now need the actual interrupt stubs, and since we've just moved the + * interrupt vector table away, we can fill the area with stubs instead... */ + DOSMEM_MakeIsrStubs(); + already_mapped = 1; + } + return TRUE; +} diff --git a/dlls/winedos/dosvm.c b/dlls/winedos/dosvm.c index b4046b25088..9adf28ed940 100644 --- a/dlls/winedos/dosvm.c +++ b/dlls/winedos/dosvm.c @@ -693,26 +693,6 @@ void WINAPI DOSVM_AcknowledgeIRQ( CONTEXT86 *context ) } -/********************************************************************** - * DOSVM_BiosData - * - * Get pointer to BIOS data area. This is not at fixed location - * because those Win16 programs that do not use any real mode code have - * protected NULL pointer catching block at low linear memory and - * BIOS data has been moved to another location. - */ -BIOSDATA *DOSVM_BiosData( void ) -{ - LDT_ENTRY entry; - FARPROC16 proc; - - proc = GetProcAddress16( GetModuleHandle16( "KERNEL" ), - (LPCSTR)(ULONG_PTR)193 ); - wine_ldt_get_entry( LOWORD(proc), &entry ); - return (BIOSDATA *)wine_ldt_get_base( &entry ); -} - - /********************************************************************** * DllMain (DOSVM.0) */ @@ -723,6 +703,7 @@ BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved ) if (fdwReason == DLL_PROCESS_ATTACH) { DisableThreadLibraryCalls(hinstDLL); + DOSMEM_InitDosMemory(); DOSVM_InitSegments(); event_notifier = CreateEventW(NULL, FALSE, FALSE, NULL); diff --git a/dlls/winedos/int21.c b/dlls/winedos/int21.c index d1abc16e39c..f08b1bc7828 100644 --- a/dlls/winedos/int21.c +++ b/dlls/winedos/int21.c @@ -4904,7 +4904,7 @@ void WINAPI DOSVM_Int21Handler( CONTEXT86 *context ) selector = LOWORD( rv ); } else - DOSMEM_GetBlock( bytes, &selector ); + DOSMEM_AllocBlock( bytes, &selector ); if (selector) { @@ -4934,7 +4934,7 @@ void WINAPI DOSVM_Int21Handler( CONTEXT86 *context ) context->SegEs = 0; } else - ok = DOSMEM_FreeBlock( (void*)((DWORD)context->SegEs << 4) ); + ok = DOSMEM_FreeBlock( PTR_REAL_TO_LIN(context->SegEs, 0) ); if (!ok) { diff --git a/dlls/winedos/int31.c b/dlls/winedos/int31.c index 564558c9c72..94b6a31bebc 100644 --- a/dlls/winedos/int31.c +++ b/dlls/winedos/int31.c @@ -470,7 +470,7 @@ callrmproc_again: if (!already) { if (!context->SegSs) { alloc = 1; /* allocate default stack */ - stack16 = addr = DOSMEM_GetBlock( 64, (UINT16 *)&(context->SegSs) ); + stack16 = addr = DOSMEM_AllocBlock( 64, (UINT16 *)&(context->SegSs) ); context->Esp = 64-2; stack16 += 32-1; if (!addr) { @@ -649,7 +649,7 @@ static RMCB *DPMI_AllocRMCB( void ) if (NewRMCB) { - LPVOID RMCBmem = DOSMEM_GetBlock(4, &uParagraph); + LPVOID RMCBmem = DOSMEM_AllocBlock(4, &uParagraph); LPBYTE p = RMCBmem; *p++ = 0xcd; /* RMCB: */ @@ -968,7 +968,7 @@ void WINAPI DOSVM_Int31Handler( CONTEXT86 *context ) /* check if Win16 app wants to access lower 64K of DOS memory */ if (base < 0x10000 && DOSVM_IsWin16()) - DOSMEM_InitDosMem(); + DOSMEM_MapDosLayout(); SetSelectorBase( sel, base ); } diff --git a/dlls/winedos/module.c b/dlls/winedos/module.c index 50a989546af..1bbc1c28d6d 100644 --- a/dlls/winedos/module.c +++ b/dlls/winedos/module.c @@ -169,7 +169,7 @@ static WORD MZ_InitEnvironment( LPCSTR env, LPCSTR name ) while (env[sz++]) sz+=strlen(env+sz)+1; } else sz++; /* allocate it */ - envblk=DOSMEM_GetBlock(sz+sizeof(WORD)+strlen(name)+1,&seg); + envblk=DOSMEM_AllocBlock(sz+sizeof(WORD)+strlen(name)+1,&seg); /* fill it */ if (env) { memcpy(envblk,env,sz); @@ -195,7 +195,7 @@ static BOOL MZ_InitMemory(void) { /* initialize the memory */ TRACE("Initializing DOS memory structures\n"); - DOSMEM_InitDosMem(); + DOSMEM_MapDosLayout(); DOSDEV_InstallDOSDevices(); return TRUE; @@ -283,7 +283,7 @@ static BOOL MZ_DoLoadImage( HANDLE hFile, LPCSTR filename, OverlayBlock *oblk, W goto load_error; } if (avail>max_size) avail=max_size; - psp_start=DOSMEM_GetBlock(avail,&DOSVM_psp); + psp_start=DOSMEM_AllocBlock(avail,&DOSVM_psp); if (!psp_start) { ERR("error allocating DOS memory\n"); SetLastError(ERROR_NOT_ENOUGH_MEMORY); diff --git a/dlls/winedos/winedos.spec b/dlls/winedos/winedos.spec index 6f9ca78f16f..98f9048d44c 100644 --- a/dlls/winedos/winedos.spec +++ b/dlls/winedos/winedos.spec @@ -6,3 +6,11 @@ # I/O functions @ stdcall inport(long long) DOSVM_inport @ stdcall outport(long long long) DOSVM_outport + +# DOS memory functions +@ cdecl FreeDosBlock(ptr) DOSMEM_FreeBlock +@ cdecl AllocDosBlock(long ptr) DOSMEM_AllocBlock +@ cdecl ResizeDosBlock(ptr long long) DOSMEM_ResizeBlock + +# BIOS functions +@ cdecl BiosTick(long) diff --git a/include/miscemu.h b/include/miscemu.h index 3f2e73c3486..4770df6419a 100644 --- a/include/miscemu.h +++ b/include/miscemu.h @@ -26,83 +26,5 @@ #include /* msdos/dosmem.c */ -#include - -typedef struct -{ - WORD Com1Addr; /* 00: COM1 I/O address */ - WORD Com2Addr; /* 02: COM2 I/O address */ - WORD Com3Addr; /* 04: COM3 I/O address */ - WORD Com4Addr; /* 06: COM4 I/O address */ - WORD Lpt1Addr; /* 08: LPT1 I/O address */ - WORD Lpt2Addr; /* 0a: LPT2 I/O address */ - WORD Lpt3Addr; /* 0c: LPT3 I/O address */ - WORD Lpt4Addr; /* 0e: LPT4 I/O address */ - WORD InstalledHardware; /* 10: Installed hardware flags */ - BYTE POSTstatus; /* 12: Power-On Self Test status */ - WORD MemSize; /* 13: Base memory size in Kb */ - WORD unused1; /* 15: Manufacturing test scratch pad */ - BYTE KbdFlags1; /* 17: Keyboard flags 1 */ - BYTE KbdFlags2; /* 18: Keyboard flags 2 */ - BYTE unused2; /* 19: Keyboard driver workspace */ - WORD NextKbdCharPtr; /* 1a: Next character in kbd buffer */ - WORD FirstKbdCharPtr; /* 1c: First character in kbd buffer */ - WORD KbdBuffer[16]; /* 1e: Keyboard buffer */ - BYTE DisketteStatus1; /* 3e: Diskette recalibrate status */ - BYTE DisketteStatus2; /* 3f: Diskette motor status */ - BYTE DisketteStatus3; /* 40: Diskette motor timeout */ - BYTE DisketteStatus4; /* 41: Diskette last operation status */ - BYTE DiskStatus[7]; /* 42: Disk status/command bytes */ - BYTE VideoMode; /* 49: Video mode */ - WORD VideoColumns; /* 4a: Number of columns */ - WORD VideoPageSize; /* 4c: Video page size in bytes */ - WORD VideoPageStartAddr; /* 4e: Video page start address */ - BYTE VideoCursorPos[16]; /* 50: Cursor position for 8 pages, column/row order */ - WORD VideoCursorType; /* 60: Video cursor type */ - BYTE VideoCurPage; /* 62: Video current page */ - WORD VideoCtrlAddr; /* 63: Video controller address */ - BYTE VideoReg1; /* 65: Video mode select register */ - BYTE VideoReg2; /* 66: Video CGA palette register */ - DWORD ResetEntry; /* 67: Warm reset entry point */ - BYTE LastIRQ; /* 6b: Last unexpected interrupt */ - DWORD Ticks; /* 6c: Ticks since midnight */ - BYTE TicksOverflow; /* 70: Timer overflow if past midnight */ - BYTE CtrlBreakFlag; /* 71: Ctrl-Break flag */ - WORD ResetFlag; /* 72: POST Reset flag */ - BYTE DiskOpStatus; /* 74: Last hard-disk operation status */ - BYTE NbHardDisks; /* 75: Number of hard disks */ - BYTE DiskCtrlByte; /* 76: Disk control byte */ - BYTE DiskIOPort; /* 77: Disk I/O port offset */ - BYTE LptTimeout[4]; /* 78: Timeouts for parallel ports */ - BYTE ComTimeout[4]; /* 7c: Timeouts for serial ports */ - WORD KbdBufferStart; /* 80: Keyboard buffer start */ - WORD KbdBufferEnd; /* 82: Keyboard buffer end */ - BYTE RowsOnScreenMinus1; /* 84: EGA only */ - WORD BytesPerChar; /* 85: EGA only */ - BYTE ModeOptions; /* 87: EGA only */ - BYTE FeatureBitsSwitches; /* 88: EGA only */ - BYTE VGASettings; /* 89: VGA misc settings */ - BYTE DisplayCombination; /* 8A: VGA display combinations */ - BYTE DiskDataRate; /* 8B: Last disk data rate selected */ -} BIOSDATA; - -#include - -extern WORD DOSMEM_0000H; -extern WORD DOSMEM_BiosDataSeg; -extern WORD DOSMEM_BiosSysSeg; - -/* msdos/dosmem.c */ -extern BOOL DOSMEM_Init(void); -extern BOOL DOSMEM_InitDosMem(void); -extern void DOSMEM_Tick(WORD timer); -extern WORD DOSMEM_AllocSelector(WORD); -extern LPVOID DOSMEM_GetBlock(UINT size, WORD* p); -extern BOOL DOSMEM_FreeBlock(void* ptr); -extern UINT DOSMEM_ResizeBlock(void* ptr, UINT size, BOOL exact); -extern UINT DOSMEM_Available(void); -extern LPVOID DOSMEM_MapRealToLinear(DWORD); /* real-mode to linear */ -extern LPVOID DOSMEM_MapDosToLinear(UINT); /* linear DOS to Wine */ -extern UINT DOSMEM_MapLinearToDos(LPVOID); /* linear Wine to DOS */ #endif /* __WINE_MISCEMU_H */