MS-DOS 6.22 DOSKEY Macro Processing Explained

This article explores how MS-DOS 6.22 processes command macros stored in DOSKEY memory, covering its memory architecture, interrupt interception, lookup routines, and parameter substitution mechanisms. When a user defines and runs a macro, DOSKEY operates at the system level to capture keyboard input, scan internal memory buffers, parse variable tokens, and redirect expanded commands back to the COMMAND.COM command interpreter.

Memory Allocation and Storage Architecture

When loaded into conventional or upper memory, DOSKEY installs itself as a Terminate-and-Stay-Resident (TSR) program. By default, it allocates a dedicated 512-byte internal buffer—configurable via the /BUFSIZE switch—which holds both command history and active macros.

Macros are stored in a contiguous, byte-oriented table within DOSKEY’s resident data segment. Each macro entry consists of:

  • The macro name in plain text, terminated by a null byte (0x00) or an equals sign (=).
  • The expansion string containing the commands and metadata symbols.
  • A final null terminator denoting the end of that specific macro record.

Because memory is shared with the command history, defining new macros shrinks the available capacity for previous command recall.

Input Interception via Interrupt 21h

To detect when a macro is invoked, DOSKEY hooks MS-DOS Interrupt 21h (the DOS system API). It specifically monitors Function 0Ah (Buffered Keyboard Input), which is the standard system call COMMAND.COM uses to retrieve a line of text from standard input (CON).

  1. Interception: When the user types a command and presses Enter, DOSKEY’s custom INT 21h handler intercepts the call before COMMAND.COM receives the raw string.
  2. Parsing the Primary Token: DOSKEY reads the user's input buffer, strips leading whitespace, and isolates the first word (the potential command or macro name).
  3. Table Scanning: The TSR performs a linear, case-insensitive search through its macro memory block, comparing the initial token against stored macro labels. If no match is found, DOSKEY simply updates its command history buffer and passes the unmodified input back to the original INT 21h handler.

Token Expansion and Parameter Parsing

When DOSKEY finds a matching label, it replaces the typed command with the expanded string stored in memory. During this substitution phase, it parses specific dollar-sign ($) meta-characters:

  • Positional Arguments ($1 through $9): DOSKEY scans the remainder of the user’s typed line, separates parameters delimited by spaces or tabs, and substitutes them into corresponding $1–$9 tokens within the macro string.
  • Global Arguments ($*): Any text typed after the macro name is captured in its entirety and substituted wherever $* appears.
  • Redirection and Piping ($G, $L, $B): Because standard symbols like >, <, and | would be interpreted immediately during macro creation, DOSKEY preserves them internally as meta-characters and converts them back to ASCII redirection symbols ($G to >, $L to <, $B to |) during expansion.

Multi-Command Processing ($T)

DOSKEY macros support command chaining through the $T delimiter. Because MS-DOS is single-tasking and COMMAND.COM expects a single command per input line:

  1. When $T is encountered, DOSKEY splits the macro expansion into discrete commands.
  2. The first command segment is written into the buffer provided by COMMAND.COM's INT 21h Function 0Ah request.
  3. DOSKEY retains the remaining command segments in an internal execution queue.
  4. On subsequent INT 21h Function 0Ah calls from COMMAND.COM, DOSKEY checks its execution queue before reading the keyboard. If queued commands remain, it automatically populates the input buffer with the next command segment without prompting the user.

Final Execution Hand-off

Once the macro text is fully expanded and formatted, DOSKEY writes the completed string directly into the calling program's memory buffer (provided by COMMAND.COM via the registers passed to INT 21h). DOSKEY sets the buffer's character count byte, restores CPU registers, and executes an IRET (Interrupt Return) instruction. COMMAND.COM processes the resulting string as if it had been typed manually at the command prompt.