Fast-File- und Link-Viren-Finder

Vor einiger Zeit war es mal wieder soweit: Ich suchte das Backup von einem Source-Text auf einer der 10 Partitions meiner Megafile 60. Aber in welchem Ordner befand es sich?
Eine Szene später: Habe ich nun einen Link- Virus auf der Platte oder nicht? Sagrotan starten und warten, ...

Wie sich der geneigte Leser sicherlich vorstellen kann, habe ich für beide Probleme eine Lösung anzubieten - und zwar in Form eines Fast-File-Finders bzw. des Link-Virus-Finders: Der Fast-File-Finder (FFF) ist in der Lage, die gesamte Festplatte nach einem Dateinamen zu durchsuchen. Ganz nebenbei ermittelt er auch noch, wieviel Platz auf jeder Partition noch frei ist.

Der Link-Virus-Finder (LVF) durchsucht alle ausführbaren Programme auf der Festplatte nach Link-Viren. Er tut dies, indem er den Programm-Header mit den Mustern des VCS und des Milzbrand-Virus' vergleicht. Beim jedem Programm wird zudem eine Prüfsumme über die ersten 256 Bytes berechnet, mit welcher bei jedem neuerlichen Start vom LVF verglichen wird. Ach ja, der LVF braucht für meine 10 Partitions (mit über 2800 Dateien in mehr als 280 Ordnern) etwa 13.5s, um festzustellen, ob Link-Viren vorhanden sind oder nicht. Ich denke, daß ist im Vergleich zu 15 min bei Sagrotan angenehm schnell...

Kommen wir nun zum Eigentlichen: dem Programm. Wie man leicht feststellen kann, ist das Programm in Assembler geschrieben. Es besteht aus einigen Teilen, die man auch prima einzeln verwenden kann. Deshalb glaube ich, daß jeder Assembler-Programmierer etwas von dem Listing hat. Es ist recht gut dokumentiert, so daß man es wohl auf jeden fortgeschrittenen Assembler-Programmierer ohne weiteres loslassen kann.

Da der FFF und der LVF in wesentlichen Teilen gleich sind, gibt es nur ein Listing, aus welchem man mit dem Flag virus wahlweise den FFF oder den LVF erzeugen kann.

Um ein Laufwerk nach Link-Viren oder Dateien zu durchsuchen, wird zuerst die Routine set_drive für das zu durchsuchende Laufwerk aufgerufen. Diese Routine ermittelt den BPB des Laufwerkes, liest die FAT und ermittelt so ganz nebenbei noch dessen freien Speicherplatz.

Nun wird read_dir aufgerufen. Diese Routine liest das Root-Directory des Laufwerkes ein und ruft für jeden Ordner die Routine read_sub_dir auf. Diese Routine liest dann rekursiv alle weiteren Ordner ein.

Der Speicherplatz für die Ordner wird durch die Routine get_mem ermittelt, die eine Heap-Verwaltung darstellt. Das Prinzip eines solchen Heaps ist sehr einfach: Man hat einen Zeiger auf einen großen Speicherblock - den Heap. Will nun jemand Speicher alloziieren. wird dieser Wert des Zeigers zurückgegeben. Das ist die Adresse des Speicherblockes, den man angefordert hat. Bevor das Unterprogramm verlassen wird, erhöht man diesen Zeiger noch um die Größe des angeforderten Speicherblockes, so daß bei einem erneuten Aufruf der Zeiger hinter den ersten Speicherblock zeigt.

Noch zwei Dinge gilt es zu beachten: Die Größe des Speicherblockes sollte aufgerundet werden, damit dieser stets auf einer geraden Adresse anfängt. Und man sollte abfragen, ob der Zeiger hinter den Speicherblock zeigt, der für den Heap reserviert ist. Wenn dem so ist, reicht der Speicher nicht, und das Programm wird abgebrochen.

Jetzt wird sich der eine oder andere eventuell noch fragen: Wie kann man denn Speicherblöcke wieder freigeben? Die Antwort ist ganz einfach: gar nicht! Das ist bei unserem Directory-Baum auch gar nicht nötig, da wir vor jedem erneuten Einlesen eines Baumes den Heap-Pointer einfach wieder zurück auf den Anfang setzen und somit alle Blöcke freigegeben haben.

So, nach diesem Ausflug in die Heap-Verwaltung kommen wir zur Routine hunt_dir. Sie durchsucht einen eingelesenen Directory-Baum nach Dateien bzw. die Dateien nach Viren. Auch diese Routine funktioniert rekursiv, d.h. wenn ein Ordner gefunden wird, ruft sie sich mit einem Zeiger auf den Ordner und der Anzahl der Dateien des Ordners erneut auf. Wenn der Ordner komplett durchsucht wurde, wird zurückgekehrt, und es geht weiter.

In dieser Routine besteht auch der größte Unterschied zwischen dem FFF und dem LVF. Während der FFF den Dateinamen nur mit seiner Suchmaske vergleicht und den Dateinamen mit Pfad gegebenenfalls ausgibt, hat der LVF erheblich mehr zu tun. Wenn ein erster Vergleich der Extensions „PR?“, „TO?“, „TT?“, „AC?\ „AP?“, „DR?“ (Treiber), „SY?“ (Festplattentreiber!) positiv ist. wird der erste Sektor dieser Datei eingelesen. Bei einer Programmdatei müssen am Anfang die Bytes $601A stehen. Nun wird der Anfang des TEXT-Segmentes mit den zwei bekannten Link-Viren verglichen.

Wenn kein Link-Virus vorhanden ist, wird eine Prüfsumme über die ersten 256 Bytes der Datei berechnet. Dies klingt zwar nach sehr wenig, wenn man bedenkt, daß z.B. Sagrotan eine CRC-Prüfsumme über die gesamte Datei berechnet, reicht aber, da in den ersten 256 Bytes die Länge der einzelnen Programmsegmente steht, welche durch einen Virus an sich immer verändert wird. Zudem muß sich ein Virus am Programmanfang aufrufen. und über genau diese Stelle wird die Prüfsumme ja auch berechnet. Wie man sieht, wird zwar Zeit gespart, aber die Sicherheit ist immer noch gewährleistet.

Nach der Berechnung der Prüfsumme wird in der Vergleichsliste nach dem Programm gesucht. Wenn das Programm bereits in der Liste steht, wird die Prüfsumme in der Liste gesucht, denn für jedes Programm merkt sich der LVF bis zu sieben Prüfsummen. Das ist sehr praktisch, wenn man z.B. verschiedene Programmversionen auf derFestplatte hat. Wenn die Prüfsumme nicht in der Liste steht, wird gefragt, ob die neue Prüfsumme mit in die Liste aufgenommen werden soll. Steht der Programmname noch nicht in der Liste, wird ein neuer Eintrag für das Programm angelegt und eine entsprechende Meldung ausgegeben. Die Vergleichsliste wird beim Start vom LVF automatisch geladen, und sie kann vor dem Programmende natürlich auch geschrieben werden.

