Generationswechsel: Objektorientiertes Programmieren unter GNU-C++

Mit der Einführung des Atari ST im Jahre 1985 kam es auch zu einem Generationswechsel der Programmiersprachen. Basic und Assembler, auf Rechnern wie C64 oder Atari XE die Entwicklungssprachen schlechthin, erhielten Konkurrenz von C Heute macht C sich selbst Konkurrenz mit C++.

Acht Jahre sind seit der Einführung des ersten ST vergangen, letzter Streich ist der Falcon030. Auch im Softwarebereich macht ein neues Schlagwort die Runde: »objektorientierte Programmierung«. Während auf dem PC- oder Apple-Sektor bereits zahlreiche kommerzielle OOP-Entwicklungssysteme auf dem Markt sind, wurde dieses Thema im Atari-Bereich recht stiefmütterlich behandelt. Einen Einblick in die objektorientierte Programmierung können Sie mit Pure Pascal von Application Systems erhalten [1]. Doch als »die« OOP-Sprache schlechthin mit wesentlich weitreichenderen Möglichkeiten gilt C++. Im Verlaufe dieses Kurses lernen Sie am Beispiel der als Public-Domain-Software verfügbaren Programmiersprache GNU-C++ die objektorientierte Programmierung kennen. Hierbei setzen wir lediglich C-Grundkenntnissen voraus.

C++ wurde von Bjarne Stroustrup entwickelt und umfaßt neben den objektorientierten Erweiterungen auch Verbesserungen der konventionellen C-Sprachelemente. Größter Vorteil der unter dem Dach von AT&T entstandenen Sprache ist die Vereinfachung der Programmierung anhand objektorientierter Verfahren. Der Zentralgedanke hierbei ist die Kombination von Daten und den dazugehörigen Funktionen in Objekte, die somit kompakte und übersichtliche Einheiten bilden. Ähnlich wie in einem Familienstammbaum können aus Objekten Nachfahren erzeugt werden, die die Eigenschaften ihrer Vorfahren erben und neue erhalten.

Eine objektorientierte GEM-Bibliothek würde beispielsweise Objekte für Fenster enthalten, die Attribute wie Position, Ausmaße und zugehörige Funktionen zum Darstellen des Fensterinhaltes oder zur Reaktion auf GEM-Mitteilungen umfassen würden. Bei der Verwendung dieser Bibliothek im eigenen Programm, würden wir einen Nachfahren dieses Fensters herleiten und um die gewünschten Eigenschaften erweitern. Beispielsweise um spezielle Darstellungsroutinen oder einen Speicherbereich für den Editortext. Typisch für C++ ist auch das dynamische Konzept: In vielen Fällen sind Objekte nicht statisch, sondern in dynamischen, separat vom Betriebssystem angeforderten Speicherblöcken abgelegt. Für jedes vom Anwender neu geöffnete GEM-Fenster erzeugt das Programm einfach ein dynamisches Fensterobjekt. Schier unendliche Möglichkeiten bieten sich dem Entwickler durch das sogenannte Überladen von Operatoren. So läßt sich etwa Vektorarithmetik tatsächlich in herkömmlicher Schreibweise realisieren.

vektor3=vektor1+vektor2;

Dies als kleiner Überblick über die Möglichkeiten, die wir im Verlaufe des Kurses genauer kennenlernen werden. Doch bevor Sie Ihr erstes C++-Programm schreiben können, müssen Sie das GNU-C-Paket erstehen und installieren. Hardwarevoraussetzung ist ein Atari mit mindestens 2 bis 4 MByte RAM und 6 MByte verfügbarer Festplattenkapazität. Im Textkasten finden Sie eine Bezugsquelle für die aktuelle GNU-Version. Diese sind zwar auch in einigen PD-Sammlungen vorzufinden, dann aber zumeist stark veraltet. Für unsere Zwecke reicht das »einfache« GNU-Paket völlig aus, das ANSI-C-/C++-Compiler, Debugger, Profiler und weitere Tools umfaßt. MiNT- bzw. Multi-TOS-Entwickler sollten das zweite Paket mit den passenden Bibliotheken anfordern.

Wie der Name »GNU« mit der Bedeutung »GNU is not UNIX« schon vermuten läßt, ist die Installation des Paketes ähnlich unkomfortabel wie bei so macher UNIX-Software. Der Grund hierfür liegt darin, daß GNU nicht eine integrierte Entwicklungsumgebung wie Pure C bietet, sondern aus zahlreichen im Textmodus arbeitenden Einzelprogrammem besteht. Dafür gibt es zahlreiche andere GNU-Adaptionen auf Plattformen wie DOS oder UNIX, so daß eine Portierung nicht Hardware-spezifischer Anwendungen mit wenig Aufwand machbar ist.

Environment-Variablen

setenv GCC_EXEC_PREFIX d:\ gnu\ bin\ gcc- 
setenv GNULIB d:\ gnu\ lib 
setenv GNUINC d:\ gnu\ include 
setenv GXXINC d:\ gnu\ inc++ 
setenv TMPDIR d:\ gnu\ temp 
setenv UNIXMODE .,/d 
setenv PATH d:\ gnu\ bin

Tabelle 2. Gute Umgebung gewährt gute Aussicht mit GNU-C

Die Arbeit mit GNU erfolgt deshalb über einen Kommandozeilen-Interpreter mit Environment-Variablen-Verwaltung wie Mupfel (Gemini) oder MiNT-Shell. Weiterhin benötigen Sie einen ASClI-Editor wie beispielsweise MicroEmacs (PD). Sind diese Programme eingerichtet, so kopieren Sie die auf den GNU-Disketten enthaltenen Archive auf Ihre Festplatte. Hierzu kann leider aufgrund der sich ständig ändernden Zusammenstellung der Distributionsdisketten nur eine grobe Anleitung gegeben werden:

Legen Sie zunächst ein Stammverzeichnis namens »GNU« an, in dem Sie wiederum die Verzeichnisse »INCLUDE« für die C-Include-Dateien, »INC++« für die C++-Include-Dateien, »LIB« für die Bibliotheken und »BIN« für die ausführbaren Dateien erzeugen. Die Archive entpacken Sie jeweils wie folgt: Kopieren Sie das Archiv »XXX.ZOO« und den Entpacker »ZOO.TTP« in das Zielverzeichnis und entpacken Sie die Dateien mit »zoo xSO XXX.ZOO«. Archiv und Entpacker sollten Sie anschließend wieder aus dem Verzeichnis löschen. Tabelle 1 zeigt Ihnen, welche Archive der aktuellen GNU-Disktributiori in welches Verzeichnis gehören. Auf den Disketten ist übrigens auch die Dokumentation als druckbereite TeX-DVI-Datei im Archiv »GCCDVI.ZOO« enthalten.

Nach dem Entpacken der Dateien müssen Sie dafür sorgen, daß verschiedene Environment-Variablen in Ihrem Kommandozeilen-Interpreter stets korrekt gesetzt sind. Dieser verfügt dazu normalerweise über eine ASCII-Skriptdatei, die beim Starten gelesen und Zeile für Zeile ausgeführt wird. Ändern Sie diese Datei wie beschrieben ab, so daß alle Environment-Variablen per »setenv <Variable> <Wert>«-Kommando korrekt gesetzt werden.

Mit der Variablen »GNULIB« zeigen Sie dem GNU-Paket den Zugriffspfad auf die Bibliotheken, mit »GNUINC« auf die ANSI-C-Include-Dateien, mit »GXXINC« auf die C++-Include-Dateien und mit »TMPDIR« legen Sie ein Verzeichnis für temporäre Dateien fest. »GCC_EXEC_PREFIX« gibt den ersten Teil aller Compilerpfadnamen an, »UNIXMODE« (bitte auf ».,/d« setzen) klärt näheres über die Interpretation von UNIX-Pfadnamen unter GEMDOS/MultiTOS (siehe [2]). Setzen Sie die Environment-Variablen einfach wie in Tabelle 2 angegeben, wobei Sie bitte ggf. die Zugriffspfade anpassen. Starten Sie anschließend

Ihren Kommandozeilen-Interpreter neu - geschafft! Zum Testen des GNU-C++-Compilers bietet sich eine Adaption des klassischen »Hello world«-Pro-grammes an, das Sie bitte in den Editor eingeben:

include <iostream.h> 
int main()
{
    cout « "Hello world!" « end1;
}

