Commit 16df50ef authored by Eric Pouech's avatar Eric Pouech Committed by Alexandre Julliard

Moved DOS memory handling to winedos.

parent e6267369
......@@ -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);
/***********************************************************************
* DOSMEM_MemoryTop
*
* Gets the DOS memory top.
*/
static char *DOSMEM_MemoryTop(void)
{
return DOSMEM_dosmem+0x9FFFC; /* 640K */
}
/***********************************************************************
* 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.
*/
GetLocalTime( &time );
static LONG WINAPI dosmem_handler(EXCEPTION_POINTERS* except);
return (((time.wHour * 3600 + time.wMinute * 60 +
time.wSecond) * 18206) / 1000) +
(time.wMilliseconds * 1000 / 54927);
}
struct winedos_exports winedos;
/***********************************************************************
* DOSMEM_FillBiosSegments
*
* Fill the BIOS data segment with dummy values.
*/
static void DOSMEM_FillBiosSegments(void)
void load_winedos(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);
}
static HANDLE hRunOnce /* = 0 */;
static HMODULE hWineDos /* = 0 */;
/***********************************************************************
* DOSMEM_InitMemory
*
* Initialises the DOS memory structures.
/* FIXME: this isn't 100% thread safe, as we won't catch access to 1MB while
* loading winedos (and may return uninitialized valued)
*/
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 (hWineDos) return;
if (hRunOnce == 0)
{
HANDLE hEvent = CreateEventW( NULL, TRUE, FALSE, NULL );
if (InterlockedCompareExchangePointer( (PVOID)&hRunOnce, hEvent, 0 ) == 0)
{
HMODULE hModule;
if (!bios_created)
/* ok, we're the winning thread */
VirtualProtect( DOSMEM_dosmem + DOSMEM_protect,
DOSMEM_SIZE - DOSMEM_protect,
PAGE_EXECUTE_READWRITE, NULL );
if (!(hModule = LoadLibraryA( "winedos.dll" )))
{
DOSMEM_FillBiosSegments();
DOSMEM_FillIsrTable();
bios_created = TRUE;
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,
DOSMEM_BiosDataSeg = GLOBAL_CreateBlock( GMEM_FIXED, sysmem + 0x400,
0x100, 0, WINE_LDT_FLAGS_DATA );
DOSMEM_BiosSysSeg = GLOBAL_CreateBlock(GMEM_FIXED,DOSMEM_dosmem + 0xf0000,
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
*
......
......@@ -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)<globalArenaSize)
#define GET_ARENA_PTR(handle) (pGlobalArena + ((handle) >> __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 )
{
......
......@@ -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 <XX> */
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 );
}
......
......@@ -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)
......
......@@ -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
......@@ -14,6 +14,7 @@ C_SRCS = \
dma.c \
dosaspi.c \
dosconf.c \
dosmem.c \
dosvm.c \
fpu.c \
himem.c \
......
......@@ -24,11 +24,11 @@
#include <stdarg.h>
#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 <pshpack1.h>
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 <poppack.h>
/* 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*);
......
/*
* 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 <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#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;
}
......@@ -694,26 +694,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)
*/
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
......@@ -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);
......
......@@ -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)
{
......
......@@ -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 );
}
......
......@@ -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);
......
......@@ -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)
......@@ -26,83 +26,5 @@
#include <wine/windef16.h>
/* msdos/dosmem.c */
#include <pshpack1.h>
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 <poppack.h>
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 */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment