Terminate-and-stay-resident (TSR) programs are DOS utilities that load into memory, return control to the command prompt, and remain active in the background so they can be triggered later by a hotkey, a timer interrupt, or another program’s request. You run a TSR once, and it stays ready until you reboot or explicitly remove it, giving single-tasking DOS a primitive form of multitasking. This article explains the mechanics behind TSRs, their real-world uses, and the compatibility issues that made them both powerful and notoriously unstable.
Background: Why DOS Needed TSRs
DOS (Disk Operating System) was designed as a single-tasking environment: one program at a time, with full control of the CPU, memory, and hardware. When you quit a program, DOS reclaimed the memory and returned you to the command prompt. There was no built-in way to keep a utility running while you worked in another application.
TSRs emerged as a workaround. A TSR would load itself into memory, then issue a DOS function call (INT 21h, function 31h) to terminate but keep its code and data resident. The program’s exit code was set to 0, and the memory block was marked as “system” so DOS would not reuse it. From the user’s perspective, the program simply returned to the prompt, but part of it was still lurking in memory, waiting for an event.
The first popular TSRs appeared in the mid-1980s—things like Borland’s SideKick (a desktop accessory with a notepad, calculator, and calendar) and various print spoolers. They solved a real problem: you wanted quick access to a calendar or a calculator without closing your spreadsheet. TSRs made that possible, and they became a staple of DOS productivity until Windows 95 made them largely obsolete.
How TSRs Work: The Mechanics
A TSR has two distinct phases: initialization and resident operation. During initialization, the program sets up its environment, installs interrupt handlers, and then terminates while leaving part of itself in memory. The resident portion contains the interrupt service routines (ISRs) and any data they need.
The key to a TSR’s operation is the interrupt vector table (IVT), a set of 256 pointers in the first 1 KB of memory. Each pointer corresponds to an interrupt number (0–255). A TSR can hook one or more of these vectors by saving the original address and replacing it with its own. When that interrupt fires, the CPU jumps to the TSR’s handler, which decides whether to act or pass control to the original handler.
Common interrupts a TSR would hook:
- INT 9 (keyboard): To detect a hotkey combination. The TSR checks the keyboard scan codes and triggers when it sees its own sequence.
- INT 8 (timer tick): Fires about 18.2 times per second. Used for background tasks like updating a clock or checking a print spooler.
- INT 28h (DOS idle): Called when DOS is waiting for keyboard input. A TSR can safely use DOS functions here because DOS is in a known state.
- INT 2Fh (multiplex): A generic “pass-through” interrupt used for communication between programs. A TSR can respond to queries from other programs.
When the TSR’s handler is invoked, it must save all registers, set up its own stack (or use a reserved area), and then either perform its task or chain to the previous handler. After finishing, it restores registers and returns. This is delicate work—any mistake can crash the system.
How to Choose a TSR for Your Needs
Choosing a TSR in the DOS era was about matching features to your workflow and hardware. Here are the factors to consider:
- Memory footprint: TSRs consume conventional memory (below 640 KB), which was precious. A small TSR (10–20 KB) was preferable to a 100 KB monster. Some TSRs could be loaded high into UMBs (upper memory blocks) using
LOADHIGHorDEVICEHIGHin CONFIG.SYS. - Hotkey conflicts: Many TSRs used Ctrl+Alt or Shift+Alt combinations. If two TSRs used the same hotkey, only one would respond. Check the documentation for the exact keys.
- Compatibility with your applications: Some TSRs interfered with programs that used the same interrupts. For example, a TSR that hooks INT 9 might conflict with a game that reads the keyboard directly.
- Resident vs. pop-up: Some TSRs were “pop-ups” that appeared over the current screen (like SideKick). Others ran silently in the background (like a print spooler). Decide which behavior you need.
- Removal: Some TSRs could be unloaded from memory with a command-line switch or a hotkey sequence. Others required a reboot. If you needed to free memory frequently, a removable TSR was valuable.
In practice, users often created a boot menu in CONFIG.SYS to load different sets of TSRs depending on the task. A typical setup might include a mouse driver, a disk cache, a print spooler, and a pop-up notepad—all loaded high to preserve conventional memory.
Common Problems and How to Fix Them
TSRs were notorious for causing system instability. Here are the most common problems and their typical solutions:
- Memory exhaustion: Loading too many TSRs left insufficient conventional memory for large applications. Fix: Use
LOADHIGHto place TSRs in UMBs, or reduce the number of TSRs. - Hotkey conflicts: Two TSRs using the same hotkey meant one would never trigger. Fix: Change the hotkey in one TSR’s configuration, or load only one at a time.
- System crashes when a TSR pops up: This often happened because the TSR attempted to use DOS functions while another program was in the middle of a DOS call. Fix: Use the
INT 28hhook for DOS calls, or avoid TSRs that pop up over graphics modes. - Video mode corruption: A pop-up TSR that saved and restored the screen could corrupt the display if it didn’t handle the current video mode correctly. Fix: Ensure the TSR supports the video mode you’re using (text vs. graphics).
- Cannot unload a TSR: Some TSRs lacked an unload function. Fix: Reboot, or use a third-party unloader that manipulates the MCB chain (risky).
One of the most common mistakes was loading a TSR that hooks INT 9 and uses DOS functions inside the handler. The keyboard interrupt can fire at any time, including when DOS is in an unsafe state. A well-written TSR would use the INT 28h hook for DOS calls, but many did not, leading to random crashes.
Comparison: TSRs vs. Modern Background Processes
TSRs are often compared to modern background processes, but the differences are stark:
- Memory protection: TSRs ran in the same address space as the foreground program, with no protection. A buggy TSR could overwrite the foreground program’s memory, causing a crash. Modern processes have isolated address spaces.
- Preemptive vs. cooperative: TSRs only ran when an interrupt fired—they were cooperative. They had to return quickly to avoid slowing the system. Modern OSes use preemptive multitasking, where the scheduler decides when each process runs.
- Resource management: TSRs directly manipulated hardware and interrupts. Modern processes use APIs and device drivers, which abstract the hardware.
- Removal: TSRs often required a reboot to remove. Modern processes can be terminated by the OS.
In essence, TSRs were a hack—a way to achieve background functionality in an OS that didn’t support it. They worked, but they were fragile. The shift to Windows 95’s 32-bit protected mode made TSRs largely unnecessary, as the OS handled multitasking and memory management properly.
Safety and Risks of Using TSRs
Using TSRs carried several risks that modern users rarely think about:
- Data loss: If a TSR crashed while a word processor had unsaved data in memory, the data could be lost. TSRs had no way to protect against this.
- System instability: A TSR that hooked an interrupt incorrectly could cause random freezes or reboots. This was especially true for TSRs that used undocumented DOS internals.
- Hardware damage: In rare cases, a TSR that misprogrammed a hardware device (like a sound card or printer) could cause a malfunction, though permanent damage was unlikely.
- Security risks: A malicious TSR could act as a keylogger or a backdoor, capturing keystrokes or network traffic. Since TSRs ran with full system privileges, they had complete access to the machine.
To minimize risks, users were advised to test TSRs in a safe environment, keep backups, and avoid loading TSRs from untrusted sources. In a corporate setting, IT departments often standardized the set of TSRs allowed on company machines.
Alternatives to TSRs
As DOS matured, alternatives to TSRs emerged:
- Task switchers: Programs like DESQview and Windows 3.0 in 386 enhanced mode could run multiple applications by swapping them to disk or using virtual memory. These provided true multitasking without the fragility of TSRs.
- Resident device drivers: Some functions that TSRs provided (like mouse support) were better implemented as device drivers loaded in CONFIG.SYS, which had a more controlled interface.
- DOS shell enhancements: Tools like Norton Commander integrated file management and viewing into a single program, reducing the need for separate TSRs.
- Windows 95 and beyond: The 32-bit protected mode environment provided preemptive multitasking, memory protection, and a stable API for background tasks, making TSRs obsolete.
For modern users, the closest analog to a TSR might be a system tray utility that runs in the background and can be triggered with a hotkey. But the underlying technology is vastly different—modern OSes provide the infrastructure that DOS lacked.
Frequently Asked Questions
Can I run TSRs on modern Windows?
No. TSRs are 16-bit real-mode programs that rely on DOS interrupts and direct hardware access. Modern Windows (64-bit) cannot run them natively. You could use a DOS emulator like DOSBox, which simulates the environment, but TSRs may not work perfectly due to timing and hardware differences.
What does “terminate but stay resident” mean exactly?
It means the program tells DOS to end its execution but keep its code and data in memory. DOS function 31h (INT 21h) does this. The program specifies how much memory to reserve, and then returns to the command prompt. The memory block is marked as “system” so DOS won’t reuse it for other programs.
How did TSRs communicate with each other?
They used the multiplex interrupt (INT 2Fh). Each TSR registered a unique ID (a number) and would respond to queries sent to that ID. For example, a print spooler might respond to a “get status” query. This allowed programs to check if a TSR was loaded and to send commands.
Why did TSRs cause so many crashes?
The main reason was reentrancy. A TSR could be invoked at any time, including when DOS or another program was in the middle of a sensitive operation. If the TSR tried to use DOS functions or access hardware without proper synchronization, it could corrupt data or cause a crash. Also, TSRs had no memory protection, so a bug could overwrite anything.
What was the largest TSR ever made?
There’s no official record, but some TSRs (like full-featured desktop accessories) could occupy 100–200 KB of conventional memory. That was a significant amount when many programs needed 400–500 KB to run. This is why loading TSRs high was so important.
Are there any TSRs still in use today?
In embedded systems or legacy industrial equipment that still runs DOS, some TSRs might be in use. Also, DOS emulators like DOSBox can run TSRs for retrocomputing enthusiasts. But in mainstream computing, TSRs are a historical curiosity.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Campus Tech Innovation Hubs: What They Are
- Poco C51 Airtel Prepaid Locked Variant India?
- Jenkins vs GitHub Actions: Which CI/CD Fits Your Team?
- Turn Off Backup Encryption in iTunes?
- Shared VPS Hosting: Is It Better?
- Disk Defrag Pro: What It Does and Whether You Need It