Skip to content
FreeDOSDeep Dive Published Updated 6 min readViews unavailable

FreeDOS INT 21h/AH=50h: Current PSP Context, Ownership, and Safe Switching

Treat FreeDOS INT 21h/AH=50h as mutable process context, trace memory and handle effects, and switch PSPs only with ownership and reentrancy controls.

The Program Segment Prefix is not merely a 256-byte startup record. DOS also maintains a current PSP value that process services consult when they attribute memory, file handles, and other state. INT 21h/AH=50h changes that value. This is a narrow process-manager interface: ordinary programs should inspect their PSP through a getter, not switch the kernel’s active ownership context to reach another process.

FreeDOS implements the call by assigning the segment in BX to its current-PSP variable. That simple operation has broad effects because later DOS calls use the selected PSP as context. Treat the call like temporarily changing a process identity inside a cooperative, single-tasking system: it is safe only when the caller owns the transition, the target PSP is valid and live, the execution point permits DOS calls, and every exit path restores the prior value.

Register contract and compatibility boundary

Function Input Output Practical meaning
AH=50h BX = segment of the PSP to make current No defined result Changes the DOS current-PSP context
AH=51h None BX = current PSP Historical getter; compatibility varies
AH=62h None BX = current PSP Getter introduced for DOS 3.0 and later

The historical reference classifies the setter as an internal DOS call and notes that it was undocumented before DOS 5. FreeDOS’s kernel function table lists it as supported, and the current FreeDOS dispatcher handles AH=51h and AH=62h through the same getter. Those statements are not interchangeable portability guarantees. A utility targeting several DOS-compatible kernels should test its exact version matrix and avoid assuming every clone documents identical reentrancy or validation behavior.

In particular, the function has no ordinary success/failure result. Do not interpret a returned register value as proof that BX referred to a valid PSP. Current FreeDOS source assigns the supplied segment directly to its current-PSP variable; it does not turn this call into a process lookup or validate that the target is an allocated, live process. Passing an arbitrary segment can make subsequent DOS services operate on unrelated memory.

The PSP selection affects later operations

The current PSP influences more than a diagnostic query. In FreeDOS source, memory allocation records the current PSP as the owner of a new DOS memory block. Handle operations index the current PSP’s Job File Table, and the sharing layer associates some file activity with the current PSP. Process execution also changes the current PSP as DOS enters a child and restores the parent when that child terminates.

That creates important failure modes:

  • A memory block allocated under the wrong PSP can be charged to, or later reclaimed with, the wrong process.
  • A handle number is interpreted using the selected PSP’s handle table. A valid number in one table can mean a different handle (or a closed slot) in another.
  • Sharing and cleanup behavior that uses process identity can be misattributed if a TSR or callback changes the PSP and leaves it changed.
  • Changing the PSP is not a way to duplicate another program’s open handles. The target PSP must already contain the appropriate handle-table state, and the normal DOS inheritance rules still apply.

The current PSP is therefore not equivalent to the parent-segment field inside a PSP, an environment segment, a shell variable, or a conventional process ID allocated by a protected operating system. It is a mutable piece of DOS kernel context used by subsequent services.

Read current context before considering a switch

When a diagnostic needs to identify its own process segment, use a getter and leave the state untouched. On DOS 3.0 and later, the documented AH=62h form returns the current PSP segment in BX. The following real-mode outline reports that value; it does not edit the PSP or access another process:

        mov     ah, 62h         ; Get current PSP (DOS 3.0+)
        int     21h             ; BX = current PSP segment
        mov     [saved_psp], bx ; Store only in the caller's own data

The example assumes DS addresses the program’s data segment and that saved_psp is a word in that segment. Code targeting an older DOS version must use a separately reviewed compatibility path rather than silently invoking a newer function.

If a specialized process manager genuinely must switch context, its invariant is “save, set, perform only the intended DOS work, restore.” Conceptually:

        mov     ah, 62h
        int     21h
        mov     [saved_psp], bx

        mov     ah, 50h         ; BX must name a valid live PSP
        mov     bx, [service_psp]
        int     21h
        ; Perform only the operations designed for service_psp.

        mov     ah, 50h
        mov     bx, [saved_psp]
        int     21h             ; Restore before returning to the caller.

This is a register-contract illustration, not a drop-in TSR handler. Interrupts can arrive between these instructions, a DOS call can fail, and an ordinary hardware interrupt handler is not made reentrant by changing the PSP. Production code must protect the whole transition according to the exact DOS/kernel and interrupt context, preserve required registers, and have a restoration path for every recoverable branch.

Reentrancy and interrupt context are separate questions

Changing cu_psp does not make DOS safe to call while DOS is already active. A TSR still has to respect the InDOS and critical-error state, the restrictions of the interrupt it hooks, stack ownership, and the exact DOS version. The historical interrupt reference records a DOS 2.x caveat for calling AH=50h inside INT 28h; later DOS versions changed their internal stack behavior. That is a version-specific rule, not a blanket promise that an arbitrary FreeDOS interrupt handler may call the setter at any time.

The conservative design is to keep the hardware hook minimal, defer work to a documented safe point, and test under the exact FreeDOS kernel, memory manager, redirector, and TSR stack that the application supports. If the operation can be performed by the foreground process without PSP mutation, prefer that simpler route. Keep a clean boot profile available for recovering from a resident program that corrupts DOS context.

Validation plan for process-manager code

Test context transitions in a disposable VM image and record the kernel build. Before and after each intended switch, read the current PSP with the supported getter. Verify memory-block ownership with a diagnostic utility, open and close only handles known to belong to the selected PSP, and exercise child EXEC followed by normal and error termination. Add a negative test that rejects a PSP segment not created by the test harness; never use random segments to probe behavior on a real filesystem.

For TSR code, test nested interrupts, repeated hotkeys, DOS idle entry, a critical-error prompt, and an interrupted cleanup path. Confirm that the original PSP is restored before the handler chains or returns. Also test the oldest and newest DOS versions claimed; FreeDOS’s current source behavior is useful evidence about FreeDOS, not a substitute for compatibility testing on MS-DOS, PC DOS, DR-DOS, or a Windows DOS environment.

AH=50h is valuable precisely because it changes implicit ownership context. Treat it as a process-manager primitive, not as a general-purpose handle or memory shortcut. A valid PSP, a safe execution point, a bounded operation, and unconditional restoration are the minimum conditions for using it responsibly.

Related:

Sources:

Comments