← ST-Computer 08 / 1987

Floppyspielereien Teil 2

Kurs

Eine Vielzahl von Themen erwartet Sie in der zweiten Folge unseres Floppy-Kurses. Neben Informationen zu Directory und Bootsektor gibt es zwei Listings sowie Hinweise fĂŒr einen Kopierschutz.

Vorab noch einige Anmerkungen zu HYPERFORMAT, dem Formatierprogramm aus der letzten ST. Mir ist zu Ohren gekommen, daß es vereinzelt Schwierigkeiten mit dem Beschreiben HYPERFORMATierter Disketten gab. Diese Probleme gibt es offenbar dann, wenn zwei Bedingungen Zusammentreffen:

  1. Das Laufwerk dreht zu schnell, oder
  2. Laufwerk und/oder Controller stammen aus einer alten Bauserie des ST.

Mein Verdacht ist also, daß es mehrere Bauserien des Controllers gibt. Sollten Sie einer von den Pechvögeln sein, könnten Sie - wenn Sie sich das Zutrauen und keine GarantieansprĂŒche mehr haben - Ihren ST öffnen und mal nachsehen, welche genaue Bezeichnung Ihr Floppy-Controller hat, das ist der Chip, der ’WD1772’ heißt. Schreiben Sie bitte mir oder dieser Zeitschrift und geben Sie diese Bezeichnung mit an. Ich arbeite daran, auch die Ă€ltesten Bauserien noch glĂŒcklich zu machen.

HĂ€ufig stellt sich offenbar auch die Frage, ob das HYPERFORMAT kompatibel zum AMIGA-Format sei. Zwar haben beide Formate die gleiche Anzahl von Sektoren pro Tracks, zudem verwenden beide das sogenannte MFM-Verfahren zur physikalischen Aufzeichnung. Doch leider leistet sich der AMIGA (wie bei Commodore ĂŒblich) einen eigenen Standard, was das Format der einzelnen Sektoren selbst angeht. Zum GlĂŒck ist der Floppy-Controller des AMIGA sehr flexibel, da er hauptsĂ€chlich softwaregetrieben ist. Und so arbeite ich bereits an einem Programm fĂŒr den AMIGA, das ihm ST-Disketten verstĂ€ndlich macht.

Und fĂŒr diejenigen, die meinem Formatierprogramm nicht so recht trauen, habe ich auch noch ein Schmankerl, nĂ€mlich Listing 1. Dort finden Sie CHKDISK, ein Programm, das Disketten auf Lesefehler untersucht. Es ist nicht nur fĂŒr HYPERFORMATierte Disketten brauchbar. Beim Start meldet es sich mit einem MenĂŒ. Mit ’t’ können Sie die Anzahl der Spuren verĂ€ndern, die geprĂŒft werden sollen, mit ’s’ die Anzahl der Seiten und mit ’p’ die Zahl der Sektoren pro Spur. ’C’ bricht das Programm jederzeit ab, mit RETURN wird es gestartet. WĂ€hrend des Checkvorgangs gibt das Programm den aktuellen Track aus. Wenn ein Fehler auf der Diskette ist, meldet sich CHKDISK mit einem Piepsen und einer Fehlermeldung mit genauer Angabe, wo der Fehler aufgetreten ist. Bei CHKDISK zeigt sich der Vorteil von FTYPERFORMATierten Disketten am deutlichsten: Lassen Sie mal eine HYPERFORMATierte Diskette checken und dann eine normale, und messen Sie jeweils die Zeit, die dafĂŒr benötigt wird!

Am besten, Sie setzen sich gleich mal vor Ihren Rechner und laden Ihren besten Diskmonitor, von denen es ja mittlerweile genug public domain gibt (zum Beispiel auch auf PD-Disketten der ST). Ich gehe davon aus, daß Ihr Diskmon zumindest Sektoren lesen, schreiben und in ASCII und Hexadezimal anzeigen kann sowie eine wenigstens minimale Editierfunktion hat. Sollten Sie keinen solchen Monitor, aber zumindest GfA-Basic haben, können Sie auch das Listing 2 abtippen -dann haben Sie einen kleinen Diskmonitor mit den erwĂ€hnten FĂ€higkeiten.

Formatieren Sie zunĂ€chst eine freie Diskette auf einseitiges Normalformat. Dazu mĂŒssen Sie Laufwerk A einmal anklicken und dann 'formatiere’ im DATEI-MenĂŒ wĂ€hlen. Geben Sie dabei als Diskettennamen ’GRUMMEL. TST’ an.

In der letzten Folge hatte ich bereits erwĂ€hnt, daß die Directory, das Inhaltsverzeichnis der Diskette, beim einseitigen Normalformat bei Track 1, Sektor 3 beginnt. Darin stehen der Diskettenname (wenn angegeben) und die Namen aller Dateien, die Sie darauf abspeichern, mitsamt einigen wichtigen Informationen. So, ist das Laufwerk fertig? Dann starten Sie jetzt Ihren Diskmonitor, legen Sie die formatierte Diskette ein und lesen Sie Track 1, Sektor 3 ein (siehe Anleitung Ihres Diskmons). Es sollte Ă€hnlich Verwirrendes erscheinen wie das hier:

47 52 55 4D 4D 45 4C 20 54 53 54 08 00 00 00 00 GRUMMEL TST......
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .................

In den ersten 32 Bytes der Directory steht jetzt der Eintrag fĂŒr den Diskettennamen. Die ersten 11 Bytes sind fĂŒr den Namen und die Extension reserviert. Filenamen unter 8 Bytes werden mit ZwischenrĂ€umen erweitert, der Punkt wird nicht dargestellt, danach folgt die dreistellige Extension. Was die LĂ€nge der Dateinamen angeht, ist TOS ja - wie MS-DOS - recht geizig. Wenn man da an die Filenamen des AMIGA denkt... Die amputierten Dateinamen haben immerhin den Vorteil, daß das Verzeichnis kompakter wird und damit schneller zu laden ist: und wer will schon eine so einschlĂ€fernde Directory-Leseroutine wie beim AMIGA?

