Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 19

Terminate and stay resident

In computers, a terminate and stay resident program (commonly referred to by


the initialism TSR) is a computer program that uses a system call in DOS operating
systems to return control of the computer to the operating system, as though the
program has quit, but stays resident in computer memory so it can be reactivated by
a hardware or software interrupt. This technique partially overcame DOS operating
systems' limitation of executing only one program, or task, at a time. TSR is unique
to DOS and not used in Windows.

Introduction
When you enter the name of a TSR program at the DOS prompt, it is treated like any
other program. DOS will allocate memory for it and load the .exe or .com file. It will
then pass control to the first instruction in the program. Since DOS is a single tasking
operating system, the TSR begins execution with complete control over the PC.
The PC has 256 interrupts (0 through 255), each with an associated vector in
memory. A vector, consisting of four bytes, contains the far address of a routine to be
performed when the associated interrupt occurs. So when you press a key, an
interrupt 9 is generated. This results in the CPU freezing whatever it is doing, picking
up the address held in the associated vector, and passing control to this routine.
When this called routine returns the processor continues from where it left off.
A TSR usually intercepts several interrupts. If a TSR wants to monitor the keyboard
or check for a special key combination, it can intercept interrupt 9 (INT 9). DOS calls
INT 9 for every press or release of a key. To take advantage of the background
scheduler, a TSR must also intercept interrupt 0x28 (INT 28).
Intercepting an interrupt consists of placing the address in a TSR program in the
interrupt vector. If the vector for INT 9 is changed to point to a function in your
program, that function will automatically be called every time someone presses a
key. Once the TSR has processed the keystroke, it will need to call the function that
had previously owned INT 9. When a TSR program is removed from memory, it
should restore the original vector for INT 9.
Once a TSR has intercepted interrupts, it must determine the minimum amount of
memory it requires. When a TSR terminates, it tells DOS how large it is, so that DOS
can reserve that memory for the TSR before executing other programs. At this stage,
the TSR is in a state of suspended animation. When an intercepted interrupt occurs,
the program will activate.

A TSR program can be loaded at any time; sometimes, they are loaded immediately
after the operating system's boot, by being explicitly loaded in the AUTOEXEC.BAT
batch

program,

or

alternatively

at

the

user's

request

(for

example, Borland's Sidekick and Turbo Debugger, Quicken's QuickPay, or FunStuff


Software's Personal Calendar). These programs will, as 'TSR' implies, stay resident
in memory while other programs are executing. Some of them do not have an option
for unloading themselves from memory, so calling TSR means the program will
remain in memory until a reboot. However unloading is possible externally, using
utilities like the MARK.EXE/RELEASE.EXE combo byTurboPower Software or soft
reboot TSRs which will catch a specific key combination and release all TSRs loaded
after them. As the chain of ISRs is singly linked, there is no provision for discovering
the previous handler's address (other than attempting to trace back the interrupt
chain), or to inform its predecessor that it needs to update its "next address to which
to jump" not to point to the TSR which desires to remove itself, so that in order to
safely unload TSRs in the middle of a chain, stubs had to be left in memory in most
cases, thereby causing memory fragmentation. This gave rise to TSR cooperation
frameworks such as TesSeRact and AMIS.

Faults
While very useful, or even essential to overcome DOS's limitations, TSR programs
had a reputation as troublemakers. Many of the programs effectively hijacked the
operating system in varying documented or undocumented ways, often causing
systems to crash on their activation or deactivation when used with particular
application programs or other TSRs. As explained above, some viruses were coded
as TSRs, and were deliberately troublesome. Additionally, all program code
in DOS systems, even those with large amounts of physical RAM, had to be loaded
into the first 640 KB of RAM (the conventional memory). TSRs were no exception,
and took chunks from that 640 KB that were thus unavailable to application
programs. This meant that writing a TSR was a challenge of achieving the smallest
possible size for it, and checking it for compatibility with a lot of software products
from different vendorsoften a very frustrating task.
In the late 1980s and early 1990s, many video games on the PC platform pushed up
against this limit and left less and less space for TSRseven essential ones like CDROM driversand arranging things so that there was enough free RAM to run the
games, while keeping the necessary TSRs present, became a black art. Many
gamers had several boot disks with different configurations for different games. In
later versions of MS-DOS, "boot menu" scripts allowed various configurations to be
selectable via a single "boot disk". In the mid- to later 1990s, while many games
were still written for DOS, the 640 KB limit was eventually overcome by putting parts
of the game's data and/or program code above the first 1 MB of memory and using
the code below 640 KB to access the extended memory (using DOS
extension methods), with code being swapped into the lowest 1 MB of RAM
as overlays. Because programming with many overlays is a challenge in and of itself,
once the program was too big to fit entirely into about 512 KB, use of extended
memory was almost always done using a third-party DOS extender
implementing VCPI or DPMI, because it becomes much easier and faster to access
memory above the 1 MB boundary, and possible to run code in that area, when the
x86 processor is switched from real mode to protected mode. However, since DOS
and most DOS programs run in real mode (VCPI or DPMI makes a protected mode
program look like a real mode program to DOS and the rest of the system by
switching back and forth between the two modes), DOS TSRs and device drivers
also run in real mode, and so any time one gets control, the DOS extender has to
switch back to real mode until it relinquishes control, incurring a time penalty (unless
they utilize techniques such as DPMS or CLOAKING).

Return
With

the

arrival

of expanded

memory boards

and

especially

of Intel

80386 processors in the second half of the 1980s, it became possible to use memory
above 640 KB to load TSRs. This required complex software solutions,
named expanded

memory

managers.

Some

memory

managers

are QRAM and QEMM by Quarterdeck, 386Max by Qualitas,CEMM by Compaq and


later EMM386 by Microsoft. The memory areas usable for loading TSRs above
640 KB are called "upper memory blocks" (UMBs) and loading programs into them is
called loading high. Later, memory managers started including programs which
would try to automatically determine how to best allocate TSRs between low and
high memory (Quarterdeck's Optimize or Microsoft's MemMaker) in order to try to
maximize the available space in the first 640 KB.

Decline
With the development of games using DOS extenders (an early example was Doom)
which bypassed the 640 KB barrier, many of the issues relating to TSRs
disappeared, and with the widespread adoption of Microsoft Windows and
especially Windows 95 (followed by Windows 98) which rendered most TSRs
unnecessary and some TSRs incompatible the TSR faded into the obsolescence,
though Win16 applications could do TSR-like tricks such as patching the interrupt
descriptor table (IDT) because Windows allowed it. TSRs have now almost
disappeared, as multitasking operating systems such as Windows Vista, Windows
7, Mac OS X, and Linux provide the facilities for multiple programs and device
drivers to run simultaneously without the need for special programming tricks, and
the modern notion of protected memory makes the kernel and its modules
exclusively responsible for modifying an interrupt table.

TSR Capabilities
When a TSR is activated via one of the intercepted interrupts, it must first
check to see if DOS and the BIOS are stable. Remember: DOS is a single
user operating system. If DOS is doing one thing and a TSR pops up and
asks it to do something different, the most likely result will be a frozen
computer- or a trashed disk drive.
This is overcome by the TSR program monitoring certain events. For
instance, it monitors any non-re-entrant code and refuses to pop up when
this is active. The TSR must monitor disk access and DOS operations to
detect if they are in use.
The algorithm looks something like this:
is TSR is active?
end
else
isDisk being used?
end
else
is a graphics application running?
end
else
if the program didn't get here via INT 28,
is DOS busy?
end

Most TSRs intercept the scheduler, INT 28, which is only called by DOS
when DOS is not busy. For example, while DOS may be busy awaiting a
keystroke at the prompt, it will call INT 28 periodically to let TSR programs
operate. A TSR can use INT 28 as an "all clear" indicator; whenever a TSR
responds to an INT 28, it can skip checking to see is DOS is busy. Chaining
INT 28 guarantees that a TSR will get a chance at executing.
Once your TSR gets past those checks, it can continue as normal. The only
catch: a TSR cannot call a DOS INT 21 service below 0Dh.
While TSR programming may seem complicated, tsr_functions allow you to
ignore many of the details above when creating TSRs.

Compatible TSRs
Following these guidelines can make a TSR more compatible with DOS
and other TSRs.
1.
Intercept
as
few
interrupts
as
possible.
2. Interrupt handlers must always service the old interrupt routine first unless they are adding features to an existing service.
3. Do not restore interrupt vectors unless the TSR knows that it is the
owner of the vector. If a TSR owns a vector, it should restore it to its original
state before being removed from memory.
Files for Building TSRs
tsr.h
This is the header file for tsr_functions. It must be included in your TSR
program. It contains #defines for the scan codes and shift values that you
can use when declaring your hotkey combination in your programs. It also
contains the prototypes for the functions in the toolkit. Most importantly it
ensures that the correct memory allocation method is used by the compiler.
Failure to use this header file could result in your program taking far more
memory than it actually needs.
resdemo.c
This file contains a sample program that you may wish to use for reference
when writing your own programs. It shows exactly how to declare the
necessary hot key and fingerprint declarations. It makes use of many
standard library functions, including the disp_functions.
tsrclock.c
This sample file program uses the background option of the tsr_ functions.
Background mode is explained in detail later.
Writing a TSR Program
Write and debug a potential TSR program as a normal program. Do not
make it memory resident until you are confident it is bug free.
While writing your program keep the following points in mind:
Do NOT use functions that allocate memory (such as malloc). You
can use the page_functions to manage memory if you wish.

Do NOT use buffered file (* FILE) routines; instead, use the


untranslated functions (open/ read/ write/ close).
Do NOT exit from any function, simply return.
Making Your Program Resident
If you examine the resdemo. c source code you will see that this program
starts in exactly the same way as any other program. The main function is
entered and the program examines the command line arguments. If none
are supplied it presumes you are attempting to load the software in memory
resident mode. So the software attempts to make itself resident by calling:
tsr_install(int argument)

If this is successful, the function will NOT return. If it does return it will pass
back an error code. Remember that you do not have to make your
programs immediately become memory resident; selecting a menu item
might tell a program to become a TSR.
The function tsr_install takes one argument; which can have one of two
values: POPONLY and TIMESLICE.
tsr_install(POPONLY);

POPONLY converts the program to a pop-up TSR; the special function


popmain is called only when the user presses the chosen hotkey
combination.
The
alternative
is:

tsr_install(TIMESLICE);

Installed with TIMESLICE, your program will be converted into a


background task and popmain will be called repeatedly - up to a maximum
of 18 times per second. With this latter method, popmain will also be
entered when the user presses the correct hotkey.
A program may need to determine whether popmain was entered through
the hotkey or because of the timeslice algorithm. You can ascertain the
answer to this by examining a global variable called _tsr_timeslice. If
popmain() was entered by the timeslice algorithm, _tsr_timeslice will be set
to 1; otherwise it will be set to 0. Examine the tsrclock.c source code to see
this in action.

If you decide to write a background task (using TIMESLICE), you should


design your program to be as efficient as possible. Try and keep the
processing done in each time slice to a minimum.
As mentioned in the earlier introduction to memory resident programming
you can make your program more compatible with other TSRs by giving
them a chance to pop up when you are at a convenient point within your
own TSR. You can do this with the tsr_service function.
void tsr_service(void)

For

instance,

instead

of

waiting

for

key

press

like

this:

bioskey(0);

Try

this

instead:

while(bioskey(1)==0) /* while no key press */


tsr_service(); /* give other TSRs a chance */
bioskey(0);

/* then get key as normal */

The function tsr_service simply fires off a scheduler interrupt (int 28h). No
value is passed to or returned from tsr_service.
Debugging TSRs
When you enter the world of TSR programming, you have to accept that
you can never do certain things and you can only do other things at certain
times. Failure to adhere to these rules can result in a frozen computer.
These problems can compound in a TSR written with C or C++ because
you may know the rules, but a calling function might not, resulting in
problems that are difficult to trace.
To help you with debugging TSRs, we have included a facility that will trap
and alert you to any possible bad practice within your TSR program. When
you use this facility and any illegal actions are detected, a window will open
with a (hopefully) meaningful message within it. This will help you to track
down the particular function call that is causing the problems.
To switch this debugging aid on, simply add the command,
TSR_DEBUG

to your existing tsr_install command. For instance, if you normally use the
form:

tsr_install(POPONLY);

Simply

extend

this

to,

tsr_install(POPONLY| TSR_DEBUG);

likewise

you

could

use,

tsr_install(TIMESLICE| TSR_DEBUG);

Note:
This facility does not attempt to cure any illegal actions; it only alerts the
user (programmer), waits for a key press and then allows the request to
continue as normal. It will not stop a faulty program from freezing the
computer, but it will explain why the computer is about to freeze!
When invoked the debugging routines can trap several of the most
common pitfalls that you may encounter. For each different problem you
will see a meaningful message displayed:
Dos function 0dh
Press a key

Meaning a function in your program called INT 21h (DOS function


dispatcher) with the AH register set to a value below hexadecimal 0dh. This
is illegal in a TSR program.
To cure this, you could place displays in your code to track down the exact
function call that caused the problem. Likely culprits are the getch family.
Attempt to close
std handle
Press a key

Every time you open a file, DOS allocates a handle to that file. Then when
you want to read or write to it, you use the handle that DOS gave you on
opening. The handles that DOS allocates start from 5 and increment with
each open request. The handles from 0000 to 0004 are reserved by DOS

for its standard devices. These are such things as keyboard/ screen/ printer
and com port.
It is quite possible for your program to close these reserved devices, either
intentionally or by accident. If you do ask DOS to close one of its standard
handles, the debugging code will presume you have done so in error and it
will inform you accordingly.
Memory Allocation
Not inside TSR!

Memory resident programs are given a chunk of DOS's 640k when they
make the transition from normal programs to TSRs. If they later make
further requests for additional memory, DOS will try to oblige and get itself
well and truly tangled. With this in mind the debugging software will watch
for any attempts to get additional memory and the above window will
appear to alert you of the request.
Note:
To dynamically manage memory within your TSR, create a static buffer and
convert it to a heap using page_initialize. You can then use functions such
as page_malloc to manage memory. See the page_functions for more
details.
One additional error mesage might be observed:
Exit detected
Use return instead

In a normal program you probably used the exit function to abort your
program. However, DOS does not really know about or understand TSR
programs. It thinks only one program is running. It presumes the underlying
application and your TSR are the same. Iif you end your TSR with a call to
exit, DOS presumes the underlying program has asked to exit and will
abort it. To avoid conflict, use return only; never use exit.
Removing a TSR from Memory
Notice in the resdemo. c source code, that if a /R is placed on the
command line, the program attempts to unload a previously loaded copy of
itself.
It
does
this
with
a
call
to:

int tsr_uninstall(void);

This function always returns a value. This function can be called, either
from within the TSR when it is active, or from a routine that is executed
when your program is called from the DOS prompt. If called from within the
pop up when active it removes the current copy of the TSR program from
memory. Therefore, once the program has popped down it cannot pop up
again. If called when the program is executed from DOS it removes any
previously loaded copy of itself.
If you intend to remove your program from memory, when it is popped up, it
is worth understanding how DOS allocates and de-allocates memory.
When a program is loaded by DOS, it is allocated one or more segments of
memory. A segment is up to 64k bytes. A clever program can trace through
the DOS allocated memory records and ascertain the owner of any
segment (or part segment) of memory. When you use tsr_functions, your
programs automatically have this ability and this is used when you try to
remove your program from memory.
A call to tsr_uninstall, looks through the memory and returns to DOS any
segments that have been allocated to your program. The call also unhooks
interrupts used by the TSR routines. However just because the segments
are returned to DOS does not mean your program is no longer in memory.
It is and it will continue to run after tsr_uninstall returns. Although DOS now
considers the memory previously allocated to your program to be free, in
reality it still contains an image of your program, which is why it will
continue to run. The freed blocks of memory will onlbe reused only when
DOS needs to allocate memory for another program.
Consider the following situation:
1. You load your TSR program, DOS allocates memory to it and returns to
the
DOS
prompt.
2. You load an ordinary program, Wordstar for instance.
3. You pop up your program from within Wordstar and it contains an option
to remove itself from memory. (just like the RESDEMO example)
4. You select the option to uninstall the pop up.
You have, in effect, created a hole in DOS memory because the memory
was allocated as follows:
DOS DRIVERS
...

Your POP UP
Wordstar