Speichern Sie diesen etwas seltsam erscheinenden Quelltext unter dem Namen »HELLO.CC« ab. An der Extension ».CC« erkennt der GNU-Compiler »GCC«, daß sich um ein C++-Programm handelt. Verlassen Sie den Editor und compilieren Sie den Quelltext durch Eingabe von »gcc hello.ee -lg++ -ohello.tos«. Dies nimmt einige Zeit in Anspruch, da GNU zunächst compiliert,den erzeugten Assembler-Quelltext assembliert und anschließend mit den Standard-Bibliotheken linkt, wobei die Zwischenergebnisse jeweils in Dateien abgelegt werden. Als Resultat liegt nun die ausführbare Datei »hello.tos« vor, deren Ausführung keine großen Überraschungen offenbart.

Eine Eigenheit von GCC sollte an dieser Stelle jedoch angegeben werden: Mitunter hat GCC Probleme bei der Verwendung von deutschen Umlauten bei merkwürdigen Fehlermeldungen sollten Sie Ihren Quelltext nach Umlauten durchsuchen und diese entfernen bzw. durch entsprechende ASCII-Sequenzen ersetzen.

Verzeichnis Archive
BIN GCC233BA.Z00
UTLBIN33.ZOO
MAKE360.ZOO
GPROF08B.ZOO
GDB_18B.ZOO GCC233BB.ZOO
INCLUDE INCLUD86.ZOO
INC++ G++INC16.ZOO
LIB GEMOLB16.ZOO
CRSOLB16.ZOO
CRSDOC16.ZOO
LIBOLB86.ZOOPMLOLB16.ZOO
G++20o16.ZOO
G++OLB16.ZOO

Tabelle 1. Verteilung der Archive

GCC erkennt automatisch anhand der Dateinamenserweiterung, welche Aktionen durchzuführen sind. ».CC«- oder ».C«-Dateien werden compiliert, assemblies und gelinkt, ».S«-Assemblerdateien assemblies und gelinkt, Objektdateien ».O« nur gelinkt. Die weitere Steuerung des Compilers erfolgt über nach dem Dateinamen anzugebende Compilerschalter, deren wichtigste in Tabelle 3 aufgelistet sind. Normalerweise erzeugt GCC wie unter UNIX eine Ausgabedatei mit dem Namen »A.OLJT«, mit der Option »-o« geben Sie ihr einen anderen Namen. Wie bei unserem Beispiel zu sehen, darf zwischen dem Schalter und dem Namen kein Leerzeichen stehen.

Im Normalfall linkt GCC lediglich die Standard-Bibliotheken zu, welche betriebssystemunabhängige Funktionen umfassen. Für C++-Programme ist per »-lg++« die G++-Bibliothek zuzulinken. Für GEM-Anwendungen ist »-|gem« und für Fließkomma-Anwendungen »-|pml« anzugeben. MiNT und MultiTOS-Entwickler müssen den Schalter »-mint« aktivieren. Die Compilierung kann nach Bedarf eingeschränkt werden, mit »-S« wird nur compiliert, mit »-c« compiliert und gelinkt und mit »-E« startet nur der Präprozessor, der seine Ausgaben auf dem Standard-Ausgabekanal, also dem Bildschirm, tätigt. Informationen über die Aufrufsequenz der Compilerprogramme liefert GCC durch Angabe der Option »-v«.

Die Compiler-Optionen

Schalter Wirkung
-c Programm wird nur compiliert und assembliert, nicht gelinkt
-g Debugging-Information wird erzeugt
-Kname> Die angegebene Bibliothek wird zum Programm gelinkt (bei C++-Programmen: stets »-lg++< verwenden).
-mint Statt GNULIB wird MINTLIB verwendet
-o<name> Ausgabedatei erhält statt »A.OUT« den angegebenen Namen
-pg Profiling wird eingeschaltet
-v Es werden lediglich die GCC-Aufrufe auf dem Bildschirm ausgegeben, keine Compilierung
-D<Name> Entspricht define <Name> 1« im Programmtext
-D<Name> ~<Text> Entspricht define <Name> <Text> im Programmtext
-E Nur der Präprozessor wird aufgerufen, Ausgaben erfolgen auf Bildschirm
-G Symbole in der ausführbaren Datei werden im GST-Format abgelegt und können somit länger als 8 Zeichen sein
-O Einfache Optimierung
-O2 Maximale Optimierung
-S Programm wird nur compiliert, nicht assembliert und gelinkt

Tabelle 3. Schalter des GCC-Programmes (Groß-/Kleinschreibung beachten)

Werfen wir nun einen Blick auf die Anwendung der im GNU-Paket enthaltenen Werkzeuge »Debugger« und »Profiler«. Letzteren wenden Sie an, um nähere Erkenntnisse über das Laufzeitverhalten Ihres Programmes zu erhalten, wozu der Profiler eine selbsterklärende ASCII-Datei mit Informationen über Verweildauern in den verschiedenen Programmteilen erzeugt.

Zum Anwenden des Profilers compilieren wir zunächst den Quelltext unter Anwendung der Schalter »-pg« (Profiling einschalten) und »-G« (lange Symbolnamen) und starten anschließend das erzeugte Programm. Dieses erzeugt nun während seiner Ausführung die Datei »GMON.OUT«, die mit dem Profiler in ein lesbares Format umzuwandeln ist. Dazu rufen wir den Profiler mit »gprof <Optionen> <Programmdatei> <Profiledatei>« (z.B. »gprof hello.tos gmon.out«) auf, worauf dieser die Profiling-Information über die Standard-Ausgabe, also normalerweise den Bildschirm, ausgibt. Geben Sie keine Programm-bzw. Profiledatei an, so verwendet der Profiler die Dateien »A.OUT« und »GMON.OUT«. Mit dem Kürzel "> DATEI.TXT" können Sie bei den gängigen Kommandozeilen-Interpretern die Informationen in die Datei »DATEI.TXT« umleiten.

Zum »Entwanzen« von Programmen verwenden wir den Debugger »GDB«, dessen Verwendung stets etwas Vorarbeit voraussetzt, die aber mit geeigneten Shell-Skripten automatisiert werden kann. Zunächst übersetzten wir das Programm mit »gcc -g -D__NO_INLINE__-c hello.cc« in eine Objektdatei mit Debugging-Informationen, wobei »NO__INLINE« als Makro deklariert ist, damit Betriebssystemaufrufe nicht per integriertem Programmcode, sondern überzwischengeschaltete Funktionen aufgerufen wird. Dies ist eine wichtige Voraussetzung für das korrekte Arbeiten des Debuggers.

Nun ist die Objektdatei zweimal zu linken. Zunächst erzeugen wir die ausführbare Datei mit dem Aufruf »gcc hello.o -ohello.tos«. Die Symboltabelle »HEL-LO.SYM« für den Debugger erzeugt die Zeile »gcc hello.o -ohello.sym -Bd:\gnu\bin\sym-«. Hinter der Option »-B« ist der komplette Pfad des Utilities »sym-Id.ttp« gefolgt von »sym-« anzugeben. Anschließend starten wir den Debugger mit »gdb -shello.sym -ehello.tos«.

Zur Ausführung und zur Fehlersuche stehen nun zahlreiche, in Tabelle 4 aufgeführte Befehle zur Verfügung, die Sie in der Kommandozeile eingeben. Mit »help« erhalten Sie stets eine Übersicht der Funktionen. »run« startet die Programmausführung, mit »next« führen Sie die angegebene Zahl einzelner Befehle aus, wobei nicht in Funktionen hi nein verzweigt wird. Dies erreichen Sie mit dem ansonsten identischen Befehl »Step«. Sollen alle restlichen Befehle einer so angesprungenen Funktionen en bloc ausgeführt und nach dem Verlassen die Programmausführung unterbrochen werden, so geben Sie »finish« ein. Mit »until« durchlaufen Sie eine Programmschleife komplett.

GDB unterstützt die Verwendung von Breakpoints, bei deren Erreichen die Programmausführung angehalten wird, »cont« beendet wiederum diese Zwangspause. Zum Setzen eines Breakpoints geben Sie »break«, gefolgt von Zeilennummer oder Funktionsnamen ein.

Ist Ihr Programm in mehrere Quelltexte aufgeteilt, so geben Sie zuvor den zugehörigen Dateinamen gefolgt von einem Doppelpunkt ein. Mit »break hello.cc:12« setzen Sie einen Breakpoint in Zeile 12 der Datei »hello.cc«.

»break main« setzt einen Breakpoint an den Anfang Funktion »main«. GDB liefert die Nummer des gesetzten Breakpoints zurück, den Sie mit »clear <nr>« wieder entfernen. Mit »clear <adr>« löschen Sie alle Breakpoints nach der Position <adr>, »info breakpoints« liefert eine Übersicht.