Bitnr. Bit = 1 Bit = 0 Anmerkungen
0 nur Lesen möglich Lesen und Schreiben erlaubt per 'Disk Info' im Desktop zu erreichen
1 'Hidden file' kein 'hidden file' Listschutz fĂŒr das Verzeichnis
2 Systemdatei normale Datei Systemdateien werden auch nicht gelistet
3 Name ist Diskettenname Name ist Dateiname siehe Text oben
4 Datei ist ein Ordner Datei ist kein Ordner siehe Text unten
5 Datei wurde ordnungsgemĂ€ĂŸ bearbeitet 'Archive-Bit', vor allem fĂŒr den Backup bei der Festplatte

Abb. 1

(Bytenummer)	
0	1	2	3	4	5	6	7	8	9	10	11	12	13	14	15	
52	4F	4E	4E	49	45	20	20	55	53	41	00	00	00	00	00 BONNIE	USA	
00	00	00	00	00	00	86	03	46	oc	02	00	98	13	00		
16	17	IS	19	20	21	22	23	24	25	26	27	28	29	30	31	

Abb. 2

Wenn Sie genau nachzĂ€hlen, haben wir noch nicht alle Bytes besprochen, in denen etwas steht. Im zwölften Byte steht ’08’. Dies ist das Dateikennzeichen und gibt in diesem Fall an, daß ’GRUMMEL.TST’ der Name der Diskette ist. In das Dateikennzeichen sind folgende Informationen kodiert:

MS-DOS kennt genau das gleiche Dateikennzeichen, sogar die Bitnummern stimmen ĂŒberein. Wieder sieht man, daß die TOS-Entwickler auf der Diskettenebene sehr bemĂŒht waren, sich an den IBM-Standard zu halten.

BITGESTEUERTE TARNKAPPEN UND ANDERE MAGIE

NatĂŒrlich sind Kombinationen der Bits möglich. Sie könnten also einen sichtgeschĂŒtzten Ordner anlegen, der nicht beschreibbar ist, indem Sie das Dateikennzeichen auf $13 Ă€ndern - in der Theorie. Praktisch scheinen hier noch ein paar Unsauberkeiten von TOS verborgen zu sein. Experimentieren Sie ruhig ein wenig mit Ihrem Diskmonitor herum.

Kopieren Sie nun irgendeine Datei auf die leere Diskette und schauen Sie mit dem Diskmon im Verzeichnis (Track 1, Sektor 3) nach. Nehmen wir einmal an, die Datei hieße ’RONNIE.USA’. Dann bekĂ€men Sie etwa folgendes Bild:

Byte 11 zeigt uns, daß RONNIE.USA eine ganz normale Datei ist. Die restlichen Bytes in dieser Zeile bedeuten schlicht & einfach nichts. Ein Directoryeintrag ist nun aber 32 und nicht 16 Bytes lang. Was ATARI in den Bytes der zweiten HĂ€lfte versteckt hat, ist schnell erklĂ€rt:

Byte Inhalt
22-23 Uhrzeit der letzten Änderung
24-25 Datum
26-27 Erster Datencluster der Datei
28-31 GrĂ¶ĂŸe der Datei (Langwort)

Alle diese Bytepaare sind im 8086-Format abgespeichert (niedrigstwertiges Byte zuerst). Man muß sie also erst einmal umdrehen. Sehen wir uns das Datum von RONNIE.USA in den Bytes 22-23 an: Wir drehen ’86 03’ um und erhalten ’03 86’. AufgeschlĂŒsselt in Bits:

0000 0 011 100 0 0110 (0 3 8 6) 
Stunden—Minuten—Sekunden/2

Also in diesem Fall: 0 Uhr (0000 0), 24 Minuten (011100), 12 Sekunden (00110). FĂŒr das Datum von RONNIE.USA gilt Ă€hnliches:

0000 110 0 010 0 0110 (0 C 4 6)
Jahr seit—Monat—Tag—1980

Was bedeutet, daß ich RONNIE.USA am 6.2.1986 gespeichert habe - aber das heißt auch nur, daß in meinem ST keine Echtzeituhr eingebaut ist, denn TOS setzt als Standardwert genau diesen Tag ein.

Bytes 26-27 geben an, bei welchem Cluster (das sind zwei aufeinanderfolgende logische Sektoren) die Datei beginnt. Auch diese Bytes muß man erst krĂ€ftig aufmischen, ebenso wie die darauffolgenden Bytes fĂŒr die GrĂ¶ĂŸenangabe der Datei - waschechtes 8086-Format.

VON PHANTOMEN, FRÖSCHEN UND PRINZEN

Sollten Sie auf der Reise durch diverse Verzeichnisse einmal einen Phantomeintrag entdecken, der im Directoryfenster des Desktops nicht zu finden ist, dann ist das wahrscheinlich ein Eintrag, der mit dem Byte ’E5’ beginnt. ’E5’ heißt: Dieses File ist gelöscht. Nun kommen Sie mir ja nicht mit der Idee, Sie brĂ€uchten nur dieses Byte zu Ă€ndern, und schon hĂ€tten Sie eine bereits abgetakelte Datei ins Leben zurĂŒckgekĂŒĂŸt. Dieser Frosch wird dadurch nĂ€mlich nicht zum schönen Prinzen ( = intakte Datei), es sei denn, die Datei ist höchstens ein Cluster (1024 Bytes) lang. Denn im Directoryeintrag steht nur ein Zeiger auf den ersten Cluster der Datei in der FAT (siehe letzte Folge) - der Rest der Verkettung wird in der FAT gelöscht, wenn Sie einen Prinzen, Verzeihung: eine Datei in die WĂŒste schicken.

Tun Sie jetzt mal etwas ganz Verwegenes: Legen Sie die GRUMMEL-Diskette ein, löschen sie alle Dateien auf ihr und klicken Sie im Desktop 'Neuer Ordner’ an. Nennen Sie ihn, wie Sie gerade lustig sind, also zum Beispiel LUSTIG.FLD (FLD fĂŒr englisch FOLDER = Ordner). Entern Sie ihren Diskmonitor und lesen Sie den Directorysektor. Sie erhalten folgendes:

< Bytenummer 1													
0	1	2	3	4	5	6	7	8	9	10	11	12	13	14	15	
4C	55	53	54	49	47	20	20	46	4C	44	10	00	00	00	00	LUSTIG FLD	
00	00	00	00	00	00	55	04	46	0C	02	00	00	00	00	00	
16	17	18	19	20	21	22	23	24	25	26	27	28	29	30	31	

Abb. 3

Byte 11 (Dateikennzeichen) sagt uns: LUSTIG.FLD ist ein Ordner. Alle restlichen Informationen sind die gleichen wie bei normalen Dateien, nur zeigen Byte 26 und 27 nicht auf den ersten Datencluster der Datei, sondern auf den ersten Cluster des Unterverzeichnisses (Subdirectory). Folgen wir diesem Hinweis: 'Cluster 2’ ist der erste von TOS vergebene Cluster ĂŒberhaupt und beginnt bei einseitigen Disketten auf Spur 2, Sektor 1. Lesen wir diesen Sektor ein...


2E	20	20	20	20	20	20	20	20	20	20	10	00	00	00	00 .		
00	00	00	00	00	00	04	55	0C	46	02	00	00	00	00	00 . .		
2E	2E	20	20	20	20	20	20	20	20	20	10	00	00	00	00 .			
00	00	00	00	00	00	04	55	0C	46	00	00	00	00	00	00 . .		

Abb.4

Es werden also in diesem Unterverzeichnis gleich zwei EintrĂ€ge angelegt: Eine fiktive Datei mit Namen und eine mit Namen Diese beiden No-bodies sind nur dazu da, um TOS im Wirrwarr der Unterverzeichnisse die Orientierung zu erleichtern. (beziehungsweise das Clusterbyte dieses Eintrags) zeigt auf das Unterverzeichnis selbst, so daß TOS nicht immer den ganzen Baum von Verzeichnissen lesen muß, um herauszufinden, wo die aktuelle Directory beginnt. zeigt auf die „Vater“directory, die hier angeblich bei Cluster 0 beginnt - das Zeichen dafĂŒr, daß die Vaterdirectory das oberste, das Wurzelverzeichnis ist.

ICH TARZAN - DU JANE

Mit Hilfe dieser speziellen EintrÀge schwingt sich TOS (Tarzan Operating System) an Zeigerlianen durch den Dateidschungel:

Die ’..'-EintrĂ€ge zeigen jeweils auf das ĂŒbergeordnete Verzeichnis, die ’.'-EintrĂ€ge praktisch auf sich selbst.

Innerhalb eines Unterverzeichnisses können beliebig viele weitere Unter-Verzeichnisse angelegt werden - das heißt, fast. Wenn mehr als 40 Unterverzeichnisse auf einer Diskette sind, stĂŒrzt Ihnen Ihr TOS ab: Wenn Sie eine Diskette einlegen und das Verzeichnis einiesen, merkt sich TOS alle Di-rectories in einer speziellen Liste.

Nur ist der Platz fĂŒr diese Liste leider beschrĂ€nkt - mehr als 40 Verzeichnisse passen nicht rein.

Bei Disketten ist das nicht weiter tragisch, aber Festplattenbesitzer haben daran schwer zu knabbern - auf 20 Megabyte sind schnell 40 Directories erzeugt. Zum GlĂŒck gibt es korrigierte ROMs zu kaufen, und gerĂŒchteweise habe ich auch davon gehört, daß die ROMs in den Mega-STs diesen Fehler nicht mehr aufweisen werden.

Alle diese kleinen Details der Diskettenverwaltung gibt es so oder ganz Ă€hnlich sowohl in MS-DOS wie in UNIX - und daher auch das viele Gerede ĂŒber die KompatibilitĂ€t (ein fĂŒrchterliches Wort) von MS-DOS und TOS. Wahr ist: Auf Diskettenebene verstehen sich MS-DOS und TOS ganz gut. Aber sonst werden Sie sich schön umschauen, wenn Sie eine PC-Datei auf einem ST zu starten versuchen.

MÜNCHHAUSEN UND DER ST

Nun zu einem weiteren rĂ€tselhaften Bereich auf der Diskette, dem Bootsektor. Der Bootsektor ist der erste Sektor auf der Diskette und wird bei jedem Diskettenwechsel ĂŒberprĂŒft. Er enthĂ€lt wichtige Daten ĂŒber das Format einer Diskette, kann aber auch selbst ein kleines Programm enthalten, das automatisch ausgefĂŒhrt wird und das System hochbootet - er ist also der Haarschopf, an dem sich der Rechner selbst aus dem Sumpf zieht. Deshalb reagiert Ihr ST auch ziemlich heftig, wenn er so einen Bootsektor nicht lesen kann - hĂŒten Sie also Ihre Systemdisketten wie Ihr Glasauge, machen Sie am besten eine Kopie auf eine hochwertige Diskette, schließen Sie sie weg, geschĂŒtzt vor KinderhĂ€nden, Hundeschnauzen, Kaffeetassen, Erdbeben, Feuer, Wasserrohrbruch und grĂ¶ĂŸeren intergalaktischen Katastrophen.

Es gibt zwei Typen von Bootsektoren: ausfĂŒhrbare und nicht ausfĂŒhrbare. Erstere werden fĂŒr Systemdisketten verwendet, von denen das TOS gebootet wird. AusfĂŒhrbare Bootsektoren werden vom Betriebssystem so abgeĂ€ndert, daß ihre Checksumme (das heißt, die auf 16 Bit reduzierte Summe ihrer Bytes) gleich $ 1234 ist. Trifft das zu, versucht TOS, ein Programm im Bootsektor auszufĂŒhren.

Gemeinsam ist allen Bootsektoren folgender Aufbau: (Byte - ErklÀrung)

0-1
Verzweigungsbefehl auf Bootprogramm (wenn ausfĂŒhrbar)

2-7
’Loader’

8-10
24Bit-Seriennummer

11-12
BPS: Bytes pro Sektor (normal 512)

13
SPC: Sektoren pro Datencluster (normal 2)

14-15
RES: reservierte Sektoren (normal 1)

16
FAT: Anzahl der FATs (normal 2) 17-18

DIR: Anzahl der EintrÀge in der Directory (normal 112)

19-20
SEC: Anzahl der Sektoren auf der Diskette

21
MEDIA: „Media-Descriptor-Byte“

22-23
SPF: Sektoren pro FAT

24-25
SPT: Sektoren pro Track

26-27
SIDE: Anzahl der Seiten (SF354:1 SF314:2)

28-29
HID: versteckte Sektoren 510-511

Ausgleichswerte fĂŒr die Checksumme (Differenz der Checksumme ĂŒber die voranstehenden Bytes und $1234)

Alle 16-Bit-Worte sind wieder im 8086/ INTEL-Format abgesichert (also niedrigstwertiges Byte zuerst). Bei ausfĂŒhrbaren Bootsektoren sind weitere Bytes interessant:

