Autumn leaves, Terminate and Stay Resident!

In the year 1991 it was not very common in old Germany that we pupils in eleventh grade had computer science classes at school. As unusual as the computer science lessons were for us students, those lessons were even more unusual for our teachers. Few of my mates and me already had computers since the 4th or 5th grade, however our teachers encountered computers and programming in their adulthood for the first time. So there was some kind of generation gap.

Nevertheless, our computer science teacher had very solid knowledge on programming with Pascal (especially Turbo Pascal) and he was very keen to see what we youngsters were up to on computing. So he had a great idea and proposed that for our certificate grade of that year’s computer science course we were allowed to present a program of our own (if we wanted to). Yes, I wanted to! A class mate of mine already got very much into object orientated programming with the newest version of Turbo Pascal back then, and as I did not want to submit some “me-too” code, I decided to go for an X86 assembly program for MS-DOS.

As in the 1980s and 1990s computer viruses for the MS-DOS operating system were doing the rounds, I stumbled over some reports regarding the Herbstlaub Virus, also known as Cascade Virus. As an effect of being infected by the Cascade Virus, different letters printed on a PC’s text screen would fall down from time to time, one after the other (as of this video ).

“Herbstlaub” is German for “autumn leaves” - the Cascade Virus is also called Herbstlaub Virus as it makes the letters drop down from the top of the screen to the bottom, similar to autumn, which makes the leaves of the trees drop down from the branches to the ground.

Yes, that would be the kind of program I wanted to submit! Don’t get me wrong, I did not want to submit a computer virus, I just wanted to submit a program simulating that Herbstlaub Virus’s effect of letters falling down on a computer’s screen. Or what I thought that effect was, as there neither was Youtube back then where I could watch a video of that effect nor I ever saw the Herbstlaub Virus in action. So I implemented my interpretation of that effect as a TSR program and presented it to my computer sciences teacher

As it now turns out now, my TSR’s “Herbstlaub” effect was like the Cascade Virus on speed.

I was lucky, my teacher liked the program, he was keen on understanding the assembly code and gave me an A grade! Below find some images before and after invoking “Herbstlaub” (and above an according video). The screenshots were taken from a DOSBox emulator software emulating MS-DOS on a modern operating system with “Herbstalub” up and running:

The screenshots above on the right hand side suggest that the Herbstlaub algorithm could also be used to create artificial worlds for side scroller games. Imagine a little space ship shooting holes into the pile of letters with the letters rearranging immediately as of the Herbstlaub algorithm, making the pile collapse accordingly…

The assembly code for “Herbstlaub” requires the Turbo Assembler for getting assembled to a running “Herbstlaubexecutable:

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
TITLE     HERBST.ASM version 3.2b

;NOCH MACHEN --> IN AL,60H: ...PUSH AX ...POP AX!!!!!

;=============================================================================
; HOTKEY --> FRAME für ASSEMBLER-HOTKEY-PROGRAMME
; (W) 1991 BY S.STEINER
;=============================================================================

MAXCHAR   EQU 80                        ;80 Zeichen pro Zeile!?!
KEYMASK   EQU 00001001B                 ;ALT+RSHIFT ausmaskieren...
INVMASK   EQU 11110110B                 ;Invertierte KEYMASK!!!

SCANKEY   EQU 32                        ;SCAN-Code des HOT-KEYs ("D")...

ID_WORD   EQU "HK"                      ;Kenn-Word!
;-----------------------------------------------------------------------------

SAVEREGS  MACRO                         ;Alle REGs auf dem STACK sichern

          PUSHF
          PUSH AX
          PUSH BX
          PUSH CX
          PUSH DX
          PUSH DS
          PUSH ES
          PUSH DI
          PUSH SI
          PUSH BP

          ENDM

;=============================================================================

LOADREGS  MACRO                         ;Alle REGs vom STACK zurückholen

          POP BP
          POP SI
          POP DI
          POP ES
          POP DS
          POP DX
          POP CX
          POP BX
          POP AX
          POPF

          ENDM

;=============================================================================

NEUSTACK  MACRO
          LOCAL NEUSCHL

          MOV CS:[ALTSS],SS             ;<SS> in ALTSS sichern
          MOV CS:[ALTSP],SP             ;<SP> in ALTSP sichern

          PUSH CS                       ;Neuen STACK einrichten
          POP SS
          MOV SP,OFFSET STAPEL

          PUSH CX                       ;REGS sichern
          PUSH DS
          PUSH SI

          MOV CX,64                     ;Schleifenzähler
          MOV DS,CS:[ALTSS]             ;<DS> mit DOS-SS laden
          MOV SI,CS:[ALTSP]             ;<SI> mit DOS-SI laden

NEUSCHL:  PUSH WORD PTR DS:[SI]         ;Word aus DOS-Stack auf MAIN-Stack
          INC SI                        ;SI auf das nächste Word
          INC SI
          LOOP NEUSCHL                  ;alle 64 Words abarbeiten

          POP SI                        ;REGS restaurieren
          POP DS                        
          POP CX

          ENDM

;=============================================================================

ALTSTACK  MACRO
          LOCAL ALTSCHL

          PUSH CX                       ;REGS sichern
          PUSH DS
          PUSH SI

          MOV CX,64                     ;Schleifenzähler
          MOV DS,CS:[ALTSS]             ;<DS> mit DOS-SS laden
          MOV SI,CS:[ALTSP]             ;<SI> MIT DOS-SP laden
          ADD SI,128                    ;SI auf den Anfang des DOS-Stack
ALTSCHL:  DEC SI                        ;SI auf das vorhergehende Stack-Word
          DEC SI
          POP WORD PTR DS:[SI]          ;Word vom MAIN-Stack auf DOS-Stack
          LOOP ALTSCHL                  ;alle 64 Words abarbeiten

          POP SI                        ;REGS restaurieren
          POP DS
          POP CX

          MOV SP,CS:[ALTSP]             ;<SP> und <SS> restaurieren
          MOV SS,CS:[ALTSS]

          ENDM

;=============================================================================

ASSUME CS:PRGM, DS:PRGM

PRGM      SEGMENT

          ORG 100H

START:    JMP INSTALL

;=============================================================================

          DB 192 DUP (0)
STAPEL    EQU $                         ;Neuer Stapel

ALTSS     DW 0                          ;<SS> und <SP> hier merken
ALTSP     DW 0

ALT09     DD 0                          ;Alte Vektoren der INTS 09H
ALT13     DD 0                          ;                       13H
ALT28     DD 0                          ;                       28H
ALT4A     DD 0                          ;                       4AH

INDOS     DD 0                          ;ADR des INDOS-Flags

AKTIV     DB 0                          ;AKTIV-Flag (Hauptprog aktiv)
INBIOS    DB 0                          ;INBIOS-Flag (INT 13H aktiv)

;=============================================================================
;Speicher für das zu aktivierende Programm
;=============================================================================

XMAX      DW ?
YMAX      DW ?
MERK      DB ?

;==============================================================================

NEU13     PROC FAR                      ;HD/DISK-INT (NICHT unterbrechen!)

          MOV CS:[INBIOS],1             ;INT 13H ist aktiv
          PUSHF                         ;INT-Aufruf simulieren
          CALL CS:[ALT13]
          MOV CS:[INBIOS],0             ;INT 13H nicht mehr aktiv
          RET 2                         ;INT beenden

NEU13     ENDP

;=============================================================================

NEU4A     PROC FAR                      ;Schon installiert-INT
                                        ;(4A: Alarm INT...)
          CMP AX,ID_WORD                   ;<AX> gleich ID_WORD?
          JNZ END4A                     ;NEIN --> END4A

          MOV AX,0FFH                   ;<AX> auf 0FFH
          IRET                          ;INT beenden

END4A:    PUSHF                         ;INT-Aufruf simulieren
          CALL CS:[ALT4A]
          IRET                          ;INT beenden

NEU4A     ENDP

;==============================================================================

