KnowledgeBase Archive

An Archive of Early Microsoft KnowledgeBase Articles

View on GitHub

Q39524: TSR in Assembly

Article: Q39524
Product(s): See article
Version(s): 5.00 5.10
Operating System(s): MS-DOS
Keyword(s): ENDUSER | | mspl13_masm
Last Modified: 12-JAN-1989

Terminate and stay resident (TSR) programs consist of RAM-resident and
transient portions. When a TSR is executed, the transient portion
initializes data and installs the interrupt handlers; the transient
portion only takes place once. On exit, the transient code executes a
MS-DOS TSR function to leave the RAM-resident portion in memory. The
RAM-resident portion of the code is now ready and waiting to be
invoked.

Typically a TSR is invoked by another program by a sequence of
keystrokes, or by a system interrupt. The following is an example of a
TSR that displays the system time in the upper-right corner of the
screen. During the transient portion, the TSR replaces the
system-timer interrupt code with its own code and saves the old
address so the original system-timer interrupt can be invoked by the
new code. The system-timer interrupt occurs approximately 19 times a
second, and is required for the system to execute correctly. The
RAM-resident portion of the TSR first invokes the original
system-timer interrupt, then calls Interrupt 1AH function 02H to
obtain the current time. The time is then displayed in the upper-right
corner of the screen.

The following sample program demonstrates this behavior:

_text   segment para public 'CODE'
        org     100h
        .286                    ; Used for PUSHA and POPA intstuctions
                                ; If 8086 you will need to explictly
                                ; push all the registers on the stack
        assume  cs:_text,ds:_text
start:
        jmp     initialize
old_timer       dd  ?           ; space for old timer vector
timer_int       proc    far
        sti                     ; enable interupts
        assume  ds:nothing
        pushf                   ; put flags on stack
        call    old_timer
        push    ax              ; save registers
        push    cx
        push    dx
        mov     ah,02h
        int     1ah             ; get time  Returns:
                                ; ch = hours in BCD
                                ; cl = minutes in BCD
                                ; dh = seconds in BCD
                                ; dl  ignored
        call    print_drv       ; call time to read time
        pop     dx
        pop     cx
        pop     ax              ; restore registers
        iret
timer_int       endp

print_drv       proc    near
        assume  cs:_text,ds:_text
        pusha                   ; Save registers
        push    es              ; Save Segment registers
        push    ds
        mov     bx,cs
        mov     ds,bx           ; data seg = code seg
        call    getpos
        push    bx              ; save cursor on stack
        mov     bx,41h
        call    setpos          ; move cursor to right hand corner
        call    display_time
        pop     bx
        call    setpos          ; restore cursor
        pop     ds              ; restore Segment registers
        pop     es              ; restore registers
        popa
        ret
print_drv       endp

display_time proc near
        assume cs:_text,ds:_text
        push   ax
        push   bx
        push   cx
        push   si
        mov    ax,cs           ; Data Seg = Code Seg
        mov    ds,ax
;   Output hours
        mov        bx,cx       ; move hours to bx
        mov        al,bh
        mov        cl,4        ; Set shift count to 4
        shr        ax,cl       ; rotate to low nybble
        and        ax,000FH    ; Mask out all but low nybble
        mov        si,ax       ; Use low nybble as index into
        mov        al,byte ptr asciitab  [si] ; asciitable
        call       dchar       ; print 1st digit of hours
        mov        al,bh
        and        ax,000FH    ; Mask out all but low nybble
        mov        si,ax       ; Use low nybble as index into
        mov        al,byte ptr asciitab  [si] ; asciitable
        call       dchar
        call       dcolon

;   Output Minutes
        mov        al,bl        ; move hours to bx
        mov        cl,4         ; Set shift count to 8
        shr        ax,cl        ; rotate to low nybble
        and        ax,000FH     ; Mask out all but low nybble
        mov        si,ax        ; Use low nybble as index into
        mov        al,byte ptr asciitab  [si] ; asciitable
        call       dchar        ; print 1st digit of hours
        mov        al,bl
        and        ax,000FH     ; Mask out all but low nybble
        mov        si,ax        ; Use low nybble as index into
        mov        al,byte ptr asciitab  [si] ; asciitable
        call       dchar        ; print 1st digit of hours
        call       dcolon

;   Output Seconds
        mov        al,dh        ; move hours to bx
        mov        cl,4         ; Set shift count to 4
        shr        ax,cl        ; rotate to low nybble
        and        ax,000FH     ; Mask out all but low nybble
        mov        si,ax        ; Use low nybble as index into
        mov        al,byte ptr asciitab  [si] ; asciitable
        call       dchar        ; print 1st digit of hours
        mov        al,dh
        and        ax,000FH     ; Mask out all but low nybble
        mov        si,ax        ; Use low nybble as index into
        mov        al,byte ptr asciitab  [si] ; asciitable
        call       dchar

        pop     si
        pop     cx
        pop     bx
        pop     ax
        ret
display_time endp

; Get the current cursor position and return it in BX
getpos  proc    near
        push    ax
        push    cx
        push    dx

        mov     ah,03h
        mov     bh,0    ; page zero
        int     10h
        mov     bx,dx   ; Return the positon in BX

        pop     dx
        pop     cx
        pop     ax
        ret
getpos  endp

; Set the current cursor postion to the vaule in BX
setpos  proc    near
        push    ax
        push    bx
        push    dx

        mov     ah,02h  ; use int 10h function 2 to set cursor position
        mov     dx,bx
        mov     bh,0
        int     10h

        pop     dx
        pop     bx
        pop     ax
        ret
setpos endp

dcolon  proc near
        mov     al,':'
        call    dchar
        ret
dcolon  endp

; Display the character contained in Al
dchar   proc near
        push    ax
        push    bx
        mov     bh,1
        mov     ah,0eh
        int     10h
        pop     bx
        pop     ax
        ret
dchar   endp

asciitab:     db      '0123456789',0
; The following section  of code executes during initial program
; execution.
initialize:
                assume ds:_text
                mov bx,cs               ; data_seg = code_seg
                mov ds,bx

                mov     al,08h          ; get vector of interrupt timer
                mov     ah,35h          ;
                int     21h
                mov     word ptr old_timer, bx ; save vector
                mov     word ptr old_timer[2],es

                mov     dx,offset timer_int
                mov     al,08h
                mov     ah,25h          ; replace system interrupt
                int     21h             ; with our timer_int

                mov     dx,offset initialize  ; terminate and stay resident
                int     27h                   ; int 21h would work
_text   ends
        end start

THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Copyright Microsoft Corporation 1986-2002.