30
EXEC: Ist das Flag gesetzt, wird versucht, nach dem Betriebssystem gleich COMMAND.PRG zu finden und zu starten (ohne Umweg ĂŒber das DeskTop)

32
MODE: Ist MODE Null, wird das File FNAME gesucht. Wenn nicht, ist man im „Sektormodus“: Es werden einzelne Sektoren geladen (siehe SCNT und START)

34
START: erster Sektor, der bei MODE= 1 geladen werden soll

36
SCNT: Anzahl der Sektoren, die bei MODE=l geladen werden sollen

38
ADDR: Speicheradresse, wohin das Bootfile oder die Sektoren geladen werden sollen

42
Speicheradresse, wohin FATs und Directory geladen werden sollen

46
FNAME: Filename bei MODE=0 (maximal 11 Zeichen)

58
Anfang des Bootprogramms

Alle diese Flags werde ich Ihnen ganz bestimmt nicht vorkauen, sonst paßt in dieses Heft höchstens noch das Vorwort hinein. Aber einige interessante Stellen werden wir uns schon ansehen.

Besorgen Sie sich zunĂ€chst einmal einen ausfĂŒhrbaren Bootsektor. Sie haben doch sicher noch eine bootfĂ€hige Systemdiskette? Wenn nicht, formatieren Sie eine Diskette mit HYPERFORMAT und geben Sie dabei an, daß sie ausfĂŒhrbar sein soll. HYPERFORMAT erzeugt dann einen ausfĂŒhrbaren Bootsektor auf Track 0, Sektor 1. Lesen Sie mit Ihrem Diskmonitor den Bootsektor ein und Ă€ndern Sie EXEC (Byte 30, hexadezimal $1E) von Null auf Eins. Jetzt mĂŒssen Sie noch in den Ausgleichsbytes eine Eins abziehen, damit die Checksumme wieder stimmt. Schreiben Sie diesen modifizierten Sektor auf Ihre Diskette zurĂŒck und kopieren Sie auf diese Diskette ein TOS. IMG, ein DESKTOP.INF und ein COMMAND.PRG. DrĂŒcken Sie den Reset-Knopf, und Sie werden sehen, daß COMMAND.PRG noch vor dem Desktop ausgefĂŒhrt wird.

Das kann man dazu verwenden, komplizierte BootvorgĂ€nge auszufĂŒhren. Denn wenn COMMAND.PRG startet, sucht es auf der Diskette eine Batchdatei namens AUTOEXEC.BAT und fĂŒhrt sie aus, falls vorhanden. Wenn Sie sich also so eine Diskette zurechtgezimmert haben, brauchen Sie wirklich nur noch den Netzschalter, um Ihr System hochzufahren, wie Sie es wollen - beispielsweise mit installierter, gefĂŒllter RAM-Disk, sauber gefĂŒhrtem Logbuch und einer fertigen Anwendung. Allerdings macht TOS beim Ausstieg aus COMMAND.PRG per ’exit’ noch Arger. Ich nehme an, daß das an nicht initialisierten GEM-Vektoren liegt - vielleicht hat sich ja schon ein Leser damit herumgeschlagen und kann darĂŒber berichten.

Sie ahnen bestimmt, welche Tricks mit dem Bootsektor möglich sind. Andern Sie doch mal FNAME von TOS. IMG nach UNIX.IMG, ebenso den Namen von TOS.IMG auf Ihrer Bootdiskette und verblĂŒffen damit Ihre Freunde! Oder gaukeln Sie Ihrem Rechner vor, das Laufwerk hĂ€tte 3 Seiten, 150 Spuren oder 0 Sektoren pro Track. KĂŒrzen Sie die Anzahl der FATs oder deren GrĂ¶ĂŸe (vielleicht auch die der Directory) und gewinnen so Speicher auf der Diskette. Allerdings mĂŒssen Sie bei allen Änderungen auf einem ausfĂŒhrbaren Bootsektor immer die Checksumme ausgleichen, indem Sie die letzten beiden Bytes des Bootsektor abĂ€ndern!

Das sollte Ansporn genug sein, sich mit dem Bootsektor zu beschĂ€ftigen. Es sei noch angemerkt, daß Sie natĂŒrlich nicht gezwungen sind, vom Bootsektor immer nur TOS.IMG zu laden...

Sollten Sie im Verlauf des Artikels bisher nicht alle Details verstanden haben, machen Sie sich keine großen Sorgen: Daran erkennt man die besonders nachdenklichen Menschen. Probieren Sie ein bißchen mit Ihrem Diskmonitor herum, kopieren oder löschen Sie Dateien, formatieren Sie mal normal, mal mit HYPERFORMAT, schnippeln Sie am Bootsektor herum und schauen Sie sich die Ergebnisse an. Kurz: Spielen Sie (das kann man auch ohne Joystick)! Was Sie dabei lernen, ist die Zeit sicher wert.

Wenn Sie beim Spielen einmal TOS-Fehlernummern bekommen (solche, die per Alert-Box im Desktop angezeigt werden), dann sind das bestimmt die Fehlernummern 33 und 35. Andere Nummern werden nicht angezeigt, obwohl TOS mehr Fehler unterscheidet. Das liegt an einem Fehler gerade in jener Routine des TOS, die die Fehler erkennen soll - ist das nicht tragisch? Man kann nur hoffen, daß ATARI bei den Mega-ST-Roms auch an diesen Fehler denkt.

