For some really strange reason I added EMS support to Wine in order to get some good old DOS games to work. I don't know if this patch is of any use (only really old DOS programs should be using EMS) and I don't like adding new DOS devices either (yes, full EMS support means we need device EMMXXXX0). Anyway, here follows the patch: Changelog: Added support for DOS EMS memory. Index: wine/dlls/winedos/int21.c =================================================================== RCS file: /home/wine/wine/dlls/winedos/int21.c,v retrieving revision 1.1 diff -u -r1.1 int21.c --- wine/dlls/winedos/int21.c 2001/12/04 19:54:45 1.1 +++ wine/dlls/winedos/int21.c 2002/01/26 03:31:44 @@ -12,10 +12,41 @@ #include "miscemu.h" #include "msdos.h" #include "console.h" +#include "file.h" #include "debugtools.h" DEFAULT_DEBUG_CHANNEL(int21); +void WINAPI DOSVM_Int21Handler_Ioctl( CONTEXT86 *context ) +{ + const DOS_DEVICE *dev = DOSFS_GetDeviceByHandle( + DosFileHandleToWin32Handle(BX_reg(context)) ); + + if (dev && !strcasecmp( dev->name, "EMMXXXX0" )) { + EMS_Ioctl_Handler(context); + return; + } + + switch (AL_reg(context)) + { + case 0x0b: /* SET SHARING RETRY COUNT */ + TRACE("IOCTL - SET SHARING RETRY COUNT pause %d retries %d\n", + CX_reg(context), DX_reg(context)); + if (!CX_reg(context)) + { + AX_reg(context) = 1; + SET_CFLAG(context); + break; + } + DOSMEM_LOL()->sharing_retry_delay = CX_reg(context); + if (!DX_reg(context)) + DOSMEM_LOL()->sharing_retry_count = DX_reg(context); + RESET_CFLAG(context); + break; + default: + DOS3Call( context ); + } +} /*********************************************************************** * DOSVM_Int21Handler @@ -114,23 +145,7 @@ break; case 0x44: /* IOCTL */ - switch (AL_reg(context)) - { - case 0x0b: /* SET SHARING RETRY COUNT */ - TRACE("IOCTL - SET SHARING RETRY COUNT pause %d retries %d\n", - CX_reg(context), DX_reg(context)); - if (!CX_reg(context)) - { - AX_reg(context) = 1; - SET_CFLAG(context); - break; - } - DOSMEM_LOL()->sharing_retry_delay = CX_reg(context); - if (!DX_reg(context)) - DOSMEM_LOL()->sharing_retry_count = DX_reg(context); - RESET_CFLAG(context); - break; - } + DOSVM_Int21Handler_Ioctl( context ); break; case 0x4b: /* "EXEC" - LOAD AND/OR EXECUTE PROGRAM */ Index: wine/dlls/winedos/Makefile.in =================================================================== RCS file: /home/wine/wine/dlls/winedos/Makefile.in,v retrieving revision 1.5 diff -u -r1.5 Makefile.in --- wine/dlls/winedos/Makefile.in 2002/01/01 01:13:04 1.5 +++ wine/dlls/winedos/Makefile.in 2002/01/26 03:32:53 @@ -21,6 +21,8 @@ int29.c \ int31.c \ int33.c \ + int67.c \ + ems.c \ ioports.c \ module.c \ vga.c \ Index: wine/dlls/winedos/dosexe.h =================================================================== RCS file: /home/wine/wine/dlls/winedos/dosexe.h,v retrieving revision 1.1 diff -u -r1.1 dosexe.h --- wine/dlls/winedos/dosexe.h 2001/12/04 19:54:45 1.1 +++ wine/dlls/winedos/dosexe.h 2002/01/26 03:33:21 @@ -92,6 +92,13 @@ extern void WINAPI DOSVM_Int33Handler(CONTEXT86*); extern void WINAPI DOSVM_Int33Message(UINT,WPARAM,LPARAM); +/* int67.c */ +extern void WINAPI DOSVM_Int67Handler(CONTEXT86*); + +/* ems.c */ +extern void WINAPI EMS_Int67_Handler(CONTEXT86*); +extern void WINAPI EMS_Ioctl_Handler(CONTEXT86*); + /* xms.c */ extern void WINAPI XMS_Handler(CONTEXT86*); Index: wine/dlls/winedos/dosvm.c =================================================================== RCS file: /home/wine/wine/dlls/winedos/dosvm.c,v retrieving revision 1.10 diff -u -r1.10 dosvm.c --- wine/dlls/winedos/dosvm.c 2002/01/06 18:34:02 1.10 +++ wine/dlls/winedos/dosvm.c 2002/01/26 03:33:39 @@ -603,7 +603,13 @@ /* 18 */ 0, 0, INT_Int1aHandler, 0, 0, 0, 0, 0, /* 20 */ DOSVM_Int20Handler, DOSVM_Int21Handler, 0, 0, 0, INT_Int25Handler, 0, 0, /* 28 */ 0, DOSVM_Int29Handler, INT_Int2aHandler, 0, 0, 0, 0, INT_Int2fHandler, - /* 30 */ 0, DOSVM_Int31Handler, 0, DOSVM_Int33Handler + /* 30 */ 0, DOSVM_Int31Handler, 0, DOSVM_Int33Handler, 0, 0, 0, 0, + /* 38 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 40 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 58 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 60 */ 0, 0, 0, 0, 0, 0, 0, DOSVM_Int67Handler }; Index: wine/files/dos_fs.c =================================================================== RCS file: /home/wine/wine/files/dos_fs.c,v retrieving revision 1.99 diff -u -r1.99 dos_fs.c --- wine/files/dos_fs.c 2001/11/30 18:46:45 1.99 +++ wine/files/dos_fs.c 2002/01/26 03:34:03 @@ -78,7 +78,8 @@ { "COM3", 0x80c0 }, { "COM4", 0x80c0 }, { "SCSIMGR$", 0xc0c0 }, - { "HPSCAN", 0xc0c0 } + { "HPSCAN", 0xc0c0 }, + { "EMMXXXX0", 0x0000 } }; #define GET_DRIVE(path) \ @@ -786,7 +787,8 @@ return handle; } if (!strcmp(DOSFS_Devices[i].name,"SCSIMGR$") || - !strcmp(DOSFS_Devices[i].name,"HPSCAN")) + !strcmp(DOSFS_Devices[i].name,"HPSCAN") || + !strcmp(DOSFS_Devices[i].name,"EMMXXXX0")) { return FILE_CreateDevice( i, access, sa ); } Index: wine/winedos/int67.c (new file!) =================================================================== /* * DOS interrupt 67h handler (EMS) */ #include "dosexe.h" /********************************************************************** * DOSVM_Int67Handler * * Handler for int 67h. */ void WINAPI DOSVM_Int67Handler( CONTEXT86 *context ) { EMS_Int67_Handler(context); } Index: wine/winedos/ems.c (new file!) =================================================================== /* * EMS emulation */ #include <assert.h> #include "wine/winbase16.h" #include "dosexe.h" #include "miscemu.h" #include "debugtools.h" 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 */ } /********************************************************************** * EMS_Int67_Handler * * Handler for interrupt 67h EMS routines. */ void WINAPI EMS_Int67_Handler( 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; } } -- Jukka Heinonen <http://www.iki.fi/jhei/>