.XLIST ;----------------------------------------------------------------------- ; Alternate Multiplex Interrupt Specification Library ; AMIS.MAC Public Domain 1992 Ralf Brown ; ; Version 0.91.AM2 (custom version by Pino Navato for ALTMENU v.2) ;----------------------------------------------------------------------- AMIS_VERSION = 350 ;(version 3.5 of the Alternate Multiplex Interrupt Spec) AMISLIB_VERSION = 091 ;(version 0.91 of this library) ;----------------------------------------------------------------------- ; ; installation flags (see INSTALL_TSR below) ; BEST_FIT = 1 ; use best-fit rather than first-fit UMB_ONLY = 2 ; don't load into low memory, only into a UMB LOW_ONLY = 4 ; don't use UMB even if high memory available ; (note: can't set both UMB_ONLY and LOW_ONLY) USE_TOPMEM = 8 ; use the top of low memory if no high memory ; (this is not always the best place to load) NO_TOPMEM = 0 ; do not use the top of low memory if no UMB (default) PATCH_RESIDENT = 80h ; patch resident code with actual memory block address ;----------------------------------------------------------------------- ; ; 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 ;----------------------------------------------------------------------- ; ; 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 TSRcode@ 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 ELSE DIST equ FAR ENDIF ENDIF 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__ 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 ;----------------------------------------------------------------------- ; ; 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 ;----------------------------------------------------------------------- ; ; 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 ;----------------------------------------------------------------------- ; ; *** NOTE: I have modified the following macro. ; *** Now it includes ALTMENU API (by putting my API in this macro ; *** I have saved some bytes). ; ; 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 ; 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: 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,remover,psp LOCAL our_int_2Dh,int2D_func_00,int2D_func_02 LOCAL int2D_func_04,int2D_func_10 LOCAL func_not_supported,return_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 je int2D_func_02 cmp al,4 je int2D_func_04 cmp al,10h je int2D_func_10 func_not_supported: mov al,0 iret int2D_func_00: mov al,0FFh mov cx,version mov di,offset ALTMPX_SIGNATURE return_segDX: mov dx,cs iret 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 int2D_func_04: ;mov al,4 ;not needed since AL=04h anyway mov dx,cs mov bx,offset cs:$AMIS$HOOKED_INT_LIST iret ;ALTMENU API int2D_func_10: mov bx,offset TGROUP:key_code jmp return_segDX ENDM ;----------------------------------------------------------------------- .LIST