dosmem.c 25.4 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3 4
/*
 * DOS memory emulation
 *
 * Copyright 1995 Alexandre Julliard
Alexandre Julliard's avatar
Alexandre Julliard committed
5
 * Copyright 1996 Marcus Meissner
Alexandre Julliard's avatar
Alexandre Julliard committed
6 7
 */

8 9
#include "config.h"

Alexandre Julliard's avatar
Alexandre Julliard committed
10 11
#include <signal.h>
#include <stdlib.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
12
#include <string.h>
13
#include <sys/types.h>
14 15 16 17
#ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#endif

Alexandre Julliard's avatar
Alexandre Julliard committed
18
#include "winbase.h"
19
#include "wine/winbase16.h"
20
#include "wine/port.h"
21

Alexandre Julliard's avatar
Alexandre Julliard committed
22
#include "global.h"
23
#include "selectors.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
24
#include "miscemu.h"
25
#include "vga.h"
26
#include "dosexe.h"
27
#include "debugtools.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
28

29 30
DEFAULT_DEBUG_CHANNEL(dosmem);
DECLARE_DEBUG_CHANNEL(selector);
31

32 33 34
WORD DOSMEM_0000H;        /* segment at 0:0 */
WORD DOSMEM_BiosDataSeg;  /* BIOS data segment at 0x40:0 */
WORD DOSMEM_BiosSysSeg;   /* BIOS ROM segment at 0xf000:0 */
Alexandre Julliard's avatar
Alexandre Julliard committed
35

36
DWORD DOSMEM_CollateTable;
Alexandre Julliard's avatar
Alexandre Julliard committed
37

Alexandre Julliard's avatar
Alexandre Julliard committed
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
/* 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))
Alexandre Julliard's avatar
Alexandre Julliard committed
61

Alexandre Julliard's avatar
Alexandre Julliard committed
62 63 64
#define VM_STUB(x) (0x90CF00CD|(x<<8)) /* INT x; IRET; NOP */
#define VM_STUB_SEGMENT 0xf000         /* BIOS segment */

65 66
/* DOS memory base */
static char *DOSMEM_dosmem;
67 68 69 70 71
/* 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;
72 73 74 75 76 77 78

/* various real-mode code stubs */
WORD DOSMEM_wrap_seg;
WORD DOSMEM_xms_seg;
WORD DOSMEM_dpmi_seg;
WORD DOSMEM_dpmi_sel;

79 80 81 82 83 84 85 86 87 88
/***********************************************************************
 *           DOSMEM_SystemBase
 *
 * Gets the virtual DOS memory base (interrupt table).
 */
