[Chapter Eighteen][Previous] [Next] [Art of Assembly][Randall Hyde]
Art of Assembly: Chapter Eighteen
- 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
-
18.2 - Active vs. Passive TSRs
Art of Assembly: Chapter Eighteen - 29 SEP 1996
[Chapter Eighteen][Previous] [Next] [Art of Assembly][Randall Hyde]
Number of Web Site Hits since Jan 1, 2000: