• Please review our updated Terms and Rules here

Is there an EMM document or specification somewhere?

alank2

Veteran Member
Joined
Aug 3, 2016
Messages
2,256
Location
USA
Just messing around with some DOS programming - is there a specification or documentation about expanded memory somewhere?
 

This is the best book I know of on the subject. =)

Also a great book covering some of it, is the Black book of game programming Wolf3d Edition...

 
Extended Memory/Expanded Memory. Real quick now and be honest - can you remember which is which.
 
I do, unfortunately. Spent too much time working out how to use the various forms of memory 30 years ago.
 
Thanks for the links; that is exactly what I wanted to know!

Let's see if I get this right:

XMS - Extended = 286 and above, CPU's capable of addressing more than 1MB. This is memory past the 640K that begins at 1MB and goes up from there.

EMS - Expanded = 8088/8086 capable. Area within real mode above 640KB used as a page frame to switch in and out the expanded memory pages. Came before XMS?

I used to think if a system had 1MB of RAM that it would be in the upper range 640KB-1MB, but it seems that this is not the case (unless I am wrong now). That the extra 384K of the 1MB would begin at 1MB and not at 640K because of everything in the 640K-1MB range already.
 
LIM/EMS is pretty much fancy bank-switching. Mostly implemented because PC (5150/5160 and clones) wanted to be able to run large spreadsheets (hence the "L" in LIM). Bankswitching was not uncommon on 8-bit (x80=8080, 8085, Z80) PCs.

There's another dimension to all of this--the "A20 Gate" issue. On an 8088/8086, accessing memory addresses beyond 1M result in the addresses wrapping back to 000000. It's easy to see how this can happen; FFFF:0010H will be the same as 0000:0000. (all numbers are hex).
Things don't work like this in the 80286 real mode. Attempting to access FFFF:0010 will result in an address of 100000 and not 000000. So there was a feature called "A20 Gate" that would force bit 20 in an address to be 0, so you'd get the address wrap. That's the reason for the "A20 Gate" selection in the system BIOS. On the 5170, this feature was controlled by the keyboard controller.
 
That is for most IBM PCs and compatibles in the late-80s. There were a number of other software packages that used the term "extended memory" in different ways*.

Extended Memory was mentioned in the 5170 documentation which would be 1984. Expanded memory showed up in 1985. Tall Tree had a sort of prototype Expanded Memory in 1983 but the implementation and terminology differed for what LIM did. There was also IBM XMA which was designed as a specialized form of memory for use with 3270 emulators that could also be used as extended memory and with a driver could be used as normal EMS but some software did not like IBM's design. IBM also had DOS 4 place disk buffers in expanded memory but if the EMS card was not an IBM XMA card, the disk accesses would be corrupted. Computers are fun.

Some systems had shadow memory which placed RAM in the 640-1MB region with copies of the ROMs to make the system faster. That did prevent the memory being used as extended.

Weird fact: A number of AT clones with 1 MB on the motherboard could either run it as 640K losing the other 384K or split into 512K of conventional and 512K of extended which required a 128K memory board to reach 640K of conventional. BIOS updates to fix that happened fast.

Expanded memory with EMS 4 (or EEMS) can be confusing because there can be multiple banks of different sizes doing different actions. So called large page frames could lie below 640K and an entire block could be swapped out at once. That was common with some multitaskers where everything above DOS and the multitasker kernel was set as a page frame and each application got its own copy of the region.

* Just to prove how annoying the term extended memory could be, UCSD Pascal when allocated 128K referred to the second block of 64K as extended memory. Troubleshooting problems on a 5170 where UCSD Pascal was installed on a VDISK in IBM extended memory could result in questions to figure out which form of extended memory was creating the issue. Poor management for UCSD Pascal solved the problem by having it largely disappear from the market before large numbers could afford to install more than a MB on a 286.
 
Weird fact: A number of AT clones with 1 MB on the motherboard could either run it as 640K losing the other 384K or split into 512K of conventional and 512K of extended which required a 128K memory board to reach 640K of conventional. BIOS updates to fix that happened fast

A semi-good, albeit not great, reason for addressing RAM like this is quite a few extended memory cards are limited to having their starting addresses set on 512k boundaries. (To avoid having to have a billion dip switches.) A machine with 384k of Extended memory may well need to have it disabled if you want to expand it with an ISA card.
 
Can a program just use function 2 to get the page frame base address and only use physical page 0 for mapping?

There is another function called 25 (GET MAPPABLE PHYSICAL ADDRESS ARRAY) that returns a full list of physical pages. Is the address returned from function 2 always physical page 0?
 
