Hello World,
Noch vor nicht allzulanger Zeit war es durchaus üblich, für große Programmierprojekte "handgestrickte" Entwicklungsumgebungen einzusetzen. Eins hatten diese Umgebungen jedoch gemein: Als Programmiersprache kam Assembler zum Einsatz.
Maschinencode vs. Hochsprache
Naturgemäß ist eine Assembler-Lösung hinsichtlich Speichereffizienz und Ausführungsgeschwindigkeit die beste, leider glänzt diese Sprache jedoch nicht mit besonderer Lesbarkeit. Hochsprachen jedoch sind dafür bekannt, daß sie zwar sehr gut lesbar sind, dem durch Compilierung (Übersetzung) erzeugten Maschinencode sagt man jedoch nach, daß er bisweilen alles andere als optimal und darüberhinaus sehr register- und stackintensiv ist.
6502 - compilergeeignet?
Schuld hieran ist im wesentlichen das typische Programmierverhalten unter einer Hochsprache wie C oder Pascal. Betrachtet man solchen Code genauer, so zeigt sich, daß er meist aus Funktions- bzw. Prozeduraufrufen besteht, bisweilen sind diese Aufrufe sogar geschachtelt. Die Übergabe von Aufruf- und Rückgabeparametern sowie Rücksprungadressen geschieht hierbei über den Stack - beim C64 ist dieser bekanntermaßen auf Page 1, d.h. den Bereich zwischen $0100 und $01ff, limitiert. Eine Schachtelung von Funktions- bzw. Prozeduraufrufen kann also sehr schnell einen Stacküberlauf (stack overflow) bewirken und es obliegt dem Programmierer hier entsprechende Sorgfalt bei der Programmierung walten zu lassen.
Beim 6502 kommt hinzu, daß diese Architektur über insgesamt drei nutzbare Register verfügt, wovon lediglich eines, der Akkumulator, für arithmetische Operationen nutzbar ist. Die Indexregister X und Y lassen sich bedingt ebenfalls für einfache Arithmetik (Zählen) nutzen, ihr eigentlicher Zweck liegt, wie der Name bereits sagt, in der Indexierung von Speicherzugriffen.
An den Programmierer eines Compilers stellen diese architekturbedingten Einschränkungen besondere Anforderungen, soll der Compiler effizienten Code erzeugen.
Und es geht doch
cc65 hat bereits eine längere Entwicklungsgeschichte hinter sich. Ursprünglich von John R. Dunning für die 8Bitter von Atari entwickelt, erweiterte Ullrich von Bassewitz den cc65 weiter zu einem nahezu ANSI-C konformen Compiler. Im Zuge dieser Erweiterungen wurde das Originalprogramm durch komplett neue geschriebene Teile ersetzt und um Libraries für CBM-Maschinen ergänzt. cc65 ist frei erhältlich, unter der Adresse http://www.cc65.org finden sich Programmpaket und Dokumentation.
Ein vollständiges Entwicklungssystem umfaßt mehr als nur den Übersetzer, drum besteht das cc65-Paket auch aus Compiler, Assembler und Linker. Wie bereits angesprochen übersetzt der Compiler die Hochsprache in Assembler-Source, welcher dann vom Assembler in Objektfiles gewandelt wird. Diese Objektfiles enthalten neben Assembler-Source zusätzliche Informationen wie globale Variablen und nicht aufgelöste (externe) Referenzen wie z.B. Library-Aufrufe. Abschließend faßt der Linker die einzelnen Objektfiles zu einem lauffähigen Programm zusammen. Dies geschieht ausschließlich statisch, d.h. anders als bei dynamischem Linking (DLLs unter Windows, shared libraries unter Unix) werden Programm und Libraryroutinen zu einem (teilweise dicken) Packen verschmolzen.
Libraries
Erfahrene C-Programmierer wissen es: Der Compiler ist nur die halbe Miete, fast noch wichtiger sind die Bibliotheksfunktionen, ohne die z.B. gar keine Bildschirmausgabe möglich wäre.
Auch cc65 wartet mit entsprechenden Bibliotheken auf, ja es existieren sogar C64-spezifische Routinen, welche neben Bildschirm-Ein/Ausgabe auch die direkte Programmierung von VIC, SID und CIA ermöglichen. Über vordefinierte Strukturen lassen sich auf komfortable Weise alle Register beschreiben und auslesen.
Die Codequalität
Wie eingangs erwähnt ist der 6502 eine relativ undankbare Architektur zur Abbildung einer typischen Compiler-Hochsprache. Umso gespannter war ich auf die Qualität des erzeugten Codes. Tatsächlich war ich sehr überrascht von Dichte und Umfang des Compilats - der generierte Assembler-Source war von einer Qualität, welche sich auch vor den Augen erfahrener Assembler-Programmierer nicht verstecken muß.
Auch die Größe des erzeugten Executables ist - für eine Compilat - moderat. Hier hat man natürlich das nahezu unvermeidbare Problem, daß Bibliotheksroutinen aufgrund ihrer Universalität stets umfangreicher sind, als sie für den jeweiligen Anwendungszweck tatsächlich sein müßten. Der Aufschlag liegt bei kleinen Programmen (z.B. dem Klassiker "Hello World!") bei etwa 30 bis 50 Prozent, bei größeren Projekten sinkt der Anteil der Libraries an der Gesamtgröße natürlich.
Worunter läuft's?
cc65 ist ein Crosscompiler, d.h. Compilation und Linking finden auf einem anderen System als dem C64/C128 statt. Historisch unterstützt cc65 zunächst Linux und anderen Unix-Derivate, es stehen jedoch auch Portierungen auf DOS/Windows und OS/2 zur Verfügung. An einem besonderen Leckerbissen wird derzeit gearbeitet, nämlich der Unterstützung des 65816. Hiermit wäre es dann schließlich möglich, unter Verwendung einer SCPU nebst entsprechendem Speicherausbau im native mode des 65816 direkt auf einem C64/128 zu entwickeln.
Für diesen Artikel wurde die Linux-Version getestet. Die cc65-Sourcen ließen sich anstandslos übersetzen, allerdings erst nach hinreichender Lektüre der mitgelieferten README-Files. Hier wäre es wünschenswert, sich dem Stand der Zeit anzupassen und entsprechende configure-Skripten mitzuliefern - oder doch zumindest eine "make all"-Option zur Verfügung zu stellen. Leider muß die Übersetzung jeder Bibliothek manuell angestoßen werden.
Universalität ist Trumpf
cc65 unterstützt über entsprechende Bibliotheken nicht nur den C64, sondern auch C128 sowie die Rechner der C(1)16/Plus4-Serie, darüberhinaus die CBM600/700 Familie und natürlich auch der PET. An nicht-Commodores werden die Atari-8Bitter sowie der Apple][ unterstützt - ein echtes Multitalent also.
Auch unterstützt der cc65 die Entwicklung von GEOS-Applikationen und stellt hierfür eine eigene Bibliothek, die GEOSlib, zur Verfügung. Weiterhin existiert mit "grc" ein Utility zur Handhabung von GEOS-Headerfiles.
Einschränkungen
Mit Hinblick auf die Zielarchitekturen wurden einige kleine Modifikationen vorgenommen. Der Datentyp INT entspricht einem 16bit-Wert, und die Datetypen FLOAT bzw. DOUBLE sind überhaupt nicht implementiert. Zwar stimmt es, daß 6502-Systeme nativ keine Fließkommadarstellung beherrschen, die Abbildung solcher Funktionen in Software ist jedoch kein Problem und bekanntermaßen auch im BASIC-Interpreter implementiert. Entsprechend kann man Fließkommaarithmetik auf eine spezielle Mathematik-Bibliothek abbilden. Dies wäre sicherlich ein Kandidat für eine spätere Version von cc65. Wirklich tragisch ist der Mangel an Fließkommafähigkeiten jedoch nicht, da sich die meisten Anwendungen mit Integerarithmetik bewältigen lassen.
Anders als z.B. beim GNU C-Compiler gcc ist in cc65 keine Code-Optimierung implementiert. Diese muß bereits bei der Programmierung erfolgen, entsprechende Hinweise werden in der cc65-Dokumentation gegeben. Dies mag für C-Programmierer auf anderen Systemen zunächst etwas verwunderlich erscheinen, ist aber im "Small C"-Bereich durchaus nicht unüblich. (Anm.: Natürlich ist in den CC65 eine Codeoptimierung implementiert, dieser Absatz entstand aufgrund einer mißverständlichen Formulierung der Dokumentation. RB, 3.1.2001)
Fazit
Mit cc65 steht dem ambitionierten C-Programmierer ein ausgereiftes Werkzeug zur Programmentwicklung für den C64/128 und weitere 6502-basierte Systeme zur Verfügung. Die Qualität des erzeugten Codes ist, entsprechende Sorgfalt bei der Programmierung vorausgesetzt, sehr gut und man darf zu recht auf die 65816-Umsetzung gespannt sein. Wünschenswert wäre eine Dokumentation über das Erstellen eigener Libraries.