char *DOSMEM_SystemBase(void)
{
    return DOSMEM_sysmem;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
89 90 91 92 93
/***********************************************************************
 *           DOSMEM_MemoryBase
 *
 * Gets the DOS memory base.
 */
94
char *DOSMEM_MemoryBase(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
95
{
96
    return DOSMEM_dosmem;
Alexandre Julliard's avatar
Alexandre Julliard committed
97
}
Alexandre Julliard's avatar
Alexandre Julliard committed
98

Alexandre Julliard's avatar
Alexandre Julliard committed
99 100 101 102 103
/***********************************************************************
 *           DOSMEM_MemoryTop
 *
 * Gets the DOS memory top.
 */
104
static char *DOSMEM_MemoryTop(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
105
{
106
    return DOSMEM_dosmem+0x9FFFC; /* 640K */
Alexandre Julliard's avatar
Alexandre Julliard committed
107
}
Alexandre Julliard's avatar
Alexandre Julliard committed
108 109 110 111 112 113

/***********************************************************************
 *           DOSMEM_InfoBlock
 *
 * Gets the DOS memory info block.
 */
114
static dosmem_info *DOSMEM_InfoBlock(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
115
{
116
    return (dosmem_info*)(DOSMEM_dosmem+0x10000); /* 64K */
Alexandre Julliard's avatar
Alexandre Julliard committed
117 118 119 120 121 122 123
}

/***********************************************************************
 *           DOSMEM_RootBlock
 *
 * Gets the DOS memory root block.
 */
124
static dosmem_entry *DOSMEM_RootBlock(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
125 126
{
    /* first block has to be paragraph-aligned */
127
    return (dosmem_entry*)(((char*)DOSMEM_InfoBlock()) +
Alexandre Julliard's avatar
Alexandre Julliard committed
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
                           ((((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.
 */
143
static void DOSMEM_FillIsrTable(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
144
{
145
    SEGPTR *isr = (SEGPTR*)DOSMEM_sysmem;
Alexandre Julliard's avatar
Alexandre Julliard committed
146 147
    int x;
 
148
    for (x=0; x<256; x++) isr[x]=MAKESEGPTR(VM_STUB_SEGMENT,x*4);
149 150 151 152 153 154 155
} 

static void DOSMEM_MakeIsrStubs(void)
{
    DWORD *stub = (DWORD*)(DOSMEM_dosmem + (VM_STUB_SEGMENT << 4));
    int x;
 
Alexandre Julliard's avatar
Alexandre Julliard committed
156 157 158
    for (x=0; x<256; x++) stub[x]=VM_STUB(x);
} 

159 160 161 162 163 164 165
/***********************************************************************
 *           DOSMEM_InitDPMI
 *
 * Allocate the global DPMI RMCB wrapper.
 */
static void DOSMEM_InitDPMI(void)
{
166 167 168
    LPSTR ptr;

    static const char wrap_code[]={
169 170 171 172
     0xCD,0x31, /* int $0x31 */
     0xCB       /* lret */
    };

173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
    static const char enter_xms[]=
    {
        /* XMS hookable entry point */
        0xEB,0x03,           /* jmp entry */
        0x90,0x90,0x90,      /* nop;nop;nop */
                             /* entry: */
        /* real entry point */
        /* for simplicity, we'll just use the same hook as DPMI below */
        0xCD,0x31,           /* int $0x31 */
        0xCB                 /* lret */
    };

    static const char enter_pm[]=
    {
        0x50,                /* pushw %ax */
        0x52,                /* pushw %dx */
        0x55,                /* pushw %bp */
        0x89,0xE5,           /* movw %sp,%bp */
        /* get return CS */
        0x8B,0x56,0x08,      /* movw 8(%bp),%dx */
        /* just call int 31 here to get into protected mode... */
        /* it'll check whether it was called from dpmi_seg... */
        0xCD,0x31,           /* int $0x31 */
        /* we are now in the context of a 16-bit relay call */
        /* need to fixup our stack;
         * 16-bit relay return address will be lost, but we won't worry quite yet */
        0x8E,0xD0,           /* movw %ax,%ss */
        0x66,0x0F,0xB7,0xE5, /* movzwl %bp,%esp */
        /* set return CS */
        0x89,0x56,0x08,      /* movw %dx,8(%bp) */
        0x5D,                /* popw %bp */
        0x5A,                /* popw %dx */
        0x58,                /* popw %ax */
        0xCB                 /* lret */
    };

    ptr = DOSMEM_GetBlock( sizeof(wrap_code), &DOSMEM_wrap_seg );
    memcpy( ptr, wrap_code, sizeof(wrap_code) );
    ptr = DOSMEM_GetBlock( sizeof(enter_xms), &DOSMEM_xms_seg );
    memcpy( ptr, enter_xms, sizeof(enter_xms) );
    ptr = DOSMEM_GetBlock( sizeof(enter_pm), &DOSMEM_dpmi_seg );
    memcpy( ptr, enter_pm, sizeof(enter_pm) );
215
    DOSMEM_dpmi_sel = SELECTOR_AllocBlock( ptr, sizeof(enter_pm), WINE_LDT_FLAGS_CODE );
216 217
}

218 219
BIOSDATA * DOSMEM_BiosData()
{
220
    return (BIOSDATA *)(DOSMEM_sysmem + 0x400);
221 222 223 224
}

BYTE * DOSMEM_BiosSys()
{
225
    return DOSMEM_dosmem+0xf0000;
226 227
}

228 229 230
struct _DOS_LISTOFLISTS * DOSMEM_LOL()
{
    return (struct _DOS_LISTOFLISTS *)DOSMEM_MapRealToLinear
231
      (MAKESEGPTR(HIWORD(DOS_LOLSeg),0));
232 233
}

Alexandre Julliard's avatar
Alexandre Julliard committed
234
/***********************************************************************
235
 *           DOSMEM_FillBiosSegments
Alexandre Julliard's avatar
Alexandre Julliard committed
236
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
237
 * Fill the BIOS data segment with dummy values.
Alexandre Julliard's avatar
Alexandre Julliard committed
238
 */
239
static void DOSMEM_FillBiosSegments(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
240
{
241
    BYTE *pBiosSys = DOSMEM_BiosSys();
242
    BYTE *pBiosROMTable = pBiosSys+0xe6f5;
243
    BIOSDATA *pBiosData = DOSMEM_BiosData();
244

245
    /* bogus 0xe0xx addresses !! Adapt int 0x10/0x1b if change needed */
246 247
    VIDEOFUNCTIONALITY *pVidFunc = (VIDEOFUNCTIONALITY *)(pBiosSys+0xe000);
    VIDEOSTATE *pVidState = (VIDEOSTATE *)(pBiosSys+0xe010);
248
    int i;
Alexandre Julliard's avatar
Alexandre Julliard committed
249 250 251

      /* Clear all unused values */
    memset( pBiosData, 0, sizeof(*pBiosData) );
252 253
    memset( pVidFunc,  0, sizeof(*pVidFunc ) );
    memset( pVidState, 0, sizeof(*pVidState) );
Alexandre Julliard's avatar
Alexandre Julliard committed
254 255

    /* FIXME: should check the number of configured drives and ports */
Alexandre Julliard's avatar
Alexandre Julliard committed
256

257 258
    pBiosData->Com1Addr             = 0x3f8;
    pBiosData->Com2Addr             = 0x2f8;
Alexandre Julliard's avatar
Alexandre Julliard committed
259 260
    pBiosData->Lpt1Addr             = 0x378;
    pBiosData->Lpt2Addr             = 0x278;
261
    pBiosData->InstalledHardware    = 0x5463;
Alexandre Julliard's avatar
Alexandre Julliard committed
262 263 264
    pBiosData->MemSize              = 640;
    pBiosData->NextKbdCharPtr       = 0x1e;
    pBiosData->FirstKbdCharPtr      = 0x1e;
265
    pBiosData->VideoMode            = 3;
Alexandre Julliard's avatar
Alexandre Julliard committed
266 267 268 269 270 271 272 273
    pBiosData->VideoColumns         = 80;
    pBiosData->VideoPageSize        = 80 * 25 * 2;
    pBiosData->VideoPageStartAddr   = 0xb800;
    pBiosData->VideoCtrlAddr        = 0x3d4;
    pBiosData->Ticks                = INT1A_GetTicksSinceMidnight();
    pBiosData->NbHardDisks          = 2;
    pBiosData->KbdBufferStart       = 0x1e;
    pBiosData->KbdBufferEnd         = 0x3e;
274 275 276 277
    pBiosData->RowsOnScreenMinus1   = 23;
    pBiosData->BytesPerChar         = 0x10;
    pBiosData->ModeOptions          = 0x64;
    pBiosData->FeatureBitsSwitches  = 0xf9;
278
    pBiosData->VGASettings          = 0x51;
279 280
    pBiosData->DisplayCombination   = 0x08;
    pBiosData->DiskDataRate         = 0;
281 282

    /* fill ROM configuration table (values from Award) */
283 284
    *(pBiosROMTable+0x0)	= 0x08; /* number of bytes following LO */
    *(pBiosROMTable+0x1)	= 0x00; /* number of bytes following HI */
285 286 287 288 289 290 291 292 293
    *(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 */

294
    
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
    for (i = 0; i < 7; i++)
        pVidFunc->ModeSupport[i] = 0xff;
    
    pVidFunc->ScanlineSupport     = 7;
    pVidFunc->NumberCharBlocks    = 0;
    pVidFunc->ActiveCharBlocks    = 0;
    pVidFunc->MiscFlags           = 0x8ff;
    pVidFunc->SavePointerFlags    = 0x3f;

    pVidState->StaticFuncTable    = 0xf000e000;  /* FIXME: always real mode ? */
    pVidState->VideoMode          = pBiosData->VideoMode; /* needs updates! */
    pVidState->NumberColumns      = pBiosData->VideoColumns; /* needs updates! */
    pVidState->RegenBufLen        = 0;
    pVidState->RegenBufAddr       = 0;

    for (i = 0; i < 8; i++)
        pVidState->CursorPos[i] = 0;

    pVidState->CursorType         = 0x0a0b;  /* start/end line */
    pVidState->ActivePage         = 0;
    pVidState->CRTCPort           = 0x3da;
    pVidState->Port3x8            = 0;
    pVidState->Port3x9            = 0;
    pVidState->NumberRows         = 23;     /* number of rows - 1 */
    pVidState->BytesPerChar       = 0x10;
    pVidState->DCCActive          = pBiosData->DisplayCombination;
    pVidState->DCCAlternate       = 0;
    pVidState->NumberColors       = 16;
    pVidState->NumberPages        = 1;
    pVidState->NumberScanlines    = 3; /* (0,1,2,3) = (200,350,400,480) */
    pVidState->CharBlockPrimary   = 0;
    pVidState->CharBlockSecondary = 0;
    pVidState->MiscFlags =
                           (pBiosData->VGASettings & 0x0f)
                         | ((pBiosData->ModeOptions & 1) << 4); /* cursor emulation */
    pVidState->NonVGASupport      = 0;
    pVidState->VideoMem           = (pBiosData->ModeOptions & 0x60 >> 5);
    pVidState->SavePointerState   = 0;
    pVidState->DisplayStatus      = 4;
334

335 336 337 338 339
    /* BIOS date string */
    strcpy((char *)pBiosSys+0xfff5, "13/01/99");

    /* BIOS ID */
    *(pBiosSys+0xfffe) = 0xfc;
Alexandre Julliard's avatar
Alexandre Julliard committed
340 341 342 343 344 345 346
}

/***********************************************************************
 *           DOSMEM_InitCollateTable
 *
 * Initialises the collate table (character sorting, language dependent)
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
347 348 349 350 351 352
static void DOSMEM_InitCollateTable()
{
	DWORD		x;
	unsigned char	*tbl;
	int		i;

353
	x = GlobalDOSAlloc16(258);
Alexandre Julliard's avatar
Alexandre Julliard committed
354 355
	DOSMEM_CollateTable = MAKELONG(0,(x>>16));
	tbl = DOSMEM_MapRealToLinear(DOSMEM_CollateTable);
Alexandre Julliard's avatar
Alexandre Julliard committed
356
	*(WORD*)tbl	= 0x100;
Alexandre Julliard's avatar
Alexandre Julliard committed
357 358
	tbl += 2;
	for ( i = 0; i < 0x100; i++) *tbl++ = i;
Alexandre Julliard's avatar
Alexandre Julliard committed
359 360
}

Alexandre Julliard's avatar
Alexandre Julliard committed
361 362 363 364 365 366 367
/***********************************************************************
 *           DOSMEM_InitErrorTable
 *
 * Initialises the error tables (DOS 5+)
 */
static void DOSMEM_InitErrorTable()
{
368
#if 0  /* no longer used */
Alexandre Julliard's avatar
Alexandre Julliard committed
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
	DWORD		x;
	char 		*call;

        /* We will use a snippet of real mode code that calls */
        /* a WINE-only interrupt to handle moving the requested */
        /* message into the buffer... */

        /* FIXME - There is still something wrong... */

        /* FIXME - Find hex values for opcodes...
           
           (On call, AX contains message number
                     DI contains 'offset' (??)
            Resturn, ES:DI points to counted string )

           PUSH BX
           MOV BX, AX
           MOV AX, (arbitrary subfunction number)
           INT (WINE-only interrupt)
           POP BX
           RET

        */
           
        const int	code = 4;	
        const int	buffer = 80; 
        const int 	SIZE_TO_ALLOCATE = code + buffer;

        /* FIXME - Complete rewrite of the table system to save */
        /* precious DOS space. Now, we return the 0001:???? as */
        /* DOS 4+ (??, it seems to be the case in MS 7.10) treats that */
        /* as a special case and programs will use the alternate */
        /* interface (a farcall returned with INT 24 (AX = 0x122e, DL = */
        /* 0x08) which lets us have a smaller memory footprint anyway. */
 
404
 	x = GlobalDOSAlloc16(SIZE_TO_ALLOCATE);  
Alexandre Julliard's avatar
Alexandre Julliard committed
405 406 407 408 409 410 411

	DOSMEM_ErrorCall = MAKELONG(0,(x>>16));
        DOSMEM_ErrorBuffer = DOSMEM_ErrorCall + code;

	call = DOSMEM_MapRealToLinear(DOSMEM_ErrorCall);

        memset(call, 0, SIZE_TO_ALLOCATE);
412
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
413 414 415
        /* Fixme - Copy assembly into buffer here */        
}

Alexandre Julliard's avatar
Alexandre Julliard committed
416 417 418 419 420
/***********************************************************************
 *           DOSMEM_InitMemory
 *
 * Initialises the DOS memory structures.
 */
421
static void DOSMEM_InitMemory(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
422 423 424 425
{
   /* Low 64Kb are reserved for DOS/BIOS so the useable area starts at
    * 1000:0000 and ends at 9FFF:FFEF. */

426 427
    dosmem_info*        info_block = DOSMEM_InfoBlock();
    dosmem_entry*       root_block = DOSMEM_RootBlock();
Alexandre Julliard's avatar
Alexandre Julliard committed
428 429
    dosmem_entry*       dm;

430
    root_block->size = DOSMEM_MemoryTop() - (((char*)root_block) + sizeof(dosmem_entry));
Alexandre Julliard's avatar
Alexandre Julliard committed
431 432 433 434 435 436 437 438

    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__
Andreas Mohr's avatar
Andreas Mohr committed
439
		     | DM_BLOCK_DEBUG
Alexandre Julliard's avatar
Alexandre Julliard committed
440 441 442
#endif
		     ;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
443

444 445
/**********************************************************************
 *		setup_dos_mem
446
 *
447
 * Setup the first megabyte for DOS memory access
448
 */
449
static void setup_dos_mem( int dos_init )
450
{
451
    int sys_offset = 0;
452 453 454
    int page_size = getpagesize();
    void *addr = wine_anon_mmap( (void *)page_size, 0x110000-page_size,
                                 PROT_READ | PROT_WRITE | PROT_EXEC, 0 );
455 456 457
    if (addr == (void *)page_size)  /* we got what we wanted */
    {
        /* now map from address 0 */
458
        addr = wine_anon_mmap( NULL, 0x110000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED );
459 460 461 462 463 464 465 466 467 468 469 470 471
        if (addr)
        {
            ERR("MAP_FIXED failed at address 0 for DOS address space\n" );
            ExitProcess(1);
        }

        /* inform the memory manager that there is a mapping here */
        VirtualAlloc( addr, 0x110000, MEM_RESERVE | MEM_SYSTEM, PAGE_EXECUTE_READWRITE );

        /* protect the first 64K to catch NULL pointers */
        if (!dos_init)
        {
            VirtualProtect( addr, 0x10000, PAGE_NOACCESS, NULL );
472 473
            /* move the BIOS and ISR area from 0x00000 to 0xf0000 */
            sys_offset += 0xf0000;
474 475 476 477 478 479 480 481 482 483 484 485 486
        }
    }
    else
    {
        ERR("Cannot use first megabyte for DOS address space, please report\n" );
        if (dos_init) ExitProcess(1);
        /* allocate the DOS area somewhere else */
        addr = VirtualAlloc( NULL, 0x110000, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
        if (!addr)
        {
            ERR( "Cannot allocate DOS memory\n" );
            ExitProcess(1);
        }
487
    }
488
    DOSMEM_dosmem = addr;
489
    DOSMEM_sysmem = (char*)addr + sys_offset;
490 491
}

492

Alexandre Julliard's avatar
Alexandre Julliard committed
493 494 495 496
/***********************************************************************
 *           DOSMEM_Init
 *
 * Create the dos memory segments, and store them into the KERNEL
Alexandre Julliard's avatar
Alexandre Julliard committed
497
 * exported values.
Alexandre Julliard's avatar
Alexandre Julliard committed
498
 */
499
BOOL DOSMEM_Init(BOOL dos_init)
Alexandre Julliard's avatar
Alexandre Julliard committed
500
{
501
    static int already_done, already_mapped;
502

503
    if (!already_done)
Alexandre Julliard's avatar
Alexandre Julliard committed
504
    {
505
        setup_dos_mem( dos_init );
506

507
        DOSMEM_0000H = GLOBAL_CreateBlock( GMEM_FIXED, DOSMEM_sysmem,
508
                                           0x10000, 0, WINE_LDT_FLAGS_DATA );
509
        DOSMEM_BiosDataSeg = GLOBAL_CreateBlock(GMEM_FIXED,DOSMEM_sysmem + 0x400,
510
                                                0x100, 0, WINE_LDT_FLAGS_DATA );
511
        DOSMEM_BiosSysSeg = GLOBAL_CreateBlock(GMEM_FIXED,DOSMEM_dosmem+0xf0000,
512
                                               0x10000, 0, WINE_LDT_FLAGS_DATA );
513
        DOSMEM_FillBiosSegments();
514
        DOSMEM_FillIsrTable();
515
        DOSMEM_InitMemory();
Alexandre Julliard's avatar
Alexandre Julliard committed
516
        DOSMEM_InitCollateTable();
Alexandre Julliard's avatar
Alexandre Julliard committed
517
        DOSMEM_InitErrorTable();
518
        DOSMEM_InitDPMI();
519 520
        DOSDEV_InstallDOSDevices();
        already_done = 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
521
    }
522
    else if (dos_init && !already_mapped)
Alexandre Julliard's avatar
Alexandre Julliard committed
523
    {
524 525 526 527 528 529 530 531
        if (DOSMEM_dosmem)
        {
            ERR( "Needs access to the first megabyte for DOS mode\n" );
            ExitProcess(1);
        }
        MESSAGE( "Warning: unprotecting the first 64KB of memory to allow real-mode calls.\n"
                 "         NULL pointer accesses will no longer be caught.\n" );
        VirtualProtect( NULL, 0x10000, PAGE_EXECUTE_READWRITE, NULL );
532 533 534
        /* copy the BIOS and ISR area down */
        memcpy( DOSMEM_dosmem, DOSMEM_sysmem, 0x400 + 0x100 );
        DOSMEM_sysmem = DOSMEM_dosmem;
535
        SetSelectorBase( DOSMEM_0000H, 0 );
536
        SetSelectorBase( DOSMEM_BiosDataSeg, 0x400 );
537 538 539 540
        /* 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;
Alexandre Julliard's avatar
Alexandre Julliard committed
541
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
542 543 544 545 546
    return TRUE;
}


/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
547
 *           DOSMEM_Tick
Alexandre Julliard's avatar
Alexandre Julliard committed
548
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
549
 * Increment the BIOS tick counter. Called by timer signal handler.
Alexandre Julliard's avatar
Alexandre Julliard committed
550
 */
551
void DOSMEM_Tick( WORD timer )
Alexandre Julliard's avatar
Alexandre Julliard committed
552
{
553
    BIOSDATA *pBiosData = DOSMEM_BiosData();
Alexandre Julliard's avatar
Alexandre Julliard committed
554
    if (pBiosData) pBiosData->Ticks++;
Alexandre Julliard's avatar
Alexandre Julliard committed
555 556 557
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
558
 *           DOSMEM_GetBlock
Alexandre Julliard's avatar
Alexandre Julliard committed
559
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
560
 * Carve a chunk of the DOS memory block (without selector).
Alexandre Julliard's avatar
Alexandre Julliard committed
561
 */
562
LPVOID DOSMEM_GetBlock(UINT size, UINT16* pseg)
Alexandre Julliard's avatar
Alexandre Julliard committed
563
{
564
   UINT  	 blocksize;
Alexandre Julliard's avatar
Alexandre Julliard committed
565
   char         *block = NULL;
566
   dosmem_info  *info_block = DOSMEM_InfoBlock();
Alexandre Julliard's avatar
Alexandre Julliard committed
567 568 569 570 571 572
   dosmem_entry *dm;
#ifdef __DOSMEM_DEBUG_
   dosmem_entry *prev = NULL;
#endif
 
   if( size > info_block->free ) return NULL;
573
   dm = DOSMEM_RootBlock();
Alexandre Julliard's avatar
Alexandre Julliard committed
574 575 576 577 578 579

   while (dm && dm->size != DM_BLOCK_TERMINAL)
   {
#ifdef __DOSMEM_DEBUG__
       if( (dm->size & DM_BLOCK_DEBUG) != DM_BLOCK_DEBUG )
       {
580
	    WARN("MCB overrun! [prev = 0x%08x]\n", 4 + (UINT)prev);
Alexandre Julliard's avatar
Alexandre Julliard committed
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618
	    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;
619
	       if( pseg ) *pseg = (block - DOSMEM_dosmem) >> 4;
Alexandre Julliard's avatar
Alexandre Julliard committed
620 621 622 623 624 625 626 627 628 629 630
#ifdef __DOSMEM_DEBUG__
               dm->size |= DM_BLOCK_DEBUG;
#endif
	       break;
	   }
 	   dm = next;
       }
       else dm = NEXT_BLOCK(dm);
   }
   return (LPVOID)block;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
631

Alexandre Julliard's avatar
Alexandre Julliard committed
632 633 634
/***********************************************************************
 *           DOSMEM_FreeBlock
 */
635
BOOL DOSMEM_FreeBlock(void* ptr)
Alexandre Julliard's avatar
Alexandre Julliard committed
636
{
637
   dosmem_info  *info_block = DOSMEM_InfoBlock();
Alexandre Julliard's avatar
Alexandre Julliard committed
638

639 640
   if( ptr >= (void*)(((char*)DOSMEM_RootBlock()) + sizeof(dosmem_entry)) &&
       ptr < (void*)DOSMEM_MemoryTop() && !((((char*)ptr)
641
                  - DOSMEM_dosmem) & 0xf) )
Alexandre Julliard's avatar
Alexandre Julliard committed
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
   {
       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;
Alexandre Julliard's avatar
Alexandre Julliard committed
659
}
Alexandre Julliard's avatar
Alexandre Julliard committed
660

Alexandre Julliard's avatar
Alexandre Julliard committed
661 662 663
/***********************************************************************
 *           DOSMEM_ResizeBlock
 */
664
LPVOID DOSMEM_ResizeBlock(void* ptr, UINT size, UINT16* pseg)
Alexandre Julliard's avatar
Alexandre Julliard committed
665 666
{
   char         *block = NULL;
667
   dosmem_info  *info_block = DOSMEM_InfoBlock();
Alexandre Julliard's avatar
Alexandre Julliard committed
668

669 670
   if( ptr >= (void*)(((char*)DOSMEM_RootBlock()) + sizeof(dosmem_entry)) &&
       ptr < (void*)DOSMEM_MemoryTop() && !((((char*)ptr)
671
                  - DOSMEM_dosmem) & 0xf) )
Alexandre Julliard's avatar
Alexandre Julliard committed
672 673 674
   {
       dosmem_entry  *dm = (dosmem_entry*)(((char*)ptr) - sizeof(dosmem_entry));

675
       if( pseg ) *pseg = ((char*)ptr - DOSMEM_dosmem) >> 4;
Alexandre Julliard's avatar
Alexandre Julliard committed
676 677 678 679 680

       if( !(dm->size & (DM_BLOCK_FREE | DM_BLOCK_TERMINAL))
	 )
       {
	     dosmem_entry  *next = NEXT_BLOCK(dm);
681
	     UINT blocksize, orgsize = dm->size & DM_BLOCK_MASK;
Alexandre Julliard's avatar
Alexandre Julliard committed
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709

	     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 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;

		 info_block->free += orgsize - dm->size;
	     } else {
710
		 /* the collapse didn't help, try getting a new block */
711
		 block = DOSMEM_GetBlock(size, pseg);
Alexandre Julliard's avatar
Alexandre Julliard committed
712
		 if (block) {
713 714 715 716
		     /* we got one, copy the old data there (we do need to, right?) */
		     memcpy(block, ((char*)dm) + sizeof(dosmem_entry),
				   (size<orgsize) ? size : orgsize);
		     /* free old block */
Alexandre Julliard's avatar
Alexandre Julliard committed
717 718 719 720
		     info_block->blocks--;
		     info_block->free += dm->size;

		     dm->size |= DM_BLOCK_FREE;
721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
		 } else {
		     /* and Bill Gates said 640K should be enough for everyone... */

		     /* need to split original and collapsed blocks apart again,
		      * and free the collapsed blocks again, before exiting */
		     if( blocksize - orgsize > 0x20 )
		     {
			 /* split dm so that the next one stays
			  * paragraph-aligned (and next gains free bit) */

			 dm->size = (((orgsize + 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;
Alexandre Julliard's avatar
Alexandre Julliard committed
739 740 741 742 743 744 745 746 747 748 749
		 }
	     }
       }
   }
   return (LPVOID)block;
}


/***********************************************************************
 *           DOSMEM_Available
 */
750
UINT DOSMEM_Available(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
751
{
752
   UINT  	 blocksize, available = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
753 754
   dosmem_entry *dm;
   
755
   dm = DOSMEM_RootBlock();
Alexandre Julliard's avatar
Alexandre Julliard committed
756 757 758 759 760 761

   while (dm && dm->size != DM_BLOCK_TERMINAL)
   {
#ifdef __DOSMEM_DEBUG__
       if( (dm->size & DM_BLOCK_DEBUG) != DM_BLOCK_DEBUG )
       {
762
	    WARN("MCB overrun! [prev = 0x%08x]\n", 4 + (UINT)prev);
Alexandre Julliard's avatar
Alexandre Julliard committed
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
	    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;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
787

Alexandre Julliard's avatar
Alexandre Julliard committed
788
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
789
 *           DOSMEM_MapLinearToDos
Alexandre Julliard's avatar
Alexandre Julliard committed
790
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
791
 * Linear address to the DOS address space.
Alexandre Julliard's avatar
Alexandre Julliard committed
792
 */
793
UINT DOSMEM_MapLinearToDos(LPVOID ptr)
Alexandre Julliard's avatar
Alexandre Julliard committed
794
{
795 796 797
    if (((char*)ptr >= DOSMEM_dosmem) &&
        ((char*)ptr < DOSMEM_dosmem + 0x100000))
	  return (UINT)ptr - (UINT)DOSMEM_dosmem;
798
    return (UINT)ptr;
Alexandre Julliard's avatar
Alexandre Julliard committed
799 800
}

Alexandre Julliard's avatar
Alexandre Julliard committed
801

Alexandre Julliard's avatar
Alexandre Julliard committed
802
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
803
 *           DOSMEM_MapDosToLinear
Alexandre Julliard's avatar
Alexandre Julliard committed
804
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
805
 * DOS linear address to the linear address space.
Alexandre Julliard's avatar
Alexandre Julliard committed
806
 */
807
LPVOID DOSMEM_MapDosToLinear(UINT ptr)
Alexandre Julliard's avatar
Alexandre Julliard committed
808
{
809
    if (ptr < 0x100000) return (LPVOID)(ptr + (UINT)DOSMEM_dosmem);
Alexandre Julliard's avatar
Alexandre Julliard committed
810
    return (LPVOID)ptr;
Alexandre Julliard's avatar
Alexandre Julliard committed
811 812
}

Alexandre Julliard's avatar
Alexandre Julliard committed
813

Alexandre Julliard's avatar
Alexandre Julliard committed
814
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
815
 *           DOSMEM_MapRealToLinear
Alexandre Julliard's avatar
Alexandre Julliard committed
816
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
817
 * Real mode DOS address into a linear pointer
Alexandre Julliard's avatar
Alexandre Julliard committed
818
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
819
LPVOID DOSMEM_MapRealToLinear(DWORD x)
Alexandre Julliard's avatar
Alexandre Julliard committed
820
{
Alexandre Julliard's avatar
Alexandre Julliard committed
821
   LPVOID       lin;
Alexandre Julliard's avatar
Alexandre Julliard committed
822

823
   lin=DOSMEM_dosmem+(x&0xffff)+(((x&0xffff0000)>>16)*16);
824
   TRACE_(selector)("(0x%08lx) returns 0x%p.\n", x, lin );
Alexandre Julliard's avatar
Alexandre Julliard committed
825
   return lin;
Alexandre Julliard's avatar
Alexandre Julliard committed
826 827 828
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
829
 *           DOSMEM_AllocSelector
Alexandre Julliard's avatar
Alexandre Julliard committed
830 831 832 833 834
 *
 * Allocates a protected mode selector for a realmode segment.
 */
WORD DOSMEM_AllocSelector(WORD realsel)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
835
	HMODULE16 hModule = GetModuleHandle16("KERNEL");
Alexandre Julliard's avatar
Alexandre Julliard committed
836 837
	WORD	sel;

838 839
	sel=GLOBAL_CreateBlock( GMEM_FIXED, DOSMEM_dosmem+realsel*16, 0x10000,
                                hModule, WINE_LDT_FLAGS_DATA );
840
	TRACE_(selector)("(0x%04x) returns 0x%04x.\n", realsel,sel);
Alexandre Julliard's avatar
Alexandre Julliard committed
841 842
	return sel;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
843