[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: