Category : BASIC Source Code
Archive   : QBXM10.ZIP
Filename : QBXMAPND.DOC

 
Output of file : QBXMAPND.DOC contained in archive : QBXM10.ZIP

QBXM ver 1.0
Appendix A


EMS Specification vs XMS Specification

Allocation Units.

The EMS specification calls for a minimum allocation unit of
16,384 bytes, called a page. All memory size specifications are
expressed in these 16k pages. The XMS specification calls for a
1024 byte minimum unit, so any allocation needs to be a multiple
of 1k. QBXM converts calls for a page within an XMS environment
to 16k multiples and uses the 'page' concept throughout. For
example, when a call is made to QBXM to allocate 1 page, in an
XMS environment, the page count is multiplied by 16 and the
request is passed through to the driver for 16k.


Handle Counts.

There are both a default number of handles available in both
specifications and a maximum count allowed. With EMS the maximum
number of handles is 256, with handle 0 being reserved for the
operating system. XMS allows for a number of handles I can't
find in the documentation, but HIMEM.SYS will allow up to 128
maximum. Since HIMEM.SYS will only allow a maximum of 128 han-
dles, that is also the maximum handles for QBXM.

HIMEM.SYS defaults to 32 handles, and the count can be adjusted
with the /NUMHANDLES= command line parameter. EMS drivers, I
believe, may vary in the number of default handles. QEMM which I
use, and an AST driver that I use on an AT&T 6300, both default
to 64 handles. Setting the number of handles desired is a func-
tion of each EMS driver, and is done via a command line switch in
the CONFIG.SYS file.


File Buffer Space

This may be tedious for the reader, but here I go. When using
EMS, there is a 64k block of conventional memory between 640k and
1 megabyte available that is called the page frame. Different
sections of expanded memory can be moved into and out of this
space, and any application can address this space, including DOS
and BIOS routines. When the Load (or Save) File routine is
called, a buffer is needed to read the disk data into. If EMS is
in use, 16k of this section of memory is used as a buffer by
QBXM. The data is moved from disk to the buffer and then to
expanded memory in 16k chunks. No memory need be taken from the
BASIC data area.









1


QBXM ver 1.0
Appendix A

Within the XMS specification, four types of memory are defined.
Conventional from 0k to 640k, Upper Memory from 640k to 1024k,
High Memory from 1024k-16 to 1088K, and Extended Memory from
1088k to the top of installed memory. The upper memory area is
the only one (other than conventional) that could be reliably
used as a buffer area for disk transfers. The XMS specification,
written by Microsoft, Lotus, Intel and AST Research, specifies
calls to allocate and deallocate portions of the upper memory
block. This should allow QBXM to handle disk transfers in an XMS
environment the same as it does for EMS. However, Microsoft does
not implement the upper memory calls in HIMEM.SYS. I know, if
they had a hand in writing the spec, why don't they implement all
it's features? Who knows? They use the high memory area exten-
sively with Windows (with 2.0 first?), but I think they wanted to
avoid stepping on a possibly loaded EMS manager. The high memory
area cannot be used for disk transfer buffers however, DOS and
the BIOS may not correctly handle the addresses.

This left me in a quandary. When running under XMS, QBXM would
need a buffer area for the file transfer, and it would have to be
allocated from the BASIC data area. Three alternatives presented
themselves.

One, I could have written the code to use a buffer area supplied
by the calling program from it's data area, and read the disk
file into it, then transferred from that buffer into extra memo-
ry. If I did that though, then the 64k buffer available to EMS
implementations would not have been used, and memory cramp might
ensue in some situations. Just seems to be a waste of a resource
that might be available, and required the passing of three more
parameters, the segment, offset and length of the buffer.

Two, taking the above a step further, I could have the QBXM
routine check for EMS or XMS. If EMS I would load from disk
into the EMS page frame's 64k at 16k per loop, and from there to
extra memory. If XMS was in use, then the calling BASIC program
would have to allocate the buffer (an array of x bytes) and
specify where the buffer was allocated (VARSEG and VARPTR) in
the BASIC data space, and the size of the buffer. The buffer
could be larger than 16k to speed things up a bit, but a disk
transfers only at such and such a speed. This presents some com-
plexities in that there would be the DIM'ing of the buffer (only
for XMS) and the passing of three extra parameters for a
load/save file call, regardless of which type of extra memory was
installed at run time. To be honest, it is a more efficient
method than what I implemented, but I am trying to keep the
interface as simple as possible. The code in BASIC would require
something like what's on the following page.








2


QBXM ver 1.0
Appendix A

rLen = LEN(record)

IF ems THEN
bSeg = 0
bOfs = 0
bytes = 0
CALL LoadFileXM (f$,rlen,bseg,bofs,bytes,handle,recs&,eCode)
ELSEIF xms THEN
x& = FRE(-1)
.... that gives you the free memory from which you
.... would calculate the number of elements to DIM an
.... array based on the memory available. Elements
.... would vary based on the type of array of course..
DIM temp(1 TO elements)
bSeg = VARSEG(temp(1))
bOfs = VARPTR(temp(1))
bytes = elements * bytesPerElement
CALL LoadFileXM (f$,rlen,bseg,bofs,bytes,handle,recs&,eCode)
ERASE temp
END IF


The third option, and the one I settled on, was to allocate a
1024 byte buffer area within the QBXM code. This 1k buffer comes
out of the data area that would normally be available to your
BASIC program, and is only used when XMS is in use, though it is
allocated regardless of the extra memory type. This seemed to be
an acceptable trade off, because EMS is more common than XMS, it
does not use very much of the data space available, and QBXM does
try to allocate some upper memory first, before using this 1k
buffer. Let's just say I'm hopeful that upper memory support may
be out there in a driver other then HIMEM, or HIMEM may soon
implement it. It doesn't in DOS 5 by the way. Anyway, the code
tries for a 16k upper memory block buffer, and if the block is
not available, or upper memory blocks are not implemented, the
code falls back to using the 1k internal buffer.

What this boils down to is that a file load loop using EMS would
read 16384 bytes of the file, then transfer 16384 to EMS extra
memory with each loop. The same loop using XMS would require
roughly 16 times as many loops. This sounds terrible on the
surface, but assembly loops being quicker than the equivalent
BASIC loops, and trying to preserve memory available to your
BASIC program, it might not seem so terrible.

Bottom line is that I would like to hear from users on whether
they agree with my logic. Maybe I was to conservative on the 1k
buffer? Maybe a 4k buffer would be acceptable? Maybe a second
object file and routine that used 8k at a clip, and if testing
showed memory cram, you could fall back to the 1k routines?
Maybe the extended calling routine, where the buffer is allocated
from within the calling program? Self doubt is a terrible thing,
tell me what you think!



3


QBXM ver 1.0
Appendix A

QBXM & EVEN Length Records

At first glance, the insistence that record lengths and block
moves be an even number of bytes may seem odd. Read on.

EMS was developed to extend the useful life of PC and XT comput-
ers. PC and XT class machines use an eight bit data bus. So a
byte transfer is no problem, a byte being eight bits.

Extended memory is a different matter though. Extended memory is
only available on AT class machines which have as a minimum, a 16
bit data bus. Data transfers being more efficient with 16 bits
at a time than with 8 bits, and not being available on an 8 bit
machine, why the heck should the Extended Memory Spec (XMS)
bother with byte sized transfers? Exactly, so it doesn't.
Interesting thing is that XMS memory moves are specified in bytes
instead of words. Probably thought it was a more common unit of
measurement for programers.

Therefore, there is no choice but to write QBXM to use word (2
byte) sized memory moves. This includes user defined types of
course.

The best way to handle odd length records? Maybe DIM an odd
length record to 2 elements, two times anything seems to be an
even number. Then tell QBXM that all records are two times there
actual length and adjust accordingly. For example:

TYPE AddressRecord
Name AS STRING * 26
Addr AS STRING * 26
Town AS STRING * 14
St AS STRING * 2
Zip AS STRING * 5
END TYPE

' That zip code will get you each time!
' A record length here works out to 73 bytes.

DIM SHARED address AS AddressRecord
DIM SHARED tempAddress(0 TO 1) AS AddressRecord

rLen = LEN(address) * 2 'This is the value you pass to QBXM













4


QBXM ver 1.0
Appendix A

' For any given record number, actualRecord, you use the follow-
' ing formulas:

getRecord& = ((actualRecord - 1) \ 2) + 1
' Which results in the following:
' actualRecord: ((actualRecord - 1) \ 2) + 1 = getRecord:
' 1 (( 0 ) \ 2) = 0 + 1 = 1
' 2 (( 1 ) \ 2) = 0 + 1 = 1
' 3 (( 2 ) \ 2) = 1 + 1 = 2
' 4 (( 3 ) \ 2) = 1 + 1 = 2
' 5 (( 4 ) \ 2) = 2 + 1 = 3
' 6 (( 5 ) \ 2) = 2 + 1 = 3
' etc...

Then you use GetRecXM to retrieve the record from QBXM's record
orientated routines, and access the final desired record like
this:

vSeg = VARSEG(tempAddress(0))
vPtr = VARSEG(tempAddress(0))
GetRecXM (handle, getRecord&, vSeg, vPtr, errCode)

address = tempAddress((actualRecord + 1) MOD 2)

' Which results in the following: Element
' actualRecord: (actualRecord+1) MOD 2 = Returned:
' 1 ( 2 ) MOD 2 = 0
' 2 ( 3 ) MOD 2 = 1
' 3 ( 4 ) MOD 2 = 0
' 4 ( 5 ) MOD 2 = 1
' 5 ( 6 ) MOD 2 = 0
' 6 ( 7 ) MOD 2 = 1
' etc...

If you have a more compact, efficient method, let me know, it is
late as I write this.

Putting a record would require the reverse logic.

tempAddress((actualRecord + 1) MOD 2) = address

' Which results in the following: Element
' actualRecord: (actualRecord+1) MOD 2 = Returned:
' 1 ( 2 ) MOD 2 = 0
' 2 ( 3 ) MOD 2 = 1
' 3 ( 4 ) MOD 2 = 0
' 4 ( 5 ) MOD 2 = 1
' 5 ( 6 ) MOD 2 = 0
' 6 ( 7 ) MOD 2 = 1
' etc...






5


QBXM ver 1.0
Appendix A

putRecord& = ((actualRecord - 1) \ 2) + 1

' Which results in the following:
' actualRecord: ((actualRecord - 1) \ 2) + 1 = putRecord:
' 1 (( 0 ) \ 2) = 0 + 1 = 1
' 2 (( 1 ) \ 2) = 0 + 1 = 1
' 3 (( 2 ) \ 2) = 1 + 1 = 2
' 4 (( 3 ) \ 2) = 1 + 1 = 2
' 5 (( 4 ) \ 2) = 2 + 1 = 3
' 6 (( 5 ) \ 2) = 2 + 1 = 3
' etc...

Then you use PutRecXM to store the record into extra memory:

vSeg = VARSEG(tempAddress(0))
vPtr = VARSEG(tempAddress(0))
PutRecXM (handle, getRecord&, vSeg, vPtr, errCode)
I hate putting an important warning at the start of a new page,
when it applies to a previous page, but I've no choice here.

NOTE: When using a 'doubled' type variable as in the previous
example, remember not to destroy the data in extra memory for the
element not being worked on.

What's that? Well for each actual element or record you retrieve
there is an innocent bystander with it. Take for example, a case
where you need to retrieve the first record from extra memory.
The record you want to work with will be in element zero of the
double record, and valid data will also be in element one. If
you destroy the temporary double record after extracting element
zero, work with it, then DIM the double record again, insert the
revised data in element zero and write the double element back to
extra memory, actual record two (element one in the double re-
cord) is all zeros. Your extra memory 'file' now has a blank
record in it. If you, as I did in the example, dim the double
record as a SHARED variable, and keep in mind that there is extra
data all the time you're working with the double record, all
should be OK.

A better method for storing data may be to force a read (Ge-
tRecXM) immediately before a write (PutRecXM), calculations as
before:

Calculate the double size record number.
Retrieve it from extra memory.
Calculate the element (0 or 1) in the double record.
Update the proper element (0 or 1).
Write the double record back to extra memory.


All in all, a great argument can be made for padding your records
with one byte to assure they are an even length.




6


QBXM ver 1.0
Appendix A

Extended Memory and Vdisk et al.

When the AT was introduced with it's extended memory, an inter-
rupt, 15 hex, was supplied in the BIOS to make limited use of the
extended memory available. INT 15 hex can be called to determine
the amount of extended memory installed. At the same time,
VDISK.SYS was distributed with PC-DOS 3.0+, which took advantage
of extended memory also.

These two items presented two different ways to access extended
memory. The "INT 15 method" is one, and what the basic implemen-
tation consists of is that a program (a TSR maybe) that uses
extended memory would call INT 15 to find out how much extended
memory was available. Next it would deduct what it needed from
'the top' so to speak, then intercept any other calls to the INT
15 "How much Extended is there?" function. When an intercept is
made, the program returns the reduced amount to any applications
that ask.

