On this disk (or .ARC file) you should have the following files:
TCTSR DOC This documentation file you are reading now
TSRCALC C Sample program. Resident four function calculator
WINDOW H Header file for Window Management routines
WINDOW INC Window Management routines
TSRCALC EXE Compiled TSRCALC.C
PURPOSE OF THIS PROGRAM
The program TSRCALC is my first attempt at writing memory resident
programs in C, specifically for the Turbo C compiler. The goal is
to provide an environment for easily adapting any C program to act
as a memory resident program (aka Sidekick). I've by no means got
all the bugs out, and still have a lot to learn. My goal is to be
able to write resident C programs that can access dBASE files from
within other programs. I maintain Wholesale distribution systems
written in CLIPPER in a number of locations through the country,
and desire a way to easily allow an operator to perform searches
without leaving data entry programs. I've already written the dBASE
to C interface, if you're interested, let me know.
The program TSRCALC will emulate a 4 function pocket calculator with
memory. An 8087 is not required. If you desire to make your own
program memory resident, (you already have enough calculator programs)
the following discussion will help you modify your program.
First, the rules:
1). Your program must be able to be compiled in the LARGE MODEL.
this requirement could be changed easily, but in my laziness,
it's much easier to manipulate memory in the Large model.
2). You cannot make use of the 8087. This can also be changed, if
you write a routine to save and restore the context of the 8087
at entry and exit.
3). Since I don't have an EGA or VGA or Hercules card, I don't know
if my window routines will run on them. It's been tested on a
monchrome and a CGA card.
4). File I/O. Frankly I don't know what will happen, I haven't tried
it yet. One pitfall I'm aware of is that when a TSR gets control,
the Program Segment Prefix still belongs to the interrupted program.
You will have to save the callers' PSP, and set up your own. In
TSRCALC.C, I have already included the functions get_psp() and
put_psp(). I've also saved my PSP in the variable my_psp.
Through much experimentation I've uncovered a few details that allow
at least console and keyboard IO with no complications:
In DOS there is a byte known as the Critical Flag. Its address is found
through function 0x34 of INT 21. If this byte is zero, then it's
absolutely safe to perform Character I/O. I've installed my intercept
routine to trap the timer interrupt (INT 8), which occurs 18.2 times per
second on an IBM PC. My intercept routine first chains to the BIOS
interrupt routine, which updates the clock and disk unload timer. The
last thing BIOS does is to re-enable the CTC chip. I spent hours trying
to figure out why my program would hang on BIOS calls until I moved
the chain to BIOS from the end of my interrupt routine to the beginning.
The routine 'tickhandler' performs two functions, depending on what has
happened in previous intercepts. First, it checks the state of the
keyboard shift status byte at 0x417L to determine if Alt-Lshift-Rshift
is being held down. If it is, then I check the critical flag to see
if it's safe to call DOS. If it is safe (zero), then I call the function
'service()' which is the application routine. If it's not safe, then
the variable 'wait' is set to non-zero, and the program exits.
The next time tickhandler recieves control, it checks the critical flag,
and if it is zero, then 'service()' is called. If the critical flag is
still non-zero, tickhandler exits again (and again...).
It's important to note that if 'hold_service' is set to TRUE (by
'service()'), then tickhandler won't even bother to check the
critical flag, but will simply exit after chaining the interrupt
to BIOS. This is so because we don't want to call 'service()' if
it's already running. (it makes an awfull mess).
Included in TSRCALC, but disabled is an interrupt handler for INT 28.
I've read somewhere that DOS uses INT 28 to tell itself whether IO
is already in progress elsewhere in the system. DOS first pushes a
byte (actually a word) onto the stack before issuing an INT 28. This
word will be either a 0 or a 1. If it's a 1 then DOS knows it's safe
to perform I/O. My problem is twofold:
1). In my tests, the value pushed on the stack is not limited to
zero or one, but something like 0 to 255.
2). If there's a way to get the value from the stack from within
a Turbo C interrupt function, I've yet to find out. The closest
I've come is to write the program to access BP, and adjust the
resulting code later with debug.com to BP+18. (the 18 comes from
the position of the pushed argument AFTER TurboC saves the registers
on entry to the function.
If you try to HOT KEY the program from the command line, it won't pop up
immediately, until you cause DOS to update the critical flag by either
running a program, or by hitting control-C. Why this is is one of the
great mysteries of TSR's (for me anyway).
Please feel free to modify or change this program to your hearts content.
If you find out anything interesting, Please let me know.
All The Best.....
Mark E Johnson
end of file