Skip to content
FreeDOSDeep Dive Published Updated 4 min readViews unavailable

DOS INT 2Fh: The Multiplex Interface Shared by TSRs and System Utilities

Understand DOS INT 2Fh multiplex calls, installation checks, identifier collisions, register conventions, and safe resident-program integration.

DOS software did not have a single registry for every resident utility that wanted to expose a service. The software interrupt INT 2Fh became a multiplex interface: a caller places a program or service identifier in AH, a function selector in AL, and invokes the vector. DOS components and third-party terminate-and-stay-resident programs (TSRs) can use the same interrupt vector while distinguishing their own subinterfaces.

This is a convention layered on the real-mode interrupt mechanism, not one uniform API with one universal function table. INT 21h remains the main DOS system-call interface; INT 2Fh is shared by DOS components, drivers, and resident utilities, and the meaning of a particular call depends on the handler that owns its identifier.

A multiplex call has two selectors

The AH value selects an identifier namespace and AL selects a function within that interface. A common convention uses AL=00h for an installation check, but the exact returned signature and register contract are service-specific. Ralf Brown’s Interrupt List warns that callers should initialize BX, CX, and DX to zero for an installation check when searching for a free AH identifier, and should not assume arbitrary registers are preserved. That advice reflects the historically crowded environment, not a guarantee that every handler behaves identically.

The interrupt list records conventional identifier ranges, including ranges associated with IBM, Microsoft, network software, and applications. These ranges reduce collisions when followed, but they are not a centrally enforced allocation service. Old TSRs can ignore them, use undocumented values, or collide with software installed later. A presence check therefore needs to validate a service-specific signature, not merely observe that some handler responded.

Why TSRs use the interface

A resident program can hook INT 2Fh, examine incoming selectors, process calls it owns, and chain unrelated calls to the previous handler. This lets another program ask whether the TSR is installed or request one of its functions without knowing the resident program’s memory address. Utilities such as print spooling and network redirectors historically exposed multiplex calls in this fashion.

Hook order matters. Multiple programs can wrap the same vector, so a handler must preserve registers and flags according to the contract, and must pass unknown function selectors to the prior handler rather than swallowing them. A faulty chain can break another TSR even when both programs work correctly in isolation. The RBIL notes specifically call out preserving returned flags when a TSR calls the prior handler and then returns to the original caller.

A defensive caller pattern

The following is a contract sketch, not a complete assembly routine. The caller must replace the placeholder identifier and signature with the values defined by the target service’s own documentation:

; Select the service's documented multiplex identifier.
mov ah, SERVICE_ID
mov al, 00h              ; only if the service defines this as its check
xor bx, bx
xor cx, cx
xor dx, dx
int 2Fh

; Validate the service-specific signature and version here.
; Do not infer presence merely because the interrupt returned.

If the check succeeds, call only the version and function range the service documents. Preserve any registers the interface requires, and treat carry flags or returned values as part of that service’s contract. A generic INT 2Fh wrapper cannot safely invent a universal “installed” response.

FreeDOS compatibility and source inspection

FreeDOS implements the DOS-compatible kernel and installs an INT 2Fh handler as part of its real-mode interrupt environment. That does not mean every historical Microsoft or vendor subfunction is implemented in every FreeDOS kernel build. Compatibility must be tested against the actual FreeDOS version and the specific utility. The kernel’s public source is useful for investigating a behavior, while RBIL is a reference for historical interfaces assembled from documented and observed DOS software.

When debugging a TSR collision, boot with a minimal configuration, then add resident programs one at a time. Record the load order, inspect the interrupt vector before and after each installation, and test the exact application that fails. If two programs claim or answer the same multiplex identifier, change the installation set or use a service-specific compatibility option; changing an unrelated DOS setting will not resolve an interrupt-chain conflict.

Practical limits

INT 2Fh is a useful historical interoperability pattern, but it has no modern isolation boundary. A resident handler executes in a shared real-mode environment and can corrupt state or intercept calls system-wide. Avoid installing untrusted TSRs, preserve a known-good boot configuration, and keep recovery media available when testing low-level interrupt hooks on real hardware.

Think of INT 2Fh as a shared switchboard with conventions, not a namespaced kernel API. The interrupt number identifies the switchboard; AH and AL route a request; the owning software defines the actual service contract. That mental model explains both the interface’s flexibility and its collision risk.

Related:

Sources:

Comments