Skip to content
FreeDOSDeep Dive Published Updated 7 min readViews unavailable

LIM EMS INT 67h: Allocate Handles and Map 16 KiB Pages Safely

Use LIM EMS handles and page-frame mappings deliberately, check every INT 67h status, preserve mappings, and release allocations on every exit.

Expanded Memory Specification (EMS) exposes memory through an interrupt-driven page-mapping interface. The application allocates a set of logical pages under a handle, then maps a selected logical page into a physical page window so that ordinary real-mode code can access it. The mapping is temporary address-space state, not a pointer to a permanently linear block. Every operation needs a valid handle, a known page-frame mapping, and an error path that can release resources.

This deep dive focuses on the common LIM EMS 3.2/4.0 standard-page workflow through INT 67h. Standard logical pages are 16 KiB, but EMS 4.0 also defines raw-page and extended mapping facilities with different constraints. Do not assume that a 16 KiB page frame is present at a fixed segment or that every manager implements every optional subfunction. Query the installed expanded-memory manager and use its returned addresses and status codes.

Detect a manager and query before allocating

The conventional EMS interface is INT 67h. The LIM specification provides a manager-status function, a page-frame segment query, and an unallocated-page count. In an application, perform detection and status checks before trying to allocate pages. A missing manager, unsupported function, or hardware error is a different condition from a valid request for more pages than are available.

The page-frame segment is where physical EMS pages become accessible after mapping. Query it through the manager rather than hard-coding the common D000h or E000h examples. Other memory managers may place the frame elsewhere, and LIM EMS 4.0 can expose additional mappable physical regions. The page-frame query gives the base segment for traditional page-frame access; use the EMS 4.0 mappable-address APIs for nonstandard regions.

Before allocating, query the number of unallocated standard pages and estimate the actual working set. A 16 KiB logical page can hold code or data, but an application must explicitly arrange which portion is mapped at any given time. Requesting a large fixed number can fail on machines with other EMS consumers. If allocation fails with a recoverable status such as insufficient pages, reduce the request or switch to a conventional/XMS fallback rather than treating the manager as corrupt.

Allocate a handle, then map a logical page

LIM EMS AH=43h allocates a requested number of standard pages. BX carries the count; on success, the manager returns the handle in DX and status zero in AH. The handle identifies the pages owned by this application and must be saved. The specification reserves handle zero for operating-system use; applications should use the handle returned by the allocation function.

The core map function is AH=44h: AL contains a zero-based physical page number, BX contains a zero-based logical page number, and DX contains the allocated handle. The manager maps that logical page into the selected physical page in the frame. After success, code accesses the mapped data through the queried segment address and an offset within the 16 KiB window.

The example below illustrates the register contract only; handle, page_frame_segment, and logical_page must be populated from successful manager calls. In real code, preserve any registers required by the calling convention and check AH after every interrupt:

    mov ah, 44h                 ; map one EMS handle page
    mov al, 00h                 ; physical page 0
    mov bx, [logical_page]      ; logical page owned by this handle
    mov dx, [emm_handle]        ; handle returned by AH=43h
    int 67h
    or  ah, ah
    jnz ems_error               ; AH is the EMS status

    mov ax, [page_frame_segment]
    mov es, ax
    mov di, 0010h               ; offset inside the mapped 16 KiB page
    ; ES:DI now addresses the selected mapped page

Do not assume that a mapping call has succeeded because no visible error occurred. On failure, the prior physical-page contents or mapping may remain active. Check the returned status before using the page-frame address. Validate logical_page against the count allocated to the handle and the physical-page number against the manager’s available map.

A mapping changes what an address means

Mapping a new EMS page into physical page zero changes which logical data appears at the same segment:offset address. A stale pointer into the page frame does not retain the old logical page’s contents after remapping. This is the central programming rule: treat the physical page window as a cacheable view of a chosen logical page, and never hold a reference across a remap unless the program explicitly knows which mapping is active.

Keep a software map table that records physical frame slots, logical page numbers, owning handles, and whether the page is currently valid. Before accessing a page, assert that the requested logical page is mapped. After any call that changes page mappings, invalidate pointers or cached descriptors that referred to the prior mapping. Test boundary offsets at the start and end of the 16 KiB window, and ensure no access crosses into another mapped region by accident.

A program that calls another library, DOS function, TSR, or child process while custom mappings are active must consider whether that code also uses EMS. Save and restore mapping context when needed. The LIM EMS specification defines save/restore functions; EMS 4.0 adds page-map variants and multi-page mapping. Select a function based on the supported EMS version and required compatibility, not just because a newer subfunction exists.

Handle and mapping lifetimes must be paired

Every successful allocation must be matched by AH=45h deallocation before the program exits. The LIM specification warns that otherwise other applications cannot use those pages or the handle. A program should trap its own error conditions and arrange cleanup for normal exit, user cancellation, and handled critical errors. A TSR or program that returns to DOS while still holding an EMS allocation leaks system resources until reboot or manager-specific recovery.

Before deallocating, restore any page-map context that must be preserved. The specification can reject deallocation when a saved mapping state has not been restored. A cleanup routine should track ownership explicitly instead of assuming a handle variable is valid after a failed allocation. Set a flag only after the function returns success, and clear it only after successful release.

Remember that unmapping a physical page makes its current mapping inaccessible through that frame slot. If you need to access the previous data later, save the mapping context first. Unmap by using logical page FFFFh for the selected physical page according to the standard function contract. Do not use FFFFh as if it were a normal page number.

Status codes are part of the API

LIM EMS returns status in AH. Common outcomes include success (00h), manager or hardware malfunction (80h/81h), undefined function (84h), exhausted handle table (85h), insufficient total pages (87h), or insufficient unallocated pages (88h). Function 44h has its own mapping-specific recoverable statuses, including invalid handle/page/frame combinations. Consult the exact EMS version’s status table before deciding whether an error is retryable.

Never read other returned registers as valid data after a nonzero status unless the specification says they remain meaningful. In particular, do not use DX as a handle after an allocation failure. Log the function number, input registers, status byte, EMS version, available pages, and manager identification. This creates actionable diagnostics instead of a generic “out of memory” message.

Detect, exercise, and stress the mapping model

Use a test program that allocates at least two pages, writes a unique pattern to page A, maps page B to the same physical window, writes a different pattern, then remaps A and checks that its pattern is preserved. Repeat with every physical page frame slot the application intends to use. Include a checksum per logical page, data near the 16 KiB boundaries, nested DOS calls, and a forced allocation failure. A test that only maps one page cannot detect incorrect remapping logic.

Run tests with each supported manager and emulator configuration. EMS behavior can differ with hardware emulation, manager version, UMB configuration, and other applications that consume handles. Do not assume that success under one JEMM or EMM386 setup proves physical expanded-memory board compatibility. Record manager version, EMS version, page-frame segment, page counts, function return codes, and cleanup results.

Use EMS only when an application truly requires its bank-switched model. For ordinary data movement, XMS or a DOS memory allocation may be simpler. The EMS contract is powerful for legacy applications and controlled compatibility targets, but it makes address mapping explicit. Correct programs query, allocate, map, verify, save/restore as necessary, and release every handle.

Related:

Sources:

Comments