Compiler

Aus C64-Wiki
Zur Navigation springenZur Suche springen

Ein Compiler (in deutsch auch Kompilierer) dient dazu, eine Quellsprache in eine Zielsprache zu übersetzen. In den meisten Fällen wird Quelltext einer höheren Programmiersprache in Maschinensprache übersetzt. Ein kompiliertes Computerprogramm kann nicht mehr ohne Weiteres manipuliert und bearbeitet werden.

Ein Compiler arbeitet normalerweise auf dem System, auf dem auch das zur übersetzende Programm laufen soll. Läuft er auf einem anderen System (übersetzt man z.B. einen Assembler-Quelltext mit einem auf einem PC laufenden Compiler in ein PRG, das für einen C64 gedacht ist), spricht man von einem Cross-Compiler.

Es gibt auch Decompiler, die aus einem Kompilat wieder Quelltext gewinnen sollen, allerdings ist deren Erfolg und die Qualität des rückgewonnenen Codes stark von den Informationen abhängig, die noch im Kompilat stecken. Zum Beispiel gehen beim Kompilieren oft die Bezeichner "verloren" und der Dekompiler muss sich dann irgendwelche eigenen Namen "ausdenken". Außerdem kann die Lizenz einer zu bearbeitenden Software verbieten, sie zu dekompilieren. So ein Verbot kann unter bestimmten Umständen allerdings auch unwirksam sein. Das deutsche Urheberrecht kennt beispielsweise Ausnahmen in §69e, wenn es um die Interoperabilität mit anderen Programmen geht.

BASIC-Compiler[Bearbeiten | Quelltext bearbeiten]

Das im C64 eingebaute BASIC V2 lässt in vielerlei Hinsicht Optimierungspotenzial offen.

  • Das Programm wird interpretiert. Das heißt, dass letztendlich gar nicht das BASIC-Programm selbst direkt auf der CPU läuft, sondern eine Art "Mittelmann" - der Interpreter - jeweils den nächsten BASIC-Befehl und seine Operanden liest, alle nötigen Daten zur Ausführung zusammensammelt und den Befehl dann ausführt. Das alles braucht Zeit und hat Effekte wie z.B. dass lange Variablennamen die Ausführung des Programms verlangsamen, da der Interpreter sie komplett lesen muss.
    • Selbst Zahlenkonstanten werden im Speicher nicht in direkt verwertbarer Form, sondern PETSCII-kodiert abgelegt. Das führt dazu, dass z.B. A=A+9999 etwas langsamer ist als A=A+1.
    • Variablen werden jeweils bei Bedarf gesucht. D.h. wird im Programm fünfmal direkt nacheinander A=A+1 ausgeführt, sucht der Interpreter insgesamt zehnmal in seinem Variablenindex (siehe Artikel Speicherbelegung) nach Variable A. Bei kompilierten Programmen taucht der Variablenname dagegen typischerweise gar nicht mehr auf, stattdessen wird im Code immer direkt auf einen Speicherbereich verwiesen: aus A=A+1 wird sinngemäß speicherzelle4711=speicherzelle4711+1.
    • Programmzeilen müssen ebenso bei Bedarf gesucht werden: Wird z.B. per GOTO 10000 zu Zeile 10000 gesprungen, geht der Interpreter das Programm im Speicher von vorne bis hinten durch, bis er Zeile 10000 findet.
    • IF springt in BASIC V2 bei nicht erfüllter Bedingung nicht etwa einfach zur nächsten Zeile, sondern überliest die nachfolgenden Befehle, was Zeit kostet. Das Gleiche gilt für REM. Kompilate überspringen den jeweilige Code einfach (oder lassen die Daten im Fall von REM einfach ganz weg).
    • Das Programm muss während der Ausführung auf Syntaxfehler geprüft werden. Kompilierter Code wurde bereits beim Kompilieren auf Syntaxfehler geprüft, entsprechend sind weitere Prüfungen während des Programmablaufs nicht nötig.
  • Die CPU 6510 des C64 ist eine 8-Bit-CPU, aber BASIC kennt den einzigen "natürlichen" Datentypen der CPU - das Byte, also eine Ganzzahl zwischen 0 und 255 - nicht. Alle Operationen mit Zahlen werden per langsamen Fließkommaoperationen abgebildet, selbst wenn z.B. im ganzen BASIC-Programm ausschließlich Ganzzahlen von 0 bis 255 benutzt werden.
    • Es gibt Ganzzahlvariablen, die per % (wie A%) gekennzeichnet werden, aber arithmetische Operationen mit ihnen werden trotzdem mit den langsamen Fließkommaoperationen ausgeführt.
    • Auch der Platzbedarf ist dadurch größer als meist nötig: Jede Zahlenvariable braucht fünf Bytes (außer in Feldern), obwohl Ganzzahlen sich meist platzsparender speichern lassen.
    • Ein Beispiel: FOR I=0 TO 255:NEXT, also das ganzzahlige Durchzählen von 0 bis 255, braucht in BASIC etwa 0,4 Sekunden und 10 Bytes Platz für das Programm. Das vergleichbare Assembler (LDX #$00, loop: INX, BNE loop, 5 Bytes) braucht rund 1,3 Millisekunden, ist also rund 300mal so schnell. Der Fairness halber muss erwähnt werden, dass der Geschwindigkeitsvorteil abseits vom Datentyp Byte deutlich geringer ausfällt und compiler-generierter Maschinencode bei weitem nicht so optimiert wie handgeschriebener Code ist, sowohl was Geschwindigkeit als auch Platzbedarf angeht.
  • Die Verwaltung von Strings ist nicht sonderlich geschickt umgesetzt. Werden Strings intensiv genutzt, wird u.U. eine langsame Garbage Collection durch den BASIC-Interpreter nötig.
  • Der für Programm und Variablen nutzbare Bereich ist auf 38 kBytes begrenzt, obwohl eigentlich mehr RAM zur Verfügung steht.

BASIC-Compiler können ein BASIC-Programm in eine schnellere Form überführen, unter Umständen benötigt das Programm dann aber auch mehr Speicherplatz. Grundsätzlich kann das Programm dabei in eine Zwischensprache überführt werden (häufig P-Code genannt), die von einem weiteren Interpreter abgearbeitet wird, den der Compiler automatisch dem Ergebnisprogramm hinzufügt (passiert z.B. beim Blitz! Compiler). Alternativ kann das BASIC-Programm direkt in Maschinensprache (im Kontext auch M-Code genannt) überführt werden (z.B. BASIC-BOSS und MOSpeed). Einige Compiler wie z.B. der BASIC 64 Compiler unterstützen beide Varianten und können im Programm sogar umschalten. Typischerweise ist M-Code schneller und P-Code platzsparender: M-Code ist meist (deutlich) größer als das Ursprungsprogramm, P-Code unter Umständen sogar kleiner.

Das nach einer gewissen Menge von String-Operationen auftretende Problem der Garbage-Collection wird nach bisherigen Erkenntnissen nur durch den Basic-Boss und durch MOSpeed wirklich effektiv gelöst. Es können also durchaus auch im Kompilat früher oder später ähnlich gravierende Verzögerungen auftreten wie im BASIC-Sourcecode.

Weitere Einschränkungen finden sich in der nachfolgenden Übersicht.

Compiler Compilierbare Dialekte Besonderheiten
Basic 64 Compiler
Basic 64
Basic 128 Compiler
Basic 128
Blitz! Compiler (Skyles Electrical Works)
Blitz
BASIC V2, ? pro Unterstützt mehrere Laufwerke.
pro Erzeugt ein Protokoll-File "Z/...", das nach dem Laden und LIST-Befehl aufschlüsselt, welche neuen Pseudozeilennummern welchen alten Zeilennummern entsprechen, hilfreich für Fehlersuche, falls trotz erfolgreichem Kompilieren das Kompilat abstürzt.
pro Kompilat ist relativ klein (nutzt P-Code).
pro Der Compiler unterstützt Integer-Optimierungen.
pro Es existiert eine Anleitung, die zwar recht oberflächlich ist, aber die Bedienung ist sehr unkompliziert.
pro Der Compiler kann mehrere Dateien in einem Rutsch übersetzen, die dann zusammen als Overlays agieren und Variablen teilen können.

Es existieren mit blitz 0.1 und Reblitz64 von PC-Kommandozeile nutzbare Versionen (Cross-Development).
Speedcompiler
Speedcompiler
BASIC V2, ?
Basic-Boss
Basic-Boss
BASIC V2, ? pro Es existiert eine umfangreiche und detaillierte Anleitung.
pro Vielfältige Befehle bieten enorm viele Einsatzmöglichkeiten, unter anderem flexible Speicherverwaltung, günstig für die Einbindung des Kompilats in ein Assembler-Programm.
pro Der Compiler unterstützt Integer-Optimierungen.
pro Beschleunigte Garbage-Collection.
neutral Bedingt durch die Komplexität müssen einige Restriktionen beim Erstellen des Ursprungscodes beachtet werden.
contra Erzeugt ein relativ großes Kompilat.

contra Sollte das Kompilieren ohne Fehlermeldung gelingen und das Kompilat dennoch abstürzen, lässt sich nicht ohne Weiteres zurückverfolgen, welche Zeile des Ursprungscodes Probleme bereitet.

Austro-Speed
Austro Speed
BASIC V2, ? Identisch mit Blitz!, Nachfolger von Austro-Comp.
Simon's
Austro Speed
Laser BASIC Compiler
Laser Compiler
pro Ein Kompilat mit Laser BASIC-Befehlen ist ohne Laden der Erweiterung lauffähig.
pro Kompiliert trotz 3-Pass recht flott.
pro Spezieller Integer-Modus, mit dem Programme ohne Fließkommazahlen schneller und kleiner werden.
pro Unterstützt erweiterte Kontrollflussbefehle (ELSE, mehrzeilige Blöcke).

contra LOAD aus dem Programm ist nicht möglich.
contra Mäßige Ausführungsgeschwindigkeit des Kompilats (im Fließkommamodus).

Diabolo
Diabolo
BASIC V2 (nur Integer-Arithmetik)
PETSPEED 64
Petspeed 64
BASIC V2
PETSPEED 128
Petspeed 128
BASIC 7.0
MOSpeed
MOSpeed
BASIC V2 pro Cross-Compiler, läuft nativ auf dem PC, erzeugt Code für den C64, den VC20, den X16, Javascript oder Powershell.
pro Open Source
pro Optimierender Compiler.
pro Startadresse des Kompilats frei wählbar.
pro Erzeugt echten Maschinencode.
pro Schreibt auf Wunsch den erzeugten Assemblerquelltext mit raus.
contra Erzeugt systembedingt ein relativ großes Kompilat (das aber über eine Compiler-Option zu Lasten der Geschwindigkeit verkleinert werden kann).
contra Der Compiler kennt keinen expliziten Integer-Typ. Integer-Berechnungen lassen sich also nur sehr eingeschränkt beschleunigen.
DTL-Basic 64
Drive Technology Ltd.[1]
BASIC V2 pro Auch als Kassettenversion verfügbar.
contra Limitierung auf 12 KByte Sourcecode (zumindest bei der Kassettenversion).
super limited basic compiler (sl-basic)[2] Untermenge von BASIC V2
(nur Integer, nur eindimensionale Arrays etc.)
pro schnell (M-Code)
pro Bringt eigene schnelle Version von PRINT
pro Compiler startet komfortabel aus Steckmodul
pro Kann BASIC- und KERNAL-ROM ausblenden
pro Inkl. Quelltexten
contra Sehr eingeschränkter Befehlssatz
Micro Compiler[3] Untermenge von BASIC V2 pro schnell (M-Code)
contra sehr eingeschränkter Befehlssatz
contra scheint häufig fehlerhaften Code zu erzeugen

Benchmarks[Bearbeiten | Quelltext bearbeiten]

Aus u.a. einem Forum64-Beitrag von muffi. Zeiten in 60tel Sekunden, Benchmark-PRG siehe Link.
R: (Schleifen) mit Real-Variablen, I: (Schleifen) mit Integer-Variablen (%).
Die Größe des kompilierten Programms bezieht sich auf den gesamten Benchmark, differenziert also nicht zwischen Real und Integer.

Compiler FOR/NEXT Init IntArr Init StrArr Append StrArr Cosinus Init RealArr Arr-Zugriff/IFs DEF FN GOSUB PRINT SYS49152,X,Y Code-Größe (Blöcke)
(BASIC V2) 7452 305 274 485 1792 309 8475 4800 5776 2993 6147 13
Basic Boss (R) 6499 100 138 233 1682 127 3002 1426 1366 2585 2936 33
Basic Boss (I) 1645 18 62 108 1676 45 611 1465 1468 2548 3225 33
BASIC64 (P-Code, R) 7047 115 123 193 1691 124 2365 1513 1408 2723 5731 32
BASIC64 (P-Code, I) 1767 54 64 114 1657 63 1377 1814 1827 2680 5296 32
BASIC64 (M-Code, R) 6946 97 109 168 1677 107 1718 1416 1325 2702 5365 45
BASIC64 (M-Code, I) 1665 36 51 90 1642 47 723 1712 1739 2661 4953 45
Austro-Comp (R) 6472 114 125 213 1700 122 1642 1675 1647 2496 3395 24
Austro-Comp (I) 1911 64 76 150 1609 73 1761 1676 1518 2461 3849 24
Austro-Speed/Blitz (R) 6027 89 97 155 1680 96 1753 1539 1535 2464 3263 *1 32
Austro-Speed/Blitz (I) 1272 39 48 95 1647 47 1151 1532 1412 2428 3706 *1 32
SC (R) 6472 114 125 214 1699 122 2710 1675 1647 2495 3395 24
SC (I) 1911 64 76 150 1669 73 1761 1679 1521 2461 3837 24
Sauron (R) 6027 89 97 156 1680 97 1732 1538 1533 2465 3271 32
Sauron (I) 1271 40 48 95 1647 47 1141 1532 1412 2427 3713 32
Petspeed (R) 1580 38 44 76 1654 40 1149 1401 1333 2671 x (90)
Petspeed (I) 1494 37 46 75 1654 39 1140 1477 1421 2668 x (90)
Laser BASIC Compiler (R) 6448 161 154 287 1703 151 4626 1666 1620 2620 x 37
Laser BASIC Compiler (I) 1925 69 79 173 / *2 68 1760 / *2 / *2 2586 x 29
DTL (R) 6355 124 128 225 1694 148 2893 1619 1592 2998 3560 11
DTL (I) 1666 52 57 123 1671 77 1434 1687 1551 2963 3968 11
MOSpeed (R) 6213 99 110 205 1667 108 2522 911 816 2687 x 78

*1: Inkorrekte Ausgabe (Leerzeichen fehlen). Reblitz64 hat dieses Problem nicht.
*2: Im Integer-Modus unterstützt der Laser BASIC Compiler keinerlei Fließkommaberechnungen.

Beobachtungen

  • Praktisch alle Benchmarks profitieren stark von Integer-Optimierungen.
  • M-Code ist insbesondere im Zusammenhang mit Integer-Operationen und Array-Zugriffen deutlich schneller als P-Code, bringt ansonsten aber wenig Gewinn.
  • Auch kompilierter Code ruft z.B. für Cosinus und PRINT typischerweise die Originalroutinen des BASIC bzw. KERNALs auf. Als Vergleich: Wird der PRINT-Benchmark mit Crank the PRINT! optimiert, braucht er statt rund 42 Sekunden (2500 Sechzigstelsekunden) nur ca. 5,5 Sekunden, weil das langsame KERNAL-PRINT umgangen wird.

Übersicht zu Einschränkungen[Bearbeiten | Quelltext bearbeiten]

Compiler Explizite Integer-Unterstützung P-Code/M-Code DIM für einfache Variablen[4] Implizites DIM[4] alternative Garbage-Collection
Basic 64 Compiler pro beides contra Nachfrage contra
Basic 128 Compiler pro beides contra Nachfrage contra
Blitz! Compiler pro P-Code pro Nachfrage contra
Speedcompiler ? ? contra Nachfrage contra
Basic-Boss pro M-Code contra implizit immer 11 (0-10) pro
Austro Speed pro P-Code pro Nachfrage contra
Simon's Austro Speed pro P-Code pro Nachfrage contra
Laser BASIC Compiler ? ? contra Nachfrage contra
Diabolo ? ? contra Nachfrage contra
PETSPEED 64 ? ? contra Nachfrage contra
PETSPEED 128 ? ? contra Nachfrage contra
MOSpeed contra M-Code pro
(in Verwendung als Konstante[5])
pro pro
DTL-Basic 64 ? ? contra Nachfrage contra

Quellen[Bearbeiten | Quelltext bearbeiten]

  1. DTL-Basic 64 Compiler angekündigt in Personal Computer News: Go Faster With DTL, David Janda looks at a cassette-based C64 Basic compiler Sprache:englisch
  2. Thema: Super limited basic compiler for Commodore 64 auf Forum64.de
  3. Micro Compiler aus der RUN 07/1986 S. 83, siehe Thema: Heute so compiliert... auf Forum64.de (D64), Anleitung siehe Posting #371
  4. 4,0 4,1 Behandlung von DIM in BASIC-Compilern: Thema: BASIC V2 Interpreter/Compiler in/für Java auf Forum64.de
  5. Behandlung von DIM bei MOSpeed: Thema: BASIC V2 Interpreter/Compiler in/für Java auf Forum64.de

Weblinks[Bearbeiten | Quelltext bearbeiten]

WP-W11.png Wikipedia: Compiler


Auswahl ursprünglich in BASIC geschriebener kompilierter Programme