-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtransien.asm
2149 lines (1919 loc) · 53 KB
/
transien.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
%if 0
Transient code of TSR example; command line parser and setup
2020 by C. Masloch
Usage of the works is permitted provided that this
instrument is retained with the works, so that any entity
that uses the works is notified of this instrument.
DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.
%endif
%assign __lMACROS1_MAC__DEBUG_DEFAULTS 1
%include "lmacros3.mac"
%include "AMIS.MAC"
%idefine TSRNAME "123123"
defaulting
verdef TSR,C, 1,00 ; Version number of last compatible
verdef TSR,P, 0,01 ; Private version
numdef PSPRELOC, 1 ; relocate PSP
numdef UPXPAD, 0 ; pad executable to >=3062 bytes for UPX
; Option flags (hold in bp)
_onlystate equ 0001h
_installlow equ 0002h
_installnew equ 0004h
_uninstall equ 0008h
_state1 equ 0040h ; must match bit in ctrl0
_state2 equ 0080h ; must match bit in ctrl0
_state equ _state1 | _state2
_1set equ _state1<<2
_2set equ _state2<<2
_foundresident equ 1000h
_incompatresident equ 2000h
_cur1234 equ 0010h
_curwasd equ 0020h
_nochain equ 4000h
_numpad equ 8000h
_1234set equ 0001h
_wasdset equ 0002h
_numpadset equ 0004h
%ifdef _MAP
[map all _MAP]
%endif
cpu 8086
org 100h
section TRANSIENT align=1 ; Transient program part (command line parser, setup)
transient:
cld
mov dx, msg.name
call disp_msg ; Start with message
; The UMB link and memory allocation strategy is now always safed here
; and restored by abort_msg as well as any other termination code. This
; saves any code changing the strategy the effort to restore it proper.
mov ax, 5802h
int 21h
xor ah, ah ; some "DOS" only return al
mov word [ restored.umblink ], ax ; save UMB link
mov ax, 5800h
int 21h
mov word [ restored.strategy ], ax ; save strategy
mov dx, i23
mov ax, 2523h
int 21h ; set Int23 so that we restore link/strategy on Ctrl-C as well
mov dx, i24
mov ax, 2524h
int 21h ; set Int24 so all hard errors are changed to the soft error
checkhandlers:
mov ax, 352Dh
int 21h
cmp bx, -1
je .inv
mov ax, es
test ax, ax
jz .inv
mov ax, 352Fh
int 21h
cmp bx, -1
je .inv
mov ax, es
test ax, ax
; jz .inv
jnz @F
.inv:
mov dx, msg.handlersbad
jmp abort_msg
@@:
getfirstumcb:
; We try to get the first UMCB for various uses later. Note that we
; never use the result as an MCB address so if it's wrong we won't
; crash or something. (We compare memory block or MCB addresses to
; the value to check whether they're in the UMA.)
mov ax, 1261h ; PTS-DOS: Get first UMCB
stc
int 2Fh
jc .determine ; not supported -->
inc ax
cmp ax, byte 2 ; -1, 0, 1 ?
jbe .determine ; not supported (or none) -->
dec ax
mov word [ firstumcb ], ax ; set UMB
jmp short .got ; got it -->
.determine:
mov ax, 5803h
xor bx, bx
stc
int 21h ; disable UMB link, leave only LMA chain
jc .none ; that isn't supported either -->
mov bx, -1
mov ah, 52h
int 21h ; get Lists of Lists
cmp bx, -1 ; unchanged ?
je .none ; yes -->
cmp bx, 1 ; bx - 2 would be FFFFh ?
je .none ; yes, avoid fault -->
mov ax, word [ es:bx - 2 ]
mov dx, ax ; first MCB
xor bx, bx ; use offsets from bx, not addresses
.looplmb:
mov ds, ax
inc ax
add ax, word [ bx + 3 ] ; next MCB's address
cmp byte [ bx ], 'M'
je .looplmb ; not last -->
cmp byte [ bx ], 'Z'
jne .none ; corrupted -->
xchg ax, dx ; dx = what we assume to be the first UMA chain MCB
; ax = first MCB
push ax
inc bx ; = 1
mov ax, 5803h
int 21h ; enable UMB link, include UMA chain
pop ax
jc .none ; so we can disable it but not enable? -->
dec bx ; = 0
xor cx, cx ; flag if assumed first UMCB found
.loopumb:
cmp ax, dx
jne .notlastlmb
inc cx ; there it is
.notlastlmb:
mov ds, ax
cmp byte [ bx ], 'M'
jne .islastumb? ; last or corrupted -->
inc ax
add ax, word [ bx + 3 ]
jmp short .loopumb ; process next -->
.islastumb?:
cmp byte [ bx ], 'Z'
jne .none ; corrupted -->
jcxz .none ; didn't find that UMCB -->
; The MCB at dx which was behind the one that contained the 'Z'
; signature when we disabled the UMB link is now a valid MCB in
; the MCB chain after we enabled the UMB link. All previous MCBs
; are now 'M'.
mov word [ cs:firstumcb ], dx
.none:
.got:
; no need to restore es here since we do that below
; no need to restore ds since we set it below
scancl: ; bp = selected options
xor bp, bp ; initial option flags
push cs
pop es
push cs
pop ds ; ds, es changed while searching UMCB
mov si, 81h ; PSP command line
.loop: lodsb
cmp al, 9 ; tab ?
je .loop
cmp al, 32 ; blank ?
je .loop
jb .end ; zero, cr, lf ? -->
cmp al, '-'
je .switch
cmp al, '/'
jne .invalid ; unknown character -->
; parse switch
.switch:
lodsb ; get next character
call capitalise
; check against known switches
push si
mov si, parameters
xchg al, ah
.loopswitch:
lodsb
xchg ax, dx
lodsw ; get function to call
xchg ax, dx
xchg ax, cx
lodsw
xchg ax, cx ; cx = option flags
cmp al, 0FFh
je .invalid ; not found!
cmp al, ah
jne .loopswitch ; try next -->
pop si
call dx
jmp .loop
.switch_j:
lodsb
cmp al, '-'
je .j_plusminus
cmp al, '+'
je .j_plusminus
dec si
cmp al, '/'
je .clear_umb
cmp al, 13
je .clear_umb
cmp al, 9
je .clear_umb
cmp al, 32
je .clear_umb
mov al, '+'
.j_plusminus:
mov ah, 0
mov byte [old_j_switch], ah
cmp al, '+'
je @F
mov ah, -1
@@:
lodsb
cmp al, '-'
je .j_plusminus
cmp al, '+'
je .j_plusminus
cmp al, 13
je .dec_retn
cmp al, 9
je .dec_retn
cmp al, 32
je .dec_retn
call capitalise
mov di, j_options
mov cx, j_options.amount
repne scasb
jne .invalid
mov byte [di - (j_options + 1) + j_flags], ah
jmp @B
.clear_umb:
mov byte [j_flags.umb], 0
retn
.set_in_bp:
test cl, _state ; state ?
jz .normalbp ; no, normal option --> (sets bit in bp)
; cx = bit to set or clear in bp
; cx<<2 = bit to set in bp to show the cx bit was set
; ds:si-> '+' to set, '-' to clear
lodsb
cmp al,'-' ; OFF ?
jne .notminus
call flagset
not cx ; reverse bits
and bp,cx ; do bp &= ~cx
retn
.notminus:
cmp al,'+' ; ON ?
jne .invalid
call flagset
; then fall through to do bp |= cx
.normalbp:
or bp, cx
retn ; ds:si-> character after switch character. just continue
.dec_retn:
dec si
retn
.invalid: ; display help if invalid
mov dx, msg.help
jmp abort_msg
.multiplexnumber:
lodsb
cmp al, '='
je @FF
cmp al, ':'
je @FF
db __TEST_IMM8
@@:
lodsb
cmp al, 9
je @B
cmp al, 32
je @B
jb .invalid
db __TEST_IMM8
@@:
lodsb
call getnybble
jc .invalid
xor bx, bx
mov bl, al
lodsb
call getnybble
jc .check_end_multiplexnumber
add bx, bx
add bx, bx
add bx, bx
add bx, bx
add bl, al
lodsb
.check_end_multiplexnumber:
cmp al, 9
je @F
cmp al, 32
ja .invalid
@@:
xchg bh, bl
mov word [trymultiplexnumber], bx
dec si
retn
.limnumber:
lodsb
cmp al, '='
je @FF
cmp al, ':'
je @FF
db __TEST_IMM8
@@:
lodsb
cmp al, 9
je @B
cmp al, 32
je @B
jb .invalid
db __TEST_IMM8
@@:
lodsb
call getnybble
jc .invalid
xor bx, bx
mov bl, al
lodsb
@@:
mov byte [lim_to_set], bl
dec si
retn
.end:
; Search for installed debugger and our resident
findinstalled:
lframe none
lenter
mov ax, cs
add ax, 10h+transient_size_p
lvar dword, signature_us
push ax
mov bx, amissig
push bx
lvar dword, signature_debugger
push ds
mov bx, msg.debuggeramissig
push bx
lvar word, function
mov bx, .checkboth
rol byte [j_flags.detectdebugger], 1
jc @F
mov bx, .checkus
@@:
push bx
lvar dword, our_ctrl0
mov bx, ctrl0 ; ds:bx -> resident segment's ctrl0
push ax
push bx
lvar word, our_multiplex_high
push ax ; (uninitialised)
; This loop is optimised so that the minimum
; number of instructions is run, aiding the
; debugger when it traces this loop.
; An indirect function call is used to decide
; which checks to run, either the debugger
; only, our resident only, or both. This
; starts out as both, but will change if the
; both handler finds one of them.
; If a single handler subsequently also finds
; its multiplexer it won't return here and
; instead branch to the end, discarding the
; near return address to here.
; We can check both multiplexers using only
; one int 2Dh call, avoiding the need for
; querying the same multiplexer twice.
mov ax, word [trymultiplexnumber]
test al, al
jnz @F
; This changes the behaviour slightly: The
; try number now is used for the debugger,
; as well. This is considered acceptable.
call near [bp + ?function]
@@:
mov ah, 0FFh ; start with multiplex number 0FFh
.loop:
call near [bp + ?function]
sub ah, 1 ; search is backward (to find latest installed first), from 0FFh to 00h including
jnc .loop ; try next if we didn't check all yet -->
jmp .end ; if not both found, continue -->
; INP: ah = multiplex number to check
; word [bp + ?function] -> .checkboth
; OUT: always returns
; ah = multiplex number (unmodified)
; word [bp + ?function] -> function to call for next check
; if debugger found,
; word [debuggerfunction] = multiplex number high, 30h low
; if resident instance of us found,
; word [bp + ?frame_bp] = flags set appropriately
; dword [bp + ?our_ctrl0] -> ctrl0 byte
; byte [bp + ?our_multiplex_high + 1] = multiplex number
; if neither found,
; variables unmodified
; CHG: al, ds, si, es, di, dx, cx, bx
.checkboth:
mov al, 00h ; AMIS installation check
int 2Dh ; AMIS (or "DOS reserved" = iret if no AMIS present)
cmp al, 0FFh
jne .notfound
lds si, [bp + ?signature_debugger]
; ds:si -> debugger AMIS name strings
mov es, dx ; es:di -> name strings of AMIS multiplexer that just answered
mov cx, 8 ; Ignore description, only compare vendor and program name
push di
repe cmpsw
pop di
jne @F
mov al, 30h ; al = 30h to indicate found, ah = multiplex number
; ! ds from ?signature_debugger same as for debuggerfunction
mov word [debuggerfunction], ax
mov word [bp + ?function], .checkus
; next only check for our resident
@@:
lds si, [bp + ?signature_us]
; ds:si -> our AMIS name strings
mov cx, 8 ; Ignore description, only compare vendor and program name
repe cmpsw
je .found_from_both ; if match -->
.notfound: ; No match, try next
retn
.found_from_both:
%if 0
; Commented because we know that the debugger
; and our resident cannot both match the same
; multiplexer as the signatures differ. Uncomment
; if that is not true.
call .foundus ; handle our resident
cmp word [bp + ?function], .checkus
; debugger matched for same multiplexer ?
je .end_pop ; yes, do not continue search -->
mov word [bp + ?function], .checkdebugger
; next only check for debugger
retn
%else
mov word [bp + ?function], .checkdebugger
; next only check for debugger
jmp .foundus ; (tail call)
%endif
; INP: ah = multiplex number to check
; word [bp + ?function] -> .checkus
; OUT: returns if multiplex number unused or no signature match,
; ah = multiplex number (unmodified)
; if resident instance of us found,
; does not return and branches to .end instead
; word [bp + ?frame_bp] = flags set appropriately
; dword [bp + ?our_ctrl0] -> ctrl0 byte
; byte [bp + ?our_multiplex_high + 1] = multiplex number
; if not found,
; variables unmodified
; CHG: al, ds, si, es, di, dx, cx, bx
.checkus:
mov al, 00h ; AMIS installation check
int 2Dh ; AMIS (or "DOS reserved" = iret if no AMIS present)
cmp al, 0FFh
jne .notfound
mov es, dx ; es:di -> name strings of AMIS multiplexer that just answered
lds si, [bp + ?signature_us]
; ds:si -> our AMIS name strings
mov cx, 8 ; Ignore description, only compare vendor and program name
repe cmpsw
jne .notfound
.found_from_us:
call .foundus
jmp .end_pop
; INP: ah = multiplex number to check
; word [bp + ?function] -> .checkdebugger
; OUT: returns if multiplex number unused or no signature match,
; ah = multiplex number (unmodified)
; if debugger found,
; does not return and branches to .end instead
; word [debuggerfunction] = multiplex number high, 30h low
; if not found,
; variables unmodified
; CHG: al, ds, si, es, di, dx, cx, bx
.checkdebugger:
mov al, 00h ; AMIS installation check
int 2Dh ; AMIS (or "DOS reserved" = iret if no AMIS present)
cmp al, 0FFh
jne .notfound
lds si, [bp + ?signature_debugger]
; ds:si -> debugger AMIS name strings
mov es, dx ; es:di -> name strings of AMIS multiplexer that just answered
mov cx, 8 ; Ignore description, only compare vendor and program name
repe cmpsw
jne .notfound
mov al, 30h ; al = 30h to indicate found, ah = multiplex number
; ! ds from ?signature_debugger same as for debuggerfunction
mov word [debuggerfunction], ax
jmp .end_pop
.foundus:
mov word [bp + ?our_ctrl0 + 2], dx
mov word [bp + ?our_multiplex_high], ax
mov al, 10h ; get compatible TSR version
int 2Dh
cmp al, 0FFh ; al = 0FFh if supported
jne .unknown ; doesn't support this question -->
cmp cx, TSR_VERC ; Compatible version?
jne .unknown
mov al, 20h ; Get TSR ctrl0
int 2Dh
cmp al, 00h ; al = size of returned ctrl data. at least one byte if supported
jne .known ; if size ok, known -->
.unknown:
setopt [bp + ?frame_bp], _incompatresident
; indicate resident copy is incompatible
.known:
setopt [bp + ?frame_bp], _foundresident
; indicate resident copy found
mov word [bp + ?our_ctrl0], bx
retn
.end_pop:
pop cx ; (discard near return address)
.end:
pop ax ; pop from ?our_multiplex_high
pop bx
pop ds ; pop from ?our_ctrl0
; set ds to our resident copy's segment
lleave
; ah = AMIS multiplex number of resident copy if bp&_foundresident (else 0FFh)
; ds:bx-> ctrl0 of either resident copy or stored handler
process:
rol byte [ss:j_flags.umb], 1
jc @F
or bp, _installlow
@@:
test bp, _uninstall
jz .dontuninstall
rol byte [ss:old_j_switch], 1
jnc @F
test bp, _installlow
jnz scancl.invalid ; old /J switch invalid if /u selected
@@:
test bp, _installnew|_1set|_2set
jnz scancl.invalid ; all invalid if /u selected
jmp uninstall
.dontuninstall:
test bp, _foundresident ; no resident found ?
jz .install ; yes, install -->
test bp, _installnew ; forced to install new ?
jz .dontinstall ; no -->
test bp, _onlystate
jnz scancl.invalid ; invalid combination -->
.install:
test bp, _onlystate ; don't install ?
jz .installallowed
mov dx, msg.notyet
jmp short .abort_msg
.installallowed:
jmp install
.dontinstall:
rol byte [ss:old_j_switch], 1
jnc .changeflags
test bp, _installlow
jz .changeflags
mov dx, msg.already ; can't install new, already resident
jmp short .abort_msg
.changeflags:
test bp, _incompatresident
jz .compatible
mov dx, msg.incompatible ; can't change flags of incompatible version
.abort_msg:
jmp abort_msg
.compatible:
push ax
call changeflags
pop ax ; AMIS multiplex number
mov al, 20h
int 2Dh ; clear unsupported bits
; Display status
mov dx, msg.state1 ; "state 1: "
call disp_msg
test byte [bx], 40h
call onoff ; "ON (+)" or "OFF (-)" depending on ZF
mov dx, msg.state2 ; ", 2: "
call disp_msg
test byte [bx], 80h
call onoff
mov al, ah
mov dx, msg.multiplex.1
call disp_msg
call disp_amisnum
call restorestate
mov ax, 4C00h
int 21h
flagset:
push cx
rol cx, 1
rol cx, 1
or bp, cx
pop cx
retn
capitalise:
cmp al, 'a'
jb .notlowercase
cmp al, 'z'
ja .notlowercase
and al, ~ 20h ; Uppercase if required
.notlowercase:
retn
getnybble:
cmp al, '0'
jb .invalid ; unknown character -->
cmp al, '9'
jbe @F
call capitalise
cmp al, 'A'
jb .invalid
cmp al, 'F'
ja .invalid
sub al, 'A' - 10 - '0'
@@:
sub al, '0'
db __TEST_IMM8 ; (NC, skip stc)
.invalid:
stc
retn
; INP: ds:si -> source IISP header (or pseudo header)
; es:di -> destination IISP header
; OUT: EI
; si and di both incremented by 6
; CHG: -
; STT: UP
update_iisp_header:
push ax
mov ax, word [cs:debuggerfunction]
test ax, ax ; found the debugger ?
jz @F ; no -->
int 2Dh ; call its Update IISP Header function
cmp al, 0FFh ; supported ?
pop ax
je .ret ; yes. done -->
db __TEST_IMM8 ; (skip pop)
@@:
pop ax ; restore ax, then do manual update
cli ; try to rest while updating chain
cmpsw ; skip over first word (entrypoint)
; (generally xxEBh or 0EA90h)
movsw
movsw ; transfer source ieNext to dest ieNext
sti
.ret:
retn
; INP: al = interrupt number
; ds:si-> interrupt entry
; OUT: CY if unhooking failed
; NC if unhooking successful
; CHG: ah, es, di, si
UnhookInterrupt:
; UnhookInterruptSim (below) only checks if it's possible to unhook this interrupt.
; This function really unhooks the interrupt if possible.
;
; This is to cover the situation when some of the hooked interrupts can unhook,
; but some can't. If the uninstaller would start to unhook the interrupts and then
; catch the interrupt that can't be unhooked the user would end up with a dead TSR
; that's uninstalled halfway. Very bad.
;
; "Simulating" the unhooking first and checking if all interrupts can unhook
; usually will not return such a state.
call UnhookInterruptSim
jc .ret ; bad. --> (CY)
jz .easy
.hard:
; "hard" case: UnhookInterruptSim has however already done the work,
; so the hard case is here indeed easier than the easy case.
call update_iisp_header ; copies our stored pointer into the other's entry
clc
retn
.easy:
push ds
push dx
lds dx, [ si + 2 ] ; get what we stored in the entry
mov ah, 25h ; easy case - just reset to the value stored
int 21h ; doesn't alter CF (leaves NC from UnhookInterruptSim) or sets NC
pop dx
pop ds
.ret:
retn
; INP: ds:si-> IISP entry
; al = interrupt number
; OUT: NC if no error (either hard or easy case),
; ZR if easy case,
; ds:si-> our IISP entry, containing stored interrupt
; NZ if hard case,
; ds:si-> our IISP entry
; es:di-> IISP entry to modify
; CY if error (not first handler and no IISP chain to this handler)
; CHG: ah, es, di
UnhookInterruptSim:
push bx
; harden this, check we are an IISP entry
push ds
pop es ; es => our handler segment
mov bx, si ; es:bx -> our handler
call IsIISPEntry? ; does it have an IISP header ?
jne .fail ; fail if not
mov ah, 35h ; get current vector
int 21h ; es:bx-> current interrupt handler
cmp si, bx ; our pointer ?
jne .hard
push ax
push si
mov si, ds
mov ax, es
cmp si, ax ; our segment ?
pop si
pop ax
jne .hard
and ah, 00h ; NC, ZR
pop bx
retn
.hard:
; INP: ds:si-> IISP entry
; es:bx-> current interrupt entry
; OUT: CY if error
; NC, NZ if no error,
; ds:si-> our IISP entry
; es:di-> IISP entry to modify
; CHG: ah, es, di, (bx)
call SearchIISPChain
jne .harder
.found: ; found reference to our interrupt handler
mov di, bx ; es:di-> IISP entry that references our's
or ah, 0FFh ; NC, NZ
pop bx
retn
.harder:
; Desperate attempt to find IISP entry that references ours by
; searching through the interrupts hooked by other AMIS TSRs. Note
; that the plexer loop will find and search through the list of
; hooked interrupts of the uninstalling TSR itself, but this causes
; no trouble.
; INP: ds:si-> IISP entry
; OUT: CY if error
; NC, NZ if no error,
; ds:si-> our IISP entry
; es:di-> IISP entry to modify
; CHG: ah, es, di, (bx)
d4bp
push cx
push dx
push ax ; register with interrupt number last
xor ax, ax
.loopplex:
mov al, 00h ; AMIS installation check
int 2Dh ; enquire whether there's anyone
; but we don't care who it might be
inc al
jz .search
.nextplex:
inc ah
jnz .loopplex ; try next multiplexer -->
pop ax
pop dx
pop cx
.fail: ; IISP incompatible TSR between current interrupt entry and our entry
; and no AMIS compatible TSR installed on top of our entry
stc
pop bx
retn
; INP: ah = multiplex number of AMIS TSR to search through
; ss:sp-> interrupt number (byte), must be preserved
; CHG: es, di, dx, bx
.search:
mov al, 04h
pop bx
push bx ; low byte is the interrupt number
int 2Dh
cmp al, 03h ; returned its interrupt entry ?
; RBIL doesn't explicitly state that this interrupt entry has to
; be IISP compatible. But I'm too lazy to look up the older AMIS,
; and SearchIISPChain checks the interrupt entry anyway.
je .search_dxbx
cmp al, 04h ; returned list of hooked interrupts ?
jne .nextplex ; no, try next multiplexer -->
mov di, bx
pop bx
push bx ; bl = interrupt number
mov al, bl
.search_intlist_seg:
mov es, dx ; es:di-> list
.search_intlist: ; Search the returned list for the required interrupt number.
scasb ; our interrupt number ?
je .search_found_intlist
cmp byte [es:di-1], 2Dh ; was last in list ?
je .nextplex
scasw ; skip pointer
jmp short .search_intlist ; try next entry -->
.search_found_intlist:
mov bx, word [es:di] ; dx:bx-> IISP entry
scasw ; skip pointer
call SearchIISPChain
je .search_found ; found entry -->
; This specific jump supports TSRs that hook the same
; interrupt more than once; jumping to .nextplex instead
; (as previously) aborts the search after the first match
; in the interrupt list. This support might become useful.
cmp al, 2Dh ; was last in list ?
je .nextplex
jmp short .search_intlist_seg
.search_dxbx:
mov es, dx ; es:bx-> (IISP) interrupt entry
; The entry we found now is possibly behind the non-IISP entry that
; terminated our first SearchIISPChain call (at .hard). We then
; possibly might find our entry in this hidden part of the chain.
call SearchIISPChain
jne .nextplex ; didn't find our entry in the chain -->
.search_found:
pop ax
pop dx
pop cx
jmp short .found
SearchIISPChain.next:
les bx, [es:bx +2] ; get next interrupt entry
; INP: ds:si-> IISP entry
; es:bx-> current interrupt entry
; OUT: NZ if reference to ds:si not found in IISP chain es:bx->
; ZR if reference found,
; es:bx-> IISP (or uninstalled iHPFS) interrupt entry with reference
; CHG: es, bx
SearchIISPChain:
call IsIISPEntry? ; that an IISP entry ?
jnz .return ; nope --> (NZ)
cmp si, word [ es:bx + ieNext ] ; our pointer ?
jne .next ; no, try next -->
push ax
mov ax, ds
cmp ax, word [ es:bx + ieNext + 2] ; our segment ?
pop ax
jne .next ; no, try next -->
.return: ; yes, found (ZR)
retn
; If unhooking (or hooking) failed after the simulation succeeded for all
; interrupts, at least one interrupt changed between the simulation and the
; attempt to really (un)hook that interrupt. This is kind of a critical
; error because it'll happen very rarely, and I'd be interested to hear
; about software with which it does happen.
unhookerror:
xor cx, cx ; (don't display critical failure message)
unhookerrorcritical:
mov dx, msg.cantuninstall
.install:
call disp_msg
pop dx
cmp dx, byte -1 ; at least another interrupt failed ? (at least two interrupts failed ?)
push dx
je .single
mov dx, msg.interrupts ; first message for multiple interrupts
jmp short .first
.single:
mov dx, msg.interrupt ; first message for single interrupt
.first:
mov bx, dx
.loop:
call disp_msg ; msg.interrupts or msg.hcomma
call disp_al_hex
pop ax ; get next value from stack
pop dx
cmp dx, byte -1 ; at least two more ?
push dx
mov dx, msg.hcomma ; message if at least two more interrupts to display
jne .notlast ; yes -->
mov dx, msg.hand ; only one more interrupt to display
.notlast:
cmp ax, byte -1 ; last error marker?
jne .loop ; no, keep looping -->
mov dx, msg.hooked ; last message for single interrupt
cmp bx, msg.interrupt ; was first message for single interrupt ?
je .abort ; yes -->
mov dx, msg.hookeds ; else use last message for multiple interrupts
.abort:
jcxz .abort_msg ; not critical (or a critical installation failure) -->
call disp_msg
mov dx, msg.criticalunins ; else display critical failure message
.abort_msg:
jmp abort_msg
uninstall:
d4bp
test bp, _foundresident
jnz .found