Moved DOS memory handling to winedos.
This commit is contained in:
parent
e6267369b2
commit
16df50efbe
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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*);
|
||||
|
|
|
@ -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 <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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue