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
InDOScounter. - When the service exits, the kernel decrements the counter.
PRINT.COMinspects this flag before performing any disk reads to populate its print buffer. IfInDOSis non-zero,PRINT.COMdefers 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 ticksPRINT.COMcan 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.