NEU09     PROC FAR                      ;Tastatur-INT abfangen

          PUSHF                         ;INT-Aufruf simulieren
          CALL CS:[ALT09]

          CLI                           ;INTS sperren
          PUSH BX                       ;Benutzten REGS sichern
          PUSH DS
          LDS BX,CS:[INDOS]             ;INDOS-SEG in <CS>, INDOS-OFS in <BX>
          CMP BYTE PTR [BX],0           ;DOS AKTIV?
          POP DS                        ;REGS restaurieren
          POP BX
          JZ PRUEF                      ;NEIN --> PRUEF

          IRET

NEU09     ENDP

;-----------------------------------------------------------------------------

NEU28     PROC FAR                      ;Wird vom DOS aufgerufen...

          PUSHF                         ;INT-Aufruf simulieren
          CALL CS:[ALT28]
          CLI                           ;INTS sperren

;------------------------------------------------------------------------------

PRUEF:    CMP BYTE PTR CS:[INBIOS],0    ;DISK/HD-INT aktiv?
          JNZ ENDCHECK                  ;JA --> ENDCHECK
          CMP BYTE PTR CS:[AKTIV],0     ;MAIN schon AKTIV?
          JNZ ENDCHECK                  ;JA --> ENDCHECK

          PUSH DS
          PUSH AX

          MOV AX,40H                    ;DS auf BIOS-VAR-SEG
          MOV DS,AX

          MOV AL,BYTE PTR DS:[17H]      ;BIOS-Tastatur-Flag in <AL>
          AND AL,KEYMASK                ;Flag maskieren mit 'KEYMASK'
          CMP AL,KEYMASK                ;'KEYMASK'gedrückt?

          POP AX
          POP DS

          JNZ ENDCHECK                  ;NEIN --> ENDCHECK

          IN AL,60H                     ;Tastatur-PORT abfragen
          CMP AL,32                     ;'D' gedrückt?
          JNZ ENDCHECK
          JMP MAIN                       ;JA --> MAIN

ENDCHECK: IRET                          ;INT beenden

NEU28     ENDP

;------------------------------------------------------------------------------
; XYCALC: Aus XY die VIDMEM-ADR. berechnen
;------------------------------------------------------------------------------
XYCALC    PROC NEAR

          MOV AX,DI                     ;Y-Pos in <AX>
          MUL CS:[XMAX]                 ;<AX>:=YPOS*XMAX
          MOV BX,SI                     ;<BX> mit XPOS laden
          SHL BX,1                      ;<BX>:=XPOS*2
          ADD BX,AX                     ;<BX>:=<AX>+<BX>
          ADD BX,DS:[4EH]               ;Offset der Bildschirmseite drauf

          RET

XYCALC    ENDP

;------------------------------------------------------------------------------
; READCH: Zeichen an <SI>/<DI> (X/Y) in <DX> einlesen
;------------------------------------------------------------------------------
READCH    PROC NEAR

          CALL XYCALC
          MOV DX,ES:[BX]                ;Adressierter VIDMEM in <BX>

          RET

READCH    ENDP

;------------------------------------------------------------------------------
; WRITECH: Zu schreibendes Zeichen in <SI>/<DI> (X/Y) in <CX> übergeben
;------------------------------------------------------------------------------
WRITECH   PROC NEAR

          PUSH DX                       ;<DX> merken
          CALL XYCALC
          POP DX                        ;<DX> restaurieren
          MOV ES:[BX],CX                ;<CX> in den VIDEOSPEICHER hauen

          RET

WRITECH   ENDP

;------------------------------------------------------------------------------
; PAUSE : Länge in <CX> übergeben
;------------------------------------------------------------------------------
PAUSE     PROC NEAR

LAUBPAUS: PUSH CX
          MOV CX,0FFFFH                 ;PAUSE ----->
          REP NOP                       ;<----- PAUSE
          POP CX
          LOOP LAUBPAUS                 ;<----- PAUSE

          RET

PAUSE     ENDP
;------------------------------------------------------------------------------
; FALLDOWN
;------------------------------------------------------------------------------
FALLDOWN  PROC NEAR

          CALL READCH
          CMP DL,32
          JNZ CHECK0

          RET

CHECK0:   PUSH SI
          PUSH DI

