How MS-DOS 6.22 Executes 16-Bit Real Mode Binaries

Executing a 16-bit real-mode binary in MS-DOS 6.22 is a low-level process that involves interpreting the binary format, allocating physical memory, preparing execution metadata, and directly configuring the x86 CPU registers. The operating system utilizes the INT 21h EXEC system service to distinguish between flat .COM files and relocatable .EXE files, constructs a 256-byte Program Segment Prefix (PSP) in conventional memory, resolves any necessary segment fixups, and transfers execution control directly to the microprocessor within the 1 MB real-mode address space.

Binary Invocation and the EXEC Service

When a user or batch script runs an executable file from the command line, COMMAND.COM parses the input string, locates the specified file on disk, and invokes the MS-DOS EXEC function: Interrupt 21h, Function 4Bh (Load and Execute Program).

DOS opens the target file, reads its initial bytes, and determines its format:

  • COM Binaries: Flat memory models containing raw machine code with no file header, capped at roughly 64 KB (one segment minus the 256-byte PSP).
  • EXE Binaries: Structured files identified by the signature ASCII characters MZ (0x4D, 0x5A) in the first two bytes, indicating an MZ-format header with relocation data.

Memory Allocation via MCBs

MS-DOS manages conventional memory (the first 640 KB) using a linked list of Memory Control Blocks (MCBs). Each MCB contains an identifier byte, the owning process ID, and the size of the contiguous memory block that follows it.

  1. For .COM Files: DOS requests the largest available contiguous block of memory. By default, MS-DOS allocates all remaining conventional memory to the .COM program.
  2. For .EXE Files: DOS reads the MINALLOC and MAXALLOC fields from the MZ header. It computes the required paragraph count (16-byte blocks) to hold the program image plus its uninitialized data and stack, then traverses the MCB chain using a first-fit or best-fit strategy to allocate the necessary space.

Constructing the Program Segment Prefix (PSP)

Before loading the binary code, DOS creates a standardized 256-byte (100h) data block called the Program Segment Prefix (PSP) at the base of the allocated segment. The PSP acts as a control interface between the running binary and DOS, containing:

  • Offset 0x00–0x01: An INT 20h instruction (exit to DOS) for backward compatibility with CP/M.
  • Offset 0x02–0x03: The segment address of the top of allocated memory.
  • Offset 0x0A–0x15: Saved vectors for the termination address (INT 22h), Ctrl-C exit (INT 23h), and critical error handler (INT 24h).
  • Offset 0x16–0x17: Parent process segment address.
  • Offset 0x18–0x2B: The Job File Table (JFT), tracking default open file handles (stdin, stdout, stderr, stdaux, stdprn).
  • Offset 0x2C–0x2D: Segment address of the process environment block.
  • Offset 0x80–0xFF: The Command Tail (raw command-line arguments) and the default Disk Transfer Area (DTA).

Loading the Program Image

The mechanism for reading the executable file from disk into memory depends entirely on whether the target is a .COM or an .EXE file.

Loading .COM Programs

Because a .COM file is a direct memory image, DOS reads the entire file into memory starting immediately after the PSP at offset 0x0100 within the allocated segment. No segment adjustment, relocation, or header parsing occurs.

Loading .EXE Programs and Handling Relocations

An .EXE file contains structured code and data designed to span multiple 64 KB segments:

  1. Header Parsing: DOS reads the MZ header to extract the header size, image size, initial register states, and the file offset of the Relocation Table.
  2. Reading the Image: DOS loads the binary image into memory directly following the PSP, aligning it on a paragraph boundary.
  3. Relocation Processing: Because real mode addresses depend on where the program resides in physical memory, hardcoded segment references within the binary must be adjusted:
    • DOS reads the relocation table, which consists of segment:offset pointers referencing every 16-bit segment value inside the loaded image.
    • For every entry, DOS adds the base segment address where the program was loaded to the word located at the target address. This process patches all internal segment pointers so they point to valid physical memory locations.

Register Initialization and Execution Handoff

Once the memory image is prepared, MS-DOS prepares the CPU's general-purpose and segment registers for execution.

.COM State Setup

  • Segment Registers (CS, DS, ES, SS): All point to the base segment of the PSP.
  • Instruction Pointer (IP): Initialized to 0x0100 (the entry point following the PSP).
  • Stack Pointer (SP): Set to 0xFFFE (the top of the 64 KB segment, or the memory boundary if less than 64 KB is available). A word of 0x0000 is pushed onto the stack.
  • General Registers: Cleared to zero (or specific BIOS drive flags where applicable).

.EXE State Setup

  • Code Segment (CS) and Instruction Pointer (IP): Initialized to the start address specified in the MZ header, offset by the program's base load segment.
  • Data Segment (DS) and Extra Segment (ES): Set to the base segment of the PSP, ensuring the program can immediately access its command tail and environment variables.
  • Stack Segment (SS) and Stack Pointer (SP): Derived directly from the MZ header values, with SS adjusted by the program's base segment.

The Real-Mode Jump

Because MS-DOS 6.22 runs as a single-tasking operating system without hardware protection rings or virtual memory paging, the operating system executes a far jump (JMP FAR CS:IP) or an IRET instruction directly to the entry point. The 16-bit binary gains unmediated control over the CPU, using the standard segmented memory model (Segment * 16 + Offset) to access memory and using software interrupts (INT 21h, INT 10h, INT 13h) to request operating system and BIOS services.