Support for DOS EMS memory

Jukka Heinonen jhei at iki.fi
Fri Jan 25 23:30:42 CST 2002


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




More information about the wine-patches mailing list