CHECK1:   CMP DI,CS:[YMAX]                        ;Y < YMAX ?
          JAE CHECK2                              ;NEIN --> CHECK2

          PUSH DX                                 ;Zeichen merken!
          PUSH DI                                 ;Y merken
          INC DI                                  ;Y:=Y+1
          CALL READCH                             ;Zeichen an neuer Y-Pos lesen
          CMP DL,32                               ;Leerzeichen?
          POP DI                                  ;Y zurückholen
          POP DX                                  ;Zeichen zurückholen
          JNZ CHECK2                              ;KEIN Leerzeichen --> CHECK2

          MOV CX,0220H                            ;und SPACE in <AX>
          CALL WRITECH                            ;Aktuelle Pos mit SPACE löschen
          INC DI                                  ;Y:=Y+1 (nächste Zeile)
          MOV CX,DX                               ;Gelesenes CHAR in zu schreibendes
          CALL WRITECH                            ;Zeichen verschieben

          JMP CHECKEND

CHECK2:   CMP DI,CS:[YMAX]                        ;Y < YMAX ?
          JAE CHECK3                              ;NEIN --> CHECK2

          CMP SI,0                                ;X > 0 ?
          JZ CHECK3                               ;NEIN --> CHECK3

          PUSH DX                                 ;Zeichen merken!
          PUSH SI                                 ;X merken
          DEC SI                                  ;X:=X-1
          CALL READCH                             ;Zeichen an neuer X-Pos lesen
          CMP DL,32                               ;Leerzeichen?
          POP SI                                  ;X zurückholen
          POP DX                                  ;Zeichen zurückholen
          JNZ CHECK3                              ;KEIN Leerzeichen --> CHECK3

          PUSH DX                                 ;Zeichen merken!
          PUSH SI                                 ;X merken
          PUSH DI                                 ;Y merken
          DEC SI                                  ;X:=X-1
          INC DI                                  ;Y:=Y+1
          CALL READCH                             ;Zeichen an neuer XY-Pos lesen
          CMP DL,32                               ;Leerzeichen?
          POP DI                                  ;Y zurückholen
          POP SI                                  ;X zurückholen
          POP DX                                  ;Zeichen zurückholen
          JNZ CHECK3                              ;KEIN Leerzeichen --> CHECK3

          MOV CX,0220H                            ;und SPACE in <AX>
          CALL WRITECH                            ;Aktuelle Pos mit SPACE löschen
          DEC SI                                  ;Y:=Y+1 (nächste Zeile)
          MOV CX,DX                               ;Gelesenes CHAR in zu schreibendes
          CALL WRITECH                            ;Zeichen verschieben

          JMP CHECKEND                             ;Zeichen wurde verschoben
                                                  ;Nochmal --> CHECK1

CHECK3:   CMP DI,CS:[YMAX]                        ;Y < YMAX ?
          JAE CHECKEND                            ;NEIN --> CHECK2

          MOV AX,CS:[XMAX]                        ;<AX> mit XMAX laden
          SHR AX,1                                ;Durch 2 Teilen (Attr+Char!)
          DEC AX                                  ;Nochmal einen runter
          CMP SI,AX                               ;<SI> gleich rechter Rand?
          JZ CHECKEND                             ;NEIN --> CHECKEND

          PUSH DX                                 ;Zeichen merken!
          PUSH SI                                 ;X merken
          INC SI                                  ;X:=X+1
          CALL READCH                             ;Zeichen an neuer Y-Pos lesen
          CMP DL,32                               ;Leerzeichen?
          POP SI                                  ;Y zurückholen
          POP DX                                  ;Zeichen zurückholen
          JNZ CHECKEND                            ;KEIN Leerzeichen --> CHECKEND

          PUSH DX                                 ;Zeichen merken!
          PUSH SI                                 ;X merken
          PUSH DI                                 ;Y merken
          INC SI                                  ;X:=X-1
          INC DI                                  ;Y:=Y+1
          CALL READCH                             ;Zeichen an neuer XY-Pos lesen
          CMP DL,32                               ;Leerzeichen?
          POP DI                                  ;Y zurückholen
          POP SI                                  ;X zurückholen
          POP DX                                  ;Zeichen zurückholen
          JNZ CHECKEND                            ;KEIN Leerzeichen --> CHECKEND

          MOV CX,0220H                            ;und SPACE in <AX>
          CALL WRITECH                            ;Aktuelle Pos mit SPACE löschen
          INC SI                                  ;Y:=Y+1 (nächste Zeile)
          MOV CX,DX                               ;Gelesenes CHAR in zu schreibendes
          CALL WRITECH                            ;Zeichen verschieben

