blob: 0f80d61fc11c4542a72fa1d68ce85b15569ffe7a [file] [log] [blame]
/*
* Int67 (EMS) emulation
*
* Copyright 2002 Jukka Heinonen
*
* 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 <assert.h>
#include "wine/winbase16.h"
#include "dosexe.h"
#include "miscemu.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(int);
/*
* EMS page size == 16 kilobytes.
*/
#define EMS_PAGE_SIZE (16*1024)
/*
* Linear address of EMS page.
*/
#define EMS_PAGE_ADDRESS(base,page) (((char*)base) + EMS_PAGE_SIZE * page)
/*
* Maximum number of pages that can be allocated using EMS.
*/
#define EMS_MAX_PAGES 1024
/*
* Maximum number of EMS handles (allocated blocks).
*/
#define EMS_MAX_HANDLES 256
/*
* Global EMM Import Record.
* Applications can get address of this record
* and directly access allocated memory if they use
* IOCTL interface.
*
* FIXME: Missing lots of fields, packing is not correct.
*/
struct {
struct {
UCHAR hindex; /* handle number */
BYTE flags; /* bit 0: normal handle rather than system handle */
char name[8]; /* handle name */
WORD pages; /* allocated pages */
void *address; /* physical address*/
} handle[EMS_MAX_HANDLES];
/* Wine specific fields... */
int used_pages; /* Number of allocated pages. */
void *frame_address; /* Address of 64k EMS page frame */
WORD frame_selector; /* Segment of 64k EMS page frame */
struct {
UCHAR hindex; /* handle number */
WORD logical_page; /* logical page */
} mapping[4];
} *EMS_record = 0;
/**********************************************************************
* EMS_init
*
* Allocates and initialized page frame and EMS global import record.
*/
static void EMS_init(void)
{
/*
* FIXME: Should dynamically allocate upper memory block for EMS frame.
*/
ULONG base = 0xd0000;
if(EMS_record)
return;
EMS_record = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(*EMS_record));
EMS_record->frame_address = DOSMEM_MapDosToLinear(base);
EMS_record->frame_selector = base >> 4;
}
/**********************************************************************
* EMS_alloc
*
* Get handle and allocate memory.
*/
static void EMS_alloc( CONTEXT86 *context )
{
int hindex = 1; /* handle zero is reserved for system */
while(hindex < EMS_MAX_HANDLES && EMS_record->handle[hindex].address)
hindex++;
if(hindex == EMS_MAX_HANDLES) {
AH_reg(context) = 0x85; /* status: no more handles available */
} else {
int pages = BX_reg(context);
void *buffer = HeapAlloc( GetProcessHeap(), 0, pages * EMS_PAGE_SIZE );
if(!buffer) {
AH_reg(context) = 0x88; /* status: insufficient pages available */
} else {
EMS_record->handle[hindex].address = buffer;
EMS_record->handle[hindex].pages = pages;
EMS_record->used_pages += pages;
DX_reg(context) = hindex; /* handle to allocated memory*/
AH_reg(context) = 0; /* status: ok */
}
}
}
/**********************************************************************
* EMS_access_name
*
* Get/set handle name.
*/
static void EMS_access_name( CONTEXT86 *context )
{
char *ptr;
int hindex = DX_reg(context);
if(hindex < 0 || hindex >= EMS_MAX_HANDLES) {
AH_reg(context) = 0x83; /* invalid handle */
return;
}
switch AL_reg(context) {
case 0x00: /* get name */
ptr = MapSL(MAKESEGPTR(context->SegEs, DI_reg(context)));
memcpy(ptr, EMS_record->handle[hindex].name, 8);
AH_reg(context) = 0;
break;
case 0x01: /* set name */
ptr = MapSL(MAKESEGPTR(context->SegDs, SI_reg(context)));
memcpy(EMS_record->handle[hindex].name, ptr, 8);
AH_reg(context) = 0;
break;
default:
INT_BARF(context,0x67);
break;
}
}
/**********************************************************************
* EMS_map
*
* Map logical page into physical page.
*/
static void EMS_map( CONTEXT86 *context )
{
int physical_page = AL_reg(context);
int new_hindex = DX_reg(context);
int new_logical_page = BX_reg(context);
int old_hindex = EMS_record->mapping[physical_page].hindex;
int old_logical_page = EMS_record->mapping[physical_page].logical_page;
void *physical_address = EMS_PAGE_ADDRESS(EMS_record->frame_address,
physical_page);
/* unmap old page */
if(old_hindex) {
void *ptr = EMS_PAGE_ADDRESS(EMS_record->handle[old_hindex].address,
old_logical_page);
memcpy(ptr, physical_address, EMS_PAGE_SIZE);
}
/* map new page */
if(new_hindex && new_logical_page != 0xffff) {
void *ptr = EMS_PAGE_ADDRESS(EMS_record->handle[new_hindex].address,
new_logical_page);
memcpy(physical_address, ptr, EMS_PAGE_SIZE);
EMS_record->mapping[physical_page].hindex = new_hindex;
EMS_record->mapping[physical_page].logical_page = new_logical_page;
} else {
EMS_record->mapping[physical_page].hindex = 0;
EMS_record->mapping[physical_page].logical_page = 0;
}
AH_reg(context) = 0; /* status: ok */
}
/**********************************************************************
* EMS_free
*
* Free memory and release handle.
*/
static void EMS_free( CONTEXT86 *context )
{
int hindex = DX_reg(context);
int i;
if(hindex < 0 || hindex >= EMS_MAX_HANDLES) {
AH_reg(context) = 0x83; /* status: invalid handle */
return;
}
if(!EMS_record->handle[hindex].address) {
AH_reg(context) = 0; /* status: ok */
return;
}
EMS_record->used_pages -= EMS_record->handle[hindex].pages;
/* unmap pages */
for(i=0; i<4; i++)
if(EMS_record->mapping[i].hindex == hindex)
EMS_record->mapping[i].hindex = 0;
/* free block */
HeapFree( GetProcessHeap(), 0, EMS_record->handle[hindex].address );
EMS_record->handle[hindex].address = 0;
AH_reg(context) = 0; /* status: ok */
}
/**********************************************************************
* DOSVM_Int67Handler
*
* Handler for interrupt 67h EMS routines.
*/
void WINAPI DOSVM_Int67Handler( CONTEXT86 *context )
{
switch AH_reg(context) {
case 0x40: /* EMS - GET MANAGER STATUS */
AH_reg(context) = 0; /* status: ok */
break;
case 0x41: /* EMS - GET PAGE FRAME SEGMENT */
EMS_init();
BX_reg(context) = EMS_record->frame_selector; /* segment of page frame */
AH_reg(context) = 0; /* status: ok */
break;
case 0x42: /* EMS - GET NUMBER OF PAGES */
EMS_init();
/* unallocated 16k pages */
BX_reg(context) = EMS_MAX_PAGES - EMS_record->used_pages;
/* total number of 16k pages */
DX_reg(context) = EMS_MAX_PAGES;
/* status: ok */
AH_reg(context) = 0;
break;
case 0x43: /* EMS - GET HANDLE AND ALLOCATE MEMORY */
EMS_init();
EMS_alloc(context);
break;
case 0x44: /* EMS - MAP MEMORY */
EMS_init();
EMS_map(context);
break;
case 0x45: /* EMS - RELEASE HANDLE AND MEMORY */
EMS_init();
EMS_free(context);
break;
case 0x46: /* EMS - GET EMM VERSION */
AL_reg(context) = 0x40; /* version 4.0 */
AH_reg(context) = 0; /* status: ok */
break;
case 0x47: /* EMS - SAVE MAPPING CONTEXT */
case 0x48: /* EMS - RESTORE MAPPING CONTEXT */
INT_BARF(context,0x67);
break;
case 0x49: /* EMS - reserved - GET I/O PORT ADDRESSES */
case 0x4a: /* EMS - reserved - GET TRANSLATION ARRAY */
INT_BARF(context,0x67);
break;
case 0x4b: /* EMS - GET NUMBER OF EMM HANDLES */
BX_reg(context) = EMS_MAX_HANDLES; /* EMM handles */
AH_reg(context) = 0; /* status: ok */
break;
case 0x4c: /* EMS - GET PAGES OWNED BY HANDLE */
case 0x4d: /* EMS - GET PAGES FOR ALL HANDLES */
case 0x4e: /* EMS - GET OR SET PAGE MAP */
case 0x4f: /* EMS 4.0 - GET/SET PARTIAL PAGE MAP */
case 0x50: /* EMS 4.0 - MAP/UNMAP MULTIPLE HANDLE PAGES */
case 0x51: /* EMS 4.0 - REALLOCATE PAGES */
case 0x52: /* EMS 4.0 - GET/SET HANDLE ATTRIBUTES */
INT_BARF(context,0x67);
break;
case 0x53: /* EMS 4.0 - GET/SET HANDLE NAME */
EMS_init();
EMS_access_name(context);
break;
case 0x54: /* EMS 4.0 - GET HANDLE DIRECTORY */
case 0x55: /* EMS 4.0 - ALTER PAGE MAP AND JUMP */
case 0x56: /* EMS 4.0 - ALTER PAGE MAP AND CALL */
case 0x57: /* EMS 4.0 - MOVE/EXCHANGE MEMORY REGION */
case 0x58: /* EMS 4.0 - GET MAPPABLE PHYSICAL ADDRESS ARRAY */
case 0x59: /* EMS 4.0 - GET EXPANDED MEMORY HARDWARE INFORMATION */
case 0x5a: /* EMS 4.0 - ALLOCATE STANDARD/RAW PAGES */
case 0x5b: /* EMS 4.0 - ALTERNATE MAP REGISTER SET */
case 0x5c: /* EMS 4.0 - PREPARE EXPANDED MEMORY HARDWARE FOR WARM BOOT */
case 0x5d: /* EMS 4.0 - ENABLE/DISABLE OS FUNCTION SET FUNCTIONS */
default:
INT_BARF(context,0x67);
}
}
/**********************************************************************
* EMS_Ioctl_Handler
*
* Handler for interrupt 21h IOCTL routine for device "EMMXXXX0".
*/
void WINAPI EMS_Ioctl_Handler( CONTEXT86 *context )
{
assert(AH_reg(context) == 0x44);
switch AL_reg(context) {
case 0x00: /* IOCTL - GET DEVICE INFORMATION */
RESET_CFLAG(context); /* operation was successful */
DX_reg(context) = 0x4080; /* bit 14 (support ioctl read) and
* bit 7 (is_device) */
break;
case 0x02: /* EMS - GET MEMORY MANAGER INFORMATION */
/*
* This is what is called "Windows Global EMM Import Specification".
* Undocumented of course! Supports three requests:
* GET API ENTRY POINT
* GET EMM IMPORT STRUCTURE ADDRESS
* GET MEMORY MANAGER VERSION
*/
INT_BARF(context,0x21);
break;
case 0x07: /* IOCTL - GET OUTPUT STATUS */
RESET_CFLAG(context); /* operation was successful */
AL_reg(context) = 0xff; /* device is ready */
break;
default:
INT_BARF(context,0x21);
break;
}
}