]> 4ch.mooo.com Git - 16.git/commitdiff
added more ems xms junk to the 16/ dir for reference
authorsparky4 <sparky4@cock.li>
Tue, 8 May 2018 13:57:37 +0000 (08:57 -0500)
committersparky4 <sparky4@cock.li>
Tue, 8 May 2018 13:57:37 +0000 (08:57 -0500)
16/XMSARRAY.CPP [new file with mode: 0755]
16/ems_.c [new file with mode: 0755]
16/emstulkt.zip [new file with mode: 0755]
16/tweak16/16.Q [new file with mode: 0755]
16/vgmtools

diff --git a/16/XMSARRAY.CPP b/16/XMSARRAY.CPP
new file mode 100755 (executable)
index 0000000..3ae2489
--- /dev/null
@@ -0,0 +1,353 @@
+//--------------------------------------------------------------------------
+//
+//      XMSARRAY.CPP: body of XMSarray interface library.
+//      Copyright (c) J.English 1993.
+//      Author's address: je@unix.brighton.ac.uk
+//
+//      Permission is granted to use copy and distribute the
+//      information contained in this file provided that this
+//      copyright notice is retained intact and that any software
+//      or other document incorporating this file or parts thereof
+//      makes the source code for the library of which this file
+//      is a part freely available.
+//
+//--------------------------------------------------------------------------
+//
+//      Revision history:
+//      1.0     Jun 1993        Initial coding
+//      1.01    Sep 1993        Filenames changed from XMS.* to XMSARRAY.*
+//      2.0     Nov 1993        Revised to use general purpose XMS class
+//
+//--------------------------------------------------------------------------
+
+#include "xms.h"
+#include "xmsarray.h"
+#include <dos.h>
+#include <stdlib.h>
+\f
+//--------------------------------------------------------------------------
+//
+//      Constants.
+//
+const int USED      = 1,    // cache line is in use
+          DIRTY     = 2;    // cache line has been written to
+
+
+//--------------------------------------------------------------------------
+//
+//      Data structures.
+//
+struct XMSbuffer            // cache structure
+{
+    int      mask;          // ... mask to extract item number in buffer
+    long     where;         // ... offset to start of XMS block
+    unsigned width;         // ... unit for XMS transfers in bytes
+    char*    cache;         // ... buffer for XMS transfers
+    long     tag;           // ... current index held in buffer
+    char     flags;         // ... buffer state (in use, dirty)
+};
+
+struct XMSheader            // header for XMS block
+{
+    long size;              // ... block size in bytes
+    int  free;              // ... is it a free block?
+};
+
+struct XMSmove              // descriptor for XMS moves
+{
+    long count;             // ... number of bytes to move
+    int  srchandle;         // ... source handle (0 for real memory)
+    long srcoffset;         // ... source offset (or far pointer)
+    int  dsthandle;         // ... destination handle (0 for real memory)
+    long dstoffset;         // ... destination offset (or far pointer)
+};
+
+
+//--------------------------------------------------------------------------
+//
+//      Globals (statics).
+//
+static XMS* arrayBlock = 0; // block used to hold XMS arrays
+static int  arrayCount = 0; // number of allocated XMS arrays
+static long XMSsize    = 0; // current allocation size, internal reckoning
+                            // (XMS driver rounds up to nearest 4K or 16K)
+
+//--------------------------------------------------------------------------
+//
+//      Function to return the number of bits required to represent "n",
+//      used for rounding up to powers of 2 and generating masks.
+//
+static int bits (unsigned n)
+{
+    int b = 0;
+    for (long i = 1; i > 0 && i < n; i <<= 1)
+        b++;
+    return b;
+}
+\f
+//--------------------------------------------------------------------------
+//
+//      XMSlocate: locate a free block of XMS.
+//
+//      This is used by XMSalloc to check if there is a block of suitable
+//      size which has been returned to the free list.  If not, it returns
+//      0 (which is never a legitimate offset) and if so, it divides the
+//      block as necessary, marks the allocated block as in use and returns
+//      its offset.  XMS blocks are prefaced by an XMSheader giving their
+//      size and status (free/in use).  Offsets refer to the first byte
+//      of the allocation (i.e. the byte after the header); the size then
+//      gives the address of the next block.  Adjacent free blocks are merged
+//      while scanning the list.  If the last block is free, it can't be
+//      merged with anything and so XMSsize is adjusted to remove it from
+//      the internal record of the allocation size.
+//
+static long XMSlocate (long size)
+{
+    long p = 0, oldp = 0;
+    XMSheader h, oldh = {0,0};
+
+    //--- scan block list from offset 0
+    while (p < XMSsize)
+    {
+        //--- get header of next block
+        XMS::copy (&h, (*arrayBlock)[p], sizeof(h));
+
+        //--- merge adjacent free blocks by changing size of previous block
+        if (oldh.free && h.free)
+        {   oldh.size += h.size + sizeof(h);
+            h = oldh, p = oldp;
+            XMS::copy ((*arrayBlock)[p], &h, sizeof(h));
+        }
+
+        //--- check if block (after possible merging) is big enough
+        if (h.free && h.size >= size + sizeof(XMSheader))
+            break;
+
+        //--- keep value and offset for block and get offset of next header
+        oldh = h, oldp = p;
+        p += h.size + sizeof(h);
+    }
+
+    //--- no suitable block available: remove last block if it is free
+    if (p >= XMSsize)
+    {   if (oldh.free)
+            XMSsize = oldp;
+        return 0;
+    }
+
+    //--- divide block if necessary
+    if (h.free && h.size >= size + sizeof(XMSheader))
+    {
+        XMSheader t = {h.size - size - sizeof(t), 1};
+        XMS::copy (arrayBlock->at(p+sizeof(h)+size), &t, sizeof(t));
+        h.size = size;
+    }
+
+    //--- write new block's header and return offset of first usable byte
+    h.free = 0;
+    XMS::copy (arrayBlock->at(p), &h, sizeof(h));
+    return p + sizeof(h);
+}
+\f
+//--------------------------------------------------------------------------
+//
+//      XMSalloc: allocate a block of XMS.
+//
+//      This function tries to allocate "size" bytes of XMS and returns 0
+//      if it fails.
+//
+static long XMSalloc (long size)
+{
+    //--- check XMS initialised before proceeding
+    if (arrayCount++ == 0)
+    {   arrayBlock = new XMS (sizeof(XMSheader) + size);
+        if (arrayBlock == 0 || !arrayBlock->valid())
+        {   delete arrayBlock;
+            arrayCount--;
+            arrayBlock = 0;
+        }
+        else
+        {   XMSheader h = {0,0};
+            XMS::copy (arrayBlock->at(0), &h, sizeof(XMSheader));
+        }
+    }
+    if (arrayBlock == 0)
+        return 0;
+
+    //--- try to find a suitable free block in the current allocation
+    long pos = XMSlocate (size);
+    if (pos != 0)
+        return pos;
+
+    //--- try to extend current allocation if none found
+    if (arrayBlock->resize (XMSsize + size + sizeof(XMSheader)) != XMS::SUCCESS)
+        return 0;
+
+    //--- write the header for the new block at the end of current allocation
+    XMSheader h = {size,0};
+    XMS::copy (arrayBlock->at(XMSsize), &h, sizeof(h));
+
+    //--- get offset of first usable byte of new block
+    pos = XMSsize + sizeof(h);
+
+    //--- increase allocation size to accomodate it and return its offset
+    XMSsize += size + sizeof(h);
+    return pos;
+}
+
+
+//--------------------------------------------------------------------------
+//
+//      XMSfree: mark a block of XMS as free.
+//
+//      This returns a block to the free list by rewriting its header.
+//
+static void XMSfree (long offset)
+{
+    if (--arrayCount == 0)
+    {   delete arrayBlock;
+        return;
+    }
+    XMSheader h;
+    XMS::copy (&h, arrayBlock->at(offset-sizeof(h)), sizeof(h));
+    h.free = 1;
+    XMS::copy (arrayBlock->at(offset-sizeof(h)), &h, sizeof(h));
+}
+\f
+//--------------------------------------------------------------------------
+//
+//      XMSarrayBase::XMSarrayBase.
+//
+//      Constructor for base class XMS, called by constructor for XMSarray.
+//      Constructs an XMS array containing the specified number of items of
+//      the specified size and also a one-line cache of the specified size.
+//
+XMSarrayBase::XMSarrayBase (long items, unsigned size, unsigned cachesize)
+{
+    //--- initial state is "unallocated"
+    state = 0;
+
+    //--- create the cache
+    buffer = new XMSbuffer;
+    if (buffer == 0)
+        return;
+
+    //--- set up the item offset mask (items in line - 1)
+    buffer->mask = (size > cachesize) ? 0 : 
+                   (1 << (bits(cachesize) - bits(size))) - 1;
+
+    //--- calculate number of items needed (1 extra rounded up to nearest line)
+    if (items < 0)
+        items = 0;
+    items += buffer->mask;
+    items &= ~buffer->mask;
+
+    //--- calculate cache size in bytes and allocate it
+    buffer->width = (buffer->mask + 1) * size;
+    buffer->cache = new char [buffer->width];
+    if (buffer->cache == 0)
+        return;
+
+    //--- try to allocate the XMS block
+    buffer->where = XMSalloc (items * size);
+
+    //--- mark cache line as unused
+    buffer->tag   = 0;
+    buffer->flags = 0;
+
+    //--- record if allocation succeeded
+    state = (buffer->where != 0);
+}
+
+
+//--------------------------------------------------------------------------
+//
+//      XMSarrayBase::XMSarrayBase.
+//
+//      Copy constructor used by XMSitem to clone a reference to the
+//      base array.
+//
+XMSarrayBase::XMSarrayBase (const XMSarrayBase& base)
+{
+    state  = base.state;
+    buffer = base.buffer;
+}
+\f
+//--------------------------------------------------------------------------
+//
+//      XMSarrayBase::free.
+//
+//      Try to free an array.  Check that it's there before doing anything,
+//      then free the XMS block and delete the cache.  This is not a written
+//      as a destructor so that XMSitem doesn't call it.  Unfortunately
+//      Borland C++ won't let you nominate all possible instantiations of
+//      a template as friends of a non-template class, i.e.
+//          class X { template<class T> friend class Y<T>; ... };
+//      gives a compilation error.
+//
+void XMSarrayBase::free ()
+{
+    if (buffer == 0)
+        return;
+    if (buffer->where != 0)
+        XMSfree (buffer->where);
+    delete buffer->cache;
+    delete buffer;
+}
+
+
+//--------------------------------------------------------------------------
+//
+//      XMSarrayBase::mask.
+//
+//      Return the item number within the cache line for a given subscript.
+//
+int XMSarrayBase::mask (int n)
+{
+    if (!state)
+        return 0;
+    return n & buffer->mask;
+}
+\f
+//--------------------------------------------------------------------------
+//
+//      XMSarrayBase::get.
+//
+//      Get a block from XMS into conventional memory.  If it's not in the
+//      cache, load the relevant line (writing back the old line if it was
+//      dirty).  Return a pointer to the cached copy of the item.  "Base"
+//      is the base address of the line to load and "offset" is the offset
+//      into the cache buffer.
+//
+void* XMSarrayBase::get (long base, int offset)
+{
+    if (!state)
+        return 0;
+    if (buffer->tag != base || !(buffer->flags & USED))
+    {   if (buffer->flags & DIRTY)
+            XMS::copy (arrayBlock->at (buffer->where + buffer->tag),
+                       buffer->cache, buffer->width);
+        XMS::copy (buffer->cache, arrayBlock->at (buffer->where + base),
+                   buffer->width);
+        buffer->flags = USED;
+        buffer->tag = base;
+    }
+    return buffer->cache + offset;
+}
+
+
+//--------------------------------------------------------------------------
+//
+//      XMSarrayBase::put.
+//
+//      Gets a copy of the relevant cache line into memory and then marks
+//      the line as dirty (about to be written to).
+//
+void* XMSarrayBase::put (long base, int offset)
+{
+    if (!state)
+        return 0;
+    void* addr = get (base, offset);
+    buffer->flags |= DIRTY;
+    return addr;
+}
diff --git a/16/ems_.c b/16/ems_.c
new file mode 100755 (executable)
index 0000000..9571fc2
--- /dev/null
+++ b/16/ems_.c
@@ -0,0 +1,1693 @@
+/*-
+ * Copyright (c) 1997 Helmut Wirth <hfwirth@ping.at>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice immediately at the beginning of the file, witout modification,
+ *    this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.bin/doscmd/ems.c,v 1.3.2.2 2002/04/25 11:04:51 tg Exp $");
+
+/* 
+ * EMS memory emulation
+ *
+ * To emulate Expanded Memory we use a DOS driver (emsdriv.sys) which
+ * routes calls to int 0x67 to this emulator routine. The main entry point
+ * is ems_entry(..). The emulator needs to be initialized before the first
+ * call. The first step of the initialization is done during program startup
+ * the second part is done during DOS boot, from a call of the DOS driver.
+ * The DOS driver is neccessary because DOS programs look for it to
+ * determine if EMS is available.
+ *
+ * To emulate a configurable amount of EMS memory we use a file created
+ * at startup with the size of the configured EMS memory. This file is
+ * mapped into the EMS window like any DOS memory manager would do, using
+ * mmap calls.
+ *
+ * The emulation follows the LIM EMS 4.0 standard. Not all functions of it
+ * are implemented yet. The "alter page map and jump" and "alter page map
+ * and call" functions are not implemented, because they are rather hard to
+ * do. (It would mean a call to the emulator executes a routine in EMS 
+ * memory and returns to the emulator, the emulator switches the page map
+ * and then returns to the DOS program.) LINUX does not emulate this 
+ * functions and I think they were very rarely used by DOS applications.
+ *
+ * Credits: To the writers of LINUX dosemu, I looked at their code
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "doscmd.h"
+#include "ems.h"
+
+/* Will be configurable */
+u_long ems_max_size   = EMS_MAXSIZE * 1024;
+u_long ems_frame_addr = EMS_FRAME_ADDR;
+
+/*
+ * Method for EMS: Allocate a mapfile with the size of EMS memory
+ * and map the needed part into the page frame 
+ */
+
+#define EMS_MAP_PATH   "/var/tmp/"     /* Use a big file system */
+#define EMS_MAP_FILE   "doscmd.XXXXXX"
+static int mapfile_fd = -1;
+
+/* Pages are always 16 kB in size. The page frame is 64 kB, there are
+ * 4 positions (0..3) for a page to map in. The pages are numbered from 0 to
+ * the highest 16 kB page in the mapfile, depending on the EMS size
+ */
+
+EMS_mapping_context ems_mapping_context;
+
+/* Handle and page management (see ems.h) */
+
+/* The handle array. If the pointer is NULL, the handle is unallocated */
+static EMS_handle *ems_handle[EMS_NUM_HANDLES];
+static u_long ems_alloc_handles;
+/* The active handle, if any */
+static short active_handle;
+
+/* The page array. It is malloced at runtime, depending on the total
+ * allocation size
+ */
+
+static EMS_page *ems_page = NULL;
+static u_long ems_total_pages;
+static u_long ems_alloc_pages;
+static u_long ems_free_pages;
+
+/* Local structure used for region copy and move operations */
+
+struct copydesc {
+#define SRC_EMS 1
+#define DST_EMS 2
+    short     copytype;                /* Type of source and destination memory */
+    EMS_addr  src_addr;                /* Combined pointer for source */
+    EMS_addr  dst_addr;                /* Combined pointer for destination */
+    u_long  rest_len;          /* Lenght to copy */
+};
+
+
+/* Local prototypes */
+static int     init_mapfile(void);
+static void    map_page(u_long, u_char, short, int);
+static EMS_handle      *get_new_handle(long);
+static void    context_to_handle(short);
+static long    find_next_free_handle(void);
+static short   lookup_handle(Hname *hp);
+static void    allocate_pages_to_handle(u_short, long);
+static void    allocate_handle(short, long);
+static void    reallocate_pages_to_handle(u_short, long);
+static void    free_handle(short);
+static void    free_pages_of_handle(short);
+static void    restore_context(EMS_mapping_context *);
+static void    save_context_to_dos(EMScontext *);
+static int     check_saved_context(EMScontext *);
+static void    *get_valid_pointer(u_short, u_short, u_long);
+static u_long  move_ems_to_conv(short, u_short, u_short, u_long, u_long);
+static u_long  move_conv_to_ems(u_long, u_short, u_short, u_short, u_long);
+static u_long  move_ems_to_ems(u_short, u_short, u_short, u_short, 
+                               u_short, u_short, u_long);
+
+/* 
+ * EMS initialization routine: Return 1, if successful, return 0 if
+ * init problem or EMS disabled
+ */
+
+int
+ems_init()
+{
+    unsigned i;
+
+    if (ems_max_size == 0)
+       return 0;
+    if (init_mapfile() == 0)
+       return 0;
+    /* Sanity */
+    bzero((void *)(&ems_handle[0]), sizeof(ems_handle));
+    ems_total_pages = ems_max_size / EMS_PAGESIZE;
+    ems_alloc_pages = 0;
+    ems_free_pages = ems_total_pages;
+    ems_alloc_handles = 0;
+    active_handle = 0;
+    /* Malloc the page array */
+    ems_page = (EMS_page *)malloc(sizeof(EMS_page) * ems_total_pages);
+    if (ems_page == NULL) {
+       debug(D_ALWAYS, "Could not malloc page array, EMS disabled\n");
+       ems_frame_addr = 0;
+       ems_max_size = 0;
+       ems_total_pages = 0;
+       return 0;
+    }
+    for (i = 0; i < ems_total_pages; i++) {
+       ems_page[i].handle = 0;
+       ems_page[i].status = EMS_FREE;
+    }
+    debug(D_EMS, "EMS: Emulation init OK.\n");
+    return 1;
+}
+
+
+/* Main entry point */
+
+void
+ems_entry(regcontext_t *REGS)
+{
+    /*
+     * If EMS is not enabled, the DOS ems.exe module should not have
+     * been loaded. If it is loaded anyway, report software malfunction
+     */
+    if (ems_max_size == 0) {
+       R_AH = EMS_SW_MALFUNC;
+       debug(D_EMS, "EMS emulation not enabled\n");
+       return;
+    }
+
+    switch (R_AH)
+    {
+       case GET_MANAGER_STATUS:
+           debug(D_EMS, "EMS: Get manager status\n");
+           R_AH = EMS_SUCCESS;     
+           break;
+
+       case GET_PAGE_FRAME_SEGMENT:
+           debug(D_EMS, "EMS: Get page frame segment\n");
+           R_BX = ems_frame_addr >> 4;
+           R_AH = EMS_SUCCESS;     
+           break;
+
+       case GET_PAGE_COUNTS:
+           R_BX = ems_total_pages - ems_alloc_pages;
+           R_DX = ems_total_pages;
+           debug(D_EMS, "EMS: Get page count: Returned total=%d, free=%d\n", 
+               R_DX, R_BX);
+           R_AH = EMS_SUCCESS;     
+           break;
+
+       case GET_HANDLE_AND_ALLOCATE:
+        {
+           u_short npages;
+           short handle;
+
+           npages = R_BX;
+           debug(D_EMS, "EMS: Get handle and allocate %d pages: ", npages);
+
+           /* Enough handles? */
+           if ((handle = find_next_free_handle())  < 0) {
+               debug(D_EMS,"Return error:No handles\n");
+               R_AH = EMS_OUT_OF_HANDLES;
+               break;
+           }
+           /* Enough memory for this request ? */
+           if (npages > ems_free_pages) {
+               debug(D_EMS,"Return error:Request too big\n");
+               R_AH = EMS_OUT_OF_LOG;
+               break;
+           }
+           if (npages > ems_total_pages) {
+               debug(D_EMS,"Return error:Request too big\n");
+               R_AH = EMS_OUT_OF_PHYS;
+               break;
+           }
+           /* Not allowed to allocate zero pages with this function */
+           if (npages == 0) {
+               debug(D_EMS,"Return error:Cannot allocate 0 pages\n");
+               R_AH = EMS_ZERO_PAGES;
+               break;
+           }
+           /* Allocate the handle */
+           allocate_handle(handle, npages);
+
+           /* Allocate the pages */
+           allocate_pages_to_handle(handle, npages);
+           R_DX = handle;
+           R_AH = EMS_SUCCESS;     
+           debug(D_EMS,"Return success:Handle = %d\n", handle);
+           break;
+       }
+
+       case MAP_UNMAP:
+       {
+           u_char position;
+           u_short hpagenum, spagenum;
+           short handle;
+           
+           debug(D_EMS, "EMS: Map/Unmap handle=%d, pos=%d, pagenum=%d ", 
+               R_DX, R_AL, R_BX);
+           handle = R_DX;
+           position = R_AL;
+           if (position > 3) {
+               debug(D_EMS, "invalid position\n");
+               R_AH = EMS_ILL_PHYS;
+               break;
+           }
+           hpagenum = R_BX;
+           /* This succeeds without a valid handle ! */
+           if (hpagenum == 0xffff) {
+               /* Unmap only */
+               map_page(0, position, handle, 1);
+               debug(D_EMS, "(unmap only) success\n");
+               R_AH = EMS_SUCCESS;
+               break;
+           }
+           if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
+               R_AH = EMS_INV_HANDLE;
+               debug(D_EMS, "invalid handle\n");
+               break;
+           }
+           if (hpagenum >= ems_handle[handle]->npages) {
+               R_AH = EMS_LOGPAGE_TOOBIG;
+               debug(D_EMS, "invalid pagenumber\n");
+               break;
+           }
+           spagenum = ems_handle[handle]->pagenum[hpagenum];
+           map_page(spagenum, position, handle, 0);
+           debug(D_EMS, "success\n");
+           R_AH = EMS_SUCCESS;
+           break;
+       }
+
+       case DEALLOCATE_HANDLE:
+       {
+           short handle;
+
+           /* Handle valid ? */
+           handle = R_DX;
+           debug(D_EMS, "EMS: Deallocate handle %d\n", handle);
+           if (handle > 255 || ems_handle[handle] == NULL) {
+               R_AH = EMS_INV_HANDLE;
+               break;
+           }
+           /* Mapping context saved ? */
+           if (ems_handle[handle]->mcontext != NULL) {
+               R_AH = EMS_SAVED_MAP;
+               break;
+           }
+
+           free_pages_of_handle(handle);
+           free_handle(handle);
+           R_AH = EMS_SUCCESS;     
+           break;
+       }
+
+       case GET_EMM_VERSION:
+           debug(D_EMS, "EMS: Get version\n");
+           R_AL = EMS_VERSION;
+           R_AH = EMS_SUCCESS;     
+           break;
+
+       case SAVE_PAGE_MAP:
+       {
+           short handle;
+
+           debug(D_EMS, "EMS: Save page map\n");
+           handle = R_DX;
+           if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
+               R_AH = EMS_INV_HANDLE;
+               break;
+           }
+           if (ems_handle[handle]->mcontext != NULL) {
+               /* There is already a context saved */
+               if (memcmp((void *)ems_handle[handle]->mcontext,
+                          (void *)&ems_mapping_context,
+                          sizeof(EMS_mapping_context)) == 0)
+                    R_AH = EMS_ALREADY_SAVED;
+               else 
+                    R_AH = EMS_NO_ROOM_TO_SAVE;
+               break;
+           }
+           context_to_handle(handle);
+           R_AH = EMS_SUCCESS;     
+           break;
+       }
+
+       case RESTORE_PAGE_MAP:
+       {
+           short handle;
+
+           debug(D_EMS, "EMS: Restore page map\n");
+           handle = R_DX;
+           if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
+               R_AH = EMS_INV_HANDLE;
+               break;
+           }
+           if (ems_handle[handle]->mcontext == NULL) {
+               R_AH = EMS_NO_SAVED_CONTEXT;
+               break;
+           }
+           restore_context(ems_handle[handle]->mcontext);
+           free((void *)ems_handle[handle]->mcontext);
+           ems_handle[handle]->mcontext = NULL;
+           R_AH = EMS_SUCCESS;     
+           break;
+       }
+
+       case RESERVED_1:
+       case RESERVED_2:
+           debug(D_ALWAYS, "Reserved function called: %02x\n", R_AH);
+           R_AH = EMS_FUNC_NOSUP;
+           break;
+
+       case GET_HANDLE_COUNT:
+           debug(D_EMS, "EMS: Get handle count\n");
+           R_BX = ems_alloc_handles + 1;
+           R_AH = EMS_SUCCESS;     
+           break;
+
+       case GET_PAGES_OWNED:
+       {
+           short handle;
+
+           debug(D_EMS, "EMS: Get pages owned\n");
+           /* Handle valid ? */
+           handle = R_DX;
+           if (handle > 255 || ems_handle[handle] == NULL) {
+               R_AH = EMS_INV_HANDLE;
+               break;
+           }
+           if (handle == 0)
+               R_BX = 0;
+           else
+               R_BX = ems_handle[handle]->npages;
+           R_AH = EMS_SUCCESS;     
+           break;
+       }
+
+       case GET_PAGES_FOR_ALL:
+       {
+           EMShandlepage *ehp;
+           unsigned safecount;
+           int i;
+
+           debug(D_EMS, "EMS: Get pages for all\n");
+           /* Get the address passed from DOS app */
+           ehp = (EMShandlepage *)get_valid_pointer(R_ES, R_DI,
+                       sizeof(EMShandlepage) * ems_alloc_handles); 
+           if (ehp == NULL) {
+               R_AH = EMS_SW_MALFUNC;
+               break;
+           }
+
+           R_BX = ems_alloc_handles;
+           safecount = 0;
+           for (i = 0; i < 255; i++) {
+               if (ems_handle[i] != NULL) {
+                   if (safecount > (ems_alloc_handles+1))
+                       fatal("EMS: ems_alloc_handles is wrong, "
+                             "cannot continue\n");
+                   ehp->handle = i;
+                   ehp->npages = ems_handle[i]->npages;
+                   ehp++;
+                   safecount++;
+               }
+           }
+           R_AH = EMS_SUCCESS;     
+           break;
+        }
+
+       case PAGE_MAP:
+       /* This function is a nuisance. It was invented to save time and
+         * memory, but in our case it is useless. We have to support it
+         * but we use the same save memory as for the page map function.
+         * It uses only 20 bytes anyway. We store/restore the entire mapping
+        */
+       case PAGE_MAP_PARTIAL:
+       {
+           int subfunction;
+           EMScontext *src, *dest;
+
+           debug(D_EMS, "EMS: Page map ");
+           subfunction = R_AL;
+           if (R_AH == PAGE_MAP_PARTIAL) {
+               debug(D_EMS, "partial ");
+               /* Page map partial has slightly different subfunctions
+                * GET_SET does not exist and is GET_SIZE in this case 
+                */
+               if (subfunction == GET_SET)
+                   subfunction = GET_SIZE;
+           }
+           switch (subfunction)
+           {
+               case GET:
+               {
+                   debug(D_EMS, "get\n");
+                   /* Get the address passed from DOS app */
+                   dest = (EMScontext *)get_valid_pointer(R_ES, R_DI, 
+                       sizeof(EMScontext));
+                   if (dest == NULL) {
+                       R_AH = EMS_SW_MALFUNC;
+                       break;
+                   }
+                   save_context_to_dos(dest);
+                   R_AH = EMS_SUCCESS;     
+                   break;
+               }
+               case SET:
+               {
+                   debug(D_EMS, "set\n");
+                   src = (EMScontext *)get_valid_pointer(R_DS, R_SI, 
+                       sizeof(EMScontext));
+                   if (src == NULL) {
+                       R_AH = EMS_SW_MALFUNC;
+                       break;
+                   }
+                   if (check_saved_context(src) == 0) {
+                       R_AH = EMS_SAVED_CONTEXT_BAD;
+                       break;
+                   }
+                   restore_context(&src->ems_saved_context);
+                   R_AH = EMS_SUCCESS;     
+                   break;
+               }
+               case GET_SET:
+               {
+                   debug(D_EMS, "get/set\n");
+                   dest = (EMScontext *)get_valid_pointer(R_ES, R_DI, 
+                       sizeof(EMScontext));
+                   if (dest == NULL) {
+                       R_AH = EMS_SW_MALFUNC;
+                       break;
+                   }
+                   save_context_to_dos(dest);
+                   src = (EMScontext *)get_valid_pointer(R_DS, R_SI, 
+                       sizeof(EMScontext));
+                   if (src == NULL) {
+                       R_AH = EMS_SW_MALFUNC;
+                       break;
+                   }
+                   if (check_saved_context(src) == 0) {
+                       R_AH = EMS_SAVED_CONTEXT_BAD;
+                       break;
+                   }
+                   restore_context(&src->ems_saved_context);
+                   R_AH = EMS_SUCCESS;     
+                   break;
+               }
+               case GET_SIZE:
+                   debug(D_EMS, "get size\n");
+                   R_AL = (sizeof(EMScontext) + 1) & 0xfe;
+                   R_AH = EMS_SUCCESS;     
+                   break;
+               default:
+                   debug(D_EMS, "invalid subfunction\n");
+                   R_AH = EMS_INVALID_SUB;
+                   break;
+           }
+           break;
+       }
+
+       case MAP_UNMAP_MULTI_HANDLE:
+       {
+           u_char position;
+           u_short hpagenum, spagenum;
+           short handle;
+           EMSmapunmap *mp;
+           int n_entry, i;
+           
+           
+           debug(D_EMS, "EMS: Map/Unmap multiple ");
+
+           if ((n_entry = R_CX) > 3) {
+               R_AH = EMS_ILL_PHYS;
+           }
+
+           /* This is valid according to the LIM EMS 4.0 spec */
+           if (n_entry == 0) {
+               R_AH = EMS_SUCCESS;     
+               break;
+           }
+
+           handle = R_DX;
+           if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
+               R_AH = EMS_INV_HANDLE;
+               break;
+           }
+
+           mp = (EMSmapunmap *)get_valid_pointer(R_DS, R_SI,
+                       sizeof(EMSmapunmap) * n_entry);
+           if (mp == NULL) {
+               R_AH = EMS_SW_MALFUNC;
+               break;
+           }
+
+           R_AH = EMS_SUCCESS;
+           /* Walk through the table and map/unmap */
+           for (i = 0; i < n_entry; i++) {
+               hpagenum = mp->log;
+               /* Method is in R_AL */
+               if (R_AL == 0) {
+                   debug(D_EMS, "phys page method\n");
+                   if (mp->phys <= 3) {
+                       position = mp->phys;
+                   } else {
+                       R_AH = EMS_ILL_PHYS;
+                       break;
+                   }
+               } else if (R_AL == 1) {
+                   /* Compute position from segment address */
+                   u_short p_seg;
+
+                   debug(D_EMS, "segment method\n");
+                   p_seg = mp->phys;
+                   p_seg -= ems_frame_addr;
+                   p_seg /= EMS_PAGESIZE;
+                   if (p_seg <= 3) {
+                       position = p_seg;
+                   } else {
+                       R_AH = EMS_ILL_PHYS;
+                       break;
+                   }
+               } else {
+                   debug(D_EMS, "invalid subfunction\n");
+                   R_AH = EMS_INVALID_SUB;
+                   break;
+               }
+
+               mp++;
+               if (hpagenum == 0xffff) {
+                   /* Unmap only */
+                   map_page(0, position, handle, 1);
+                   continue;
+               }
+               if (hpagenum >= ems_handle[handle]->npages) {
+                   R_AH = EMS_LOGPAGE_TOOBIG;
+                   break;
+               }
+               spagenum = ems_handle[handle]->pagenum[hpagenum];
+               map_page(spagenum, position, handle, 0);
+           }
+           break;
+       }
+
+       case REALLOC_PAGES:
+       {
+           short handle;
+           u_long newpages;
+
+           debug(D_EMS, "EMS: Realloc pages ");
+
+           handle = R_DX;
+           if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
+               R_AH = EMS_INV_HANDLE;
+               debug(D_EMS, "invalid handle\n");
+               break;
+           }
+           newpages = R_BX;
+           debug(D_EMS, "changed from %ld to %ld pages\n", 
+               ems_handle[handle]->npages, newpages);
+
+           /* Case 1: Realloc to zero pages */
+           if (newpages == 0) {
+               free_pages_of_handle(handle);
+               R_AH = EMS_SUCCESS;     
+               break;
+           }
+           /* Case 2: New allocation is equal to allocated number */
+           if (newpages == ems_handle[handle]->npages) {
+               R_AH = EMS_SUCCESS;     
+               break;
+           }
+           /* Case 3: Reallocate to bigger and smaller sizes */
+           if (newpages > ems_handle[handle]->npages) {
+               if (newpages > ems_free_pages) {
+                   R_AH = EMS_OUT_OF_LOG;
+                    break;
+               }
+               if (newpages > ems_total_pages) {
+                   R_AH = EMS_OUT_OF_PHYS;
+                    break;
+               }
+           }
+           reallocate_pages_to_handle(handle, newpages);
+           R_AH = EMS_SUCCESS;     
+           break;
+       }
+
+       /* We do not support nonvolatile pages */
+       case HANDLE_ATTRIBUTES:
+           debug(D_EMS, "Handle attributes called\n");
+           switch (R_AL) {
+               case GET:
+               case SET:
+                   R_AH = EMS_FEAT_NOSUP;
+                   break;
+               case HANDLE_CAPABILITY:
+                   R_AL = 0;           /* Volatile only */
+                   R_AH = EMS_SUCCESS;     
+                   break;
+               default:
+                   R_AH = EMS_FUNC_NOSUP;
+                   break;
+           }
+           break;
+
+       case HANDLE_NAME:
+       {
+           short handle;
+           Hname *hp;
+
+           handle = R_DX;
+           if (handle > 255 || handle == 0 || ems_handle[handle] == NULL) {
+               R_AH = EMS_INV_HANDLE;
+               debug(D_EMS, "invalid handle\n");
+               break;
+           }
+           switch (R_AL) {
+               case GET:
+                   if ((hp = (Hname *)get_valid_pointer(R_ES, R_DI, 8))
+                    == NULL) {
+                       R_AH = EMS_SW_MALFUNC;
+                       break;
+                   }
+                   *hp = ems_handle[handle]->hname;
+                   R_AH = EMS_SUCCESS;     
+                   break;
+
+               case SET:
+                   if ((hp = (Hname *)get_valid_pointer(R_DS, R_SI, 8))
+                    == NULL) {
+                       R_AH = EMS_SW_MALFUNC;
+                       break;
+                   }
+                   /* If the handle name is not 0, it may not exist */
+                   if ((hp->ul_hn[0] | hp->ul_hn[1]) != 0) {
+                       if (lookup_handle(hp) == 0) {
+                           ems_handle[handle]->hname = *hp;
+                           R_AH = EMS_SUCCESS;     
+                       } else {
+                           R_AH = EMS_NAME_EXISTS;     
+                           break;
+                       }
+                   } else {
+                       /* Name is deleted (set to zeros) */
+                       ems_handle[handle]->hname = *hp;
+                       R_AH = EMS_SUCCESS;     
+                   }
+                   break;
+
+               default:
+                   R_AH = EMS_FUNC_NOSUP;
+                   break;
+           }
+           break;
+       }
+
+       case HANDLE_DIRECTORY:
+       {
+           int i;
+           EMShandledir *hdp;
+           Hname *hp;
+           short handle;
+
+           switch(R_AL) {
+               case GET:
+                   hdp = (EMShandledir *)get_valid_pointer(R_ES, R_DI, 
+                       sizeof(EMShandledir) * ems_alloc_handles);
+                   if (hdp == NULL) {
+                       R_AH = EMS_SW_MALFUNC;
+                       break;
+                   }
+                   for (i = 0; i < EMS_NUM_HANDLES; i++) {
+                       if (ems_handle[i] != NULL) {
+                           hdp->log = i;
+                           hdp->name = ems_handle[i]->hname;
+                       }
+                   }
+                   R_AH = EMS_SUCCESS;     
+                   break;
+
+               case HANDLE_SEARCH:
+                   hp = (Hname *)get_valid_pointer(R_DS, R_SI, 8);
+                   if (hp == NULL) {
+                       R_AH = EMS_SW_MALFUNC;
+                       break;
+                   }
+                   /* Cannot search for NULL handle name */
+                   if ((hp->ul_hn[0] | hp->ul_hn[1]) != 0) {
+                       R_AH = EMS_NAME_EXISTS;
+                       break;
+                   }
+                   if ((handle = lookup_handle(hp)) == 0) {
+                       R_AH = EMS_HNAME_NOT_FOUND;     
+                   } else {
+                       R_DX = handle;
+                       R_AH = EMS_SUCCESS;     
+                   }
+                   break;
+
+               case GET_TOTAL_HANDLES:
+                   R_AH = EMS_SUCCESS;     
+                   R_BX = EMS_NUM_HANDLES;     /* Includes OS handle */
+                   break;
+
+               default:
+                   R_AH = EMS_FUNC_NOSUP;
+                   break;
+           }
+           break;
+       }
+
+
+       /* I do not know if we need this. LINUX emulation leaves it out
+         * so I leave it out too for now.
+        */
+       case ALTER_PAGEMAP_JUMP:
+           debug(D_ALWAYS, "Alter pagemap and jump used!\n");
+           R_AH = EMS_FUNC_NOSUP;
+           break;
+       case ALTER_PAGEMAP_CALL:
+           debug(D_ALWAYS, "Alter pagemap and call used!\n");
+           R_AH = EMS_FUNC_NOSUP;
+           break;
+
+
+       case MOVE_MEMORY_REGION:
+       {
+           EMSmovemem *emvp;
+           u_long src_addr, dst_addr;
+           u_short src_handle, dst_handle;
+
+           if (R_AL == EXCHANGE)
+               debug(D_EMS, "EMS: Exchange memory region ");
+           else
+               debug(D_EMS, "EMS: Move memory region ");
+
+           emvp = (EMSmovemem *)get_valid_pointer(R_DS, R_SI,
+                       sizeof(EMSmovemem));
+           if (emvp == NULL) {
+               debug(D_EMS, "Invalid structure pointer\n");
+               R_AH = EMS_SW_MALFUNC;
+               break;
+           }
+           /* Zero length is not an error */
+           if (emvp->length == 0) {
+               debug(D_EMS, "Zero length\n");
+               R_AH = EMS_SUCCESS;     
+               break;
+           }
+           /* Some checks */
+           if (emvp->src_type == EMS_MOVE_CONV) {
+               /* Conventional memory source */
+               src_addr = MAKEPTR(emvp->src_seg, emvp->src_offset);
+               /* May not exceed conventional memory */
+               if ((src_addr + emvp->length) > 640 * 1024) {
+                   R_AH = EMS_SW_MALFUNC;
+                   break;
+               }
+           } else {
+               /* Check the handle */
+               src_handle = emvp->src_handle;
+               if (src_handle > 255 || src_handle == 0 || 
+                       ems_handle[src_handle] == NULL) {
+                   R_AH = EMS_INV_HANDLE;
+                   debug(D_EMS, "invalid source handle\n");
+                   break;
+               }
+               /* Offset may not exceed page size */
+               if (emvp->src_offset >= (16 * 1024)) {
+                   R_AH = EMS_PAGEOFFSET;
+                   debug(D_EMS, "source page offset too big\n");
+                   break;
+               }
+           }
+
+           if (emvp->dst_type == EMS_MOVE_CONV) {
+               /* Conventional memory source */
+               dst_addr = MAKEPTR(emvp->dst_seg, emvp->dst_offset);
+               /* May not exceed conventional memory */
+               if ((dst_addr + emvp->length) > 640 * 1024) {
+                   R_AH = EMS_SW_MALFUNC;
+                   break;
+               }
+           } else {
+               /* Check the handle */
+               dst_handle = emvp->dst_handle;
+               if (dst_handle > 255 || dst_handle == 0 || 
+                       ems_handle[dst_handle] == NULL) {
+                   R_AH = EMS_INV_HANDLE;
+                   debug(D_EMS, "invalid destination handle\n");
+                   break;
+               }
+               /* Offset may not exceed page size */
+               if (emvp->dst_offset >= (16 * 1024)) {
+                   R_AH = EMS_PAGEOFFSET;
+                   debug(D_EMS, "destination page offset too big\n");
+                   break;
+               }
+           }
+
+           if (R_AL == MOVE) {
+               /* If it is conventional memory only, do it */
+               if (emvp->src_type == EMS_MOVE_CONV &&
+                   emvp->dst_type == EMS_MOVE_CONV) {
+                   memmove((void *)dst_addr, (void *)src_addr, 
+                       (size_t) emvp->length);
+                   debug(D_EMS, "conventional to conventional memory done\n");
+                   R_AH = EMS_SUCCESS;     
+                   break;
+               }
+               if (emvp->src_type == EMS_MOVE_EMS &&
+                   emvp->dst_type == EMS_MOVE_CONV)
+                   R_AH = move_ems_to_conv(src_handle, emvp->src_seg,
+                       emvp->src_offset, dst_addr, emvp->length);
+               else if (emvp->src_type == EMS_MOVE_CONV &&
+                    emvp->dst_type == EMS_MOVE_EMS)
+                   R_AH = move_conv_to_ems(src_addr, dst_handle,
+                             emvp->dst_seg, emvp->dst_offset, emvp->length);
+               else
+                   R_AH = move_ems_to_ems(src_handle, emvp->src_seg,
+                       emvp->src_offset, dst_handle, emvp->dst_seg,
+                       emvp->dst_offset, emvp->length);
+               debug(D_EMS, " done\n");
+               break;
+           } else {
+               /* exchange memory region */
+
+               /* We need a scratch area for the exchange */
+               void *buffer;
+               if ((buffer = malloc(emvp->length)) == NULL)
+                   fatal("EMS: Could not malloc scratch area for exchange");
+
+               /* If it is conventional memory only, do it */
+               if (emvp->src_type == EMS_MOVE_CONV &&
+                   emvp->dst_type == EMS_MOVE_CONV) {
+                   /* destination -> buffer */
+                   memmove(buffer, (void *)dst_addr, (size_t) emvp->length);
+                   /* Source -> destination */
+                   memmove((void *)dst_addr, (void *)src_addr, 
+                       (size_t) emvp->length);
+                   /* Buffer -> source */
+                   memmove((void *)src_addr, buffer, (size_t) emvp->length);
+                   free(buffer);
+                   debug(D_EMS, "conventional to conventional memory done\n");
+                   R_AH = EMS_SUCCESS;     
+                   break;
+               }
+
+               /* Exchange EMS with conventional */
+               if (emvp->src_type == EMS_MOVE_EMS &&
+                   emvp->dst_type == EMS_MOVE_CONV) {
+                   /* Destination -> buffer */
+                   memmove(buffer, (void *)dst_addr, (size_t) emvp->length);
+                   /* Source -> destination */
+                   R_AH = move_ems_to_conv(src_handle, emvp->src_seg,
+                              emvp->src_offset, dst_addr, emvp->length);
+                    if (R_AH != EMS_SUCCESS) {
+                       free(buffer);
+                       break;
+                   }
+                   /* Buffer -> source */
+                   R_AH = move_conv_to_ems((u_long)buffer, src_handle,
+                             emvp->src_seg, emvp->src_offset, emvp->length);
+
+                /* Exchange conventional with EMS */
+               } else if (emvp->src_type == EMS_MOVE_CONV &&
+                           emvp->dst_type == EMS_MOVE_EMS) {
+                   /* Destination -> buffer */
+                   R_AH = move_ems_to_conv(dst_handle, emvp->dst_seg,
+                       emvp->dst_offset, (u_long)buffer, emvp->length);
+                    if (R_AH != EMS_SUCCESS) {
+                       free(buffer);
+                       break;
+                   }
+                   /* Source -> destination */
+                   R_AH = move_conv_to_ems((u_long)buffer, dst_handle,
+                             emvp->dst_seg, emvp->dst_offset, emvp->length);
+                   /* Buffer -> source */
+                   memmove(buffer, (void *)src_addr, (size_t) emvp->length);
+
+               /* Exchange EMS with EMS */
+               } else {
+                   /* Destination -> buffer */
+                   R_AH = move_ems_to_conv(dst_handle, emvp->dst_seg,
+                       emvp->dst_offset, (u_long)buffer, emvp->length);
+                    if (R_AH != EMS_SUCCESS) {
+                       free(buffer);
+                       break;
+                   }
+                   /* Source -> destination */
+                   R_AH = move_ems_to_ems(src_handle, emvp->src_seg,
+                       emvp->src_offset, dst_handle, emvp->dst_seg,
+                       emvp->dst_offset, emvp->length);
+                   if (R_AH != EMS_SUCCESS) {
+                       free(buffer);
+                       break;
+                   }
+                   /* Buffer -> source */
+                   R_AH = move_conv_to_ems((u_long)buffer, src_handle,
+                             emvp->src_seg, emvp->src_offset, emvp->length);
+               }
+               free(buffer);
+           }
+           debug(D_EMS, " done\n");
+           break;
+       }
+
+       case GET_MAPPABLE_PHYS_ADDR:
+       {
+           switch (R_AL) {
+               case GET_ARRAY:
+               {
+                   EMSaddrarray *eadp;
+                   int i;
+                   u_short seg;
+
+                   eadp = (EMSaddrarray *)get_valid_pointer(R_ES, R_DI, 
+                        sizeof(EMSaddrarray) * 4);
+                   if (eadp == NULL) {
+                       R_AH = EMS_SW_MALFUNC;
+                       break;
+                   }
+                   for (i = 0, seg = (ems_frame_addr >> 4); i < 4; i++) {
+                       eadp->segm = seg;
+                       eadp->phys = i;
+                       eadp++;
+                       seg += 1024;
+                   }
+                   R_AH = EMS_SUCCESS;     
+                   break;
+               }
+               case GET_ARRAY_ENTRIES:
+                   /* There are always 4 positions, 4*16kB = 64kB */
+                   R_CX = 4;
+                   R_AH = EMS_SUCCESS;
+                   break;
+               default:
+                   R_AH = EMS_FUNC_NOSUP;
+                   break;
+           }
+           break;
+       }
+
+       /* This is an OS function in the LIM EMS 4.0 standard: It is
+        * usable only by an OS and its use can be disabled for all other
+        * programs. I think we do not need to support it. It is not
+        * implemented and it reports "disabled" to any caller.
+        */
+       case GET_HW_CONFIGURATION:
+           R_AH = EMS_FUNCTION_DISABLED;
+           break;
+
+       /* This function is a little different, it was defined with
+        * LIM EMS 4.0: It is allowed to allocate zero pages and raw
+        * page size (i.e. page size != 16kB) is supported. We have 
+        * only 16kB pages, so the second difference does not matter.
+        */
+       case ALLOCATE_PAGES:
+        {
+           u_short npages;
+           short handle;
+
+           npages = R_BX;
+           debug(D_EMS, "EMS: Get handle and allocate %d pages: ", npages);
+
+           /* Enough handles? */
+           if ((handle = find_next_free_handle()) < 0) {
+               debug(D_EMS,"Return error:No handles\n");
+               R_AH = EMS_OUT_OF_HANDLES;
+               break;
+           }
+           /* Enough memory for this request ? */
+           if (npages > ems_free_pages) {
+               debug(D_EMS,"Return error:Request too big\n");
+               R_AH = EMS_OUT_OF_LOG;
+               break;
+           }
+           if (npages > ems_total_pages) {
+               debug(D_EMS,"Return error:Request too big\n");
+               R_AH = EMS_OUT_OF_PHYS;
+               break;
+           }
+
+           /* Allocate the handle */
+           allocate_handle(handle, npages);
+
+           /* Allocate the pages */
+           allocate_pages_to_handle(handle, npages);
+           R_DX = handle;
+           R_AH = EMS_SUCCESS;     
+           debug(D_EMS,"Return success:Handle = %d\n", handle);
+           break;
+       }
+
+       /* This is an OS function in the LIM EMS 4.0 standard: It is
+        * usable only by an OS and its use can be disabled for all other
+        * programs. I think we do not need to support it. It is not
+        * implemented and it reports "disabled" to any caller.
+        */
+       case ALTERNATE_MAP_REGISTER:
+           R_AH = EMS_FUNCTION_DISABLED;
+           break;
+
+       /* We cannot support that ! */
+       case PREPARE_WARMBOOT:
+           R_AH = EMS_FUNC_NOSUP;
+           break;
+
+       case OS_FUNCTION_SET:
+           R_AH = EMS_FUNCTION_DISABLED;
+           break;
+
+       default:
+           debug(D_ALWAYS, "EMS: Unknown function called: %02x\n", R_AH);
+           R_AH = EMS_FUNC_NOSUP;
+           break;
+    }
+}
+
+/* Initialize the EMS memory: Return 1 on success, 0 on failure */
+
+static int
+init_mapfile()
+{
+    char path[256];
+    int mfd;
+
+    /* Sanity */
+    if (ems_max_size == 0)
+       return 0;
+    strcpy(path, EMS_MAP_PATH);
+    strcat(path, EMS_MAP_FILE);
+
+    mfd = mkstemp(path);
+
+    if (mfd < 0) {
+        debug(D_ALWAYS, "Could not create EMS mapfile, ");
+       goto fail;
+    }
+    unlink(path);
+    mapfile_fd = squirrel_fd(mfd);
+
+    if (lseek(mapfile_fd, (off_t)(ems_max_size - 1), 0) < 0) {
+       debug(D_ALWAYS, "Could not seek into EMS mapfile, ");
+       goto fail;
+    }
+    if (write(mapfile_fd, "", 1) < 0) {
+       debug(D_ALWAYS, "Could not write to EMS mapfile, ");
+       goto fail;
+    }
+    /* Unmap the entire page frame */
+    if (munmap((caddr_t)ems_frame_addr, 64 * 1024) < 0) {
+       debug(D_ALWAYS, "Could not unmap EMS page frame, ");
+       goto fail;
+    }
+    /* DOS programs will access the page frame without allocating 
+     * pages first. Microsoft diagnose MSD.EXE does this, for example
+     * We need to have memory here to avoid segmentation violation
+     */
+    if (mmap((caddr_t)ems_frame_addr, 64 * 1024,
+              PROT_EXEC | PROT_READ | PROT_WRITE,
+              MAP_ANON | MAP_FIXED | MAP_SHARED,
+             -1, 0) == MAP_FAILED) {
+       debug(D_ALWAYS, "Could not map EMS page frame, ");
+       goto fail;
+    }
+    bzero((void *)&ems_mapping_context, sizeof(EMS_mapping_context));
+    return (1);
+
+fail:
+    debug(D_ALWAYS, "EMS disabled\n");
+    ems_max_size = 0;
+    ems_frame_addr = 0;
+    return (0);
+}
+
+/* Map/Unmap pages into one of four positions in the frame segment */
+
+static void
+map_page(u_long pagenum, u_char position, short handle, int unmaponly)
+{
+    caddr_t map_addr;
+    size_t  len;
+    off_t   file_offs;
+
+    if (position > 3)
+       fatal("EMS: Internal error: Mapping position\n");
+
+    map_addr = (caddr_t)(ems_frame_addr + (1024 * 16 * (u_long)position));
+    len = 1024 * 16;
+    file_offs = (off_t)(pagenum * 16 * 1024);
+
+    if (ems_mapping_context.pos_mapped[position]) {
+        if (munmap(map_addr, len) < 0) {
+             fatal("EMS unmapping error: %s\nCannot recover\n", 
+               strerror(errno));
+       }
+       ems_page[ems_mapping_context.pos_pagenum[position]].status 
+               &= ~EMS_MAPPED;
+       ems_mapping_context.pos_mapped[position] = 0;
+       ems_mapping_context.handle[position] = 0;
+    }
+    if (unmaponly) {
+        /* DOS programs will access the page frame without allocating 
+         * pages first. Microsoft diagnose MSD.EXE does this, for example
+         * We need to have memory here to avoid segmentation violation
+         */
+       if (mmap((caddr_t)ems_frame_addr, 64 * 1024,
+              PROT_EXEC | PROT_READ | PROT_WRITE,
+              MAP_ANON | MAP_FIXED | MAP_SHARED,
+             -1, 0) == MAP_FAILED)
+           fatal("Could not map EMS page frame during unmap only\n");
+       return;
+    }
+    if (mmap(map_addr, len,
+              PROT_EXEC | PROT_READ | PROT_WRITE,
+              MAP_FILE | MAP_FIXED | MAP_SHARED,
+              mapfile_fd, file_offs) == MAP_FAILED) {
+        fatal("EMS mapping error: %s\nCannot recover\n", strerror(errno));
+    }
+    ems_mapping_context.pos_mapped[position] = 1;
+    ems_mapping_context.pos_pagenum[position] = pagenum;
+    ems_mapping_context.handle[position] = handle;
+    ems_page[pagenum].status |= EMS_MAPPED;
+}
+
+/* Get a pointer from VM86 app, check it and return it. This returns NULL
+ * if the pointer is not valid. We can check only for very limited
+ * criteria: The pointer and the area defined by size may not point to
+ * memory over 1MB and it may not may to addresses under 1kB, because there
+ * is the VM86 interrupt table.
+ */
+static void 
+*get_valid_pointer(u_short seg, u_short offs, u_long size)
+{
+    u_long addr;
+    addr = MAKEPTR(seg, offs);
+    /* Check bounds */
+    if ((addr + size) >= (1024 * 1024) || addr < 1024)
+       return NULL;
+    else
+       return (void *)addr;
+}
+
+/* Malloc a new handle */
+static EMS_handle
+*get_new_handle(long npages)
+{
+    EMS_handle *ehp;
+    size_t dynsize = sizeof(EMS_handle) + sizeof(short) * npages;
+
+    if ((ehp = calloc(1, dynsize)) == NULL)
+       fatal("Cannot malloc EMS handle, cannot continue\n");
+    return ehp;
+}
+
+/* Allocate a mapping context to a handle */
+static void
+context_to_handle(short handle)
+{
+    EMS_mapping_context *emc;
+
+    if (ems_handle[handle] == NULL)
+       fatal("EMS context_to_handle called with invalid handle\n");
+    if ((emc = calloc(1, sizeof(EMS_mapping_context))) == NULL)
+       fatal("EMS Cannot malloc mapping context, cannot continue\n");
+    ems_handle[handle]->mcontext = emc;
+    memmove((void *)emc, (void *)&ems_mapping_context, 
+             sizeof(EMS_mapping_context));
+}
+   
+/* Find the next free handle, returns -1 if there are no more handles */
+static long
+find_next_free_handle()
+{
+    int i;
+
+    if (ems_alloc_handles >= 255)
+       return (-1);
+    /* handle 0 is OS handle */
+    for (i = 1; i < EMS_NUM_HANDLES; i++) {
+       if (ems_handle[i] == NULL)
+           return (i);
+    }
+    fatal("EMS handle count garbled, should not happen\n");
+    /* quiet 'gcc -Wall' */
+    return (-1);
+}
+
+/* Look for a named handle, returns 0 if not found, else handle */
+static short
+lookup_handle(Hname *hp)
+{
+    int i;
+
+    for (i = 1; i < EMS_NUM_HANDLES; i++) {
+       if (ems_handle[i] != NULL) {
+           if (hp->ul_hn[0] == ems_handle[i]->hname.ul_hn[0] &&
+               hp->ul_hn[1] == ems_handle[i]->hname.ul_hn[1])
+               return (i);
+       }
+    }
+    return (0);
+}    
+
+/* Malloc a new handle struct and put into array at index handle */
+static void
+allocate_handle(short handle, long npages)
+{
+    if (ems_handle[handle] != NULL)
+       fatal("EMS allocate_handle, handle was not free\n");
+    ems_handle[handle] = get_new_handle(npages);
+    ems_alloc_handles++;
+}
+
+/* Free a handle, return its memory. Call this *after* freeing the
+ * allocated pages !
+ */
+static void
+free_handle(short handle)
+{
+    if (ems_handle[handle] == NULL)
+       fatal("EMS free_handle, handle was free\n");
+    if (ems_handle[handle]->mcontext != NULL)
+       free((void *)ems_handle[handle]->mcontext);
+    free((void *)ems_handle[handle]);
+    ems_handle[handle] = NULL;
+    ems_alloc_handles--;
+}
+
+
+/* Allocates npages to handle. Call this routine only after you have
+ * ensured there are enough free pages *and* the new handle is in place
+ * in the handle array !
+ */
+static void
+allocate_pages_to_handle(u_short handle, long npages)
+{
+    unsigned syspagenum;
+    int pages_to_alloc = npages;
+    int allocpagenum = 0;
+
+    /* sanity */
+    if (handle > 255 || ems_handle[handle] == NULL)
+       fatal("EMS allocate_pages_to_handle called with invalid handle\n");
+
+    ems_handle[handle]->npages = npages;    
+    for (syspagenum = 0; syspagenum < ems_total_pages; syspagenum++) {
+       if (ems_page[syspagenum].status == EMS_FREE) {
+           ems_page[syspagenum].handle = handle;
+           ems_page[syspagenum].status = EMS_ALLOCED;
+           ems_handle[handle]->pagenum[allocpagenum] = syspagenum;
+           allocpagenum++;
+           pages_to_alloc--;
+           if (pages_to_alloc == 0)
+               break;
+       }
+    }
+    if (pages_to_alloc > 0)
+       fatal("EMS allocate_pages_to_handle found not enough free pages\n");
+    ems_alloc_pages += npages;
+    ems_free_pages -= npages;
+}
+
+/* Reallocates npages to handle. Call this routine only after you have
+ * ensured there are enough free pages *and* the new handle is in place
+ * in the handle array !
+ */
+static void
+reallocate_pages_to_handle(u_short handle, long npages)
+{
+    unsigned allocpagenum;
+    unsigned syspagenum;
+    int pages_to_alloc;
+    long delta;
+    size_t dynsize;
+    EMS_handle *emp;
+
+    /* sanity */
+    if (handle > 255 || ems_handle[handle] == NULL)
+       fatal("EMS allocate_pages_to_handle called with invalid handle\n");
+
+    delta = npages - ems_handle[handle]->npages;
+    if (delta > 0) {
+       /* Grow array size and allocation */
+
+       emp = ems_handle[handle];
+       dynsize = sizeof(EMS_handle) + sizeof(short) * npages;
+
+       /* First step: Make room in the handle pagenum array */
+       if ((emp = (EMS_handle *)realloc((void *)emp, dynsize)) == NULL)
+           fatal("Cannot malloc EMS handle, cannot continue\n");
+       ems_handle[handle] = emp;
+
+       /* Second step: Add pages to the handle */ 
+        pages_to_alloc = delta;
+       allocpagenum = ems_handle[handle]->npages;
+        ems_handle[handle]->npages = npages;    
+        for (syspagenum = 0; syspagenum < ems_total_pages; syspagenum++) {
+           if (ems_page[syspagenum].status == EMS_FREE) {
+               ems_page[syspagenum].handle = handle;
+               ems_page[syspagenum].status = EMS_ALLOCED;
+               ems_handle[handle]->pagenum[allocpagenum] = syspagenum;
+               allocpagenum++;
+               pages_to_alloc--;
+               if (pages_to_alloc == 0)
+                   break;
+           }
+        }
+       if (pages_to_alloc > 0)
+           fatal("EMS allocate_pages_to_handle found not enough free pages\n");
+
+    } else {
+       /* Shrink array size and allocation */
+
+       /* First step: Deallocate all pages from new size to old size */
+       for (allocpagenum = npages; 
+               allocpagenum < ems_handle[handle]->npages; 
+               allocpagenum++) { 
+           syspagenum = ems_handle[handle]->pagenum[allocpagenum];
+
+           /* sanity */
+            if (syspagenum > ems_total_pages)
+                fatal("EMS free_pages_of_handle found invalid page number\n");
+           if (!(ems_page[syspagenum].status & EMS_ALLOCED))
+               fatal("EMS free_pages_of_handle tried to free page already free\n");
+           ems_page[syspagenum].handle = 0;
+           ems_page[syspagenum].status = EMS_FREE;
+       }
+
+       /* Second step: Shrink the dynamic array of the handle */       
+       dynsize = sizeof(EMS_handle) + sizeof(short) * npages;
+       emp = ems_handle[handle];
+       if ((emp = (EMS_handle *)realloc((void *)emp, dynsize)) == NULL)
+           fatal("Cannot realloc EMS handle, cannot continue\n");
+       ems_handle[handle] = emp;
+       ems_handle[handle]->npages = npages;
+    }
+    ems_alloc_pages += delta;
+    ems_free_pages -= delta;
+}
+
+/* Free all pages belonging to a handle, handle must be valid */
+static void
+free_pages_of_handle(short handle)
+{
+    int allocpagenum;
+    unsigned syspagenum;
+    int npages;
+
+    /* sanity */
+
+    if (handle > 255 || ems_handle[handle] == NULL)
+       fatal("EMS free_pages_of_handle called with invalid handle\n");
+
+    if ((npages = ems_handle[handle]->npages) == 0)
+       return;
+
+    for (allocpagenum = 0; allocpagenum < npages; allocpagenum++) {
+       syspagenum = ems_handle[handle]->pagenum[allocpagenum];
+       /* sanity */
+       if (syspagenum > ems_total_pages)
+           fatal("EMS free_pages_of_handle found invalid page number\n");
+       if (!(ems_page[syspagenum].status & EMS_ALLOCED))
+           fatal("EMS free_pages_of_handle tried to free page already free\n");
+       ems_page[syspagenum].handle = 0;
+       ems_page[syspagenum].status = EMS_FREE;
+    }
+    ems_alloc_pages -= npages;
+    ems_free_pages += npages;
+}
+
+/* Restore a saved mapping context, overwrites current mapping context */
+static void
+restore_context(EMS_mapping_context *emc)
+{
+    int i;
+
+    for (i = 0; i < 4; i++) {
+       ems_mapping_context.handle[i] = emc->handle[i];
+       if (emc->pos_mapped[i] != 0 &&
+           ems_mapping_context.pos_pagenum[i] != emc->pos_pagenum[i]) {
+           map_page(emc->pos_pagenum[i], (u_char) i, emc->handle[i], 0);
+       } else {
+           ems_mapping_context.pos_mapped[i] = 0;
+       }
+    }
+}
+
+/* Prepare a special context save block for DOS and save it to
+ * VM86 memory
+ */
+static void
+save_context_to_dos(EMScontext *emp)
+{
+    int i, end;
+    EMScontext context;
+    u_short *sp;
+    u_short sum;
+
+    context.ems_saved_context = ems_mapping_context;
+    context.magic = EMS_SAVEMAGIC;
+    context.checksum = 0;
+    sp = (u_short *)&context;
+    end = sizeof(EMScontext) / sizeof(short);
+    /* Generate checksum */
+    for (i = 0, sum = 0; i < end; i++) {
+       sum += *sp++;
+       sum &= 0xffff;
+    }
+    context.checksum = 0x10000L - sum;
+    /* Save it to VM86 memory */
+    *emp = context;
+}
+
+/* Check a context returned from VM86 app for validity, return 0, if
+ * not valid, else return 1
+ */
+static int
+check_saved_context(EMScontext *emp)
+{
+    int i, end;
+    u_short *sp;
+    u_short sum;
+
+    if (emp->magic != EMS_SAVEMAGIC)
+       return 0;
+
+    sp = (u_short *)emp;
+    end = sizeof(EMScontext) / sizeof(short);
+    /* Generate checksum */
+    for (i = 0, sum = 0; i < end; i++) {
+        sum += *sp++;
+        sum &= 0xffff;
+    }
+    if (sum != 0)
+       return 0;
+    else
+       return 1;
+}
+
+/* Helper routine for the move routines below: Check if length bytes
+ * can be moved from/to handle pages (i.e are there enough pages)
+ */
+static int
+check_alloc_pages(u_short handle, u_short firstpage, u_short offset, 
+                  u_long length __unused)
+{
+    u_long nbytes;
+
+    if (firstpage > ems_handle[handle]->npages)
+       return (0);
+    nbytes = (ems_handle[handle]->npages - firstpage) * EMS_PAGESIZE - offset;
+    return (ems_handle[handle]->npages >= nbytes);
+}
+
+/* Copy a block of memory up to the next 16kB boundary in the source
+ * to the destination in upward direction (i.e. with ascending addresses)
+ * XXX Could be an inline function.
+ */
+static void 
+copy_block_up(struct copydesc *cdp)
+{
+    size_t size;
+    void *srcp;
+    void *dstp;
+
+    /* If source or both memory types are EMS, source determines the
+     * block lenght, else destination determines the block lenght
+     */
+    if (cdp->copytype & SRC_EMS)
+       size = EMS_PAGESIZE - cdp->EMS_OFFS(src_addr);
+    else
+       size = EMS_PAGESIZE - cdp->EMS_OFFS(dst_addr);
+
+    if (size > cdp->rest_len)
+       size = cdp->rest_len;
+    /* If src is EMS memory, it is mapped into position 0 */
+    if (cdp->copytype & SRC_EMS)
+       srcp = (void *)(ems_frame_addr + cdp->EMS_OFFS(src_addr));
+    else
+       srcp = (void *)(cdp->EMS_PTR(src_addr));
+
+    /* If dest is EMS memory, it is mapped into position 1,2 */
+    if (cdp->copytype & DST_EMS)
+       dstp = (void *)(ems_frame_addr + EMS_PAGESIZE + 
+                                       cdp->EMS_OFFS(dst_addr));
+    else
+       dstp = (void *)(cdp->EMS_PTR(dst_addr));
+
+    /* Move this block */
+    memmove(dstp, srcp, size);
+
+    /* Update the copy descriptor: This updates the address of both 
+     * conventional and EMS memory 
+     */
+    cdp->EMS_PTR(src_addr) += size;
+    cdp->EMS_PTR(dst_addr) += size;
+
+    cdp->rest_len -= size;
+}
+
+
+/* Move EMS memory starting with handle page src_seg and offset src_offset
+ * to conventional memory dst_addr for length bytes
+ * dst_addr is checked, handle is valid 
+ */
+static u_long 
+move_ems_to_conv(short src_handle, u_short src_seg, 
+                       u_short src_offset, u_long dst_addr, u_long length)
+{
+    EMS_mapping_context ems_saved_context;
+    EMS_handle *ehp;
+    int pageindx = src_seg;
+    struct copydesc cd;
+
+    if (check_alloc_pages(src_handle, src_seg, src_offset, length) == 0)
+       return EMS_MOVE_OVERFLOW;
+
+    ehp = ems_handle[src_handle];
+
+    /* Prepare the move: Save the mapping context */
+    ems_saved_context = ems_mapping_context;
+
+    /* Setup the copy descriptor struct */
+
+    cd.copytype = SRC_EMS;
+    cd.EMS_PAGE(src_addr) = ehp->pagenum[pageindx];
+    cd.EMS_OFFS(src_addr) = src_offset;
+    cd.EMS_PTR(dst_addr) = dst_addr;
+    cd.rest_len = length;
+
+    do {
+       /* Map for the first block copy, source is mapped to position zero */
+       map_page(cd.EMS_PAGE(src_addr), 0, src_handle, 0);
+        copy_block_up(&cd);
+    } while(cd.rest_len > 0);   
+
+    /* Restore the original mapping */
+    restore_context(&ems_saved_context);
+    return EMS_SUCCESS;
+}
+
+/* Move conventional memory starting with src_addr
+ * to EMS memory starting with handle page src_seg and offset src_offset
+ * for length bytes
+ * dst_addr is checked, handle is valid 
+ */
+static u_long
+move_conv_to_ems(u_long src_addr, u_short dst_handle, u_short dst_seg,
+                 u_short dst_offset, u_long length)
+{
+    EMS_mapping_context ems_saved_context;
+    EMS_handle *ehp;
+    int pageindx = dst_seg;
+    struct copydesc cd;
+    
+    if (check_alloc_pages(dst_handle, dst_seg, dst_offset, length) == 0)
+       return EMS_MOVE_OVERFLOW;
+    
+    ehp = ems_handle[dst_handle];
+    
+    /* Prepare the move: Save the mapping context */
+    ems_saved_context = ems_mapping_context;
+
+    /* Setup the copy descriptor struct */
+
+    cd.copytype = DST_EMS;
+    cd.EMS_PAGE(dst_addr) = ehp->pagenum[pageindx];
+    cd.EMS_OFFS(dst_addr) = dst_offset;
+    cd.EMS_PTR(src_addr) = src_addr;
+    cd.rest_len = length;
+
+    do {
+       map_page(cd.EMS_PAGE(dst_addr), 1, dst_handle, 0);
+        copy_block_up(&cd);
+    } while(cd.rest_len > 0);   
+
+    /* Restore the original mapping */
+    restore_context(&ems_saved_context);
+    return EMS_SUCCESS;
+}
+    
+static u_long
+move_ems_to_ems(u_short src_handle, u_short src_seg, u_short src_offset,
+                u_short dst_handle, u_short dst_seg, u_short dst_offset,
+                u_long length)
+{
+    EMS_mapping_context ems_saved_context;
+    EMS_handle *src_hp, *dst_hp;
+    struct copydesc cd;
+    
+    if (check_alloc_pages(src_handle, src_seg, src_offset, length) == 0)
+       return EMS_MOVE_OVERFLOW;
+    if (check_alloc_pages(dst_handle, dst_seg, dst_offset, length) == 0)
+       return EMS_MOVE_OVERFLOW;
+    
+    src_hp = ems_handle[src_handle];
+    dst_hp = ems_handle[dst_handle];
+
+    /* Prepare the move: Save the mapping context */
+    ems_saved_context = ems_mapping_context;
+
+    /* Setup the copy descriptor struct */
+
+    cd.copytype = SRC_EMS | DST_EMS;
+    cd.EMS_PAGE(src_addr) = src_hp->pagenum[src_seg];
+    cd.EMS_OFFS(src_addr) = src_offset;
+    cd.EMS_PAGE(dst_addr) = dst_hp->pagenum[dst_seg];
+    cd.EMS_OFFS(dst_addr) = dst_offset;
+    cd.rest_len = length;
+    
+    /* Copy */
+    do {
+        map_page(cd.EMS_PAGE(src_addr), 0, src_handle, 0);
+       map_page(cd.EMS_PAGE(dst_addr), 1, dst_handle, 0);
+        /* If there are more pages, map the next destination page to
+         * position 2. This removes a compare between source and dest
+         * offsets.
+         */
+        if (cd.EMS_PAGE(dst_addr) < dst_hp->npages)
+           map_page((cd.EMS_PAGE(dst_addr) + 1), 2, dst_handle, 0);        
+        copy_block_up(&cd);
+    } while(cd.rest_len > 0);   
+
+    /* Restore the original mapping */
+    restore_context(&ems_saved_context);
+    return EMS_SUCCESS;
+}
diff --git a/16/emstulkt.zip b/16/emstulkt.zip
new file mode 100755 (executable)
index 0000000..c37594b
Binary files /dev/null and b/16/emstulkt.zip differ
diff --git a/16/tweak16/16.Q b/16/tweak16/16.Q
new file mode 100755 (executable)
index 0000000..8adfa57
Binary files /dev/null and b/16/tweak16/16.Q differ
index a59529ede4026a6d7fb0005621537d53c78f211a..057e0126a66a4d287508d79f9a407a0a30293c5f 160000 (submodule)
@@ -1 +1 @@
-Subproject commit a59529ede4026a6d7fb0005621537d53c78f211a
+Subproject commit 057e0126a66a4d287508d79f9a407a0a30293c5f