Sowohl LVF als auch auf FFF können in der Kommandozeile einige Parameter übergeben werden.

Mit einem am Anfang der Kommandozeile wird die Ausgabe der Dateinamen unterdrückt. Beim LVF spart es einfach Zeit, beim FFF ist es lediglich dann praktisch, wenn man nur wissen wollte, wieviel „*.TXT“-Dateien z.B. auf der Festplatte stehen, aber nicht, wo sie sind. Eventuell hat man den FFF auch nur gestartet, um festzustellen, auf welcher Partition noch Platz ist, denn das wird ja praktischerweise auch ausgegeben.

Mit einem „+“ wird beim LVF eine unbekannte Datei oder eine unbekannte Prüfsumme automatisch, d.h. ohne Rückfrage, übernommen. Das ist insbesondere dann praktisch, wenn man den LVF zum ersten Mal, also ohne die „LVF.DAT"-Datei startet.

Sowohl beim LVF als auch beim FFF kann man mit „:x“ nur das Laufwerk „x“ durchsuchen lassen. Beispiel: „:C“, es wird nur das Laufwerk C: durchsucht. Ansonsten wird ab Laufwerk „C:“ bis zum letzten benutzten Laufwerk alles durchsucht - also üblicherweise die gesamte Festplatte.

Beim FFF kann man nun noch eine Suchmaske angeben. Diese kann ein kompletter Dateiname wie z.B. „TEST.DOC' sein oder aber auch nur ein Teil davon („T*.D?C“), wie beim GEMDOS also.

Literatur:

[1] Scheibenkleister II. MAXON Computer
[2] Atari ST Profibuch, Sybex
[3] Anleitung zum Turbo Ass

*************************************************
* Link-Virus-Finder V1.5 *
* und Fast-File-Finder V1.5 *
* (c)1991 by Maxon Computer *
* von X-soft, Markus Fritze *
* entwickelt mit Turbo-Ass V1.4 *
************************************************* 
;Schalter:
virus EQU 0
;0:Fast-File-Finder, <>0:Link-Virus-Finder 

    IF virus
    OUTPUT 'LVF.TTP' ;Link-Virus-Finder 
    ELSE
    OUTPUT 'FFF.TTP' ;Fast-File-Finder
    ENDC
    OPT F+,W+       ;Warnungen an,Fast-Load an

max_prgs EQU 1000 ;max.1000 Programme im LVF 
max_sektorsize EQU 8192 ;max.SektorgröPe

;A6-Register zeigt auf das BSS-Segment 
    BASE A6,BSS

;Struktur des BIOS-Parameter-Blocks definieren 
    PART 'BPB-Struktur'
    RSRESET

recsiz:RS.W 1   ;Bytes pro Sektor
clsiz: RS.W 1   ;Sektoren pro Cluster
clsizb:RS.W 1   ;Bytes pro Cluster
rdlen: RS.W 1   ;Länge des Root-Directories
fsiz:  RS.W 1   ;Länge einer FAT
fatrec:RS.W 1   ;Startsektor der 2.FAT
datrec:RS.W 1   ;Starts, des 1.freien Sektors 
numcl: RS.W 1   ;Gesamtanzahl der Cluster 
bflags:RS.W 1   ;Bit0=1 bei 16-bit-FAT 
    ENDPART

_drvbits EQU $04C2 ;Die Bitmaske der Laufwerke

    PART 'main' 
anfang:
    movea.l 4(SP),A0        ;Ptr auf die Basepage
    movea.l $18(A0),A6      ;Ptr auf den BSS-Bereich
    lea     own_stack(PC),SP

    pea     init_text(PC)
    bsr     print_line      ;Startup-Meldung

    IF virus
    lea data_buff(A6),A0
    move.l A0,data_base(A6)

    clr.w   -(SP)
    pea     fname(A6)
    move.w  #$3D,-(SP)
    trap    #1          ;Fopen()
    addq.l  #8,SP
    move.l  D0,D7
    bmi.s   anfang1 ;Fehler
    move.l  data_base(A6),-(SP)
    move.l  #max_prgs*32,-(SP)
    move.w  D7,-(SP)
    move.w  #$3F,-(SP)
    trap    #1          ;Fread() - Datenblock einlesen
    lea     12(SP),SP
    move.w  D7,-(SP)
    move.w  #$3E,-(SP)
    trap    #1 ;Fclose()
    addq.l  #4,SP
anfang1:
    ENDC

    bsr     convert     ;Commandline auswerten

    clr.l   -(SP)
    move.w  #$20,-(SP)
    trap    #1          ;Supervisormode an
    move.l  D0,2(SP)
    move.l  _drvbits.w,D7 ;Variable auslesen
    trap    #1          ;Usermode wieder an
    addq.l  #6,SP
    move.w  D7,D1       ;_drvbits nach D1

    IF !virus
    lea search_mask(A6),A1 ;Suchmaske
    lea search_chars(A6),A2 ;Suchdaten
    ENDC
    moveq   #$18,D7     ;Ordner & Vol-Label ignorieren
    moveq   #0,D5       ;Bytes der gefundenen Dateien
    moveq   #0,D4       ;Anzahl der gefundenen Dateien
    moveq   #0,D3       ;Anzahl der Ordner

    moveq   #2,D2       ;mit Drive C geht es los
    tst.w   D6          ;keine Laufwerksangabe?
    bmi.s   loop        ;Genau! =>
    move.w  D6,D2       ;das spezielle Laufwerk testen
loop:
    btst    D2,D1       ;Laufwerk vorhanden?
    beq.s   loop1       ;Nein! =>
    movea.l #heap,A0
    adda.l  A6,A0
    move.l  A0,heap_pnt(A6) ;Heap zurücksetzen 
    move.w  D2,D0
    bsr     set_drive   ;Laufwerk anmelden, FAT lesen
    movem.l D1-D3,-(SP)
    move.l  D0,D1 
    pea drive_text(PC)
    bsr     print_line
    moveq   #'A',D0 
    add.b D2,D0
    bsr     chrout      ;aktuelles Drive ausgeben
    pea     drive_text2(PC)
    bsr     print_line
    moveq   #7,D2       ;max.8 Stellen ausgeben
    bsr     dez_out     ;freien Speicher ausgeben
    pea     drive_text3(PC)
    bsr     print_line  ;"Bytes frei"
    movem.l (SP)+,D1-D3

    bsr     read_dir    ;Kompletten Baum einlesen

    lea     pfad(A6),A3
    move.b  #'A',(A3)
    add.b   D2,(A3)+    ;Laufwerkskennung move.b #':',(A3)+
    move.b  #'\',(A3)+  ;Root-Pfad festlegen 

    move.w  D1,-(SP)
    bsr     hunt_dir    ;Baum durchsuchen
    move.w  (SP)+,D1 
