Dec 062017
C++ class for virtual memory (conventional, disk, EMS). | |||
---|---|---|---|
File Name | File Size | Zip Size | Zip Type |
CONV.CPP | 1481 | 500 | deflated |
DISK.CPP | 4498 | 1216 | deflated |
EMS.CPP | 5670 | 1428 | deflated |
MEMORY.CPP | 1095 | 268 | deflated |
MEMORY.H | 9328 | 2954 | deflated |
READ.ME | 4628 | 1965 | deflated |
TEST.CPP | 2135 | 728 | deflated |
TEST.DSK | 3273 | 927 | deflated |
TEST.PRJ | 5799 | 1260 | deflated |
VMEMMGR.CPP | 3942 | 933 | deflated |
VMEMMGR.H | 871 | 398 | deflated |
Download File VMEMPP.ZIP Here
Contents of the READ.ME file
The following files should be included:
test.cpp:test program
conv.cpp:source for ConvMemory class.
ems.cpp:source for EmsMemory class.
disk.cpp:source for DiskMemory class.
memory.cpp:source for VMemManager class.
VMEMPP.ZIP is public domain - use it to your heart's content, modify at
will (in particular you may want to add defragmentation methods - I know
__I__ do!), and I'm not responsible for anything!
Whew! Now that that's over, lets get into the details.
What you have downloaded is primitive virtual memory capabilities for use
with C++ (I developed it using Borland Intl's BC++ 3.0). The functionality
for any particular type of memory allocation (conventional, EMS and disk are
included) are derived from the pure abstract base class MemoryObject.
MemoryObject is a "lock-and-load" memory object. To use, you allocate()
the desired memory amount (this version only supports 1-65535 bytes per
allocation). If allocate() passes (returns non-zero), then your memory block
has been allocated, but is not necessarily ready to use. To access the
memory block as a void far *, you call lock(). This will either return a
pointer to the memory block, or 0 if it failed to lock the block. For a
MemConventional type memory (conventional memory), lock() should never fail
(unless you never allocated memory). For MemEMS type memory (EMS memory),
lock() can fail for one reason: EmsMemory::lock() could not locate enough
contiguous frame buffer pages to hold the pages that contain the block of
memory. This is an inherent restriction for EMS memory. EMS has a 64K 'frame'
buffer located in conventional memory space, which actually comprises four
16K 'pages'. When you want to access a particular portion of EMS memory,
you need to 'map' the 16K page that holds that data into one of these pages.
Since EmsMemory supports up to 65535 bytes/alloc, that means its conceivable
that to lock this block in, it has to use all 4 frame buffer pages. But if
you have previously lock()'d an EmsMemory object, its using at least one of
these pages - so the lock fails. The lesson here is to lock a block, use it,
and unlock it again as soon as you can. For MemDisk type (disk memory), lock
will fail if MemDisk::lock() cannot lock either a new ConvMemory type block,
or a EmsMemory type block. Since DiskMemory type memory resides in a disk
file, it has to be loaded __somewhere__ in memory for you to access it -
thats what the ConvMemory and EmsMemory allocations are for. And, therefore,
this has to go OK for you to have a memory block available to load the disk
data in.
To tie these classes into one memory manager, I've included a primitive
class called VMemManager. Basically, all this class does is automatically
step through all 3 types of memory trying to allocate a block. It has a
allocScheme member that you can set to one of the following:
ConvEmsDisk : try conventional, EMS, then disk.
ConvDiskEms : try conventional, disk, then EMS.
EmsConvDisk : try EMS, conventional, then disk.
EmsDiskConv : try EMS, disk, then conventional.
DiskConvEms : try disk, conventional, then EMS.
DiskEmsConv : try disk, EMS, then conventional.
This all may sound confusing, but it needn't be. To just __use__ these
classes, all you need to do is the following:
1) instantiate a memory object.
Example: EmsMemory ems;
2) allocate some memory, and ensure that it passed.
Example: if( !ems.allocate(10000) ) {... // failed }
Note: using VMemManager, these 2 steps are combined:
Example:MemoryObject *mem = VMemManager::allocate(10000);
if( mem == 0 ){... // failed }
3) Forgot how big the memory block was?
Example:size_t sz = ems.memSize();
4) When you need to access this memory, lock it; make sure it passed.
Example: void far *ptr = ems.lock();
if( ptr != 0 ){...}
5) When you're done with it, unlock the memory.
Example:ems.unlock();
6) When you no longer need the memory, free it (it must be unlocked).
Example: ems.free();
7) Want to know what the largest block of ems you can allocate is?
Example:long emsAvail = EmsMemory::maxAvail();
8) Want to know what the total free ems avail is (not necessarily contiguous)
Example:long emsTotal = EmsMemory::memAvail();
That's it! The included program test.cpp shows an example of allocating
800,000 bytes using VMemManager.
Like I said, this is still a primitive memory manager. It does not have
methods to perform defragmentation (tho' the lock-and-load method makes this
fairly simple to implement). But it __does__ work.
Pat Reilly
70274,161
test.cpp:test program
conv.cpp:source for ConvMemory class.
ems.cpp:source for EmsMemory class.
disk.cpp:source for DiskMemory class.
memory.cpp:source for VMemManager class.
VMEMPP.ZIP is public domain - use it to your heart's content, modify at
will (in particular you may want to add defragmentation methods - I know
__I__ do!), and I'm not responsible for anything!
Whew! Now that that's over, lets get into the details.
What you have downloaded is primitive virtual memory capabilities for use
with C++ (I developed it using Borland Intl's BC++ 3.0). The functionality
for any particular type of memory allocation (conventional, EMS and disk are
included) are derived from the pure abstract base class MemoryObject.
MemoryObject is a "lock-and-load" memory object. To use, you allocate()
the desired memory amount (this version only supports 1-65535 bytes per
allocation). If allocate() passes (returns non-zero), then your memory block
has been allocated, but is not necessarily ready to use. To access the
memory block as a void far *, you call lock(). This will either return a
pointer to the memory block, or 0 if it failed to lock the block. For a
MemConventional type memory (conventional memory), lock() should never fail
(unless you never allocated memory). For MemEMS type memory (EMS memory),
lock() can fail for one reason: EmsMemory::lock() could not locate enough
contiguous frame buffer pages to hold the pages that contain the block of
memory. This is an inherent restriction for EMS memory. EMS has a 64K 'frame'
buffer located in conventional memory space, which actually comprises four
16K 'pages'. When you want to access a particular portion of EMS memory,
you need to 'map' the 16K page that holds that data into one of these pages.
Since EmsMemory supports up to 65535 bytes/alloc, that means its conceivable
that to lock this block in, it has to use all 4 frame buffer pages. But if
you have previously lock()'d an EmsMemory object, its using at least one of
these pages - so the lock fails. The lesson here is to lock a block, use it,
and unlock it again as soon as you can. For MemDisk type (disk memory), lock
will fail if MemDisk::lock() cannot lock either a new ConvMemory type block,
or a EmsMemory type block. Since DiskMemory type memory resides in a disk
file, it has to be loaded __somewhere__ in memory for you to access it -
thats what the ConvMemory and EmsMemory allocations are for. And, therefore,
this has to go OK for you to have a memory block available to load the disk
data in.
To tie these classes into one memory manager, I've included a primitive
class called VMemManager. Basically, all this class does is automatically
step through all 3 types of memory trying to allocate a block. It has a
allocScheme member that you can set to one of the following:
ConvEmsDisk : try conventional, EMS, then disk.
ConvDiskEms : try conventional, disk, then EMS.
EmsConvDisk : try EMS, conventional, then disk.
EmsDiskConv : try EMS, disk, then conventional.
DiskConvEms : try disk, conventional, then EMS.
DiskEmsConv : try disk, EMS, then conventional.
This all may sound confusing, but it needn't be. To just __use__ these
classes, all you need to do is the following:
1) instantiate a memory object.
Example: EmsMemory ems;
2) allocate some memory, and ensure that it passed.
Example: if( !ems.allocate(10000) ) {... // failed }
Note: using VMemManager, these 2 steps are combined:
Example:MemoryObject *mem = VMemManager::allocate(10000);
if( mem == 0 ){... // failed }
3) Forgot how big the memory block was?
Example:size_t sz = ems.memSize();
4) When you need to access this memory, lock it; make sure it passed.
Example: void far *ptr = ems.lock();
if( ptr != 0 ){...}
5) When you're done with it, unlock the memory.
Example:ems.unlock();
6) When you no longer need the memory, free it (it must be unlocked).
Example: ems.free();
7) Want to know what the largest block of ems you can allocate is?
Example:long emsAvail = EmsMemory::maxAvail();
8) Want to know what the total free ems avail is (not necessarily contiguous)
Example:long emsTotal = EmsMemory::memAvail();
That's it! The included program test.cpp shows an example of allocating
800,000 bytes using VMemManager.
Like I said, this is still a primitive memory manager. It does not have
methods to perform defragmentation (tho' the lock-and-load method makes this
fairly simple to implement). But it __does__ work.
Pat Reilly
70274,161
December 6, 2017
Add comments