home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
DP Tool Club 16
/
CD_ASCQ_16_0994.iso
/
news
/
vr386
/
emmfsppt.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-01-09
|
13KB
|
513 lines
/* EMM memory support: all algoritims, math and assembly by Dave Stampe */
/* MAP-EMM Copyright 26/12/93 by Dave Stampe */
/* Written by Dave Stampe, December 1993 */
/* Contact: dstampe@sunee.waterloo.edu */
/*
This code is part of the VR-386 project, created by Dave Stampe.
VR-386 is a desendent of REND386, created by Dave Stampe and
Bernie Roehl. Almost all the code has been rewritten by Dave
Stampre for VR-386.
Copyright (c) 1994 by Dave Stampe:
May be freely used to write software for release into the public domain
or for educational use; all commercial endeavours MUST contact Dave Stampe
(dstampe@psych.toronto.edu) for permission to incorporate any part of
this software or source code into their products! Usually there is no
charge for under 50-100 items for low-cost or shareware products, and terms
are reasonable. Any royalties are used for development, so equipment is
often acceptable payment.
ATTRIBUTION: If you use any part of this source code or the libraries
in your projects, you must give attribution to VR-386 and Dave Stampe,
and any other authors in your documentation, source code, and at startup
of your program. Let's keep the freeware ball rolling!
DEVELOPMENT: VR-386 is a effort to develop the process started by
REND386, improving programmer access by rewriting the code and supplying
a standard API. If you write improvements, add new functions rather
than rewriting current functions. This will make it possible to
include you improved code in the next API release. YOU can help advance
VR-386. Comments on the API are welcome.
CONTACT: dstampe@psych.toronto.edu
*/
// an EMM memory manager utility
// this one HAS a block-based free() function
#include <alloc.h>
#include <dos.h>
#include <bios.h>
#include <stdio.h>
#include <stdlib.h>
#include "xmem.h" // to check types
//////////////////// EMM INTERFACE ////////////////
static unsigned EMMversion;
static unsigned EMMhandle = 0; // careful to check before using!
static unsigned EMMsegment;
static unsigned EMMtotpages;
static unsigned EMMfreepages;
#define EMM 0x67
#define EMM_STATUS 0x40
#define EMM_SEG 0x41
#define EMM_NPAGES 0x42
#define EMM_ALLOC 0x43
#define EMM_MAP 0x44
#define EMM_FREE 0x45
#define EMM_VERSION 0x46
#define EMM_MAPALL 0x5000
static union REGS regs;
static struct SREGS sregs;
static int EMMsetup()
{
unsigned i;
char *v = (char far *) _dos_getvect(EMM);
v = MK_FP(FP_SEG(v), 10);
if(strnicmp(v , "EMMXXXX0", 8) ) return 0; // no EMM!
regs.h.ah = EMM_STATUS;
int86(EMM,®s,®s);
if(regs.h.ah) return 0; // no driver
regs.h.ah = EMM_VERSION;
int86(EMM,®s,®s);
EMMversion = regs.h.al;
if(regs.h.ah) return 0;
if(EMMversion<0x40) return 0; // not version 4.0!
regs.h.ah = EMM_SEG;
int86(EMM,®s,®s);
EMMsegment = regs.x.bx;
if(regs.h.ah) return 0;
regs.h.ah = EMM_NPAGES;
int86(EMM,®s,®s);
EMMtotpages = regs.x.dx;
EMMfreepages = regs.x.bx;
if(regs.h.ah) return 0;
return EMMfreepages; // return free EMM, 0 is error
}
int EMMavail() // number of free pages
{
regs.h.ah = EMM_NPAGES;
int86(EMM,®s,®s);
EMMtotpages = regs.x.dx;
EMMfreepages = regs.x.bx;
if(regs.h.ah) return 0; // 0 = none/error
return EMMfreepages;
}
static unsigned EMMalloc(unsigned pages) // grab some pages
{
regs.h.ah = EMM_ALLOC;
regs.x.bx = pages;
int86(EMM,®s,®s);
EMMhandle = regs.x.dx;
return regs.h.ah; // return nonzero if error
}
static int EMMfree() // drop the pages we got
{
if(EMMhandle==0) return -1; // none allocated!
regs.h.ah = EMM_FREE;
regs.x.dx = EMMhandle;
int86(EMM,®s,®s);
return regs.h.ah; // return nonzero if error
}
// map page p to window part w
// one 16K page only (not used)
static int EMMmap(unsigned p_page, unsigned w_page)
{
regs.h.ah = EMM_MAP;
regs.h.al = w_page;
regs.x.bx = p_page;
int86(EMM,®s,®s);
return regs.h.ah; // return nonzero if error
}
static unsigned map[8] = {0,0,1,1,2,2,3,3}; // even bytes are pages
static int allEMMmap(int start) // map WHOLE window at once!
{
map[0] = start; // initialize map table
map[2] = start+1;
map[4] = start+2;
map[6] = start+3;
regs.x.ax = EMM_MAPALL; // map all 4 parts of window at once
regs.x.dx = EMMhandle;
regs.x.cx = 4;
regs.x.si = FP_OFF(map);
sregs.ds = FP_SEG(map);
int86x(EMM, ®s, ®s, &sregs);
return regs.h.ah; // return nonzero if error
}
////////////////// MEMORY BLOCK MANAGER SYSTEM /////////////////
///
// idea: each byte in a table represents fixed block of memory (512 bytes)
// each byte is offset to next, with the MSB reserved as a "free" flag
// So we have a maximum memory allocation size of 127 blocks (63.5K).
// free: simply sets "free" flag. An additional step is to check if the
// next block in the chain is free too, and combine them if the resultant
// block is small enough.
// alloc: searches for free block by incrementing pointer by byte values
// when a free block is found, it either slices a chunk off it, or if it
// is too small, checks to see if the next blocks are free to make a new
// large block. If not, it finds the next free block and continues.
#define FAILS 0xFFFF // returned if alloc fails
typedef unsigned char BYTE;
static BYTE *fbuf; // the block table
static int size = 8192; // records number of blocks;
#define INITM 64 // initial alloc units
static reset_block_manager()
{
if (fbuf) free(fbuf);
fbuf = NULL;
}
static int init_block_manager(int blocks) // clears, sets up initial large blocks
{
int i;
size = blocks;
fbuf = malloc(size);
if(fbuf==NULL) return -1;
for(i=0;i<blocks;i++) fbuf[i] = 0xff;
for(i=0;i<blocks-INITM;i+=INITM)
{
fbuf[i] = INITM | 128;
}
if(i<blocks) fbuf[i] = (blocks - i) | 128;
return 0;
}
static unsigned scan_free_blocks() // returns total free blocks
{
unsigned s = 0;
unsigned i = 0;
while(i<size)
{
if ((fbuf[i]&128)==128) s += fbuf[i] & 0x7F;
i += fbuf[i] & 0x7F;
}
return s;
}
static unsigned scan_used_blocks() // returns total blocks allocated
{
unsigned s = 0;
unsigned i = 0;
while(i<size)
{
if ((fbuf[i]&128)==0) s += fbuf[i];
i += fbuf[i] & 0x7F;
}
return s;
}
static void free_blocks(unsigned s) // frees memory, compacts with next
{
unsigned i, p;
i = fbuf[s]; // record for tests
fbuf[s] = i | 0x80; // that's it!
// now compact a wee bit
if(fbuf[s+i] & 0x80) // is next free too?
{
p = fbuf[i+s] & 0x7F; // size of next
if (i+p<120) // block would be too big: forget it
{
fbuf[s] = 0x80 | (p+i); // else make one big block
}
}
}
unsigned alloc_blocks(unsigned s) // allocate memory. See description
{
unsigned p; // block remainder
unsigned i; // search ptr
unsigned sacc; // aggregate accumulator
unsigned saccptr; // aggregate start
if(s>127 || s==0) return FAILS; // block too big
i = 0;
while(i<size) // scan till end
{
if( (fbuf[i]&0x80)==0 )
{
i += fbuf[i]; // block allocated: move on
}
else if( (fbuf[i]&0x7F) >= s) // big enough?
{
p = (fbuf[i]&0x7F) - s; // how much left
if(p) fbuf[i+s] = p | 128; // new free block
fbuf[i] = s; // alloc block
return i;
}
else // scan to see if contiguous free
{
saccptr = i; // record start
sacc = fbuf[i] & 0x7F; // record size to accum
while(i<size)
{
i += fbuf[i] & 0x7F; // next
if(fbuf[i] & 0x80) // free?
{
sacc += fbuf[i] & 0x7F; // add to total
if(sacc>=s) // got enough?
{
p = sacc - s; // how much left
if(p) fbuf[saccptr+s] = p | 128; // free block
fbuf[saccptr] = s; // alloc block
return saccptr;
}
}
else
{
i = saccptr + sacc; // we hit a used block! keep looking
break;
}
}
}
}
return FAILS; // got to end w/o block
}
///////////////////// EMM POINTER ACCESS SYSTEM ////////////////
/// uses a composite pointer, with the segment and offset
/// fiddled to contain a page number. Handles up to 4 Megs
int EMM_active = 0; // is EMM system operating?
static long EMMpages; // pages allocated
static long EMMblocksfree; // pages allocated
static long EMMblocks; // 512-byte blocks allocated
static unsigned allocseg = 0xCF00; // will contain "magic" segment base
static unsigned allocoff = 0; // will conain "magic" offset base
long alloccount = 0; // EMM blocks used <DEBUG>
int initEMMalloc(long wanted) // inits system arg is desired pages
{ // returns actual pages available
unsigned avp;
fprintf(stderr,"MAP-EMM Expanded memory access system for VR-386\n");
fprintf(stderr," Copyright (c) 1994 by Dave Stampe\n");
if(EMMsetup()==0)
{
fprintf(stderr,"No EMM driver found!\n");
return 0;
}
fprintf(stderr,"EMM driver version: %d.%d\n", EMMversion>>4, EMMversion & 0x0f);
if(wanted>256) wanted = 256; // can only handle 256 pages with coding
avp = EMMavail(); // take what we can get
if(wanted>avp) wanted = avp;
EMMpages = wanted; // total pages
EMMblocks = wanted<<5; // total blocks
EMMblocksfree = EMMblocks;
if(init_block_manager(EMMblocks) || // startup blocks
EMMalloc(wanted) )
{
fprintf(stderr,"Error: cannot allocate EMM memory!\n");
return NULL;
}
fprintf(stderr,"%d pages (%ldK) available, allocating %ldK\n",avp, ((long)avp)<<4, wanted<<4);
allEMMmap(0); // reset EMM map
allocseg = EMMsegment - 0x0100; // bump segment down so we can use 8 LSB
allocoff = 0x1000; // compensates for bump-down
EMM_active = 1;
return wanted; // returns actual pages granted
}
void resetEMMalloc() // shut down system
{
EMMfree();
reset_block_manager(); // reset manager
EMMblocks = 0;
EMM_active = 0;
}
// allocates memory, makes up "magic" pointer to it
void *EMMallocp(long n)
{
unsigned page, offset;
long block;
if(!EMM_active) return NULL;
if(n>48000L) return NULL; // cannot access more than 48K for sure
// n += 20000; // STRESS TEST
n = (n+511)>>9; // bump up to block size
block = alloc_blocks(n);
if(block==FAILS) return NULL; // not enough blocks!
EMMblocksfree -= n;
page = block>>5; // compute page, offset
offset = (block<<9) & 0x00003FFF;
alloccount++;
allEMMmap(page); // map into window
return MK_FP(allocseg | page,
allocoff + offset - (page<<4) ); // make a pointer into page
}
void EMMfreep(void *p)
{
long page, offset;
page = FP_SEG(p) & 0xFF;
offset = FP_OFF(p) - 0x1000 + page<<4 + page<<14; // true EMM address
free_blocks(offset>>9);
}
// WARNING: the access routine checks the external map! if you do any
// non-mapped access, you gotta reset it! Use this routine:
void restoreEMM()
{
if(!EMM_active) return;
allEMMmap(0); // restore if external EMM mapping used
}
//////////////////////////////////////////////////////////
/// REND386 interface
int accessptr(void *p) // makes pointer accessible up to 60K
{
unsigned page;
if(!EMM_active) return 0;
if(FP_SEG(p) < allocseg) return 0; // not in EMM
page = FP_SEG(p) & 0x00FF; // extract EMM page
if (page!=map[0]) // is it accessible now?
{
allEMMmap(page); // map WHOLE window at once!
return page+1; // had to map it.
}
return 0; // no mapping required
}
// the REND386 call: alloc from EMM if possible
// else do the usual way from DOS
void *extmalloc(long n)
{
void *p = NULL;
if(EMM_active) p = EMMallocp(n); // try to get EMM
if(p) return p;
return malloc(n); // if not, use MALLOC
}
// frees memory: tests to see if EMM, else
// does usual heap free
void extfree(void *p)
{
if(EMM_active!=0 && FP_SEG(p)>=allocseg)
{
EMMfreep(p);
return; // NO EMM FREE YET!
}
else
{
if(FP_SEG(p)<1)
{
fprintf(stderr,"ERROR: ATTEMPT TO FREE NULL POINTER!\n");
exit(0);
}
free(p);
}
}
long EMMheapsize() // EMM left
{
return EMMblocksfree<<9;
}
long EMMheapused() // EMM used so far
{
return (EMMblocks-EMMblocksfree)<<9;
}