.XLIST ;----------------------------------------------------------------------- ; Alternate Multiplex Interrupt Specification Library ; AMIS.MAC 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 ;----------------------------------------------------------------------- AMIS_VERSION equ 340 ;(version 3.4 of the Alternate Multiplex Interrupt Spec) AMISLIB_VERSION equ 083 ;(version 0.83 of this library) ;----------------------------------------------------------------------- ; Return codes for various API calls ; ; general, applies to all standard calls AMIS_NOTIMPLEMENTED equ 0 AMIS_SUCCESSFUL equ 0FFh ; additional return codes for Uninstall (function 02h) AMIS_UNINST_FAILED equ 1 AMIS_UNINST_WILL_DO equ 2 AMIS_UNINST_SAFE_ON equ 3 AMIS_UNINST_SAFE_OFF equ 4 AMIS_UNINST_TRYLATER equ 5 ; additional return codes for Popup (function 03h) AMIS_POPUP_TRYLATER equ 1 AMIS_POPUP_WILLDO equ 2 AMIS_POPUP_BUSY equ 3 AMIS_POPUP_NEEDHELP equ 4 ; additional return codes for Check Interrupt Chained (function 04h) AMIS_CHAIN_DONTKNOW equ 1 AMIS_CHAIN_HOOKED equ 2 AMIS_CHAIN_HOOKED_ADDR equ 3 AMIS_CHAIN_HOOKLIST equ 4 AMIS_CHAIN_NOTUSED equ 0FFh ;----------------------------------------------------------------------- ; ; Set up a shorthand for the segment containing all the resident code and ; data. ; Note: the alignment must be PARA for the code to be properly relocatable ; in small-code memory models. ; TSRcode@ MACRO TGROUP GROUP RESIDENT_CODE RESIDENT_CODE SEGMENT PUBLIC PARA 'TSRCODE' ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING ASSUME CS:TGROUP ENDM TSRcodeEnd@ MACRO RESIDENT_CODE ENDS ENDM ;----------------------------------------------------------------------- ; ; Set up shorthands for the segments containing all the resident data, ; initialized and uninitialized. ; TSRdata@ MACRO RESIDENT_DATA SEGMENT PUBLIC BYTE 'TSRCODE' ENDM TSRdataEnd@ MACRO RESIDENT_DATA ENDS ENDM TSRbss@ MACRO RESIDENT_BSS SEGMENT PUBLIC BYTE 'TSRCODE' ENDM TSRbssEnd@ MACRO RESIDENT_BSS ENDS ENDM ;----------------------------------------------------------------------- ; ; Set up a shorthand for declaring all three resident segments and a group ; TGROUP for those segments. ; TSRgroup@ MACRO TSRcode@ TSRcodeEnd@ TSRdata@ TSRdataEnd@ TSRbss@ TSRbssEnd@ TGROUP GROUP RESIDENT_CODE,RESIDENT_DATA,RESIDENT_BSS ENDM ;----------------------------------------------------------------------- ; ; Some of the code in ALTMPX.ASM uses conditional assembly to handle ; segment registers differently in Tiny model than in other models, so ; we need to ensure that __TINY__ is defined in tiny model. ; IFDEF @Model ; simplified memory models being used? IF @Model eq 1 ; tiny model IFNDEF __TINY__ __TINY__ equ 1 ENDIF ;NDEF ENDIF IF (@Model eq 1) or (@Model eq 2) or (@Model eq 3) ; Tiny, Small, or Compact? DIST equ NEAR ELSE DIST equ FAR ENDIF ELSE ;DEF @Model ; else assume TCC/BCC memory-model #defines IFDEF __TINY__ DIST equ NEAR ELSEIFDEF __SMALL__ DIST equ NEAR ELSEIFDEF __COMPACT__ DIST equ NEAR ELSE DIST equ FAR ENDIF ENDIF ;----------------------------------------------------------------------- ; ; installation flags (mainly internal use--see INSTALL_TSR below) ; BEST_FIT equ 1 ; use best-fit rather than first-fit UMB_ONLY equ 2 ; don't load into low memory, only into a UMB LOW_ONLY equ 4 ; don't use UMB even if high memory available ; (note: can't set both UMB_ONLY and LOW_ONLY) USE_TOPMEM equ 8 ; use the top of low memory if no high memory ; (this is not always the best place to load) PATCH_RESIDENT equ 80h ; patch resident code with actual memory block address ;----------------------------------------------------------------------- ; ; DISPLAY_STRING output a '$'-terminated string to standard output ; arguments: string the label of the string to be displayed ; dataseg [opt] the segment of the string ; DISPLAY_STRING MACRO string,dataseg IFNB push ds mov ax,dataseg mov ds,ax ENDIF mov dx,offset string mov ah,9 int 21h IFNB pop ds ENDIF ENDM ;----------------------------------------------------------------------- ; ; CHECK_DOS_VER ensure that the program is running under the proper ; version of DOS, and terminate with an error message ; specifying the minimum required version if not. ; CHECK_DOS_VER MACRO major,minor LOCAL bad_version_msg,version_OK IF major GE 5 mov ax,3306h ; get true DOS version ELSE mov ax,3000h ENDIF int 21h xchg al,ah cmp ax,256*major + minor jae version_OK IFNDEF __TINY__ push cs pop ds ENDIF DISPLAY_STRING bad_version_msg int 20h ; terminate program bad_version_msg label byte db "This program requires DOS " db major+'0',".",(minor/10)+'0',(minor mod 10)+'0' db " or higher.",13,10,"$" version_OK: ENDM ;----------------------------------------------------------------------- ; ; IF_INSTALLED conditionally branch somewhere if TSR is already installed ; arguments: ; rescode segment of TSR code within executable (to get signature and ; hook list) ; rtype type of segment reference: REL = paras offset from CS ; ABS = absolute segment number ; dest label to branch to if already installed ; at exit: ; CF set if installed ; AH = multiplex number ; CX = version number ; CF clear if not installed ; IF_INSTALLED MACRO rescode,rtype,dest LOCAL not_installed IFIDNI , mov ax,cs add ax,rescode ELSE IFIDNI , mov bx,rescode ; note: loc must always be paragraph-aligned; TSRcode@ ensures this mov cl,4 shr bx,cl mov ax,cs add ax,bx ELSE mov ax,rescode ENDIF ENDIF extrn check_if_installed:DIST call check_if_installed jnc not_installed jmp dest not_installed: ENDM ;----------------------------------------------------------------------- ; ; INSTALL_TSR ; arguments: ; loc location of resident code ; ltype type of above location: REL = para offset from CS ; ABS = absolute paragraph number ; siz size of resident code ; stype type of above size: BYTE or PARA ; extra [opt] number of additional paragraphs needed in resident part ; fit [opt] FIRST (default) or BEST fit allocation ; high [opt] HIGHONLY to only use UMBs, TOPMEM to allocate block at ; high end of conventional memory if no UMBs available, ; LOWONLY to ignore UMBs, and TOPLOW to allocate at high ; end of conventional memory whether or not UMBs are ; available ; init [opt] function to call after installing TSR but before exiting ; if_inst [opt] label to branch to if already installed ; on_err [opt] label to branch to if unable to install ; more_flags [opt] label of byte containing additional flags to OR into ; flags setup by and ; ; if 'init' is specified, the indicated function will be called with ; AX = segment at which TSR was loaded ; if 'if_inst' is specified, the indicated function will be jumped at with ; AH = multiplex number ; CX = version number ; INSTALL_TSR MACRO loc,ltype,siz,stype,extra,fit,high,init,if_inst,on_err,more_flags LOCAL not_installed,install_failed,iflags mov bx,loc IFIDNI , mov ax,cs add bx,ax ELSE IFIDNI , ; note: loc must always be paragraph-aligned; TSRcode@ ensures this mov cl,4 shr bx,cl mov ax,cs add bx,ax ENDIF ENDIF push bx mov ax,bx extrn check_if_installed:DIST call check_if_installed pop bx jnc not_installed install_failure: IFNB jmp if_inst ELSE jmp short install_failed ENDIF not_installed: cmp al,1 je install_failure push ax ; remember multiplex number IFIDNI , mov cx,siz ELSE mov ax,siz add ax,15 ; convert bytes to paragraphs mov cl,4 shr ax,cl mov cx,ax ENDIF IFNB mov dx,extra ELSE xor dx,dx ; no extra memory required ENDIF pop ax ; get back multiplex number iflags = 0 IFDIFI , iflags = iflags OR BEST_FIT ENDIF IFIDNI , iflags = iflags OR UMB_ONLY ENDIF IFIDNI , iflags = iflags OR LOW_ONLY ENDIF IFIDNI , iflags = iflags OR USE_TOPMEM ENDIF IFIDNI , iflags = iflags OR USE_TOPMEM OR LOW_ONLY ENDIF IFDEF ALTMPX$PSP iflags = iflags OR PATCH_RESIDENT ENDIF mov al,iflags IFNB or al,more_flags ENDIF extrn $install_TSR:DIST call $install_TSR ; if success, returns CF clear, AX=segment at which TSR was installed jc install_failed IFNB <&init> call init ENDIF extrn $go_TSR:DIST call $go_TSR ; never returns install_failed: IFNB jmp on_err ELSE push cs pop ds DISPLAY_STRING cs:install_error_msg mov ax,4CFFh ; exit with ERRORLEVEL 255 int 21h install_error_msg db "Unable to go resident.",13,10,"$" ENDIF ENDM ;----------------------------------------------------------------------- ; ; UNINSTALL remove the TSR from memory ; arguments: ; rescode segment of TSR code within executable (to get signature and ; hook list) ; rtype type of segment reference: REL = paras offset from CS ; ABS = absolute segment number ; on_err [opt] label to branch to if unable to remove from memory ; ; If 'on_err' is omitted, check CF after this macro to determine whether ; the removal was successful (CF clear if successful, set on error) ; UNINSTALL MACRO rescode,rtype,on_err LOCAL success IFIDNI , mov ax,cs add ax,rescode ELSE IFIDNI , mov bx,rescode ; note: loc must always be paragraph-aligned; TSRcode@ ensures this mov cl,4 shr bx,cl mov ax,cs add ax,bx ELSE mov ax,rescode ENDIF ENDIF extrn $uninstall_TSR:DIST call $uninstall_TSR IFNB jnc success jmp on_err ENDIF success: ENDM ;----------------------------------------------------------------------- ; ; I M P O R T A N T ! ! ! ; Note: in order to work properly with the code in ALTMPX.ASM, all of ; the following macros must be used inside TSRcode@ ; ;----------------------------------------------------------------------- ; ; ISP_HEADER set up Interrupt Sharing Protocol header for an interrupt ; arguments: ; intr interrupt number ; reset [opt] name of routine to perform hardware reset ; eoi [opt] if nonzero, this is the primary handler for a hardware int ; exported labels: (for example "ISP_HEADER 00h,reset_func,0") ; INT00h_handler (public), ORIG_INT00h (public), HWRESET_00h, ; EOI_FLAG_00h ; [in addition, hw_reset_00h would be present for ISP_HEADER 00h,,0] ; ISP_HEADER MACRO intr,reset,eoi public INT&intr&_handler,ORIG_INT&intr ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING IFB hw_reset_&intr: db 0CBh ; RETF ENDIF ;reset INT&intr&_handler: db 0EBh,10h ; short JMP to skip the header ORIG_INT&intr dd ? ; previous handler in chain dw 424Bh ; ISP signature EOI_FLAG_&intr label byte IFB db 0 ; software int or secondary hardware int ELSE IF eoi eq 0 db 0 ; software int or secondary hardware int ELSE db 80h ; primary hardware int handler ENDIF ;eoi eq 0 ENDIF ;B eoi IFB HWRESET_&intr: jmp short hw_reset_&intr ELSE HWRESET_&intr: jmp short reset ENDIF ;B reset db 7 dup (0) ENDM ;----------------------------------------------------------------------- ; ; HOOKED_INTS declare the interrupts this TSR hooks ; arguments: up to 32 interrupt numbers ; exported labels: HOOKED_INT_LIST (public) ; HOOKED_INTS MACRO a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,aa,ab,ac,ad,ae,af,over public HOOKED_INT_LIST HOOKED_INT_LIST label byte IFNB %out Too many interrupts hooked! .err ENDIF ;NB over IRP intrpt, IFNB IF intrpt ne 2Dh ; ignore INT 2Dh if in the list DB 0&&intrpt DW INT&&intrpt&&_handler ENDIF ;DIFI ENDIF ;NB ENDM ; ; the list terminator is INT 2Dh, since we know that one will always ; be hooked; thus, all interrupts from 00h to FFh may be hooked ; DB 2Dh DW INT2Dh_handler ENDM ;----------------------------------------------------------------------- ; ; ALTMPX define the alternate multiplex interrupt handler for the program ; arguments: ; manuf one- to eight-character manufacturer's name ; prodname one- to eight-character product name ; version four-digit hex version number (hi byte = major, lo = minor) ; descrip [opt] string (max 63 char) describing the product ; priv_funcs [opt] name of routine to handle private INT 2Dh functions ; api_entry [opt] name of FAR routine giving non-INT 2Dh API entry point ; popup [opt] name of function to call to request a popup ; remover [opt] name of function to call to remove TSR from memory ; psp [opt] if nonblank, set up patch word for memblk segment to ; be returned if omitted; returns CS if both ; and blank ; limitations on routines: ; all: must be located inside TSRcode@ ; ; input: AL = function number (10h-FFh) ; AH = multiplex number (ignore) ; others available for handler ; return: via IRET, with regs as appropriate for requested func ; ; input: registers as desired (no limitations) ; return: registers as desired (no limitations) ; ; input: nothing ; return: AL = status ; 01h can not pop up now, try again later ; 02h can not pop up yet, will do so when able ; 03h already popped up ; 04h unable to popup, user intervention required ; BX = standard reason code ; 0000h unknown failure ; 0001h int chain passes through memory ; which must be swapped out ; 0002h swap-in failed ; CX = application's reason code if nonzero ; FFh TSR popped up and was exited by user ; BX = return value ; 0000h no return value ; 0001h TSR unloaded ; 0002h-00FFh reserved ; 0100h-FFFFh application-specific ; ; input: DX:BX = return address if uninstall successful ; return: AL = status ; 01h unable to remove from memory ; 02h can't remove now, will do so when able ; 03h safe to remove, but no resident uninstaller ; (TSR still enabled) ; BX = segment of memory block ; 04h safe to remove, but no resident uninstaller ; (TSR now disabled) ; BX = segment of memory block ; 05h not safe to remove now, try again later ; FFh successful (DX:BX were ignored) ; return at DX:BX with AX destroyed if successful and ; honors specific return address ; if omitted, ALTMPX returns AL=03h ; exported labels: ; INT2Dh_handler (public), ORIG_INT2Dh (public), HWRESET_2Dh, ; EOI_FLAG_2Dh, hw_reset_00h, MULTIPLEX_NUMBER (public), ; ALTMPX_SIGNATURE (public), ALTMPX$PSP [patch word] ; ALTMPX MACRO manuf,prodname,version,descrip,priv_funcs,api_entry,popup,remover,psp LOCAL our_int_2Dh,int2D_func_00,int2D_func_01,int2D_func_02 LOCAL int2D_func_03,int2D_func_04 LOCAL func_is_supported,func_not_supported,func_supported_segDX PUBLIC MULTIPLEX_NUMBER,ALTMPX_SIGNATURE,ALTMPX$PSP ALTMPX_SIGNATURE label byte db manuf IF ($-ALTMPX_SIGNATURE) gt 8 ERR "Manufacturer name >8 chars" ELSEIF ($-ALTMPX_SIGNATURE) lt 8 db (ALTMPX_SIGNATURE+8-$) dup (' ') ENDIF db prodname IF ($-ALTMPX_SIGNATURE) gt 16 ERR "Product name >8 chars" ELSEIF ($-ALTMPX_SIGNATURE) lt 16 db (ALTMPX_SIGNATURE+16-$) dup (' ') ENDIF IFNB db descrip ENDIF db 0 IF ($-ALTMPX_SIGNATURE) gt 80 ERR "Description >63 chars" ENDIF ; save an additional byte by overlaying the null hardware reset handler over ; other code, if possible IFNB hw_reset_2Dh: ; not blank db 0CBh ; RETF IFNDEF ALTMPX$PSP ALTMPX$PSP equ word ptr ($+12) ; point harmlessly into the ISP header ENDIF ELSE IFB ALTMPX$PSP equ word ptr ($+12) ; point harmlessly into the ISP header ENDIF ENDIF IFNB IFB hw_reset_2Dh: ; blank but not db 0CBh ; RETF ENDIF ENDIF ; if both and blank, ; hw_reset_2Dh is defined below ; if is blank and not, ; ALTMPX$PSP is defined below ISP_HEADER 2Dh,hw_reset_2Dh cmp ah,0 ; will be patched with multiplex number MULTIPLEX_NUMBER equ byte ptr ($-1) je our_int_2Dh jmp ORIG_INT2Dh our_int_2Dh: sti ; OK to interrupt from now on cmp al,0 je int2D_func_00 cmp al,2 IFNB jb int2D_func_01 ELSE IFNB jb func_not_supported ENDIF ENDIF je int2D_func_02 cmp al,4 IFNB jb int2D_func_03 ENDIF ;popup je int2D_func_04 IFNB cmp al,10h jb func_not_supported jmp priv_funcs ENDIF ;priv_funcs func_not_supported: mov al,0 iret int2D_func_00: mov cx,version mov di,offset ALTMPX_SIGNATURE func_supported_segDX: mov dx,cs func_is_supported: mov al,0FFh iret IFNB int2D_func_01: mov bx,offset api_entry jmp func_supported_segDX ENDIF ;api_entry int2D_func_02: IFNB call remover ELSE ; mov al,3 ; safe to remove, no resident uninstaller inc ax ; AL was 02h, now 03h IFNB mov bx,0 ; will be patched at installation time ALTMPX$PSP equ word ptr ($-2) ELSE mov bx,cs hw_reset_2Dh equ near ptr ($-1) ; prev instruction happens to expand to 8Ch CBh ENDIF ;psp ENDIF ;remover iret IFNB int2D_func_03: call popup iret ENDIF ;popup int2D_func_04: ;mov al,4 ;not needed since AL=04h anyway mov dx,cs mov bx,offset cs:hooked_int_list iret ENDM ;----------------------------------------------------------------------- ; GRAB_INTERRUPT MACRO intnum,handler TSRbss@ TSR_old_INT&intnum dd ? TSRbssEnd@ mov ax,3500h+intnum int 21h mov word ptr TSR_old_INT&intnum,bx mov word ptr TSR_old_INT&intnum+2,es mov dx,offset TGROUP:handler mov ax,2500h+intnum int 21h ENDM ;----------------------------------------------------------------------- ; RESTORE_INTERRUPT MACRO intnum push ds lds dx,TSR_old_INT&intnum mov ax,2500h+intnum int 21h pop ds ENDM ;----------------------------------------------------------------------- .LIST