home *** CD-ROM | disk | FTP | other *** search
- File: MEMORY.TXT
- Path: ...\REHACK\TEXT\MEMORY.TXT
- Version: 0.01
- Author: Pat Reilly
- CIS Id: 71333,2764
- Created On: 6/29/93
- Modified On:
- Description: Info on MEMORY classes for REHACK.
- Tabs: 4
-
-
- Class Hierarchy
- ===============
-
- VMemManager
-
- MemoryObject --+-- ConvMemory
- |
- +-- EmsMemory
- |
- +-- XmsMemory
- |
- +-- DiskMemory
- |
- +-- UmbMemory (used internally by XmsMemory)
-
- Description
- ===========
-
- The classes in MEMORY.HPP form the basis for a virtual memory management
- system for REHACK. Because memory is virtual, it has to be loaded into
- conventional (0..1MB) space for your program to manipulate it. Because of
- this, you need to "lock" an object to get its address, and "unlock" it when
- you're done - the unlocking is so that you don't run out of places in
- conventional memory for buffers.
-
- Enum MemType
- ============
-
- This enumerates the memory types; its returned by an object's overriden
- virtual type() method, and is used in the VMemManager class (see below).
- MemTypes currently are:
-
- MemUnknown
- The "unknown" type - currently, UMB memory objects return this type,
- since they are designed to only be used internally by the XmsMemory
- class.
-
- MemConv
- ConvMemory object type - uses the program's heap space.
-
- MemEMS
- EmsMemory object type - uses EMS memory (v3.0 and above).
-
- MemXMS
- XmsMemory object type - uses XMS memory (v2.0 and above).
-
- MemDisk
- DiskMemory object type - uses a temporary disk file.
-
- Class VMemManager
- =================
-
- This class is extrememly simple - it just allows you to set a 'strategy'
- for the order in which to try to allocate memory (use EMS first? etc) and
- then just walks through the strategy returning the first MemoryObject type
- that successfully allocted your memory request. So you can use a "fastest
- first" strategy that would try allocaing in the following order: Conventional,
- EMS, XMS, Disk. Or you might want a strategy that is non-disk; in this case,
- the list wouldn't have MemDisk in it.
- An example of using the first strategy is:
-
- MemType fastAllocStrategy[] =
- { MemConv, MemEMS, MemXMS, MemDisk, MemConv, MemUnknown };
-
- void setFastestStrategy()
- {
- int n = 0;
- do VMemManager::allocScheme[n] = fastAllocStrategy[n];
- while(fastAllocStrategy[n++] != MemUnknown);
- // Alternatively:
- // memcpy(VMemManager::allocScheme, fastAllocStrategy, 5*sizeof(MemType));
- }
-
- int main()
- {
- setFastestStrategy();
- MemoryObject* mem = VMemManager::allocate(sizeof(dword)*1000);
- dword FAR* ptr;
- if(mem != 0 && (ptr = (dword*) mem->lock()) != 0)
- {
- // can now access the memory as a 1000-element dword array.
- ...
- mem->unlock();
- }
- ...
- delete mem;
- }
-
- For fastest order without using disk memory, you could do the identical
- code, but change the fastAllocStrategy[] array to not include MemDisk.
-
-
- Class MemoryObject
- ==================
-
- This is the base class for memory object classes. Almost all of its
- methods are pure, so you cannot create an instance of MemoryObject. The
- following definitions describe what a derived class must perform for each
- method in MemoryObject:
-
- Methods
- -------
-
- MemType type() virtual
- This is a type identifier method. If a new memory class is derived
- from MemoryObject, then the MemType enum needs to have a new entry
- for this memory type, and VMemManager needs to have its allocScheme
- array increased in size, as well as code modification.
-
- bool allocate(word sz) pure virtual
- A MemoryObject must only attempt allocating memory when it is not
- currently in use (previous successful call to allocate()); if the
- object already has allocated some memory, this method must return
- false. Otherwise, the class must try to allocate <sz> bytes and
- return true on success, or false on failure. Note that the object
- should NOT lock the memory - just allocate it.
-
- void free() pure virtual
- Object should do nothing if it currently has locks (see lockFlag);
- otherwise, it should free any memory previously allocated by a call
- to allocate().
-
- void FAR* lock() pure virtual
- If the object has no memory allocated (see allocate()), then must
- return 0. If the object already has locks in place, must return the
- SAME address that previous calls to lock() returned (IE don't go
- locking the data in different buffers). Otherwise, must attempt to
- place the data in conventional memory (0..1MB) in such a manner that
- either the various MemoryObject classes, or the compiler's heap
- manager, know about it, and return a pointer to it. If MemoryObject
- classes are used as the buffer, then note that only ConvMemory is
- guaranteed to be able to lock() a successful allocation (and the
- internal UmbMemory class of XmsMemory) - all others (including EMS)
- might be able to allocate, but then unable to lock(), a buffer.
- If a buffer is successfully created, this function must copy its
- data into the buffer and return the buffer address - also, it MUST
- increment the lockFlag member. On failure, return 0.
-
- void unlock() pure virtual
- If the data is not locked, perform nothing. Otherwise (lockFlag != 0)
- then you must decrement the lock flag: lockFlag--. If it still has
- locks on it (lockFlag != 0), then just return. Otherwise you must
- copy (if necessary) the data from the buffer to its normal location
- and free the buffer.
-
- long memAvail() pure virtual
- Should return the total amount of unused memory for that class. Note
- that for a class like DiskMemory, it can be misleading to just
- include all the remaining disk space, since some memory is required
- by the operating system, and the program might be writing/deleting
- other files on that disk.
-
- long maxAvail() pure virtual
- Should return the largest single block that is available for that
- memory type. Since allocate() uses a word (unsigned 16-bit) for
- allocation sizes, this is NOT the maximum block that can be allocated;
- its the maximum contiguous area free for that data type. Again, the
- same caveats apply as for memAvail().
-
- word memSize()
- Returns the size of the memory allocated by this object, or 0 if
- none has been allocated.
-
- Members
- -------
-
- word memsize
- Size of this object's allocated memory. Must be maintained by the
- derived class.
-
- word lockFlag
- Number of locks on the object's data. Must be maintained by the
- derived class' overridden lock() and unlock() methods.
-
-
- Class ConvMemory
- ================
- Derived from MemoryObject
- Source: CONV.CPP
-
- ConvMemory is a MemoryObject whose data is stored in the program's heap
- space (allocated through a call to global new() operator). It has the benefit
- of ALWAYS being able to be locked() (since its own data is used as a buffer)
- but is not overly useful.
- This class adds a "safety pool" to its heap usage. For instance, if you
- need to make sure that 10K of the heap is never touched by allocating
- MemoryObjects (perhaps to ensure that other classes, such as XMS or Disk, can
- always be locked by creating ConvMemory buffers) then you can just set the
- static memPoolSize member to 10K, and ConvMemory will fail any calls to
- allocate() that leave less than 10K as the maxAvail() conventional memory.
- For example: the following code will (try) and allocate 800K of any type
- memory (using VMemManager's current allocation strategy) as 80 blocks of
- 10K each - it will ALSO ensure that, as long as you only lock() one block at
- a time, it will always pass:
-
- ...
- MemoryObject* mem[80];
- int n;
- ConvMemory cmem; // just for using its maxAvail() function.
-
- if(cmem.maxAvail() < 10016) // extra 16 is for heap manager
- ... fail operation - can't guarantee 10000 buffer can be made.
-
- ConvMemory::memPoolSize = 10016;
- for(n = 0; n < 80; n++)
- if((mem[n] = VMemManager::allocate(10000)) == 0)
- ... fail.
- ConvMemory::memPoolSize = 0;
-
- Now mem[0..80] can be lock()'d with success (as long as you don't allocate
- any more memory from the heap or with ConvMemory) because we've guaranteed
- that there's enough heap space to create a ConvMemory of 10K, and all the
- Memory classes that don't have access to conv memory (XMS, Disk) will try
- using a ConvMemory as their buffer on a call to lock().
-
- Class EmsMemory
- ===============
- Derived from MemoryObject.
- Source EMS.CPP
-
- EmsMemory attempts to use the systems EMS memory for allocation. By
- default, EmsMemory will attempt to use ALL available EMS memory. You can
- force EmsMemory to use less by modifying the static variable MaxEmsPages
- in the file EMS.CPP. If this value is less than 4 (need 4*16K as a minimum)
- then EmsMemory tries to allocate ALL EMS. If this value is >= 4, then it
- will only allocate MaxEmsPages pages from EMS; each page is EmsPageSize in
- size (16K - defined in MEMORY.HPP).
- Many EMS memory systems just use EMS' built-in 16K page manager; this
- means that the minimum allocation is 16384 bytes. Instead, EmsMemory
- allocates MaxEmsPages (or all available pages) and considers this as a
- contiguous block of memory. So if it allocated 100 pages, then EmsMemory
- considers that it has one long block of 16384*100 = 1,638,400 bytes. This
- eliminates a lot of wasted memory.
- When allocating into this space, EmsMemory uses a first-fit strategy. So
- if the EMS memory looked like:
-
- +--------------+-------------+--------+------
- | In use | Available | In use | ...
- +--------------+-------------+--------+----
- Page 0
-
- and "Available" was 14K in size, and you allocate(300), then the memory
- would look like:
-
- +--------------+---+---------+--------+------
- | In use | |Available| In use | ...
- +--------------+---+---------+--------+----
- Page 0 ^
- Block just allocated.
-
- This scheme makes the most use of EMS memory, but had side effects. Since
- we're storing the linear offset of the block, we have to use a long instead
- of a page number (word) - this is 2 extra bytes per EmsMemory instance. Also,
- the EMS manager only works on page numbers (every 16K block), so we have to
- convert this linear offset into a page number + (word) offset.
-
- EMS memory is not directly accessible by the computer. In order to access
- that 300-byte block you just allocated, that area has to be moved so that the
- CPU can address it. What EMS does is to have a 64KB region of conventional
- memory in which is 'maps' its pages into one of 4 "frame" pages which the
- computer can access. So somewhere above 640K, the EMS manager has 64K set
- aside that is its frame buffer, which is can be accessed as 64K contiguous
- conv memory, but which actually modifies the page which you mapped into it.
- This is actually kinda goofy <g>; for instance, you could map EMS page 0 into
- frame page 0, EMS page 2 into frame page 1, EMS page 4 into frame page 2, and
- EMS page 47 into frame page 3. To your program, they all just look like a
- 64K block of memory; but if you then swapped frame page 1 and 0, it would
- be as if you 'swapped' the second 16K with the first 16K!
- EmsMemory uses this fact to its advantage. For instance, if you have one
- allocation block which fits into 2 16K pages, one which fits into one page,
- and another that fits into one page, then all 4 blocks can be lock()'d at
- the same time. Note that I said "fit into x pages"; this does NOT mean that
- their SIZE is <= 16K. For example, the above 300 byte allocation might have
- occurred such that, page-wise, EMS memory looks like:
-
-
- | 16K | 16K | 16K | 16K | 16K |
- +--------------+---+---------+--------+------
- | In use | |Available| In use | ...
- +--------------+---+---------+--------+----
- Page 0 ^
- Block just allocated.
-
- If I "blow up" the area around the allocation block itself, it looks like:
-
- ------+-------------+-------------
- | 300 bytes | Available...
- ------+---------+---+------------
- |
- ...16K PageN | 16K Page(N+1) ...
-
- Now to load the full 300-byte block into the frame buffer, I have to load
- both PageN AND Page(N+1):
-
- +---------------+---------------+---------------+---------------+
- | Page0 | Page1 | Page2 | Page3 |
- +---------------+---------------+---------------+---------------+
- | EMS PageN | EMS Page(N+1) |
-
- | 300 bytes |
-
- ^
- Pointer returned by lock().
-
- For searching purposes, EmsMemory objects form a sorted linked list; the
- class has a static pointer to the head of the list. Whenever a new EmsMemory
- allocates memory, its added to the list in the proper location; when it
- successfully free()s its memory, its removed from the list. The sort order
- for the list is by the linear "address" of its memory block. This is somewhat
- similar to the tagged boundary heap management method, but the tag info is
- contained in the object, instead of the EMS memory block.
-
-
- Class XmsMemory
- ===============
-
- This class operates very much like the EmsMemory class, except that XMS
- memory (driver v2.0 and above) is used as the data storage location, not
- EMS memory. One disadvantage with XmsMemory is that, unlike EMS, XMS memory
- does not have a "frame buffer" in conventional memory space. This means that
- to lock() an XMS memory block, memory has to come from somewhere in the
- conventional memory space. On 386 machines, where UMBs are available, the
- XmsMemory class will try to lock() by allocting a UmbMemory object (internal
- to XMS.CPP) - if successful, then the XMS memory is copied here and you
- don't have to use heap space for the buffer. If no more UMBs are available,
- them XMS tries ConvMemory and EmsMemory to allocate and lock() a buffer.
-
- Class DiskMemory
- ================
-
- This class also is much like the EmsMemory class, but it uses a temporary
- disk file as its storage location. You can change the drive/path used for
- the temp file (default is current drive/directory) by strcpy() to the
- external string DefaultDiskMemDir. For example, if you have a ramdisk set up
- as F drive, then you would:
-
- extern char DefaultDiskMemDir[];
- ...
- strcpy(DefaultDiskMemDir, "F:");
-
- This MUST be done before ANY DiskMemory objects are instantiated (their ctor
- run), since this file is opened the first time any DiskMemory::DiskMemory()
- runs. Like XmsMemory, DiskMemory has no default "frame buffer" so a buffer
- has to be allocated to lock() an object. By default DiskMemory::lock() will
- first try to allocate and lock() a ConvMemory; on failure, it will then try
- to allocate and lock a EmsMemory. You can swap the order of this (try EMS
- first) by changing the external int DiskFrameOrder to non-zero:
-
- extern int DiskFrameOrder;
- ...
- DiskFrameOrder = 1; // Now will try EMS before Conventional.
- ...
- DiskFrameOrder = 0; // Now will try Conventional before EMS (default).
- ...
-
-