1 2 %if 0 3 4 Transient code of KEEPHOOK; command line parser and setup 5 2021 by C. Masloch 6 7 Usage of the works is permitted provided that this 8 instrument is retained with the works, so that any entity 9 that uses the works is notified of this instrument. 10 11 DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. 12 13 %endif 14 15 %assign __lMACROS1_MAC__DEBUG_DEFAULTS 1 16 %include "lmacros3.mac" 17 <1> [list -] 43 <2> [list -] 30 <1> [list -] 17 %include "AMIS.MAC" 18 <1> [list -] 18 19 defaulting 20 21 verdef TSR,C, 1,00 ; Version number of last compatible 22 verdef TSR,P, 1,00 ; Private version 23 24 numdef HEAPENTRIES, 8 25 numdef PSPRELOC, 1 ; relocate PSP 26 numdef UPXPAD, 0 ; pad executable to >=3062 bytes for UPX 27 28 ; Option flags (hold in bp) 29 _onlystate equ 0001h 30 _installlow equ 0002h 31 _installnew equ 0004h 32 _uninstall equ 0008h 33 _expose equ 0010h 34 _cover equ 0020h 35 _size equ 0040h 36 _setints equ 0100h 37 _all equ 0200h 38 _foundresident equ 1000h 39 _incompatresident equ 2000h 40 41 %ifdef _MAP 42 [map all _MAP] 43 %endif 44 45 cpu 8086 46 org 100h 47 48 section TRANSIENT align=1 ; Transient program part (command line parser, setup) 49 50 transient: 51 ; This is a simple MS-DOS 1.x detection (8 Byte). 52 00000000 B44D mov ah, 4Dh ; the only function I found that doesn't need much setup or cleanup 53 00000002 F9 stc ; but that uses MS-DOS 2.x error reporting. clears CF (Int21.30 does not) 54 00000003 CD21 int 21h 55 00000005 7301 jnc .dosgood ; it is MS-DOS 2.x+ or compatible --> 56 00000007 C3 retn ; it isn't so simply terminate 57 .dosgood: 58 00000008 FC cld 59 00000009 BA[1800] mov dx, msg.name 60 0000000C E8730D call disp_msg ; Start with message 61 62 ; The UMB link and memory allocation strategy is now always safed here 63 ; and restored by abort_msg as well as any other termination code. This 64 ; saves any code changing the strategy the effort to restore it proper. 65 0000000F B80258 mov ax, 5802h 66 00000012 CD21 int 21h 67 00000014 30E4 xor ah, ah ; some "DOS" only return al 68 00000016 A3[060E] mov word [ restored.umblink ], ax ; save UMB link 69 70 00000019 B80058 mov ax, 5800h 71 0000001C CD21 int 21h 72 0000001E A3[080E] mov word [ restored.strategy ], ax ; save strategy 73 74 00000021 BA[570D] mov dx, i23 75 00000024 B82325 mov ax, 2523h 76 00000027 CD21 int 21h ; set Int23 so that we restore link/strategy on Ctrl-C as well 77 00000029 BA[540D] mov dx, i24 78 0000002C B82425 mov ax, 2524h 79 0000002F CD21 int 21h ; set Int24 so all hard errors are changed to the soft error 80 81 getfirstumcb: 82 ; We try to get the first UMCB for various uses later. Note that we 83 ; never use the result as an MCB address so if it's wrong we won't 84 ; crash or something. (We compare memory block or MCB addresses to 85 ; the value to check whether they're in the UMA.) 86 00000031 B86112 mov ax, 1261h ; PTS-DOS: Get first UMCB 87 00000034 F9 stc 88 00000035 CD2F int 2Fh 89 00000037 720C jc .determine ; not supported --> 90 00000039 40 inc ax 91 0000003A 83F802 cmp ax, byte 2 ; -1, 0, 1 ? 92 0000003D 7606 jbe .determine ; not supported (or none) --> 93 0000003F 48 dec ax 94 00000040 A3[000E] mov word [ firstumcb ], ax ; set UMB 95 00000043 EB51 jmp short .got ; got it --> 96 97 .determine: 98 00000045 B80358 mov ax, 5803h 99 00000048 31DB xor bx, bx 100 0000004A CD21 int 21h ; disable UMB link, leave only LMA chain 101 0000004C 7248 jc .none ; that isn't supported either --> 102 103 0000004E B452 mov ah, 52h 104 00000050 CD21 int 21h 105 00000052 268B47FE mov ax, word [ es:bx - 2 ] 106 00000056 89C2 mov dx, ax ; first MCB 107 00000058 31DB xor bx, bx ; use offsets from bx, not addresses 108 .looplmb: 109 0000005A 8ED8 mov ds, ax 110 0000005C 40 inc ax 111 0000005D 034703 add ax, word [ bx + 3 ] ; next MCB's address 112 00000060 803F4D cmp byte [ bx ], 'M' 113 00000063 74F5 je .looplmb ; not last --> 114 00000065 803F5A cmp byte [ bx ], 'Z' 115 00000068 752C jne .none ; corrupted --> 116 0000006A 92 xchg ax, dx ; dx = what we assume to be the first UMA chain MCB 117 ; ax = first MCB 118 119 0000006B 50 push ax 120 0000006C 43 inc bx ; = 1 121 0000006D B80358 mov ax, 5803h 122 00000070 CD21 int 21h ; enable UMB link, include UMA chain 123 00000072 58 pop ax 124 00000073 7221 jc .none ; so we can disable it but not enable? --> 125 126 00000075 4B dec bx ; = 0 127 00000076 31C9 xor cx, cx ; flag if assumed first UMCB found 128 .loopumb: 129 00000078 39D0 cmp ax, dx 130 0000007A 7501 jne .notlastlmb 131 0000007C 41 inc cx ; there it is 132 .notlastlmb: 133 0000007D 8ED8 mov ds, ax 134 0000007F 803F4D cmp byte [ bx ], 'M' 135 00000082 7506 jne .islastumb? ; last or corrupted --> 136 00000084 40 inc ax 137 00000085 034703 add ax, word [ bx + 3 ] 138 00000088 EBEE jmp short .loopumb ; process next --> 139 .islastumb?: 140 0000008A 803F5A cmp byte [ bx ], 'Z' 141 0000008D 7507 jne .none ; corrupted --> 142 0000008F E305 jcxz .none ; didn't find that UMCB --> 143 ; The MCB at dx which was behind the one that contained the 'Z' 144 ; signature when we disabled the UMB link is now a valid MCB in 145 ; the MCB chain after we enabled the UMB link. All previous MCBs 146 ; are now 'M'. 147 00000091 2E8916[000E] mov word [ cs:firstumcb ], dx 148 .none: 149 .got: 150 ; no need to restore es here since we do that below 151 ; no need to restore ds since we set it below 152 153 scancl: ; bp = selected options 154 00000096 31ED xor bp, bp ; initial option flags 155 156 00000098 0E push cs 157 00000099 07 pop es 158 0000009A 0E push cs 159 0000009B 1F pop ds ; ds, es changed while searching UMCB 160 0000009C BE8100 mov si, 81h ; PSP command line 161 0000009F AC .loop: lodsb 162 000000A0 3C09 cmp al, 9 ; tab ? 163 000000A2 74FB je .loop 164 000000A4 3C20 cmp al, 32 ; blank ? 165 000000A6 74F7 je .loop 166 000000A8 7303E95F01 jb .end ; zero, cr, lf ? --> 167 000000AD 3C2D cmp al, '-' 168 000000AF 7503E98900 je .switch 169 000000B4 3C2F cmp al, '/' 170 000000B6 7503E98200 je .switch 171 000000BB E83703 call capitalise 172 000000BE 3C41 cmp al, "A" 173 000000C0 7514 jne @F 174 000000C2 8B1C mov bx, word [si] 175 000000C4 81E3DFDF and bx, ~2020h 176 000000C8 81FB4C4C cmp bx, "LL" 177 000000CC 7508 jne @F 178 000000CE 81CD0002 or bp, _all 179 000000D2 46 inc si 180 000000D3 46 inc si 181 000000D4 EBC9 jmp .loop 182 183 @@: 184 000000D6 E82703 call getnybble 185 000000D9 7303E9E500 jc .invalid 186 000000DE 31DB xor bx, bx 187 000000E0 88C3 mov bl, al 188 000000E2 AC lodsb 189 000000E3 E81A03 call getnybble 190 000000E6 720B jc .check_end_number 191 000000E8 01DB add bx, bx 192 000000EA 01DB add bx, bx 193 000000EC 01DB add bx, bx 194 000000EE 01DB add bx, bx 195 000000F0 00C3 add bl, al 196 000000F2 AC lodsb 197 .check_end_number: 198 000000F3 3C09 cmp al, 9 199 000000F5 7407 je @F 200 000000F7 3C20 cmp al, 32 201 000000F9 7603E9C500 ja .invalid 202 @@: 203 204 .number: 205 000000FE 4E dec si 206 000000FF 88D8 mov al, bl 207 00000101 E86B08 call bx_intnum_to_dh_bx_intsel 208 209 00000104 BF[1D0E] mov di, invalidinterrupts 210 00000107 B90900 mov cx, invalidinterrupts_size 211 0000010A F2AE repne scasb 212 0000010C 750F jne @F 213 0000010E BA[E902] mov dx, msg.invalid_interrupt_1 214 00000111 E86E0C call disp_msg 215 00000114 E87D0C call disp_al_hex 216 00000117 BA[FB02] mov dx, msg.invalid_interrupt_2 217 0000011A E9A108 jmp abort_msg 218 219 @@: 220 0000011D 84B7[400E] test byte [selectedinterrupts + bx], dh 221 00000121 740F jz @F 222 00000123 BA[E902] mov dx, msg.double_interrupt_1 223 00000126 E8590C call disp_msg 224 00000129 E8680C call disp_al_hex 225 0000012C BA[1603] mov dx, msg.double_interrupt_2 226 0000012F E98C08 jmp abort_msg 227 228 @@: 229 00000132 08B7[400E] or byte [selectedinterrupts + bx], dh 230 00000136 81CD0001 or bp, _setints 231 0000013A E962FF jmp .loop 232 233 234 ; parse switch 235 .switch: 236 0000013D AC lodsb ; get next character 237 0000013E E8B402 call capitalise 238 ; check against known switches 239 00000141 3C58 cmp al, 'X' 240 00000143 7503E98100 je .multiplexnumber 241 00000148 56 push si 242 00000149 BE[0C0E] mov si, parameters 243 0000014C 86C4 xchg al, ah 244 .loopswitch: 245 0000014E AC lodsb 246 0000014F 91 xchg ax, cx 247 00000150 AC lodsb 248 00000151 91 xchg ax, cx ; cx = option flags 249 00000152 3CFF cmp al, 0FFh 250 00000154 746D je .invalid ; not found! 251 00000156 38E0 cmp al, ah 252 00000158 75F4 jne .loopswitch ; try next --> 253 0000015A 5E pop si 254 0000015B 30ED xor ch, ch 255 0000015D 09CD or bp, cx 256 0000015F F6C140 test cl, _size 257 00000162 7503 jnz .parametersize 258 00000164 E938FF jmp .loop ; ds:si-> character after switch character. just continue 259 260 .parametersize: 261 00000167 AC lodsb 262 00000168 3C3D cmp al, '=' 263 0000016A 7411 je @FF 264 0000016C 3C3A cmp al, ':' 265 0000016E 740D je @FF 266 267 00000170 A8 db __TEST_IMM8 268 @@: 269 00000171 AC lodsb 270 00000172 3C09 cmp al, 9 271 00000174 74FB je @B 272 00000176 3C20 cmp al, 32 273 00000178 74F7 je @B 274 0000017A 7247 jb .invalid 275 276 0000017C A8 db __TEST_IMM8 277 @@: 278 0000017D AC lodsb 279 0000017E E89902 call getdecit 280 00000181 7240 jc .invalid 281 00000183 31DB xor bx, bx 282 00000185 88C3 mov bl, al 283 @@: 284 00000187 AC lodsb 285 00000188 E88F02 call getdecit 286 0000018B 721B jc .check_end_size_number 287 0000018D 89D9 mov cx, bx 288 0000018F 01DB add bx, bx ; * 2 289 00000191 7230 jc .invalid 290 00000193 01DB add bx, bx ; * 4 291 00000195 722C jc .invalid 292 00000197 01CB add bx, cx ; * 5 293 00000199 7228 jc .invalid 294 0000019B 01DB add bx, bx ; * 10 295 0000019D 7224 jc .invalid 296 0000019F 00C3 add bl, al 297 000001A1 80D700 adc bh, 0 298 000001A4 721D jc .invalid 299 000001A6 EBDF jmp @B 300 .check_end_size_number: 301 000001A8 3C09 cmp al, 9 302 000001AA 7404 je @F 303 000001AC 3C20 cmp al, 32 304 000001AE 7713 ja .invalid 305 @@: 306 307 000001B0 4E dec si 308 000001B1 83FB02 cmp bx, 2 309 000001B4 720D jb .invalid 310 000001B6 81FBA30A cmp bx, (64 * 1024 - resident_size_p * 16) / 24 - 1 311 000001BA 7707 ja .invalid 312 000001BC 891E[0A0E] mov word [size], bx 313 000001C0 E9DCFE jmp .loop 314 315 316 .invalid: ; display help if invalid 317 000001C3 BA[4906] mov dx, msg.help 318 000001C6 E9F507 jmp abort_msg 319 320 .multiplexnumber: 321 000001C9 AC lodsb 322 000001CA 3C3D cmp al, '=' 323 000001CC 7411 je @FF 324 000001CE 3C3A cmp al, ':' 325 000001D0 740D je @FF 326 327 000001D2 A8 db __TEST_IMM8 328 @@: 329 000001D3 AC lodsb 330 000001D4 3C09 cmp al, 9 331 000001D6 74FB je @B 332 000001D8 3C20 cmp al, 32 333 000001DA 74F7 je @B 334 000001DC 72E5 jb .invalid 335 336 000001DE A8 db __TEST_IMM8 337 @@: 338 000001DF AC lodsb 339 000001E0 E81D02 call getnybble 340 000001E3 72DE jc .invalid 341 000001E5 31DB xor bx, bx 342 000001E7 88C3 mov bl, al 343 000001E9 AC lodsb 344 000001EA E81302 call getnybble 345 000001ED 720B jc .check_end_multiplexnumber 346 000001EF 01DB add bx, bx 347 000001F1 01DB add bx, bx 348 000001F3 01DB add bx, bx 349 000001F5 01DB add bx, bx 350 000001F7 00C3 add bl, al 351 000001F9 AC lodsb 352 .check_end_multiplexnumber: 353 000001FA 3C09 cmp al, 9 354 000001FC 7404 je @F 355 000001FE 3C20 cmp al, 32 356 00000200 77C1 ja .invalid 357 @@: 358 359 00000202 86FB xchg bh, bl 360 00000204 891E[020E] mov word [trymultiplexnumber], bx 361 00000208 4E dec si 362 00000209 E993FE jmp .loop 363 364 365 .end: 366 367 368 findinstalleddebugger: 369 0000020C B4FF mov ah, 0FFh ; start with multiplex number 0FFh 370 .loop: 371 0000020E E80D00 call .check 372 00000211 B030 mov al, 30h ; al = 30h to indicate found, ah = multiplex number 373 00000213 731F jnc .end 374 00000215 80EC01 sub ah, 1 ; search is backward (to find latest installed first), from 0FFh to 00h including 375 00000218 73F4 jnc .loop ; try next if we didn't check all yet --> 376 377 0000021A 31C0 xor ax, ax ; al = 0 to indicate none found 378 0000021C EB16 jmp .end ; If not found, continue --> 379 380 381 ; INP: ah = multiplex number to check 382 ; ds = ss = cs 383 ; OUT: CY if multiplex number unused or no signature match, 384 ; bp, ah, ds unmodified 385 ; NC if match found, 386 ; ah = multiplex number (unmodified) 387 ; CHG: si, di, es, cx, dx 388 .check: 389 0000021E B000 mov al, 00h ; AMIS installation check 390 00000220 CD2D int 2Dh ; AMIS (or "DOS reserved" = iret if no AMIS present) 391 00000222 3CFF cmp al, 0FFh 392 00000224 750C jne .notfound 393 00000226 BE[0000] mov si, debuggeramissig ; ds:si -> our AMIS name strings 394 00000229 8EC2 mov es, dx ; es:di -> name strings of AMIS multiplexer that just answered 395 0000022B B90800 mov cx, 8 ; Ignore description, only compare vendor and program name 396 0000022E F3A7 repe cmpsw 397 00000230 7401 je .checkret ; ZR, NC = match --> 398 .notfound: 399 00000232 F9 stc ; NZ, CY no match 400 .checkret: 401 00000233 C3 retn 402 403 .end: 404 00000234 A3[040E] mov word [debuggerfunction], ax 405 406 407 findinstalled: 408 00000237 8CC8 mov ax, cs 409 00000239 05F600 add ax, 10h+transient_size_p 410 0000023C 8ED8 mov ds, ax 411 0000023E BB[2C00] mov bx, ctrl0 ; ds:bx -> resident segment's ctrl0 412 413 00000241 2EA1[020E] mov ax, word [cs:trymultiplexnumber] 414 00000245 84C0 test al, al 415 00000247 7505 jnz @F 416 00000249 E81000 call .check 417 0000024C 7345 jnc .end 418 @@: 419 420 0000024E B4FF mov ah, 0FFh ; start with multiplex number 0FFh 421 .loop: 422 00000250 E80900 call .check 423 00000253 733E jnc .end 424 00000255 80EC01 sub ah, 1 ; search is backward (to find latest installed first), from 0FFh to 00h including 425 00000258 73F6 jnc .loop ; try next if we didn't check all yet --> 426 427 0000025A EB37 jmp .end ; If not found, continue --> 428 429 430 ; INP: ah = multiplex number to check 431 ; ds => not yet installed resident segment 432 ; OUT: CY if multiplex number unused or no signature match, 433 ; bp, ah, ds unmodified 434 ; NC if match found, 435 ; bp = flags set appropriately 436 ; ah = multiplex number (unmodified) 437 ; ds => found already resident segment 438 ; ds:bx -> resident ctrl0 439 .check: 440 0000025C B000 mov al, 00h ; AMIS installation check 441 0000025E CD2D int 2Dh ; AMIS (or "DOS reserved" = iret if no AMIS present) 442 00000260 3CFF cmp al, 0FFh 443 00000262 752D jne .notfound 444 00000264 BE[0000] mov si, amissig ; ds:si -> our AMIS name strings 445 00000267 8EC2 mov es, dx ; es:di -> name strings of AMIS multiplexer that just answered 446 00000269 B90800 mov cx, 8 ; Ignore description, only compare vendor and program name 447 0000026C F3A7 repe cmpsw 448 0000026E 7521 jne .notfound ; No match, try next 449 00000270 8EDA mov ds, dx ; set ds to our resident copy's segment 450 00000272 B010 mov al, 10h ; get compatible TSR version 451 00000274 CD2D int 2Dh 452 00000276 3CFF cmp al, 0FFh ; al = 0FFh if supported 453 00000278 750E jne .unknown ; doesn't support this question --> 454 0000027A 81F90001 cmp cx, TSR_VERC ; Compatible version? 455 0000027E 7508 jne .unknown 456 00000280 B020 mov al, 20h ; Get TSR ctrl0 457 00000282 CD2D int 2Dh 458 00000284 3C00 cmp al, 00h ; al = size of returned ctrl data. at least one byte if supported 459 00000286 7504 jne .known ; if size ok, known --> 460 .unknown: 461 00000288 81CD0020 or bp, _incompatresident; indicate resident copy is incompatible 462 .known: 463 0000028C 81CD0010 or bp, _foundresident ; indicate resident copy found (NC) 464 00000290 C3 retn 465 466 .notfound: 467 00000291 F9 stc 468 00000292 C3 retn 469 470 .end: 471 472 ; ah = AMIS multiplex number of resident copy if bp&_foundresident (else 0FFh) 473 ; ds:bx-> ctrl0 of either resident copy or stored handler 474 process: 475 476 00000293 89EB mov bx, bp 477 00000295 83E330 and bx, _expose | _cover 478 00000298 83FB30 cmp bx, _expose | _cover 479 0000029B 7503E923FF je scancl.invalid 480 481 000002A0 F7C50800 test bp, _uninstall 482 000002A4 740C jz .dontuninstall 483 000002A6 F7C51600 test bp, _installlow | _installnew | _expose 484 000002AA 7403E914FF jnz scancl.invalid ; all invalid if /u selected 485 000002AF E99402 jmp uninstall 486 487 .dontuninstall: 488 000002B2 F7C50010 test bp, _foundresident ; no resident found ? 489 000002B6 740F jz .install ; yes, install --> 490 000002B8 F7C50400 test bp, _installnew ; forced to install new ? 491 000002BC 7420 jz .dontinstall ; no --> 492 000002BE F7C50100 test bp, _onlystate 493 000002C2 7403E9FCFE jnz scancl.invalid ; invalid combination --> 494 495 .install: 496 000002C7 F7C50100 test bp, _onlystate ; don't install ? 497 000002CB 7405 jz .installallowed 498 000002CD BA[5500] mov dx, msg.notyet 499 000002D0 EB20 jmp short .abort_msg 500 .installallowed: 501 000002D2 F7C51000 test bp, _expose 502 000002D6 7403E9E8FE jnz scancl.invalid ; invalid if installing 503 000002DB E9EB06 jmp install 504 505 .dontinstall: 506 000002DE F7C50200 test bp, _installlow 507 000002E2 7405 jz .changeflags 508 000002E4 BA[5603] mov dx, msg.already ; can't install new, already resident 509 000002E7 EB09 jmp short .abort_msg 510 511 .changeflags: 512 000002E9 F7C50020 test bp, _incompatresident 513 000002ED 7406 jz .compatible 514 000002EF BA[3403] mov dx, msg.incompatible ; can't change flags of incompatible version 515 .abort_msg: 516 000002F2 E9C906 jmp abort_msg 517 518 .compatible: 519 000002F5 B020 mov al, 20h 520 000002F7 CD2D int 2Dh ; clear unsupported bits 521 522 000002F9 F7C51000 test bp, _expose 523 000002FD 7403E9B900 jnz expose_signon 524 00000302 F7C52002 test bp, _cover | _all 525 00000306 7403E91105 jnz cover_signon 526 0000030B F7C50001 test bp, _setints 527 0000030F 7403E91705 jnz cover_signon.not_all 528 529 00000314 BA[BF03] mov dx, msg.state 530 00000317 E8680A call disp_msg 531 0000031A 88E0 mov al, ah 532 0000031C BA[4000] mov dx, msg.multiplex.1 533 0000031F E8600A call disp_msg 534 00000322 E8570A call disp_amisnum 535 536 00000325 31C9 xor cx, cx 537 00000327 8D7F04 lea di, word [bx + heapfree_offset - ctrl0] 538 @@: 539 0000032A 034D02 add cx, word [di + 2] 540 0000032D 8B3D mov di, word [di] 541 0000032F 83FFFF cmp di, -1 542 00000332 75F6 jne @B 543 544 00000334 BA[C903] mov dx, msg.state.mem.1 545 00000337 E8480A call disp_msg 546 0000033A 91 xchg ax, cx 547 0000033B E8740A call disp_ax_dec 548 0000033E 91 xchg ax, cx 549 0000033F 034F02 add cx, word [bx + amisintr.length - ctrl0] 550 00000342 8B3F mov di, word [bx + amisintr.offset - ctrl0] 551 00000344 A8 db __TEST_IMM8 ; (skip inc) 552 @@: 553 00000345 41 inc cx 554 00000346 803D2D cmp byte [di], 2Dh 555 00000349 8D7D03 lea di, [di + 3] 556 0000034C 75F7 jne @B 557 0000034E BA[D703] mov dx, msg.state.mem.2 558 00000351 E82E0A call disp_msg 559 00000354 91 xchg ax, cx 560 00000355 E85A0A call disp_ax_dec 561 00000358 91 xchg ax, cx 562 00000359 BA[DE03] mov dx, msg.state.mem.3 563 0000035C E8230A call disp_msg 564 565 0000035F B004 mov al, 4 ; AMIS function 4, determine chained interrupts 566 00000361 B32D mov bl, 2Dh ; placeholder value for interrupt to check 567 00000363 CD2D int 2Dh 568 00000365 3C04 cmp al, 4 ; returned interrupt list ? 569 00000367 7405 je @F ; yes --> 570 571 00000369 BA[2704] mov dx, msg.state.nolist 572 0000036C EB84 jmp .abort_msg 573 574 @@: 575 0000036E 8EC2 mov es, dx 576 00000370 26803F2D cmp byte [es:bx], 2Dh 577 00000374 7508 jne @F 578 579 00000376 BA[0B04] mov dx, msg.state.none 580 00000379 E8060A call disp_msg 581 0000037C EB35 jmp .terminate 582 583 @@: 584 0000037E 268A07 mov al, byte [es:bx] 585 00000381 3C2D cmp al, 2Dh 586 00000383 742E je .terminate 587 00000385 BA[E803] mov dx, msg.state.1 588 00000388 E8F709 call disp_msg 589 0000038B E8060A call disp_al_hex 590 0000038E BA[F303] mov dx, msg.state.2 591 00000391 E8EE09 call disp_msg 592 00000394 50 push ax 593 00000395 8CC0 mov ax, es 594 00000397 E8F309 call disp_ax_hex 595 0000039A BA[0304] mov dx, msg.state.3 596 0000039D E8E209 call disp_msg 597 000003A0 268B4701 mov ax, word [es:bx + 1] 598 000003A4 E8E609 call disp_ax_hex 599 000003A7 BA[0604] mov dx, msg.state.4 600 000003AA E8D509 call disp_msg 601 000003AD 58 pop ax 602 000003AE 83C303 add bx, 3 603 000003B1 EBCB jmp @B 604 605 .terminate: 606 000003B3 E8AD09 call restorestate 607 000003B6 B8004C mov ax, 4C00h 608 000003B9 CD21 int 21h 609 610 611 expose_signon: 612 000003BB BA[4F05] mov dx, msg.signon.expose_all 613 000003BE F7C50002 test bp, _all 614 000003C2 7509 jnz @F 615 000003C4 F7C50001 test bp, _setints 616 000003C8 7403 jz @F 617 000003CA BA[1105] mov dx, msg.signon.expose 618 @@: 619 000003CD E8B209 call disp_msg 620 expose: 621 000003D0 F7C50002 test bp, _all 622 000003D4 7506 jnz @F 623 000003D6 F7C50001 test bp, _setints 624 000003DA 750F jnz @FF 625 @@: 626 000003DC 81CD0001 or bp, _setints 627 000003E0 E8A905 call mark_hooked_interrupts 628 000003E3 7306 jnc @F 629 .none: 630 000003E5 BA[D004] mov dx, msg.expose.none 631 000003E8 E9D305 jmp abort_msg 632 633 @@: 634 000003EB E8A601 call uninstall_specific 635 000003EE 85DB test bx, bx 636 000003F0 74F3 jz .none 637 000003F2 E9DE03 jmp ..@exit 638 639 640 capitalise: 641 000003F5 3C61 cmp al, 'a' 642 000003F7 7206 jb .notlowercase 643 000003F9 3C7A cmp al, 'z' 644 000003FB 7702 ja .notlowercase 645 000003FD 24DF and al, ~ 20h ; Uppercase if required 646 .notlowercase: 647 000003FF C3 retn 648 649 650 getnybble: 651 00000400 3C30 cmp al, '0' 652 00000402 7214 jb .invalid ; unknown character --> 653 00000404 3C39 cmp al, '9' 654 00000406 760D jbe @F 655 00000408 E8EAFF call capitalise 656 0000040B 3C41 cmp al, 'A' 657 0000040D 7209 jb .invalid 658 0000040F 3C46 cmp al, 'F' 659 00000411 7705 ja .invalid 660 00000413 2C07 sub al, 'A' - 10 - '0' 661 @@: 662 00000415 2C30 sub al, '0' 663 00000417 A8 db __TEST_IMM8 ; (NC, skip stc) 664 .invalid: 665 00000418 F9 stc 666 00000419 C3 retn 667 668 669 getdecit: 670 0000041A 3C30 cmp al, '0' 671 0000041C 7207 jb .invalid ; unknown character --> 672 0000041E 3C39 cmp al, '9' 673 00000420 7703 ja .invalid 674 00000422 2C30 sub al, '0' 675 00000424 A8 db __TEST_IMM8 ; (NC, skip stc) 676 .invalid: 677 00000425 F9 stc 678 00000426 C3 retn 679 680 681 ; INP: ds:si -> source IISP header (or pseudo header) 682 ; es:di -> destination IISP header 683 ; OUT: EI 684 ; si and di both incremented by 6 685 ; CHG: - 686 ; STT: UP 687 update_iisp_header: 688 00000427 50 push ax 689 00000428 2EA1[040E] mov ax, word [cs:debuggerfunction] 690 0000042C 85C0 test ax, ax ; found the debugger ? 691 0000042E 7408 jz @F ; no --> 692 00000430 CD2D int 2Dh ; call its Update IISP Header function 693 00000432 3CFF cmp al, 0FFh ; supported ? 694 00000434 58 pop ax 695 00000435 7407 je .ret ; yes. done --> 696 00000437 A8 db __TEST_IMM8 ; (skip pop) 697 @@: 698 00000438 58 pop ax ; restore ax, then do manual update 699 00000439 FA cli ; try to rest while updating chain 700 0000043A A7 cmpsw ; skip over first word (entrypoint) 701 ; (generally xxEBh or 0EA90h) 702 0000043B A5 movsw 703 0000043C A5 movsw ; transfer source ieNext to dest ieNext 704 0000043D FB sti 705 .ret: 706 0000043E C3 retn 707 708 709 ; INP: al = interrupt number 710 ; ds:si-> interrupt entry 711 ; OUT: CY if unhooking failed 712 ; NC if unhooking successful 713 ; CHG: ah, es, di, si 714 UnhookInterruptExpose: 715 0000043F E81E00 call UnhookInterruptSim 716 00000442 7203 jc .ret ; bad. --> (CY) 717 00000444 740E jz UnhookInterrupt.easy ; do easy unhooking (Expose) --> 718 00000446 F9 stc ; signal that it is not easy 719 .ret: 720 00000447 C3 retn 721 722 723 ; INP: al = interrupt number 724 ; ds:si-> interrupt entry 725 ; OUT: CY if unhooking failed 726 ; NC if unhooking successful 727 ; CHG: ah, es, di, si 728 UnhookInterrupt: 729 ; UnhookInterruptSim (below) only checks if it's possible to unhook this interrupt. 730 ; This function really unhooks the interrupt if possible. 731 ; 732 ; This is to cover the situation when some of the hooked interrupts can unhook, 733 ; but some can't. If the uninstaller would start to unhook the interrupts and then 734 ; catch the interrupt that can't be unhooked the user would end up with a dead TSR 735 ; that's uninstalled halfway. Very bad. 736 ; 737 ; "Simulating" the unhooking first and checking if all interrupts can unhook 738 ; usually will not return such a state. 739 00000448 E81500 call UnhookInterruptSim 740 0000044B 7212 jc .ret ; bad. --> (CY) 741 0000044D 7405 jz .easy 742 .hard: 743 ; "hard" case: UnhookInterruptSim has however already done the work, 744 ; so the hard case is here indeed easier than the easy case. 745 0000044F E8D5FF call update_iisp_header ; copies our stored pointer into the other's entry 746 00000452 F8 clc 747 00000453 C3 retn 748 .easy: 749 00000454 1E push ds 750 00000455 52 push dx 751 00000456 C55402 lds dx, [ si + 2 ] ; get what we stored in the entry 752 00000459 B425 mov ah, 25h ; easy case - just reset to the value stored 753 0000045B CD21 int 21h ; doesn't alter CF (leaves NC from UnhookInterruptSim) or sets NC 754 0000045D 5A pop dx 755 0000045E 1F pop ds 756 .ret: 757 0000045F C3 retn 758 759 ; INP: ds:si-> IISP entry 760 ; al = interrupt number 761 ; OUT: NC if no error (either hard or easy case), 762 ; ZR if easy case, 763 ; ds:si-> our IISP entry, containing stored interrupt 764 ; NZ if hard case, 765 ; ds:si-> our IISP entry 766 ; es:di-> IISP entry to modify 767 ; CY if error (not first handler and no IISP chain to this handler) 768 ; CHG: ah, es, di 769 UnhookInterruptSim: 770 00000460 53 push bx 771 772 ; harden this, check we are an IISP entry 773 00000461 1E push ds 774 00000462 07 pop es ; es => our handler segment 775 00000463 89F3 mov bx, si ; es:bx -> our handler 776 00000465 E88F08 call IsIISPEntry? ; does it have an IISP header ? 777 00000468 7539 jne .fail ; fail if not 778 779 0000046A B435 mov ah, 35h ; get current vector 780 0000046C CD21 int 21h ; es:bx-> current interrupt handler 781 0000046E 39DE cmp si, bx ; our pointer ? 782 00000470 7511 jne .hard 783 784 00000472 50 push ax 785 00000473 56 push si 786 00000474 8CDE mov si, ds 787 00000476 8CC0 mov ax, es 788 00000478 39C6 cmp si, ax ; our segment ? 789 0000047A 5E pop si 790 0000047B 58 pop ax 791 0000047C 7505 jne .hard 792 793 0000047E 80E400 and ah, 00h ; NC, ZR 794 00000481 5B pop bx 795 00000482 C3 retn 796 .hard: 797 ; INP: ds:si-> IISP entry 798 ; es:bx-> current interrupt entry 799 ; OUT: CY if error 800 ; NC, NZ if no error, 801 ; ds:si-> our IISP entry 802 ; es:di-> IISP entry to modify 803 ; CHG: ah, es, di, (bx) 804 00000483 E86100 call SearchIISPChain 805 00000486 7507 jne .harder 806 .found: ; found reference to our interrupt handler 807 00000488 89DF mov di, bx ; es:di-> IISP entry that references our's 808 0000048A 80CCFF or ah, 0FFh ; NC, NZ 809 0000048D 5B pop bx 810 0000048E C3 retn 811 812 .harder: ; Desperate attempt to find IISP entry that references ours by 813 ; searching through the interrupts hooked by other AMIS TSRs. Note 814 ; that the plexer loop will find and search through the list of 815 ; hooked interrupts of the uninstalling TSR itself, but this causes 816 ; no trouble. 817 ; INP: ds:si-> IISP entry 818 ; OUT: CY if error 819 ; NC, NZ if no error, 820 ; ds:si-> our IISP entry 821 ; es:di-> IISP entry to modify 822 ; CHG: ah, es, di, (bx) 823 d4bp 824 0000048F 52 push dx 825 00000490 50 push ax ; register with interrupt number last 826 00000491 31C0 xor ax, ax 827 .loopplex: 828 00000493 B000 mov al, 00h ; AMIS installation check 829 00000495 51 push cx 830 00000496 CD2D int 2Dh ; enquire whether there's anyone 831 00000498 59 pop cx ; but we don't care who it might be 832 00000499 FEC0 inc al 833 0000049B 7409 jz .search 834 .nextplex: 835 0000049D FEC4 inc ah 836 0000049F 75F2 jnz .loopplex ; try next multiplexer --> 837 000004A1 58 pop ax 838 000004A2 5A pop dx 839 .fail: ; IISP incompatible TSR between current interrupt entry and our entry 840 ; and no AMIS compatible TSR installed on top of our entry 841 000004A3 F9 stc 842 000004A4 5B pop bx 843 000004A5 C3 retn 844 845 ; INP: ah = multiplex number of AMIS TSR to search through 846 ; ss:sp-> interrupt number (byte), must be preserved 847 ; CHG: es, di, dx, bx 848 .search: 849 000004A6 B004 mov al, 04h 850 000004A8 5B pop bx 851 000004A9 53 push bx ; low byte is the interrupt number 852 000004AA CD2D int 2Dh 853 000004AC 3C03 cmp al, 03h ; returned its interrupt entry ? 854 ; RBIL doesn't explicitly state that this interrupt entry has to 855 ; be IISP compatible. But I'm too lazy to look up the older AMIS, 856 ; and SearchIISPChain checks the interrupt entry anyway. 857 000004AE 7428 je .search_dxbx 858 000004B0 3C04 cmp al, 04h ; returned list of hooked interrupts ? 859 000004B2 75E9 jne .nextplex ; no, try next multiplexer --> 860 000004B4 89DF mov di, bx 861 000004B6 5B pop bx 862 000004B7 53 push bx ; bl = interrupt number 863 000004B8 88D8 mov al, bl 864 .search_intlist_seg: 865 000004BA 8EC2 mov es, dx ; es:di-> list 866 .search_intlist: ; Search the returned list for the required interrupt number. 867 000004BC AE scasb ; our interrupt number ? 868 000004BD 740A je .search_found_intlist 869 000004BF 26807DFF2D cmp byte [es:di-1], 2Dh ; was last in list ? 870 000004C4 74D7 je .nextplex 871 000004C6 AF scasw ; skip pointer 872 000004C7 EBF3 jmp short .search_intlist ; try next entry --> 873 .search_found_intlist: 874 000004C9 268B1D mov bx, word [es:di] ; dx:bx-> IISP entry 875 000004CC AF scasw ; skip pointer 876 000004CD E81700 call SearchIISPChain 877 000004D0 740D je .search_found ; found entry --> 878 ; This specific jump supports TSRs that hook the same 879 ; interrupt more than once; jumping to .nextplex instead 880 ; (as previously) aborts the search after the first match 881 ; in the interrupt list. This support might become useful. 882 000004D2 3C2D cmp al, 2Dh ; was last in list ? 883 000004D4 74C7 je .nextplex 884 000004D6 EBE2 jmp short .search_intlist_seg 885 886 .search_dxbx: 887 000004D8 8EC2 mov es, dx ; es:bx-> (IISP) interrupt entry 888 ; The entry we found now is possibly behind the non-IISP entry that 889 ; terminated our first SearchIISPChain call (at .hard). We then 890 ; possibly might find our entry in this hidden part of the chain. 891 000004DA E80A00 call SearchIISPChain 892 000004DD 75BE jne .nextplex ; didn't find our entry in the chain --> 893 .search_found: 894 000004DF 58 pop ax 895 000004E0 5A pop dx 896 000004E1 EBA5 jmp short .found 897 898 899 SearchIISPChain.next: 900 000004E3 26C45F02 les bx, [es:bx +2] ; get next interrupt entry 901 902 ; INP: ds:si-> IISP entry 903 ; es:bx-> current interrupt entry 904 ; OUT: NZ if reference to ds:si not found in IISP chain es:bx-> 905 ; ZR if reference found, 906 ; es:bx-> IISP (or uninstalled iHPFS) interrupt entry with reference 907 ; CHG: es, bx 908 SearchIISPChain: 909 000004E7 E80D08 call IsIISPEntry? ; that an IISP entry ? 910 000004EA 7510 jnz .return ; nope --> (NZ) 911 000004EC 263B7702 cmp si, word [ es:bx + ieNext ] ; our pointer ? 912 000004F0 75F1 jne .next ; no, try next --> 913 000004F2 50 push ax 914 000004F3 8CD8 mov ax, ds 915 000004F5 263B4704 cmp ax, word [ es:bx + ieNext + 2] ; our segment ? 916 000004F9 58 pop ax 917 000004FA 75E7 jne .next ; no, try next --> 918 .return: ; yes, found (ZR) 919 000004FC C3 retn 920 921 922 ; If unhooking (or hooking) failed after the simulation succeeded for all 923 ; interrupts, at least one interrupt changed between the simulation and the 924 ; attempt to really (un)hook that interrupt. This is kind of a critical 925 ; error because it'll happen very rarely, and I'd be interested to hear 926 ; about software with which it does happen. 927 unhookerror: 928 000004FD 31C9 xor cx, cx ; (don't display critical failure message) 929 unhookerrorcritical: 930 000004FF BA[6600] mov dx, msg.cantuninstall 931 .install: 932 00000502 E87D08 call disp_msg 933 934 00000505 5A pop dx 935 00000506 83FAFF cmp dx, byte -1 ; at least another interrupt failed ? (at least two interrupts failed ?) 936 00000509 52 push dx 937 0000050A 7405 je .single 938 0000050C BA[9802] mov dx, msg.interrupts ; first message for multiple interrupts 939 0000050F EB03 jmp short .first 940 .single: 941 00000511 BA[A602] mov dx, msg.interrupt ; first message for single interrupt 942 .first: 943 00000514 89D3 mov bx, dx 944 945 .loop: 946 00000516 E86908 call disp_msg ; msg.interrupts or msg.hcomma 947 00000519 E87808 call disp_al_hex 948 0000051C 58 pop ax ; get next value from stack 949 0000051D 5A pop dx 950 0000051E 83FAFF cmp dx, byte -1 ; at least two more ? 951 00000521 52 push dx 952 00000522 BA[BA02] mov dx, msg.hcomma ; message if at least two more interrupts to display 953 00000525 7503 jne .notlast ; yes --> 954 00000527 BA[B302] mov dx, msg.hand ; only one more interrupt to display 955 .notlast: 956 0000052A 83F8FF cmp ax, byte -1 ; last error marker? 957 0000052D 75E7 jne .loop ; no, keep looping --> 958 959 0000052F BA[BE02] mov dx, msg.hooked ; last message for single interrupt 960 00000532 81FB[A602] cmp bx, msg.interrupt ; was first message for single interrupt ? 961 00000536 7403 je .abort ; yes --> 962 00000538 BA[BE02] mov dx, msg.hookeds ; else use last message for multiple interrupts 963 .abort: 964 0000053B E306 jcxz .abort_msg ; not critical (or a critical installation failure) --> 965 0000053D E84208 call disp_msg 966 00000540 BA[F401] mov dx, msg.criticalunins ; else display critical failure message 967 .abort_msg: 968 00000543 E97804 jmp abort_msg 969 970 971 uninstall: 972 d4bp 973 00000546 F7C50010 test bp, _foundresident 974 0000054A 7506 jnz .found 975 0000054C BA[5500] mov dx, msg.notinstalled ; nothing to uninstall there 976 .abort_msg: 977 0000054F E96C04 jmp abort_msg 978 979 .found: 980 00000552 F7C50020 test bp, _incompatresident 981 00000556 7405 jz .compat 982 00000558 BA[3403] mov dx, msg.incompatible ; resident, but incompatible version 983 0000055B EBF2 jmp short .abort_msg 984 985 .compat: 986 0000055D F7C50002 test bp, _all 987 00000561 7403E99301 jnz uninstall_completely 988 00000566 F7C50001 test bp, _setints 989 0000056A 7503E98A01 jz uninstall_completely 990 991 0000056F BA[A805] mov dx, msg.signon.uninstall_specific 992 00000572 F7C52000 test bp, _cover 993 00000576 7403 jz @F 994 00000578 BA[7005] mov dx, msg.signon.uninstall_specific_cover 995 @@: 996 0000057B E80408 call disp_msg 997 998 0000057E E81300 call uninstall_specific 999 00000581 F7C52000 test bp, _cover 1000 00000585 7403E9A702 jnz cover 1001 0000058A 85C9 test cx, cx 1002 0000058C 7403E93004 jnz abort 1003 00000591 E93F02 jmp ..@exit 1004 1005 1006 uninstall_specific: 1007 00000594 31C9 xor cx, cx 1008 00000596 51 push cx 1009 00000597 51 push cx 1010 .loop: 1011 00000598 51 push cx 1012 00000599 89CB mov bx, cx 1013 0000059B E8D103 call bx_intnum_to_dh_bx_intsel 1014 0000059E 59 pop cx 1015 0000059F 2E84B7[400E] test byte [cs:selectedinterrupts + bx], dh 1016 000005A4 7426 jz .next 1017 000005A6 E82A00 call uninstall_handler 1018 000005A9 5E pop si 1019 000005AA 5F pop di 1020 000005AB 09D6 or si, dx 1021 000005AD 09DF or di, bx 1022 000005AF 57 push di 1023 000005B0 56 push si 1024 000005B1 09DA or dx, bx 1025 000005B3 7517 jnz .next 1026 000005B5 F7C52000 test bp, _cover 1027 000005B9 7511 jnz .next 1028 000005BB BA[7504] mov dx, msg.uninstall_specific.none_1 1029 000005BE E8C107 call disp_msg 1030 000005C1 88C8 mov al, cl 1031 000005C3 E8CE07 call disp_al_hex 1032 000005C6 BA[B604] mov dx, msg.uninstall_specific.none_2 1033 000005C9 E8B607 call disp_msg 1034 .next: 1035 000005CC FEC1 inc cl 1036 000005CE 75C8 jnz .loop 1037 000005D0 59 pop cx 1038 000005D1 5B pop bx 1039 000005D2 C3 retn 1040 1041 1042 ; INP: ah = multiplex number 1043 ; cl = interrupt number 1044 ; bp = command line flags 1045 ; OUT: bx = success count 1046 ; dx = error-or-failure count 1047 ; CHG: al, di, si, es, ds 1048 uninstall_handler: 1049 lframe near 1050 000005D3 5589E5 lenter 1051 000005D6 31DB xor bx, bx 1052 000005D8 53 push bx 1053 lvar word, ignore 1054 000005D9 53 push bx 1055 lvar word, success 1056 000005DA 53 push bx 1057 lvar word, failure 1058 .outerloop: 1059 000005DB B004 mov al, 4 1060 000005DD B32D mov bl, 2Dh 1061 000005DF CD2D int 2Dh 1062 000005E1 3C04 cmp al, 4 1063 000005E3 7403E90601 jne .error 1064 000005E8 8EC2 mov es, dx 1065 .loop: 1066 000005EA 268A07 mov al, byte [es:bx] 1067 000005ED 43 inc bx 1068 000005EE 268B3F mov di, word [es:bx] 1069 000005F1 43 inc bx 1070 000005F2 43 inc bx 1071 000005F3 3C2D cmp al, 2Dh 1072 000005F5 7503E9EE00 je .end 1073 000005FA 38C8 cmp al, cl 1074 000005FC 75EC jne .loop 1075 000005FE 1E push ds 1076 000005FF 06 push es 1077 00000600 57 push di 1078 00000601 89FE mov si, di 1079 00000603 06 push es 1080 00000604 1F pop ds 1081 00000605 50 push ax 1082 00000606 F6460010 testopt [bp + ?frame_bp], _expose 1083 0000060A 7405 jz @F 1084 0000060C E830FE call UnhookInterruptExpose 1085 0000060F EB03 jmp @FF 1086 @@: 1087 00000611 E834FE call UnhookInterrupt 1088 @@: 1089 00000614 58 pop ax 1090 00000615 5F pop di 1091 00000616 07 pop es 1092 00000617 1F pop ds 1093 00000618 7327 jnc .success 1094 .fail: 1095 0000061A 837EFE00 cmp word [bp + ?ignore], 0 1096 0000061E 7405 je @F 1097 00000620 FF4EFE dec word [bp + ?ignore] 1098 00000623 EBC5 jmp .loop 1099 1100 @@: 1101 00000625 FF46FA inc word [bp + ?failure] 1102 00000628 F6460010 testopt [bp + ?frame_bp], _expose 1103 0000062C 75BC jnz .loop 1104 0000062E 52 push dx 1105 0000062F BA[7504] mov dx, msg.uninstall_handler.fail_1 1106 00000632 E84D07 call disp_msg 1107 00000635 E85C07 call disp_al_hex 1108 00000638 BA[8004] mov dx, msg.uninstall_handler.fail_2 1109 0000063B E84407 call disp_msg 1110 0000063E 5A pop dx 1111 0000063F EBA9 jmp .loop 1112 1113 .success: 1114 00000641 837EFE00 cmp word [bp + ?ignore], 0 1115 00000645 7403 je @F 1116 00000647 FF4EFE dec word [bp + ?ignore] 1117 @@: 1118 0000064A 51 push cx 1119 0000064B 52 push dx 1120 0000064C FF46FC inc word [bp + ?success] 1121 0000064F BA[7504] mov dx, msg.uninstall_handler.success_1 1122 00000652 E82D07 call disp_msg 1123 00000655 E83C07 call disp_al_hex 1124 00000658 BA[9B04] mov dx, msg.uninstall_handler.success_2 1125 0000065B E82407 call disp_msg 1126 0000065E 57 push di 1127 0000065F 8D7FFD lea di, [bx - 3] ; es:di -> list entry to overwrite 1128 00000662 06 push es 1129 00000663 1F pop ds 1130 00000664 89DE mov si, bx ; ds:si -> next list entry 1131 @@: 1132 00000666 803F2D cmp byte [bx], 2Dh ; end of list yet ? 1133 00000669 8D5F03 lea bx, [bx + 3] ; -> behind next list entry (next next) 1134 0000066C 75F8 jne @B ; no, loop --> 1135 0000066E 89D9 mov cx, bx ; -> after end entry 1136 00000670 29F1 sub cx, si ; cx = length of trailer 1137 00000672 F3A4 rep movsb ; relocate trailing int list 1138 00000674 B000 mov al, 0 1139 00000676 AA stosb ; clear freed entry 1140 00000677 AA stosb 1141 00000678 AA stosb 1142 00000679 5F pop di ; -> IISP entrypoint 1143 0000067A 57 push di 1144 0000067B B91800 mov cx, 24 1145 0000067E F3AA rep stosb ; clear it 1146 00000680 5F pop di ; -> free block 1147 00000681 5E pop si ; = handler segment 1148 00000682 59 pop cx ; whatever 1149 1150 00000683 B020 mov al, 20h 1151 00000685 CD2D int 2Dh ; get ctrl0 1152 00000687 3C08 cmp al, heapfree_offset + 4 - ctrl0 1153 ; does it include the free offset/size ? 1154 00000689 7263 jb .error 1155 0000068B 39F2 cmp dx, si ; does it match the segment ? 1156 0000068D 755F jne .error 1157 1158 0000068F 83C304 add bx, heapfree_offset - ctrl0 ; word [ds:bx] = next offset 1159 ; word [ds:bx + 2] = placeholder size (0) 1160 00000692 EB07 jmp @FF 1161 1162 @@: 1163 00000694 833FFF cmp word [bx], -1 ; is terminator ? 1164 00000697 7455 je .error ; shouldn't see this here 1165 00000699 8B1F mov bx, word [bx] ; ds:bx -> next block 1166 @@: 1167 0000069B 3B3F cmp di, word [bx] ; is our block to free below their next ? 1168 0000069D 73F5 jae @BB ; no, loop --> 1169 ; heapfree -> 4 -> 8 1170 ; freeing 2 1171 ; 2 >= 4 ? no, return bx = heapfree 1172 ; freeing 6 1173 ; 6 >= 4 ? yes, go to 4 1174 ; 6 >= 8 ? no, return bx = 4 1175 ; freeing 10 1176 ; 10 >= 4 ? yes, go to 4 1177 ; 10 >= 8 ? yes, go to 8 1178 ; word [bx] == -1, return bx = 8 1179 ; di is Below next pointer in word [ds:bx] 1180 ; bx is Below di 1181 ; ds:bx -> free block before ours 1182 1183 0000069F 8B5702 mov dx, [bx + 2] ; get size of this free block 1184 000006A2 E8DA02 call dx_units_to_bytes 1185 000006A5 01DA add dx, bx ; -> end of this free block 1186 1187 000006A7 39FA cmp dx, di ; consecutive ? 1188 000006A9 740D je @F ; yes --> 1189 000006AB FF37 push word [bx] ; get next offset (may be -1) 1190 000006AD 8F05 pop word [di] ; write into our freed block 1191 000006AF C745020100 mov word [di + 2], 1 ; set size of our freed block 1192 000006B4 893F mov word [bx], di ; link prior free block to ours 1193 ; ds:di -> free block 1194 000006B6 EB05 jmp @FF 1195 1196 @@: ; freed block follows consecutively 1197 000006B8 FF4702 inc word [bx + 2] ; increase length 1198 000006BB 89DF mov di, bx ; for trail check: ds:di -> free block 1199 @@: 1200 000006BD 8B1D mov bx, word [di] ; get next block 1201 000006BF 83FBFF cmp bx, -1 ; none ? 1202 000006C2 741B je @F ; yes, done --> 1203 000006C4 8B5502 mov dx, word [di + 2] ; size of leading free block 1204 000006C7 E8B502 call dx_units_to_bytes 1205 000006CA 01FA add dx, di ; -> end of this free block 1206 1207 000006CC 39DA cmp dx, bx ; is following block consecutive ? 1208 000006CE 750F jne @F ; no, done --> 1209 1210 000006D0 31D2 xor dx, dx ; clean up the following block 1211 000006D2 875702 xchg dx, word [bx + 2] ; get size 1212 000006D5 FF37 push word [bx] ; get next block after following (may be -1) 1213 000006D7 8F05 pop word [di] ; link in next block 1214 000006D9 015502 add word [di + 2], dx ; increase size of our block 1215 000006DC 832700 and word [bx], 0 ; clean up 1216 1217 @@: 1218 000006DF FF76FA push word [bp + ?failure] 1219 000006E2 8F46FE pop word [bp + ?ignore] 1220 000006E5 E9F3FE jmp .outerloop ; try for another handler of the same int 1221 1222 .end: 1223 000006E8 5A pop dx ; ?failure 1224 000006E9 5B pop bx ; ?success 1225 000006EA 89EC5D lleave code 1226 000006ED C3 lret 1227 1228 .error: 1229 000006EE BA[5504] mov dx, msg.uninstall_handler.error 1230 000006F1 E88E06 call disp_msg 1231 000006F4 FF46FA inc word [bp + ?failure] 1232 000006F7 EBEF jmp .end 1233 1234 lleave ctx 1235 1236 1237 uninstall_completely: 1238 000006F9 BA[1306] mov dx, msg.signon.uninstall_completely_all 1239 000006FC F7C50002 test bp, _all 1240 00000700 7503 jnz @F 1241 00000702 BA[3006] mov dx, msg.signon.uninstall_completely 1242 @@: 1243 00000705 F7C52000 test bp, _cover 1244 00000709 7403 jz @F 1245 0000070B BA[D705] mov dx, msg.signon.uninstall_completely_cover 1246 @@: 1247 0000070E E87106 call disp_msg 1248 1249 00000711 81CD0001 or bp, _setints 1250 00000715 E87402 call mark_hooked_interrupts 1251 00000718 7315 jnc @FF 1252 0000071A F7C52002 test bp, _cover | _all 1253 0000071E 7446 jz .ready 1254 00000720 BA[1201] mov dx, msg.uninstall.none 1255 00000723 F7C52000 test bp, _cover 1256 00000727 7403 jz @F 1257 00000729 BA[DC00] mov dx, msg.uninstall_cover.none 1258 @@: 1259 0000072C E98F02 jmp abort_msg 1260 1261 @@: 1262 0000072F E862FE call uninstall_specific 1263 1264 00000732 F7C52002 test bp, _cover | _all 1265 00000736 7410 jz .checkready 1266 00000738 81CD0001 or bp, _setints 1267 0000073C F7C52000 test bp, _cover 1268 00000740 7403E90701 jnz cover.already_marked 1269 00000745 E98B00 jmp ..@exit 1270 1271 .checkready: 1272 00000748 B32D mov bl, 2Dh 1273 0000074A B004 mov al, 4 1274 0000074C CD2D int 2Dh 1275 0000074E 8EC2 mov es, dx 1276 00000750 3C04 cmp al, 4 1277 00000752 BA[A800] mov dx, msg.uninstall_no_list 1278 00000755 7403E96402 jne abort_msg 1279 1280 0000075A 26803F2D cmp byte [es:bx], 2Dh 1281 0000075E 7406 je .ready 1282 00000760 BA[7300] mov dx, msg.uninstall_unhook_failed 1283 00000763 E95802 jmp abort_msg 1284 1285 .ready: 1286 ; ah = AMIS multiplex number of resident copy 1287 00000766 B002 mov al, 02h 1288 00000768 8CCA mov dx, cs 1289 0000076A BB[CD07] mov bx, .done ; dx:bx = return address if successful 1290 0000076D CD2D int 2Dh ; AMIS uninstall TSR 1291 0000076F 3CFF cmp al, 0FFh ; 0FFh successful 1292 00000771 745A je .done ; TSR has already done everything --> 1293 00000773 84C0 test al, al ; 00h not implemented 1294 00000775 741A jz .continue_noseg ; do it myself --> 1295 00000777 3C03 cmp al, 03h ; 03h safe, no resident uninstaller (still enabled). bx=segment 1296 00000779 7418 je .continue_seg ; expected --> 1297 0000077B 3C04 cmp al, 04h ; 04h safe, no resident uninstaller (now disabled). bx=segment 1298 0000077D 7414 je .continue_seg ; unexpected, but continue --> 1299 ; (other values: 01h unsuccessful or internal, 02h can't uninstall yet, 1300 ; but will do so when able, 05h not safe, 06h,07h device driver; all fail) 1301 .fail: 1302 0000077F BA[6600] mov dx, msg.cantuninstall ; some error occured 1303 00000782 E8FD05 call disp_msg 1304 00000785 B22E mov dl, '.' 1305 00000787 B402 mov ah, 02h 1306 00000789 CD21 int 21h ; display dot to end message 1307 0000078B BA[5309] mov dx, msg.crlf 1308 .abort_msg_1: 1309 0000078E E92D02 jmp abort_msg 1310 1311 .continue_noseg: 1312 00000791 31DB xor bx, bx ; = 0 1313 1314 .continue_seg: 1315 00000793 53 push bx 1316 1317 00000794 B004 mov al, 04h 1318 00000796 B32D mov bl, 2Dh ; Dummy, for the API. We only accept code 04h. 1319 00000798 CD2D int 2Dh ; AMIS determine hooked interrupts 1320 0000079A 3C04 cmp al, 04h 1321 0000079C 75E1 jne .fail ; General uninstallers should be prepared for at least 04h or 03h 1322 ; as return code here. 1323 0000079E 8EDA mov ds, dx ; ds:bx-> interrupt table 1324 000007A0 8A07 mov al, byte [bx] 1325 000007A2 8B7701 mov si, word [bx + 1] 1326 000007A5 3C2D cmp al, 2Dh 1327 000007A7 75D6 jne .fail 1328 1329 000007A9 BAFFFF mov dx, -1 1330 000007AC 52 push dx 1331 1332 000007AD E8B0FC call UnhookInterruptSim 1333 000007B0 7303E948FD jc unhookerror 1334 1335 000007B5 E890FC call UnhookInterrupt 1336 000007B8 7303E942FD jc unhookerrorcritical ; (display "critical error") 1337 1338 000007BD 58 pop ax ; (discard) 1339 1340 000007BE 1E push ds 1341 000007BF 07 pop es ; es = segment of interrupt list 1342 000007C0 58 pop ax ; stacked segment 1343 000007C1 85C0 test ax, ax ; marker to use interrupt list segment ? (zero) 1344 000007C3 7402 jz .uselistsegment ; yes --> 1345 000007C5 8EC0 mov es, ax ; else use segment returned by uninstall function 1346 1347 .uselistsegment: 1348 000007C7 B449 mov ah, 49h ; Free memory 1349 000007C9 CD21 int 21h 1350 ; General deinstallation code should not assume the TSR was just in 1351 ; that single memory block. If the deinstallation code doesn't know 1352 ; the TSR, it should search (and if it finds any, free) memory blocks 1353 ; that have the MCB owner either that's now in es (code segment of TSR) 1354 ; or the same MCB owner value that the TSR's code segment's MCB had. 1355 ; (Only if the TSR's code segment was a valid memory block with MCB.) 1356 000007CB 72B2 jc .fail ; if that causes an error, still report "fail" 1357 ; (but interrupts are already unhooked now) 1358 1359 .done: 1360 000007CD BA[6B03] mov dx, msg.removed 1361 000007D0 E8AF05 call disp_msg ; "removed." 1362 ..@stack equ $ - (($-$$) % 2) ; (above is overwritten by stack while installing) 1363 ..@exit: ; terminate successful, re-used by some code (install too) 1364 000007D3 E88D05 call restorestate 1365 000007D6 B8004C mov ax, 4C00h 1366 000007D9 CD21 int 21h 1367 1368 hookerror: 1369 000007DB B9[BE09] mov cx, abort_msg 1370 ; jmp short displayhookerror 1371 displayhookerror: 1372 000007DE BA[8A02] mov dx, msg.cantinstall 1373 000007E1 E89E05 call disp_msg 1374 1375 000007E4 5A pop dx 1376 000007E5 83FAFF cmp dx, byte -1 ; at least another interrupt failed ? (at least two interrupts failed ?) 1377 000007E8 52 push dx 1378 000007E9 7405 je .single 1379 000007EB BA[9802] mov dx, msg.interrupts ; first message for multiple interrupts 1380 000007EE EB03 jmp short .first 1381 .single: 1382 000007F0 BA[A602] mov dx, msg.interrupt ; first message for single interrupt 1383 .first: 1384 000007F3 89D3 mov bx, dx 1385 1386 .loop: 1387 000007F5 E88A05 call disp_msg ; msg.interrupts or msg.hcomma 1388 000007F8 E89905 call disp_al_hex 1389 000007FB 58 pop ax ; get next value from stack 1390 000007FC 5A pop dx 1391 000007FD 83FAFF cmp dx, byte -1 ; at least two more ? 1392 00000800 52 push dx 1393 00000801 BA[BA02] mov dx, msg.hcomma ; message if at least two more interrupts to display 1394 00000804 7503 jne .notlast ; yes --> 1395 00000806 BA[B302] mov dx, msg.hand ; only one more interrupt to display 1396 .notlast: 1397 00000809 83F8FF cmp ax, byte -1 ; last error marker? 1398 0000080C 75E7 jne .loop ; no, keep looping --> 1399 1400 0000080E BA[DC02] mov dx, msg.invalid ; last message for single interrupt 1401 00000811 81FB[A602] cmp bx, msg.interrupt ; was first message for single interrupt ? 1402 00000815 7403 je .ret ; yes --> 1403 00000817 BA[DC02] mov dx, msg.invalids ; else use last message for multiple interrupts 1404 .ret: 1405 0000081A FFE1 jmp cx 1406 1407 1408 cover_signon: 1409 0000081C BA[2E05] mov dx, msg.signon.cover_all 1410 0000081F F7C50002 test bp, _all 1411 00000823 7509 jnz @F 1412 00000825 F7C50001 test bp, _setints 1413 00000829 7403 jz @F 1414 .not_all: 1415 0000082B BA[F404] mov dx, msg.signon.cover 1416 @@: 1417 0000082E E85105 call disp_msg 1418 cover: 1419 00000831 F7C50002 test bp, _all 1420 00000835 7506 jnz @F 1421 00000837 F7C50001 test bp, _setints 1422 0000083B 750F jnz @FF 1423 @@: 1424 0000083D 81CD0001 or bp, _setints 1425 00000841 E84801 call mark_hooked_interrupts 1426 00000844 7306 jnc @F 1427 00000846 BA[DC00] mov dx, msg.uninstall_cover.none 1428 00000849 E97201 jmp abort_msg 1429 1430 .already_marked: 1431 @@: 1432 0000084C 31C9 xor cx, cx 1433 0000084E 51 push cx 1434 .loop: 1435 0000084F 51 push cx 1436 00000850 89CB mov bx, cx 1437 00000852 E81A01 call bx_intnum_to_dh_bx_intsel 1438 00000855 59 pop cx 1439 00000856 2E84B7[400E] test byte [cs:selectedinterrupts + bx], dh 1440 0000085B 7503E9EC00 jz .next 1441 00000860 88C8 mov al, cl 1442 00000862 E83B04 call HookInterruptCover 1443 00000865 7303E9E200 jc .next 1444 1445 0000086A B020 mov al, 20h 1446 0000086C CD2D int 2Dh ; get ctrl0 1447 0000086E 3C08 cmp al, heapfree_offset + 4 - ctrl0 1448 ; does it include the free offset/size ? 1449 00000870 7303E9E600 jb .error_ctrl0 1450 00000875 8EDA mov ds, dx 1451 1452 00000877 53 push bx 1453 00000878 8B5702 mov dx, word [amisintr.length - ctrl0 + bx] 1454 0000087B 8B1F mov bx, word [amisintr.offset - ctrl0 + bx] 1455 0000087D E8FF00 call dx_units_to_bytes 1456 00000880 01DA add dx, bx 1457 @@: 1458 00000882 803F2D cmp byte [bx], 2Dh 1459 00000885 8D5F03 lea bx, [bx + 3] 1460 00000888 75F8 jne @B 1461 0000088A 39D3 cmp bx, dx 1462 0000088C 89DE mov si, bx 1463 0000088E 5B pop bx 1464 0000088F 7203E9CC00 jae .error_no_list_entry 1465 1466 00000894 83C304 add bx, heapfree_offset - ctrl0 ; word [ds:bx] = next offset 1467 ; word [ds:bx + 2] = placeholder size (0) 1468 1469 00000897 89DF mov di, bx 1470 00000899 57 push di 1471 0000089A 8B1F mov bx, word [bx] ; ds:bx -> next block 1472 0000089C 83FBFF cmp bx, -1 ; is terminator ? 1473 0000089F 7503E9C100 je .error_no_block 1474 000008A4 FF7702 push word [bx + 2] 1475 000008A7 FF37 push word [bx] 1476 000008A9 837F0201 cmp word [bx + 2], 1 1477 000008AD 7303E9B800 jb .error_internal 1478 000008B2 7417 je .alloc_block_single 1479 .alloc_block_multiple: 1480 000008B4 FF4F02 dec word [bx + 2] ; decrement size 1481 000008B7 FF7702 push word [bx + 2] ; get new size 1482 000008BA FF37 push word [bx] ; get next 1483 000008BC 83C318 add bx, 24 ; -> subsequent blocks of ours 1484 000008BF 8F07 pop word [bx] ; set next 1485 000008C1 8F4702 pop word [bx + 2] ; set size 1486 000008C4 891D mov word [di], bx ; update reference in prior block 1487 000008C6 83EB18 sub bx, 24 ; -> allocated block 1488 000008C9 EB04 jmp @F 1489 1490 .alloc_block_single: 1491 000008CB FF37 push word [bx] ; get next 1492 000008CD 8F05 pop word [di] ; update reference in prior block 1493 1494 @@: 1495 000008CF 89F2 mov dx, si 1496 000008D1 1E push ds 1497 000008D2 07 pop es 1498 000008D3 89DF mov di, bx ; es:di -> allocated block 1499 1500 000008D5 51 push cx 1501 000008D6 06 push es 1502 000008D7 57 push di 1503 000008D8 0E push cs 1504 000008D9 1F pop ds 1505 000008DA BE[280E] mov si, intrtemplate ; ds:si -> template 1506 000008DD B90C00 mov cx, 24 / 2 1507 000008E0 F3A5 rep movsw 1508 000008E2 5E pop si 1509 000008E3 1F pop ds ; ds:si -> allocated + init block 1510 000008E4 8D4C02 lea cx, [si + ieNext] 1511 000008E7 894C15 mov word [si + intrtemplate.next_offset - intrtemplate], cx 1512 ; fix the absolute offset 1513 000008EA 59 pop cx 1514 000008EB 88C8 mov al, cl ; = interrupt number 1515 000008ED 50 push ax 1516 000008EE E85003 call HookInterrupt 1517 000008F1 58 pop ax 1518 000008F2 7227 jc .undo 1519 000008F4 89D7 mov di, dx ; di -> after int list 1520 000008F6 C6052D mov byte [di], 2Dh ; re-init new list entry 1521 000008F9 8B55FE mov dx, word [di - 3 + 1] ; get their i2D offset 1522 000008FC 895501 mov word [di + 1], dx ; write their i2D offset 1523 000008FF 8975FE mov word [di - 3 + 1], si ; write our new handler offset 1524 00000902 8845FD mov byte [di - 3], al ; set the int number 1525 00000905 83C406 add sp, 6 ; discard the allocation undo 1526 1527 00000908 BA[4401] mov dx, msg.cover.done_1 1528 0000090B E87404 call disp_msg 1529 0000090E 88C8 mov al, cl 1530 00000910 E88104 call disp_al_hex 1531 00000913 BA[4F01] mov dx, msg.cover.done_2 1532 00000916 E86904 call disp_msg 1533 1534 00000919 EB31 jmp .next 1535 1536 .undo: 1537 ; undo the allocation 1538 0000091B 8F07 pop word [bx] ; restore old link (next after allocated) 1539 0000091D 5F pop di ; get old size 1540 0000091E 897F02 mov word [bx + 2], di ; restore it 1541 00000921 83FF01 cmp di, 1 ; ! was a single block ? 1542 00000924 5F pop di ; -> prior block 1543 00000925 51 push cx 1544 00000926 50 push ax 1545 00000927 B90A00 mov cx, (24 - 4) >> 1 ; clear block except link and size 1546 0000092A 7602 jbe @F ; ! yes, cx is right --> 1547 0000092C 41 inc cx ; +1 word = link 1548 0000092D 41 inc cx ; +1 word = size 1549 @@: 1550 0000092E 1E push ds 1551 0000092F 07 pop es 1552 00000930 891D mov word [di], bx ; restore prior block's link 1553 00000932 8D7F04 lea di, [bx + 4] ; -> behind link and size 1554 00000935 31C0 xor ax, ax 1555 00000937 F3AB rep stosw ; zero 1556 00000939 58 pop ax 1557 0000093A 59 pop cx 1558 1559 0000093B BA[4401] mov dx, msg.cover.undo_1 1560 0000093E E84104 call disp_msg 1561 00000941 88C8 mov al, cl 1562 00000943 E84E04 call disp_al_hex 1563 00000946 BA[5C01] mov dx, msg.cover.undo_2 1564 00000949 E83604 call disp_msg 1565 1566 .next: 1567 0000094C FEC1 inc cl 1568 0000094E 7403E9FCFE jnz .loop 1569 00000953 59 pop cx 1570 00000954 85C9 test cx, cx 1571 00000956 7569 jnz abort 1572 00000958 E978FE jmp ..@exit 1573 1574 .error_ctrl0: 1575 0000095B BA[6901] mov dx, msg.cover.ctrl0 1576 .abort_msg: 1577 0000095E EB5E jmp abort_msg 1578 1579 .error_no_list_entry: 1580 00000960 BA[9201] mov dx, msg.cover.no_list_entry 1581 00000963 EBF9 jmp .abort_msg 1582 1583 .error_no_block: 1584 00000965 BA[B901] mov dx, msg.cover.no_block 1585 00000968 EBF4 jmp .abort_msg 1586 1587 .error_internal: 1588 0000096A BA[D101] mov dx, msg.cover.error_internal 1589 0000096D EBEF jmp .abort_msg 1590 1591 1592 ; INP: bx = interrupt number 0..255 1593 ; OUT: dh = mask, bx = offset into selectedinterrupts 1594 ; CHG: cl 1595 bx_intnum_to_dh_bx_intsel: 1596 0000096F 88D9 mov cl, bl 1597 00000971 80E107 and cl, 7 1598 00000974 D1EB shr bx, 1 1599 00000976 D1EB shr bx, 1 1600 00000978 D1EB shr bx, 1 1601 0000097A B601 mov dh, 1 1602 0000097C D2E6 shl dh, cl 1603 0000097E C3 retn 1604 1605 1606 dx_units_to_bytes: 1607 0000097F 01D2 add dx, dx ; * 2 1608 00000981 01D2 add dx, dx ; * 4 1609 00000983 01D2 add dx, dx ; * 8 1610 00000985 89D6 mov si, dx ; si = times 8 1611 00000987 01D2 add dx, dx ; * 16 1612 00000989 01F2 add dx, si ; * 24 1613 0000098B C3 retn 1614 1615 1616 ; INP: ah = multiplex number 1617 ; OUT: CY if no interrupts (other than 2Dh) hooked 1618 ; NC else, bits ORed into selectedinterrupts 1619 ; CHG: al, bx, cx, dx, si, di, es 1620 mark_hooked_interrupts: 1621 0000098C B32D mov bl, 2Dh 1622 0000098E B004 mov al, 4 1623 00000990 CD2D int 2Dh 1624 00000992 8EC2 mov es, dx 1625 00000994 3C04 cmp al, 4 1626 00000996 BA[A800] mov dx, msg.uninstall_no_list 1627 00000999 7523 jne abort_msg 1628 1629 0000099B 26803F2D cmp byte [es:bx], 2Dh 1630 0000099F F9 stc 1631 000009A0 741B je .ret 1632 1633 @@: 1634 000009A2 268A07 mov al, byte [es:bx] 1635 000009A5 8D5F03 lea bx, [bx + 3] 1636 000009A8 3C2D cmp al, 2Dh 1637 000009AA 7410 je @F 1638 000009AC 50 push ax 1639 000009AD B400 mov ah, 0 1640 000009AF 93 xchg ax, bx 1641 000009B0 E8BCFF call bx_intnum_to_dh_bx_intsel 1642 000009B3 2E08B7[400E] or byte [cs:selectedinterrupts + bx], dh 1643 000009B8 93 xchg ax, bx 1644 000009B9 58 pop ax 1645 000009BA EBE6 jmp @B 1646 1647 @@: 1648 000009BC F8 clc 1649 .ret: 1650 000009BD C3 retn 1651 1652 1653 abort_msg: 1654 000009BE E8C103 call disp_msg 1655 abort: 1656 000009C1 E89F03 call restorestate 1657 000009C4 B8FF4C mov ax, 4CFFh 1658 000009C7 CD21 int 21h 1659 1660 1661 install: ; ds = handler segment inside cs 1662 ; bp = option flags 1663 d4bp 1664 000009C9 8CC8 mov ax, cs 1665 000009CB 05F600 add ax, 10h+transient_size_p 1666 000009CE 8ED8 mov ds, ax ; ds = handler segment (could have changed with /n switch) 1667 1668 000009D0 BB[2C00] mov bx, ctrl0 ; ds:bx -> ctrl0 1669 1670 000009D3 31C0 xor ax, ax ; (plus inc) ; %assign ii 1 1671 000009D5 2E8B0E[0A0E] mov cx, word [cs:size] ; _HEAPENTRIES 1672 @@: 1673 000009DA 40 inc ax ; %assign ii ii + 1 1674 000009DB 50 push ax 1675 ; ax ; %assign _HEAPINTENTRIES ii 1676 000009DC 89CA mov dx, cx 1677 000009DE 29C2 sub dx, ax ; %assign _HEAPLISTENTRIES _HEAPENTRIES - _HEAPINTENTRIES 1678 000009E0 83C008 add ax, 7 + 1 ; %if (_HEAPINTENTRIES + 7 + 1) 1679 000009E3 D1E8 shr ax, 1 1680 000009E5 D1E8 shr ax, 1 1681 000009E7 D1E8 shr ax, 1 ; / 8 1682 000009E9 39D0 cmp ax, dx ; > _HEAPLISTENTRIES 1683 000009EB 58 pop ax 1684 000009EC 76EC jna @B 1685 ; %exitrep 1686 000009EE 48 dec ax ; %assign _HEAPINTENTRIES ii - 1 1687 000009EF 89CA mov dx, cx 1688 000009F1 29C2 sub dx, ax ; %assign _HEAPLISTENTRIES _HEAPENTRIES - _HEAPINTENTRIES 1689 000009F3 8916[2E00] mov word [amisintr.length], dx 1690 000009F7 E885FF call dx_units_to_bytes ; dx = bytes for int list 1691 000009FA 81C2[9000] add dx, heap ; dx = bytes for program plus int list 1692 ; this is -> free heap allocation for blocks 1693 000009FE 8916[3000] mov word [heapfree_offset], dx 1694 00000A02 50 push ax 1695 00000A03 92 xchg ax, dx 1696 00000A04 E878FF call dx_units_to_bytes ; dx = bytes for block heap 1697 00000A07 92 xchg ax, dx 1698 00000A08 01D0 add ax, dx ; ax = bytes for total program 1699 00000A0A 83C00F add ax, + 15 ; + 15 for rounding up to paragraph boundary 1700 00000A0D 24F0 and al, ~ 15 ; round up to paragraph boundary 1701 00000A0F 2D[9800] sub ax, heap_uninit ; = number of uninitialised bytes 1702 00000A12 89C1 mov cx, ax 1703 00000A14 5E pop si 1704 1705 00000A15 31C0 xor ax, ax 1706 00000A17 2E87062C00 xchg ax, word [ cs:2Ch ]; set PSP field to zero 1707 00000A1C 8EC0 mov es, ax 1708 00000A1E B449 mov ah, 49h 1709 00000A20 CD21 int 21h ; Free our environment 1710 00000A22 BC[D207] mov sp, ..@stack ; change to stack inside now unused code 1711 1712 00000A25 56 push si ; number of interrupt blocks 1713 00000A26 51 push cx ; number of uninitialised bytes to allocate 1714 1715 00000A27 B44A mov ah, 4Ah 1716 00000A29 0E push cs 1717 00000A2A 07 pop es ; Resize the segment allocated to this program 1718 00000A2B BB9501 mov bx, 10h+transient_size_p+resident_size_p+msg_size_p ; 10h is the size of the PSP 1719 %if _PSPRELOC 1720 00000A2E 53 push bx 1721 %endif 1722 00000A2F CD21 int 21h 1723 1724 %if _PSPRELOC 1725 00000A31 31DB xor bx, bx ; disable UMB link 1726 00000A33 BA0200 mov dx, 2 ; last fit, low then high/low only 1727 00000A36 E80103 call setstrat 1728 00000A39 BA[8703] mov dx, msg.mcbcorrupted 1729 00000A3C 7303E9D200 jc .abort_msg 1730 00000A41 5B pop bx 1731 ; The next instruction tries to work around a bug in NTVDM's COMMAND.COM. 1732 ; It is disabled in all actual releases. Just ignore it for now. 1733 ; add bx, 0029h ; +64 KiB to try leaving command shell transient in memory 1734 00000A42 B448 mov ah, 48h 1735 00000A44 CD21 int 21h ; allocate new memory for process 1736 00000A46 7303E99100 jc .cantrelocate ; not possible, try to allocate resident --> 1737 00000A4B 8CCA mov dx, cs ; dx = old process 1738 00000A4D 8EC0 mov es, ax ; newly allocated block 1739 00000A4F 8EDA mov ds, dx ; (no cs prefix to avoid an 8086 bug) 1740 00000A51 31FF xor di, di 1741 00000A53 31F6 xor si, si 1742 00000A55 B9A30C mov cx, 80h+transient_size_w+resident_size_w+msg_size_w 1743 1744 ; This used to have a cs prefix, but it was removed 1745 ; to ensure proper operation on 8086 implementations 1746 ; with a bug when rep and another prefix occurred. 1747 ; Instead ds is set to dx = cs, costing one byte. 1748 00000A58 F3A5 rep movsw ; copy process into newly allocated block 1749 1750 00000A5A E8C802 call setmcb ; make the block own itself 1751 ; ds = old PSP 1752 1753 00000A5D 50 push ax ; new cs 1754 00000A5E E80103 call retf_inst ; "push ip". the retf jumps to our new cs 1755 .relocated: 1756 00000A61 8ED0 mov ss, ax ; change stack, too. sp is still valid! 1757 %if 1 1758 00000A63 8EC0 mov es, ax ; es = new PSP 1759 00000A65 BF1800 mov di, 18h 1760 00000A68 B91400 mov cx, 20 1761 00000A6B 26A33600 mov word [ es:34h+2 ], ax 1762 00000A6F 26893E3400 mov word [ es:34h ], di ; fix the new PSP's PHT pointer 1763 00000A74 26890E3200 mov word [ es:32h ], cx ; and the count of PHT entries field 1764 00000A79 50 push ax 1765 00000A7A B0FF mov al, -1 1766 00000A7C 57 push di 1767 00000A7D 89CB mov bx, cx ; = 20 1768 00000A7F F3AA rep stosb ; initialise new PHT with empty entries 1769 00000A81 5F pop di 1770 00000A82 8B0E3200 mov cx, word [ 32h ] ; cx = count of PHT entries 1771 00000A86 39D9 cmp cx, bx ; >= 20 ? 1772 00000A88 7202 jb .shortertable ; no --> 1773 00000A8A 89D9 mov cx, bx ; limit to 20 1774 .shortertable: 1775 00000A8C C5363400 lds si, [ 34h ] ; ds:si-> old PHT 1776 00000A90 56 push si 1777 00000A91 51 push cx 1778 00000A92 F3A4 rep movsb ; get all entries 1779 00000A94 59 pop cx 1780 00000A95 5F pop di 1781 00000A96 1E push ds 1782 00000A97 07 pop es ; es:di-> old PHT 1783 00000A98 F3AA rep stosb ; fill moved entries with -1 (closed) 1784 00000A9A 58 pop ax 1785 00000A9B 8EDA mov ds, dx ; ds = old PSP 1786 00000A9D C7060A00[D30A] mov word [ 0Ah ], .terminated 1787 00000AA3 A30C00 mov word [ 0Ah+2 ], ax 1788 00000AA6 C7060E00[570D] mov word [ 0Eh ], i23 1789 00000AAC A31000 mov word [ 0Eh+2 ], ax 1790 00000AAF C7061200[540D] mov word [ 12h ], i24 1791 00000AB5 A31400 mov word [ 12h+2 ], ax ; set interrupt vectors to ours 1792 00000AB8 A31600 mov word [ 16h ], ax ; set parent PSP to the relocated one 1793 00000ABB FF362E00 push word [ 2Eh ] 1794 00000ABF 2E8F062E00 pop word [ cs:2Eh ] 1795 00000AC4 2EA33000 mov word [ cs:2Eh+2 ], ax ; set SS:SP used by process termination 1796 ; In order to set the correct stack address here, 1797 ; the last Int21 call to a usual function (such as 1798 ; Int21.48) must've been made with the same stack 1799 ; pointer as the Int21.4C call below gets. 1800 1801 00000AC8 93 xchg ax, bx ; bx = new location, dx = old location 1802 00000AC9 B85D33 mov ax, 335Dh 1803 00000ACC CD21 int 21h ; PSP relocated call-out 1804 1805 00000ACE B8004C mov ax, 4C00h 1806 00000AD1 CD21 int 21h ; terminate, and make the new PSP active 1807 ; also handles freeing all memory allocated to the old PSP 1808 ; also closes any handles >20 if PHT larger 1809 ; also relocates Int23, Int24 1810 ; also notifies resident software old PSP is no longer valid 1811 .terminated: ; (ax, bx, es might be changed) 1812 00000AD3 0E push cs 1813 00000AD4 1F pop ds 1814 %else 1815 mov word [ cs:34h+2 ], ax ; fix up the PHT pointer 1816 xchg ax, bx 1817 mov ah, 50h 1818 int 21h ; set current PSP to new 1819 ; bx = new PSP location 1820 ; dx = old PSP location 1821 mov ax, 335Dh 1822 int 21h ; PSP relocated call-out 1823 1824 push dx 1825 push cs 1826 pop ds 1827 mov dx, i23 1828 mov ax, 2523h ; set Int23 to relocated position 1829 int 21h 1830 mov dx, i24 1831 mov ax, 2524h ; set Int24 to relocated position 1832 int 21h 1833 pop es ; former process 1834 mov ah, 49h 1835 int 21h ; free memory of former process location 1836 %endif 1837 00000AD5 B41A mov ah, 1Ah 1838 00000AD7 BA8000 mov dx, 80h 1839 00000ADA CD21 int 21h ; set DTA 1840 1841 .cantrelocate: ; We could display an error message here. 1842 00000ADC 8CCB mov bx, cs 1843 00000ADE 81C3F600 add bx, 10h+transient_size_p 1844 00000AE2 8EDB mov ds, bx ; ds = handler segment 1845 %endif 1846 1847 00000AE4 59 pop cx ; number of bytes to allocate 1848 00000AE5 51 push cx 1849 00000AE6 81C1[9800] add cx, heap_uninit 1850 00000AEA D1E9 shr cx, 1 1851 00000AEC D1E9 shr cx, 1 1852 00000AEE D1E9 shr cx, 1 1853 00000AF0 D1E9 shr cx, 1 ; get amount of paragraphs 1854 1855 00000AF2 F7C50200 test bp, _installlow ; try UMA installation ? 1856 00000AF6 751E jnz .allocatelow ; nope --> 1857 00000AF8 BB0100 mov bx, 1 ; enable UMB link 1858 00000AFB BA4100 mov dx, 41h ; best fit, high only strategy 1859 00000AFE E83902 call setstrat 1860 00000B01 7213 jc .allocatelow 1861 1862 00000B03 B448 mov ah, 48h 1863 00000B05 89CB mov bx, cx ; required size (paragraphs) 1864 00000B07 CD21 int 21h ; try to allocate in upper memory 1865 00000B09 732A jnc .allocated ; successfully allocated --> 1866 00000B0B 83F808 cmp ax, byte 8 ; "not enough memory" ? 1867 00000B0E 7406 je .allocatelow ; yes, retry in low memory --> 1868 .corrupted: ; else: MCBs corrupted 1869 00000B10 BA[8703] mov dx, msg.mcbcorrupted 1870 .abort_msg: 1871 00000B13 E9A8FE jmp abort_msg 1872 1873 .allocatelow: 1874 00000B16 31DB xor bx, bx ; disable UMB link 1875 00000B18 BA0100 mov dx, 1 ; best fit, low then high/low only 1876 00000B1B E81C02 call setstrat 1877 00000B1E BA[8703] mov dx, msg.mcbcorrupted 1878 00000B21 72F0 jc .abort_msg 1879 1880 00000B23 B448 mov ah, 48h 1881 00000B25 89CB mov bx, cx ; required size (paragraphs) 1882 00000B27 CD21 int 21h ; try to allocate in low memory 1883 00000B29 730A jnc .allocated ; successfully allocated --> 1884 00000B2B 83F808 cmp ax, byte 8 ; "not enough memory" ? 1885 00000B2E 75E0 jne .corrupted ; no, assume corrupted chain --> 1886 00000B30 BA[7603] mov dx, msg.nomemory ; else report no memory 1887 00000B33 EBDE jmp short .abort_msg 1888 1889 .allocated: 1890 ; ax = allocated memory block 1891 ; ds = segment of resident (behind transient) 1892 00000B35 8EC0 mov es, ax 1893 ; es = ax 1894 00000B37 B95000 mov cx, (resident_size_p)*8 1895 00000B3A 31FF xor di, di ; es:di-> start of allocated block 1896 00000B3C 31F6 xor si, si ; ds:si-> source inside this block 1897 00000B3E F3A5 rep movsw 1898 00000B40 BF[9800] mov di, heap_uninit ; es:di -> heap uninitialised 1899 00000B43 31C0 xor ax, ax ; = 0 1900 00000B45 59 pop cx 1901 00000B46 D1E9 shr cx, 1 ; to words 1902 00000B48 F3AB rep stosw ; initialise heap 1903 00000B4A 89F9 mov cx, di ; -> behind memory block 1904 00000B4C 268B3E[3000] mov di, word [es:heapfree_offset] 1905 ; es:di -> heap free block 1906 00000B51 48 dec ax ; = -1 1907 00000B52 AB stosw ; store next pointer 1908 00000B53 58 pop ax ; = number of interrupt blocks 1909 00000B54 AB stosw 1910 00000B55 51 push cx ; size of allocation 1911 1912 00000B56 2EA1[020E] mov ax, word [cs:trymultiplexnumber] 1913 00000B5A 84C0 test al, al 1914 00000B5C 7506 jnz @F 1915 00000B5E CD2D int 2Dh 1916 00000B60 84C0 test al, al ; still 00h if free 1917 00000B62 7413 jz .foundamis 1918 @@: 1919 1920 00000B64 B400 mov ah, 00h ; start with multiplex number 00h 1921 .loopamis: 1922 00000B66 B000 mov al, 00h ; installation check 1923 00000B68 CD2D int 2Dh 1924 00000B6A 84C0 test al, al ; still 00h if free 1925 00000B6C 7409 jz .foundamis 1926 00000B6E FEC4 inc ah ; search from bottom to top 1927 00000B70 75F4 jnz .loopamis ; loop if zero not yet reached --> 1928 00000B72 BA[9E03] mov dx, msg.noamisnumber; abort if no free AMIS multiplex number left 1929 00000B75 EB9C jmp short .abort_msg 1930 1931 .foundamis: 1932 ; ah = free AMIS multiplex number 1933 00000B77 268826[4C00] mov byte [ es:amisnum ], ah ; Set multiplex number 1934 1935 ; Check that all the interrupt handlers we'll chain to are valid. 1936 ; We don't do these checks at run-time for speed and resident code 1937 ; size reasons. Doing them here instead isn't perfect (since other 1938 ; software could modify our IISP entry at run time to contain an 1939 ; invalid address) but still is better than nothing. 1940 00000B7C 50 push ax 1941 00000B7D 06 push es 1942 00000B7E 1F pop ds 1943 00000B7F BE[3800] mov si, i2D 1944 00000B82 B02D mov al, 2Dh 1945 1946 00000B84 BAFFFF mov dx, -1 1947 00000B87 52 push dx 1948 1949 00000B88 E89400 call HookInterruptSim ; do the hook simulation 1950 00000B8B 7303E94BFC jc hookerror ; failed --> 1951 1952 ; All error conditions must be checked above because we'll now 1953 ; modify the MCB so that it won't be deallocated when terminating. 1954 00000B90 8CC0 mov ax, es 1955 00000B92 E89001 call setmcb 1956 ; (Note that in case of critical hook failure, we still want the 1957 ; handler to stay allocated in case some of the already hooked 1958 ; interrupts can't be unhooked anymore. Otherwise, the code to 1959 ; handle a nullified critical failure will free the MCB itself.) 1960 1961 00000B95 8ED8 mov ds, ax ; restore ds !! 1962 00000B97 BE[3800] mov si, i2D 1963 00000B9A B02D mov al, 2Dh 1964 00000B9C E8A200 call HookInterrupt ; really hook interrupt 1965 00000B9F 7235 jc .handle_crithookfailure 1966 1967 00000BA1 58 pop ax ; (discard) 1968 00000BA2 5E pop si ; restore multiplex number 1969 1970 ; Now that everything is fine, display the final message. 1971 00000BA3 8CD8 mov ax, ds ; ax = segment of resident copy 1972 00000BA5 2E3B06[000E] cmp ax, word [ cs:firstumcb ] ; is that high ? 1973 00000BAA BA[2800] mov dx, msg.highloadedusing 1974 00000BAD 7703 ja .highloaded ; of course --> 1975 00000BAF BA[2D00] mov dx, msg.loadedusing ; no, other message 1976 1977 .highloaded: 1978 00000BB2 E8CD01 call disp_msg ; "[high ]loaded using " "xxxx bytes." 1979 00000BB5 8ED8 mov ds, ax ; ds = allocated block 1980 00000BB7 58 pop ax ; restore allocated size 1981 00000BB8 83C010 add ax, byte 16 ; add 16 for the MCB 1982 00000BBB E8F401 call disp_ax_dec ; "xxxx" 1983 00000BBE BA[3B00] mov dx, msg.byte 1984 00000BC1 E8BE01 call disp_msg ; " bytes." 1985 00000BC4 96 xchg ax, si ; restore multiplex number to ah 1986 00000BC5 88E0 mov al, ah 1987 00000BC7 E8B201 call disp_amisnum 1988 00000BCA F7C52001 test bp, _cover | _setints 1989 00000BCE 7403E95EFC jnz cover 1990 00000BD3 E9FDFB jmp ..@exit ; done if we ever were --> 1991 1992 1993 ; Advanced Critical Failure Management(TM) 1994 ; 1995 ; In case of a critical installation failure (see docs and commentary), 1996 ; we'll try to unhook all the interrupts that we successfully hooked. 1997 ; If that succeeded, the whole TSR can simply be freed again and the 1998 ; problem of the partway installed TSR has been nullified. This is still 1999 ; considered a case to report, but isn't critical in itself because 2000 ; there's no TSR lingering around. 2001 .handle_crithookfailure: 2002 00000BD6 89DE mov si, bx ; save address of failed interrupt's list entry 2003 00000BD8 B9[DE0B] mov cx, .critintdisplayed 2004 00000BDB E900FC jmp displayhookerror 2005 .critintdisplayed: 2006 00000BDE E8A101 call disp_msg ; display the last message (displayhookerror expects 2007 ; to jump to abort_msg) 2008 00000BE1 89F1 mov cx, si ; restore address (don't use the stack here) 2009 2010 ; We now don't simulate the unhooking because we just want to unhook 2011 ; these interrupts that will unhook no matter what. Additionally, it's 2012 ; very likely that all the unhooking succeeds. 2013 ; 2014 ; This code uses an unrolled form of loopamisintr copied from there 2015 ; because I just can't be bothered to add special checking for a 2016 ; shorter list there. I also considered just calling loopamisintr 2017 ; then filtering out the errors of the interrupts that we didn't hook, 2018 ; but that's somewhat hacky too. So copied the code be. 2019 00000BE3 BB[9000] mov bx, default_amisintr 2020 00000BE6 B8FFFF mov ax, 0FFFFh 2021 00000BE9 39CB cmp bx, cx 2022 00000BEB 741B je .nullifiedcrit ; the very first interrupt failed to hook, none to unhook --> 2023 00000BED 50 push ax 2024 .lai_loop: 2025 00000BEE 8A07 mov al, byte [ bx ] 2026 00000BF0 8B7701 mov si, word [ bx+1 ] 2027 2028 00000BF3 E852F8 call UnhookInterrupt 2029 00000BF6 7303 jnc .lai_noerror ; no error --> 2030 00000BF8 30E4 xor ah, ah 2031 00000BFA 50 push ax ; else remember number of interrupt, but continue looping 2032 .lai_noerror: 2033 00000BFB 83C303 add bx, byte 3 2034 00000BFE 39CB cmp bx, cx 2035 00000C00 75EC jne .lai_loop ; do until the next one would be the one that didn't hook --> 2036 2037 00000C02 58 pop ax 2038 00000C03 83F8FF cmp ax, byte -1 ; if it's below (CY), there were errors 2039 00000C06 720F jc .critical ; there were errors --> 2040 2041 ; One of the interrupts critically failed to hook, but we were able to 2042 ; unhook everything again. 2043 .nullifiedcrit: 2044 00000C08 40 inc ax 2045 00000C09 8CDB mov bx, ds 2046 00000C0B 4B dec bx 2047 00000C0C 8EDB mov ds, bx 2048 00000C0E A30100 mov word [ 1 ], ax ; free the MCB 2049 2050 00000C11 BA[1A02] mov dx, msg.criticalundone 2051 00000C14 E9A7FD jmp abort_msg 2052 2053 ; One of the interrupts critically failed to hook. Additionally, at 2054 ; least one other interrupt failed to unhook. 2055 .critical: 2056 00000C17 BA[6202] mov dx, msg.criticalins 2057 00000C1A 31C9 xor cx, cx 2058 00000C1C E9E3F8 jmp unhookerrorcritical.install ; this TSR be damned --> 2059 2060 2061 2062 2063 ; This one works similar to UnhookInterruptSim. Here we check that 2064 ; the interrupt currently contains a valid address since we want to 2065 ; chain to the previous handler. 2066 ; 2067 ; INP: ds:si-> IISP entry 2068 ; al = interrupt number 2069 ; OUT: NC if current interrupt handler valid, or we install a non-chaining handler 2070 ; CY if current interrupt handler invalid (offset FFFFh or segment 0000h) 2071 ; CHG: ah 2072 HookInterruptSim: 2073 00000C1F 06 push es 2074 00000C20 53 push bx 2075 2076 ; harden this, check we are an IISP entry 2077 00000C21 1E push ds 2078 00000C22 07 pop es ; es => our handler segment 2079 00000C23 89F3 mov bx, si ; es:bx -> our handler 2080 00000C25 E8CF00 call IsIISPEntry? ; does it have an IISP header ? 2081 00000C28 7513 jne .error ; fail if not 2082 2083 00000C2A F6440880 test byte [ si + ieEOI ], 80h ; should be last handler ? 2084 00000C2E 750E jnz .valid ; yes, handler doesn't matter if we don't chain --> (NC) 2085 00000C30 B435 mov ah, 35h 2086 00000C32 CD21 int 21h 2087 00000C34 43 inc bx ; offset FFFFh ? 2088 00000C35 7406 jz .error 2089 00000C37 8CC3 mov bx, es 2090 00000C39 85DB test bx, bx ; or segment 0000h ? 2091 00000C3B 7501 jnz .valid ; no, valid --> (NC) 2092 .error: 2093 00000C3D F9 stc ; invalid, CY 2094 .valid: 2095 00000C3E 5B pop bx 2096 00000C3F 07 pop es 2097 00000C40 C3 retn 2098 2099 2100 ; INP: ds:si-> IISP entry 2101 ; al = interrupt number 2102 ; OUT: NC if current interrupt handler valid, or we install a non-chaining handler 2103 ; CY if current interrupt handler invalid (offset FFFFh or segment 0000h), 2104 ; though this SHOULD never happen 2105 ; CHG: ah, es 2106 HookInterrupt: 2107 00000C41 E8DBFF call HookInterruptSim 2108 00000C44 7259 jc .ret 2109 00000C46 53 push bx 2110 00000C47 52 push dx 2111 00000C48 B435 mov ah, 35h 2112 00000C4A CD21 int 21h ; get current handler 2113 00000C4C F6440880 test byte [ si + ieEOI ], 80h ; should be last handler ? 2114 00000C50 743E jz .notlast 2115 ; If we are a last (non-chaining) handler 2116 ; then HookInterruptSim always shortcuts 2117 ; to succeeding. However, the family of 2118 ; IsIISPEntry? functions doesn't check 2119 ; for invalid handler addresses the same 2120 ; way as HookInterruptSim. (An offset of 2121 ; FFFFh happens to be rejected by both, 2122 ; but that is not true of a zero segment.) 2123 ; If we were to install a chaining handler 2124 ; then the invalid address would be reason 2125 ; not to install. Consequently, when we 2126 ; install a last handler then it should 2127 ; not be installed into an IISP chain that 2128 ; is reachable from the first handler, 2129 ; the address of which was invalid. 2130 ; Instead it should install atop the handler 2131 ; that has an invalid address. Therefore, 2132 ; check for a segment of zero here. 2133 ; (This check could be added to the function 2134 ; IsIISPEntry? but it may be useful to have 2135 ; them otherwise allow addresses with a 2136 ; segment of zero.) 2137 00000C52 50 push ax 2138 00000C53 8CC0 mov ax, es ; get the segment 2139 00000C55 85C0 test ax, ax ; is it zero ? 2140 00000C57 58 pop ax 2141 00000C58 7436 jz .notlast ; yes --> 2142 00000C5A E89300 call IsChainingIISPEntry? ; is the first handler a chaining IISP ? 2143 00000C5D 7531 jne .notlast ; no, can't hook below it --> 2144 00000C5F A9 db __TEST_IMM16 ; (skip 2 pop ax) 2145 .loop: 2146 00000C60 58 pop ax 2147 00000C61 58 pop ax ; discard last entry's address 2148 00000C62 06 push es 2149 00000C63 53 push bx 2150 00000C64 26C45F02 les bx, [ es:bx + ieNext ] ; load next entry 2151 00000C68 E88500 call IsChainingIISPEntry? ; this one also a chaining IISP ? 2152 00000C6B 74F3 je .loop 2153 ; This write need not be atomic because 2154 ; the handler is not yet in the chain. 2155 00000C6D 895C02 mov word [ si + ieNext ], bx 2156 00000C70 8C4404 mov word [ si + ieNext + 2 ], es ; store address of next entry (non-IISP or non-chaining) 2157 00000C73 5B pop bx 2158 00000C74 07 pop es 2159 ; The next write needs to be atomic, 2160 ; so build a header on the stack and 2161 ; call the update function here. 2162 00000C75 57 push di 2163 00000C76 BA4E48 mov dx, "NH" ; "New Handler" 2164 00000C79 52 push dx ; fake IISP ieSignature 2165 00000C7A 1E push ds 2166 00000C7B 56 push si ; fake IISP ieNext 2167 00000C7C BAEBFE mov dx, 0FEEBh ; "jmp short $" 2168 00000C7F 52 push dx ; fake IISP ieEntry 2169 00000C80 16 push ss 2170 00000C81 1F pop ds 2171 00000C82 89E6 mov si, sp ; ds:si -> fake IISP header 2172 00000C84 89DF mov di, bx ; es:di -> IISP header to update 2173 00000C86 E89EF7 call update_iisp_header ; store our handler's address in this chaining IISP entry 2174 00000C89 5A pop dx ; discard 2175 00000C8A 5E pop si 2176 00000C8B 1F pop ds ; restore 2177 00000C8C 5A pop dx ; discard 2178 00000C8D 5F pop di ; restore 2179 00000C8E EB0C jmp short .return 2180 2181 .notlast: 2182 ; This write need not be atomic because 2183 ; only once we call 21.25 the handler 2184 ; is linked into the chain. 2185 00000C90 895C02 mov word [ si + ieNext ], bx 2186 00000C93 8C4404 mov word [ si + ieNext + 2 ], es ; store 2187 00000C96 B425 mov ah, 25h 2188 00000C98 89F2 mov dx, si ; ds:dx-> interrupt entry 2189 00000C9A CD21 int 21h ; set new handler 2190 .return: 2191 00000C9C 5A pop dx 2192 00000C9D 5B pop bx 2193 00000C9E F8 clc 2194 .ret: 2195 00000C9F C3 retn 2196 2197 2198 ; INP: al = interrupt number 2199 ; ah = our multiplex number 2200 ; OUT: NC if to hook 2201 ; CY if not to hook 2202 HookInterruptCover: 2203 lframe near 2204 lvar dword, topmostinthandler 2205 00000CA0 5589E55050 lenter 2206 00000CA5 50 push ax 2207 lvar word, intnumber_low_multiplexnumber_high 2208 lequ ?intnumber_low_multiplexnumber_high, intnumber 2209 lequ ?intnumber_low_multiplexnumber_high + 1, multiplexnumber 2210 00000CA6 53 push bx 2211 00000CA7 52 push dx 2212 00000CA8 06 push es 2213 00000CA9 57 push di 2214 00000CAA 3C2D cmp al, 2Dh ; int 2Dh is not supported 2215 00000CAC F9 stc ; CY 2216 00000CAD 7438 je .ret_pop 2217 00000CAF B435 mov ah, 35h 2218 00000CB1 CD21 int 21h ; es:bx -> topmost handler 2219 00000CB3 895EFC mov word [bp + ?topmostinthandler], bx 2220 00000CB6 8C46FE mov word [bp + ?topmostinthandler + 2], es 2221 00000CB9 B32D mov bl, 2Dh ; placeholder, only list accepted 2222 00000CBB B004 mov al, 4 2223 00000CBD 8A66FB mov ah, byte [bp + ?multiplexnumber] 2224 00000CC0 CD2D int 2Dh ; get our interrupt list 2225 00000CC2 3C04 cmp al, 4 ; expect list 2226 00000CC4 F9 stc ; CY 2227 00000CC5 7520 jne .ret_pop 2228 2229 00000CC7 3956FE cmp word [bp + ?topmostinthandler + 2], dx 2230 ; matches the segment ? 2231 00000CCA F8 clc ; NC 2232 00000CCB 751A jne .cover ; no, not found, so do cover --> 2233 2234 00000CCD 8EC2 mov es, dx ; es:bx -> list 2235 .loop: 2236 00000CCF 268A07 mov al, [es:bx] ; get interrupt number 2237 00000CD2 43 inc bx 2238 00000CD3 268B3F mov di, [es:bx] ; get entrypoint offset 2239 00000CD6 43 inc bx 2240 00000CD7 43 inc bx 2241 00000CD8 3C2D cmp al, 2Dh ; end of list ? 2242 00000CDA 740B je .cover ; yes, not found, so do cover --> 2243 00000CDC 3A46FA cmp al, byte [bp + ?intnumber] ; for the interrupt we're checking ? 2244 00000CDF 75EE jne .loop ; no, try next --> 2245 00000CE1 397EFC cmp word [bp + ?topmostinthandler], di ; does it match ? 2246 00000CE4 75E9 jne .loop 2247 ; yes, already covered 2248 .dontcover: 2249 00000CE6 F9 stc 2250 .cover: 2251 .ret_pop: 2252 00000CE7 5F pop di 2253 00000CE8 07 pop es 2254 00000CE9 5A pop dx 2255 00000CEA 5B pop bx 2256 00000CEB 58 pop ax 2257 00000CEC 89EC5D lleave 2258 00000CEF C3 lret 2259 2260 2261 ; INP: es:bx-> interrupt entry 2262 ; OUT: NZ if non-IISP entry, 2263 ; or IISP entry that doesn't chain 2264 ; ZR if IISP entry that chains 2265 IsChainingIISPEntry?: 2266 00000CF0 26F6470880 test byte [ es:bx + ieEOI ], 80h ; this one a non-chaining handler ? (or non-IISP) 2267 00000CF5 752D jnz IsIISPEntry?.return ; yes --> 2268 ; otherwise fall through to check if really an IISP entry 2269 2270 ; INP: es:bx-> interrupt entry 2271 ; OUT: NZ if non-IISP entry 2272 ; ZR if IISP entry 2273 IsIISPEntry?: 2274 00000CF7 83FBF8 cmp bx, - (ieSignature + 2) ; may access word at offset FFFFh ? 2275 00000CFA 7728 ja .return ; yes, avoid --> (NZ) 2276 00000CFC 26817F064B42 cmp word [ es:bx + ieSignature ], "KB" ; "KB"/424Bh ? ("BK" in MASM) 2277 00000D02 7520 jne .return 2278 00000D04 26813F90EA cmp word [ es:bx + ieEntry ], 0EA90h ; nop\jmp far imm16:imm16 ? 2279 00000D09 7419 je .return ; unused IISP entry (created by iHPFS) --> 2280 00000D0B 26803FEB cmp byte [ es:bx + ieEntry ], 0EBh ; jmp short ... ? 2281 ; (This opcode should strictly be jmp short $+18 but there's programs 2282 ; that save an additional jmp opcode by jumping directly into their 2283 ; code even though it's not right behind the header.) 2284 00000D0F 7513 jne .return 2285 00000D11 26807F09EB cmp byte [ es:bx + ieJmphwreset ], 0EBh ; jmp short ... ? 2286 00000D16 740C je .return ; usual IISP entry --> 2287 00000D18 26807F09CB cmp byte [ es:bx + ieJmphwreset ], 0CBh ; retf ? 2288 00000D1D 7405 je .return ; a shorter variant --> 2289 00000D1F 26807F09CF cmp byte [ es:bx + ieJmphwreset ], 0CFh ; iret ? 2290 .return: 2291 00000D24 C3 retn 2292 2293 2294 ; INP: ax = memory block 2295 ; OUT: es = MCB, name and owner set 2296 ; ds = cs 2297 ; CHG: di, si 2298 setmcb: 2299 00000D25 48 dec ax 2300 00000D26 8EC0 mov es, ax ; es = MCB of allocated block 2301 00000D28 40 inc ax ; ax = allocated block! 2302 00000D29 BF0800 mov di, 8 ; es:di-> MCB name field 2303 00000D2C 0E push cs 2304 00000D2D 1F pop ds 2305 00000D2E BE[1000] mov si, msg.mcbname ; ds:si-> content for field 2306 00000D31 A5 movsw 2307 00000D32 A5 movsw 2308 00000D33 A5 movsw 2309 00000D34 A5 movsw ; Force MCB string 2310 00000D35 26A30100 mov word [ es:1 ], ax ; Set owner to itself 2311 00000D39 C3 retn 2312 2313 2314 ; The DOS memory allocation strategy classes are actually "Low then high", 2315 ; "High only" and "High then low". RBIL tells trash regarding that. We've 2316 ; to disable the UMB link to insure only low memory is allocated. Normally, 2317 ; the UMB link is already disabled when the program starts. But some 2318 ; programs, like CMDEDIT 3.21, are buggy and leave the UMB link enabled 2319 ; after they used UMBs. 2320 ; INP: bx = UMB link status to use 2321 ; dx = memory allocation strategy to use 2322 ; OUT: CF/ax error status 2323 setstrat: 2324 00000D3A B80358 mov ax, 5803h 2325 00000D3D CD21 int 21h ; set UMB link 2326 00000D3F 730B jnc .linkdone ; no error 2327 00000D41 83F801 cmp ax, byte 1 ; "invalid function" ? 2328 00000D44 F9 stc 2329 00000D45 750C jne .return ; no, actual error --> 2330 00000D47 85DB test bx, bx ; wanted to disable anyway ? 2331 00000D49 F9 stc 2332 00000D4A 7507 jnz .return ; no, regard as error --> 2333 .linkdone: 2334 00000D4C B80158 mov ax, 5801h 2335 00000D4F 89D3 mov bx, dx 2336 00000D51 CD21 int 21h ; set strategy 2337 .return: 2338 00000D53 C3 retn 2339 2340 2341 i24: 2342 00000D54 B003 mov al, 3 ; always return fail, to handle the error as a soft one 2343 00000D56 CF iret 2344 2345 i23: 2346 00000D57 2EC706[570D]EB08 mov word [ cs:$ ], (__JMP_REL8|__REL16__(.return)<<8) ; don't reenter 2347 00000D5E E80200 call restorestate 2348 .return: 2349 00000D61 F9 stc ; always abort program (what default DOS handler also does) 2350 retf_inst: 2351 00000D62 CB retf 2352 2353 ; Restore modified DOS data 2354 ; 2355 ; CHG: - 2356 ; USE: Int21 2357 restorestate: 2358 00000D63 50 push ax 2359 00000D64 53 push bx 2360 00000D65 2E8B1E[080E] mov bx, word [ cs:restored.strategy ] 2361 00000D6A B80158 mov ax, 5801h 2362 00000D6D CD21 int 21h ; restore strategy 2363 2364 00000D6F 2E8B1E[060E] mov bx, word [ cs:restored.umblink ] 2365 00000D74 B80358 mov ax, 5803h 2366 00000D77 CD21 int 21h ; restore UMB link 2367 00000D79 5B pop bx 2368 00000D7A 58 pop ax 2369 00000D7B C3 retn 2370 2371 2372 disp_amisnum: 2373 00000D7C E81500 call disp_al_hex 2374 00000D7F BA[5000] mov dx, msg.multiplex.2 2375 2376 disp_msg: 2377 00000D82 1E push ds 2378 00000D83 50 push ax 2379 00000D84 0E push cs 2380 00000D85 1F pop ds 2381 00000D86 B409 mov ah, 09h 2382 00000D88 CD21 int 21h 2383 00000D8A 58 pop ax 2384 00000D8B 1F pop ds 2385 00000D8C C3 retn 2386 2387 2388 disp_ax_hex: 2389 00000D8D 86C4 xchg al, ah 2390 00000D8F E80200 call disp_al_hex 2391 00000D92 86C4 xchg al, ah 2392 disp_al_hex: 2393 00000D94 51 push cx 2394 00000D95 B104 mov cl, 4 2395 00000D97 D2C8 ror al, cl 2396 00000D99 E80300 call .nibble 2397 00000D9C D2C8 ror al, cl 2398 00000D9E 59 pop cx 2399 .nibble: 2400 00000D9F 50 push ax 2401 00000DA0 240F and al, 0Fh 2402 00000DA2 0430 add al, '0' 2403 00000DA4 3C39 cmp al, '9' 2404 00000DA6 7602 jbe .isdigit 2405 00000DA8 0407 add al, 'A'-('9'+1) 2406 .isdigit: 2407 00000DAA 86D0 xchg dl, al 2408 00000DAC B402 mov ah, 02h 2409 00000DAE CD21 int 21h 2410 00000DB0 58 pop ax 2411 00000DB1 C3 retn 2412 2413 2414 ; Following call: Display number in ax decimal 2415 ; all registers preserved except dx 2416 disp_ax_dec: ; ax (no leading zeros) 2417 ; In: number in ax 2418 ; Out: displayed 2419 00000DB2 53 push bx 2420 00000DB3 31DB xor bx, bx 2421 .pushax: 2422 00000DB5 50 push ax 2423 .pushend: 2424 00000DB6 08DB or bl, bl 2425 00000DB8 7405 jz .nobl 2426 00000DBA 80EB05 sub bl, 5 2427 00000DBD F6DB neg bl 2428 .nobl: 2429 00000DBF 51 push cx 2430 00000DC0 B91027 mov cx, 10000 2431 00000DC3 E81D00 call .divide_out 2432 00000DC6 B9E803 mov cx, 1000 2433 00000DC9 E81700 call .divide_out 2434 00000DCC B96400 mov cx, 100 2435 00000DCF E81100 call .divide_out 2436 00000DD2 B10A mov cl, 10 2437 00000DD4 E80C00 call .divide_out 2438 ; (Divisor 1 is useless) 2439 00000DD7 0430 add al, '0' 2440 00000DD9 86D0 xchg dl, al 2441 00000DDB B402 mov ah, 02h 2442 00000DDD CD21 int 21h 2443 00000DDF 59 pop cx 2444 00000DE0 58 pop ax 2445 00000DE1 5B pop bx ; Caller's register 2446 00000DE2 C3 retn 2447 2448 .divide_out: 2449 ; In: ax = number 2450 ; cx = divisor 2451 ; Out: ax = remainder of operation 2452 ; result displayed 2453 00000DE3 52 push dx 2454 00000DE4 31D2 xor dx, dx 2455 00000DE6 F7F1 div cx ; 0:ax / cx 2456 00000DE8 52 push dx ; remainder 2457 00000DE9 FECB dec bl 2458 00000DEB 7503 jnz .nobl2 2459 00000DED 80CF01 or bh, 1 2460 .nobl2: 2461 00000DF0 08C7 or bh, al 2462 00000DF2 7408 jz .leadingzero 2463 00000DF4 0430 add al, '0' 2464 00000DF6 86D0 xchg dl, al 2465 00000DF8 B402 mov ah, 02h 2466 00000DFA CD21 int 21h ; display result 2467 .leadingzero: 2468 00000DFC 58 pop ax ; remainder 2469 00000DFD 5A pop dx 2470 00000DFE C3 retn 2471 2472 2473 00000DFF 00 align 2, db 0 2474 00000E00 FF9F firstumcb: dw 9FFFh ; guess of where UMBs start (A000h+) 2475 trymultiplexnumber: 2476 00000E02 FFFF dw -1 ; low byte = 0 if set 2477 ; high byte = multiplex number to try first 2478 debuggerfunction: 2479 00000E04 0000 dw 0 ; = 0 if unused 2480 restored: 2481 00000E06 0000 .umblink: dw 0 2482 00000E08 0000 .strategy: dw 0 2483 00000E0A 0800 size: dw _HEAPENTRIES 2484 2485 align 2, db 0 2486 parameters: ; Valid switches on the command line, followed 2487 ; by a byte with one option flag set. 2488 00000E0C 4F01 db "O", _onlystate 2489 00000E0E 4A02 db "J", _installlow 2490 00000E10 4E04 db "N", _installnew 2491 00000E12 5508 db "U", _uninstall 2492 00000E14 5208 db "R", _uninstall 2493 00000E16 4510 db "E", _expose 2494 00000E18 4320 db "C", _cover 2495 00000E1A 5340 db "S", _size 2496 00000E1C FF db 0FFh ; table end marker 2497 ; The help switches are not in this table (and neither in the option 2498 ; flags) because on invalid switches, we'll show the help anyway. 2499 2500 invalidinterrupts: 2501 00000E1D 1D1E1F2223242D3031 db 1Dh, 1Eh, 1Fh, 22h, 23h, 24h, 2Dh, 30h, 31h 2502 endarea invalidinterrupts 2503 2504 2505 00000E26 00 align 8, db 0 2506 00000E28 EB10000000004B4200- iispentry intrtemplate, 0, intrtemplate 2506 00000E31 EB0C00 2507 00000E3A 2EFF2E0200 jmp far [cs:.next - intrtemplate] 2508 .next_offset: equ $ - 2 ; 16-bit offset of jmp far mem 2509 00000E3F CB iisphwreset intrtemplate 2510 %if ($ - intrtemplate) != 24 2511 %error Expected template to fit exactly in 24 bytes 2512 %endif 2513 2514 2515 align 16, db 0 2516 selectedinterrupts: 2517 00000E40 00 times 32 db 0 ; 32 bytes * 8 bit/byte = 256 bits 2518 2519 align 16, db 0 2520 endarea transient 2521 2522 2523 section RESIDENT vstart=0 align=16 follows=TRANSIENT 2524 ; The handler is later copied into a seperate memory block. 2525 ; The vstart= value tells NASM to use all addresses as if this 2526 ; location is actually at vstart. (In this case, like if it's 2527 ; at the start of the segment, not at 100h+transient_size.) 2528 resident: 2529 2530 %include "resident.asm" 2531 <1> 2532 <1> %if 0 2533 <1> 2534 <1> Resident code of KEEPHOOK 2535 <1> 2021 by C. Masloch 2536 <1> 2537 <1> Usage of the works is permitted provided that this 2538 <1> instrument is retained with the works, so that any entity 2539 <1> that uses the works is notified of this instrument. 2540 <1> 2541 <1> DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. 2542 <1> 2543 <1> %endif 2544 <1> 2545 <1> %if 0 2546 <1> 2547 <1> Supported Int2D functions: 2548 <1> 2549 <1> AMIS - Installation check 2550 <1> INP: al = 00h 2551 <1> OUT: al = 0FFh 2552 <1> cx = Private version number (currently 0100h) 2553 <1> dx:di-> signature: "ecm ","KEEPHOOK" 2554 <1> 2555 <1> AMIS - Get private entry point - NOP: no private entry point 2556 <1> INP: al = 01h 2557 <1> OUT: al = 00h 2558 <1> 2559 <1> AMIS - Uninstall - NOP: no resident uninstaller or NOP: can't uninstall 2560 <1> INP: al = 02h 2561 <1> OUT: If installed from command line, 2562 <1> al = 03h 2563 <1> bx = memory block of resident TSR (cs) 2564 <1> (If internal (pre-installed), 2565 <1> al = 01h 2566 <1> If with resident uninstaller, 2567 <1> al = 01h if unsuccessful 2568 <1> al = 0FFh if successful) 2569 <1> 2570 <1> AMIS - Request pop-up - NOP: no pop-up 2571 <1> INP: al = 03h 2572 <1> OUT: al = 00h 2573 <1> 2574 <1> AMIS - Determine chained interrupts 2575 <1> INP: al = 04h 2576 <1> OUT: al = 04h 2577 <1> dx:bx-> interrupt hook list (Int2D always.) 2578 <1> 2579 <1> AMIS - Get hotkeys - NOP: no hotkeys 2580 <1> INP: al = 05h 2581 <1> OUT: al = 00h 2582 <1> 2583 <1> AMIS - Get device driver information - NOP: no device 2584 <1> INP: al = 06h 2585 <1> OUT: al = 00h 2586 <1> 2587 <1> AMIS - Reserved for AMIS 2588 <1> INP: al = 07h..0Fh 2589 <1> OUT: al = 00h 2590 <1> 2591 <1> TSR - Get compatible version 2592 <1> INP: al = 10h 2593 <1> OUT: al = 0FFh 2594 <1> cx = compatible version number (currently 0100h) 2595 <1> 2596 <1> TSR - Get ctrl0 2597 <1> INP: al = 20h 2598 <1> OUT: al = size of returned control data (not 0) 2599 <1> dx:bx-> control data 2600 <1> Unsupported bits switched off 2601 <1> 2602 <1> TSR - Reserved for TSR 2603 <1> INP: al = 11h..1Fh, 21h..FFh 2604 <1> OUT: al = 00h 2605 <1> 2606 <1> %endif 2607 <1> 2608 <1> %if TSR_VERP != ver(1,00) ; if defined one doesn't match current 2609 <1> %error "TSR version doesn't match" 2610 <1> %endif 2611 <1> 2612 <1> handler: 2613 <1> ; AMIS stuff 2614 <1> amissig: 2615 00000000 65636D20 <1> .ven: fill 8,32,db "ecm" ; vendor 2616 00000008 4B454550484F4F4B <1> .prod: fill 8,32,db "KEEPHOOK" ; product 2617 <1> %if 0 2618 <1> .desc: asciz ;"TSR to keep hooks available" 2619 <1> ; save memory by omitting the description 2620 <1> %else 2621 00000010 54535220746F206B65- <1> .desc: asciz "TSR to keep hooks available" 2621 00000019 657020686F6F6B7320- <1> 2621 00000022 617661696C61626C65- <1> 2621 0000002B 00 <1> 2622 <1> ; description 2623 <1> %endif 2624 <1> %if $ - .desc > 64 2625 <1> %error AMIS description too long 2626 <1> %endif 2627 <1> 2628 <1> 2629 <1> ; TSR data 2630 <1> align 2, db 0 2631 <1> ctrl0: 2632 <1> amisintr: 2633 <1> .offset: 2634 0000002C [9000] <1> dw default_amisintr 2635 <1> .length: 2636 0000002E 0000 <1> dw 0 2637 <1> heapfree_offset: 2638 00000030 0000 <1> dw 0 2639 00000032 0000 <1> dw 0 2640 <1> endarea ctrl0 2641 <1> 2642 <1> 2643 <1> i2D.uninstall: 2644 00000034 40 <1> inc ax ; (= 03h) safe to remove but no resident uninstaller 2645 00000035 8CCB <1> mov bx, cs ; = segment 2646 <1> i2D.hwreset equ $-1 ; (second byte of mov bx, cs is same as the retf opcode) 2647 00000037 CF <1> iret 2648 <1> 2649 00000038 EB10000000004B4200- <1> iispentry i2D, 0, i2D 2649 00000041 EBF300 <1> 2650 0000004A 80FC00 <1> cmp ah, 0 2651 <1> amisnum equ $-1 ; AMIS multiplex number (data for cmp opcode) 2652 0000004D 7405 <1> je .handle ; our multiplex number --> 2653 0000004F 2EFF2E[3A00] <1> jmp far [cs:.next] ; else go to next handler --> 2654 <1> 2655 <1> .handle: 2656 00000054 84C0 <1> test al, al 2657 00000056 7413 <1> jz .installationcheck ; installation check --> 2658 00000058 3C02 <1> cmp al, 02h 2659 0000005A 74D8 <1> je .uninstall ; uninstallation --> 2660 0000005C 3C04 <1> cmp al, 04h 2661 0000005E 7415 <1> je .determineinterrupts ; determine hooked interrupts --> 2662 00000060 3C10 <1> cmp al, 10h 2663 00000062 7418 <1> je .getver ; get compatible version --> 2664 00000064 3C20 <1> cmp al, 20h 2665 00000066 741A <1> je .getctrl0 ; get ctrl0 --> 2666 <1> ; all other functions are reserved or not supported by TSR 2667 <1> .nop: 2668 00000068 B000 <1> mov al, 0 ; show not implemented 2669 0000006A CF <1> iret 2670 <1> 2671 <1> .installationcheck: 2672 0000006B FEC8 <1> dec al ; (= FFh) show we're here 2673 0000006D B90001 <1> mov cx, TSR_VERP ; = version 2674 00000070 31FF <1> xor di, di ; dx:di -> AMIS signature strings of this program 2675 <1> .iret_dx_cs: 2676 00000072 8CCA <1> mov dx, cs 2677 <1> .iret: 2678 00000074 CF <1> iret 2679 <1> 2680 <1> .determineinterrupts: ; al = 04h, always returns list 2681 00000075 2E8B1E[2C00] <1> mov bx, [cs:amisintr.offset] ; dx:bx -> hooked interrupts list 2682 0000007A EBF6 <1> jmp short .iret_dx_cs 2683 <1> 2684 <1> .getver: 2685 0000007C B0FF <1> mov al, 0FFh ; show call supported 2686 0000007E B90001 <1> mov cx, TSR_VERC ; = compatible version 2687 00000081 CF <1> iret 2688 <1> 2689 <1> .getctrl0: 2690 00000082 BB[2C00] <1> mov bx, ctrl0 ; dx:bx -> ctrl0 2691 00000085 B008 <1> mov al, ctrl0_size ; size of ctrl0 2692 00000087 EBE9 <1> jmp short .iret_dx_cs 2693 <1> 2694 <1> 2695 00000089 90 <1> align 8 2696 <1> heap: 2697 <1> default_amisintr: 2698 <1> .: 2699 00000090 2D <1> db 2Dh 2700 00000091 [3800] <1> dw i2D 2701 00000093 00 <1> times 8 - ($ - .) db 0 2702 <1> heap_uninit: 2703 <1> 2704 00000098 00 <1> align 16, db 0 2705 <1> endarea handler 2706 <1> 2531 2532 align 16 2533 endarea resident 2534 2535 section MESSAGE align=1 follows=RESIDENT 2536 ; Putting the messages behind the handler is a kludge to omit 2537 ; the disp_ax_dec function. We can only use the right _xdigits 2538 ; macro conditionally if the handler's size is already known to 2539 ; the preprocessor, i.e. the handler has been assembled. 2540 2541 debuggeramissig: 2542 00000000 65636D20 .ven: fill 8,32,db "ecm" ; vendor 2543 00000008 6C446562756720- .prod: fill 8,32,db "lDebug" ; product 2543 00000008 2544 msg: 2545 ; These strings are international. 2546 00000010 4B454550484F4F4B .mcbname: fill 8,0,db "KEEPHOOK" 2547 00000018 4B454550484F4F4B20- .name: db "KEEPHOOK ",TSR_VERP_STR,": ",36 2547 00000021 312E30303A2024 2548 %include "messages.asm" 2549 <1> %if 0 2550 <1> 2551 <1> English messages for KEEPHOOK 2552 <1> 2021 by C. Masloch 2553 <1> 2554 <1> Usage of the works is permitted provided that this 2555 <1> instrument is retained with the works, so that any entity 2556 <1> that uses the works is notified of this instrument. 2557 <1> 2558 <1> DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. 2559 <1> 2560 <1> %endif 2561 <1> 2562 00000028 6869676820 <1> .highloadedusing:db "high " 2563 0000002D 6C6F61646564207573- <1> .loadedusing: db "loaded using ",36 2563 00000036 696E672024 <1> 2564 0000003B 2062797465 <1> .byte: db " byte" 2565 00000040 2C206F6E206D756C74- <1> .multiplex.1: db ", on multiplex ",36 2565 00000049 69706C65782024 <1> 2566 00000050 682E0D0A24 <1> .multiplex.2: db "h.",13,10,36 2567 <1> .notyet: 2568 00000055 6E6F7420696E737461- <1> .notinstalled: db "not installed.",13,10,36 2568 0000005E 6C6C65642E0D0A24 <1> 2569 00000066 63616E27742072656D- <1> .cantuninstall: db "can't remove",36 2569 0000006F 6F766524 <1> 2570 <1> .uninstall_unhook_failed: 2571 00000073 43616E6E6F74207265- <1> ascic "Cannot remove, not able to uninstall all handlers.",13,10 2571 0000007C 6D6F76652C206E6F74- <1> 2571 00000085 2061626C6520746F20- <1> 2571 0000008E 756E696E7374616C6C- <1> 2571 00000097 20616C6C2068616E64- <1> 2571 000000A0 6C6572732E0D0A24 <1> 2572 <1> .uninstall_no_list: 2573 000000A8 43616E6E6F74207265- <1> ascic "Cannot remove, resident copy did not return list.",13,10 2573 000000B1 6D6F76652C20726573- <1> 2573 000000BA 6964656E7420636F70- <1> 2573 000000C3 7920646964206E6F74- <1> 2573 000000CC 2072657475726E206C- <1> 2573 000000D5 6973742E0D0A24 <1> 2574 <1> .uninstall_cover.none: 2575 000000DC 43616E6E6F7420636F- <1> ascic "Cannot cover anew because no interrupts are hooked.",13,10 2575 000000E5 76657220616E657720- <1> 2575 000000EE 62656361757365206E- <1> 2575 000000F7 6F20696E7465727275- <1> 2575 00000100 707473206172652068- <1> 2575 00000109 6F6F6B65642E0D0A24 <1> 2576 <1> .uninstall.none: 2577 00000112 43616E6E6F7420756E- <1> ascic "Cannot unhook because no interrupts are hooked.",13,10 2577 0000011B 686F6F6B2062656361- <1> 2577 00000124 757365206E6F20696E- <1> 2577 0000012D 746572727570747320- <1> 2577 00000136 61726520686F6F6B65- <1> 2577 0000013F 642E0D0A24 <1> 2578 <1> .cover.done_1: 2579 00000144 496E74657272757074- <1> .cover.undo_1: ascic "Interrupt " 2579 0000014D 2024 <1> 2580 0000014F 6820636F7665726564- <1> .cover.done_2: ascic "h covered.",13,10 2580 00000158 2E0D0A24 <1> 2581 0000015C 6820696E76616C6964- <1> .cover.undo_2: ascic "h invalid.",13,10 2581 00000165 2E0D0A24 <1> 2582 00000169 4572726F723A20436F- <1> .cover.ctrl0: ascic "Error: Control variables incompatible.",13,10 2582 00000172 6E74726F6C20766172- <1> 2582 0000017B 6961626C657320696E- <1> 2582 00000184 636F6D70617469626C- <1> 2582 0000018D 652E0D0A24 <1> 2583 <1> .cover.no_list_entry: 2584 00000192 4572726F723A204E6F- <1> ascic "Error: No free interrupt list entry.",13,10 2584 0000019B 206672656520696E74- <1> 2584 000001A4 657272757074206C69- <1> 2584 000001AD 737420656E7472792E- <1> 2584 000001B6 0D0A24 <1> 2585 <1> .cover.no_block: 2586 000001B9 4572726F723A204E6F- <1> ascic "Error: No free block.",13,10 2586 000001C2 206672656520626C6F- <1> 2586 000001CB 636B2E0D0A24 <1> 2587 <1> .cover.error_internal: 2588 000001D1 4572726F723A20496E- <1> ascic "Error: Internal error (report!).",13,10 2588 000001DA 7465726E616C206572- <1> 2588 000001E3 726F7220287265706F- <1> 2588 000001EC 727421292E0D0A24 <1> 2589 000001F4 435249544943414C20- <1> .criticalunins: db "CRITICAL removal failure (report!).",13,10,36 2589 000001FD 72656D6F76616C2066- <1> 2589 00000206 61696C757265202872- <1> 2589 0000020F 65706F727421292E0D- <1> 2589 00000218 0A24 <1> 2590 0000021A 437269746963616C20- <1> .criticalundone:db "Critical installation failure nullified (report!), " 2590 00000223 696E7374616C6C6174- <1> 2590 0000022C 696F6E206661696C75- <1> 2590 00000235 7265206E756C6C6966- <1> 2590 0000023E 69656420287265706F- <1> 2590 00000247 727421292C20 <1> 2591 0000024D 5453522066756C6C79- <1> db "TSR fully removed.",13,10,36 2591 00000256 2072656D6F7665642E- <1> 2591 0000025F 0D0A24 <1> 2592 00000262 435249544943414C20- <1> .criticalins: db "CRITICAL installation failure (report!)",36 2592 0000026B 696E7374616C6C6174- <1> 2592 00000274 696F6E206661696C75- <1> 2592 0000027D 726520287265706F72- <1> 2592 00000286 74212924 <1> 2593 0000028A 63616E277420696E73- <1> .cantinstall: db "can't install",36 2593 00000293 74616C6C24 <1> 2594 00000298 2C20696E7465727275- <1> .interrupts: db ", interrupts ",36 2594 000002A1 7074732024 <1> 2595 000002A6 2C20696E7465727275- <1> .interrupt: db ", interrupt ",36 2595 000002AF 70742024 <1> 2596 000002B3 6820616E642024 <1> .hand: db "h and ",36 2597 000002BA 68 <1> .hcomma: db "h" 2598 000002BB 2C2024 <1> .comma: db ", ",36 2599 <1> .hookeds: 2600 000002BE 6820686F6F6B656420- <1> .hooked: db "h hooked AMIS incompatible.",13,10,36 2600 000002C7 414D495320696E636F- <1> 2600 000002D0 6D70617469626C652E- <1> 2600 000002D9 0D0A24 <1> 2601 <1> .invalids: 2602 000002DC 6820696E76616C6964- <1> .invalid: db "h invalid.",13,10,36 2602 000002E5 2E0D0A24 <1> 2603 <1> .invalid_interrupt_1: 2604 <1> .double_interrupt_1: 2605 000002E9 4572726F723A20496E- <1> db "Error: Interrupt ",36 2605 000002F2 746572727570742024 <1> 2606 <1> .invalid_interrupt_2: 2607 000002FB 6820697320696E7661- <1> db "h is invalid to specify.",13,10,36 2607 00000304 6C696420746F207370- <1> 2607 0000030D 65636966792E0D0A24 <1> 2608 <1> .double_interrupt_2: 2609 00000316 682073706563696669- <1> db "h specified multiple times.",13,10,36 2609 0000031F 6564206D756C746970- <1> 2609 00000328 6C652074696D65732E- <1> 2609 00000331 0D0A24 <1> 2610 00000334 696E636F6D70617469- <1> .incompatible: db "incompatible version installed.",13,10,36 2610 0000033D 626C65207665727369- <1> 2610 00000346 6F6E20696E7374616C- <1> 2610 0000034F 6C65642E0D0A24 <1> 2611 00000356 616C72656164792069- <1> .already: db "already installed.",13,10,36 2611 0000035F 6E7374616C6C65642E- <1> 2611 00000368 0D0A24 <1> 2612 0000036B 72656D6F7665642E0D- <1> .removed: db "removed.",13,10,36 2612 00000374 0A24 <1> 2613 00000376 6F7574206F66206D65- <1> .nomemory: db "out of memory.",13,10,36 2613 0000037F 6D6F72792E0D0A24 <1> 2614 00000387 4D434220636861696E- <1> .mcbcorrupted: db "MCB chain corrupted.",13,10,36 2614 00000390 20636F727275707465- <1> 2614 00000399 642E0D0A24 <1> 2615 0000039E 6E6F20667265652041- <1> .noamisnumber: db "no free AMIS multiplex number.",13,10,36 2615 000003A7 4D4953206D756C7469- <1> 2615 000003B0 706C6578206E756D62- <1> 2615 000003B9 65722E0D0A24 <1> 2616 000003BF 696E7374616C6C6564- <1> .state: db "installed",36 2616 000003C8 24 <1> 2617 000003C9 4865617020626C6F63- <1> .state.mem.1: db "Heap blocks: ",36 2617 000003D2 6B733A2024 <1> 2618 000003D7 20667265652F24 <1> .state.mem.2: db " free/",36 2619 000003DE 20746F74616C2E0D0A- <1> .state.mem.3: db " total.",13,10,36 2619 000003E7 24 <1> 2620 000003E8 496E74657272757074- <1> .state.1: db "Interrupt ",36 2620 000003F1 2024 <1> 2621 000003F3 6820697320686F6F6B- <1> .state.2: db "h is hooked at ",36 2621 000003FC 65642061742024 <1> 2622 00000403 683A24 <1> .state.3: db "h:",36 2623 00000406 682E0D0A24 <1> .state.4: db "h.",13,10,36 2624 0000040B 4E6F20696E74657272- <1> .state.none: db "No interrupts are hooked.",13,10,36 2624 00000414 757074732061726520- <1> 2624 0000041D 686F6F6B65642E0D0A- <1> 2624 00000426 24 <1> 2625 00000427 496E7465726E616C20- <1> .state.nolist: db "Internal error, no interrupt list returned!",13,10,36 2625 00000430 6572726F722C206E6F- <1> 2625 00000439 20696E746572727570- <1> 2625 00000442 74206C697374207265- <1> 2625 0000044B 7475726E6564210D0A- <1> 2625 00000454 24 <1> 2626 <1> .uninstall_handler.error: 2627 00000455 4572726F7220647572- <1> ascic "Error during handler removal.",13,10 2627 0000045E 696E672068616E646C- <1> 2627 00000467 65722072656D6F7661- <1> 2627 00000470 6C2E0D0A24 <1> 2628 <1> .uninstall_handler.fail_1: 2629 <1> .uninstall_handler.success_1: 2630 <1> .uninstall_specific.none_1: 2631 00000475 496E74657272757074- <1> ascic "Interrupt " 2631 0000047E 2024 <1> 2632 <1> .uninstall_handler.fail_2: 2633 00000480 68206661696C656420- <1> ascic "h failed to be unhooked.",13,10 2633 00000489 746F20626520756E68- <1> 2633 00000492 6F6F6B65642E0D0A24 <1> 2634 <1> .uninstall_handler.success_2: 2635 0000049B 6820756E686F6F6B65- <1> ascic "h unhooked successfully.",13,10 2635 000004A4 642073756363657373- <1> 2635 000004AD 66756C6C792E0D0A24 <1> 2636 <1> .uninstall_specific.none_2: 2637 000004B6 68206E6F7420637572- <1> ascic "h not currently hooked.",13,10 2637 000004BF 72656E746C7920686F- <1> 2637 000004C8 6F6B65642E0D0A24 <1> 2638 000004D0 4E6F20696E74657272- <1> .expose.none: ascicline "No interrupts were to be exposed." 2638 000004D9 757074732077657265- <1> 2638 000004E2 20746F206265206578- <1> 2638 000004EB 706F7365642E0D0A24 <1> 2639 000004F4 636F766572696E6720- <1> .signon.cover: ascicline "covering interrupt chains." 2639 000004FD 696E74657272757074- <1> 2639 00000506 20636861696E732E0D- <1> 2639 0000050F 0A24 <1> 2640 00000511 6578706F73696E6720- <1> .signon.expose: ascicline "exposing interrupt chains." 2640 0000051A 696E74657272757074- <1> 2640 00000523 20636861696E732E0D- <1> 2640 0000052C 0A24 <1> 2641 <1> .signon.cover_all: 2642 0000052E 636F766572696E6720- <1> ascicline "covering all interrupt chains." 2642 00000537 616C6C20696E746572- <1> 2642 00000540 727570742063686169- <1> 2642 00000549 6E732E0D0A24 <1> 2643 <1> .signon.expose_all: 2644 0000054F 6578706F73696E6720- <1> ascicline "exposing all interrupt chains." 2644 00000558 616C6C20696E746572- <1> 2644 00000561 727570742063686169- <1> 2644 0000056A 6E732E0D0A24 <1> 2645 <1> .signon.uninstall_specific_cover: 2646 00000570 756E696E7374616C6C- <1> ascicline "uninstalling handlers then covering interrupt chains." 2646 00000579 696E672068616E646C- <1> 2646 00000582 657273207468656E20- <1> 2646 0000058B 636F766572696E6720- <1> 2646 00000594 696E74657272757074- <1> 2646 0000059D 20636861696E732E0D- <1> 2646 000005A6 0A24 <1> 2647 <1> .signon.uninstall_specific: 2648 000005A8 756E696E7374616C6C- <1> ascicline "uninstalling handlers from interrupt chains." 2648 000005B1 696E672068616E646C- <1> 2648 000005BA 6572732066726F6D20- <1> 2648 000005C3 696E74657272757074- <1> 2648 000005CC 20636861696E732E0D- <1> 2648 000005D5 0A24 <1> 2649 <1> .signon.uninstall_completely_cover: 2650 000005D7 756E696E7374616C6C- <1> ascicline "uninstalling all handlers then covering interrupt chains." 2650 000005E0 696E6720616C6C2068- <1> 2650 000005E9 616E646C6572732074- <1> 2650 000005F2 68656E20636F766572- <1> 2650 000005FB 696E6720696E746572- <1> 2650 00000604 727570742063686169- <1> 2650 0000060D 6E732E0D0A24 <1> 2651 <1> .signon.uninstall_completely_all: 2652 00000613 756E696E7374616C6C- <1> ascicline "uninstalling all handlers." 2652 0000061C 696E6720616C6C2068- <1> 2652 00000625 616E646C6572732E0D- <1> 2652 0000062E 0A24 <1> 2653 <1> .signon.uninstall_completely: 2654 00000630 756E696E7374616C6C- <1> ascicline "uninstalling resident." 2654 00000639 696E67207265736964- <1> 2654 00000642 656E742E0D0A24 <1> 2655 00000649 54535220746F206B65- <1> .help: db "TSR to keep hooks available. AMIS v3.6 compliant.",13,10 2655 00000652 657020686F6F6B7320- <1> 2655 0000065B 617661696C61626C65- <1> 2655 00000664 2E20414D4953207633- <1> 2655 0000066D 2E3620636F6D706C69- <1> 2655 00000676 616E742E0D0A <1> 2656 0000067C 4F7074696D616C2069- <1> db "Optimal installation, advanced deinstallation method.",13,10 2656 00000685 6E7374616C6C617469- <1> 2656 0000068E 6F6E2C20616476616E- <1> 2656 00000697 636564206465696E73- <1> 2656 000006A0 74616C6C6174696F6E- <1> 2656 000006A9 206D6574686F642E0D- <1> 2656 000006B2 0A <1> 2657 000006B3 4672656520736F6674- <1> db "Free software by C. Masloch",13,10 2657 000006BC 776172652062792043- <1> 2657 000006C5 2E204D61736C6F6368- <1> 2657 000006CE 0D0A <1> 2658 000006D0 4F7074696F6E733A20- <1> db "Options: (no) Install or show state",13,10 2658 000006D9 286E6F292020202049- <1> 2658 000006E2 6E7374616C6C206F72- <1> 2658 000006EB 2073686F7720737461- <1> 2658 000006F4 74650D0A <1> 2659 000006F8 20202020202020202F- <1> db " /o Only show or set state, don't install",13,10 2659 00000701 6F202020202020204F- <1> 2659 0000070A 6E6C792073686F7720- <1> 2659 00000713 6F7220736574207374- <1> 2659 0000071C 6174652C20646F6E27- <1> 2659 00000725 7420696E7374616C6C- <1> 2659 0000072E 0D0A <1> 2660 00000730 20202020202020202F- <1> db " /j Install into low memory area (LMA)",13,10 2660 00000739 6A2020202020202049- <1> 2660 00000742 6E7374616C6C20696E- <1> 2660 0000074B 746F206C6F77206D65- <1> 2660 00000754 6D6F72792061726561- <1> 2660 0000075D 20284C4D41290D0A <1> 2661 00000765 20202020202020202F- <1> db " /n Install new even if already installed",13,10 2661 0000076E 6E2020202020202049- <1> 2661 00000777 6E7374616C6C206E65- <1> 2661 00000780 77206576656E206966- <1> 2661 00000789 20616C726561647920- <1> 2661 00000792 696E7374616C6C6564- <1> 2661 0000079B 0D0A <1> 2662 0000079D 20202020202020202F- <1> db " /x=NN Try multiplex number NNh first",13,10 2662 000007A6 783D4E4E2020202054- <1> 2662 000007AF 7279206D756C746970- <1> 2662 000007B8 6C6578206E756D6265- <1> 2662 000007C1 72204E4E6820666972- <1> 2662 000007CA 73740D0A <1> 2663 000007CE 20202020202020202F- <1> db " /s=num Set amount of allocation units to reserve (install only)",13,10 2663 000007D7 733D6E756D20202053- <1> 2663 000007E0 657420616D6F756E74- <1> 2663 000007E9 206F6620616C6C6F63- <1> 2663 000007F2 6174696F6E20756E69- <1> 2663 000007FB 747320746F20726573- <1> 2663 00000804 657276652028696E73- <1> 2663 0000080D 74616C6C206F6E6C79- <1> 2663 00000816 290D0A <1> 2664 00000819 20202020202020202F- <1> db " /c Cover listed interrupts (or all hooked ones)",13,10 2664 00000822 632020202020202043- <1> 2664 0000082B 6F766572206C697374- <1> 2664 00000834 656420696E74657272- <1> 2664 0000083D 7570747320286F7220- <1> 2664 00000846 616C6C20686F6F6B65- <1> 2664 0000084F 64206F6E6573290D0A <1> 2665 00000858 20202020202020202F- <1> db " /e Expose listed interrupts (or all hooked ones)",13,10 2665 00000861 652020202020202045- <1> 2665 0000086A 78706F7365206C6973- <1> 2665 00000873 74656420696E746572- <1> 2665 0000087C 727570747320286F72- <1> 2665 00000885 20616C6C20686F6F6B- <1> 2665 0000088E 6564206F6E6573290D- <1> 2665 00000897 0A <1> 2666 00000898 20202020202020202F- <1> db " /u, /r Remove from memory, or uninstall listed interrupts",13,10 2666 000008A1 752C202F7220202052- <1> 2666 000008AA 656D6F76652066726F- <1> 2666 000008B3 6D206D656D6F72792C- <1> 2666 000008BC 206F7220756E696E73- <1> 2666 000008C5 74616C6C206C697374- <1> 2666 000008CE 656420696E74657272- <1> 2666 000008D7 757074730D0A <1> 2667 000008DD 20202020202020202F- <1> db " /?, /h This help message",13,10 2667 000008E6 3F2C202F6820202054- <1> 2667 000008EF 6869732068656C7020- <1> 2667 000008F8 6D6573736167650D0A <1> 2668 00000901 0D0A <1> db 13,10 2669 00000903 546865206F7074696F- <1> db "The options /j and /n can be combined.",13,10 2669 0000090C 6E73202F6A20616E64- <1> 2669 00000915 202F6E2063616E2062- <1> 2669 0000091E 6520636F6D62696E65- <1> 2669 00000927 642E0D0A <1> 2670 0000092B 546865207377697463- <1> db "The switch character - is also accepted." 2670 00000934 682063686172616374- <1> 2670 0000093D 6572202D2069732061- <1> 2670 00000946 6C736F206163636570- <1> 2670 0000094F 7465642E <1> 2671 00000953 0D0A24 <1> .crlf: db 13,10,36 2549 2550 endarea msg 2551 2552 %if _UPXPAD && (transient_size + resident_size + msg_size) < 3062 2553 section UPXPADDING align=1 follows=MESSAGE 2554 times (3062 -(transient_size + resident_size + msg_size))/ 8 db "UPXPAD " 2555 times (3062 -(transient_size + resident_size + msg_size))% 8 db 'U' 2556 %endif