Skip to content
FreeDOSDeep Dive Published Updated 7 min readViews unavailable

Virtual DMA Services in DOS: Locking Buffers and Translating Addresses

Use DOS Virtual DMA Services to validate physical contiguity, honor DMA boundaries, manage bounce buffers, and unlock memory after transfers.

Direct memory access is not just a faster way to copy bytes. A DMA controller or bus-mastering adapter needs a physical address that the hardware can reach, while a protected-mode program may hold only a selector:offset or virtual address. The buffer may be fragmented in physical memory, outside the device’s address range, or cross a boundary that the controller cannot traverse. DOS Virtual DMA Services (VDS) were designed to let drivers and applications ask the environment to validate or map such regions.

Microsoft’s VDS application note describes the classic INT 4Bh interface and its DOS-era assumptions. It is an archival specification, not a promise that every FreeDOS boot configuration includes a VDS provider. A driver must detect support and handle absence. In particular, a clear availability bit does not prove that linear and physical addresses are identical: the program may be running in virtual-8086 mode under software that lacks VDS.

Detect a provider, then query its contract

The VDS note identifies bit 5 of byte 0040:007Bh in the BIOS data area as the availability indicator. A VDS-aware driver checks it before issuing an INT 4Bh VDS function. It must not assume that the provider persists forever: the note warns that memory managers and virtual-mode environments may initialize or terminate services as their state changes.

VDS uses AH=81h with AL selecting a function. The version query (AL=02h) reports the interface version and provider capabilities. Read that result before relying on optional behavior such as automatic remapping. Check carry after every service; on failure, AL carries the defined error code. A successful call is a contract for the requested region and flags, not a blanket guarantee that every buffer in the process is DMA safe.

The request structure, called the DMA Descriptor Structure (DDS), carries the region size, linear or virtual address components, a buffer identifier, and the physical address returned by the provider. The old specification defines offsets for those fields. Match the structure layout and calling convention exactly; a pointer to a compiler-packed C structure is not automatically compatible with an assembly interface that expects a particular field width and far pointer.

Lock a region and specify hardware constraints

AH=81h, AL=03h locks a DMA buffer region. The caller describes the logical region in the DDS and passes the descriptor pointer in ES:DI. The VDS provider determines whether the region is physically contiguous and satisfies the requested alignment restrictions. On success it returns the physical starting address and may supply a nonzero buffer identifier if it allocated a bounce buffer. A zero identifier conventionally means no separate buffer was allocated.

Classic PC/AT DMA channels have boundary constraints: standard controllers wrap address calculations at 64 KiB or 128 KiB physical boundaries depending on the channel and addressing model. The VDS flags allow the requester to state relevant 64 KiB or 128 KiB boundary requirements. Do not choose a flag by habit; select the requirement from the actual controller, channel, device, and transfer mode documentation. The provider cannot infer the driver’s hardware constraints.

An automatic bounce-buffer allocation can hide a non-contiguous or boundary-crossing source region, but it changes data movement semantics. For a device that reads host memory, the input data may need to be copied into the VDS buffer before DMA. For a device that writes host memory, the completed data may need to be copied back when the region is unlocked. The caller must specify the appropriate flags and preserve the DDS fields returned by the provider.

The important lifecycle is:

detect VDS and query supported version
describe the exact source/destination region and hardware boundary
lock the region; on carry set, do not start DMA
program the device with the returned physical address
wait for transfer completion or abort through the device driver
unlock the same region, requesting copy-back when required

This is a lifecycle sketch, not a complete driver. Starting DMA after a failed lock is a correctness bug. Releasing a region before the device has stopped can let memory move or be reused while hardware is still writing. Forgetting to unlock leaks locks or bounce-buffer resources. Preserve the descriptor and buffer ID until cleanup is complete.

Physical addresses are not linear addresses

In real mode, linear and physical addresses commonly coincide on a conventional PC-compatible mapping, but a V86 monitor or protected-mode extender can change that assumption. The Microsoft note explicitly warns that when the VDS-present bit is clear, software may still be in V86 mode and may have no way to prove physical and linear addresses match. Never turn “no VDS detected” into “safe to program DMA with this pointer.”

VDS also distinguishes a region already known to have a physical address from a logical buffer needing translation. Do not pass an adapter’s physical memory or an address allocated through a separate hardware-specific API to a service that interprets its address fields as linear. The specification documents separate translation controls for these cases; mixing address domains can send a device to the wrong memory.

Large requests need particular care. A VDS implementation may impose a maximum buffer size, fail when a bounce buffer is busy, or return a maximum contiguous length on failure. A driver should split a transfer only where the device protocol permits it, such as at a sector or track boundary documented for that device. Do not split a SCSI command or disk request arbitrarily and assume its state machine allows partial completion.

Failure handling and concurrency

Common VDS failures include a non-contiguous region, a physical boundary crossing, an unavailable buffer, a locked-page failure, an invalid address, and reserved flag bits set by the caller. Each error has different implications. A caller may be able to split a transfer after a boundary failure; an invalid descriptor is a programming bug; a busy buffer may justify a bounded retry; an absent provider means the driver must choose a separately supported transfer path or refuse the operation.

Do not busy-loop forever on a buffer-in-use error. Another device or driver may own the provider’s shared bounce buffer. Apply a finite wait policy, preserve system responsiveness, and report the underlying error. Multiple concurrent DMA requests need separate descriptors and correct lock accounting. The VDS note states that page locks may be counted and that a region must be unlocked after DMA completes; treat that lifetime as a strict resource obligation.

FreeDOS deployment considerations

FreeDOS is often used in real mode, where older drivers may program a DMA controller directly, but that does not imply a VDS service is installed. A protected-mode extender, memory manager, or host may change the mapping model. Detect the actual environment during initialization and consult the specific driver and memory-manager documentation. Do not tell users to install a random VDS provider merely to suppress a detection error; the provider must match the DMA device, operating mode, and system configuration.

Testing should include a real or accurately emulated controller path, a non-contiguous or forced-boundary test region, provider absence, maximum-size requests, timeout/abort cleanup, and the transfer direction that exercises both copy-in and copy-back behavior. Verify buffer contents before and after the device operation. Test that every failure path unlocks any region that had already been acquired.

Operational rule

The VDS boundary is where a DOS driver translates a software memory region into a hardware-valid DMA target. Detect support, query capability, declare the device’s physical constraints, honor the returned address and buffer identifier, and unlock only after the hardware is done. If any step fails, do not start DMA with a guessed address. This disciplined lifecycle is what makes direct DMA compatible with memory managers rather than merely lucky on one real-mode test machine.

Related:

Sources:

Comments