mirror of
https://github.com/opsxcq/mirror-textfiles.com.git
synced 2025-08-12 20:24:31 +02:00
315 lines
14 KiB
Plaintext
315 lines
14 KiB
Plaintext
|
||
|
||
Page 1
|
||
|
||
Class TSR: an abstract base class for DOS resident programs.
|
||
------------------------------------------------------------
|
||
|
||
Author: John English (je@unix.brighton.ac.uk)
|
||
Department of Computing
|
||
University of Brighton
|
||
Brighton BN2 4GJ, England.
|
||
|
||
Copyright (c) J.English 1993.
|
||
|
||
Permission is granted to use copy and distribute the
|
||
information contained in this file provided that this
|
||
copyright notice is retained intact and that any software
|
||
or other document incorporating this file or parts thereof
|
||
makes the source code for the TSR class of which this file
|
||
is a part freely available.
|
||
|
||
|
||
1. Introduction.
|
||
----------------
|
||
This class provides a framework for writing memory-resident DOS
|
||
programs (TSRs). TSRs produced using this class can be woken up
|
||
by a specific key (the "hotkey") or after a specified number of
|
||
timer ticks (the "timeslice") or a combination of both. Writing
|
||
TSRs unaided is a non-trivial task, but this class provides the
|
||
essential TSR functionality which allows you to concentrate on
|
||
your application-specific requirements. TSRs written using this
|
||
class will require a PC/AT compatible machine running DOS version
|
||
3 or higher. Compile them with Borland C++ version 3.0 or higher.
|
||
|
||
To create a TSR using this class, you must derive a class for your
|
||
application from it. You must supply a function "main" in your
|
||
application class, which will be called whenever the TSR is woken
|
||
up. To create a TSR, declare an instance of your derived class.
|
||
|
||
A program built using an instance of your class can make itself
|
||
resident using the member function "run", it can test if a copy
|
||
is already loaded using the member function "loaded", and it can
|
||
unload a previously loaded copy using the member function "unload".
|
||
A foreground copy can also communicate with a resident copy using
|
||
the functions "request" and "respond". Each TSR must be given a
|
||
unique name up to 32 characters long which can be used to identify
|
||
it once it is made resident.
|
||
|
||
The constructor and "run" will set an internal status code if any
|
||
errors are detected. If "run" returns, this will show the cause
|
||
of the error. The status can be tested during program execution
|
||
using the member function "status". This can be used to display
|
||
meaningful error messages or to return as the program exit status.
|
||
The member function "name" returns the name of the TSR, which can
|
||
also be used in error messages.
|
||
|
||
If you find this class useful or have any suggestions as to how it
|
||
can be enhanced, please contact the author at one of the addresses
|
||
given above. E-mail and postcards will both be welcome!
|
||
|
||
|
||
2. Deriving a new TSR class "MyTSR" from class TSR.
|
||
---------------------------------------------------
|
||
The constructor for your derived class "MyTSR" must invoke the
|
||
constructor for class TSR. The constructor for class TSR takes
|
||
|
||
|
||
Page 2
|
||
|
||
two parameters:
|
||
|
||
* a unique string which will be used to identify your TSR, and
|
||
|
||
* the size in bytes of the stack to be used when the resident
|
||
part of your TSR is active. If no stack size is specified,
|
||
the default stack size is taken to be 1024 bytes.
|
||
|
||
Class MyTSR must also provide a definition of a member function
|
||
called "main" which will contain the application-specific code
|
||
for your TSR. This will be executed whenever your TSR is woken
|
||
up. It must be declared as follows:
|
||
|
||
void main (int hotkey);
|
||
|
||
Class MyTSR may also provide a destructor (~MyTSR), but this will
|
||
only be called when the TSR is not made resident.
|
||
|
||
Having created a derived class, you should then declare a single
|
||
instance of this class in your program, as for example:
|
||
|
||
MyTSR my_tsr;
|
||
|
||
You must not declare more than one instance of a class derived
|
||
from TSR in your program.
|
||
|
||
Your program can obtain status information by calling the following
|
||
member functions:
|
||
|
||
my_tsr.name ();
|
||
This returns the name of the TSR instance (as specified
|
||
in the constructor call to class TSR).
|
||
|
||
my_tsr.loaded ();
|
||
This returns an integer result which will be non-zero
|
||
if a copy of the TSR is already loaded.
|
||
|
||
my_tsr.status ();
|
||
This returns an integer result which will be non-zero
|
||
if any errors have been detected by the constructor or
|
||
by "run". The error codes are as follows:
|
||
|
||
1: incompatible DOS version (version 3 or higher required)
|
||
2: attempt to declare more than one instance of a TSR
|
||
3: unable to create stack of specified size
|
||
4: cannot allocate self-identify multiplex function
|
||
5: TSR already loaded
|
||
6: failed to make TSR resident (unlikely to occur)
|
||
7: user "startup" function reported failure
|
||
|
||
my_tsr.unload ();
|
||
This attempts to unload a previously-loaded copy of the
|
||
TSR. It returns an integer result which will be non-zero
|
||
if it failed, as follows:
|
||
|
||
1: TSR not loaded, so it cannot be unloaded.
|
||
2: Something else has hooked the same interrupts, so it
|
||
cannot be unloaded.
|
||
|
||
|
||
Page 3
|
||
|
||
3: Unable to free memory, so it cannot be unloaded (but it
|
||
will now be disabled and will no longer respond to the
|
||
hotkey or to timer ticks). This is unlikely to occur.
|
||
4: unable to free TSR environment space (although the TSR
|
||
itself will have been successfully unloaded). This is
|
||
unlikely to occur.
|
||
|
||
|
||
3. Making your TSR resident.
|
||
----------------------------
|
||
To make your TSR resident in memory, call the member function
|
||
"run". "Run" requires two parameters:
|
||
|
||
* A value representing the hotkey to be used to activate the
|
||
TSR. This is described further below.
|
||
|
||
* An optional timeslice size. This is an integer giving the
|
||
number of timer ticks between TSR activations (a timer tick
|
||
is approximately 55 milliseconds). If this parameter is
|
||
omitted or zero, the TSR will only be activated when the
|
||
hotkey is pressed.
|
||
|
||
The hotkey should be a combination of values from the following
|
||
list:
|
||
|
||
Modifiers: TSR::ALT, TSR::CTRL, TSR::LSHIFT, TSR::RSHIFT
|
||
Keycodes: TSR::KEY_A to TSR::KEY_Z, TSR::KEY_0 to TSR::KEY_9,
|
||
TSR::ENTER, TSR::SPACE, TSR::F1 to TSR::F10
|
||
|
||
The hotkey value must not use more than one of the values from
|
||
the "keycodes" list above. If you do not wish a hotkey to be
|
||
used, specify a value of TSR::NONE for the hotkey parameter.
|
||
Some examples of valid hotkey specifications are shown below:
|
||
|
||
my_tsr.run (TSR::ALT + TSR::F1);
|
||
// "my_tsr" should be woken up whenever Alt-F1 is pressed.
|
||
|
||
my_tsr.run (TSR::LSHIFT + TSR::RSHIFT);
|
||
// "my_tsr" should be woken up whenever the left and right
|
||
// shift keys are pressed at the same time.
|
||
|
||
If you specify a hotkey of TSR::NONE and no timeslice is specified
|
||
either, "main" will never be woken up. You can use this to load
|
||
interrupt handlers (using "startup" and "shutdown" as described
|
||
below) which need to remain resident but do not need to interact
|
||
with the user in any way. "Main" should be an empty function if
|
||
this is the case, since it will never be called.
|
||
|
||
If the TSR is installed successfully, "run" will not return.
|
||
If "run" returns, it indicates that an error has occurred. The
|
||
member function "status" (see above) can be used to determine
|
||
the cause of the error.
|
||
|
||
|
||
4. Writing the member function "main".
|
||
--------------------------------------
|
||
"MyTSR::main" (the main function of your derived class) will be
|
||
called whenever the TSR is woken up, either as the result of the
|
||
hotkey being pressed or the specified timeslice expiring. The
|
||
|
||
|
||
Page 4
|
||
|
||
parameter "hotkey" will be non-zero if the TSR was woken up by
|
||
the hotkey being pressed and zero if it was woken up because the
|
||
timeslice expired. "Main" cannot perform operations which call
|
||
DOS functions 00 - 0C (character I/O), 48 (allocate memory), 4C
|
||
(terminate process) or 3E (close file, standard files only), but
|
||
otherwise it is a normal C++ function.
|
||
|
||
The following member functions can be used within "main":
|
||
|
||
void pause ();
|
||
This should be called whenever your "main" function is
|
||
performing any lengthy processing. It allows other TSRs
|
||
to execute while your TSR is active.
|
||
|
||
void sync ();
|
||
Timed activations normally occur every N timer ticks after
|
||
"run" is called. This function resets the timer so that
|
||
the next timed activation will happen N timer ticks from
|
||
now, rather than when the current timer count expires.
|
||
This can be useful to resynchronise timed activations if
|
||
a hotkey is used to enable/disable TSR activity.
|
||
|
||
int userbreak ();
|
||
This returns a non-zero result if "control-break" has been
|
||
pressed since it was last called. This can be polled from
|
||
"main" if control-break detection is required.
|
||
|
||
Your class may also overload the following functions to perform
|
||
error recovery for the resident part of your program:
|
||
|
||
critical_code critical_error (int n);
|
||
Called when a critical error (Abort, Retry, Fail?) occurs
|
||
during execution of "main". You must not call any DOS
|
||
services other than functions 00 - 0C within this function.
|
||
The result must be one of the values TSR::IGNORE, TSR::RETRY
|
||
or TSR::FAIL. The default action for this function is to
|
||
return TSR::FAIL.
|
||
|
||
void dos_error (int fn, int ce, int cs, int ip);
|
||
Called when an illegal DOS function is called from within
|
||
"main" or "critical_error" (see above). The parameter "fn"
|
||
is the function code from register AH; "ce" is non-zero if
|
||
the error occurred while a critical error was being handled;
|
||
"cs" and "ip" are the segment and offset of the return address
|
||
from the offending interrupt. If this function is called it
|
||
indicates a bug in your "main" or "critical_error" functions.
|
||
You must not use any DOS services in this function (although
|
||
BIOS services can still be used). The default action is to
|
||
reset the screen to text mode if it is in graphics mode and
|
||
then display an error message.
|
||
|
||
These functions should not be called directly; they will be called
|
||
automatically if an error occurs during execution of "main".
|
||
|
||
|
||
5. Initialisation and finalisation.
|
||
-----------------------------------
|
||
Since a TSR which is made resident does not exit in the normal way,
|
||
the destructor for your TSR will only be called if it is not made
|
||
|
||
|
||
Page 5
|
||
|
||
resident. However, you may need to perform some initialisation when
|
||
the TSR is made resident (e.g. hooking interrupts) and finalisation
|
||
when it is unloaded (e.g. restore the original interrupt vectors).
|
||
There are two virtual functions which can be overloaded to perform
|
||
this sort of initialisation and finalisation:
|
||
|
||
void startup ();
|
||
Called by "run" when the TSR is being installed in memory. This
|
||
can be used to provide application-specific initialisation (e.g.
|
||
hooking interrupts). The default is to do nothing.
|
||
|
||
void shutdown ();
|
||
Called by "unload" when the TSR is being unloaded from memory.
|
||
This can be used to provide application-specific finalisation
|
||
(e.g. restoring hooked interrupt vectors). The default is to
|
||
do nothing.
|
||
|
||
These functions should not be called directly; they will be called
|
||
automatically during TSR loading and unloading.
|
||
|
||
|
||
6. Communicating with a resident TSR.
|
||
-------------------------------------
|
||
Sometimes it may be necessary to communicate with a resident copy of
|
||
a TSR from a foreground program to adjust its parameters in some way.
|
||
The functions "request" and "respond" provide a method to perform
|
||
such communication. The program should provide an appropriate
|
||
implementation for the virtual function "respond", which has the
|
||
following specification:
|
||
|
||
int respond (int fn, int far& p1, int far& p2);
|
||
|
||
The parameter "fn" will be a function code in the range 0 to 127, and
|
||
the parameters "p1" and "p2" can be used for an application-specific
|
||
parameter list (which could be the segment and offset of a far pointer
|
||
of a lengthier parameter list).
|
||
|
||
A copy of the program loaded in the foreground can communicate with
|
||
a previously-loaded resident copy by calling the function "request".
|
||
"Request" requires three reference-to-integer parameters which will
|
||
be used to call "respond" in the resident copy; the first one should
|
||
contain the function code to be passed to "respond" and the remaining
|
||
two will be passed to "respond" as the parameters "p1" and "p2". The
|
||
result from "respond" will be stored in the first parameter, and the
|
||
final values of "p1" and "p2" produced by "respond" will be stored in
|
||
the last two. "Request" returns zero if the call is successful, and
|
||
a non-zero result (TSR::NOT_LOADED) if there is no resident copy to
|
||
communicate with.
|
||
|
||
|
||
7. A plea for feedback.
|
||
-----------------------
|
||
If you use this class, please contact the author via the addresses
|
||
at the beginning; if you don't have e-mail access please send me a
|
||
postcard (I like postcards!) just to let me know you've looked at
|
||
it. Feel free to suggest enhancements, find bugs or (better still)
|
||
fix them and send me patches. Happy hacking!
|