User Tools

Site Tools


blog:pushbx:2023:0707_the_amis_review

The AMIS review

This article reviews various implementations of the Alternate Multiplex Interrupt Specification (AMIS) that I found. All of them are source-available if not free software.

I started notes for this review in 2022 December and wanted to finish it by 2023 June, but the SSD failure of the Linux box interfered with that. So now here it is a little late.

All files are either mirrored at https://pushbx.org/ecm/amis/samples/ or found in my hg repos at https://hg.pushbx.org/ecm/

Ralf Brown (AMISLIB, various)

  • Public Domain
  • 1995-09-24
  • MASM
  • uses sti in the int 2Dh handler
  • bug: CHECK_DOS_VER macro expects 21.3306 to return version in AL.AH but it returns BL.BH
  • cmp ah, imm8 SMC to store and check for multiplex number in the int 2Dh handler (ecm RENUMBER compatible)
  • int 2Dh handler shares common tail code (eg func_supported_segDX)
  • int 2Dh handler can use a resident remover function? but uses a near call to call it then an iret, where the remover could just iret
    • switchar.asm uses it to return al == 4 and disable itself
    • note.asm uses it to request a popup (amipopup.mac) with a flag set for shutdown, and if that succeeds it frees its file handle, returning al == 4
  • can support functions 0 (installation check), 1 (private entrypoint), 2 (uninstall), 3 (request popup), 4 (determine chained interrupts), 5 (get hotkeys), and private functions (>= 10h)
  • extensive popup and hotkey support code
  • int 2Dh uninstall function (if no remover function defined) sets bx using mov bx, imm16 patched as variable ALTMPX$PSP or mov bx, cs
  • can go resident with 21.31 or 21.4C
  • The program called "NOTE": closes file handles 0 to 4 prior to going resident
  • The program called "NOTE": Bug, displays "Installed." message after having closed handle 1 (stdout) – and then also having overwritten the PHT with all 0FFh bytes – so the message never goes anywhere
  • checks for resident instance and free multiplex number by counting from multiplex number 0 to 255 in a single loop
  • can allocate an XMS UMB for installing resident
  • can allocate a DOS UMB for installing resident, will patch MCB owner to itself and MCB name from process MCB
  • can allocate a DOS LMB using last fit strategy, patched just like a DOS UMB
  • if none of the above then 21.31 is used to terminate, environment segment is freed explicitly then. file handles not closed
  • overwrites tail of PSP (first 64 (= 40h) bytes preserved)
  • The program called "NOTE": Creates a resident "TSR PSP" in the first 40h bytes of the resident code segment. This appears to be assumed by AMIPOPUP.ASM because it will set the PSP to equal its cs using the 21.50 service.
  • interrupt handler walker checks for word at ieEntry == 10EBh (jmp $+18), ieSignature "KB", and byte at ieJmphwreset == EBh
  • uninstaller assumes that segment of memory block (as returned by AMIS uninstall function) is a UMB if >= 0B000h. if not an UMB or DOS UMBs are available it tries to deallocate the block using 21.49, else it attempts to use the XMS UMB deallocate function
  • check_if_DOS5_UMBs tries 21.3000 to check for version >= 5 and < 10 (the upper bound failing if OS/2 DOS VM) then checks for DESQview not running using 21.2B01, and finally enables the UMB link using 21.5803 and checks whether 21.5802 returns a UMB link state as 1 in al then

ecm (RxANSI, TSR example, seekext, lClock, KEEPHOOK, lDebug (partial), FDAPM (partial), IDLEDPMS, FreeDOS SHARE (partial))

  • various: Fair License, Public Domain, GNU GPL v2+ for FDAPM and FD SHARE
  • 2023 May (still in development), 2022-11-26
  • NASM, mixed with gcc-ia16 component for FreeDOS SHARE
  • cmp ah, imm8 with SMC to write the multiplex number into the instruction, followed by je (ecm RENUMBER compatible)
  • uses several loops of AMIS installation check:
    • before 2023 May:
      • findinstalleddebugger: 255 to 0 to find lDebug for Update IISP Header utility function
      • findinstalled: /X= number first, then 255 to 0 to find resident instance
    • after 2023 May:
      • findinstalled: /X= number first, then 255 to 0, comparing each number to both the debugger signature and the current program's signature. except if /JD switch specified, in which case only the resident instance is searched, without checking for the debugger.
    • install: /X= number first, then 0 to 255 to find a free multiplex number to allocate (only if installing)
    • additionally, UnhookInterruptSim may query all multiplex numbers from 0 to 255 for every interrupt handler trying to uninstall, as part of the advanced deinstallation method, if walking IISP headers starting from the IVT did not yield a downlink matching the handler to uninstall
    • if UnhookInterruptSim succeeds for all interrupt handlers of a resident program then UnhookInterrupt will subsequently repeat all queries to any multiplex numbers that were queried the first time around by UnhookInterruptSim (UnhookInterrupt internally calls UnhookInterruptSim for every handler a second time), except if the interrupt chains were modified between the simulation loop and the real unhook loop
  • shares common tails of int 2Dh functions
  • frees environment block
  • relocates process to top of LMA during installation, except if /JP switch is specified
  • allocates a DOS memory block for resident program in the UMA (not if /JU switch specified) or LMA, without a PSP
  • hacks MCB owner to the memory block itself, MCB name to a hardcoded string
  • does not close file handles but termination using 21.4C will always take care of those
  • detects whether allocated resident block is in the UMA or LMA, but only for an informational message choice
  • provides /X= option to try a specific multiplex number first (for findinstalled (only resident instance before 2023 May, both debugger and resident instance after 2023 May) and install loops)
  • checks TSR version for compatible values
  • provide private functions to retrieve control variables, allowing to modify some behaviour
  • advanced deinstallation method, searching for the downlink matching our handler in other AMIS multiplexers' interrupt lists
  • checks for valid interrupt handlers to chain to while hooking
  • simulates hooking and unhooking interrupt handlers before actually starting, allowing to generally notice error conditions without ending up with a halfway (un)installed resident instance
  • includes uninstaller in every program, though can also be uninstalled using generic remove tools
  • interrupt handler walker checks for:
    • offset is not too high, to avoid accessing a word at offset FFFFh
    • "KB" in ieSignature
    • byte ieEntry equals 0EBh (short jump) or word ieEntry equals 0EA90h (which is an uninstalled iHPFS header)
    • byte ieJmphwreset equals 0EBh (short jump), 0CBh (retf), or 0CFh (iret)

ecm (TESTALOC)

  • Fair License
  • 2022-02-22
  • NASM
  • like other ecm TSR example based programs, but allows to include a fully resident uninstaller (2023 May updates not included)
  • builds a trampoline on the caller's stack space in order to free the resident memory block without still running code in that very block

Pino Navato (KEYBIT Lite, AltMenu)

Donald Bindner (IDLEHALT, DVORAK96)

  • GNU GPL v2+
  • 1999-02-17
  • NASM + A86 (DVORAK96), A86 (IDLEHALT)
  • does not use SMC cmp ah, imm8 for multiplex number (not ecm RENUMBER compatible), but does use a cmp then je then jmp far [cs:nexthandler]
  • after cmp al, 4 \ jne they use mov al, 4 unnecessarily
  • uses mov dx, cs \ iret in multiple places without sharing
  • needs an 186 (push imm16)
  • uses even 16 \ end_of_resident equ ($-$$ + 0100h) to allow using that equate as a dividend
  • may use 21.31 to install resident
  • may allocate PSP-alike block in DOS UMBs if the cs is ⇐ B800h, copies name from PSP MCB and sets owner to the UMB itself, then terminates with 21.4C
  • frees environment block
  • does not free file handles
  • searches for free multiplex number and a resident instance from multiplex number 255 to 0
  • hardware reset function uses a jmp short to an iret
  • sti in int 2Dh handler
  • no removal handler nor unhooking handling included
    • dvorak: docs suggest using Ralf Brown's generic remover program
    • idlehalt: function 2 is supported and disables program, docs suggest it cannot be uninstalled
  • "I would appreciate comments or bug reports sent to Don Bindner dbindner@truman.edu."
  • allows to disable and enable by using the program; expects to find the byte to XOR with 1 at a hardcoded offset within the segment of the AMIS signature