Überhaupt gibt es allerlei Fehler in den ROMs. Bei dieser Gelegenheit möchte ich die wichtigsten, die mir aufgefallen sind, erwĂ€hnen:

  1. Die Laderoutinen sind Ă€ußerst langsam. Das liegt daran, daß TOS vor jedem Lesevorgang lieber zweimal nachprĂŒft, ob der Lesekopf auch wirklich ĂŒber der richtigen Spur liegt - was aber ziemlich unnötig ist. FAST-ROMs beheben diesen Fehler beim ROM-TOS, FASTLOAD und HYPERFORMAT beim RAM-TOS.
  2. Zuweilen kommt es vor, daß man ein Dokument auf dem Desktop anklickt und es sich auf dem Schirm anzeigen lassen will. Unter bestimmten (ungeklĂ€rten) UmstĂ€nden stĂŒrzt TOS selbst bei dieser primitiven Routine ab (2-10000 Bomben)!
  3. Dialoge, die Fehler haben, deren Eingabemaske (alpha) numerisch definiert wurde, stĂŒrzen ab, wenn man den Unterstrich (_) eingibt.
  4. Fehler bei mehr als 40 Unterverzeichnissen (siehe oben).
  5. Im Desktop lassen sich weder Ordner- noch Diskettennamen Ă€ndern. Das liegt hauptsĂ€chlich daran, daß die zugehörige GEMDOS-Funktion RENAME ($56) keine solchen speziellen Dateien umnennen kann. Zwar ist im Aufruf ein Dummywort vorgesehen, das wahrscheinlich gerade zur Unterscheidung der Dateitypen dienen sollte, doch bewirkt es nichts.
  6. Fehler in der Fehlerroutine (siehe oben).
  7. Wenn man sich das Desktop mal in Zeitlupe anschaut, erkennt man, daß Window-Elemente oft mehrere Male gezeichnet, gelöscht und wieder gezeichnet werden. Darunter leidet die Geschwindigkeit.
  8. Unsauberkeiten beim Dateikennzeichen (siehe oben).

Wenn Sie weitere Fehler entdeckt haben, melden Sie sich! Wenn erst genug Fehler bekannt werden, nimmt sich ATARI vielleicht ein Flerz und killt die Bugs im ROM der Mega-STs.

Im letzten Teil dieser Folge möchte ich ein bißchen theoretischen Untergrund aufbauen, damit wir demnĂ€chst selbst einen Kopierschutz auf Disketten erzeugen können. GrundsĂ€tzlich einmal: Kopierschutz basiert immer darauf, den Disketten Eigenschaften mitzugeben, die Kopierprogramme nicht erwarten und deswegen nicht richtig ĂŒbertragen können. Das jeweilige Programm fragt dann nach dem Starten auf diese besondere Eigenschaft ab. Flat die Diskette diese herstellerbedingte Macke, ist alles in Ordnung, das Programm hat einen ehrlichen Anwender vor sich. Fehlt der gewisse Tick, verabschiedet sich das Programm je nach Temperament des Programmierers mit einem bloßen ’Un-authorized Copy - Byebye’ oder mit frisch-fröhlichem Formatieren der Diskette, wobei sich proportional zur Reaktion des Programms das Gesicht des ĂŒblen Kopierers verfinstert.

Zur Erinnerung noch einmal die Grobstruktur einer Diskette: 80 Spuren sind jeweils in mehrere Sektoren (normalerweise 9) eingeteilt. Daraus ergibt sich bereits die erste Idee fĂŒr einen Kopierschutz: Im Bootsektor gibt man an, daß auf jeder Spur nur 9 Sektoren sind, in Wirklichkeit aber formatiert man die Spuren auf 10 Sektoren. Ein argloses Kopierprogramm kopiert friedlich 9 Sektoren und vergißt den jeweils zehnten, ohne den das Programm nicht lauffĂ€hig ist.

Allerdings gibt es kaum noch ein Kopierprogramm, das auf einen solch simplen Trick noch hereinfĂ€llt. Eine verfeinerte Version dieses Kopierschutzes arbeitet mit variabler Sektoranzahl, das heißt, mal 5, dann 8, schließlich 10 Sektoren, wild durcheinander. Da muß ein Kopierprogramm schon schlauer sein und erst einmal vor dem Lesen jede Spur genau analysieren. Aber auch das ist bereits Standard.

Eine andere Idee: Wie Sie aus der letzten Folge bereits wissen, kann man die Sektoren nicht nur einfach hintereinander auf die Spur formatieren, sondern auch versetzt (Interleave), zum Beispiel so:

S(ektor) 1 S3 S5 S7 S9 S2 S4 S6 S8

Nehmen wir an, wir wollen Sektor 1 bis 9 lesen. Das funktioniert auch ganz prÀchtig. Ein unachtsames Kopierprogramm schreibt aber die Sektoren anders auf die Zieldiskette:

S1 S2 S3 S4 S5 S6 S7 S8 S9

Diesen Unterschied kann ein Programm dadurch herausfinden, indem es die Zeit mißt, die beim Lesen zweier aufeinanderfolgender Sektoren verstreicht - die Ă€ndert sich nĂ€mlich, wenn man am Interleave manipuliert. Nachteil dieses Kopierschutzes: Er kann auch mal ansprechen, wenn ein Laufwerk benutzt wird, das die Drehzahl nicht richtig einhĂ€lt - und so auch ehrlichen Anwendern schaden.

Am gewalttÀtigsten aber sind Kopierschutzmechanismen, die an der Struktur der Sektoren selbst drehen. Zur Erinnerung - ein Sektor sieht beim ST so aus:

Vorspann (Index, Trackheader; "PoST Index Mark”)

(*) LĂŒckenbytes (Pre Adress Mark) Synchronisationsbytes Adressmarke + Sektorinfo Checksumme

LĂŒckenbytes (PoST Adress Mark) Synchronisationsbytes Datenmarke + DATEN Checksumme

LĂŒckenbytes (PoST Data Mark)

zurĂŒck zu (*), bis alle Sektoren geschrieben sind, danach: LĂŒckenbytes bis zum Trackende.

Als Kopierschutz könnte man jetzt die Anzahl der Synchronisations- oder LĂŒckenbytes Ă€ndern. Kopien erkennt man wieder z. B. durch Zeitunterschiede (das ist allerdings sehr knifflig). Mein Formatierprogramm HYPERFORMAT, das Sie aus der letzten ST kennen, ist - wenn Sie so wollen -auch ein Kopierschutz, weil es Ă€hnliches tut, um 11 Sektoren auf einer Spur unterzubringen. Ich kenne nur wenige Kopierprogramme, die HY-PERFORMATierte Disketten korrekt lesen können (das normale Filecopy des Desktop funktioniert dagegen hervorragend!). Deswegen sei an dieser Stelle auch ein Kopierprogramm fĂŒr dieses Format angekĂŒndigt.

Ganz hartnĂ€ckig ist ein Schutz, wenn er im Controller Fehler erzeugt: Sei es, indem man die Checksummen im Sektor manipuliert, sei es durch sogenannte halbe Sektoren, das heißt, man kĂŒndigt dem Controller zwar einen Sektor mit 512 Bytes an, liefert aber nur 200 oder 300. Normalerweise meldet sich der Controller dann mit einem Lesefehler und streikt - und nur sehr gute Kopierprogramme sind dann so schlau und lassen das Laufwerk solange rotieren, bis sie jedes Byte rekonstruiert haben. Hier findet man eigentlich die grĂ¶ĂŸte Vielfalt von Ideen: Man könnte auch Sektoren mit jeweils 1024 Bytes einfĂŒhren, die zwar der Controller lesen kann, aber nur mit speziellen Leseroutinen. Oder man vergißt Datenmarken, das sind Stopschilder fĂŒr den Controller, an denen er innehĂ€lt und weiß: Hoppla, jetzt kommen Daten.

So, jetzt habe ich Ihnen aber den Mund lange genug wĂ€ĂŸrig gemacht. Diese Folge hat Ihnen gezeigt, wie Sie mit Directory und Bootsektor umspringen können. Sie haben jetzt ein Diskcheck-Programm, einen kleinen Diskmonitor in GfA-Basic, den wir schrittweise erweitern werden, sind jetzt vielleicht etwas aufmerksamer, was TOS-Fehler angeht und wissen -zumindest vom Prinzip her - wie ein Kopierschutz funktioniert. In der nĂ€chsten St werden wir uns daran machen, selbst einen Kopierschutz zu entwerfen. Wenn Sie ganz tief einsteigen wollen, dann halten Sie bis dahin Assembler-Kenntnisse bereit. Aber keine Angst: FĂŒr die Nur-Basic-Programmierer gibt es eine Einbindung der benötigten Routinen in BASIC. Keep hacking!

In den Untergrund

Tiefer in das Reich des Lesekopf können Sie mit dem MINIMON tauchen. Er ist in GfA-Basic verfaßt und besteht aus zwei Teilen, dem Sektormonitor und dem Trackmonitor. Der Trackmonitor ist noch nicht implementiert. Wir werden ihn im Laufe der Zeit vervollstĂ€ndigen, wenn wir im Kurs neue Tricks besprechen. Nach dem Start hat man die Auswahl zwischen Sektormonitor (S), Trackmonitor (T) und Quit (Q). Der Sektormonitor bietet folgende Funktionen:

Track einstellen: (’T’)
Eingabe der Spurnummer (bei mehr als 80 wird’s gefĂ€hrlich fĂŒr den Lesekopf Ihrer Floppy).

Seite einstellen: (’S’)
Vorder- oder RĂŒckseite (0/1). Jede andere Nummer ist sinnlos, die 1 ohnehin nur fĂŒr Besitzer doppelseitiger Laufwerke brauchbar.

Sektor einstellen: (’K’)
Sektornummer eingeben. Sinnvoll sind bei normalen Format 1-9, bei FAT-DISK und Àhnlichen Formaten 1 -10, bei HYPERFORMAT 1-11.

Read sector: (’R’)
Den vorher mit ’T’, ’S’, ’K’ eingestellten Sektor in einen Puffer im Speicher einiesen.

Write sector: (’W’)
Den Pufferinhalt an die mit ’T’, ’S’ und ’K’ eingestellte Stelle schreiben.

Dump sector: (’D’)
Den Pufferinhalt ausgeben. Eine Zeile sieht so aus: [Bytenr] [16 Bytes] [ASCII]. Mit SPACE kann man die Ausgabe anhalten, mit irgendeiner anderen Taste abbrechen.

Edit: (’E')
Ein Byte im Puffer Ă€ndern. MINI-MON fragt zunĂ€chst nach der Nummer des Bytes (mit DUMP zu erfahren), dann nach dem neuen Inhalt des Bytes. Sie können den neuen Wert als Zahl eingeben oder (in ’ - Zeichen eingeschlossen) als ASCII-Zeichen.

Printer: (’P’)
Schaltet Druckerausgabe ein/aus.

Execute: (’X’)
Anderes Programm ausfĂŒhren. Zeigt das Verzeichnis an und erlaubt, ein anderes Programm zu starten, von dem aus man wieder zum Minimon zurĂŒckkehren kann. Diese Funktion können Sie auch benutzen, um nur ein Verzeichnis auf den Schirm zu bringen. Sollten Sie am Directorysektor etwas geĂ€ndert haben, empfiehlt es sich, vor ’X’ die Diskette kurz herauszuziehen und wieder einzusetzen.

Quit: (’Q')
Raus aus dem Sektormonitor.

Bei Lese- und SchreibvorgÀngen ermittelt MINIMON den Status des Laufwerks und zeigt ihn in der Statuszeile an.

Zum Programm selbst:

Am interessantesten' ist wohl der Betriebssystemaufruf in der Prozedur Lesen:

Status% = Xbios(8,L:Buffer%,L:0, Drive%, Sector%, Track%,Seite%,l)

Hier wird das XBIOS des ST aufgerufen und ihm verschiedene Parameter ĂŒbergeben. ’8’ ist die Nummer der Leseroutine im XBIOS. ’L:Buffer%’ gibt die Adresse eines Puffers im Speicher an, wohin XBIOS den Sektorinhalt einiesen soll. ’L:’ ist GfA-spezifisch und erzwingt die ParameterĂŒbergabe als Langwort. Fehlt ’L:’, nimmt GfA-BASIC an, daß der Parameter WortgrĂ¶ĂŸe haben soll. Nach Buffer% folgt ein (noch) vom Betriebssystem unbenutztes Langwort, sodann die Angabe des Laufwerks (hier voreingestellt mit 0 fĂŒr Laufwerk A; Laufwerk B ist dann 1, C entspricht 2 usw.). Es folgen Sektor, Track, Seite und eine ominöse 1: Es soll nur ein Sektor eingelesen werden. Der Systemaufruf in der Prozedur Schreiben ist bis auf die Funktionsnummer identisch.

Wichtig: In der ST lief ja bereits eine Serie ĂŒber das Betriebssystem, wo alle Parameter bei den Aufrufen angegeben waren. FĂŒr GfA-Basic ist anzumerken, daß alle Parameter hier (wie in C) in umgekehrter Reihenfolge hinzuschreiben sind; in Assembler steht die Funktionsnummer als letztes, in Basic als erstes.

*****************
* CHKDISK
* Testet eine Diskette in Laufwerk A
* (auch HYPERFORMATiert)
* auf fehlerhafte Sektoren
*
* Written 1987 by Claus Brod
* Version 1.0, last update 23.5.1987
*
****************

start:
move.l a7,a5	* Speicherplatz	reservieren
move.l 4(a5),a5
move.l $c(a5),d0
add.l $14(a5),d0
add.l Slc(a5),d0
add.l #$500,d0	* stack
move.l d0,d1 add.l a5,d1
and.l #-2,d1	* gerade machen
move.l d1,a7
move.l d0,-(sp)
move.l a5,-(sp)
clr.w -(sp)
move.w #$4a,-(sp)
trap #1
add.l #12,sp

******************
* Hauptschleife
******************

lea header(pc),a2	* BegrĂŒssung
bsr printmsg	* ausgeben
bsr parameter	■	Parametereingabe
bsr main	* Ab	ins Hauptprogramm

exitus:	* Sense
clr.w -(sp)
trap #1

*******************
* PrĂŒfschleife 
*******************


main:
move.w #0,a4	* Aktueller Track
move.w #1,a5	* Aktueller Sektor
move.w #0,d6	* Aktuelle Seite

*****************
* Sektor lesen 
*****************

readsec:
move.w #1,-(sp)	* nur 1 Sektor lesen
move.w	d6,-(sp)	* Seite
move.w	a4,-(sp)	* Track
move.w	a5,-(sp)	* Sektor
move.w #0,-(sp)	* Laufwerk A
clr.l -(sp)
move.l #buffer,-(sp)	* Pufferadresse
move.w	#8,-(sp)	* Sektor lesen
trap #14	* XBIOS
add.l #20,sp
tst.b d0	* Fehler passiert?
bne fehler	* ja, zur Fehlerroutine
next:
addq.w #1,a5	* NĂ€chster Sektor
cmp.w spt(pc),a5	* letzter Sektor?
bls readsec
move.w #1,a5	* Sektor 1

lea sides(pc),a2
cmp.b #1,(a2)	* 1 oder 2 Seiten
beq nexttrack	* nur eine Seite
sor.w #1,d6	* Seite wechseln
bne readsec	* schon auf 0 gewechselt? nein, lesen
nexttrack:
bsr printtrack	* Tracknummer ausgeben
addq.w #1,a4	* nÀchster Track
cmp.w end(pc),a4	* schon alle Tracks?
bne readsec	* nein, lesen
rts

*****************
* Fehlerroutine 
*****************

fehler:
lea s_error(pc),a2	* Fehlernummer ausgeben
bsr mkhex
lea m_error(pc),a2	* Fehlerstring ausgeben
bsr printmsg

move.w a4,d0	* Track ausgeben
lea s_track(pc),a2	* String fĂŒr Track
bsr mkhex	* Hexzahl schreiben
lea m_track(pc),a2 
bsr printmsg

move.w a5,d0	* Sektor ausgeben
lea s_sec(pc),a2
bsr mkhex
lea m_sec(pc),a2
bsr printmsg

move.w d6,d0	* Seite ausgeben
lea s_side(pc),a2
bsr mkhex
lea m_side(pc),a2
bsr printmsg

bra next	* nÀchster Sektor

******************
* String ausgeben
******************

printmsg:	* String ausgeben (in: A2)
move.l a2,-(sp)	* Adresse des Strings
move.w #9,-(sp)
trap #1
addq.l #6,sp
rts

******************
* Track ausgeben
******************

printtrack:			* Tracknummer ausgeben
move.l a4,d0
lea s_akt(pc),a2
bsr mkhex
lea m_akt(pc),a2
bsr printmsg
rts

*****************
* D0 nach hexadezimal wandeln
*****************

mkhex:	* Hexzahl erzeugen (in:DO)
move.l #1,d1	* Nur ein Byte

nochnbyte:
clr.l d2 
move.l d0,d2
asr.l #4,d0		* Ein Nibble weiterschieben
and.l #$0f,d2	* ausmaskieren
cmp.b #9,d2		* Zahl oder Buchstabe?
bis zahl
add.b #7,d2		* 7 addieren (Buchstabe)
zahl:
add.b #48,d2	* 48 addieren
move.b d2,0(a2,d1)	* Ergebnis schreiben
dbf d1,nochnbyte	* weiteres Byte wandeln?
rts

*******************
* auf Taste warten
*******************

getkey:	* auf Taste warten (con in
move.w #7,-(sp)	* without echo)
trap #1
addq.l #2,sp
rts

*********************
* Parameter-MenĂŒ ausgeben und 
* auf Eingaben warten
*********************

parameter:	* auf Eingabe von neuen Parametern
lea m_end(pc),a2	* warten
bsr printmsg

bsr getkey	* auf Taste warten

cmp.b #3,d0		* C?
beq exitus		* ja, raus
cmp.b #13,d0	* RETURN?
beq fertig		* ja, mit Check anfangen
cmp.b #'s',d0	* Seitenanzahl
beq op_sides	* Àndern
cmp.b #'S',d0
beq op_sides
cmp.b #'t',d0	* Trackanzahl
beq op_tracks	* Àndern
cmp.b #'T',d0 
beq op_tracks
cmp.b #'p',d0	* Sektorenanzahl
beq op_spt		* Àndern
cmp.b #'P',d0
beq op_spt
bra parameter
fertig:
rts

*******************
* Seitenzahl Àndern
*******************

op_sides:	* Anzahl der Seiten Àndern
lea sides(pc),a2
lea s_seiten(pc),a3
eor.b #3,(a2)
eor.b #3,1(a3)
bra parameter

*******************
* Trackzahl Àndern
*******************

op_tracks:		* Anzahl der Tracks Àndern
lea end(pc),a2 
lea s_end(pc),a3 
move.b 1(a2),d0 
move.b 1(a3),d1 
addq.b #1,d1 
addq.b #1,d0
cmp.b #85,d0	* mehr als 85 sind nicht
bis trackok	* drin
move.b #80,d0	* Startwert
move.b #'0',d1
trackok:
move.b d0,1(a2) 
move.b d1,1(a3) 
bra parameter

********************
* Sektoren pro Track Àndern
********************

op_spt:	* Anzahl der Sektoren pro
lea spt(pc),a3	* Track Àndern
lea s_spt(pc),a2
move.b 1(a3),d0
addq.b #1,d0
cmp.b #11,d0
bis sptok
move.b #1,d0
sptok:
move.b d0,1(a3) 
bsr mkhex 
bra parameter

.even

* Fehlermeldung

m_error:
.dc.b 7,'Fehler ' 
s_error:
.dc.b '00',13,10,0 
m_track:
.dc.b ' bei Track: '
s_track:
.dc.b '00',13,10,0 
m_sec:
.dc.b ' Sektor:'
s_sec:
.dc.b '00',13,10,0 
m_side:
.dc.b ' Seite: ’
s_side:
.dc.b '00',13,10,13,10,0 
.even

* BegrĂŒĂŸung 

header:
.dc.b 27,’E',27,'pDISKCHECK’,27,'q',13,10 
.dc.b 'Written 1987 by Claus Brod'
.dc.b 13,10,0

m_end:
.dc.b 27,'Y',32+5,32+0,27,'pT',27,'qracks: '
s_end:
.dc.b '83',13,10 
m_seiten:
.dc.b 27,'pS',27,'qeiten: ' 
s_selten: 
dc.b '02',13,10 
m_spt:
.dc.b 'Sektoren ',27,'pp',27,’qro Track: $' 
s_spt:
.dc.b '0B',13,10
.dc.b 13,10,'RETURN Check starten',13,10 
.dc.b 'CTRL-C Abbrechen',13,10,0

m_akt:
.dc.b 27,'j',27,’Y’,32+0,32+65,'Track: ' 
s_akt:
.dc.b '00',27,'k',0 

end:
.dc.w 83 
spt:
.dc.w 11 
sides:
.dc.b 2

.bss
buffer:			* Sektorpuffer
.ds.b 512

Listing 1: Source Code

' Minimon fĂŒr den ST 
' Written 1987 by Claus Brod
' Am Felsenkeller 2 
' 8772 Marktheidenfeld
'
Cls
Do
	Print
	Print "Minimon ST - (C) 1987 by Claus Brod"
	Print
	Repeat
		Print "Sektormonitor oder Trackmonitor oder Quit (S/T/Q)?" 
		A$=Upper$(Input$(1))
	Until A$="S" Or A$="T" Or A$="Q"
	Exit If A$ = "Q"
	If A$ = "S"
		@Sektormon
	Else
		@Trackmon
	Endif
Loop
End
'
'
Procedure Gibmirzeit
	Alert 1,"Funktion noch nicht|implementiert.",l,"OK",A 
Return
'
' Das Modul Trackmon wird im Laufe des Kurses noch 
' vervollstÀndigt
'
Procedure Trackmon 
	@Gibmirzeit 
Return
'
' Sektormon: Kleiner Diskmon mit StandardfÀhigkeiten
'
Procedure Sektormon 
	Cls
	Prn%=0
	Status%=0
	Seite%=0
	Track%=0
	Sector%=1
	Drive%=0
	Sec$=Space$(5i2)
	R$=Chr$(27) + "p"
	O$=Chr$(27) + "q"
	@Lesen
	Do
		Print
		Print "....... Minimon (C) 1987 Claus Brod Status: ";Status%;........"
		Print R$;"R";OS:"ead, ";R$;"W";OS;"rite, ";R$;"T";O$;"rk (";Track%;"), ";
		Print R$;"S";0$;"eite (";Seite%;"), Se";R$;"k";OS;"tor (";Sector%;"), ";
		Print R$;"E";0$;"dit, ";R$;"D";O$;"ump, ";R$:"P";0$;"rn (o";
		If Prn%
			Print "n), ";
		Else
			Print "ff), ";
		Endif
		Print "E";RS;"x";O$;"ec, ";R$;"Q";O$;"uit”
		M$ = Upper$(Input$(1))
		If M$ = "X"
			@Exe 
		Endif 
		If M$="P"
			Prn%=1-Prn%
		Endif 
		If M$ = "T"
			Input "Track";Track%
		Endif 
		If M$ = "S"
			Input "Seite";Seite%
		Endif 
		If M$ = "K"
			Input "Sektor";Sector%
		Endif 
		If M$ = "R"
			@Lesen 
		Endif 
		If M$ = "W"
			@Schreiben
		Endif
		If M$ = "E" 
			@Edit 
		Endif 
		If M$ = "D"
			@Dump
		Endif
		Exit If M$ = "Q"
	Loop
Return
'
' Sektor einlesen
'
Procedure Lesen
	Buffer%=Varptr(Sec$)
	Status%=Xbios(8,L:Buffer%,L:0,Drive%,Sector%,Track%,Seite%,1)
Return
'
'
' Sektor schreiben
'
Procedure Schreiben 
	Buffer%=Varptr(Sec$)
	Status%=Xbios(9,L:Buffer%,L:0,Drive%,Sector%,Track%.Seite%,1)
Return
'
' Sektorpuffer ausgeben
'
Procedure Dump 
	If Prn%=0
		Open "O",#1,"con:"
	Else
		Open "O",#1,"prn:"
	Endif
	Print #1,"Track: ";Track%;" Sektor: ";Sector%;" Seite: ";Seite%
	For T=1 To 512 Step 16
		A$=Str$(T-1)
		While Len(A$)<3 
			A$="0"+A$
		Wend 
		D$ = "   "
		Print #1,A$;D$;
		For I=0 To 15
			V$=Mid$(Sec$,T+1,1)
			A$ = Hex$(Asc(V$))
			If V$<" " Or V$>"z"
				V$="."
			Endif
			Print #1,Right$("0"+A$,2)'
			DS=D$+V$
		Next I 
		Print #1,D$
		A$=Inkey$
		If A$ = " "
			A=Inp(2)
		Endif
		Exit If A$>"" And A$<>" "
	Next T 
	Close #1 
Return
'
' Byte Àndern
'
Procedure Edit
	Input "Byte Nr.";Byte%
	Print "Alter Wert ist: ";Asc(Mid$(Sec$,Byte%+1,1)) 
	Input "Neuer Wert in dezimal oder ASCII";Wert$ 
	Wert%=Val(Wert$)
	If Wert%=0 And Left$(Wert$,1)<>"0" 
		Wert%=Asc(Wert$)
	Endif
	Mid$(Sec$,Byte%+1,1)=Chr$(Wert%)
Return
'
'
' anderes Programm ausfĂŒhren
'
Procedure Exe
	Fileselect "*.*","",Sel$
	If Exist(Sel$)<>0 
		S=Fre(0)
		Reserve 50000 
		Showm
		Exec 0,Sel$,"",""
		Reserve S-1000 
	Endif 
Return

Listing 2: Diskmonitor in GFA-Basic

Claus Brod