From: sparky4 Date: Tue, 8 May 2018 13:57:37 +0000 (-0500) Subject: added more ems xms junk to the 16/ dir for reference X-Git-Url: http://4ch.mooo.com/gitweb/?p=16.git;a=commitdiff_plain;h=ff7ba1d826146a17e98811064334aab9bf2c9a47 added more ems xms junk to the 16/ dir for reference --- diff --git a/16/XMSARRAY.CPP b/16/XMSARRAY.CPP new file mode 100755 index 00000000..3ae24893 --- /dev/null +++ b/16/XMSARRAY.CPP @@ -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 +#include + +//-------------------------------------------------------------------------- +// +// 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; +} + +//-------------------------------------------------------------------------- +// +// 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); +} + +//-------------------------------------------------------------------------- +// +// 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)); +} + +//-------------------------------------------------------------------------- +// +// 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; +} + +//-------------------------------------------------------------------------- +// +// 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 friend class Y; ... }; +// 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; +} + +//-------------------------------------------------------------------------- +// +// 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 index 00000000..9571fc2f --- /dev/null +++ b/16/ems_.c @@ -0,0 +1,1693 @@ +/*- + * Copyright (c) 1997 Helmut Wirth + * 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 +__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 +#include +#include +#include + +#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 index 00000000..c37594bb 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 index 00000000..8adfa57a Binary files /dev/null and b/16/tweak16/16.Q differ diff --git a/16/vgmtools b/16/vgmtools index a59529ed..057e0126 160000 --- a/16/vgmtools +++ b/16/vgmtools @@ -1 +1 @@ -Subproject commit a59529ede4026a6d7fb0005621537d53c78f211a +Subproject commit 057e0126a66a4d287508d79f9a407a0a30293c5f