Davide Bresolin (EATUMBS)

  • MIT License
  • 2022-11-21
  • two-component: WASM + OpenWatcom C
  • _MPlex is a variable in the resident section, not using the SMC cmp ah, imm8 for ecm RENUMBER compatibility
  • does do cmp ah, byte [cs:…] \ jz … \ jmp far [cs:_OldInt2D]
  • does not use sti in int 2Dh handler
  • unneeded jb not_impl where the jne after cmp al, 4 would do
  • uses multiple mov dx, cs that could be shared as one
  • uses inc al to set al = 3 in function 2, could use inc ax
  • hwreset entrypoint is a short jump to a retf
  • searches from multiplex number 0 to 255
  • IISP header walker:
    • entrypoint == EBh byte, "KB" signature, hwreset entrypoint == EBh byte
  • creates orig2d pointer loading from fixed OldInt2D offset and curr2d pointer with int2d_handler offset regardless the actual position of the handler, using the segment of the AMIS signature
  • does not use interrupt list from AMIS function 4, assumes only the int 2Dh handler is hooked
  • does short jumps to iret instructions where just iret would do

George A. Theall or GAT (EATMEM, RECALL)

  • Public Domain
  • 1995-03-26
  • MASM
  • uses data behind PSP and entrypoint jump: AMIS signature (16 bytes + ASCIZ string), AMIS TSR version number (word), Multiplex number (byte), interrupt list (AMIS style)
  • RECALL: switches stack to its own during 21.0A call
  • uses multiplex number in a separate variable, not ecm RENUMBER compatible
  • int 2Dh handler starts with cmp ah, byte [cs:multiplex_number] \ je … \ jmp far [cs:.next]
  • int 2Dh handler does not run a sti
  • int 2Dh handler could share zeroing of al and iret multiple times
  • int 2Dh handler uses dec al to get al = FFh where mov al, 0FFh would do
  • int 2Dh installation check handler loads the version number in cx from the structure behind the PSP instead of hardcoding it with a mov cx, imm16 instruction
  • int 2D handler only implements the installation check (no uninstall function support, no interrupt list function support)
  • leaves resident a modified/reallocated shorter environment block to support memory mappers reading the command name from the string behind the environment variables
  • uses 21.31 to stay resident
  • does not free file handles
  • RECALL: uses RecallBuf offset hardcoded into transient program (segment of AMIS signature)
  • interrupt handler walker checks for byte at ieEntry == 0EBh, "KB" ieSignature, and byte at ieJmphwreset == 0EBh
  • checks for int 2Dh handler starting with 0CFh (iret) and does not call it if so (even though the loop would just detect none of the multiplex numbers in use if it ran)
  • checks from multiplex number 0 to 255 both for finding a resident instance and for determining free multiplex number
  • uses offset of interrupt table hardcoded into transient program (segment of AMIS signature)
  • in uninstall frees environment block found from PSP, if address is still nonzero (assumes that segment of AMIS signature is also PSP segment). so if the AMIS uninstall function was supported then the environment segment should be handled in some way, too.
  • bug: accesses the interrupt list using bp as a base register, so it actually accesses the interrupt list of the transient instance because addresses with bp use the stack segment by default. but if the contents of the transient and resident interrupt list match then the right thing happens anyway, so this is equivalent to hardcoding the offset of the interrupt entrypoints and their IISP headers.
  • includes INTPROTO.TXT (Chris Dunford text about the IISP)
  • confusingly named find_NextISR (load downlink from input IISP header) and find_PrevISR (find downlink reference to our handler by checking the IVT and walking the IISP header chain if available)
  • function check_ifInstalled returns the version number of a resident instance in cx but this is never used

Jim Leonard (SOFTHDDI)

Marcus Better (iHPFS)

  • GNU GPL v2+
  • 2000-07-29
  • TASM ideal mode
  • 386+
  • "You are welcome to send me any ideas, comments and suggestions for iHPFS. If you have found any bugs, please let me know. My email address is Marcus.Better@abc.se"
  • hwreset entrypoint is a short jump to a retf
  • int 2Dh handler behind IISP header starts with a jmp (unconditional), not ecm RENUMBER compatible. target of the jump has a cmp ah, [cs:ApiFunc] \ je \ jmp far [cs:OldInt2D] to check multiplex number. no sti
  • uses multiple mov dx, cs followed by iret
  • frees environment segment
  • counts from 0 to 255 to find a free multiplex number
  • likewise to find resident instance
  • segment size additions and shifting at run time
  • allocates XMS and sets up CDS entries, but AMIS function 2 does not free those
  • sets current PSP to resident copy to uninstall so that 21.4A will set the resident block as owner, not the transient copy's
  • does not walk IISP header chain, only checks for IVT entries pointing directly at the resident instance
  • does not call AMIS function 4 to get the interrupt list, hardcodes the interrupt handler offsets
  • resizes the memory block before patching the entrypoints so that a call could occur while block is already truncated
  • if not both handlers (2Fh and 2Dh) as topmost handlers will instead resize block and patch both IISP headers to hold 90h, EAh (nop then jmp far imm:imm) at entrypoint

