FreeDOS DPMI Transfer Buffers: Allocate and Release Conventional Memory
Allocate a bounded real-mode-visible buffer with DPMI 0100h, keep its segment and selector distinct, and release the block through 0101h.
Protected-mode DOS software sometimes has to exchange bytes with a real-mode program, TSR, BIOS-adjacent service, or device driver. Ordinary protected-mode heap memory is not automatically addressable by such a client. DPMI provides a specific DOS-memory allocation pair for this boundary: interrupt 31h functions 0100h and 0101h.
This is not a general-purpose replacement for malloc, nor is it the DPMI linear-memory allocation family. It requests memory from the DOS-controlled pool below the 1 MiB boundary and returns both a real-mode segment and a protected-mode selector for the same block. The DPMI host must be present, and the actual amount of conventional memory available depends on the DOS environment and its resident software.
The register contract keeps two addresses separate
For allocation, set AX to 0100h and BX to the number of 16-byte paragraphs requested. A successful call clears carry, returns the real-mode segment base in AX, and returns the protected-mode selector in DX. Failure sets carry and returns an error code in AX; insufficient memory is error 0008h, and BX reports the largest available block in paragraphs. Preserve the returned segment and selector in separate variables: they are not interchangeable handles.
For a 1,024-byte transfer area, the arithmetic is:
paragraphs = ceil(1024 / 16) = 64 = 0040h
So a register-level request uses AX=0100h, BX=0040h. A DOS-side service that consumes the data needs the returned segment and an agreed offset/layout. Protected-mode code uses the selector according to its compiler/runtime interface. Do not copy the numeric segment into a pointer variable or pass the selector as if it were a real-mode address.
The paired release call uses AX=0101h and DX=selector. The DPMI specification explicitly identifies DX as the selector to free; freeing the block releases its associated allocation. A common cleanup defect is retaining only the segment because that is the value sent to a real-mode caller. Store both values as one owned allocation record, and release with the selector after the consumer is finished.
A DJGPP wrapper example
DJGPP exposes wrappers with the same ownership distinction. Its allocation wrapper accepts a paragraph count, returns the DOS segment on success, and writes the protected-mode selector through the second argument. Its free wrapper takes the selector rather than the segment and returns zero on success or -1 on error.
#include <dpmi.h>
#include <stddef.h>
int
example_transfer_buffer(void)
{
size_t bytes = 1024;
int selector = -1;
int segment = __dpmi_allocate_dos_memory((int)((bytes + 15) >> 4),
&selector);
if (segment < 0) {
/* Report allocation failure; no buffer was acquired. */
return -1;
}
/* Access through the protected-mode selector as the runtime requires.
Pass segment:offset only to a service with a matching real-mode ABI. */
if (__dpmi_free_dos_memory(selector) == -1) {
/* Report cleanup failure; do not pretend the block was released. */
return -1;
}
return 0;
}
The example is a lifecycle sketch, not a complete transfer protocol. Production code should validate that the requested size is nonzero, that rounding up by 15 cannot overflow, and that the paragraph count fits the wrapper’s int parameter before making the call. Keep buffer length and offsets explicit in the protocol; the far address alone does not describe how many bytes the receiver may read.
Bound the transfer and the lifetime
Conventional memory is a limited shared resource. Request the smallest block that satisfies the exchange, not a convenient large scratch area. If allocation fails, the DPMI error and largest-available-paragraph count are useful diagnostics, but a smaller block is safe only if the protocol can operate with that capacity. Never retry indefinitely with progressively different sizes without a defined maximum and user-visible failure path.
Treat the block as owned state. Initialize the segment and selector to invalid sentinels, set them only after successful allocation, and release the selector on every normal exit and error path. Do not free while a real-mode routine, interrupt callback, or asynchronous driver can still access the buffer. Stop or drain the consumer first, then release the DPMI block; clear the saved identifiers only after successful cleanup. If cleanup itself fails, report it rather than silently reusing the identifiers for another allocation.
When passing data across the boundary, define whether the service reads or writes, the exact byte length, alignment, and when it stops using the memory. Validate any returned length before copying it into protected-mode storage. Keep the protected-mode selector scoped to the allocated region and avoid using an unrelated broad data selector as a substitute. The DJGPP documentation calls out that the selector associated with the allocation is the one to use for freeing, while the real-mode segment is for segment:offset consumers.
Test the failure path in the target environment
Test with a DPMI host and the same memory managers and resident programs expected in deployment. Include a request that succeeds, a deliberately oversized request that fails, a zero/invalid size rejected by the caller, and repeated allocate/use/free cycles. Verify that the freed block is actually reusable and that the program’s conventional-memory footprint does not steadily grow. Also test the consumer’s shutdown path: an allocation must remain live until the last real-mode user is done, but it must not remain allocated after that user exits.
The portable lesson is about ownership, not a promise of abundant memory: DPMI 0100h provides a real-mode-visible DOS block plus a protected-mode selector, and 0101h releases that block through the selector. Keeping the two address forms and the release handle distinct makes this narrow bridge auditable.
Related:
- DPMI on FreeDOS: How Protected-Mode Programs Coexist with Real-Mode DOS
- DPMI Real-Mode Callbacks: Register, Bridge, and Release Safely
Sources: