How MS-DOS 6.22 Handles Interrupt 21h Vectoring

MS-DOS 6.22 relies on the x86 real-mode software interrupt mechanism to provide an interface between user-level programs and the operating system's underlying services. Interrupt 21h (INT 21h) serves as the primary Application Programming Interface (API) dispatcher for file access, memory management, process control, and console input/output. Understanding how MS-DOS 6.22 vectors this interrupt involves analyzing the Real Mode Interrupt Vector Table (IVT), the CPU's hardware interrupt sequence, internal DOS stack switching, and function dispatching based on CPU registers.

The Interrupt Vector Table (IVT) Setup

In the 16-bit real mode of MS-DOS 6.22, memory addresses 0000:0000h through 0000:03FFh are reserved for the Interrupt Vector Table. The IVT contains 256 entries, each consisting of a 4-byte far pointer (a 16-bit segment and a 16-bit offset) pointing to the corresponding Interrupt Service Routine (ISR).

The vector for INT 21h is located at memory address 0000:0084h (calculated as 21h * 4 = 84h). During the boot process, when MSDOS.SYS initializes:

  1. The kernel loads its system routines into memory.
  2. The kernel sets the IVT entry at offset 0084h to point to the primary DOS entry point routine.
  3. This vector remains active unless hooked or modified by a Terminate and Stay Resident (TSR) program, a debugger, or device driver.

Invocation and Processor-Level Context Switching

When a program requests a DOS service, it loads specific parameters into general-purpose registers—most notably placing the service function code into the AH register—and executes the INT 21h instruction.

Upon encountering INT 21h, the x86 processor executes the following hardware sequence:

  1. Pushes the 16-bit FLAGS register onto the current stack.
  2. Clears the Interrupt Enable Flag (IF) and the Trap Flag (TF), temporarily disabling maskable hardware interrupts to ensure atomic transition.
  3. Pushes the current Code Segment (CS) register onto the stack.
  4. Pushes the current Instruction Pointer (IP) register onto the stack.
  5. Reads the 4-byte address from 0000:0084h into IP (low word) and CS (high word), transferring execution directly to the MS-DOS kernel's entry point.

Kernel Entry and Stack Switching

MS-DOS 6.22 is fundamentally non-reentrant. Because the calling application's stack space might be limited or corrupted, the DOS ISR must carefully manage internal execution contexts:

  1. Reentrancy Check (InDOS Flag): The kernel references its internal InDOS variable (tracked via an internal counter). If an interrupt occurs while InDOS is already incremented, the kernel determines whether the call is safe or if it originates from a critical hardware event (like INT 08h or INT 09h).
  2. Stack Swapping: The kernel preserves the caller's Stack Segment (SS) and Stack Pointer (SP) registers in internal variables and switches the processor to one of several internal DOS stacks (such as an auxiliary stack, an I/O stack, or an error stack).
  3. Register Preservation: General-purpose registers (AX, BX, CX, DX, SI, DI, BP, DS, ES) are saved onto the stack so that their states can be preserved or selectively returned to the caller.

Function Dispatching via the Jump Table

Once the environment is stabilized, the entry point code isolates the function number found in the AH register (and in some extended calls, AL as a sub-function number):

  1. Range Validation: The kernel verifies that the value in AH falls within the supported range of DOS API functions (functions 00h through 6Ch, alongside minor extensions).
  2. Address Resolution: The kernel indexes an internal jump table using the value in AH. Each entry in the jump table contains the near address of the specific handler subroutine inside the DOS kernel code segment.
  3. Branching: The dispatcher performs an indirect jump to the target handler (for example, reading a file via Function 3Fh or terminating a program via Function 4Ch).

Execution and Return Sequence

Once the specific function finishes its operations:

  1. Status Code Reporting: The handler updates status indicators. Success or failure is often communicated by setting or clearing the Carry Flag (CF) within the saved image of the caller's FLAGS register on the stack. Return codes are placed into AX.
  2. Stack Restoration: The kernel decrements the InDOS flag, restores the caller's original SS and SP registers, and restores the saved general-purpose registers (excluding those modified to return data).
  3. IRET Execution: The handler terminates with an IRET (Interrupt Return) instruction. The processor automatically pops IP, CS, and the modified FLAGS from the caller's stack, immediately resuming execution at the instruction directly following the original INT 21h call in the application.