;From: alan.illeman@canrem.com (Alan Illeman)
;Newsgroups: alt.lang.asm
;Subject: Re: :How can I make a TSR uninstaller ?
;--------------------------------------
; TSR.ASM   using Tasm 3.1
;           tasm /ml /m2 /q /w2 /t tsr.asm
;           tlink /c /x /t tsr, tsr, NUL
;--------------------------------------
b       equ <byte ptr>                ; shorthand
w       equ <word ptr>
d       equ <dword ptr>
o       equ <offset>
s       equ <short>
True    equ 1
False   equ 0

cseg   segment para public 'code'
       assume nothing
       assume cs:cseg, ss:cseg, ds:cseg, es:nothing

       org     100h
       .286
entry:
       jmp     install
;--------------------------------------
even
sp_save        dw  ?                  ; see deinstall
ss_save        dw  ?                  ;
ds_save        dw  ?                  ;
old2Fh         dd  ?                  ; old 2Fh handler

ident          db  ?                  ; multiplex TSR ident
ident_st       db  'TSR.ASM'          ; ident string
ident_len = $ - ident_st              ;

even
;--------------------------------------
; Int 2fh, multiplex intercept
;
; al=0 installation check
;--------------------------------------
new2Fh proc    far
       cmp     ah, cs:ident           ; our tsr ?
       je      mult1                  ; yes
       jmp     d cs:old2Fh            ; no
mult1:
       cmp     al, 0                  ; installation check ?
       jne     mult2                  ; no
       mov     al, 0ffh               ; yes, return al=0ffh
       push    cs                     ;
       pop     es                     ;
       mov     di, o cs:ident_st      ; es:[di] --> ident string
mult2:
       iret
new2Fh endp

;======================================
install proc near
       push    cs
       pop     ds
       assume  ds:cseg
;--------------------------------------
; check if deinstall required
;--------------------------------------
       mov     si, 81h                ; command line
       cld                            ;
ins1:
       lodsb
       cmp     al, 0dh                ; string-end ?
       jne     ins2                   ;
       jmp     ins9                   ; yes, no deinstall
ins2:
       cmp     al, 'd'                ; 'd' ?
       je      ins3                   ; yes
       cmp     al, 'D'                ; 'D' ?
       jne     ins1                   ; no
;--------------------------------------
; attempt deinstall
;--------------------------------------
ins3:
       call    installed              ;
       cmp     ax, True               ;
       je      ins4                   ;
       mov     dx, o errmsg6          ; 'Tsr not installed'
       jmp     error                  ; exit
ins4:
;--------------------------------------
; check if int 2Fh vector has changed
; (installed has returned with es=psp of installed TSR)
;--------------------------------------
       mov     ax, 352Fh
       push    es
       int     21h
       pop     es
       cmp     bx, o es:new2Fh
       je      ins5
       mov     dx, o errmsg5          ; 'TSR cannot be removed'
       jmp     error                  ; exit
ins5:
       mov     ax, 252Fh              ; restore old 2Fh vector
       push    ds
       lds     dx, es:old2Fh          ;
       int     21h
       pop     ds

       mov     w es:[16h], cs         ; install return psp
       mov     w es:[0ah], o gotx     ; install return offset
       mov     w es:[0ch], cs         ; install return segment

       mov     sp_save, sp            ; save sp
       mov     ss_save, ss            ; save ss
       mov     ds_save, ds            ; save ds

       mov     ah, 50h                ; set process
       mov     bx, es                 ; installed TSR
       int     21h                    ;

       mov     ax, 4c00h              ; exit, returning to goto...
       int     21h                    ; ... per Mr. A. Schulman
;--------------------------------------
; deinstall return address, regs unknown
;--------------------------------------
gotx:
       cli
       mov     ds, cs:ds_save         ;
       assume  ds:cseg                ; set ds
       mov     ss, ss_save            ; set ss
       mov     sp, sp_save            ; set sp
       sti                            ;

       mov     dx, o errmsg4          ; 'TSR removed from memory'
       jmp     error                  ; exit
;--------------------------------------
; no deinstall, continue with installation
;--------------------------------------
ins9:
       call    installed              ;
       cmp     ax, False              ;
       je      ins10                  ;
       mov     dx, o errmsg1          ; 'TSR already installed'
       jmp     error                  ;
;--------------------------------------
; search for a vacant num
;--------------------------------------
ins10:                                ; nums 0..191 are reserved
       mov     bh, 192                ; scan nums 192..255
ins11:
       mov     ah, bh                 ; ah=num
       mov     al, 0                  ; al=0, installation check
       push    bx ds
       int     2Fh                    ; multiplex
       pop     ds bx
       cmp     al, 0                  ; this num used ?
       je      ins12                  ; no, exit loop
       inc     bh                     ; next num
       jnz     ins11                  ; up to 255

       mov     dx, o errmsg2          ; out of nums (unlikely)
       jmp     error
ins12:
       mov     ident, ah              ; save num

       mov     ax, 352Fh              ; save old vector
       int     21h
       mov     w old2Fh[0], bx
       mov     w old2Fh[2], es

       mov     ax, 252Fh              ; install new vector
       mov     dx, o new2Fh
       int     21h

       mov     ah, 9
       mov     dx, o errmsg7          ; 'TSR installed'
       int     21h

       mov     dx, o install          ; install !
       shr     dx, 4
       inc     dx
       mov     ax, 3100h
       int     21h
error:
       mov     ah, 9                  ; display string
       int     21h                    ;

       mov     ax, 4c00h              ; exit to Dos
       int     21h                    ;

install endp

even
;--------------------------------------
; call installed
;
; returns AX=True, ES=PSP, if installed
;--------------------------------------
installed proc near
       assume  ds:cseg

       mov     dx, False              ;
       mov     bh, 192                ; scan nums 192..255
inst1:
       mov     ah, bh                 ; ah=num
       mov     al, 0                  ; al=0, installation check
       push    bx ds
       int     2Fh                    ; multiplex
       pop     ds bx
       cmp     al, 0ffh               ; this num used ?
       jne     inst2                  ; no

       mov     si, o ident_st         ; es:[di] addresses...
       mov     cx, ident_len          ; installed tsr string
       cld                            ;
       repe    cmpsb                  ; ident string ok ?
       jne     inst2                  ; no

       mov     dx, True               ; yes, dx=True
       jmp     s inst3                ; exit
inst2:
       inc     bh                     ; next num
       jnz     inst1                  ; up to 255
inst3:
       mov     ax, dx                 ; return ax
       ret
installed endp

errmsg1        db  'TSR already installed',       13,10,7,'$'
errmsg2        db  'TSR install problem',         13,10,7,'$'
errmsg4        db  'TSR removed from memory',     13,10,  '$'
errmsg5        db  'TSR cannot be removed',       13,10,7,'$'
errmsg6        db  'TSR not installed',           13,10,7,'$'
errmsg7        db  'TSR installed, type: <tsrname>'
               db  'D,  to deinstall',            13,10,  '$'

cseg   ends
       end     entry
       end

Some gaps in labels, messages, since code that does anything
useful has been removed. Run "mem /c" at Dos prompt, make a
note of total free memory. Invoke TSR, then deinstall it and
run "mem /c" to see that is has been completely removed.

Techniques from Microsoft's snap.asm and "Undocumented Dos",
1990, by Andrew Schulman.

Since there are so many interrupts in TSR's, I prefer to use
a structure for addressing them :

  INTS    struc
  flag    db ?           ; 1 = interrupt active
  inum    db ?           ; interrupt number
  inew    dw ?           ; new handler offset
  iold    dd ?           ; old handler addr
  INTS    ends
  szINTS  equ size INTS  ; struc size in bytes

..and so a standard TSR had this in its data area :

ints           label byte
intTimer       INTS  { flag=0, inum=08h, inew=o new08h, iold=0 }
intKeyboard    INTS  { flag=0, inum=09h, inew=o new09h, iold=0 }
intVideo       INTS  { flag=0, inum=10h, inew=o new10h, iold=0 }
intDisk        INTS  { flag=0, inum=13h, inew=o new13h, iold=0 }
intDos         INTS  { flag=0, inum=21h, inew=o new21h, iold=0 }
intIdle        INTS  { flag=0, inum=28h, inew=o new28h, iold=0 }
intMultiplex   INTS  { flag=0, inum=2fh, inew=o new2Fh, iold=0 }
ints_len = ($ - ints)/szINTS

vars           label byte
intCtrlBreak   INTS  { flag=0, inum=1bh, inew=o new1Bh, iold=0 }
intCtrlC       INTS  { flag=0, inum=23h, inew=o new23h, iold=0 }
intCritError   INTS  { flag=0, inum=24h, inew=o new24h, iold=0 }
vars_len = ($ - vars)/szINTS

A saving/installing/deinstalling loop makes the job easier.

       mov     si, o ints               ; save old vectors
       mov     cx, ints_len + vars_len
ins13:
       mov     al, (INTS ptr [si]).inum
       mov     ah, 35h
       int     21h
       mov     w (INTS ptr [si]).iold[0], bx
       mov     w (INTS ptr [si]).iold[2], es
       add     si, szINTS
       loop    ins13

Alan
