Skip to content
FreeDOSDeep Dive Published Updated 8 min readViews unavailable

DOS Version Reporting: INT 21h, FreeDOS CALLVER, and MS-DOS SETVER

Trace DOS version queries through INT 21h, FreeDOS VERSION and CALLVER, and MS-DOS SETVER, separating reported compatibility from actual capabilities.

Some DOS applications refuse to start unless a version query returns the exact release they expect. That check can be useful when software depends on a known API generation, but many programs use it as a blunt compatibility gate: a newer DOS may provide the needed service and still be rejected because its version string is unfamiliar. MS-DOS SETVER and FreeDOS VERSION/CALLVER address that reported-version problem in different ways. None of them installs missing kernel services or proves that an application is safe to run.

The useful model has three separate facts: the version a process asks DOS to report, the kernel’s actual implementation and build, and the application behavior that has been tested. Confusing those facts leads to “version fixes” that appear to solve startup but merely move the failure to a later file operation. This is a focused API and configuration guide, not a general FreeDOS-versus-MS-DOS comparison; see the broader compatibility analysis for the larger system differences.

The common application query is INT 21h, AH=30h

The traditional DOS version query is INT 21h with AH=30h. It returns the major version in AL and minor version in AH. The DOS 5-and-later interface uses the input value of AL to select additional information for BH: with AL=00h, the result includes the OEM number; with AL=01h, the result is a version flag on implementations that support that extension. Older DOS versions and compatible operating environments do not necessarily interpret the input register identically. A portable application should make only the request it understands and should not treat an OEM byte or reserved flag as a universal product identifier.

A MASM/TASM-style fragment for the conventional version result is:

        mov     ah, 30h         ; DOS Get Version
        xor     al, al           ; request the traditional/OEM form
        int     21h
        ; AL = reported major version, AH = reported minor version
        ; BH = OEM identifier on the documented DOS 5+ form

The order is easy to misremember because the function number is in AH, then the return version is split with major in AL and minor in AH. Save the outputs immediately if later DOS calls will reuse those registers. When comparing values in a debugger, print them in decimal or explicitly label hexadecimal bytes: major 7, minor 10 is commonly displayed as version 7.10, not as a single decimal register value.

The result answers “what version should this process see through this interface?” It does not answer “which kernel binary is running?” and it does not establish which extensions, filesystem behavior, memory managers, redirectors, or device drivers are present. RBIL documents historical DOS boxes and compatibility environments whose return values differ from the underlying host, which is a reminder that even an unmodified version call can describe a compatibility layer rather than the physical machine’s whole software stack.

What Microsoft SETVER changed

MS-DOS SETVER is a per-program compatibility table. Microsoft documentation describes loading SETVER.EXE from CONFIG.SYS, then maintaining entries by executable file name. A matching program receives a selected DOS version number when it asks for the version. It is not a system-wide kernel downgrade and is not a filename-independent setting. Because the resident table is loaded during startup, changes require rebooting before the updated table is active.

The old configuration pattern is:

DEVICE=C:\DOS\SETVER.EXE

An administrator could then add a particular application name and target report value using the MS-DOS SETVER command syntax. The correct version is a compatibility decision, not a guess: test the application’s documented supported range and keep the entry limited to the executable that needs it. If a program is actually incompatible with the newer kernel, lying about the version can make it proceed into code paths that depend on absent or different behavior. Microsoft’s own documentation warns that an incompatible program may lose or corrupt data or destabilize the system.

The table’s executable-name matching is materially different from changing one global system version. It makes it possible for different legacy programs to receive different reports, but it also creates an operational dependency: the resident table must be loaded, the filename must match, and the machine must be restarted after table changes. Diagnosing an unexpected result should therefore include the loaded SETVER.EXE, the table entry, the invoked executable’s actual name, and the exact process launch path.

FreeDOS uses VERSION and CALLVER instead

FreeDOS documents VERSION=x.yy as a kernel configuration directive read from CONFIG.SYS or FDCONFIG.SYS. The documented default is 7.10; the directive selects the DOS version the kernel reports, while VER /R can show reported and FreeDOS internal version information. It is a global FreeDOS kernel setting, not a per-executable filename table. FreeDOS documentation recommends 7.10 for a kernel with FAT32 support and notes that the directive does not change FreeCOM’s own shell version.