Breakpunkte dürfen auch konditional sein, sind also nur beim Zutreffen einer von Ihnen bestimmten Bedingung aktiv. Dazu hängen Sie an den Befehl »break« noch das Schlüsselwort »if« an. Einfache Breakpunkte können mit dem »condition«-Befehl in konditionale umgewandelt werden. Mit »ignore <nr,n>« legen Sie fest, daß erst beim n-ten Durchlauf am Breakpunkt »nr« angehalten wird. Beim Erreichen eines Breakpunktes können Sie auch eine Befehlssequenz ausführen lassen, wozu Sie zunächst »commands <nr>«, die auszuführenden Befehle und letztlich »end« eingeben.

Sehr wichtig beim Austesten von Programmen sind Befehle zur Anzeige verschiedener Informationen. Den Programmtext können Sie mit »list« anzeigen lassen, wobei der Debugger den Quelltext ab der aktuellen oder auch der wie bei »break« angegebenen Position anzeigt. Mit »backtrace« erhalten Sie Informationen über die aktuelle Funktions-Aufrufshierarchie, durch die Sie mit »up« und »down« wandern können. Mit »print <Ausdruck>« bzw. »output <Ausdruck>« können Sie jeden beliebigen C-Ausdruck anzeigen lassen, wobei »print« zusätzlich einen Zeilenvorschub ausführt. Durch Anwendung dieser Befehle in »commands«-Breakpunkten können Sie beim Erreichen bestimmter Programmstellen wichtige Variableninhalte anzeigen lassen, »output wert & 0xff« gibt beispielsweise das niederwertige Byte der Variablen »wert« aus. Das Gegenstück erlaubt das Setzen von Variablen mit »set <Variable> = <Wert>«. Mit »set jahr = 1993« setzen Sie die Variable »jahr« auf 1993.

GDB besitzt auch eine Reihe von Befehlen für Verwaltungsaufgaben wie das Setzen der Kommandozeilenparameter mit »setargs«. Entwickeln Sie beispielsweise ein Programm, das als Parameter zwei Dateinamen erwartet, so setzen Sie diese zum Testen mit »setargs datei1.doc datei2.doc«. Mit »set/unset-environment« verändern Sie das Environment für das zu testende Programm, nicht aber für GDB. Mit »exec/symbol-file« laden Sie eine neue Programm- bzw. Symboldatei. Mit »quit« verlassen Sie GDB.

Damit Sie beim Debuggen nicht nach jedem Start des Debuggers zahlreiche Schritte wie das Setzen von Breakpoints per Hand ausführen müssen, können Sie auch eine Datei namens ».GDBINIT« (auf dem Atari verkürzt zu ».GDB«) mit einer in ASCII-Form abgelegten Befehlssequenz erzeugen, die der Debugger nach Programmstart automatisch ausführt. Mit einem geeigneten Script kann der Debugger einfach mit »GDB« gestartet werden, worauf die betreffenden Dateien geladen und Breakpunkte gesetzt werden:

GDBINIT Ausführbare Datei laden: exec-file HELLO.TOS
Symboldatei laden: symbol-file HELLO.SYM 
Breakpunkt setzen break main

Sie können weitere Kommandodateien schreiben, die Sie bei Bedarf mit »source <Dateiname>« ausführen. Nachdem Sie nun auch das Debugging-Werkzeug kennengelernt haben, wollen wir mit einem ersten Schritt in die C++-Welt die Theorie in die Praxis umsetzen. Schon die Ausgabe von Texten und Werten erfolgt unter C++ - wie in »HELLO.CC« zu sehen - auf völlig neue Weise: Statt der C-Bibliothek »STDIO« kommt »IOSTREAM« zum Einsatz. Zum Ausgeben eines Textes rufen Sie keine Funktion auf, sondern geben zunächst einen Ausgabekanal an, der für die Standard-Ausgabe »cout« heißt. Mit Hilfe des Ausgabe-Operators "«" verknüpfen Sie nun alle auszugebenden Daten mit dem Ausgabekanal:

cout « vorname « “ “ « nachname « endl;

Die Konstante »endl« steht für einen Zeilenvorschub. Der "«"-Operator wird von C++ übrigens per Opera-toren-Überladung erzeugt, was die neuen Möglichkeiten zur übersichtlichen Gestaltung von Programmen belegt. Das Gegenstück ist »ein« mit dem Verknüpfungsoperator "»", der das Lesen vom Standard-Eingabekanal erlaubt:

ein » vorname » nachname;

(ah)

Literaturhinweise:

[1] Frank Mathy: »Tour de Pascal, Teil 5«, TOS 1/93, S. 86ff

[2] Bjarne Pohlers: »Keine Angst vorm GNU«, ST-Magazin 8-11/92

[3] Roman Gerike: »Crash-Kurs in C++«, CT 10-12/91

Bezugsquelle für GNU-C++

Bjarne Pohlers Friedericus-Str. 15 W-4400 Münster

Verfügbare Pakete:

GCC mit C++ (5 Disketten) - DM 30,00
GCC mit C++ und MiNTLIB (6 Disketten) - DM 36,00

Zusätzlich verfügbar:

Quelltexte der Bibliotheken (2 Disketten) - DM 12,00
Quelltexte von GCC (9-10 Disketten) - DM 60,00
MiNT inkl. Quelltexte (2 Disketten) - DM 12,00
Alle obengenannten Disketten - DM 110,00

Versandkosten:

Bei Vorkasse (Bar oder Scheck) - DM 5,00
Bei Nachnahme - DM 8,00

Kursübersicht

Teil 1. Installation und Anwendung des GNU-Paketes □ Streams

Teil 2. Erweiterungen des C-Sprachschatzes in C++ □ Referenzen □ Kapselung von Daten und Funkionen in Objekten □ dynamische Objekte

Teil 3. Konstruktoren und Destruktoren □ Überladen von Operatoren □ Typ-Casting □ Vererbung

Teil 4. Klassenhierarchien □ Schutz gekapselter Daten □ Ergäbe von Zugriffsrechten □ virtuelle Methoden □ Templates

Debugger-Optionen

Befehl Bedeutung
backtrace/bt Funktionsaufrufhierarchie anzeigen, durchblättern mit »up« und »down«
break <adr> break <adr> Breakpoint in Zeilennummer <adr> oder am Anfang der Funktion <adr> setzen
if <condition> Konditionalen Breakpoint mit Abbruchbedingung <condition> setzen
cd <Verzeichnis> Neues aktuelles Verzeichnis setzen
clear <adr> Alle Breakpoints nach der Position <adr> löschen
commands <nr> Nachfolgend bis zum Wart »end« eingegebene Kommandos werden bei Erreichen des Breakpoints <nr> ausgeführt
condition <nr>, <condition> Breakpoint <nr> in konditionalen Breakpoint mit Bedingung <condition> umwandeln
cont Programmausführung fortsetzen
delete <nr> Breakpoint <nr> löschen
exec-file <name> Programmdatei <name> wählen
finish Programm bis nach Verlassen der aktuellen Funktion ausführen
help Hilfe über die GDB-Befehle anzeigen
ignore <nr,n> Erst beim n-ten mal bei Breakpoint <nr> stoppen
info {<typ>} Allgemeine Informationen über lokale Variablen (»info locals«), Breakpoints (»info breakpoints«) etc. abrufen
list {<adr>} Programmtext ab angegebener bzw. aktueller Position auflisten
next {<n>} Nächste <n> Befehle ausführen, nicht in Funktionen hineinverzweigen
output <ausdruck> Ausdruck (z.B. mit Variablen) ohne Zeilenvorschub ausgeben
print <ausdruck> Ausdruck (z.B. mit Variablen) mit Zeilenvorschub ausgeben
quit GDB verlassen
run Programmausführung starten
set-args <args> set-environment Programmargumente <args> setzen
<var> <wert> Environment-Triable <var> auf <wert> setzen
set <var> = <wert> Programmvariable <var> auf Inhalt <wert> setzen
source <name> SCII-Kommandodatei »name« ausführen
step {<n>} Nächste n Befehle ausführen, in Funktionen hineinverzweigen
unset-environment Environment-Triable <var> entfernen
until Programmschleife ausführen, bis nachfolgende Zeile erreicht wurde
symbol-file <name> Symboldatei <name> auswählen

Frank Mathy
Links

Copyright-Bestimmungen: siehe Über diese Seite
Classic Computer Magazines
[ Join Now | Ring Hub | Random | << Prev | Next >> ]