home *** CD-ROM | disk | FTP | other *** search
- ;-----------------------------------------------------------------------
- ; Alternate Multiplex Interrup Specification Library
- ; AMIS.ASM Public Domain 1992 Ralf Brown
- ; You may do with this software whatever you want, but
- ; common courtesy dictates that you not remove my name
- ; from it.
- ;
- ; Version 0.83
- ; LastEdit: 5/2/92
- ;-----------------------------------------------------------------------
-
- INCLUDE AMIS.MAC
-
- TSRcode@
- EXTRN HOOKED_INT_LIST:BYTE
- EXTRN MULTIPLEX_NUMBER:BYTE
- EXTRN ALTMPX_SIGNATURE:BYTE
- EXTRN ALTMPX$PSP:WORD
- TSRcodeEnd@
-
- IFNDEF __TINY__
- EXTRN __psp:WORD
- ENDIF
-
- ;-----------------------------------------------------------------------
-
- _TEXT SEGMENT PUBLIC BYTE 'CODE'
- ASSUME CS:_TEXT
-
- ;-----------------------------------------------------------------------
-
- alloc_strat dw 0
- link_state db 0 ; are UMBs part of memory chain?
- install_flags db 0 ; mpx_number must immediately follow
- mpx_number db 0 ; multiplex number to install on/when uninstalling
-
- ;-----------------------------------------------------------------------
- ; Terminate back to DOS. Depending on where the resident code was moved,
- ; the program will be terminated either with a normal terminate call or
- ; with a terminate-and-stay-resident call.
- ;
- public $go_TSR
- $go_TSR proc DIST
- db 0BAh ; MOV DX,IMM16
- resident_size dw 0 ; number of paras to keep on going TSR
- db 0B8h ; MOV AX,IMM16
- exit_code db 0 ; exit_func must immediately follow
- exit_func db 4Ch ; will change to 31h if we must go resident
- int 21h
- $go_TSR endp
-
- ;-----------------------------------------------------------------------
- ; entry: AX = segment of TSR code within the currently executing transient
- ; program (AX:ALTMPX_SIGNATURE is used)
- ; exit: CF clear if not installed
- ; AL = 00h if free multiplex number exists
- ; AH = multiplex number to use
- ; = 01h if all multiplex numbers are already in use
- ; AH destroyed
- ; CX destroyed
- ; CF set if already installed
- ; AL = FFh
- ; AH = multiplex number being used
- ; CX = version number of resident TSR
- ;
- public check_if_installed
- check_if_installed proc DIST
- ASSUME DS:NOTHING,ES:NOTHING
- push ds
- push es
- push si
- push di
- push dx
- push bx
- mov ds,ax
- ASSUME DS:RESIDENT_CODE
- xor ax,ax ; AH=mpx #00h, AL=func 00h (instchk)
- mov bx,0001h ; BH=00h, BL=01h: all mpx numbers in use
- chk_installed_loop:
- push ax
- int 2Dh ; check if INT 2D/AH=xx is in use
- cmp al,0FFh ; multiplex number in use?
- pop ax
- je chk_installed_inuse
- or bl,bl ; if BL=00h, we've already seen a free mpx
- je chk_installed_next
- mov bl,0
- mov bh,ah
- jmp short chk_installed_next
- chk_installed_inuse:
- mov es,dx
- ASSUME ES:NOTHING
- mov si,offset RESIDENT_CODE:ALTMPX_SIGNATURE
- push cx ; remember version number
- mov cx,16/2 ; length of signature string
- cld
- rep cmpsw
- pop cx ; retrieve version
- stc ; assume already installed
- jz chk_installed_done ; and quit if it is
- chk_installed_next:
- inc ah
- jnz chk_installed_loop
- ; not yet installed
- clc
- mov ah,bh ; AH <- multiplex number to use
- mov al,bl ; AL <- 'available' flag
- chk_installed_done:
- pop bx
- pop dx
- pop di
- pop si
- pop es
- pop ds
- ASSUME DS:NOTHING,ES:NOTHING
- ret
- check_if_installed endp
-
- ;-----------------------------------------------------------------------
- ; entry: DS:SI -> hooked interrupt list
- ; exit: AX, BX, CX, DX destroyed
- ; CF set if unable to unhook all vectors
- ; CF clear if successful
- ;
- public unhook_interrupts
- unhook_interrupts proc DIST
- push es
- push ds
- push di
- push si
- cld
- chk_unhook_loop:
- lodsb
- mov dx,[si] ; get offset of interrupt handler
- inc si ; and skip that field in the hook
- inc si ; list
- cmp al,2Dh
- je all_unhookable
- mov ah,35h
- int 21h ; get interrupt vector
- mov ax,es
- mov cx,ds
- cmp ax,cx ; check segment agains of vectors
- jne chk_isp_loop
- cmp dx,bx ; check offset of vector against ours
- je chk_unhook_loop ; this int is unhookable if same
- chk_isp_loop:
- cmp word ptr es:[bx],10EBh ; handler starts with JMP SHORT $+12 ?
- jne not_unhookable
- cmp word ptr es:[bx+6],424Bh ; valid signature?
- jne not_unhookable
- cmp byte ptr es:[bx+9],0EBh ; hardware reset must also be JMP SHORT
- jne not_unhookable
- cmp cx,word ptr es:[bx+4] ; check segment of next ptr against ours
- jne chk_next_isp
- cmp dx,word ptr es:[bx+2] ; check offset of next ptr against ours
- je chk_unhook_loop ; this int is unhookable if same
- chk_next_isp:
- les bx,es:[bx+2] ; advance to next ISP header
- jmp chk_isp_loop ; and test it
-
- not_unhookable:
- stc
- unhook_ints_done:
- pop di
- pop si
- pop ds
- pop es
- ret
-
- all_unhookable:
- pop si ; get back start of hook list
- push si ; and preserve SI for return
- unhook_loop:
- lodsb
- mov dx,[si]
- inc si
- inc si
- push ds
- push ax
- mov ah,35h
- int 21h ; get interrupt vector
- mov ax,es
- mov cx,ds
- cmp ax,cx ; check segments of vectors
- jne isp_loop
- cmp dx,bx ; check offsets of vectors
- jne isp_loop
- lds dx,[bx+2] ; get our old_int?? pointer
- pop ax
- push ax
- mov ah,25h ; set interrupt vector
- int 21h
- jmp short unhooked_interrupt
- isp_next:
- les bx,es:[bx+2] ; advance to next ISP header
- isp_loop:
- ;
- ; no need to check for a valid ISP header, as we already know all chains reach
- ; our header before non-ISP code
- ;
- cmp cx,es:[bx+4] ; check segment of 'previous' ptr
- jne isp_next
- cmp dx,es:[bx+2] ; check offset of 'previous' ptr
- jne isp_next
- xchg bx,dx
- lds bx,[bx+2]
- xchg bx,dx ; ES:BX -> previous ISP
- ; DS:DX -> next ISP
- mov es:[bx+2],dx ; prev->next = curr->next
- mov es:[bx+4],ds ; thus, we are now unhooked
- unhooked_interrupt:
- pop ax
- pop ds
- cmp al,2Dh
- jne unhook_loop
- clc ; indicate success
- jmp unhook_ints_done
- unhook_interrupts endp
-
- ;-----------------------------------------------------------------------
- ; Call the XMS driver to allocate an upper memory block
- ;
- ; entry: DX = number of paragraphs needed
- ; exit: ZF set if successful
- ; BX = segment address of UMB
- ;
- alloc_UMB proc near
- mov ah,10h
- ;; fall through to XMS ;;
- alloc_UMB endp
-
- ;-----------------------------------------------------------------------
- ; Call the XMS driver
- ;
- ; entry: all registers as needed for XMS call
- ; exit: registers as returned by XMS driver
- ; ZF set if successful, ZF clear if failure
- ;
- XMS proc near
- db 09Ah ; FAR CALL
- xms_entry dd 0 ; XMS driver's entry point
- cmp ax,1
- ret
- XMS endp
-
- ;-----------------------------------------------------------------------
- ; Determine entry point of XMS driver and initialize procedure XMS to call
- ; that entry point
- ;
- ; exit: CF set if no XMS driver or other failure
- ; CF clear if initialization successful
- ; AX,BX destroyed
- ;
- get_XMS_entry proc near
- push es
- mov ax,352Fh
- int 21h ; find out whether INT 2F is valid
- mov ax,es
- or ax,bx ; don't try XMS if INT 2F is NULL
- jz no_XMS_driver ; (could be case under DOS 2.x)
- mov ax,4300h ; see if XMS is installed
- int 2Fh
- cmp al,80h ; did XMS respond?
- jnz no_XMS_driver
- mov ax,4310h ; if XMS present, get its entry point
- int 2Fh
- mov word ptr xms_entry,bx
- mov word ptr xms_entry+2,es ; and store entry point for call
- pop es
- clc
- ret
- no_XMS_driver:
- pop es
- stc
- ret
- get_XMS_entry endp
-
- ;-----------------------------------------------------------------------
- ; Get an Upper Memory Block from the XMS driver; depending on the
- ; installation flags, this block will either be the first one available
- ; or the one closest in size to the requested amount
- ;
- ; entry: AX = number of paragraphs needed
- ; exit: AX = segment of UMB or 0000h if unable to allocate one
- ;
- allocate_UMB proc near
- mov dx,ax ; remember amount of memory to alloc
- call get_XMS_entry
- jc no_XMS_avail
- test install_flags,BEST_FIT
- jnz alloc_bestfit_UMB ; DX = amount to request
- alloc_XMS:
- call alloc_UMB ; ask XMS for the memory
- mov ax,bx ; (BX -> UMB if successful)
- je allocate_UMB_done ; if we got the mem, return now
- no_XMS_avail:
- xor ax,ax ; return segment 0 if no UMB
- allocate_UMB_done:
- ret
- allocate_UMB endp
-
- alloc_bestfit_UMB proc near
- push si
- push di
- @alloc_size = SI
- @umb_addr = DI
- mov @alloc_size,dx ; remember how much to request
- mov dx,0FFFFh ; try 1 meg
- call alloc_UMB ; ask XMS for the memory
- je XMM_broken ; if we got it, XMM seriously broken!
- cmp dx,@alloc_size ; DX = largest available
- jb UMB_too_small ; not enough high memory left
- call alloc_UMB ; allocate the largest UMB
- jne XMM_broken ; if we didn't get it, XMM broken
- mov @umb_addr,bx ; remember UMB address
- mov dx,@alloc_size
- call alloc_bestfit_UMB ; recurse
- or ax,ax
- jnz got_block
- mov ah,11h ; deallocate UMB
- mov dx,@umb_addr
- call XMS
- mov dx,@alloc_size
- call alloc_UMB ; ask XMS driver for the memory
- jne UMB_too_small ; did we get it?
- mov ax,bx ; BX = addr of UMB
- jmp short alloc_best_done
-
- got_block:
- push ax ; remember address to return
- mov ah,11h ; deallocate UMB
- mov dx,@umb_addr
- call XMS
- pop ax ; retrieve return value
- jmp short alloc_best_done
-
- XMM_broken:
- UMB_too_small:
- xor ax,ax ; didn't get anything
- alloc_best_done:
- pop di
- pop si
- ret
- alloc_bestfit_UMB endp
-
- ;-----------------------------------------------------------------------
- ; entry: nothing
- ; exit: CF set if not available, clear if available
- ; AX,BX,CX,DX destroyed
- ; if available, DOS5 UMBs have been linked into the memory chain
- ;
- check_if_DOS5_UMBs proc near
- mov ax,5800h
- int 21h ; get current allocation strategy
- mov alloc_strat,ax ; and remember it for later restore
- mov ax,5802h ; get current state of UMB linkage
- int 21h
- mov link_state,al
- mov ax,3000h ; get DOS version
- int 21h
- cmp al,5 ; DOS 5.0 or higher?
- jb no_DOS5_UMBs
- cmp al,10 ; but make sure not OS/2 penalty box
- jae no_DOS5_UMBs
- mov ax,2B01h
- mov cx,4445h
- mov dx,5351h
- int 21h ; check if DESQview running
- cmp al,0FFh ; if yes, no UMB's to be allocated
- jne no_DOS5_UMBs
- mov ax,5803h
- mov bx,1 ; try to link in UMBs
- int 21h
- mov ax,5802h ; get new link state
- int 21h
- cmp al,1
- jne no_DOS5_UMBs
- clc ; yes, we have UMBs
- ret
-
- no_DOS5_UMBs:
- stc
- ret
- check_if_DOS5_UMBs endp
-
- ;-----------------------------------------------------------------------
- ;
- ; entry: BX = memory allocation strategy
- ; exit: CF set on error
- ; CF clear if successful
- ; AX = segment of memory block
- ;
- alloc_DOS_highmem proc near
- mov ax,5801h ; set allocation strategy
- int 21h
- mov ah,48h ; allocate memory
- mov bx,resident_size ; this is how much we need
- int 21h ; try to allocate the UMB
- pushf ; remember whether we succeeded
- jc no_highmem ; did we succeed?
- push ax ; yes, so remember where to relocate
- dec ax ; address the MCB for our new memory
- mov es,ax ; block
- inc ax ; back to relocation segment
- mov word ptr es:[1],ax ; make the memory block own itself
- mov ah,51h ; get current PSP
- int 21h
- dec bx ; back to MCB for main memory block
- push ds
- mov ds,bx ; point at MCB
- ASSUME DS:NOTHING
- push si
- push di
- mov si,8
- mov di,si
- cld
- movsw ; copy the DOS 4.0+ program name into
- movsw ; the new memory block's MCB
- movsw
- movsw
- pop di
- pop si
- pop ds
- ASSUME DS:NOTHING
- pop ax ; retrieve relocation address
- ;---------------------------
- ; Reasons for mucking with the MCB:
- ; DOS 5 will release any memory blocks owned by the program when
- ; it exits without going TSR, even if the blocks are in high memory
- ; and high memory has been disconnected from the memory chain. So,
- ; we need to change the owner field such that DOS thinks it belongs to
- ; somebody else and doesn't release it when we exit.
- ;---------------------------
- no_highmem:
- popf ; get back whether we were successful
- restore_link_state:
- pushf ; store flags, especially CF
- push ax
- mov ax,5801h
- mov bx,alloc_strat ; restore allocation strategy
- int 21h
- mov ax,5803h ; and restore UMB link status
- mov bh,0
- mov bl,link_state
- int 21h
- pop ax
- popf ; get back flags
- ret
- alloc_DOS_highmem endp
-
- ;-----------------------------------------------------------------------
- ; entry: AL = flags
- ; bit 0 = use first-fit alloc, nonzero = use best-fit alloc
- ; bit 1 = use UMB only, never conventional memory
- ; bit 2 = use top of lower memory (at 640K)
- ; bit 7 = patch resident portion's PSP return value
- ; AH = multiplex number
- ; BX = segment of resident code
- ; CX = size of resident code in paragraphs
- ; DX = additional paragraphs
- ; exit: CF clear if successful
- ; AX = segment at which TSR was installed
- ; CF set on error
- ;
- public $install_TSR
- $install_TSR proc DIST
- push es
- push si
- push di
- mov word ptr install_flags,ax ; set both install_flags & mpx_number
- push bx ; remember segment of resident code
- push cx ; remember size of resident code
- mov ax,cx
- add ax,dx
- mov resident_size,ax
- ;
- ; first, see if we can load into a DOS5 UMB (this is preferred because
- ; 386MAX will give us an XMS UMB even if DOS5 has grabbed them, but at
- ; a cost of an extra 80 bytes of overhead).
- ;
- test install_flags,LOW_ONLY
- jnz not_XMS
- call check_if_DOS5_UMBs ; check if UMBs avail, and link them in
- jc not_dos5
- mov bx,40h ; alloc high memory only, first-fit
- test install_flags,BEST_FIT
- jz go_allocate_DOS_highmem
- inc bx ; BX <- 41h = alloc high only, best-fit
- go_allocate_DOS_highmem:
- call alloc_DOS_highmem
- jnc relocate_TSR_code ; if successful, go install
- ;
- ; if not DOS5, see if we can load into an XMS upper memory block
- ;
- not_dos5:
- mov ax,resident_size
- call allocate_UMB ; try to get AX paragraphs
- or ax,ax ; did we get a UMB?
- jnz relocate_TSR_code ; if yes, go install at segment AX
-
- ;
- ; if not XMS, see whether we are allowed to load into conventional memory;
- ; if yes, check whether we are supposed to load at the high or low end of
- ; conventional memory
- ;
- not_XMS:
- test install_flags,UMB_ONLY
- jnz install_failure
- install_low:
- test install_flags,USE_TOPMEM
- jz not_topmem
- mov bx,2 ; last-fit in low memory
- call alloc_DOS_highmem ; try to allocate at top of memory
- jnc relocate_TSR_code ; and go install
- install_failure:
- pop cx ; clean up stack
- pop bx
- jmp install_failed
- ;
- ; as a last resort, use our own PSP to store the code, and go resident
- ;
- not_topmem:
- mov ax,cs
- add ax,4 ; copy to offset 40h in PSP
- push ax ; remember where we'll relocate
- add resident_size,4
- mov exit_func,31h ; TSR rather than normal exit
- xor ax,ax
- IFDEF __TINY__
- xchg ax,cs:[002Ch] ; get and zero environment segment
- ELSE
- mov es,__psp
- xchg ax,es:[002Ch] ; get and zero environment segment
- ENDIF
- mov es,ax
- mov ah,49h ; since we will be going resident,
- int 21h ; discard our environment
- pop ax ; get back destination segment
- ;
- ; relocate TSR code into the PSP or UMB
- ; at this point, AX must be the segment at which to relocate
- ;
- relocate_TSR_code:
- pop cx ; get back TSR code size in paragraphs
- pop bx ; get back TSR code segment
- push ds
- mov ds,bx
- ASSUME DS:NOTHING
- mov es,ax ; ES -> resident_seg
- ASSUME ES:NOTHING
- xor si,si
- xor di,di
- mov ax,16 ; bytes per paragraph
- mul cx ; get size in bytes
- or dx,dx
- jnz install_failed_pop ; can only handle 64K at this time
- mov cx,ax ; number of bytes to copy
- cld
- rep movsb ; copy the TSR's code
- mov al,mpx_number ; patch the multiplex number in the
- mov es:[multiplex_number],al ; resident code
- test install_flags,PATCH_RESIDENT
- jz install_no_patch
- mov ax,es ; AX <- resident_seg
- cmp exit_func,4Ch
- je install_patch
- IFDEF __TINY__
- mov ax,cs
- ELSE
- pop ds ; restore DS
- mov ax,__psp
- push ds ; need DS on stack
- ENDIF
- install_patch:
- mov word ptr es:[ALTMPX$PSP],ax
- install_no_patch:
- push es ; remember resident segment
- push es
- pop ds ; DS -> resident_seg
- mov si,offset RESIDENT_CODE:HOOKED_INT_LIST
- hook_interrupts:
- lodsb ; get interrupt number
- mov ah,35h ; get interrupt vector
- int 21h
- mov dx,bx ; ES:DX -> prev handler
- mov bx,[si] ; get offset of interrupt handler
- inc si
- inc si
- mov [bx+2],dx ; set 'previous' pointer in ISP header
- mov [bx+4],es
- mov dx,bx ; DS:DX -> our handler
- mov ah,25h ; AL still interrupt number
- int 21h ; hook the interrupt
- cmp al,2Dh ; INT 2Dh is last in hook list
- jne hook_interrupts
- pop ax ; AX <- resident_seg
- pop ds
- ; clc ; we were successful ;(CF already clear)
- install_TSR_done:
- pop di
- pop si
- pop es
- ret
-
- install_failed_pop:
- pop ds
- install_failed:
- stc ; signal installation failure
- jmp install_TSR_done
- $install_TSR endp
-
- ;-----------------------------------------------------------------------
- ; entry: AX = segment of TSR code within the calling executable
- ; exit: CF clear if successful
- ; CF set on error
- ; AX,BX,CX,DX destroyed
- ;
- public $uninstall_TSR
- $uninstall_TSR proc DIST
- push es
- call check_if_installed
- jnc not_installed
- ;
- ; TSR is installed, AH=multiplex number
- ;
- mov mpx_number,ah
- ;
- ; first, see whether the TSR can uninstall itself
- ;
- mov al,2
- mov dx,cs ; load return address for success
- mov bx,offset _TEXT:uninstall_successful
- int 2Dh
- cmp al,0FFh ; successful?
- je uninstall_successful
- cmp al,02h ; will uninstall itself
- je uninstall_successful
- cmp al,01h ; unable to remove at this time?
- je uninstall_failed
- cmp al,05h ; unknown return code?
- jae uninstall_failed
- ;
- ; TSR said it is safe to uninstall, but not able to do so itself,
- ; so now we find out which interrupts it has hooked
- ;
- mov es,bx ; point ES at memory block to be freed
- ASSUME ES:NOTHING
- uninst_chk_int_loop:
- mov ah,mpx_number
- mov al,4
- mov bl,0 ; start with INT 00h
- int 2Dh
- cmp al,1 ; function unsupported or can't determine?
- jbe uninstall_failed
- cmp al,4
- je go_uninstall
-
- ; jmp short uninstall_failed ; sorry, can't handle returns 02h/03h yet
- uninstall_failed:
- not_installed:
- pop es ; clean up stack
- stc ; indicate error
- ret
-
- go_uninstall:
- push ds
- push si
- mov ds,dx ; DS:SI -> hook list
- mov si,bx
- call unhook_interrupts
- pop si
- pop ds
- jc uninstall_failed
- mov ax,es ; get segment of memory block
- cmp ax,0B000h ; regular DOS memblk if below video
- jae uninstall_highmem
- mov ah,49h ; free memory block
- int 21h
- uninstall_successful:
- pop es ; clean up stack
- clc ; indicate success
- ret
-
- uninstall_highmem:
- call check_if_DOS5_UMBs ; check if UMBs, and link them in
- jc uninstall_XMS
- mov ah,49h ; free the memory block via DOS
- int 21h ; (ES already points at block)
- call restore_link_state
- jmp uninstall_successful
-
- uninstall_XMS:
- call get_XMS_entry
- jc uninstall_failed ; no XMS driver!?!?!
- mov ah,11h ; release UMB
- mov dx,es ; set DX to UMB segment
- call XMS
- jne uninstall_failed ; we deallocation successful?
- jmp uninstall_successful
- $uninstall_TSR endp
-
- ;-----------------------------------------------------------------------
-
- _TEXT ENDS
- END
-
-