For a one-program workaround, FreeDOS provides the FreeCOM command CALLVER. It can start one program in a new shell using COMSPEC with a selected fake reported DOS version, or change the reported value globally in the FreeDOS environment. For example:

CALLVER 6.22 C:\LEGACY\APP.EXE

The one-program form is usually easier to audit than a permanent global value: it records which application needs the compatibility report and avoids changing the expectation for unrelated software. Check the FreeCOM documentation for the exact behavior of the installed version. In particular, CALLVER does not override the kernel’s VERSION value shown by VER /R; do not infer the active kernel setting from the version string that one child process sees.

FreeDOS explicitly states that it does not implement MS-DOS SETVER and that CALLVER plus VERSION do not exactly reproduce its per-program table semantics. Do not copy an MS-DOS SETVER command into FreeDOS and expect it to work. Choose the FreeDOS mechanism that matches the scope required, then test the program under the resulting reported version.

“True version” calls need compatibility caveats

DOS 5-and-later systems also define INT 21h, AX=3306h, commonly described as “Get True Version.” RBIL records BL as major version, BH as minor version, DL as revision, and DH as flags; AL=FFh indicates a true DOS version below 5 in the documented interface. RBIL distinguishes this from AH=30h, whose result can be changed by MS-DOS SETVER.

The name “true version” should not be treated as a security guarantee or an omniscient hardware identity. RBIL documents differences and redirection-related caveats among DOS-compatible kernels and environments. An application that uses the function should check that the target supports the call and interpret the result as one additional compatibility signal. A simple version-report API is not an anti-spoofing boundary, and code should not use it as a substitute for probing the specific function it actually requires.

Likewise, a DOS version number is not a capability negotiation protocol. If software needs a specific service, a better test is often to exercise a documented query for that service, check a driver installation interface, or make a narrowly scoped operation and handle its failure. Version checks can remain useful for known historical differences, but they should be tied to documented behavior rather than used as the only basis for enabling a feature.

Diagnose the report at the process boundary

When a version-sensitive program behaves differently, capture at least four observations:

  1. The FreeDOS or MS-DOS kernel’s configured and internal version, using the appropriate native diagnostic such as FreeCOM VER /R where available.
  2. The value returned by INT 21h/AH=30h inside the affected program, including major and minor bytes.
  3. Whether a version-translation mechanism is active: the FreeDOS VERSION setting, the CALLVER launch path, or the loaded MS-DOS SETVER table.
  4. The actual failing API, file operation, or device service after startup succeeds.

Run the same executable without a compatibility override in a disposable disk image or snapshot, then apply one override at a time. Record the exact executable filename and launch command. Compare not only startup but a small functional test that exercises the behavior the application needs, such as opening a representative file, accessing the expected disk format, or invoking the relevant API. Preserve a rollback point before changing boot configuration or a resident table.

The outcome should be written as a bounded compatibility statement: “Application X passes its startup check when FreeDOS reports Y, and its tested workload Z passes on kernel build K under configuration C.” Avoid “DOS version Y is compatible” unless you have tested the actual program and workload. Another build, driver set, locale, or hardware configuration can change the outcome.

Choose the narrowest honest workaround

If the program only rejects a familiar but functionally compatible environment, a per-program report override can be a practical, reversible workaround. Prefer FreeDOS CALLVER for an isolated launch when it covers the case; use the VERSION kernel directive only when the whole FreeDOS environment requires that reported value. On MS-DOS, use a targeted SETVER entry only when the program’s compatibility is established and the executable match is correct. In every case, document why the override exists, which binary it applies to, how it was tested, and how to remove it.

If the program still fails after its version check passes, the version was not the missing implementation. Investigate the real requirement: unsupported DOS calls, assumptions about internal data structures, a missing driver, a disk or memory limit, timing, or hardware access. A truthful diagnosis distinguishes a version string from the kernel’s actual capabilities; that distinction is the difference between a controlled compatibility workaround and an unexplained production risk.

Related:

Sources:

Comments