Turbo-C von Heimsoeth & Borland, den ersten Compiler auf dem ST, der sich am neuen ANSI-Standard orientiert, hat die ST-Gemein-de seit der ersten Präsentation auf der CeBIT im März ungeduldig erwartet. Seit der Beta-Test-Phase beschäftigen sich die Autoren dieses Berichts eingehend mit dem neuen Compiler. So bleiben natürlich keine Schwächen verborgen, andererseits lernt man erst innerhalb einer so langen Testphase alle positiven Aspekte eines Compilers richtig zu schätzen.
Als erstes stellt sich bei einem neuen Compiler, der gegen viele Konkurrenten antritt, die Frage »Was bietet er, was andere nicht haben?«. Bei Turbo-C lautet die Antwort »ANSI« [1], Das mag sich lapidar und unscheinbar anhören, hat aber auf die Arbeitsweise bei der Programmierung einschneidende Auswirkungen, und zwar durchweg positive. Turbo-C erfüllt diesen neuen Sprachstandard fast ganz und hat damit schon einen dicken Pluspunkt verbucht. Aber zu einem Software-Entwicklungssystem gehört mehr als eine noch nicht ganz fertige Norm, auf die man sich beruft.
Da ist zunächst die Benutzeroberfläche zu nennen. Bei Turbo-C sind alle Funktionen zu einem einzigen GEM-Programm zusammengefaßt, so daß lästige Nachladezeiten entfallen. Aus diesem Grund kann man bei Turbo-C auch nicht von einer regulären Shell sprechen, wie sie beispielsweise Lattice-C mit »MENU+« beiliegt. Turbo-C verfügt über eine einzige Menüleiste, von der aus man alle Funktionen (Editor, Compiler, Linker, Accessories) erreicht. Dies geht für einige ausgewählte Funktionen auch per Control-Taste anstatt dem üblichen Mausklick auf den Menüeintrag. Da die Menüeinträge bei Turbo-C vernünftig angeordnet sind und auch von der Anzahl her den Benutzer nicht erschlagen, läßt sich mit dieser Oberfläche und den gebotenen Shortcuts hervorragend arbeiten.
Dies tut man als Programmentwickler normalerweise im Editor. Hier hat Turbo-C einige Stärken, aber wie immer bleiben auch Wünsche offen. Die Funktionen des Editors sind stark an die auf dem Macintosh üblichen angelehnt. So wird beispielsweise mit den drei Funktionen »Copy«, »Cut« und »Paste« auf Textblöcken gearbeitet, wobei Copy den markierten Block in einen internen Puffer kopiert, Cut diesen Block aus dem Text löscht und Paste den Inhalt des Puffers an der Cursorposition im Text einfügt. Hat man sich einmal an diese drei Basis-Operationen gewöhnt, wird das Jonglieren mit Text ein Kinderspiel, besonders auch wegen Turbo-Cs einzigartiger Methode der Blockmarkierung: Drücken und Festhalten der linken Maustaste im Textfenster markiert den Blockanfang, danach wird bei weiterhin gedrückter Taste der Block entsprechend der Mausbewegung markiert. Der Clou dabei: Verläßt der Mauszeiger das Fenster, fängt der Text an zu scrollen, wobei die Blockmarke selbstverständlich mitgeführt wird. Turbo-C bietet noch weitere Spezialitäten, was Blöcke angeht: Der ganze Text läßt sich in einem Rutsch selektieren, ein Wort wird durch Doppelklick, eine Zeile durch Doppelklick mit gedrückter Shift-Taste markiert.
Zwei wichtige Funktionen, die in anderen sogenannten Editoren oft vergessen wurden, sind in Turbo-C glücklicherweise vorhanden: Shift Left und Shift Right verschieben Textblöcke um einen Tabulatorschritt nach links oder rechts. Ansonsten erfolgt die Arbeit in Turbo-C so, wie man es sich intuitiv vorstellt: Die Cursortasten bewegen den Cursor um Einzelzeichen oder -zeilen, zusammen mit < Shift > bewegen sie ihn zum Zeilenanfang oder -ende beziehungsweise eine Seite auf- oder abwärts. Der Editor von Turbo-C arbeitet mit den normalen GEM-Windows, von denen er sieben gleichzeitig verwaltet. Für einen »echten« GEM-Editor legt er erstaunliche Geschwindigkeiten beim Scrollen und Blockmarkieren an den Tag. Beim Scrollen zeigt sich auch ein kleiner Programmfehler in Turbo-C: Der vertikale Slider des Windows läßt sich unter die letzte Zeile im Text positionieren. Ein Tastendruck schaltet zwischen den einzelnen Windows hin und her. Es ist selbstverständlich vorgesehen, Blöcke in einem Window zu »cutten« um sie im nächsten wieder zu »pasten«. Damit wären wir auch schon bei den Mängeln angekommen: Diese Vorgehensweise ist der einzige Weg, um den Inhalt einer Datei in eine andere einzulesen. Die Entwickler von Turbo-C haben sowohl diese Funktion als auch »Save as...« nicht implementiert. Allerdings läßt sich Save as... noch relativ einfach realisieren: Man öffnet eine neue Datei, markiert den Inhalt der alten, kopiert in den Puffer, wechselt die Windows, führt Paste aus und speichert.
Apropos Datei öffnen: Turbo-C enthält eine eigene Fileselektorbox, die es dem Anwender erlaubt, die wichtigsten Funktionen des Desktops innerhalb von Turbo-C aufzurufen, und die nebenbei die Auswahl eines beliebigen angeschlossenen Laufwerks erlaubt. Weiterhin läßt sich nach Doppelklick auf das Extension-Feld unter den Dateinamen eine neue Extension eingeben. Auch hier ein Clou: Da man bei sehr großen Verzeichnissen mit dem Slider nur sehr ungenau positioniert, springt der Cursor beim Eintippen eines Buchstabens auf der Tastatur zu der Stelle in der Liste, an der der Dateinamen mit möglichst passendem Anfangsbuchstaben steht. Alles in allem ist der Editor eine runde Sache, an die man sich sehr schnell gewöhnt.
Dreh- und Angelpunkt eines jeden Entwicklungssystems ist der Compiler. Bei Turbo C fängt es erstmal damit an, dem Compiler zu sagen, wie er sich denn nun eigentlich benehmen soll. Dies geschieht mit dem Menüpunkt »Options/ Compiler«, der eine große Dialogbox erscheinen läßt. Einige der Schalter wollen wir näher erklären: »No non-ANSI extensions« bedeutet, daß die Turbo-C-spezifischen Erweiterungen von ANSI-C nicht mehr zugelassen sind. Diese Erweiterungen sind die beiden Schlüsselworte »cdecl« und »pascal«. Externe Funktionen, die als »pascal« deklariert sind, erhalten keinen Unterstrich vor ihren Namen, die Parameterübergabe geschieht in einer anderen Reihenfolge als sonst und von diesen Funktionen wird erwartet, daß die Funktionen den Stack selbst auf-räumen, bevor sie zurückkehren, »cdecl« erzielt den gleichen Effekt wie mit »Standard Stack frames«. Diese Funktion bestimmt, ob Turbo-C die Parameter für Funktionen über den Stack oder in Registern übergibt. Es ist leicht einzusehen, daß die Übergabe in Registern zu erheblich schnellerem Code führt als die normale Stack-Übergabe, aber wer zusätzlich mit anderen Compilern arbeitet, erzeugt so für beide Seiten verträgliche Ob-jektmodule (Turbo-C benutzt das Objektformat von Digital Research). Weiterhin interessant ist »Warning Level«. Über eine Zahl steuert man, wie genau Turbo-C auf das Einhalten des ANSI-Standards achtet. Bei »2« meldet Tür-bo-C jegliche verdächtige Konstruktion, bei »0« hat man einen praktisch stummen Compiler, der sich um Typprüfungen nicht mehr kümmert.
Eine Frage, die durchaus ihre Berechtigung hat. Bei neuen Projekten, die man mit Turbo-C beginnt, hat man es als Programmierer in der Hand, sich von vornherein an den ANSI-Standard zu halten. Das erfordert zwar einiges mehr an Disziplin und auch etwas mehr Tipparbeit als bei »alten« C-Compilern, aber wer Wert darauf legt, daß auch bei Warning Level 2 der Compiler kommentarlos seine Arbeit verrichtet, sorgt damit automatisch dafür, daß Fehler wie beispielsweise falsche Parameterzahl oder falsche Parametertypen bei Funktionsaufrufen nicht mehr Vorkommen. Andererseits haben diejenigen, die ihre Programme von einem »alten« Compiler auf Turbo-C umsetzen, einiges an Arbeit vor sich. Zunächst sollte man alle Funktionsdefinitionen auf die neue Form umstellen und für alle Funktionen Prototypen in einer Include-Datei zusammenstellen. Danach sind wohl einige Typcasts nötig, um wenigstens halbwegs von den Compiler-Warnungen verschont zu bleiben. Dieser Schritt, auch wenn er mühselig ist, hilft bereits manchmal, völlig obskure Fehler zu finden. So gelang es einem der Autoren bei der Umstellung von Litt-le Smalltalk auf Turbo-C einen Fehler bei der Parameterübergabe zu entlarven.
Vom Compiler nicht zu trennen ist der Pre-Prozessor. Sowohl hier als auch beim Compiler hält sich Turbo-C mit kleinen Ausnahmen an den neuen ANSI-Standard. Die Ausnahmen sind beim Pre-Prozessor, daß er die Trigraphen nicht erkennt, aber darauf kann man wirklich verzichten. Beim Compiler wird als Argument für »switch« kein »long« zugelassen, was ANSI aber explizit fordert. Auch diese Abweichung stellt kein ernstzunehmendes Hindernis dar. Außer dem nicht zu unterschätzenden Vorteil, daß durch den neuen ANSI-Standard C-Programme sehr viel weniger fehleranfällig werden, bietet Turbo-C auch in puncto Geschwindigkeit einiges: Dhry-stone-Werte von über 1700 erreicht zur Zeit kein anderer Compiler auf dem ST (siehe auch Artikel zum Thema Benchmarks in dieser Ausgabe auf Seite 82).
Für die gute Codequalität von Turbo-C sind wohl in erster Linie die bereits oben erwähnte Parameterübergabe in Registern und die Verwendung von PC-relativen Sprüngen verantwortlich. Für die Registerübergabe stehen die Register A0 und Al für Adreßparameter (also Pointer) und die Register DO bis D2 für Integer-Parameter zu Verfügung. Floating-Point-Werte, die Turbo-C übrigens im IEEE-Format verwaltet, übergibt es immer über den Stack. Da Turbo-C mit sich selbst übersetzt wurde, geht auch das Compilieren an sich zügig voran: Ein Projekt von 16 Dateien (insgesamt etwa 3800 Zeilen Code) compiliert es auf einer ziemlich vollen Festplattenpartition in einer Minute und 22 Sekunden (altes TOS ohne Harddiskcache).
Heutzutage gehört wohl zu jedem Compilersystem ein Utility namens »make«. Die Idee, die hinter make steckt, wurde wie so viele andere auch während der Entwicklung von Unix bei AT&T geboren. Da jedes größere Programmsystem aus mehreren Quelldateien besteht, ist es für den Programmierer ohne make sehr umständlich, bei Änderungen immer alle betroffenen Module von Hand neu zu übersetzen. Mit make hingegen wird eine Beschreibungsdatei angelegt, in der alle benötigten Module und ihre Abhängigkeiten zum Beispiel von Include-Dateien aufgeführt sind. Bei Änderungen führt make vollautomatisch alle benötigten Übersetzungsvorgänge aus. Anstatt einer fast exakten Kopie von Unix-Make wie sie beispielsweise bei Lattice-C mitgeliefert wird, benutzt Turbo-C eine abgewandelte Form der gleichen Idee, den sogenannten Project-Manager. Auch hier gibt der Programmierer neben dem Namen des fertigen Programms alle benötigten Quelldateien und Objektmodule in einem sehr einfachen Format an. Turbo-C kennt aber derzeit keine Abhängigkeiten von Include-Dateien, was bei großen Projekten unter Umständen eine schwerwiegende Einschränkung ist. Allerdings erlaubt Turbo-C in der Projektbeschreibung die Angabe von Compilerschaltern pro Quelldatei. Mit dem Projekt-Manager von Turbo-C läßt sich soweit hervorragend arbeiten.
Programmierer erfinden das Rad nur sehr ungerne zum zweiten Mal. Turbo-C verhindert dies durch die mitgelieferten Bibliotheken: Neben einer fast kompletten ANSI-Standard-Bibliothek gehören komplette Bindings zu AES, VDI, GEM-DOS, BIOS und XBIOS zum Lieferumfang. Zusätzlich gibt es noch eine sogenannte »Extended Library«, die Funktionen von MS-DOS-Turbo-C bereitstellt. Nur »fast« komplett ist die Standardbibliothek von Turbo-C, weil drei Funktionen nicht implementiert wurden: signal(), raise() und offsetof(). Da offsetof() vom Standard als Makro realisiert ist, stellt es für einen guten Programmierer kein Problem dar, diese Lücke selbst zu stopfen. Man kommt auch ohne Signal() und raise() aus. Über diese Funktionen könnte ein Programm beispielsweise selbst Adreß- oder Busfehler abfangen. Zu erwähnen ist noch, daß eigene Erweiterungen an den GEM-Bibliotheken (mit Blick auf GEM 2.0 oder das neue TOS 1.4, in dem zwei Funktionen neu sind) nur schwer vorzunehmen sind, da die Implementation der Bindings nicht dokumentiert ist. Ein Manko, das hoffentlich bis zu einem Update von Turbo-C behoben sein wird. Allerdings sind die sonst so stiefmütterlich behandelten Funktionen vq_gdos(), form_keybd(), form_button(), shel_put() und shel_get() bereits implementiert.
Benchmark AG Version 1.1 for C | |
Intmath short | 0.6000 Sekunden |
Intmath long | 1.0750 Sekunden |
Realmath short | 12.0600 Sekunden |
Realmath long | 11.7200 Sekunden |
Triglog short | 12.0450 Sekunden |
Triglog long | 11.9800 Sekunden |
For | 0.4650 Sekunden |
Loop | 0.6200 Sekunden |
Repeat | 0.4650 Sekunden |
While | 0.4600 Sekunden |
Array Access | 0.6700 Sekunden |
Record Access | 0.6200 Sekunden |
Record Array | 0.2800 Sekunden |
Write To File | 11.3000 Sekunden |
Read From File | 9.9800 Sekunden |
Pass Parameters | 3.0600 Sekunden |
Pass Structures | 1.1200 Sekunden |
Conversions | 1.6250 Sekunden |
Strings | 0.3200 Sekunden |
Turbo-C-Funktionen im Benchmark-Test
Wie schon der Compiler warten auch einige Bibliotheksroutinen mit erstaunlichen Geschwindigkeiten auf. Ein Blick hinter die Kulissen (mit einem Disassembler) offenbart liebevoll handoptimierte Funktionen, speziell im Bereich der Stringfunktionen. Die Bindings von GEMDOS, BIOS und XBIOS sind nicht wie bei anderen Compilern als Makros realisiert, sondern in der Bibliothek TCTOSLIB.LIB als jeweils einzelne Funktionen implementiert. Dies ist wegen der internen Parameterübergabe nötig und hat nebenbei den Vorteil, daß für sämtliche Bindings Prototypen bereitstehen. Aus Portabilitätsgründen sind aber die »Basis-Funktionen« gemdos(), bios() und xbios() ebenfalls vorhanden. Zu den TOS-Bindings ist noch zu sagen, daß Turbo-C an einigen wenigen Stellen von den veröffentlichen Standards (zum Beispiel [2] abweicht, was die Namensgebung angeht. So sind unter anderem die Elemente der Struktur DTA anders benannt.
Ein wichtiges Detail bei jedem Compiler ist der Startup-Code. Hier glänzt Turbo-C nicht so wie bei den Libraries sonst: I/O-Redirection ist ebensowenig wie das xArg-Verfahren implementiert. Allerdings erlaubt Turbo-C eine automatische Accessory-Erkennung über die externe Variable »_app«. Der Startupcode wird übrigens im Assembler-Quelltext mitgeliefert. Offensichtlich wurde diese Version der Startup-Datei aber nicht für die endgültige Version von Turbo-C selbst benutzt, denn Turbo-C schaltet ungefragt den Blitter ein — Pech für alle ST-Besitzer mit defektem Blitter. Während der Betatest-Phase war diese Funktion sogar im allgemeinen Startup-File enthalten.
Normalerweise liegt neben dem Computer eines Programmierers immer das aufgeschlagene Compilerhandbuch, da sich wohl kaum jemand alle Bibliotheksfunktionen merken kann. Bei Turbo-C ist dies, zumindest was die Standardfunktionen angeht, überflüssig. Die eingebaute Help-Funktion stellt für alle Funktionen genaue Beschreibungen parat, außer für AES, VDI, GEMDOS, BIOS und XBIOS. Für diese Funktionen existieren allerdings Übersichtstabellen ohne genaue Parametererklärungen.
Laut Auskunft der Firma Heimsoeth & Borland hat diese Auslassung einen trivialen Grund: Die Auslieferungsdisketten von Turbo-C sind bis zum Anschlag gefüllt. Diese Auslassung sollte aber bei einem Update behoben sein. Der Aufruf von »Help« ist in Turbo-C denkbar einfach gelöst: Man stellt den Cursor auf das Wort, zu dem Hilfe benötigt wird, und drückt die Taste »Help«. Falls Turbo-C zu diesem Wort etwas weiß, zeigt es dies in Windeseile an. Natürlich läßt sich auch über den Menüeintrag »Help« auf Oberbegriffe wie »C Language« oder »Options« verzweigen. Aus einem angezeigten Hilfsfenster schneidet man problemlos Text aus und kopiert diesen in andere Windows. Querverweise sind besonders hervorgehoben, ein Doppelklick auf ein so markiertes Wort zeigt den Hilfstext zu diesem Stichwort an. Die Helpfunktion von Turbo-C stellt ein einmaliges Hilfsmittel dar, das die Arbeit beträchtlich erleichtert. An den Stellen, wo Help nichts zu sagen hat, hilft natürlich das Handbuch. Es ist in deutscher Sprache abgefaßt und sehr übersichtlich gegliedert. Nur die sehr dünne Leimbindung hinterläßt keinen guten Eindruck. Unverständlich ist in diesem Zusammenhang, daß alle sonstigen Texte und Fehlermeldungen in englischer Sprache abgefaßt sind.
Der Linker von Turbo-C ist wie alles andere auch ins Gesamtprogramm eingebunden. Es handelt sich um einen recht schnellen Linker für Digital-Research-Objektmodule, der nicht optimierend arbeitet, das heißt er bindet immer komplette Module hinzu, auch wenn diese Funktionen enthalten, die nie zum Einsatz kommen. Leider unterstützt er die erweiterten Fähigkeiten des Atari-Linkers »ALN« nicht. ALN linkt beispielsweise x-beliebige Dateien in ein Programm und ordnet der Adresse des ersten Bytes ein Symbol zu.
Was wird sonst noch geboten? Zum Lieferumfang von Turbo-C gehören ein recht spartanischer Bibliotheksverwalter, der seine Arbeit zufriedenstellend erledigt sowie der Quellcode der im Handbuch besprochenen Beispiele und das Programm MicroCalc, einer Implementation einer Tabellenkalkulation. In den Quellen von MicroCalc sind einige recht nützliche Funktionen enthalten, die die Übertragung von Turbo-C-Programmen von MS-DOS auf den ST erleichtern. Bei der »großen« Version von Turbo-C gehört noch ein Assembler und ein Debugger mit zum Lieferumfang. Der Assembler wird auch separat vertrieben. Obwohl er einige Vorteile bietet (Unterstü-zung von 68000 bis 68030, Coprozessor 68881 und 68882), hält er doch mit dem für Entwickler kostenlosen Atari-Assembler »MadMac« nicht mit — beispielsweise erlaubt er keine lokalen Labels zwischen zwei »normalen« Labels.
Der Debugger ist wohl eher als eine Verlegenheitslösung zu betrachten, da es sich, was Funktionalität und Bedienung betrifft, um einen Clone von »SID« handelt, der für Programmierer ohne Assembler-Kenntnisse völlig wertlos ist. Ein Source-Debugger ist allerdings angekündigt. Ein sehr wichtiges Programm wurde allerdings vergessen: zu Turbo-C gehört kein Resource Construction-Set.
Insgesamt gesehen ist Turbo-C trotz der kleinen Mängel ein fantastisches Entwicklungssystem mit vielen Vorteilen zu einem konkurrenzlos guten Preis.
(uh)
Name: Turbo-C
Preis:
ohne Assembler/ Debugger 151,52 Mark
mit 282,72 Mark
Hersteller: Heimsoeth & Borland
Stärken:
□ ANSI-C-Compiler □ schneller Compiler □ schneller und kurzer Code □ umfangreiche Libraries □ schneller GEM-Editor C Kommandozeilen-Version
Schwächen:
□ kein RCS □ kleinere Auslassungen in fast allen Bereichen
Fazit: Profisystem zum Einsteigerpreis
Literatur
[1] Steffens, G.: Der Stand des Standards, ST-Magazin 9/88 S. 115
[2] Jankowski, Rabich, Reschke: Atari ST Profibuch, Sybex-Verlag GmbH, Düsseldorf 1988