Category : Miscellaneous Language Source Code
Archive   : AMISL090.ZIP
Filename : AMIS.ASM

 
Output of file : AMIS.ASM contained in archive : AMISL090.ZIP
;-----------------------------------------------------------------------
; Alternate Multiplex Interrupt 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.90
; LastEdit: 9/12/92
;-----------------------------------------------------------------------

INCLUDE AMIS.MAC

TSRcode@
EXTRN $AMIS$HOOKED_INT_LIST:BYTE
EXTRN $AMIS$MULTIPLEX_NUMBER:BYTE
EXTRN ALTMPX$PSP:WORD
TSRcodeEnd@

EXTRN __psp:WORD

;-----------------------------------------------------------------------

_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: DX:AX -> TSR signature string
; 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 si
mov ds,dx
ASSUME DS:NOTHING
mov si,ax
push es
push di
push dx
push bx
xor ax,ax ; AH=mpx #00h, AL=func 00h (instlchk)
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
push cx ; remember version number
mov cx,16/2 ; length of signature string
cld
push si ; remember start of desired signature
rep cmpsw ; did we get our signature?
pop si ; get back start of desired signature
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 es
pop si
pop ds
ASSUME DS:NOTHING,ES:NOTHING
ret
check_if_installed 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 low 640K only even if UMB available
; bit 3 = 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,__psp
mov es,ax
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
xchg ax,es:[002Ch] ; get and zero environment segment
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:[$AMIS$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
pop ds ; restore DS
mov ax,__psp
push ds ; need DS on stack
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:$AMIS$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: 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

;-----------------------------------------------------------------------
; entry: DX:AX -> TSR signature string
; 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



  3 Responses to “Category : Miscellaneous Language Source Code
Archive   : AMISL090.ZIP
Filename : AMIS.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/