.XLIST ;----------------------------------------------------------------------- ; Alternate Multiplex Interrupt Specification Library ; AMIS.MAC Public Domain 1992, 1993 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.92 ; LastEdit: 2/21/93 ;----------------------------------------------------------------------- AMIS_VERSION equ 350 ;(version 3.5 of the Alternate Multiplex Interrupt Spec) AMISLIB_VERSION equ 092 ;(version 0.92 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 ; hotkey type bits returned by Get Hotkeys (function 05h) HK_INT09ENTRY equ 1 ; TSR checks keys before calling orig INT 09h HK_INT09EXIT equ 2 ; TSR checks keys after calling orig INT 09h HK_INT15ENTRY equ 4 ; TSR checks keys before chaining INT 15h/AH=4Fh HK_INT15EXIT equ 8 ; TSR checks keys after chaining INT 15h/AH=4Fh HK_INT16OLD equ 10h ; TSR checks on INT 16/AH=00h-02h HK_INT16NEW equ 20h ; TSR checks on INT 16/AH=10h-12h ; hotkey shift bits returned by Get Hotkeys (function 05h) HK_NONE equ 0000h ; no shift keys HK_RSHIFT equ 0001h ; right shift key HK_LSHIFT equ 0002h ; left shift key HK_BOTHSHIFT equ 0003h ; both Shift keys must be pressed HK_ANYCTRL equ 0004h ; either Control key must be pressed HK_ANYALT equ 0008h ; either Alt key must be pressed HK_SCRLLOCK_ON equ 0010h ; ScrollLock must be on when hotkey pressed HK_NUMLOCK_ON equ 0020h ; NumLock must be on when hotkey pressed HK_CAPSLOCK_ON equ 0040h ; CapsLock must be on when hotkey pressed HK_ANYSHIFT equ 0080h ; either Shift key must be pressed HK_LCTRL equ 0100h ; left control key HK_LALT equ 0200h ; left Alt key HK_RCTRL equ 0400h ; right control key HK_RALT equ 0800h ; right Alt key HK_BOTHCTRL equ 0500h ; both Control keys must be pressed HK_BOTHALT equ 0A00h ; both Alt keys must be pressed HK_SCROLLOCK equ 1000h ; ScrollLock must be pressed with hotkey HK_NUMLOCK equ 2000h ; NumLock must be pressed with hotkey HK_CAPSLOCK equ 4000h ; CapsLock must be pressed with hotkey HK_SYSREQ equ 8000h ; SysRq must be pressed with hotkey ; hotkey flag bits returned by Get Hotkeys (function 05h) HK_CHAINBEFORE equ 1 ; TSR chains hotkey before processing it HK_CHAINAFTER equ 2 ; TSR chains hotkey after processing it HK_MONITOR equ 4 ; other TSRs should pass through this hotkey ; so that it can be monitored HK_NOPRESSRELEASE equ 8 ; hotkey won't activate if other keys pressed ; and released before hotkey combination ; completed HK_REMAPPED equ 10h ; this key is remapped into some other key HK_NOCHAIN equ 0 ; TSR swallows hotkey ; hotkey scan codes returned by Get Hotkeys (function 05h) SCAN_NONE equ 0 SCAN_ESC equ 1 SCAN_1 equ 2 SCAN_2 equ 3 SCAN_3 equ 4 SCAN_4 equ 5 SCAN_5 equ 6 SCAN_6 equ 7 SCAN_7 equ 8 SCAN_8 equ 9 SCAN_9 equ 10 SCAN_0 equ 11 SCAN_HYPHEN equ 12 SCAN_EQUAL equ 13 SCAN_BACKSP equ 14 SCAN_TAB equ 15 SCAN_Q equ 16 SCAN_W equ 17 SCAN_E equ 18 SCAN_R equ 19 SCAN_T equ 20 SCAN_Y equ 21 SCAN_U equ 22 SCAN_I equ 23 SCAN_O equ 24 SCAN_P equ 25 SCAN_LBRACKET equ 26 SCAN_RBRACKET equ 27 SCAN_ENTER equ 28 SCAN_CTRL equ 29 SCAN_A equ 30 SCAN_S equ 31 SCAN_D equ 32 SCAN_F equ 33 SCAN_G equ 34 SCAN_H equ 35 SCAN_J equ 36 SCAN_K equ 37 SCAN_L equ 38 SCAN_SEMICOLON equ 39 SCAN_SQUOTE equ 40 SCAN_BACKQUOTE equ 41 SCAN_LSHIFT equ 42 SCAN_BACKSLASH equ 43 SCAN_Z equ 44 SCAN_X equ 45 SCAN_C equ 46 SCAN_V equ 47 SCAN_B equ 48 SCAN_N equ 49 SCAN_M equ 50 SCAN_COMMA equ 51 SCAN_PERIOD equ 52 SCAN_SLASH equ 53 SCAN_RSHIFT equ 54 SCAN_GREYSTAR equ 55 SCAN_ALT equ 56 SCAN_SPACE equ 57 SCAN_CAPSLK equ 58 SCAN_F1 equ 59 SCAN_F2 equ 60 SCAN_F3 equ 61 SCAN_F4 equ 62 SCAN_F5 equ 63 SCAN_F6 equ 64 SCAN_F7 equ 65 SCAN_F8 equ 66 SCAN_F9 equ 67 SCAN_F10 equ 68 SCAN_NUMLK equ 69 SCAN_SCRLLK equ 70 SCAN_HOME equ 71 SCAN_UP equ 72 SCAN_PGUP equ 73 SCAN_GREYMINUS equ 74 SCAN_LEFT equ 75 SCAN_KP5 equ 76 SCAN_RIGHT equ 77 SCAN_GREYPLUS equ 78 SCAN_END equ 79 SCAN_DOWN equ 80 SCAN_PGDN equ 81 SCAN_INS equ 82 SCAN_DEL equ 83 SCAN_SYSRQ equ 84 SCAN_F11 equ 87 SCAN_F12 equ 88 HK_ONRELEASE equ 80h ; hotkey activates on key release (add to scan code) ;----------------------------------------------------------------------- ; Return codes for AMISLIB functions ; ; bit flags returned by CHECK_IF_HOTKEYS_USED HC_EXACT equ 1 ; exact match found HC_SUPERSET equ 2 ; some key whose shift states include ours used HC_SUBSET equ 4 ; some key whose shift states included by one of ours used HC_OVERLAP equ 8 ; hotkey overlap found HC_MONITOR equ 80h ; other TSRs monitor one or more hotkeys, but no ; actual conflict because caller chains them HC_IS_CONFLICT equ 7Fh ; mask for testing whether conflict exists ;----------------------------------------------------------------------- ; ; Set up a shorthand for the segment containing all the resident code and ; data. ; Note: the alignment 'align' must be blank to get the PARA alignment needed ; for the code to be properly relocatable in small-code memory models. ; TSRcode@ MACRO align LOCAL alignment TGROUP GROUP RESIDENT_CODE IFB RESIDENT_CODE SEGMENT PUBLIC PARA 'TSRCODE' ELSE RESIDENT_CODE SEGMENT PUBLIC BYTE 'TSRCODE' ENDIF 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 ;----------------------------------------------------------------------- ; ; dummy segment for determining the size of the resident code in the ; executable ; TSRlast@ MACRO RESIDENT_END SEGMENT PUBLIC BYTE 'TSRCODE' ENDM TSRlastEnd@ MACRO RESIDENT_END ENDS ENDM ;----------------------------------------------------------------------- ; ; Set up a shorthand for declaring all three resident segments and a group ; TGROUP for those segments. ; TSRgroup@ MACRO align TSRcode@ align TSRcodeEnd@ TSRdata@ TSRdataEnd@ TSRbss@ TSRbssEnd@ TSRlast@ TSRlastEnd@ TGROUP GROUP RESIDENT_CODE,RESIDENT_DATA,RESIDENT_BSS,RESIDENT_END ENDM ;----------------------------------------------------------------------- ; ; Some of the code in AMIS.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 ELSEIFDEF __MEDIUM__ DIST equ FAR ELSEIFDEF __LARGE__ DIST equ FAR ELSEIFDEF __HUGE__ DIST equ FAR ENDIF ENDIF NOWARN PDC ; don't warn about pass-dependent constructions IF1 IFNDEF DIST &__DEFAULT_MODEL__ equ 1 IFDEF __TINY__ DIST equ NEAR ELSEIFDEF __SMALL__ DIST equ NEAR ELSEIFDEF __COMPACT__ DIST equ NEAR ELSEIFDEF __MEDIUM__ DIST equ FAR ELSEIFDEF __LARGE__ DIST equ FAR ELSEIFDEF __HUGE__ DIST equ FAR ENDIF ENDIF ENDIF WARN PDC IFNDEF __TINY__ TGROUP@ equ TGROUP ENDIF ;----------------------------------------------------------------------- ; ; first half of startup code (invoke right after INCLUDE AMIS.MAC in main module) ; ; arguments: major,minor,stksize ; [opt] major,minor major/minor version of min supported DOS ver ; [opt] stksize size of initial stack for non-Tiny models ; @Startup MACRO major,minor,stksize ;---------------------------------- ; Declare our segments in the order ; we want them in the executable. ; _INIT SEGMENT PUBLIC PARA 'INIT' _INIT ENDS TSRgroup@ _TEXT SEGMENT PUBLIC PARA 'CODE' ; must be aligned PARA to work properly _TEXT ENDS IFNDEF __TINY__ _STACK SEGMENT STACK 'STACK' IFNB db dup (?) ELSE db 100h dup (?) ENDIF _STACK ENDS ENDIF ;ndef __TINY__ ;---------------------------------- ; set up labels for start and end of ; resident code ; TSRcode@ $AMIS$start_TSRcode label byte ; find address of beginning of segment TSRcodeEnd@ TSRlast@ $AMIS$end_TSRcode label byte ; marker for end of resident code in executable TSRlastEnd@ IFDEF __TINY__ _INIT SEGMENT 'INIT' ASSUME CS:_INIT,DS:_INIT,ES:_INIT,SS:_INIT ORG 100h ELSE _TEXT SEGMENT 'CODE' ASSUME CS:_TEXT,DS:NOTHING,ES:NOTHING,SS:NOTHING ENDIF ;def __TINY__ INIT proc far IFNB IFNB CHECK_DOS_VER major,minor ENDIF ENDIF IFNDEF __TINY__ mov dx,TGROUP mov es,dx ASSUME ES:TGROUP jmp $AMIS$PROGRAM_START ELSE mov ax,offset _INIT:$AMIS$start_TSRcode mov cl,4 shr ax,cl mov dx,es add dx,ax mov es,dx ASSUME ES:TGROUP ; ; compute normalized address of actual program entry point ; mov dx,offset _INIT:$AMIS$PROGRAM_START mov ax,offset _TEXT:$AMIS$PROGRAM_START sub dx,ax shr dx,cl ; CL still 4 mov cx,cs add dx,cx push dx ; simulate far jump to computed address push ax ret ENDIF ;ndef __TINY__ INIT endp ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING IFDEF __TINY__ _INIT ENDS ELSE _TEXT ENDS ENDIF ;def __TINY__ ENDM ;----------------------------------------------------------------------- ; ; additional startup code (invoke at start of main program) ; ; arguments: need_psp non-blank to allocate __psp variable, blank if provided ; by some other module (such as the C runtime library) ; @Startup2 MACRO need_psp IFDEF __TINY__ public TGROUP@ TGROUP@ dw ? ENDIF ;__TINY__ IFNB public __psp __psp dw ? ENDIF ;need_psp $AMIS$PROGRAM_START: ASSUME DS:NOTHING,ES:TGROUP mov __psp,ds IFDEF __TINY__ mov TGROUP@,es ENDIF ENDM ;----------------------------------------------------------------------- ; ; 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: ; 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 dest LOCAL not_installed mov dx,TGROUP@ mov ax,offset RESIDENT_CODE:ALTMPX_SIGNATURE extrn check_if_installed:DIST call check_if_installed jnc not_installed jmp dest not_installed: ENDM ;----------------------------------------------------------------------- ; ; IF_HOTKEY_USED conditionally branch somewhere if TSR's hotkeys already in use ; arguments: ; dest label to branch to if one or more hotkeys conflict ; at exit: ; ZF clear if hotkeys conflict ; AX = conflict types ; bit 0: exact key already in use ; bit 1: key with less-strict shift states in use ; bit 2: key with stricter shift states in use ; ZF set if no conflicts ; IF_HOTKEY_USED MACRO dest LOCAL no_conflicts mov dx,TGROUP@ mov ax,offset TGROUP:$AMIS$HOTKEY_LIST extrn check_if_hotkeys_used:DIST call check_if_hotkeys_used jz no_conflicts jmp dest no_conflicts: ENDM ;----------------------------------------------------------------------- ; ; INSTALL_TSR ; arguments: ; 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 extra,fit,high,init,if_inst,on_err,more_flags LOCAL not_installed,install_failed,iflags,install_error_msg mov bx,TGROUP@ push bx ; remember location of resident code mov dx,bx mov ax,offset RESIDENT_CODE:ALTMPX_SIGNATURE extrn check_if_installed:DIST call check_if_installed pop bx ; retrieve location of resident code jnc not_installed install_failure: IFNB jmp if_inst ELSE jmp short install_failed ENDIF not_installed: cmp al,1 je install_failure mov dx,offset TGROUP:$AMIS$end_TSRcode+15 mov cl,4 ; convert bytes to paragraphs shr dx,cl mov cx,dx IFNB mov dx,extra ELSE xor dx,dx ; no extra memory required ENDIF 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: ; 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 on_err LOCAL success mov dx,TGROUP@ mov ax,offset RESIDENT_CODE:ALTMPX_SIGNATURE 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 AMIS.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: $AMIS$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 $AMIS$HOOKED_INT_LIST $AMIS$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 ;----------------------------------------------------------------------- ; ; HOTKEYS declare the type of hotkeys this TSR uses ; arguments: ; check hotkey check type ; flags [opt] default flags for following hotkeys ; exported labels: $AMIS$HOTKEY_LIST (public), ; $amis$hotkey_list_start (internal use) ; ; To declare the TSR's hotkeys, you must use macros in the following order: ; HOTKEYS type ; HOTKEY scan1,req1,disall1 ; HOTKEY scan2,req2,disall2,flags2 ; ... ; HOTKEY scanN,reqN,disallN ; HOTKEYS_DONE ; these definitions must precede the use of the ALTMPX macro ; HOTKEYS MACRO check,flags public $AMIS$HOTKEY_LIST $AMIS$HOTKEY_LIST label byte db check ; type of hotkey check db $amis$num_hotkeys ; number of hotkeys following $amis$hotkey_list_start label byte IFNB $amis$hotkey_def_flags equ flags ELSE IF (check AND HK_INT15EXIT) $amis$hotkey_def_flags equ HK_CHAINBEFORE ELSEIF (check AND HK_INT09EXIT) $amis$hotkey_def_flags equ HK_CHAINBEFORE ELSE $amis$hotkey_def_flags equ HK_NOCHAIN ENDIF ENDIF ;flags ENDM ;----------------------------------------------------------------------- ; ; HOTKEY declare one of the hotkeys this TSR uses ; arguments: ; scan scan code ; req required shift states ; disall disallowed shift states ; flags [opt] flags describing actions taken on hotkey ; exported labels: none ; ; To declare the TSR's hotkeys, you must use macros in the following order: ; HOTKEYS type ; HOTKEY scan1,req1,disall1 ; HOTKEY scan2,req2,disall2,flags2 ; ... ; HOTKEY scanN,reqN,disallN ; HOTKEYS_DONE ; these definitions must precede the use of the ALTMPX macro; the first HOTKEY ; line should define the TSR's primary hotkey ; HOTKEY MACRO scan,req,disall,flags LOCAL t db scan IFNB dw req ELSE dw HK_NONE ENDIF ;req IFNB t = disall IF (((disall) AND HK_ANYSHIFT) eq HK_ANYSHIFT) t = t OR HK_LSHIFT OR HK_RSHIFT ENDIF IF (((disall) AND HK_ANYCTRL) eq HK_ANYCTRL) t = t OR HK_LCTRL OR HK_RCTRL ENDIF IF (((disall) AND HK_ANYALT) eq HK_ANYALT) t = t OR HK_LALT OR HK_RALT ENDIF dw t ELSE dw HK_NONE ENDIF ;disall IFNB db flags ELSE db $amis$hotkey_def_flags ENDIF ;flags ENDM ;----------------------------------------------------------------------- ; ; HOTKEYS_DONE mark end of hotkey list ; arguments: none ; exported labels: $amis$num_hotkeys (equate) ; ; To declare the TSR's hotkeys, you must use macros in the following order: ; HOTKEYS type ; HOTKEY scan1,req1,disall1 ; HOTKEY scan2,req2,disall2 ; ... ; HOTKEY scanN,reqN,disallN ; HOTKEYS_DONE ; these definitions must precede the use of the ALTMPX macro ; HOTKEYS_DONE MACRO $amis$num_hotkeys equ ($-$amis$hotkey_list_start)/6 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_2Dh, $AMIS$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 $AMIS$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 $AMIS$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 IFDEF $amis$num_hotkeys cmp al,5 jne not_hotkey_check mov bx,offset TGROUP:$AMIS$HOTKEY_LIST jmp short func_supported_segDX not_hotkey_check: ENDIF ;$amis$num_hotkeys 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:$AMIS$HOOKED_INT_LIST iret ENDM ;----------------------------------------------------------------------- ; generic hotkey dispatcher ; args: ; chain when to chain INT 15h: BEFORE or AFTER ; should match what HOTKEYS macro declares ; funcs name of list of words containing offsets for the NEAR ; functions to invoke for each hotkey (in the order they are ; listed via the HOTKEY macro) ; other [opt] address to jump at if not INT 15/AH=4Fh ; the target code must chain via ORIG_INT15h ; HOTKEY_DISPATCHER MACRO chain,funcs,other LOCAL int15_handler,not_anyshift LOCAL int15_4F,int15_done,scan_hotkeys,try_next,not_hotkey LOCAL got_hotkey,no_chainbefore,no_chainafter,chain_1 int15_handler proc far ISP_HEADER 15h ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING IFIDNI , pushf call ORIG_INT15h ENDIF ;chain cmp ah,4Fh je int15_4F IFNB jmp other ELSE IFIDNI , iret ; we've already chained ELSE chain_1: jmp ORIG_INT15h ENDIF ;chain ENDIF ;other int15_4F: sti ; OK to interrupt cld ; string ops move up in memory push bx push ax mov ah,12h ; get shift states int 16h and ax,not HK_ANYSHIFT test ax,HK_RSHIFT or HK_LSHIFT jz not_anyshift or ax,HK_ANYSHIFT not_anyshift: mov bx,ax ; BX <- current shift states pop ax ; restore scan code in AL push ds push si push cx push cs pop ds ASSUME DS:TGROUP mov si,offset TGROUP:$AMIS$HOTKEY_LIST+1 mov cl,[si] mov ch,0 jcxz not_hotkey ; skip loop if no hotkeys active inc si ; skip hotkey list header scan_hotkeys: cmp al,[si] ; is it our hotkey? jne try_next ; if scan code differs, it isn't test bx,[si+3] ; any disallowed shift states active? jnz try_next ; if yes, not our hotkey push bx not bx test bx,[si+1] ; any required shift states missing? pop bx jz got_hotkey try_next: add si,6 ; move to next hotkey record loop scan_hotkeys not_hotkey: stc ; key not used by TSR pop cx pop si pop ds ASSUME DS:NOTHING pop bx IFDIFI , IFNB jmp ORIG_INT15h ELSE jmp chain_1 ENDIF ;other ELSE iret ENDIF ;chain got_hotkey: ASSUME DS:TGROUP push ax mov ax,si sub ax,offset TGROUP:$AMIS$HOTKEY_LIST+2 mov bl,3 div bl mov bx,ax ; BX <- offset of func in hk-func list pop ax pop cx IFDIFI , test byte ptr [si+5],HK_CHAINBEFORE jz no_chainbefore stc pushf call ORIG_INT15h no_chainbefore: test byte ptr [si+5],HK_CHAINAFTER ENDIF ;chain pop si pop ds ASSUME DS:NOTHING IFDIFI , pushf ENDIF ;chain call word ptr RESIDENT_CODE:[bx+funcs] IFDIFI , popf pop bx IFNB jz no_chainafter stc jmp ORIG_INT15h ELSE stc jnz chain_1 ENDIF ;other no_chainafter: ELSE pop bx ENDIF ;chain clc ; throw out scan code, all processing done ret 2 int15_handler endp ENDM ;----------------------------------------------------------------------- ; DPL STRUC dpl_ax dw ? dpl_bx dw ? dpl_cx dw ? dpl_dx dw ? dpl_si dw ? dpl_di dw ? dpl_ds dw ? dpl_es dw ? dpl_reserved dw ? dpl_machine dw ? dpl_pid dw ? DPL ENDS ;----------------------------------------------------------------------- ; TSRstack@ MACRO size TSRbss@ TSR_local_stack db size dup (?) TSR_local_stack_end label byte TSRbssEnd@ ENDM ;----------------------------------------------------------------------- ; high-level language interrupt handler interface for AMISLIB ; not yet ready to use ; ; intnum the number of the interrupt to hook ; chain one of FIRST, BEFORE, DURING, AFTER, NONE ; FIRST chains before doing anything else, including testing the ; registers; if none of the register values matches, an ; IRET is executed ; BEFORE chains after testing the register but before calling the ; user handler; if none of the register values matches, ; a FAR JMP to the original interrupt handler is ; performed ; AFTER chains after the user handler returns; if none of the ; register values matches, a FAR JMP to the original ; interrupt handler is performed ; DURING permits the user handler to chain the interrupt at its ; discretion by calling _chain_INT; if none of ; the register values matches, a FAR JMP to the original ; interrupt handler is performed ; NONE never chains the interrupt ; dseg label (or segment name) to be loaded into DS ; localstk [opt] label of DWORD containing address of local stack ; handler the user function for handling the interrupt when triggered ; resetfunc [opt] the function to call on a call to the ISP "reset" ; entry point ; reg the name of the register to test ; valN a list of up to eight values for the specfied register on which ; the user handler is to be invoked ; ; AMIS_INT MACRO intnum,chain,dseg,localstk,handler,resetfunc,reg,val1,val2,val3,val4,val5,val6,val7,val8,over LOCAL TESTVALUES, our_func, not_ours, on_local_stack LOCAL stackptr, old_SP, old_SS TESTVALUES = 0 IFB .err "Must specify DSEG" ENDIF IFB .err "Must specify an interrupt handler" ENDIF IFNB .err "Too many values specified" ENDIF IFIDNI , stackptr dw ? ENDIF ;chain during IFNB old_SP dw ? old_SS dw ? ENDIF ;NB localstk hw_reset_&intnum proc far ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING IFNB pushf push es push ds push bp push di push si push dx push cx push bx push ax mov ax,dseg mov ds,ax call resetfunc pop ax pop bx pop cx pop dx pop si pop di pop bp pop ds pop es popf ENDIF ;NB resetfunc ret hw_reset_&intnum endp ISP_HEADER intnum,hw_reset_&intnum sti ; OK to interrupt here IFIDNI , pushf call orig_INT&intnum ; simulate interrupt ENDIF ;chain IFNB pushf IRP val, IFNB cmp reg,val je our_func TESTVALUES = 1 ENDIF ;NB val ENDM IF TESTVALUES popf IFIDNI , iret ELSE jmp orig_INT&intnum ENDIF ;chain ENDIF ;TESTVALUES our_func: ENDIF IFIDNI , IFB pushf ENDIF ;reg call orig_INT&intnum ; chain, original flags already on stack ELSE IFNB popf ; clean up stack ENDIF ;reg ENDIF ;chain before IFNB push old_SS push old_SP push ax mov ax,ss mov old_SS,ax ; remember current stack pointer mov old_SP,sp cmp ax,word ptr localstk+2 ; already on local stack? pop ax je on_local_stack ; if yes, don't switch cli mov ss,word ptr localstk+2 ; switch to local stack mov sp,word ptr localstk sti on_local_stack: ENDIF ;NB localstk IFIDNI , push cs:stackptr ENDIF pushf push es push ds push bp push di push si push dx push cx push bx push ax IFIDNI , mov cs:stackptr,sp ENDIF mov ax,dseg mov ds,ax call handler pop ax pop bx pop cx pop dx pop si pop di pop bp pop ds pop es popf IFIDINI , pop cs:stackptr ENDIF IFNB cli mov ss,cs:old_SS mov sp,cs:old_SP sti pop old_SP pop old_SS ENDIF ;NB localstk IFIDNI , jmp orig_INT&intnum ELSE iret ENDIF IFIDNI , public _chain_INT&intnum _chain_INT&intnum proc DIST ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING push bp mov bp,stackptr mov ax,[bp] ; load registers from stack frame mov bx,[bp+2] mov cx,[bp+4] mov dx,[bp+6] mov si,[bp+8] mov di,[bp+0Ah] mov ds,[bp+0Eh] mov es,[bp+10h] push word ptr [bp+12h] ; push flags popf ; setup flags mov bp,[bp+0Ch] pushf call orig_INT&intnum ; chain by simulating the interrupt push bp mov bp,stackptr pop word ptr [bp+0Ch] ; store returned BP pushf pop word ptr [bp+12h] ; store returned flags mov [bp],ax ; store returned registers back in mov [bp+2],bx ; stack frame mov [bp+4],cx mov [bp+6],dx mov [bp+8],si mov [bp+0Ah],di mov [bp+0Eh],ds mov [bp+10h],es pop bp ret _chain_INT&intnum endp ENDIF ;chain during 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