CHECKEND: POP DI                                  ;Alte X-Pos und Y-Pos zur
          POP SI                                  ;weiternen Berechnung zurück-
                                                  ;holen...
          RET

FALLDOWN  ENDP

;=============================================================================

MAIN      PROC FAR

          MOV CS:[AKTIV],1              ;MAIN ist momentan aktiv
          NEUSTACK                      ;DOS-STACK sichern (sehr fraglich?!?!?)
          SAVEREGS                       ;REGS sichern

          sti

          MOV AX,40H                    ;DS auf BIOS-VAR-SEG
          MOV DS,AX

          MOV AL,DS:[17H]               ;BIOS-TAST-Flag auslesen
          AND AL,INVMASK                ;Invertierte KEYMASK!!!
          MOV BYTE PTR DS:[17H],AL      ;Flags zurücksetzen!!!

;=============================================================================
; Das war's wohl... Hier steht das zu aktivierende Programm... -->
;=============================================================================

          MOV AH,0FH                    ;Auslesen des Videomodus...
          INT 10H

          CMP AH,MAXCHAR                ;Min. 80 Zeichen Bildschirm?
          Jae aber                      ;NEIN --> ENDMAIN
          jmp endmain

aber:     CMP AL,2                      ;Grafik-Karten & Moduscheck
          JZ MONO                       ;Modus 2 --> MONO
          CMP AL,7
          JZ MONO                       ;      7 --> MONO
          CMP AL,3
          JZ COLR                       ;      3 --> COLR

          JMP ENDMAIN                   ;Grafik im Augenblick aktiv --> ENDMAIN

MONO:     MOV CX,0B000H                 ;SEGADR des VIDEOMEM bei MONO
          JMP WEITER                    ;COLR überspringen...

COLR:     MOV CX,0B800H                 ;SEGADR des VIDEOMEM bei COLR

WEITER:   MOV ES,CX                     ;In das EXTRA-SEGMENT schreiben <--
KEYCLEAR: IN AL,60H
          CMP AL,128
          JB KEYCLEAR

;=============================================================================

HERBST:   MOV AX,40H
          MOV DS,AX                     ;<DS> auf BIOS-VarSeg

          MOV AX,DS:[4AH]               ;XMAX errechnen...
          SHL AX,1
          MOV CS:[XMAX],AX

          MOV AL,DS:[84H]               ;YMAX errechnen...
          XOR AH,AH
          MOV CS:[YMAX],AX

          XOR SI,SI                     ;X-Pos.
          XOR DI,DI                     ;Y-Pos.

LOSCHFL:  INC SI                        ;X erhöhen
          CMP SI,DS:[4AH]               ;Rechter Rand? (Anz. Zeich. pro Zeil.)
          JZ LOZEIL                     ;JA --> LOZEIL
          JMP LOSCHFL                   ;NEIN -->LÖSCHL_F(lag)
LOZEIL:   XOR SI,SI                     ;X auf 0
          INC DI                        ;Y erhöhen
          CMP DI,CS:[YMAX]              ;Zuweit?
          JBE LOSCHFL                   ;Nochmal LOSCHFL

;------------------------------------------------------------------------------
; Buchstaben fallen lassen:
;------------------------------------------------------------------------------

HERBSTRT: XOR SI,SI
          MOV DI,CS:[YMAX]

SETSCHL:  CALL READCH
          CMP DL,32
          JZ SPRUNG

SPRUNG:   INC SI                        ;X erhöhen
          CMP SI,DS:[4AH]               ;Rechter Rand? (Anz. Zeich. pro Zeil.)
          JZ NEUZEIL                    ;JA --> NEUZEIL
          JMP SETSCHL                   ;NEIN -->SETSCHL