loop1:
    tst.w   D6          ;Laufwerksangabe?
    bpl.s   loop2       ;Ja! => Fertig
    addq.w  #1,D2
    cmp.w   #32,D2      ;bis Bit 32 wird hochgezählt 
    bne.s   loop        ;noch nicht alle? Stimmt! =>
loop2:
    IF virus 
    moveq   #0,D1
    move.w  virus_count(A6),D1 ;Viren gefunden?
    beq.s   loop3 ;Nein! =>
    moveq   #4,D2 ;max.5 Stellen ausgeben
    bsr     dez_out
    pea     viren_text(A6)
    bsr     print_line  ;verseuchte Dateien gefunden
loop3:
    ENDC
    moveq   #0,D1
    move.w  D4,D1       ;Anzahl der 'Hits'
    moveq   #5,D2       ;max.6 Stellen ausgeben
    bsr     dez_out
    pea     files_text(A6)
    bsr     print_line
    move.l  D5,D1       ;Länge aller Dateien zusammen
    moveq   #7,D2       ;max.8 Stellen ausgeben
    bsr     dez_out
    pea     files_text2(A6)
    bsr     print_line

    moveq   #0,D1
    move.w  D3,D1       ;Anzahl der Ordner
    moveq   #4,D2       ;max.5 Stellen ausgeben
    bsr     dez_out
    pea     folders_text(A6)
    bsr.s   print_line

    move.w  #7,-(SP)
    trap    #1          ;auf Taste warten
    addq.l  #2,SP
    IF virus 
    bclr    #5,D0
    cmp.b   #'J',D0     ;Schreiben?
    bne.s   _exit       ;Nein! =>

    movea.l data_base(A6),A4 
    movea.l A4,A3       ;Datenbasis merken
code:
    tst.l   (A4)        ;Ende der Liste?
    beq.s   code2       ;Ja! =>
    lea     24(A4),A4
    bra.s   code
code2:
    suba.l  A3,A4       ;Länge aller Daten

    clr.w   -(SP)
    pea     fname(A6)
    move.w  #$3C,-(SP)
    trap    #1          ;Fcreate()
    addq.l  #8,SP
    move.l  D0,D7
    bmi.s   _exit       ;Fehler
    move.l  A3,-(SP)    ;Basisadresse der Daten
    move.l  A4,-(SP)    ;Länge
    move.w  D7,-(SP)
    move.w  #$40,-(SP)
    trap    #1          ;Fwrite()
    lea     12(SP),SP
    move.w  D7,-(SP)
    move.w  #$3E,-(SP)
    trap    #1          ;Fclose()
    addq.l  #4,SP
    ENDC

_exit:
    pea     exit_text(A6)
    bsr.s   print_line  ;Cursor wieder aus, etc. 

    clr.w   -(SP)
    trap    #1          ;Programmende
    ENDPART

*************************************************
* String auf dem Stack ausgeben 
*************************************************
    PART 'print_line' 
print_line:
    movem.l D0-D2/A0-A2,-(SP)
    move.l  4+6*4(SP),-(SP)
    move.w  #$09,-(SP)
    trap    #1          ;Cconws()
    addq.l  #6,SP
    movem.l (SP)+,D0-D2/A0-A2
    move.l  (SP)+,(SP)  ;Stack korrigieren
    rts
    ENDPART

*************************************************
* Zeichen in D0 ausgeben 
*************************************************
    PART 'chrout' 
chrout:
    movem.l D0-D2/A0-A2,-(SP)
    move.w  D0,-(SP)
    move.w  #$02,-(SP)
    trap    #1          ;Cconout()
    addq.l  #4,SP
    movem.l (SP)+,D0-D2/A0-A2
    rts
    ENDPART

**********************************************
* positive Dezimal-Zahl in D1                *
* ohne Führungsnullen ausgeben               *
* max.Anzahl der Stellen minus 1 in D2       *
**********************************************
    PART 'dez_out' 
