Chapter Eighteen (Part 1)
|Table of Content||
Chapter Eighteen (Part 3)
RESIDENT PROGRAMS (Part 2)
|18.2 - Active vs. Passive TSRs|
|18.2 Active vs. Passive TSRs|
Microsoft identifies two types of TSR routines: active and passive. A passive TSR is one that activates in response to an explicit call from an executing application program. An active TSR is one that responds to a hardware interrupt or one that a hardware interrupt calls.
TSRs are almost always interrupt service routines. Active TSRs are typically hardware interrupt service routines and passive TSRs are generally trap handlers. Although in theory it is possible for a TSR to determine the address of a routine in a passive TSR and call that routine directly the 80x86 trap mechanism is the perfect device for calling such routines so most TSRs use it.
Passive TSRs generally provide a callable library of routines or extend some DOS or BIOS call. For example you might want to reroute all characters an application sends to the printer to a file. By patching into the int 17h vector you can intercept all characters destined for the printer. Or you could add additional functionality to a BIOS routine by chaining into its interrupt vector. For example you could add new function calls to the int 10h BIOS video services routine by looking for a special value in ah and passing all other int 10h calls on through to the original handler. Another use of a passive TSR is to provide a brand new set of services through a new interrupt vector that the BIOS does not already provide. The mouse services provided by the mouse.com driver is a good example of such a TSR.
Active TSRs generally serve one of two functions. They either service a hardware interrupt directly or they piggyback off the hardware interrupt so they can activate themselves on a periodic basis without an explicit call from an application. Pop-up programs are a good example of active TSRs. A pop-up program chains itself into the PC's keyboard interrupt (int 9). Pressing a key activates such a program. The program can read the PC's keyboard port to see if the user is pressing a special key sequence. Should this keysequence appear the application can save a portion of the screen memory and "pop-up" on the screen perform some user-requested function and then restore the screen when done. Borland's Sidekick' program is an example of an extremely popular TSR program though many others exist.
Not all active TSRs are pop-ups though. Certain viruses are good examples of active TSRs. They patch into various interrupt vectors that activate them automatically so they can go about their dastardly deeds. Fortunately some anti-viral programs are also good examples of active TSRs they patch into those same interrupt vectors and detect the activities of a virus and attempt to limit the damage the virus may cause.
Note that a TSR may contain both active and passive components. That is there may be certain routines that a hardware interrupt invokes and others that an application calls explicitly. However if any routine in a resident program is active we'll claim that the entire TSR is active.
The following program is a short example of a TSR that provides both active and passive routines. This program patches into the int 9 (keyboard interrupt) and int 16h (keyboard trap) interrupt vectors. Every time the system generates a keyboard interrupt the active routine (int 9) increments a counter. Since the keyboard usually generates two keyboard interrupts per keystroke dividing this value by two produces the approximate number of keys typed since starting the TSR. A passive routine tied into the int 16h vector returns the number of keystrokes to the calling program. The following code provides two programs the TSR and a short application to display the number of keystrokes since the TSR started running.
; This is an example of an active TSR that counts keyboard interrupts ; once activated.
; The resident segment definitions must come before everything else.
ResidentSeg segment para public 'Resident' ResidentSeg ends
EndResident segment para public 'EndRes' EndResident ends
.xlist include stdlib.a includelib stdlib.lib .list
; Resident segment that holds the TSR code:
ResidentSeg segment para public 'Resident' assume cs:ResidentSeg ds:nothing
; The following variable counts the number of keyboard interrupts KeyIntCnt word 0 ; These two variables contain the original INT 9 and INT 16h ; interrupt vector values: OldInt9 dword ? OldInt16 dword ? ; MyInt9- The system calls this routine every time a keyboard ; interrupt occus. This routine increments the ; KeyIntCnt variable and then passes control on to the ; original Int9 handler. MyInt9 proc far inc ResidentSeg:KeyIntCnt jmp ResidentSeg:OldInt9 MyInt9 endp ; MyInt16- This is the passive component of this TSR. An ; application explicitly calls this routine with an ; INT 16h instruction. If AH contains 0FFh this ; routine returns the number of keyboard interrupts ; in the AX register. If AH contains any other value ; this routine passes control to the original INT 16h ; (keyboard trap) handler. MyInt16 proc far cmp ah 0FFh je ReturnCnt jmp ResidentSeg:OldInt16 ;Call original handler. ; If AH=0FFh return the keyboard interrupt count ReturnCnt: mov ax ResidentSeg:KeyIntCnt iret MyInt16 endp ResidentSeg ends cseg segment para public 'code' assume cs:cseg ds:ResidentSeg Main proc meminit mov ax ResidentSeg mov ds ax mov ax 0 mov es ax print byte "Keyboard interrupt counter TSR program" cr lf byte "Installing...." cr lf 0 ; Patch into the INT 9 and INT 16 interrupt vectors. Note that the ; statements above have made ResidentSeg the current data segment ; so we can store the old INT 9 and INT 16 values directly into ; the OldInt9 and OldInt16 variables. cli ;Turn off interrupts! mov ax es:[9*4] mov word ptr OldInt9 ax mov ax es:[9*4 + 2] mov word ptr OldInt9+2 ax mov es:[9*4] offset MyInt9 mov es:[9*4+2] seg ResidentSeg mov ax es:[16h*4] mov word ptr OldInt16 ax mov ax es:[16h*4 + 2] mov word ptr OldInt16+2 ax mov es:[16h*4] offset MyInt16 mov es:[16h*4+2] seg ResidentSeg sti ;Okay ints back on. ; We're hooked up the only thing that remains is to terminate and ; stay resident. print byte "Installed." cr lf 0 mov ah 62h ;Get this program's PSP int 21h ; value. mov dx EndResident ;Compute size of program. sub dx bx mov ax 3100h ;DOS TSR command. int 21h Main endp cseg ends sseg segment para stack 'stack' stk db 1024 dup ("stack ") sseg ends zzzzzzseg segment para public 'zzzzzz' LastBytes db 16 dup (?) zzzzzzseg ends end Main
Here's the application that calls MyInt16 to print the number of keystrokes:
; This is the companion program to the keycnt TSR. ; This program calls the "MyInt16" routine in the TSR to ; determine the number of keyboard interrupts. It displays ; the approximate number of keystrokes (keyboard ints/2) ; and quits. .xlist include stdlib.a includelib stdlib.lib .list cseg segment para public 'code' assume cs:cseg ds:nothing Main proc meminit print byte "Approximate number of keys pressed: " 0 mov ah 0FFh int 16h shr ax 1 ;Must divide by two. putu putcr ExitPgm Main endp cseg ends sseg segment para stack 'stack' stk db 1024 dup ("stack ") sseg ends zzzzzzseg segment para public 'zzzzzz' LastBytes db 16 dup (?) zzzzzzseg ends end Main
Chapter Eighteen: Resident Programs
29 SEP 1996