NEUZEIL:  XOR SI,SI                     ;X auf 0
          DEC DI                        ;Y erniedrigen
          CMP DI,0FFFFH                 ;Zuweit?
          JNZ SETSCHL                   ;Nochmal SETSCHL

FUERDICH: XOR SI,SI
          MOV DI,CS:[YMAX]

SETSCHX:  INC CS:[MERK]
          CMP CS:[MERK],7
          JNE FUCKOFF

          MOV CS:[MERK],0

          CALL FALLDOWN

FUCKOFF:  INC SI                        ;X erhöhen
          CMP SI,DS:[4AH]               ;Rechter Rand? (Anz. Zeich. pro Zeil.)
          JZ NEUZEIX                    ;JA --> NEUZEIL
          JMP SETSCHX                   ;NEIN -->SETSCHL
NEUZEIX:  XOR SI,SI                     ;X auf 0
          DEC DI                        ;Y erniedrigen
          CMP DI,0FFFFH                 ;Zuweit?
          JNZ SETSCHX                   ;Nochmal SETSCHL
          in al,60h
          cmp al,128
          JAE FUERDICH

;=============================================================================

ENDMAIN:  LOADREGS                      ;REGS restaurieren
          ALTSTACK                      ;DOS-STACK restaurieren (Hmmm, s.oben!)
          MOV CS:[AKTIV],0              ;MAIN ist NICHT mehr aktiv
          IRET

MAIN      ENDP

;=============================================================================

IDSTR     DW ID_WORD                      ;ID_WORD als ID-STRing

;=============================================================================

INSTALL   PROC NEAR

          PUSH CS                       ;<DS> gleich <CS>
          POP DS

          MOV AX,ID_WORD                ;Kenn-BYTE in <AX>
          INT 4AH                       ;Prüf-INT aufrufen!
          CMP AX,0FFH                   ;Schon installiert ?
          JZ REINST                     ;JA --> REINST
;-----------------------------------------------------------------------------
          MOV AX,3509H                  ;Vektor von INT 09 lesen
          INT 21H
          MOV WORD PTR [ALT09],BX       ;Alten Vektor sichern
          MOV WORD PTR [ALT09+2],ES

          MOV AX,3513H                  ;Vektor von INT 13 lesen
          INT 21H
          MOV WORD PTR [ALT13],BX       ;Alten Vektor sichern
          MOV WORD PTR [ALT13+2],ES

          MOV AX,3528H                  ;Vektor von INT 28 lesen
          INT 21H
          MOV WORD PTR [ALT28],BX       ;Alten Vektor sichern
          MOV WORD PTR [ALT28+2],ES

          MOV AX,354AH                  ;Vektor von INT 4A lesen
          INT 21H
          MOV WORD PTR [ALT4A],BX       ;Alten Vektor sichern
          MOV WORD PTR [ALT4A+2],ES
;-----------------------------------------------------------------------------
          MOV AX,2509H                  ;INT 09 neu setzen
          MOV DX,OFFSET NEU09
          INT 21H

          MOV AX,2513H                  ;INT 13 neu setzen
          MOV DX,OFFSET NEU13
          INT 21H

          MOV AX,2528H                  ;INT 28 neu setzen
          MOV DX,OFFSET NEU28
          INT 21H

          MOV AX,254AH                  ;INT 4A neu setzen
          MOV DX,OFFSET NEU4A
          INT 21H

          MOV AH,34H                    ;ADR des INDOS-Flags ermitteln
          INT 21H
          MOV WORD PTR [INDOS],BX       ;In den Speicherstellen INDOS ablegen
          MOV WORD PTR [INDOS+2],ES

          MOV AH,09                     ;Installationsmeldung ausgeben
          MOV DX,OFFSET MSG0
          INT 21H

          MOV DX,OFFSET INSTALL         ;Residenten Programmteil errechnen
          ADD DX,15
          MOV CL,4
          SHR DX,CL
          MOV AX,3100H                  ;Resident beenden
          INT 21H

;=============================================================================

REINST:   MOV AX,3509H                  ;Vektor von INT 09 lesen
          INT 21H
          CMP WORD PTR ES:[IDSTR],ID_WORD  ;Gefunden?
          JNZ ERROR                     ;NEIN --> ERROR