My advice is to stick with LIM 3.2. There were many versions of LIM 4.0, not all of which were completely compatible with one another. You may also run into the IBM version, called XMA and the EMS "driver" called XMA2EMS.SYS. So keep it simple.
One of my utilities, written for commercial consumption back in the dark days of DOS, needed memory to store information. The hierarchy was DOS memory first (640K), EMS/LIM, second, XMS third, and finally hard disk. I kept with the LIM 3.2 calls and never received a support call on the issue.
 
Can a program just use function 2 to get the page frame base address and only use physical page 0 for mapping?

There is another function called 25 (GET MAPPABLE PHYSICAL ADDRESS ARRAY) that returns a full list of physical pages. Is the address returned from function 2 always physical page 0?
EMS 4.0 functions 1-15 are the same as EMS 3.2 for backwards compatibility. EMS 3.2 only had a single 64K page frame. Physical pages 0-3 (16K each) are always this page frame, even with EMS 4.0 drivers.
 
So function 2 will always get the address of the beginning of 4 16K pages (0, 1, 2, and 3)? Can it ever be shorter than 64K
 
So function 2 will always get the address of the beginning of 4 16K pages (0, 1, 2, and 3)? Can it ever be shorter than 64K
Yes, the page frame returned by function 2 will always be 64K. EMS 4.0 has the possibility of being configured with no page frame, in which case function 2 will return ah=80h ("software error"). An example of this would be EMM386 with FRAME=NONE.
 
Last edited:
So function 2 will always get the address of the beginning of 4 16K pages (0, 1, 2, and 3)? Can it ever be shorter than 64K

That depends on the driver. Some drivers will not install if there isn't a 64K block available; see https://jeffpar.github.io/kbarchive/kb/075/Q75592/ for evidence. If I read the EMS 4 documentation correctly, a driver could create page frames without having a 64K page frame but that would show up as having no EMS for any program using EMS 3.2.
 
Appendix B of the LIM above says there are two ways to see if an EMM is installed. Isn't that was the get status was for?
 
Accessing Get Status if nothing has hooked on INT 67h could have interesting results as could if something that isn't an EMM has a hook there.
The Open Handle technique is nice and safe. The INT 67h vector check is necessary for a driver to abort installation if some other EMM was installed.
 
Well, here's how I did it. Seemed to work every time:

Code:
;*      Check_EMS - Make sure that EMS servicer is in.
;       ----------------------------------------------
;
;       Returns carry set if not.
;

limint      equ     67h             ; EMS interrupt

LIM_Header      label   byte    ; the LIM header
        db      'EMMXXXX0'
LIM_Len equ     $-LIM_Header

LIM_Detected    db      0               ; nonzero if LIM handler detected

Check_EMS       proc    near private
        test    cs:LIM_Detected,-1
        jnz     Check_EMS2              ; it's okay

        push    ax
        push    si
        push    di
        push    es
        xor     ax,ax
        mov     es,ax
        mov     es,word ptr es:[4*limint+2]        ; get EMM segment
        mov     di,10
        lea     si,LIM_Header
        mov     cx,LIM_Len
        cld
        repe cmps byte ptr cs:[si],es:[di]      ; check for header
        pop     es
        pop     di
        pop     si
        pop     ax
        stc
        jne     Check_EMS2                      ; if nowhere
        mov     cs:LIM_Detected,1       ; we have it
        clc
Check_EMS2:
        ret
Check_EMS       endp
 
Some systems had shadow memory which placed RAM in the 640-1MB region with copies of the ROMs to make the system faster. That did prevent the memory being used as extended.

I have some 386/486 boards where some of this region must be used as shadow memory, but also a 256K portion (A000, B000, D000, E000) can be optionally remapped to the top of memory. I ran into this as I have a VLB video card with a linear framebuffer, and the driver would miss this extra 256K of memory and try to put the framebuffer at the top of memory rounded down to the nearest 1MB, crashing horribly. In addition, if you have the cacheable range maxed out, I could see using the remapping feature making the top 256K uncacheable as its address is too high.

* Just to prove how annoying the term extended memory could be, UCSD Pascal when allocated 128K referred to the second block of 64K as extended memory. Troubleshooting problems on a 5170 where UCSD Pascal was installed on a VDISK in IBM extended memory could result in questions to figure out which form of extended memory was creating the issue. Poor management for UCSD Pascal solved the problem by having it largely disappear from the market before large numbers could afford to install more than a MB on a 286.
This issue predates me, but I was looking through the XMS specification and it talks about backward compatibility with programs that would take an allocation of extended memory before HIMEM.SYS existed. One was to allocate memory upward by placing a VDISK-compatible header. Another was to allocate from the top of memory down, by hooking the interrupt to determine the amount of extended memory in the system and subtracting the allocation. A hack but I guess it worked.
 
Back
Top