Douglas Boling at PC Magazine / CyberRax (CALC 1.1)

  • unclear usage conditions (released with sources in PC Magazine)
  • 2001-12-09
  • TASM
  • 286+
  • standard IISP, hwreset is a short jump to retf right before the header
  • interrupt handler entrypoints both happen to be on odd offsets
  • does not issue a sti in int 2Dh handler
  • not ecm RENUMBER compatible: int 2Dh handler starts with pushf \ cmp ah, byte [cs:moffs16] \ je \ popf \ jmp far [cs:moffs16]
  • int 09h handler has the EOI flag set
  • runs its pop up on the int 09h call's stack
  • has an AMIS hotkey list
    • reverses the bit counting in "type of AMIS hotkey checking" and "AMIS hotkey flags" (both are 1000_0000b where 0000_0001b is desired, bit 7 is reserved for both types)
    • "AMIS shift states" are correct however
  • function 02h (uninstall) returns a non-standard return code 4 (now disabled, no resident uninstaller) with a return value in dx indicating "difference of allocated block into DX (a CyberRaxTSR thing :)" (add dx to bx to gain the DOS memory block of the resident portion)
  • can allocate an XMS memory block for screen swapping. AMIS private function 20h gets the XMS handle in bx, al = 0FFh. XMS handle needs to be freed by the remover, so Ralf Brown's generic remover will leak it (it is not freed by the uninstall function)
  • AMIS private function 30h returns status in bl, al = 0FFh. AMIS private function 31h sets status from bl, returns al = 0FFh (status is expected as 0 or 1).
  • int 2Dh handler ends in popf \ iret
  • does one loop from multiplex number 0 to 255, checking for already resident copy and noting the first found free number
    • uses rep cmpsw where repe is desired
    • can set a variable named allAHs called "All AH's Were Checked" which is never read
  • if found resident, checks private function 30h support ("since 1.1 beta 1") by calling and checking for al = 0FFh, and queries status this way
  • ignores blanks, tabs, slashes, and hyphens in command line, parses known switches as just the letters
    • E = Enable
    • D = Disable
    • I = Install (does not automatically install)
    • U = Uninstall
    • X = Do not use XMS
    • P = Pop up a resident instance
    • H = Help
    • ? = Help
    • all others: ignored silently!
  • uses its xms_handle variable as a flag, handle number 0 is assumed to be invalid (used to indicate no handle allocated). this is not good.
  • frees environment segment but does not clear the PSP field
  • does not free file handles
  • goes resident with interrupt 21h service 31h
  • relocates resident portion (and installer) from the default load address CS:100h to CS:40h and subtracts 0Ch from segregs to address the relocated program at offset 100h. this is the cause of the ill-advised non-standard function 2 (uninstall) return value
    • (NB this assumes DOS can terminate the process with only the first 64 bytes of the PSP preserved, much like Ralf Brown's relocation into part of the original PSP)
  • interrupt 21h service 31h return size is calculated properly at build time (delta of the initialize function and code segment start, + 15 for rounding up, - 0C0h for relocation, shr 4 to get paragraphs, optionally + 1056 to hold screen swapping contents
  • uninstaller:
    • calls function 2 and checks for return 5, but does not check for another return value (assumes all non-5 return values are return value 4)
    • checks that int 9 and int 2Dh vectors in the IVT point to "code segment" of the resident instance, which is assumed to be the bx return value from function 2
    • checks that offsets of int 9 and int 2Dh vectors match the hardcoded offsets of the transient uninstaller's copies of the interrupt handlers
    • checks that function 20h is supported with al returning 0FFh and tries to free the XMS handle if it is returned as nonzero
      • gets address of XMM without properly detecting that an XMM is present
  • can display status (enabled/disabled) of resident instance. incorrectly displays "CALC is resident but disabled" if the private status function 30h is not supported
  • never calls AMIS function 4 (determine chained interrupts)
  • uninstalling and installing take precedence over disabling or enabling or popping up
  • popping up takes precedence over disabling or enabling
  • popping up with P switch is possible even while disabled
  • if both disable and enable or both uninstall and install are specified then an error is issued
You could leave a comment if you were logged in.
blog/pushbx/2023/0707_the_amis_review.txt · Last modified: 2023-07-07 13:34:03 +0200 Jul Fri by ecm