| /* |
| * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include <assert.h> |
| #include "wine/winbase16.h" |
| #include "dosexe.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. |
| */ |
| |
| static 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]; |
| |
| struct { |
| UCHAR hindex; /* handle number */ |
| WORD logical_page; /* logical page */ |
| } mapping_save_area[EMS_MAX_HANDLES][4]; |
| |
| } *EMS_record; |
| |
| /********************************************************************** |
| * EMS_init |
| * |
| * Allocates and initialized page frame and EMS global import record. |
| */ |
| static void EMS_init(void) |
| { |
| /* |
| * Start of 64k EMS frame. |
| */ |
| ULONG base = 0xc0000; |
| |
| if(EMS_record) |
| return; |
| |
| EMS_record = HeapAlloc(GetProcessHeap(), |
| HEAP_ZERO_MEMORY, |
| sizeof(*EMS_record)); |
| |
| EMS_record->frame_address = (void *)base; |
| EMS_record->frame_selector = base >> 4; |
| } |
| |
| /********************************************************************** |
| * EMS_alloc |
| * |
| * Get handle and allocate memory. |
| */ |
| static void EMS_alloc( CONTEXT *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) { |
| SET_AH( context, 0x85 ); /* status: no more handles available */ |
| } else { |
| int pages = BX_reg(context); |
| void *buffer = HeapAlloc( GetProcessHeap(), 0, pages * EMS_PAGE_SIZE ); |
| |
| if(!buffer) { |
| SET_AH( context, 0x88 ); /* status: insufficient pages available */ |
| } else { |
| EMS_record->handle[hindex].address = buffer; |
| EMS_record->handle[hindex].pages = pages; |
| EMS_record->used_pages += pages; |
| |
| SET_DX( context, hindex ); /* handle to allocated memory*/ |
| SET_AH( context, 0 ); /* status: ok */ |
| } |
| } |
| } |
| |
| /********************************************************************** |
| * EMS_access_name |
| * |
| * Get/set handle name. |
| */ |
| static void EMS_access_name( CONTEXT *context ) |
| { |
| char *ptr; |
| int hindex = DX_reg(context); |
| if(hindex < 0 || hindex >= EMS_MAX_HANDLES) { |
| SET_AH( context, 0x83 ); /* invalid handle */ |
| return; |
| } |
| |
| switch (AL_reg(context)) { |
| case 0x00: /* get name */ |
| ptr = PTR_REAL_TO_LIN(context->SegEs, DI_reg(context)); |
| memcpy(ptr, EMS_record->handle[hindex].name, 8); |
| SET_AH( context, 0 ); |
| break; |
| |
| case 0x01: /* set name */ |
| ptr = PTR_REAL_TO_LIN(context->SegDs, SI_reg(context)); |
| memcpy(EMS_record->handle[hindex].name, ptr, 8); |
| SET_AH( context, 0 ); |
| break; |
| |
| default: |
| INT_BARF(context,0x67); |
| break; |
| } |
| } |
| |
| /********************************************************************** |
| * EMS_map |
| * |
| * Map logical page into physical page. |
| */ |
| static BYTE EMS_map( WORD physical_page, WORD new_hindex, WORD new_logical_page ) |
| { |
| int old_hindex; |
| int old_logical_page; |
| void *physical_address; |
| |
| if(physical_page > 3) |
| return 0x8b; /* status: invalid physical page */ |
| |
| old_hindex = EMS_record->mapping[physical_page].hindex; |
| old_logical_page = EMS_record->mapping[physical_page].logical_page; |
| 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); |
| |
| if(new_hindex >= EMS_MAX_HANDLES || !EMS_record->handle[new_hindex].address) |
| return 0x83; /* status: invalid handle */ |
| |
| if(new_logical_page >= EMS_record->handle[new_hindex].pages) |
| return 0x8a; /* status: invalid 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; |
| } |
| |
| return 0; /* status: ok */ |
| } |
| |
| /********************************************************************** |
| * EMS_map_multiple |
| * |
| * Map multiple logical pages into physical pages. |
| */ |
| static void EMS_map_multiple( CONTEXT *context ) |
| { |
| WORD *ptr = PTR_REAL_TO_LIN(context->SegDs, SI_reg(context)); |
| BYTE status = 0; |
| int i; |
| |
| for(i=0; i<CX_reg(context) && !status; i++, ptr += 2) |
| switch(AL_reg(context)) { |
| case 0x00: |
| status = EMS_map( ptr[1], |
| DX_reg(context), ptr[0] ); |
| break; |
| case 0x01: |
| status = EMS_map( (ptr[1] - EMS_record->frame_selector) >> 10, |
| DX_reg(context), ptr[0] ); |
| break; |
| default: |
| status = 0x8f; /* status: undefined subfunction */ |
| } |
| |
| SET_AH( context, status ); |
| } |
| |
| /********************************************************************** |
| * EMS_free |
| * |
| * Free memory and release handle. |
| */ |
| static void EMS_free( CONTEXT *context ) |
| { |
| int hindex = DX_reg(context); |
| int i; |
| |
| if(hindex < 0 || hindex >= EMS_MAX_HANDLES) { |
| SET_AH( context, 0x83 ); /* status: invalid handle */ |
| return; |
| } |
| |
| if(!EMS_record->handle[hindex].address) { |
| SET_AH( 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; |
| |
| SET_AH( context, 0 ); /* status: ok */ |
| } |
| |
| /********************************************************************** |
| * EMS_save_context |
| * |
| * Save physical page mappings into handle specific save area. |
| */ |
| static void EMS_save_context( CONTEXT *context ) |
| { |
| WORD h = DX_reg(context); |
| int i; |
| |
| for(i=0; i<4; i++) { |
| EMS_record->mapping_save_area[h][i].hindex = EMS_record->mapping[i].hindex; |
| EMS_record->mapping_save_area[h][i].logical_page = EMS_record->mapping[i].logical_page; |
| } |
| |
| SET_AX( context, 0 ); /* status: ok */ |
| } |
| |
| |
| /********************************************************************** |
| * EMS_restore_context |
| * |
| * Restore physical page mappings from handle specific save area. |
| */ |
| static void EMS_restore_context( CONTEXT *context ) |
| { |
| WORD handle = DX_reg(context); |
| int i; |
| |
| for(i=0; i<4; i++) { |
| int hindex = EMS_record->mapping_save_area[handle][i].hindex; |
| int logical_page = EMS_record->mapping_save_area[handle][i].logical_page; |
| |
| if(EMS_map( i, hindex, logical_page )) { |
| SET_AX( context, 0x8e ); /* status: restore of mapping context failed */ |
| return; |
| } |
| } |
| |
| SET_AX( context, 0 ); /* status: ok */ |
| } |
| |
| /********************************************************************** |
| * DOSVM_Int67Handler |
| * |
| * Handler for interrupt 67h EMS routines. |
| */ |
| void WINAPI DOSVM_Int67Handler( CONTEXT *context ) |
| { |
| switch (AH_reg(context)) { |
| |
| case 0x40: /* EMS - GET MANAGER STATUS */ |
| SET_AH( context, 0 ); /* status: ok */ |
| break; |
| |
| case 0x41: /* EMS - GET PAGE FRAME SEGMENT */ |
| EMS_init(); |
| SET_BX( context, EMS_record->frame_selector ); /* segment of page frame */ |
| SET_AH( context, 0 ); /* status: ok */ |
| break; |
| |
| case 0x42: /* EMS - GET NUMBER OF PAGES */ |
| EMS_init(); |
| /* unallocated 16k pages */ |
| SET_BX( context, EMS_MAX_PAGES - EMS_record->used_pages ); |
| /* total number of 16k pages */ |
| SET_DX( context, EMS_MAX_PAGES ); |
| /* status: ok */ |
| SET_AH( context, 0 ); |
| break; |
| |
| case 0x43: /* EMS - GET HANDLE AND ALLOCATE MEMORY */ |
| EMS_init(); |
| EMS_alloc(context); |
| break; |
| |
| case 0x44: /* EMS - MAP MEMORY */ |
| EMS_init(); |
| SET_AH( context, EMS_map( AL_reg(context), DX_reg(context), BX_reg(context) ) ); |
| break; |
| |
| case 0x45: /* EMS - RELEASE HANDLE AND MEMORY */ |
| EMS_init(); |
| EMS_free(context); |
| break; |
| |
| case 0x46: /* EMS - GET EMM VERSION */ |
| SET_AL( context, 0x40 ); /* version 4.0 */ |
| SET_AH( context, 0 ); /* status: ok */ |
| break; |
| |
| case 0x47: /* EMS - SAVE MAPPING CONTEXT */ |
| EMS_init(); |
| EMS_save_context(context); |
| break; |
| |
| case 0x48: /* EMS - RESTORE MAPPING CONTEXT */ |
| EMS_init(); |
| EMS_restore_context(context); |
| 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 */ |
| SET_BX( context, EMS_MAX_HANDLES ); /* EMM handles */ |
| SET_AH( 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 */ |
| INT_BARF(context,0x67); |
| break; |
| |
| case 0x50: /* EMS 4.0 - MAP/UNMAP MULTIPLE HANDLE PAGES */ |
| EMS_init(); |
| EMS_map_multiple(context); |
| break; |
| |
| 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 */ |
| INT_BARF(context,0x67); |
| break; |
| |
| case 0x59: /* EMS 4.0 - GET EXPANDED MEMORY HARDWARE INFORMATION */ |
| if(AL_reg(context) == 0x01) { |
| EMS_init(); |
| /* unallocated raw pages */ |
| SET_BX( context, EMS_MAX_PAGES - EMS_record->used_pages ); |
| /* total number raw pages */ |
| SET_DX( context, EMS_MAX_PAGES ); |
| /* status: ok */ |
| SET_AH( context, 0 ); |
| } else |
| INT_BARF(context,0x67); |
| break; |
| |
| 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 */ |
| INT_BARF(context,0x67); |
| break; |
| |
| case 0xde: /* Virtual Control Program Interface (VCPI) */ |
| if(AL_reg(context) == 0x00) { |
| /* |
| * VCPI INSTALLATION CHECK |
| * (AH_reg() != 0) means VCPI is not present |
| */ |
| TRACE("- VCPI installation check\n"); |
| return; |
| } else |
| INT_BARF(context,0x67); |
| break; |
| |
| default: |
| INT_BARF(context,0x67); |
| } |
| } |
| |
| |
| /********************************************************************** |
| * EMS_Ioctl_Handler |
| * |
| * Handler for interrupt 21h IOCTL routine for device "EMMXXXX0". |
| */ |
| void EMS_Ioctl_Handler( CONTEXT *context ) |
| { |
| assert(AH_reg(context) == 0x44); |
| |
| switch (AL_reg(context)) { |
| case 0x00: /* IOCTL - GET DEVICE INFORMATION */ |
| RESET_CFLAG(context); /* operation was successful */ |
| SET_DX( 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 */ |
| SET_AL( context, 0xff ); /* device is ready */ |
| break; |
| |
| default: |
| INT_BARF(context,0x21); |
| break; |
| } |
| } |