;-----------------------------------------------------------------------------
          MOV AX,3513H                  ;Vektor von INT 13 lesen
          INT 21H
          CMP WORD PTR ES:[IDSTR],ID_WORD  ;Gefunden?
          JNZ ERROR                     ;NEIN --> ERROR
;-----------------------------------------------------------------------------
          MOV AX,3528H                  ;Vektor von INT 28 lesen
          INT 21H
          CMP WORD PTR ES:[IDSTR],ID_WORD  ;Gefunden?
          JNZ ERROR                     ;NEIN --> ERROR
;-----------------------------------------------------------------------------
          MOV AX,354AH                  ;Vektor von INT 4A lesen
          INT 21H
          CMP WORD PTR ES:[IDSTR],ID_WORD  ;Gefunden?
          JNZ ERROR                     ;NEIN --> ERROR
;-----------------------------------------------------------------------------

          PUSH DS                       ;DS sichern
          LDS DX,ES:[ALT09]             ;INT 09 Vektor restaurieren
          MOV AX,2509H
          INT 21H

          LDS DX,ES:[ALT13]             ;INT 13 Vektor restaurieren
          MOV AX,2513H
          INT 21H

          LDS DX,ES:[ALT28]             ;INT 28 Vektor restaurieren
          MOV AX,2528H
          INT 21H

          LDS DX,ES:[ALT4A]             ;INT 4A Vektor restaurieren
          MOV AX,254AH
          INT 21H
          POP DS                        ;DS restaurieren

          PUSH ES                       ;ES sichern
          MOV ES,ES:[02CH]              ;SEG-ADR des Environment aus PSP lesen
          MOV AH,49H                    ;Speicher freigeben
          INT 21H
          POP ES                        ;ES restaurieren

          MOV AH,49H                    ;Restlichen Speicher freigeben
          INT 21H

          MOV AH,09H                    ;Reinstallationsmeldung ausgeben
          MOV DX,OFFSET MSG1
          INT 21H

          MOV AX,4C00H                  ;Prog beenden
          INT 21H

ERROR:    MOV AH,09                     ;Fehlermeldung ausgeben
          MOV DX,OFFSET MSG3
          INT 21H

          MOV AX,4C01H                  ;Prog mit FEHLER-CODE 1 beenden
          INT 21H
;-----------------------------------------------------------------------------

INSTALL   ENDP

;=============================================================================

MSG0      DB 13,10
          DB ""
          DB "ÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸",13,10
          DB "³     tsrtest... ([C] 1991 by S.STEINER)   ³",13,10
          DB "ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´",13,10
          DB "³     Das Programm <LAUBTSR-7-> wurde      ³",13,10
          DB "³        erfolgreich INSTALLIERT!          ³",13,10
          DB "³     >>> Aufruf mit ALT+RSHIFT+D <<<      ³",13,10
          db "³ Abbruch mit IRGENDEINER beliebigen Taste ³",13,10
          db "³  (also auch ALT/SHIFT oder CONTROL...)   ³",13,10
          DB "ÔÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ;",13,10
          DB "$"

MSG1      DB 13,10
          DB ""
          DB "ÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸",13,10
          DB "³     tsrtest... ([C] 1991 by S.STEINER)   ³",13,10
          DB "ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´",13,10
          DB "³     Das Programm <HOTKEY.COM> wurde      ³",13,10
          DB "³         erfolgreich entfernt...!         ³",13,10
          DB "ÔÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ;",13,10
          DB "$"

MSG3      DB 13,10,7
          DB ""
          DB "ÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͸",13,10
          DB "³     tsrtest... ([C] 1991 by S.STEINER)    ³",13,10
          DB "ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´",13,10
          DB "³  Das Programm <HOTKEY.COM> konnte leider  ³",13,10
          DB "³           NICHT entfernt werden!          ³",13,10
          DB "³  Nachträglich installierte TSR-Programme  ³",13,10
          DB "³  entferernen und nocheinmal versuchen...  ³",13,10
          DB "³         --> Sonst RESET drücken!          ³",13,10
          DB "ÔÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ;",13,10
          DB "$"

PRGM      ENDS

END       START