MS-DOS 6.22 Background Print Spooling with PRINT.COM

In MS-DOS 6.22, a fundamentally single-tasking operating system, background print spooling is achieved through PRINT.COM, an external utility that operates as a Terminate-and-Stay-Resident (TSR) program. By hooking system timer interrupts, monitoring internal operating system state flags, and utilizing the DOS idle loop, PRINT.COM creates a lightweight, cooperative multitasking mechanism. This architecture allows MS-DOS to transmit print data to output ports during idle CPU cycles while returning execution control to foreground applications.

TSR Initialization and Memory Allocation

When invoked for the first time, PRINT.COM establishes a resident footprint in conventional memory. During initialization, the utility sets up internal data structures, including a print job queue (configurable from 4 to 32 files via the /Q switch) and a memory transfer buffer (configurable from 512 to 16,384 bytes via the /B switch). Once the resident code installs its interrupt vectors and allocates its working memory, it executes DOS Interrupt 21h, Function 31h (Terminate and Stay Resident), returning the transient portion of the utility to the command prompt while leaving the core engine active in RAM.

The DOS Reentrancy Problem and the InDOS Flag

MS-DOS is strictly non-reentrant; its internal functions rely on static memory variables rather than dynamic stack frames. If a background process invokes a DOS interrupt (such as INT 21h file I/O) while a foreground application is in the middle of executing a DOS function, internal variables are overwritten, leading to data corruption or a system crash.

To circumvent this, PRINT.COM queries the undocumented MS-DOS internal flag known as the InDOS flag (located using INT 21h, Function 34h). The InDOS byte serves as an execution counter:

  • When a foreground application enters an INT 21h service, the kernel increments the InDOS counter.
  • When the service exits, the kernel decrements the counter.
  • PRINT.COM inspects this flag before performing any disk reads to populate its print buffer. If InDOS is non-zero, PRINT.COM defers disk access, preventing reentrant calls to DOS file systems.

Time-Slicing via Timer Interrupts (INT 08h and INT 1Ch)

PRINT.COM achieves regular execution intervals by hooking the hardware timer interrupt (INT 08h) or the user timer tick (INT 1Ch), which fires approximately 18.2 times per second (every 54.9 milliseconds).

On each timer tick, PRINT.COM evaluates system conditions using predefined scheduling parameters:

  • Time Slice Allocation (/S): Specifies the number of clock ticks the background spooler is allowed to retain control before handing execution back to the foreground process (default is 8 ticks).
  • Tick Wait (/T): Specifies the number of ticks the spooler must wait if the printer port is busy before trying again.
  • Maximum Ticks (/M): Specifies the maximum number of ticks PRINT.COM can hold the bus to output characters to the printer per slice (default is 2 ticks).

If the printer is ready, the resident program sends a batch of characters out of its buffer until its assigned time slice expires, after which it restores the processor registers and passes execution down the original interrupt chain to the foreground application.

The Role of the Idle Interrupt (INT 28h)

Because foreground programs frequently block inside DOS while waiting for keyboard input (such as at the command prompt or inside an editor), the InDOS counter could remain non-zero for extended periods, effectively starving the print spooler.

MS-DOS resolves this through Interrupt 28h (the DOS Idle Interrupt). When an interactive DOS routine is waiting for console input, it safely loops and repeatedly fires INT 28h. PRINT.COM hooks INT 28h to detect this condition. Even though InDOS is technically non-zero, the kernel guarantees that INT 28h is safe for low-level file I/O operations (functions above INT 21h, Function 0Ch). PRINT.COM takes advantage of these idle loops to read document chunks off the storage drive and replenish its internal print buffer.

Port Dispatch and Output Handling

Data stored in the resident buffer is pushed to the target device—typically a parallel port (LPT1 through LPT3) or a serial port (COM1 through COM4)—using ROM BIOS Interrupt 17h services or direct hardware port writes.

Before transmitting a byte, the spooler checks the printer's hardware status via INT 17h, Function 02h. If the printer signals an active BUSY condition, an out-of-paper error, or an off-line status, PRINT.COM immediately yields execution back to the foreground program rather than executing a blocking wait. Only when the printer asserts the READY state does PRINT.COM transmit the character via INT 17h, Function 00h. This non-blocking design ensures that hardware latency from the printer never freezes user interaction in the foreground shell.