dez_out:
    movem.l D0-D4/A1,-(SP)
    lea     dez_tab(PC),A1 ;Zeiger auf die Tabelle
    move.w  D2,D0       ;Anzahl der Stellen-1
    add.w   D0,D0       ;mal 4
    add.w   D0,D0       ;(schneller als LSL.W #2,D0!)
    lea     4(A1,D0.w),A1 ;Ptr auf die Stellenzahl
    moveq   #0,D4       ;führende Nullen ignorieren
dez_out1:
    move.l  -(A1),D3    ;Tabellenwert 
    moveq   #-'0',D0    ;wird zu -'1',-'2',-'3',... 
dez_out2:
    sub.l   D3,D1       ;SUB bis zum Unterlauf
    dbcs    D0,dez_out2 ;Unterlauf? Nein! => 
    neg.w   D0          ;z.B. -'1' => '1'
    cmp.b   #'0',D0     ;eine Null?
    beq.s   dez_out4    ;Ja! =>
    moveq   #-1,D4      ;ab nun werden auch Nullen
dez_out3:
    bsr.s   chrout      ;Zeichen in D0 ausgeben
dez_out5:
    add.l   D3,D1       ;den Unterlauf zurück
    dbra    D2,dez_out1 ;schon alle Stellen? 
    movem.l (SP)+,D0-D4/A1 
    rts 
dez_out4:
    tst.b   D4          ;Nullen ausgeben?
    bne.s   dez_out3    ;Ja! =>
    tst.w   D2          ;letzte Ziffer?
    bne.s   dez_out5    ;Nein! => dann ignorieren
    moveq   #'0',D0     ;wenn der gesamte Wert 0 ist,
    bra.s   dez_out3    ;zumindest eine 0 ausgeben!
dez_tab:
    DC.L 1,10,100,1000,10000,100000
    DC.L 1000000,10000000,100000000,1000000000
    ENDPART

*********************************************
* convert() - Die Commandline auswerten     *
*                                           *
* Dabei wird die Variable "flag" gesetzt,   *
* das D6-Register (Laufwerk) definiert und  *
* die Suchmaske und der Suchstring aus dem  *
* Filenamen zusammengesetzt.                *
*********************************************
    PART 'convert' 
convert:
    lea     anfang-128+1(A6),A0 ;Commandline
    clr.b   flag(A6)    ;alle Flags löschen
    moveq   #-1,D6      ;keine Laufwerksangabe
convert1:
    cmpi.b  #' ',(A0)+  ;Spaces überlesen
    beq.s   convert1 
    subq.l #1,A0
    cmpi.b  #'-',(A0)   ;folgt noch was?
    bne.s   convert2    ;Nein! =>
    addq.l  #1,A0
    bset    #0,flag(A6) ;Files nur zählen
convert2:
    cmpi.b  #' ',(A0)+  ;Spaces überlesen
    beq.s   convert2 
    subq.l  #1,A0 
    IF virus
    cmpi.b  #'+',(A0)   ;folgt noch was?
    bne.s   convert3    ;Nein! =>
    addq.l  #1,A0
    bset    #1,flag(A6) ;alle Files übernehmen
convert3:
    cmpi.b  #' ',(A0)+  ;Spaces überlesen
    beq.s   convert3
    subq.l  #1,A0
    cmpi.b  #':',(A0)
    bne.s   convert8
    addq.l  #1,A0       ;Doppelpunkt ignorieren
convert8:
    moveq   #0,D0
    move.b  (A0),D0
    bmi.s   convert14   ;Code>127 => kein Laufwerk
    bclr    #5,D0
    subi.b  #'A',D0
    bmi.s   convert14   ;<'A' => kein Laufwerk
    cmp.b   #32,D0
    bhs.s   convert14   ;>maxdrive => kein Laufwerk
    move.w  D0,D6       ;Laufwerksangabe merken
convert14: 
    rts 
    ELSE
    cmpi.b  #':',(A0)
    bne.s   convert6
    moveq   #0,D0
    move.b  1(A0),D0
    addq.l  #2,A0
    bclr    #5,D0
    cmp.b   #32,D0
    bhs.s   convert6    ;>maxdrive => kein Laufwerk 
    move.w  D0,D6       ;Laufwerksangabe merken
convert6:
    cmpi.b  #' ',(A0)+  ;Spaces überlesen
    beq.s   convert6
    subq.l  #1,A0
    lea     search_mask(A6),A1 ;Suchmaske
    lea     search_chars(A6),A2 ;Suchdaten
    clr.l   (A1)+
    clr.l   (A1)+       ;alle Suchmasken löschen
    clr.l   (A1)
    subq.l  #8,A1
    moveq   #0,D2       ;kein Allquantor
    moveq   #7,D1       ;max.8 Zeichen Filename
convert7:
    move.b  (A0)+,D0
    beq     convert19   ;Ende des Filenames
    cmp.b   #' ',D0     ;Space als Ende?
    beq     convert19   ;Ja! =>
    cmp.b   #13,D0      ;CR als Ende?
    beq     convert19   ;Ja! =>
    cmp.b   #'.',D0     ;Start der Extension?
    beq.s   convert12   ;Ja! =>
    cmp.b   #'?',D0     ;Existenzquantor?
    bne.s   convert8    ;Nein! =>
    tst.w   D2          ;Allquantor schon angegeben?
    bne.s   convert11   ;Ja! =>
    sf      (A1)+
    sf      (A2)+       ;Zeichen ignorieren
    bra.s   convert11   ;weiter =>
convert8:
    cmp.b   #'*',D0     ;Allquantor?
    bne.s   convert9    ;Nein! =>
    moveq   #-1,D2      ;Allquantor
    bra     convert7
convert9:
    tst.w   D2          ;Allquantor schon angegeben?
    bne.s   convert11   ;Ja! =>
    cmp.b   #'a',D0
    blo.s   convert10
    cmp.b   #'z',D0
    bhi.s   convert10
    bclr    #5,D0       ;in Großbuchstaben wandeln
convert10:
    move.b  D0,(A2)+    ;Zeichen übernehmen
    st      (A1)+       ;Maske dazu setzen
convert11:
    dbra    D1,convert7 ;max.8 Zeichen
    bra     convert13
convert12:
    tst.w   D2          ;Allquantor angegeben?
    bne     convert13   ;Ja! =>
    move.b  #' ',(A2)+  ;Filenamen mit ' '
    st      (A1)+       ;auffüllen
    dbra    D1,convert12
convert13:
    lea     search_mask+8(A6),A1  ;Suchmaske
    lea     search_chars+8(A6),A2 ;Suchdaten
    moveq   #2,D1       ;max.3 Zeichen Extension
convert14:
    move.b  (A0)+,D0
    beq.s   convert17   ;Ende des Filenames
    cmp.b   #' ',D0     ;Space als Ende?
    beq.s   convert17   ;Ja! =>
    cmp.b   #13,D0      ;CR als Ende?
    beq.s   convert17
    cmp.b   #'?',D0     ;Existenzquantor?
    bne.s   convert15   ;Nein! =>
    sf      (A1)+
    sf      (A2)+       ;Zeichen ignorieren
    bra.s   convert18   ;weiter =>
convert15:
    cmp.b   #'*',D0     ;Allquantor?
    beq     convert19   ;Nein! =>
    cmp.b   #'a',D0
    blo.s   convert16
    cmp.b   #'z',D0
    bhi.s   convert16
    bclr    #5,D0       ;in Großbuchstaben wandeln 
convert16:
    move.b  D0,(A2)+    ;Zeichen übernehmen
    st      (A1)+       ;Maske dazu setzen
    dbra    D1,convert14 ;schon alle max. 8 Zeicher 
    bra     convert19
convert17:
    move.b  #' ',(A2)+  ;Extension mit
    st      (A1)+       ;Space auffüllen
convert18:
    dbra    D1,convertl7
convert19: 
    rts 
    ENDC 
    ENDPART

*************************************************
* hunt_dir() - Kompletten Dir-Baum ab A0        *
* durchsuchen, dabei steht die Anzahl der       *
* Einträge im Directory in D0.                  *
*                                               *
* Diese Routine ruft sich selbst rekursiv       *
* auf, um auch beliebig tiefe Ordnerebene zu    *
* durchsuchen.                                  *
*                                               *
* Der Baum muß durch read_dir() im Speicher     *
* liegen, d.h. bereits mit Pointern ver-        *
* kettet sein.                                  *
*************************************************
    PART 'hunt_dir' 
hunt_dir:
    subq.w  #1,D0       ;für DBRA
hunt_dir1:
    move.w  D7,D1       ;Illegale Fileattr-Bits
    and.b   11(A0),D1   ;Flags dazu 
    bne     hunt_dir3   ;Datei nicht finden! =>
    cmpi.b  #$E5,(A0)   ;gelöschte Datei? 
    beq     hunt_dir2   ;Ja! =>
    IF !virus 
    move.l  (A0),D1
    beq     hunt_dir4   ;Ende des Directories =>
    and.l   (A1),D1
    cmp.l   (A2),D1     ;Zeichen 1-4 des Filesnames
    bne     hunt_dir3
    move.l  4(A0),D1
    and.l   4(A1),D1
    cmp.l   4(A2),D1    ;Zeichen 5-8 des Filenamens 
    bne     hunt_dir3
    move.l  8(A0),D1
    and.l   8(A1),D1
    cmp.l   8(A2),D1    ;die Extension vergleichen
    bne     hunt_dir3
    btst    #0,flag(A6) ;Dateien nur zählen? 
    bne.s   print_fname4 ;Ja! => 
    bsr     print_fname ;Filenamen ausgeben
print_fname4:
    addq.w  #1,D4       ;Dateien zählen
    movep.w 31(A0),D1 
    move.b  30(A0),D1 
    swap    D1          ;Dateilänge
    movep.w 29(A0),D1   ;von Intel 
    move.b  28(A0),D1   ;nach 68000er 
    add.l   D1,D5       ;zur Gesamtlänge
    ELSE
    tst.b   (A0)
    beq     hunt_dir4   ;Ende des Directories =>
    addq.w  #1,D4       ;eine Datei mehr gefunden
    move.w  8(A0),D1    ;Zeichen 1+2 der Ext. holen
    cmp.w   #'PR1,D1    ;PRG, PRX, etc.
    beq.s   found_file
    cmp.w   #'TO',D1    ;TOS
    beq.s   found_file
    cmp.w   #'TT',D1    ;TTP
    beq.s   found_file
    cmp.w   #'AC',D1    ;ACC, ACX, etc.
    beq.s   found_file
    cmp.w   #'AP',D1    ;APP
    beq.s   found_file
    cmp.w   #'DR',D1    ;DRV (Treiber)
    beq.s   found_file
    cmp.w   #'SY',D1    ;SYS (Plattentreiber!) 
    bne     hunt_dir3 
found_file:
    move.l  D0,-(SP) 
regs REG D1-D4/D6-A2/A4-A6 
    movem.l regs,-(SP) 
    movep.w 27(A0),D0
    move.b  26(A0),D0   ;Cluster ins Intel-Format 
    move.w  drive(A6),-(SP) ;aktuelles Drive subq.w #2,D0
    mulu    clsiz(A5),D0 ;mal Sektoren pro Cluster 
    add.w   datrec(A5),D0 ;+ erster freier Sektor 
    move.w  D0,-(SP)    ;= abs. Sektor
    move.w  #1,-(SP)    ;einen Sektor einlesen 
    pea     sektor_buffer(A6) ;in den Buffer
    move.l  #$040000,-(SP)
    trap    #13         ;Rwabs() - Sektor einlesen
    lea     14(SP),SP
    tst.l   D0
    bmi     _exit
    lea     sektor_buffer(A6),A2
    moveq   #0,D0
    cmpi.w  #$601A,(A2) ;Programmkennung? 
    bne.s   count_loop1 ;Nein! =>
    lea     28(A2),A0   ;Zeiger auf den Programmstart
    cmpi.w  #$487A,(A0)+ ;PEA 
    bne.s   no_virus    ;Nein! =>
    cmpi.w  #$FFFE,(A0)+ ;*-2(PC) 
    bne.s   no_virus    ;Nein! =>
    cmpi.w  #$4EF9,(A0) ;c't-Virus? 
    beq     virus2      ;JA! =»
    cmpi.l  #$207A0006,(A0)+ ;VCS-Virus? 
    bne.s   no_virus    ;Nein! =>.
    cmpi.l  #$4EFB8800,(A0) 
    beq     virus1      ;JA! =»
no_virus:
    moveq   #127,D2
count_loop:
    eor.w   D2,D0       ;Prüfsumme errechnen
    sub.w   (A2)+,D0
    addq.w  #1,D0
    dbra    D2,count_loop
    tst.w   D0
    bne.s   count_loop1
    moveq   #213,D0     ;die Prüfsumme ist NIE 0! 
count_loop1:
    movem.l (SP),regs   ;Chksumme in D0
    tst.l   D0          ;Prüfsumme=0?
    beq     check_it6   ;dann kein Programm! =>

    movea.l data_base(A6),A1
    bra.s   check_it0 
check_it2:
    lea     24(A4),A1   ;der nächste Eintrag
    movea.l A2,A0       ;auf den Filenamen zurück
check_it0:
    movea.l A0,A2       ;A0 retten
    movea.l A1,A4       ;A1 retten
    tst.l   (A1)        ;Tabelle ist leer, bzw. Ende
    beq.s   check_it3   ;Neuer Eintrag nötig! =>
    moveq   #4,D1       ;nur 10 (!) Buchstaben
check_it1:
    cmpm.w  (A1)+,(A0)+ ;vergleichen
    dbne    D1,check_it1
    bne.s   check_it2   ;ungleich =>
    lea     10(A4),A1   ;Zeiger auf die Prüfsumme
    moveq   #6,D1       ;max.7 Prüfsummen
check_it5:
    tst.w   (A1)
    beq.s   check_it4   ;Prüfsumme nicht gefunden
    cmp.w   (A1)+,D0
    dbeq    D1,check_it5 ;weiter vergleichen => 
    lea     -12(A1),A1
    beq.s   check_it8   ;Prüfsumme ist ok! =>
check_it4:
    movea.l A2,A0
    bsr     print_fname ;Filenamen ausgeben
    pea     fehler_text(A6)
    bsr     print_line ;Meldung machen!
    btst    #1,flag(A6) ;stets übernehmen?
    bne.s   check_it7   ;Ja! =>
    movem.l D0/D2/A0-A2,-(SP) 
    move.w  #7,-(SP)
    trap    #1          ;auf Taste warten
    addq.l  #2,SP
    bclr    #5,D0
    move.w  D0,D1
    movem.l (SP)+,D0/D2/A0-A2 
    cmp.b   #'J',D1     ;Übernehmen?
    bne.s   check_it6   ;Nein! =>
check_it7:
    move.w  D0,(A1)     ;Prüfsumme kopieren
    addq.w  #1,D5
    bra.s   check_it6
check_it3:
    move.l  (A2)+,(A4)+ ;neuen Eintrag
    move.l  (A2)+,(A4)+ ;Filenamen kopieren
    move.w  (A2)+,(A4)+ ;(nur 10 Buchstaben!)
    move.w  D0,(A4)     ;l.Prüfsumme
    addq.w  #1,D5
    movem.l (SP),regs
    bsr     print_fname ;Filenamen ausgeben
    pea     new_text(A6)
    bsr     print_line
    bra.s   check_it6
check_it8:
    btst    #0,flag(A6) ;Ausgabe?
    bne.s   check_it6   ;Nein! =>
    movem.l (SP),regs
    bsr     print_fname ;Filenamen ausgeben
    bra.s   check_it6

virus1:
    movem.l (SP),regs   ;Filenamen nach A0
    lea     virus1_text(A6),A4 ;VCS-Linkvirus
    bra.s   check_it9
virus2:
    movem.l (SP),regs   ;Filenamen nach A0
    lea     virus2_text(A6),A4 ;Milzbrand-Virus
check_it9:
    bsr.s   print_fname ;Fname ausgeben
    pea     virus_text(A6)
    bsr     print_line  ;Meldung machen!
    move.l  A4,-(SP)
    bsr     print_line
    addq.w  #1,virus_count(A6) ;Virenanzahl 
check_it6:
    movem.l (SP)+,regs
    move.l  (SP)+,D0
    ENDC 
hunt_dir3:
    btst    #4,11(A0)   ;ein Ordner?
    beq.s   hunt_dir2   ;Nein! =>
    cmpi.b  #$E5,(A0)   ;Gelöscht?
    beq.s   hunt_dir2   ;Ja! =>
    cmpi.w  #'. ',(A0)
    beq.s   hunt_dir2   ;Dummy-Einträge ignorieren
    cmpi.w  #'..',(A0) 
    beq.s   hunt_dir2 
    movem.l D0/A0/A3,-(SP)
    REPT 8
    move.b  (A0)+,(A3)+
    ENDR
    move.b  #'.',(A3)+
    REPT 3
    move.b  (A0)+,(A3)+ ;in den Pfad kopieren
    ENDR
    addq.l  #1,A0 
    move.b  #'\',(A3)+
    move.l  (A0)+,D0    ;Baumlänge in Einträgen
    movea.l (A0),A0 
    bsr     hunt_dir
    addq.w  #1,D3       ;INC Anzahl der Ordner
    movem.l (SP)+,D0/A0/A3 
hunt_dir2:
    lea     32(A0),A0   ;=> nächsten Eintrag
    dbra    D0,hunt_dir ;alle Einträge durch? 
hunt_dir4: 
    rts
    ENDPART
*************************************************
* Filenamen ab A0 ausgeben                      *
*************************************************
    PART    'print_fname' 
print_fname:
    movem.l D0-D2/A0-A3,-(SP)
    REPT 8
    move.b  (A0)+,(A3)+ ;Filenamen
    ENDR
    move.b  #'.',(A3)+
    REPT 3
    move.b  (A0)+,(A3)+ ;an den Pfad anhängen
    ENDR
    btst    #4,(A0)     ;ein Ordner?
    beq.s   print_fname0
    move.b  #'\',(A3)+  ;dann auch so abschließen
print_fname0:
    move.b  #13,(A3)+   ;CR
    move.b  #10,(A3)+   ;LF
    clr.b   (A3) 
    lea     pfad(A6),A3
print_fname1: 
    moveq   #0,D0
    move.b  (A3)+,D0    ;Zeichen holen
    beq.s   print_fname2 ;Ende des Pfades => 
    cmp.b   #' ',D0     ;Spaces ignorieren
    beq.s   print_fname1 ;nächstes Zeichen => 
    cmp.b   #'.',D0 ;Extension erreicht?
    bne.s   print_fname3 ;Nein! => 
    cmpi.b  #' ',(A3)   ;es folgt ein " "?
    beq.s   print_fname1 ;dann den ignorieren
print_fname3:
    bsr     chrout      ;das Zeichen ausgeben
    bra.s   print_fname1 
print_fname2:
    movem.l (SP)+,D0-D2/A0-A3
    rts
    ENDPART
*************************************************
* read_dir() - Kompletten Dir-Baum einlesen     *
*             (ab A0 liegt er, Länge in D0)     *
* Diese Routine liest das Root-Directory ein    *
* und ruft dann bei jedem Ordner die Routine    *
* "read_sub_dir" auf, welche sich selbst        *
* wieder rekursiv aufrufen kann.                *
*************************************************
    PART 'read_dir' 
read_dir:
    movem.l D1-D7/A1-A6,-(SP)
    move.w  rdlen(A5),D0    ;Länge des Root-Dirs 
    mulu    recsiz(A5),D0   ;mal Sektorgröße 
    bsr     get_mem         ;Speicher anfordern
    movea.l D0,A4           ;Zeiger auf das ROOT-Dir
    movea.l D0,A3           ;Anfang des Root-Dir
    move.w  drive(A6),-(SP) ;aktuelles Drive 
    move.w  fatrec(A5),D1 
    add.w   fsiz(A5),D1
    move.w  D1,-(SP)        ;Startsektor des Root-Dir
    move.w  rdlen(A5),-(SP) ;Länge der FAT 
    move.l  A4,-(SP)        ;in den Buffer
    move.l  #$040000,-(SP)
    trap    #13             ;Rwabs() - DIR einlesen
    lea     14(SP),SP
    tst.l   D0 
    bmi     _exit
    move.w  rdlen(A5),D7    ;Länge des Root-Dir
    mulu    recsiz(A5),D7   ;mal Sektorgröße
    lsr.l   #5,D7           ;\32 Bytes (Eintraggröße)
    subq.w  #1,D7           ;Gesamtanzahl der Eintrag-1 
hunt_dir_loop:
    btst    #4,11(A4)       ;ein Ordner?
    beq.s   hunt_dir_loop1  ;Nein! =>
    cmpi.b  #$E5,(A4)       ;gelöschter Ordner?
    beq.s   hunt_dir_loop1  ;Ja! =>
    bsr.s   read_sub_dir
hunt_dir_loop1:
    lea     32(A4),A4       ;nächster Eintrag
    dbra    D7,hunt_dir_loop ;alle Einträge?
    movea.l A3,A0           ;Anfangsadresse des Dirs
    move.w  rdlen(A5),D0    ;Länge des Dirs in Bytes 
    mulu    recsiz(A5),D0   ;errechnen
    lsr.l   #5,D0           ;\32 Bytes (Eintragsgröße)
    movem.l (SP)+,D1-D7/A1-A6 
    rts
    ENDPART
*************************************************
* read_sub_dir() - Unterverzeichnisse ab A4     *
*                  rekursiv einlesen            *
* A4 zeigt auf den Ordner, der eingelesen       *
* werden soll. Es wird die Clusternummer        *
* ermittelt, dann geht's los. Damit             *
* "hunt_dir" das alles richtig hinbekommt,      *
* werden die Einträge mit Langworten            *
* (Zeigern) verkettet. Dazu steht im Ordner-    *
* eintrag (der ist 32 Bytes lang) bei Offset    *
* 16 ein Langwort, welches auf den Ordner       *
* zeigt. Beim Offset 20 steht die max. Anzahl   *
* an Einträgen die in diesem Ordner möglich     *
* sind. Diese Angabe wird von "hunt_dir"        *
* ebenfalls benötig.                            *
*************************************************
    PART 'read_sub_dir' 
read_sub_dir:
    movem.l D0-A6,-(SP) 
    movep.w 27(A4),D3
    move.b  26(A4),D3       ;Clusternr. im Intel-Format 
    movea.l A4,A3           ;Ptr auf Hauptdirectory merken 
    moveq   #0,D5           ;Clusteranzahl des Sub-Dirs 
read_sub_dir1: 
    moveq   #0,D0
    move.w  clsizb(A5),D0   ;Bytes pro Cluster 
    bsr     get_mem         ;Speicher anfordern
    movea.l D0,A4           ;Zeiger auf den Cluster
    tst.w   D5
    bne.s   read_sub_dir5
    move.l  A4,16(A3)       ;Zeiger auf Sub-Dir
read_sub_dir5:
    move.w  drive(A6),-(SP) ;akt.Drv 
    move.w  D3,D0           ;akt.Clusternummer
    subq.w  #2,D0
    mulu    clsiz(A5),D0    ;mal Sektoren pro Cluster 
    add.w   datrec(A5),D0   ;+ erster freier Sektor 
    move.w  D0,-(SP)        ;= abs. Sektor
    move.w  clsiz(A5),-(SP) ;Cluster einlesen 
    move.l  A4,-(SP)        ;in den Buffer
    move.l  #$040000,-(SP)
    trap    #13             ;Rwabs() - Cluster einlesen
    lea     14(SP),SP
    tst.l   D0 
    bmi     _exit

    move.w  clsizb(A5),D7   ;Bytes pro Cluster 
    lsr.w   #5,D7           ;\32 Bytes (Eintragsgröße)
    subq.w  #1,D7           ;für DBRA
read_sub_dir2:
    btst    #4,11(A4)       ;ein Ordner?
    beq.s   read_sub_dir3   ;Nein! => 
    cmpi.b  #$E5,(A4)       ;gelöscht?
    beq.s   read_sub_dir3   ;Ja! => 
    cmpi.w  #'. ',(A4)
    beq.s   read_sub_dir3   ;ignore Dummy-Einträge 
    cmpi.w  #'..',(A4) 
    beq.s   read_sub_dir3 
    bsr.s   read_sub_dir 
read_sub_dir3:
    lea     32(A4),A4       ;nächster Eintrag
    dbra    D7,read_sub_dir2 ;alle Einträge? 
    addq.w  #1,D5           ;Clusteranzahl des Sub-Dir 
    move.w  D3,D0
    add.w   D0,D0           ;mal 2, als Zeiger auf die FAT 
    movea.l fat_adr(A6),A0  ;Ptr: decodierte FAT 
    move.w  0(A0,D0.w),D3   ;Nr des Folgeclusters 
    bpl.s   read_sub_dir1   ;Ende? Nein! =>
    mulu    clsizb(A5),D5   ;Bytes pro Cluster 
    lsr.l   #5,D5           ;Größe des Sub-Dir in Bytes
    move.l  D5,12(A3)       ;Anzahl der mgl.Einträge
    movem.l (SP)+,D0-A6 
    rts
    ENDPART
*************************************************
* Laufwerk D0 als akt.Laufwerk anmelden, FAT    *
* einlesen. Der freie Speicherplatz wird in     *
* D0 zurückgegeben                              *
* WICHTIG: A5 zeigt nach "set_drive" STETS      *
*      auf den BPB des aktuellen Laufwerkes.    *
*      Davon wird in "read_dir" ausgegangen.    *
************************************************* 
    PART 'set_drive' 
set_drive:
    movem.l D1-A4,-(SP)
    move.w  D0,drive(A6)    ;akt.Laufwerk setzen

    bsr.s   get_bpb         ;BPB-Adresse holen
    moveq   #0,D0 
    move.w  numcl(A5),D0
    add.l   D0,D0           ;Gesamtzahl der Cluster*2
    bsr     get_mem         ;Speicher anfordern
    move.l  D0,fat_adr(A6)

    move.w  fsiz(A5),D0     ;Länge der FAT 
    mulu    recsiz(A5),D0   ;mal Bytes pro Sektor 
    bsr     get_mem         ;Speicher anfordern
    move.l  D0,fat_buffer(A6)

    move.w  drive(A6),-(SP) ;aktuelles Drive 
    move.w  fatrec(A5),-(SP) ;Anfang der 2.FAT 
    move.w  fsiz(A5),-(SP)  ;Länge der FAT 
    move.l  fat_buffer(A6),-(SP) 
    move.l  #$040000,-(SP)
    trap    #13             ;2.FAT komplett einlesen
    lea     14(SP),SP
    tst.l   D0 
    bmi     _exit

    bsr.s   wandel_fat      ;FAT ins 68000er-Format

    movea.l fat_adr(A6),A0  ;Ptr: decodierte FAT 
    addq.l  #4,A0           ;erste 2 Cluster ignorieren
    move.w  numcl(A5),D1
    subq.w  #3,D1           ;2 Cluster abziehen (DBRA!)
    moveq   #0,D0           ;Anzahl der freien Cluster
set_drive1:
    tst.w   (A0)+           ;ein freier Cluster?
    bne.s   set_drive2      ;Nein! => 
    addq.w  #1,D0           ;INC freie Cluster
set_drive2:
    dbra    D1,set_drive1   ;alle gezählt?
    mulu    clsizb(A5),D0   ;mal Bytes pro Cluster
    movem.l (SP)+,D1-A4
    rts
    ENDPART

*************************************************
* BPB des akt.Laufwerkes nach A5                *
*************************************************
    PART 'get_bpb' 
get_bpb:
    move.w  drive(A6)(SP) 
    move.w  #7,-(SP)
    trap    #13             ;Getbpb(drive)
    addq.l  #4,SP 
    move.l  D0,akt_bpb(A6)
    beq     _exit
    movea.l D0,A5           ;Zeiger auf den BPB
    rts
    ENDPART
*************************************************
* FAT vom 12-bit- bzw. 16-bit-Intel-Format      *
* ins 16-bit-68000er-Format                     * 
*************************************************
    PART 'wandel_fat' 
wandel_fat:
    movea.l fat_buffer(A6),A0 ;Ptr: FAT
    movea.l fat_adr(A6),A1  ;Ptr: decodierte FAT
    move.w  fsiz(A5),D0     ;Länge der FAT
    mulu    recsiz(A5),D0   ;mal Bytes pro Sektor
    move.w  bflags(A5),D1   ;Flags holen
    btst    #0,D1           ;12-bit-FAT?
    beq.s   wandel_fat2     ;Ja! =>
    lsr.w   #1,D0           ;Anzahl der Worte
    subq.w  #1,D0           ;für DBRA
wandel_fat1:
    movep.w 1(A0),D1
    move.b  (A0),D1         ;Intel-Wandl. bei 16-bit-FAT
    addq.l  #2,A0
    move.w  D1,(A1)+
    dbra    D0,wandel_fat1
    rts
wandel_fat2:
    divu    #3,D0           ;12-bit-FAT wandeln
wandel_fat3:
    movep.w 1(A0),D1
    move.b  (A0),D1         ;Intel-Word holen
    and.w   #$0FFF,D1       ;Bit 12-15 sind unwichtig 
    cmp.w   #$0FF0,D1       ;Nummer $FF0-$FFF?
    blo.s   wandel_fat4     ;Nein! =>
    or.w    #$F000,D1       ;Vorzeichen erweitern
wandel_fat4:
    move.w  D1,(A1)+        ;Cluster merken
    movep.w 2(A0),D1
    move.b  1(A0),D1        ;Intel-Word holen
    lsr.w   #4,D1           ;Bit 0-3 sind unwichtig
    cmp.w   #$0FF0,D1       ;Nummer $FF0-$FFF?
    blo.s   wandel_fat5     ;Nein! =>
    or.w    #$F000,D1       ;Vorzeichen erweitern
wandel_fat5:
    move.w  D1,(A1)+        ;Cluster merken
    addq.l  #3,A0           ;3 Bytes sind fertig
    dbra    D0,wandel_fat3  ;schon alle Tripel?
    rts
    ENDPART
*************************************************
* D0=get_mem(Byteanzahl in D0)                  *
*   Speicher vom Heap anfordern                 *
*************************************************
    PART 'get_mem' 
get_mem:
    move.l  A0,-(SP)
    addq.l  #1,D0 ;EVEN
    and.b   #-2,D0
    move.l  heap_pnt(A6),D1 ;alter Heap-Pointer exg D0,D1
    add.l   D1,heap_pnt(A6) ;Platz auf dem Heap 
    movea.l anfang-256+4(PC),A0 ;Speicherende 
    cmpa.l  heap_pnt(A6),A0 ;Speicherobergrenze?
    blo     _exit           ;Ja! => raus =>
    movea.l (SP)+,A0
    rts
    ENDPART
*************************************************
* Ab hier: das DATA-Segment                     *
*************************************************
    DATA 
    IF virus 
files_text:
    DC.B ' Dateien, ',0 
files_text2:
    DC.B ' geänderte bzw. neue Dateien.',13,10,0 
folders_text:
    DC.B ' Ordner sind vorhanden.'
    DC.B ' Schreiben?',13,10,0 
init_text:
    DC.B 27,'E',27,'e'
    DC.B 'LVF V1.5 (Link-Virus-Finder)',13,10 
    DC.B '(c) 1989 by X-soft,'
    DC.B ' Markus Fritze',13,10,10,0 
fehler_text:
    DC.B 'Prüfsumme ist fehlerhaft'
    DC.B ' (Übernehmen?)',13,10,0 
virus_text:
    DC.B 7,'Datei enthält '
    DC.B ' wahrscheinlich ',0 
virus1_text:
    DC.B 'einen VCS-Linkvirus!',13,10,7,0 
virus2_text:
    DC.B 'den Milzbrand-Linkvirus!',13,10,7,0 
viren_text:
    DC.B ' wahrscheinlich verseuchte'
    DC.B ' Dateien gefunden!',7,13,10,0 
new_text:
    DC.B 'Datei wurde hinzugefügt.',13,10,0 
fname:
    DC.B 'LVF.DAT',0
    ELSE 
files_text:
    DC.B ' Dateien mit insgesamt ',0 
files_text2:
    DC.B ' Bytes gefunden.',13,10,0 
folders_text:
    DC.B ' Ordner sind vorhanden.'
    DC.B ' Taste drücken.',13,10,0 
init_text:
    DC.B 27,'E',27,'e'
    DC.B 'FFF V1.5 (Fast-File-Finder)',13,10 
    DC.B '(c) 1989 by E-soft, Markus Fritze'
    DC.B 13,10,10,0 
    ENDC
exit_text:  DC.B 27,'E',27,'f',0
drive text: DC.B 'Laufwerk ',0 
drive_text2:DC.B ': (',0
drive_text3:DC.B ' Bytes frei.)',13,10,0

**********************************************
* Ab hier: das BSS-Segment                   *
********************************************** 
            BSS
            DS.L 1024 ;4k Stack f. rekursive Suche 
own_stack:  DS.L 0
drive:      DS.W 1    ;aktuelles Laufwerk
akt_bpb:    DS.L 1    ;Ptr: BPB des akt.Laufwerks
fat_buffer: DS.L 1    ;Adresse der gelesenen FAT 
fat_adr:    DS.L 1    ;Adresse der decodierten FAT
virus_count:DS.W 1    ;Anz. der gefundenen Viren 
flag:       DS.B 1    ;Bit 0=1: keine Fileausgabe
;Bit 1=1: neue Programme übernehmen 
            EVEN
pfad:       DS.B 256  ;Platz für den Suchpfad
heap_pnt:   DS.L 1
            IF virus
data_base:  DS.L 1    ;Ptr: File-Daten im RAM 
data_buff:  DS.B 24*max_prgs ;Prg-Buffer 
sektor_buffer:DS.B max_sektorsize ;Sektorbuf. 
            ELSE
search_mask:DS.B 12   ;Suchmaske (Joker) 
search_chars:DS.B 12  ;Suchzeichen 
            ENDC
heap:       DS.L 0    ;freier Speicher ab hier
            END

Markus Fritze
Links

Copyright-Bestimmungen: siehe Über diese Seite