home *** CD-ROM | disk | FTP | other *** search
/ Shareware 1 2 the Maxx / sw_1.zip / sw_1 / PROGRAM / AMISL083.ZIP / AMIS.ASM next >
Assembly Source File  |  1992-05-02  |  19KB  |  708 lines

  1. ;-----------------------------------------------------------------------
  2. ; Alternate Multiplex Interrup Specification Library
  3. ; AMIS.ASM    Public Domain 1992 Ralf Brown
  4. ;        You may do with this software whatever you want, but
  5. ;        common courtesy dictates that you not remove my name
  6. ;        from it.
  7. ;
  8. ; Version 0.83
  9. ; LastEdit: 5/2/92
  10. ;-----------------------------------------------------------------------
  11.  
  12.     INCLUDE AMIS.MAC
  13.  
  14. TSRcode@
  15. EXTRN HOOKED_INT_LIST:BYTE
  16. EXTRN MULTIPLEX_NUMBER:BYTE
  17. EXTRN ALTMPX_SIGNATURE:BYTE
  18. EXTRN ALTMPX$PSP:WORD
  19. TSRcodeEnd@
  20.  
  21. IFNDEF __TINY__
  22. EXTRN __psp:WORD
  23. ENDIF
  24.  
  25. ;-----------------------------------------------------------------------
  26.  
  27. _TEXT SEGMENT PUBLIC BYTE 'CODE'
  28.     ASSUME    CS:_TEXT
  29.  
  30. ;-----------------------------------------------------------------------
  31.  
  32. alloc_strat    dw 0
  33. link_state    db 0    ; are UMBs part of memory chain?
  34. install_flags    db 0    ; mpx_number must immediately follow
  35. mpx_number    db 0    ; multiplex number to install on/when uninstalling
  36.  
  37. ;-----------------------------------------------------------------------
  38. ; Terminate back to DOS.  Depending on where the resident code was moved,
  39. ; the program will be terminated either with a normal terminate call or
  40. ; with a terminate-and-stay-resident call.
  41. ;
  42. public $go_TSR
  43. $go_TSR proc DIST
  44.     db    0BAh        ; MOV    DX,IMM16
  45. resident_size    dw 0        ; number of paras to keep on going TSR
  46.     db    0B8h        ; MOV    AX,IMM16
  47. exit_code    db 0        ; exit_func must immediately follow
  48. exit_func    db 4Ch        ; will change to 31h if we must go resident
  49.     int    21h
  50. $go_TSR endp
  51.  
  52. ;-----------------------------------------------------------------------
  53. ; entry: AX = segment of TSR code within the currently executing transient
  54. ;        program (AX:ALTMPX_SIGNATURE is used)
  55. ; exit:     CF clear if not installed
  56. ;        AL = 00h if free multiplex number exists
  57. ;            AH = multiplex number to use
  58. ;           = 01h if all multiplex numbers are already in use
  59. ;            AH destroyed
  60. ;        CX destroyed
  61. ;     CF set if already installed
  62. ;        AL = FFh
  63. ;        AH = multiplex number being used
  64. ;        CX = version number of resident TSR
  65. ;
  66. public check_if_installed
  67. check_if_installed proc DIST
  68.     ASSUME    DS:NOTHING,ES:NOTHING
  69.     push    ds
  70.     push    es
  71.     push    si
  72.     push    di
  73.     push    dx
  74.     push    bx
  75.     mov    ds,ax
  76.     ASSUME    DS:RESIDENT_CODE
  77.     xor    ax,ax            ; AH=mpx #00h, AL=func 00h (instchk)
  78.     mov    bx,0001h        ; BH=00h, BL=01h: all mpx numbers in use
  79. chk_installed_loop:
  80.     push    ax
  81.     int    2Dh            ; check if INT 2D/AH=xx is in use
  82.     cmp    al,0FFh            ; multiplex number in use?
  83.     pop    ax
  84.     je    chk_installed_inuse
  85.     or    bl,bl            ; if BL=00h, we've already seen a free mpx
  86.     je    chk_installed_next
  87.     mov    bl,0
  88.     mov    bh,ah
  89.     jmp short chk_installed_next
  90. chk_installed_inuse:
  91.     mov    es,dx
  92.     ASSUME    ES:NOTHING
  93.     mov    si,offset RESIDENT_CODE:ALTMPX_SIGNATURE
  94.     push    cx            ; remember version number
  95.     mov    cx,16/2            ; length of signature string
  96.     cld
  97.     rep    cmpsw
  98.     pop    cx            ; retrieve version
  99.     stc                ; assume already installed
  100.     jz    chk_installed_done    ;   and quit if it is
  101. chk_installed_next:
  102.     inc    ah
  103.     jnz    chk_installed_loop
  104. ; not yet installed
  105.     clc
  106.     mov    ah,bh            ; AH <- multiplex number to use
  107.     mov    al,bl            ; AL <- 'available' flag
  108. chk_installed_done:
  109.     pop    bx
  110.     pop    dx
  111.     pop    di
  112.     pop    si
  113.     pop    es
  114.     pop    ds
  115.     ASSUME    DS:NOTHING,ES:NOTHING
  116.     ret
  117. check_if_installed endp
  118.  
  119. ;-----------------------------------------------------------------------
  120. ; entry: DS:SI -> hooked interrupt list
  121. ; exit:     AX, BX, CX, DX destroyed
  122. ;     CF set if unable to unhook all vectors
  123. ;     CF clear if successful
  124. ;
  125. public unhook_interrupts
  126. unhook_interrupts proc DIST
  127.     push    es
  128.     push    ds
  129.     push    di
  130.     push    si
  131.     cld
  132. chk_unhook_loop:
  133.     lodsb
  134.     mov    dx,[si]            ; get offset of interrupt handler
  135.     inc    si            ;   and skip that field in the hook
  136.     inc    si            ;   list
  137.     cmp    al,2Dh
  138.     je    all_unhookable
  139.     mov    ah,35h
  140.     int    21h            ; get interrupt vector
  141.     mov    ax,es
  142.     mov    cx,ds
  143.     cmp    ax,cx            ; check segment agains of vectors
  144.     jne    chk_isp_loop
  145.     cmp    dx,bx            ; check offset of vector against ours
  146.     je    chk_unhook_loop        ; this int is unhookable if same
  147. chk_isp_loop:
  148.     cmp    word ptr es:[bx],10EBh    ; handler starts with JMP SHORT $+12 ?
  149.     jne    not_unhookable
  150.     cmp    word ptr es:[bx+6],424Bh ; valid signature?
  151.     jne    not_unhookable
  152.     cmp    byte ptr es:[bx+9],0EBh ; hardware reset must also be JMP SHORT
  153.     jne    not_unhookable
  154.     cmp    cx,word ptr es:[bx+4]    ; check segment of next ptr against ours
  155.     jne    chk_next_isp
  156.     cmp    dx,word ptr es:[bx+2]    ; check offset of next ptr against ours
  157.     je    chk_unhook_loop        ; this int is unhookable if same
  158. chk_next_isp:
  159.     les    bx,es:[bx+2]        ; advance to next ISP header
  160.     jmp    chk_isp_loop        ;   and test it
  161.  
  162. not_unhookable:
  163.     stc
  164. unhook_ints_done:
  165.     pop    di
  166.     pop    si
  167.     pop    ds
  168.     pop    es
  169.     ret
  170.  
  171. all_unhookable:
  172.     pop    si            ; get back start of hook list
  173.     push    si            ; and preserve SI for return
  174. unhook_loop:
  175.     lodsb
  176.     mov    dx,[si]
  177.     inc    si
  178.     inc    si
  179.     push    ds
  180.     push    ax
  181.     mov    ah,35h
  182.     int    21h            ; get interrupt vector
  183.     mov    ax,es
  184.     mov    cx,ds
  185.     cmp    ax,cx            ; check segments of vectors
  186.     jne    isp_loop
  187.     cmp    dx,bx            ; check offsets of vectors
  188.     jne    isp_loop
  189.     lds    dx,[bx+2]        ; get our old_int?? pointer
  190.     pop    ax
  191.     push    ax
  192.     mov    ah,25h            ; set interrupt vector
  193.     int    21h
  194.     jmp short unhooked_interrupt
  195. isp_next:
  196.     les    bx,es:[bx+2]        ; advance to next ISP header
  197. isp_loop:
  198. ;
  199. ; no need to check for a valid ISP header, as we already know all chains reach
  200. ; our header before non-ISP code
  201. ;
  202.     cmp    cx,es:[bx+4]        ; check segment of 'previous' ptr
  203.     jne    isp_next
  204.     cmp    dx,es:[bx+2]        ; check offset of 'previous' ptr
  205.     jne    isp_next
  206.     xchg    bx,dx
  207.     lds    bx,[bx+2]
  208.     xchg    bx,dx            ; ES:BX -> previous ISP
  209.                     ; DS:DX -> next ISP
  210.     mov    es:[bx+2],dx        ; prev->next = curr->next
  211.     mov    es:[bx+4],ds        ;    thus, we are now unhooked
  212. unhooked_interrupt:
  213.     pop    ax
  214.     pop    ds
  215.     cmp    al,2Dh
  216.     jne    unhook_loop
  217.     clc                ; indicate success
  218.     jmp    unhook_ints_done
  219. unhook_interrupts endp
  220.  
  221. ;-----------------------------------------------------------------------
  222. ; Call the XMS driver to allocate an upper memory block
  223. ;
  224. ; entry: DX = number of paragraphs needed
  225. ; exit:     ZF set if successful
  226. ;         BX = segment address of UMB
  227. ;
  228. alloc_UMB proc near
  229.     mov    ah,10h
  230.     ;; fall through to XMS ;;
  231. alloc_UMB endp
  232.  
  233. ;-----------------------------------------------------------------------
  234. ; Call the XMS driver
  235. ;
  236. ; entry: all registers as needed for XMS call
  237. ; exit:     registers as returned by XMS driver
  238. ;     ZF set if successful, ZF clear if failure
  239. ;
  240. XMS proc near
  241.     db    09Ah    ; FAR CALL
  242. xms_entry dd    0    ; XMS driver's entry point
  243.     cmp    ax,1
  244.     ret
  245. XMS endp
  246.  
  247. ;-----------------------------------------------------------------------
  248. ; Determine entry point of XMS driver and initialize procedure XMS to call
  249. ; that entry point
  250. ;
  251. ; exit: CF set if no XMS driver or other failure
  252. ;    CF clear if initialization successful
  253. ;    AX,BX destroyed
  254. ;
  255. get_XMS_entry proc near
  256.     push    es
  257.     mov    ax,352Fh
  258.     int    21h            ; find out whether INT 2F is valid
  259.     mov    ax,es
  260.     or    ax,bx            ; don't try XMS if INT 2F is NULL
  261.     jz    no_XMS_driver        ; (could be case under DOS 2.x)
  262.     mov    ax,4300h        ; see if XMS is installed
  263.     int    2Fh
  264.     cmp    al,80h            ; did XMS respond?
  265.     jnz    no_XMS_driver
  266.     mov    ax,4310h        ; if XMS present, get its entry point
  267.     int    2Fh
  268.     mov    word ptr xms_entry,bx
  269.     mov    word ptr xms_entry+2,es ; and store entry point for call
  270.     pop    es
  271.     clc
  272.     ret
  273. no_XMS_driver:
  274.     pop    es
  275.     stc
  276.     ret
  277. get_XMS_entry endp
  278.  
  279. ;-----------------------------------------------------------------------
  280. ; Get an Upper Memory Block from the XMS driver; depending on the
  281. ; installation flags, this block will either be the first one available
  282. ; or the one closest in size to the requested amount
  283. ;
  284. ; entry: AX = number of paragraphs needed
  285. ; exit:     AX = segment of UMB or 0000h if unable to allocate one
  286. ;
  287. allocate_UMB proc near
  288.     mov    dx,ax            ; remember amount of memory to alloc
  289.     call    get_XMS_entry
  290.     jc    no_XMS_avail
  291.     test    install_flags,BEST_FIT
  292.     jnz    alloc_bestfit_UMB    ; DX = amount to request
  293. alloc_XMS:
  294.     call    alloc_UMB        ; ask XMS for the memory
  295.     mov    ax,bx            ; (BX -> UMB if successful)
  296.     je    allocate_UMB_done    ; if we got the mem, return now
  297. no_XMS_avail:
  298.     xor    ax,ax            ; return segment 0 if no UMB
  299. allocate_UMB_done:
  300.     ret
  301. allocate_UMB endp
  302.  
  303. alloc_bestfit_UMB proc near
  304.     push    si
  305.     push    di
  306.  @alloc_size = SI
  307.  @umb_addr = DI
  308.     mov    @alloc_size,dx        ; remember how much to request
  309.     mov    dx,0FFFFh        ; try 1 meg
  310.     call    alloc_UMB        ; ask XMS for the memory
  311.     je    XMM_broken        ; if we got it, XMM seriously broken!
  312.     cmp    dx,@alloc_size        ; DX = largest available
  313.     jb    UMB_too_small        ; not enough high memory left
  314.     call    alloc_UMB        ; allocate the largest UMB
  315.     jne    XMM_broken        ; if we didn't get it, XMM broken
  316.     mov    @umb_addr,bx        ; remember UMB address
  317.     mov    dx,@alloc_size
  318.     call    alloc_bestfit_UMB    ; recurse
  319.     or    ax,ax
  320.     jnz    got_block
  321.     mov    ah,11h            ; deallocate UMB
  322.     mov    dx,@umb_addr
  323.     call    XMS
  324.     mov    dx,@alloc_size
  325.     call    alloc_UMB        ; ask XMS driver for the memory
  326.     jne    UMB_too_small        ; did we get it?
  327.     mov    ax,bx            ; BX = addr of UMB
  328.     jmp short alloc_best_done
  329.  
  330. got_block:
  331.     push    ax            ; remember address to return
  332.     mov    ah,11h            ; deallocate UMB
  333.     mov    dx,@umb_addr
  334.     call    XMS
  335.     pop    ax            ; retrieve return value
  336.     jmp short alloc_best_done
  337.  
  338. XMM_broken:
  339. UMB_too_small:
  340.     xor    ax,ax            ; didn't get anything
  341. alloc_best_done:
  342.     pop    di
  343.     pop    si
  344.     ret
  345. alloc_bestfit_UMB endp
  346.  
  347. ;-----------------------------------------------------------------------
  348. ; entry: nothing
  349. ; exit:     CF set if not available, clear if available
  350. ;     AX,BX,CX,DX destroyed
  351. ;     if available, DOS5 UMBs have been linked into the memory chain
  352. ;
  353. check_if_DOS5_UMBs proc near
  354.     mov    ax,5800h
  355.     int    21h            ; get current allocation strategy
  356.     mov    alloc_strat,ax        ;   and remember it for later restore
  357.     mov    ax,5802h        ; get current state of UMB linkage
  358.     int    21h
  359.     mov    link_state,al
  360.     mov    ax,3000h        ; get DOS version
  361.     int    21h
  362.     cmp    al,5            ; DOS 5.0 or higher?
  363.     jb    no_DOS5_UMBs
  364.     cmp    al,10            ; but make sure not OS/2 penalty box
  365.     jae    no_DOS5_UMBs
  366.     mov    ax,2B01h
  367.     mov    cx,4445h
  368.     mov    dx,5351h
  369.     int    21h            ; check if DESQview running
  370.     cmp    al,0FFh            ; if yes, no UMB's to be allocated
  371.     jne    no_DOS5_UMBs
  372.     mov    ax,5803h
  373.     mov    bx,1            ; try to link in UMBs
  374.     int    21h
  375.     mov    ax,5802h        ; get new link state
  376.     int    21h
  377.     cmp    al,1
  378.     jne    no_DOS5_UMBs
  379.     clc                ; yes, we have UMBs
  380.     ret
  381.  
  382. no_DOS5_UMBs:
  383.     stc
  384.     ret
  385. check_if_DOS5_UMBs endp
  386.  
  387. ;-----------------------------------------------------------------------
  388. ;
  389. ; entry: BX = memory allocation strategy
  390. ; exit:     CF set on error
  391. ;     CF clear if successful
  392. ;        AX = segment of memory block
  393. ;
  394. alloc_DOS_highmem proc near
  395.     mov    ax,5801h        ; set allocation strategy
  396.     int    21h
  397.     mov    ah,48h            ; allocate memory
  398.     mov    bx,resident_size    ;   this is how much we need
  399.     int    21h            ; try to allocate the UMB
  400.     pushf                ; remember whether we succeeded
  401.     jc    no_highmem        ; did we succeed?
  402.     push    ax            ; yes, so remember where to relocate
  403.     dec    ax            ; address the MCB for our new memory
  404.     mov    es,ax            ;   block
  405.     inc    ax            ; back to relocation segment
  406.     mov    word ptr es:[1],ax    ; make the memory block own itself
  407.     mov    ah,51h            ; get current PSP
  408.     int    21h
  409.     dec    bx            ; back to MCB for main memory block
  410.     push    ds
  411.     mov    ds,bx            ; point at MCB
  412.     ASSUME    DS:NOTHING
  413.     push    si
  414.     push    di
  415.     mov    si,8
  416.     mov    di,si
  417.     cld
  418.     movsw                ; copy the DOS 4.0+ program name into
  419.     movsw                ;   the new memory block's MCB
  420.     movsw
  421.     movsw
  422.     pop    di
  423.     pop    si
  424.     pop    ds
  425.     ASSUME    DS:NOTHING
  426.     pop    ax            ; retrieve relocation address
  427. ;---------------------------
  428. ; Reasons for mucking with the MCB:
  429. ;    DOS 5 will release any memory blocks owned by the program when
  430. ;    it exits without going TSR, even if the blocks are in high memory
  431. ;    and high memory has been disconnected from the memory chain.  So,
  432. ;    we need to change the owner field such that DOS thinks it belongs to
  433. ;    somebody else and doesn't release it when we exit.
  434. ;---------------------------
  435. no_highmem:
  436.     popf                ; get back whether we were successful
  437. restore_link_state:
  438.     pushf                ; store flags, especially CF
  439.     push    ax
  440.     mov    ax,5801h
  441.     mov    bx,alloc_strat        ; restore allocation strategy
  442.     int    21h
  443.     mov    ax,5803h        ; and restore UMB link status
  444.     mov    bh,0
  445.     mov    bl,link_state
  446.     int    21h
  447.     pop    ax
  448.     popf                ; get back flags
  449.     ret
  450. alloc_DOS_highmem endp
  451.  
  452. ;-----------------------------------------------------------------------
  453. ; entry: AL = flags
  454. ;        bit 0 = use first-fit alloc, nonzero = use best-fit alloc
  455. ;        bit 1 = use UMB only, never conventional memory
  456. ;        bit 2 = use top of lower memory (at 640K)
  457. ;        bit 7 = patch resident portion's PSP return value
  458. ;     AH = multiplex number
  459. ;     BX = segment of resident code
  460. ;     CX = size of resident code in paragraphs
  461. ;     DX = additional paragraphs
  462. ; exit:     CF clear if successful
  463. ;         AX = segment at which TSR was installed
  464. ;     CF set on error
  465. ;
  466. public $install_TSR
  467. $install_TSR proc DIST
  468.     push    es
  469.     push    si
  470.     push    di
  471.     mov    word ptr install_flags,ax ; set both install_flags & mpx_number
  472.     push    bx            ; remember segment of resident code
  473.     push    cx            ; remember size of resident code
  474.     mov    ax,cx
  475.     add    ax,dx
  476.     mov    resident_size,ax
  477.  ;
  478.  ; first, see if we can load into a DOS5 UMB (this is preferred because
  479.  ; 386MAX will give us an XMS UMB even if DOS5 has grabbed them, but at
  480.  ; a cost of an extra 80 bytes of overhead).
  481.  ;
  482.      test    install_flags,LOW_ONLY
  483.     jnz    not_XMS
  484.     call    check_if_DOS5_UMBs    ; check if UMBs avail, and link them in
  485.     jc    not_dos5
  486.     mov    bx,40h            ; alloc high memory only, first-fit
  487.     test    install_flags,BEST_FIT
  488.     jz    go_allocate_DOS_highmem
  489.     inc    bx            ; BX <- 41h = alloc high only, best-fit
  490. go_allocate_DOS_highmem:
  491.     call    alloc_DOS_highmem
  492.     jnc    relocate_TSR_code    ; if successful, go install
  493.  ;
  494.  ; if not DOS5, see if we can load into an XMS upper memory block
  495.  ;
  496. not_dos5:
  497.     mov    ax,resident_size
  498.     call    allocate_UMB        ; try to get AX paragraphs
  499.     or    ax,ax            ; did we get a UMB?
  500.     jnz    relocate_TSR_code    ; if yes, go install at segment AX
  501.  
  502.  ;
  503.  ; if not XMS, see whether we are allowed to load into conventional memory;
  504.  ; if yes, check whether we are supposed to load at the high or low end of
  505.  ; conventional memory
  506.  ;
  507. not_XMS:
  508.     test    install_flags,UMB_ONLY
  509.     jnz    install_failure
  510. install_low:
  511.     test    install_flags,USE_TOPMEM
  512.     jz    not_topmem
  513.     mov    bx,2            ; last-fit in low memory
  514.     call    alloc_DOS_highmem    ; try to allocate at top of memory
  515.     jnc    relocate_TSR_code    ; and go install
  516. install_failure:
  517.     pop    cx            ; clean up stack
  518.     pop    bx
  519.     jmp    install_failed
  520.  ;
  521.  ; as a last resort, use our own PSP to store the code, and go resident
  522.  ;
  523. not_topmem:
  524.     mov    ax,cs
  525.     add    ax,4            ; copy to offset 40h in PSP
  526.     push    ax            ; remember where we'll relocate
  527.     add    resident_size,4
  528.     mov    exit_func,31h        ; TSR rather than normal exit
  529.     xor    ax,ax
  530. IFDEF __TINY__
  531.     xchg    ax,cs:[002Ch]        ; get and zero environment segment
  532. ELSE
  533.     mov    es,__psp
  534.     xchg    ax,es:[002Ch]        ; get and zero environment segment
  535. ENDIF
  536.     mov    es,ax
  537.     mov    ah,49h            ; since we will be going resident,
  538.     int    21h            ;   discard our environment
  539.     pop    ax            ; get back destination segment
  540.  ;
  541.  ; relocate TSR code into the PSP or UMB
  542.  ; at this point, AX must be the segment at which to relocate
  543.  ;
  544. relocate_TSR_code:
  545.     pop    cx            ; get back TSR code size in paragraphs
  546.     pop    bx            ; get back TSR code segment
  547.     push    ds
  548.     mov    ds,bx
  549.     ASSUME    DS:NOTHING
  550.     mov    es,ax            ; ES -> resident_seg
  551.     ASSUME    ES:NOTHING
  552.     xor    si,si
  553.     xor    di,di
  554.     mov    ax,16            ; bytes per paragraph
  555.     mul    cx            ; get size in bytes
  556.     or    dx,dx
  557.     jnz    install_failed_pop    ; can only handle 64K at this time
  558.     mov    cx,ax            ; number of bytes to copy
  559.     cld
  560.     rep    movsb            ; copy the TSR's code
  561.     mov    al,mpx_number        ; patch the multiplex number in the
  562.     mov    es:[multiplex_number],al ;  resident code
  563.     test    install_flags,PATCH_RESIDENT
  564.     jz    install_no_patch
  565.     mov    ax,es            ; AX <- resident_seg
  566.     cmp    exit_func,4Ch
  567.     je    install_patch
  568. IFDEF __TINY__
  569.     mov    ax,cs
  570. ELSE
  571.     pop    ds            ; restore DS
  572.     mov    ax,__psp
  573.     push    ds            ; need DS on stack
  574. ENDIF
  575. install_patch:
  576.     mov    word ptr es:[ALTMPX$PSP],ax
  577. install_no_patch:
  578.     push    es            ; remember resident segment
  579.     push    es
  580.     pop    ds            ; DS -> resident_seg
  581.     mov    si,offset RESIDENT_CODE:HOOKED_INT_LIST
  582. hook_interrupts:
  583.     lodsb                ; get interrupt number
  584.     mov    ah,35h            ; get interrupt vector
  585.     int    21h
  586.     mov    dx,bx            ; ES:DX -> prev handler
  587.     mov    bx,[si]            ; get offset of interrupt handler
  588.     inc    si
  589.     inc    si
  590.     mov    [bx+2],dx        ; set 'previous' pointer in ISP header
  591.     mov    [bx+4],es
  592.     mov    dx,bx            ; DS:DX -> our handler
  593.     mov    ah,25h            ; AL still interrupt number
  594.     int    21h            ; hook the interrupt
  595.     cmp    al,2Dh            ; INT 2Dh is last in hook list
  596.     jne    hook_interrupts
  597.     pop    ax            ; AX <- resident_seg
  598.     pop    ds
  599. ;    clc                ; we were successful ;(CF already clear)
  600. install_TSR_done:
  601.     pop    di
  602.     pop    si
  603.     pop    es
  604.     ret
  605.  
  606. install_failed_pop:
  607.     pop    ds
  608. install_failed:
  609.     stc                ; signal installation failure
  610.     jmp    install_TSR_done
  611. $install_TSR endp
  612.  
  613. ;-----------------------------------------------------------------------
  614. ; entry: AX = segment of TSR code within the calling executable
  615. ; exit:     CF clear if successful
  616. ;     CF set on error
  617. ;     AX,BX,CX,DX destroyed
  618. ;
  619. public $uninstall_TSR
  620. $uninstall_TSR proc DIST
  621.     push    es
  622.     call    check_if_installed
  623.     jnc    not_installed
  624.     ;
  625.     ; TSR is installed, AH=multiplex number
  626.     ;
  627.     mov    mpx_number,ah
  628.     ;
  629.     ; first, see whether the TSR can uninstall itself
  630.     ;
  631.     mov    al,2
  632.     mov    dx,cs            ; load return address for success
  633.     mov    bx,offset _TEXT:uninstall_successful
  634.     int    2Dh
  635.     cmp    al,0FFh            ; successful?
  636.     je    uninstall_successful
  637.     cmp    al,02h            ; will uninstall itself
  638.     je    uninstall_successful
  639.     cmp    al,01h            ; unable to remove at this time?
  640.     je    uninstall_failed
  641.     cmp    al,05h            ; unknown return code?
  642.     jae    uninstall_failed
  643.     ;
  644.     ; TSR said it is safe to uninstall, but not able to do so itself,
  645.     ; so now we find out which interrupts it has hooked
  646.     ;
  647.     mov    es,bx            ; point ES at memory block to be freed
  648.     ASSUME    ES:NOTHING
  649. uninst_chk_int_loop:
  650.     mov    ah,mpx_number
  651.     mov    al,4
  652.     mov    bl,0            ; start with INT 00h
  653.     int    2Dh
  654.     cmp    al,1            ; function unsupported or can't determine?
  655.     jbe    uninstall_failed
  656.     cmp    al,4
  657.     je    go_uninstall
  658.  
  659. ;    jmp short uninstall_failed    ; sorry, can't handle returns 02h/03h yet
  660. uninstall_failed:
  661. not_installed:
  662.     pop    es            ; clean up stack
  663.     stc                ; indicate error
  664.     ret
  665.  
  666. go_uninstall:
  667.     push    ds
  668.     push    si
  669.     mov    ds,dx            ; DS:SI -> hook list
  670.     mov    si,bx
  671.     call    unhook_interrupts
  672.     pop    si
  673.     pop    ds
  674.     jc    uninstall_failed
  675.     mov    ax,es            ; get segment of memory block
  676.     cmp    ax,0B000h        ; regular DOS memblk if below video
  677.     jae    uninstall_highmem
  678.     mov    ah,49h            ; free memory block
  679.     int    21h
  680. uninstall_successful:
  681.     pop    es            ; clean up stack
  682.     clc                ; indicate success
  683.     ret
  684.  
  685. uninstall_highmem:
  686.     call    check_if_DOS5_UMBs    ; check if UMBs, and link them in
  687.     jc    uninstall_XMS
  688.     mov    ah,49h            ; free the memory block via DOS
  689.     int    21h            ;   (ES already points at block)
  690.     call    restore_link_state
  691.     jmp    uninstall_successful
  692.  
  693. uninstall_XMS:
  694.     call    get_XMS_entry
  695.     jc    uninstall_failed    ; no XMS driver!?!?!
  696.     mov    ah,11h            ; release UMB
  697.     mov    dx,es            ; set DX to UMB segment
  698.     call    XMS
  699.     jne    uninstall_failed    ; we deallocation successful?
  700.     jmp    uninstall_successful
  701. $uninstall_TSR endp
  702.  
  703. ;-----------------------------------------------------------------------
  704.  
  705. _TEXT ENDS
  706.     END
  707.  
  708.