VDISK.SYS was distributed with it's source code in PC-DOS, and
used a different allocation method. It took what was needed of
extended memory from the bottom of extended memory (1024k) and
worked upward. If you look back a page or two, you might notice
I mentioned the High Memory Area defined in the XMS includes the
extended memory from 1024k - 16 bytes to 1088k. Opps. This is
the same area VDISK would start allocating from, and Microsoft
wants to use with Windows! Since the source code to VDISK was
distributed with PC-DOS, a lot of developers followed it's lead
in allocating memory from the 'bottom up' with a few twists
thrown in to make sure the chain of extended memory allocation
remained intact. You could load multiple copies of VDISK.SYS for
example to create multiple ram disks. Only problem is, not all
software follows the VDISK check and double check system of
memory allocation and use. Some extended memory compatible TSR's
and application programs will use the "VDISK method" and even
identify themselves as "VDISK" to maintain compatibility (though
limited) with that method. Try a text scan of your hard drive
for "VDISK", you may be surprised at the number of applications
that include the text string, which is loaded into memory to
identify it as a VDISK type memory allocator.

I find myself getting off track all of a sudden. So we have two
methods for allocating extended memory. One is from the top
down, where each application that takes some memory intercepts
queries about how much is there and reports the amount it knows
of, less what it's using. Then VDISK, which if implemented
properly works from the bottom up, and gives an indication of
where it's extended memory use ends. A program that wants to use
extended memory is supposed to follow this chain of VDISK blocks
up, and calculate how much is left. Of course there's no guaran-
tee that the memory chain is implemented properly along the way.
When XMS was developed, I suppose that the this VDISK allocation
system had been used and abused, thus the XMS fellows said the
heck with it. If an XMS driver finds a "VDISK" type extended


7


QBXM ver 1.0
Appendix A
memory manager in use when it's loaded from CONFIG.SYS, the XMS
manager won't load. If a "VDISK" type driver is loaded after the
XMS driver, it will be detected on an XMS call. This checking
for a "post XMS driver installation of a VDISK" type program
impresses me as meaning they are something to stay away from.
After the reading I've done on the various memory related sub-
jects I can see why.

With almost every product that includes HIMEM.SYS, Microsoft
includes RAMDRIVE.SYS, a fully compatible XMS application. Just
be sure RAMDRIVE is loaded after HIMEM.SYS in the CONFIG.SYS file
and you've got a non-interfering ram disk instead of using VDISK.

When it came to accessing extended memory with QBXM, I had to
decide just how to implement it. Follow the INT 15 or VDISK
methods if an XMS driver wasn't there, and extended memory was
installed. Or insist on using a formal specification? True, if
I used the INT 15 top down approach, there would be no need to
specify that an XMS driver such as HIMEM.SYS be installed. If I
included the INT 15 method after a check for EMS and then XMS, it
would eliminate the HIMEM.SYS requirement. Of course that means
I could also allow you to walk down in memory right over a VDISK
type ram disk or cache or whatever in extended memory. Call me
dull, but I figured a specification of one type was better than a
"custom" of another.


Extended Memory And The 80286

When INTEL designed the 80286, they made the chip power up in
real mode, where it would act just like an 8088/8086 chip.
Included on the chip is a method to switch from real mode into
protected mode. Protected mode was (and is) considered so much
more efficient than real mode, a method of switching back to real
mode was not considered to be a need. A work around to the
problem involves some tricky stuff that I'm not altogether clear
on but what I think happens is interesting. When the BIOS wants
to switch out of protected mode and back to real mode, it writes
a 'magic' value to a specific location in memory. Next it sends
a command to the keyboard controller that causes the controller
to issue a warm boot instruction, as if you hit Ctrl-Alt-Del.
When the ROM BIOS starts it's warm boot routine, it checks for
that 'magic value' in memory. If it finds it, then the machine
is not really being reset, just the processor is. Somehow memory
is preserved through all this, and the processor picks up from
where it should in your program code. Interesting trivia true,
but the thing to keep in mind is how much is going on. When you
access extended memory on an 80286, it's going to be slow. The
XMS manager takes care of everything painlessly for the program-
mer, but it's a real workout for the hardware.







8




  3 Responses to “Category : BASIC Source Code
Archive   : QBXM10.ZIP
Filename : QBXMAPND.DOC

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/