Now that DOS has regained the memory allocation blocks that were
allocated to your pop up, a hole has appeared. However, DOS is capable of
managing such situations. DOS will only use the memory in the hole if the
memory is sufficient for its needs; DOS will not overwrite the application.
Finally when Wordstar is exited, DOS will regain all the memory associated
with it and the hole will disappear. The return values from tsr_install and
tsr_uninstall are as follows:
0 Function successful
1 Can not load, program already loaded
2 Can not remove, the program is NOT loaded
3

Can not remove, another TSR program has been loaded on top of your
program

Global Variables
In your source file you must specify certain variables that the TSR routines
can reference. The value you place in these variables determines how the
TSR routines work.
HOTSHIFT
and
HOTSCAN
We have already stated that your (or any other) pop up TSR program must
have a special key sequence that it recognizes as the signal for it to pop
up. This is usually called the hotkey combination. It is called a combination
because it is the combination of one or more shift keys and an ordinary key
(usually in the range A-Z). When this key combination is pressed the pop
up will take control of the machine resources and can run as if it was the
only program in the machine. To specify your hotkey combination in your
programs is to declare and initialize two variables called HOTSHIFT and
HOTSCAN.
HOTSHIFT is an integer that must contain a value that represents the shift
keys you have chosen. To determine the value to place in this integer, first
choose your shift keys from one or more of the following available keys:

LSHIFT

Left Shift key

RSHIFT

Right Shift key

CTRL

Control key

ALT

Alt key

Then declare an int called HOTSHIFT and initialize it with your chosen hot
shift,
like
this:
int HOTSHIFT = ALT+RSHIFT;

This declares your hot shift as being the alt key + the right shift key. You
must also choose and declare the key that is to be used with the shift.
Choose a key in the range A-Z and declare an int called HOTSCAN.
Initialize
it
like
this:

int HOTSCAN = SCAN_Q;

This will declare your key to be Q, so when someone presses:

ALT+RIGHT SHIFT+Q

Your program will pop up. You must initialize HOTSCAN with a scan value
not
the
character
itself:

int HOTSCAN = 'Q'; /* WRONG! */


int HOTSCAN = SCAN_Q; /* RIGHT! */

All the scan values for the keys A-Z and F1 to F10 are defined in the tsr. h
file. If you really need to use a key outside the A-Z range, simply consult
your favorite manual to find the scan value for the key you want to use and
initialize HOTSCAN with this value.
If you prefer not to use a scan value and only want the hotkey to consist of
shift keys, declare HOTSCAN as:
HOTSCAN = NO_SCAN;

This instructs TSR routines to ignore the scan value and test only the shift
key values.
tsr_fprint
This character string is your program's unique identification, used by
tsr_install and tsr_uninstall to determine if your program is loaded in
memory. For example:
char tsr_fprint[20]= "Prog ID";

Every time you write a new TSR program give it a unique ID.
_okbigbuf
The TSR routines have to determine how much memory your program
requires, so that they can free the remaining memory and thus make it
available to any applications that may be run. In order for the routines to
arrive at an optimum figure your program should contain the following line
above your main function:
extern int _okbigbuf = 0;

Failure to do this will simply result in a TSR which takes up too much
system memory.
_tsr_timeslice
As mentioned earlier, if you need to ascertain whether your popmain was
called because the hotkey was pressed or because of the timeslice
algorithm, you can use the global _tsr_timeslice.
If the hotkey was responsible this will be set to zero, if the algorithm was
responsible it will be set to 1. Using this you can provide a background task
that can still be popped up and configured in some way by the user. For
example, examining the tsrclock.c source code will show that this is how it
displays a clock on the screen and allows you to press the hotkey to toggle
the display on or off.
The Special Function: popmain
When your hotkey combination is pressed the TSR routines will pass
control to a function in your program called popmain. When writing this
function, remember that at the point you are handed control of the
computer, it is up to you to save any areas of screen that you may destroy.
Also remember to save the cursor position and shape. The disp_function
performs this.

When you have completed processing, restore any areas of the screen that
you may have overwritten and return control from popmain back to the
calling TSR functions. They will return control to the underlying application.
Limitations
Programs that use the Digital Mars C++ TSR routines have the same
limitations as any other TSR program. For example, you can only pop up
when DOS is stable and no disk access is taking place. This is taken care
of by the TSR routines. However no TSR program can allocate memory or
make calls to DOS functions below 0Dh. An added problem is that you may
use a library function to perform some invalid task and not know it.
Library functions to avoid are:
malloc (or any other memory related command)
Any buffered file usage (fread, fgets and others).
Sample Program
The following is an example of the TIMESLICE facility. When you press the
hotkey, the display's clock will toggle. This clock runs in the background
while
you
continue
to
work
in
the
foreground.

