[Home]
[Search]
[Contents]
TSR Information
This chapter describes:
- Capabilities and limitations of a Terminate and Stay Resident (TSR) program.
- How to use tsr_functions to write and debug a TSR program.
- TSR function reference
The tsr_functions allow you to write a program in Digital Mars C++
that can optionally reside in memory. Such a program is often called
a Terminate-and-Stay Resident utility or simply TSR. In addition, your
program can be given a slice of the processor's time, allowing a
carefully written program to run as a background process.
Note
The tsr_functions are intended for use with real-mode
DOS. They are not supported when using the
16 or 32-bit DOS extender.
A running TSR program becomes a DOS extension and continuously
monitors the keyboard for any key press that matches the key
combination your program declares. This hotkey combination signals
your program to spring to life, or pop up. A TSR can also be a
background process, automatically invoked by the processor
approximately 18 times every second.
DOS is a single tasking, single user operating system. However, DOS
has hooks that TSRs can use. TSRs take over certain operating system
functions and provide additional facilities without the operating
system knowing. Some DOS utilities, including PRINT, DOSKEY, and
FASTOPEN are TSRs that add capabilities to DOS when needed. New
releases of DOS document new features.
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.
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
is Disk 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 a 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(& regs,& regs);
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,& regs,& regs);
cur_pg = regs. x. bx;
regs. x. ax= 3* 256;
int86(0x10,& regs,& regs);
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,& regs,& regs);
}
restore_cursor()
{
regs. x. ax= 256;
regs. x. bx= cur_pg;
regs. x. cx= cur_s;
int86(0x10,& regs,& regs);
regs. x. dx= cur_p;
regs. x. ax= 2* 256;
int86(0x10,& regs,& regs);
}
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.
tsr_install
Header
tsr.h
Prototype
tsr_install (int argument);
Description
The tsr_install function makes a program memory-resident.
The argument must be either POPONLY or TIMESLICE.
POPONLY makes the program into a pop-up Terminate-and-Stay-Resident
(TSR) utility; the special function popmain will only be
called when the user presses the chosen hotkey combinations.
TIMESLICE makes the program into a background task and
popmain is called repeatedly -to a maximum ot 18 times per
second. In addition, popmain is entered when the user presses the
correct hotkey.
Return Value
None, if successful. If an error occurs, an error code is returned.
Compatibility
DOS Small memory models only.
tsr_service
Header
tsr.h
Prototype
void tsr_service(void);
Description
The tsr_service function executes a scheduler interrupt (int
28h) that allows other Terminate-and-Stay-Resident (TSR) programs
to pop up.
Return Value
None
Compatibility
DOS Small memory model only
tsr_uninstall
Header
tsr.h
Prototype
int tsr_uninstall(void);
Description
The tsr_uninstall function removes a Terminate-and-Stay-Resident
(TSR) utility from memory. You can call this function from
within a TSR when it is active or from a routine that is executed
when your program is called from the DOS prompt. If
tsr_uninstall is called from within the pop up when it is active,
it removes the current copy of the TSR program from memory. Once
the program has popped down, it will not be able to pop up again.
If tsr_uninstall is called when the program is executed from
DOS, it removes any previously loaded copy of itself.
Return Value
:Returns the following values:
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
Compatibility
DOS Small memory model only.