/* * 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 #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; } }