/*
TSRCLOCK. C
Demo program for Digital Mars' Memory Resident C/C++ Toolkit
This is an example of the TIMESLICE facility of the TSR toolkit. If
you press
the hot key, you will toggle ON/OFF a clock on the screen. This clock
will run
in the background, while you continue to work as normal in the
forground.
*/
#include <disp.h>
#include <dos.h>
/* All programs must have these statements */

#include <stdio.h>
#include <tsr.h> /* must use this */
/* Your hotkey combination: */
unsigned TSR_HOTSHIFT = CTRL+ LSHIFT;
char TSR_HOTSCAN = SCAN_Q;
/* Unique string: */
char tsr_fprint[20] = "tsrclock.v1";
/* In addition, background programs must
have this: */
extern unsigned _tsr_timeslice;
/************************/
/*--Enter your program--*/
union REGS regs;
int cur_pg, cur_s, cur_p;
int toggle = 1;
int hours, mins, secs;
main(argc, argv)
int argc;
char **argv;
{
int i;
if ((strncmp(argv[1],"/R", 2) == 0) ||
(strncmp(argv[1],"/r", 2)== 0))
{
i = tsr_uninstall();
if(i == 0)
printf("Program removed\n");
if(i == 2)
printf("Can not remove, Program not
loaded!\n");
if(i == 3)
printf("Can not remove, Another program
loaded above\n");

exit(0);
}
printf("Press Control+Left Shift+Q to toggle clock ON/OFF\n");
i= tsr_install(TIMESLICE+TSR_DEBUG);
/* if it returns, error has occured */
if (i == 1)
printf("Can not load, program already
loaded!\n");
else
printf("Failed to install, error %i\n", i);
}
void popmain(popmain)
{
/* POPMAIN is a special "reserved name",
function to which the TSR routines will pass
control when the hot key is pressed.
*/
if(_tsr_timeslice== 0)/* if hotkey*/
{
toggle = toggle * -1;/* set toggle on/off*/
return;
}
if(toggle < 0)/* only display when on*/
return;
regs.h.ah = 0x2c;
intdos(s, s);
if( secs== regs.h.dh)/* and if secs changed*/
return;
hours = regs.h.ch;
mins = regs.h.cl;

secs = regs.h.dh;/* save_cursor will destroy*/


save_cursor();
disp_open();
disp_move(0,66);
disp_setattr(14);
disp_printf("TIME:%2.2i:%2.2i:%2.2i",
hours, mins, secs);
disp_close();
restore_cursor();
}
save_cursor()
{
regs.x.ax = 15 * 256;
int86(0x10,s,s);
cur_pg = regs.x.bx;
regs.x.ax = 3 * 256;
int86(0x10,s,s);
cur_p = regs.x.dx;
cur_s = regs.x.cx;
regs.x.dx =(24 * 256) + 80;
regs.x.ax = 2 * 256;
regs.x.bx = cur_pg;
int86(0x10,s,s);
}
restore_cursor()
{
regs.x.ax = 256;
regs.x.bx = cur_pg;
regs.x.cx = cur_s;
int86(0x10,s,s);
regs.x.dx = cur_p;
regs.x.ax = 2 * 256;
int86(0x10,s,s);
}

Problems

The tsr_functions allow someone who has never heard of a "DOS BUSY
FLAG" or even seen an assembler program to write pop ups easily and
quickly. However, when you enter the world of the TSR you must expect
problems. We have taken every care to ensure that self induced problems
are kept to a minimum by trapping almost every action that you could
inadvertently perform to crash your own TSR.
If you experience problems with one of your programs, (most common will
be a complete lock up), follow these steps to track the problem.
1. Ensure that the debugging window does not open at any stage inside
your TSR.
2. Place displays in your program in order to identify the instruction or
section of code that is causing problems.
3. Check your own code thoroughly!
The tsr_functions have been tested extensively; no problems have been
found. Make sure your code is sound before assuming the problem lies in
the tsr_functions.

You might also like